資訊通識
&
Server

臨時被抓來教伺服器的

一四學術 蔡嘉晉

偷懶找學弟來教課的

一三學術長 吳亞倫

工商時間

lecturer

建電一四學術 蔡嘉晉 / cjtsai

2025 TOI 1!

有學術活動就有可能看到我

Disclaimer:

如果你覺得簡報很醜去找吳雅倫

我fork他的

雲端服務

cloud services

由大公司在全世界各地建設伺服器機房,並將其出租,供使用者租賃伺服器資源,包括但不限於算力、DNS服務、防火牆及很多很多

一般來說,自行架設以上所有資源並非不可行,然而其代價過大,若無大量需求,向供應商租借更為方便

不同於家用電腦,伺服器大多使用Linux作業系統,並需要24hr開啟,這會對平常使用的電腦造成負擔,所以大多數人會選擇多放置一台微型電腦或向雲端服務供應商租借伺服器

常見 CSP (Cloud Service Provider)

GCP (Google)

Azure (Microsoft)

AWS (Amazon)

Digital Ocean

還有很多

linode, oracle, bluehost ...

台灣的(通常很貴): 中華電信、國雲網路

其實用最大的就好了

像是AWS最近也才開了台北區

各CSP使用心得

為了架我的網站 我用過挺多的

AWS: 很喜歡偷收莫名其妙的小錢 買之前看清楚

GCP: 唯一不用綁信用卡就可以試用的 收費很玄

Digital Ocean: 收費比較透明 但帳號管理很爛

Azure: 這個我沒用過

目前AWS(Lightsail)使用中

VPS

全名 virtual private server

是CSP提供的一種服務,在伺服器上分割一部分資源供我們使用,有完整的檔案系統,並可以指派防火牆與靜態IP,在不同的地方有不同名字

AWS: Lightsail(你要用的打包好), EC2(全手動)

GCP: Compute Engine

Digital Ocean: Droplet

蹭資源

眾所周知,GCP提供每個新用戶300鎂的試用額度

這代表你可以開小帳去蹭

但這300鎂只能用三個月

另外的選項是去申請 GitHub Student Pack(一次性)

他會給你 Digital Ocean 200鎂可以用一年

然後因為我們很窮,所以這次要用GCP

但我們已經開好了所需的伺服器,所以問題不大

等下會按照組別把伺服器IP及密碼發下

這次不會再有相同金鑰然後有人搞事有人爆氣有人搞消失的事情發生了 真的 詳情請洽 2024 建北電資聯合寒訓 - 你資道我喜歡你嗎

Shell Command

禮拜一講過了基本的

但還有一些沒講到而我們需要用的

SSH (Secure Shell)

取代舊的不安全連線,如 telnet 你們前幾天才用過

建構安全的通訊與資料傳輸

功能很多 最常用的是連線登入遠端伺服器

你們應該也用過了

ssh username@hostip

可以透過密碼登入 也可以透過預先產生的金鑰

也可以預先建立設定檔多層跳轉

reverse ssh tunnel

ssh 的另外一種用法

建電的電腦目前正在使用他來跟外面的網路溝通

建中網管中心的巢狀網路系統使得該電腦只得連接於內網

ssh tunnel: 

Client

Server

reverse ":

Client

Server

data

轉發

透過提前允許server之某一port存取本伺服器的某一port

使接下來能透過這個tunnel存遠端傳送資料或甚至建立ssh連線

ssh -R external_port:localhost:local_port external_server_ip
ssh -R 25565:localhost:25565 203.64.52.132

scp (secure copy)

從遠端複製資料回來或丟東西上去

scp local_file external_user@external_server:external_file
scp external_user@external_server:external_file local_file

or

前者是丟上去 後者是抓下來 跟cp的邏輯一樣

scp cjtsai@ruby.cjtsai.com:~/file file

ngrok

你們可能在網路上看過這張梗圖

很顯然別人連不到你的localhost

但如果你真的很想跟別人分享你寫的東西但懶得部署怎麼辦

後面會教你怎麼部署

ngrok

他可以讓你把內網的port轉發出去

而且超快 一行 事前準備不算

ngrok http 8000

但你直接打的話會發現

他叫你去官網註冊token 跟著做就對了

弄好後再打一次 他會給你一個網址 此時你的8000口就被轉發出去了

當然你想要轉發其他口也可以

但注意他是暫時性的 ctrl+c之後即會停止 長期的好像要錢

ln (link)

在檔案間建立連結

通常拿來在一些系統設定檔或是執行檔之間的關聯

像是可能會把

/usr/bin/g++-14 ln 到 /usr/bin/g++

ln -s /usr/local/bin/g++-13 /usr/local/bin/g++

網路協定

剛剛講過

伺服器概論

防火牆

Firewall

為甚麼要防火牆

曾經有sb問過 為甚麼會有人想攻擊我們的服務/伺服器

請先相信 任何連接上網路的服務即是標靶

你從來不會知道哪天哪個吃飽太閒的學生會去攻擊建中的網頁

或是哪個俄羅斯人會把你的社網偷走

常見防火牆

iptables: 傳統 偏底層

nftables: iptables pro

firewalld: RedHat/Fedora/CentOS 使用

ufw: ubuntu 使用的高階防火牆 底層使用iptables

顯然我們今天要用 ufw

Uncomplicated Firewall (ufw)

先安裝

sudo apt install ufw
ufw allow http
ufw allow https
ufw allow ssh

先允許一些請求通過防火牆

啟動防火牆

ufw enable

查看狀態

ufw status

很重要

如果你使用的是 VPS

基本上只有使用 ssh 連線的方式

所以如果你用 ufw 把 ssh 擋掉

那就謝謝再聯絡了

所以每次 ufw enable 之前記得確認有 allow ssh

我之前架遊戲server的時候就幹過這種事

笑死

還有 GCP 有神奇的後台可以溜進去改設定

AWS好像就沒有

hosts

/etc/hosts

不知道為甚麼要講這個

可能是因為你們等下剛好會用到 但不是很重要

他可以在本地覆寫 DNS 讓連結特定網址的人去到特定的ip

他可以讓你測試時不用等待 DNS server 的長時間延遲

或是噁心人  把他電腦的校網改成怪怪的東西

DNS server

在你輸入一個網址時,你的伺服器會先去 hosts 檔找有沒有對應的 ip 有的話就連上去 沒有的話就去找全球的 DNS server

Unix: /etc/hosts

Windows: C:\WINDOWS\system32\drivers\etc\hosts

 

Format

挺簡單 對

像是你希望 aawsodian.ckefgisc.org 指到 203.64.52.132

server-ip domain-name
203.64.52.132 aawsodian.ckefgisc.org

接在最下面即可

注意因為他是系統檔案要用sudo開

windows的話建議複製好一份改完拖進去

伺服器軟體

Nginx & Apache

什麼是伺服器軟體?

伺服器硬體

伺服器軟體

電腦設備

網路設備

機房

作業系統

各種應用程式:

負責讓伺服器發揮它的功能

網頁伺服器軟體

web server 包含了一連串控制網路用戶如何訪問託管檔案 。HTTP 伺服器是其中一個部份,它理解 URLs HTTP(瀏覽器用來觀察網頁的協議)。它能透過域名(domain name)訪問託管的網站,並將其內容遞送到終端用戶的設備上。

看不懂?

就是判斷使用者輸入的網址,

給出相應的網頁內容!

(或是丟出 404)

其他用途:反向代理、Http Cache、負載平衡器

常見的網頁伺服器軟體

1. Apache HTTP Server

  • 由 Tim Berners Lee 開發並於 1995 年發布

  • 由 Apache 基金會維護。

  • 為全球約 46% 的網站提供支持

2. NGINX

  • 發音為“Engine X”

  • 由 Igor Sysoev 於 2004 年發布

3. Microsoft IIS

常見的網頁伺服器軟體

1. Apache HTTP Server

2. NGINX

3. Microsoft IIS

我們今天要交的是 Nginx 

Why?

Nginx 的基本使用

安裝 Nginx

sudo apt update
sudo apt install nginx

Nginx 的基本使用

配置 Nginx

Nginx 的設定文件都位於 /etc/nginx 目錄下

主要設定檔:nginx.conf

其他設定檔:/conf.d/*.conf

例如:iscoj 的文件放在 /conf.d/iscoj.fg.tp.edu.tw.conf 裡

主要設定檔:nginx.conf

user www-data; ##設定 Nginx 使用 www-data 用戶執行。
worker_processes auto; ##自動調整工作進程的數量
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf; ##包含額外的模塊配置文件

events {
	worker_connections 768;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	types_hash_max_size 2048;
	# server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# SSL Settings
	##

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Gzip Settings
	##

	gzip on;

	# gzip_vary on;
	# gzip_proxied any;
	# gzip_comp_level 6;
	# gzip_buffers 16 8k;
	# gzip_http_version 1.1;
	# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}


#mail {
#	# See sample authentication script at:
#	# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#	# auth_http localhost/auth.php;
#	# pop3_capabilities "TOP" "USER";
#	# imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#	server {
#		listen     localhost:110;
#		protocol   pop3;
#		proxy      on;
#	}
#
#	server {
#		listen     localhost:143;
#		protocol   imap;
#		proxy      on;
#	}
#}

預設文件:

次要設定檔:

virtual host: 一機多站

差在哪?

/conf.d/*.conf

/sites-available/*

/sites-enabled/*

  • 新作法
  • 所有放在目錄下的 vhost config 都會啟動
  • 官方推薦的
  • 舊作法
  • sites-available 存放所有 vhost config

  • link 要啟動的 config 到 sites-enable

  • Apache 的管理方式

vhost config 寫法

先來看看 default 檔案

架設靜態網站的寫法

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	root /var/www/html;

	index index.html index.htm index.nginx-debian.html;

	server_name _;

	location / {
		try_files $uri $uri/ =404;
	}
}

監聽80口 ipv4 & ipv6

設定靜態網頁的根目錄

index的檔案名稱

如果你有網域並且已經指過去 就要打在這裡

沒東西就跳404

vhost config 寫法

解析一下他的常見基本架構 - reverse proxy

upstream api {
    server localhost:5000;
}

server {
    listen 80;
    listen [::]:80;
    server_name dian.cjtsai.com;
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;


    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
            proxy_pass http://api/;
    }
}

定義了要將 request proxy 過去的應用

主區段

監聽 80 port (http)

路由設定(其實這裡跟第10行衝突)

非常重要,是反向代理的主要設定區

透過proxy_pass 可以使 / 連結至5000口

vhost config 寫法

解析一下他的常見基本架構 - reverse proxy 例子

server {
    server_name iscoj.fg.tp.edu.tw iscoj.ckefgisc.org;
    location /{
        proxy_pass http://tioj/;

        proxy_set_header X-Real-IP $remote_addr;
    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        # 舊網址 to 新網址格式 (with s)
        rewrite "^/problem/(.*)$" /problems/$1 permanent;
    }

#iscoj auto refresh and external judge server connection
    location /cable {
        proxy_pass http://localhost:4000/cable;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400; # 1 day
    }
}

vhost config 寫法

更多 - https

我們通常使用一個叫 certbot 的東西自動生成憑證與金鑰

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
  	# 憑證與金鑰的路徑
    ssl_certificate /etc/letsencrypt/live/iscoj.fg.tp.edu.tw/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/iscoj.fg.tp.edu.tw/privkey.pem; # managed by Certbot
}

server {
    if ($host = iscoj.fg.tp.edu.tw) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    if ($host = fgiscoj.fg.tp.edu.tw) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name iscoj.fg.tp.edu.tw fgiscoj.fg.tp.edu.tw;
    return 404; # managed by Certbot

}

這次要監聽 443 口

憑證金鑰路徑 由certbot自動生成

使用多個網域時

透過這個自動轉發

也是certbot生成的

不懂?來看看 iscoj 的反向代理設定

這是舊版

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#

#	passenger_enabled on;


map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}


upstream vscode{
	server 0.0.0.0:8443;
	keepalive 16;
}
upstream websocket{
	server 0.0.0.0:3000;
	keepalive 16;
}
upstream tioj {
	server 0.0.0.0:4000;
	keepalive 16;
}

upstream qduoj {
	server 0.0.0.0:1443;
	keepalive 16;
}

upstream cms {
	server 0.0.0.0:8890;
	keepalive 8;
}

upstream cms-admin {
	server 0.0.0.0:8891;
	keepalive 8;
}

upstream cms-rank {
	server 0.0.0.0:8892;
	keepalive 8;
}


# fgiscoj -> qduoj
server {
	listen 443 ssl;
  	listen [::]:443 ssl;

  	# 憑證與金鑰的路徑
    ssl_certificate /etc/letsencrypt/live/iscoj.fg.tp.edu.tw/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/iscoj.fg.tp.edu.tw/privkey.pem; # managed by Certbot

	root /var/www/html;
	server_name fgiscoj.fg.tp.edu.tw;

	location /{
		proxy_pass https://qduoj/;
		# 把 IP、Protocol 等 header 都一起送給反向代理的 server
		proxy_set_header X-Real-IP $remote_addr;
      	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      	proxy_set_header X-Forwarded-Proto https;
      	proxy_set_header X-Forwarded-Server $host;
      	proxy_set_header Host $http_host;
      	proxy_redirect off;
	}
}


server {

	# SSL configuration
	#
	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;
	
  	# 憑證與金鑰的路徑
    ssl_certificate /etc/letsencrypt/live/iscoj.fg.tp.edu.tw/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/iscoj.fg.tp.edu.tw/privkey.pem; # managed by Certbot
	# Note: You should disable gzip for SSL traffic.
	# See: https://bugs.debian.org/773332
	#
	# Read up on ssl_ciphers to ensure a secure configuration.
	# See: https://bugs.debian.org/765782
	#
	# Self signed certs generated by the ssl-cert package
	# Don't use them in a production server!
	#
	# include snippets/snakeoil.conf;

	root /var/www/html;

	# Add index.php to the list if you are using PHP
	# index index.html index.htm index.nginx-debian.html;
	
	server_name iscoj.fg.tp.edu.tw iscoj.ckefgisc.org; 
	location /{
		proxy_pass http://tioj/;

		# 把 IP、Protocol 等 header 都一起送給反向代理的 server
		proxy_set_header X-Real-IP $remote_addr;
      	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      	proxy_set_header X-Forwarded-Proto https;
      	proxy_set_header X-Forwarded-Server $host;
      	proxy_set_header Host $http_host;
      	proxy_redirect off;
		

        # 舊網址 to 新網址格式 (with s) 
		rewrite "^/problem/(.*)$" /problems/$1 permanent;
	}
	
#iscoj auto refresh and external judge server connection

    location /cable {
        proxy_pass http://localhost:4000/cable;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400; # 1 day
    }	
	
	location = /problem {
		return 302 /problem/;
	}
	
	location = /vscode {
		return 302 /vscode/;
	}
	location /vscode/ {
		proxy_pass http://127.0.0.1:8443/;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;
		proxy_set_header X-NginX-Proxy true;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_redirect off;
	}

	location = /cms {
		return 302 /cms/;
	}
	location /cms/ {
		proxy_pass http://cms/;  
	}	

	location = /cms/admin {
		return 302 /cms/admin/;
	}
	location /cms/admin/ {
		proxy_pass http://cms-admin/; 
	}	

	location = /cms/ranking {
		return 302 /cms/ranking/;
	}
	location /cms/ranking/ {
		proxy_pass http://cms-rank/;
		proxy_buffering off;
	}	

	location /cms/<ANS> {
		return 302 https://hackmd.io/_uploads/rJMJQzcNn.png;
	}
	location /cms/96 {
		return 302 https://hackmd.io/@QYi6gllvSum0RZrW3jaysg/Sy75nf9Vn;
	}
	location /cms/96/ {
		return 302 https://hackmd.io/@QYi6gllvSum0RZrW3jaysg/Sy75nf9Vn;
	}
	location /cms/2000000080{
		return 302 https://hackmd.io/@ckefgisc-izcc/BkZzbzPQ0;	
	}
	location /cms/2000000080/{
		return 302 https://hackmd.io/@ckefgisc-izcc/BkZzbzPQ0;
	}

}


server {
    if ($host = iscoj.fg.tp.edu.tw) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

	if ($host = fgiscoj.fg.tp.edu.tw) {
	    return 301 https://$host$request_uri;
	} # managed by Certbot

	if ($host = iscoj.ckefgisc.org) {
	    return 301 https://$host$request_uri;
	} # managed by Certbot


	listen 80 default_server;
	listen [::]:80 default_server;
	server_name iscoj.fg.tp.edu.tw fgiscoj.fg.tp.edu.tw iscoj.ckefgisc.org;
    return 404; # managed by Certbot

}

如何啟動 nginx ?

# 啟用
sudo systemctl enable nginx

# 啟動
sudo systemctl start nginx

# 重啟
sudo systemctl restart nginx

# 更新設定檔後 reload
sudo systemctl reload nginx

# 關閉
sudo systemctl stop nginx

# 查看狀態
sudo systemctl status nginx

##
# 無法啟動?
# 檢查誰占用了 80 port (通常是 apache2)
sudo lsof -i -P -n | grep LISTEN

# 把 apache 關掉
sudo systemctl stop apache2

nginx 更多指令

# 如果你沒有systemctl 可以使用nginx -s下達各種指令
nginx -s start/stop/reload

# 檢查conf檔案是否可用
nginx -t

# 自訂義conf檔案路徑
nginx -c [conf_file]

有時候在開啟一些服務時 如Certbot

他會很熱心的幫你把nginx打開

但這時就會跟 systemctl 的開法衝突

然後就會爛掉 要用這種方法把nginx關掉

或是直接kill 簡單快速方便

上機測驗 1

目標:將社網部署到伺服器上

要求:

  1. 設定完成 ufw 防火牆
  2. 使用 Nginx 部署
  3. 可以查資料
  4. 盡量不用 GPT 或其他 AI
  5. 限時 40 min
  6. 不可以抄作業,只能跟同組討論

提示:

1. 社網在這裡(為啥 27th? 因為 react 太麻煩)

2. 如何遠端複製檔案到 server? 用 scp

3. 可以 google:nginx static site 試試看

4. 如果你要 https ,可以用看看 certbot,但今天沒網址給你試

5. 如何檢視電腦 ip?用 hostname -I 看

Docker

什麼是 Docker ?

  • 一個類似於虛擬機的東東
  • 可以十分輕量化的建立一個服務
  • 確保系統、環境到哪裡都一樣
  • 設定好安裝指令,可以簡易的部署到任意地方

三個基本概念

  • 映像檔(Image)
  • 容器(Container)
  • 倉庫(Repository)

三個基本概念

  • 映像檔(Image)
    • 就是一個唯讀的模板
    • 例如:一個映像檔可以包含一個完整的 ubuntu 作業系統環境,裡面僅安裝了 Apache 或使用者需要的其它應用程式。
    • 映像檔可以從倉庫抓取,或是利用 Dockerfile 建立
    • 映像檔可以用來建立 Container
  • 容器(Container)
  • 倉庫(Repository)

三個基本概念

  • 映像檔(Image)
  • 容器(Container)
    • 容器是一個運作的實體
    • 可以啟動、開始、停止、刪除
    • 每個容器都是相互隔離的
    • 可以把它看作一個小 VM ,跑著特定的 linux
  • 倉庫(Repository)

三個基本概念

  • 映像檔(Image)
  • 容器(Container)
  • 倉庫(Repository)
    • 倉庫是集中存放映像檔檔案的場所
    • 最大的公開倉庫是 Docker Hub

為甚麼要用docker

  • 安全
  • 獨立
    • 在isolated的環境中執行
    • 內部程式無法觸及外部伺服器環境
  • 方便部署
    • 快速開啟關閉執行緒
  • upscale
    • 輕鬆複製
  • 在不同設備使用相同的環境運行
  • 很爽

alternatives

  • kubernetes
    • 每天都有人在吵要用哪個 笑
  • podman
    • 聽說是 docker 升級版 沒用過

來建立一個基本的 Docker 

拉取 ubuntu image

 docker image pull ubuntu

查看 image list

 docker image list

成立一個 container 並且執行指令

# 產生一個 ubuntu 並且輸出 Hello world
sudo docker run ubuntu /bin/echo 'Hello world'

# 產生一個 ubuntu 並且進入 bash
sudo docker run --name test ubuntu "/bin/bash"

# 查看現有的 container
docker ps -a

這樣感覺很雞肋

對,沒錯

所以來介紹另一種建立 image 的方式 --- Dockerfile

實務上我們會從 Docker Hub 下載官方的 Docker Image 使用,但官方的 image 功能可能過於陽春,我們可能想根據自己的需求,再安裝其他的 app,最後再打包成自己的 Docker image

Dockerfile 指令

FROM: 映像檔基底 像是 ubuntu 或是有內建一些東西的nginx node

VOLUME: 在不同的執行階段保存檔案

WORKDIR: 指定之後的 RUN/CMD/ENTRYPOINT/COPY之根目錄

COPY: 從原本路徑複製一份到image裡面的新路徑中 常用 COPY . RUN: 執行指令

EXPOSE: 揭露某一個特定端口給原本的電腦連接

ENTRYPOINT: CMD的前置作業

CMD: 由image建立container時執行的指令

 

等下會更詳細介紹 先來看 Dockerfile 架構

Dockerfile 架構

FROM ubuntu:latest #抓取預設映像檔

VOLUME /data #使 /data 跨工作階段保存
WORKDIR /data #指定在 /data 工作

COPY . . #將現有的東西複製進去

RUN apt install -y neofetch 
# 注意 -y 很重要 不然 apt 會跳prompt
# 它會讓你永遠install不完
RUN whatever you want
# 記得不要有會等待的東西就好

EXPOSE 3000
# 向外部開啟3000口(通常做測試用)

CMD ["rails", "s"]
# 啟動服務 這時如果這個指令停下整個container也會停下
# 所以通常是放 node server.js / rails s 之類的東西

背景

設定

轉移資料

做正事

做完叫大家來看

啟動跟大家分享

照這個做基本上不會出甚麼大事

但要注意有時候有些東西也不是必需的

有些則可以直接寫在等下要講的 docker compose

回來講那些指令怎麼用

分開來講 先後順序問題回去看上面

FROM

指定基礎image 寫在第一行

FROM 基礎image:版本

像是

FROM node:20

版本也可以直接寫 latest

FROM ubuntu:latest

VOLUME

在不同執行階段間固定一些文件

像是你希望 /data 不要被洗掉

VOLUME dir
VOLUME [dirs...]

也可以一次寫很多個

VOLUME /data
VOLUME ["/data1", "/data2"]

其實我不常用 大多時候直接在compose寫

WORKDIR

使接下來的 CMD / RUN / ENTRYPOINT / COPY

會發生在你指定的地方

WORKDIR dir

好像沒什麼好講的

WORKDIR /data

COPY

這個copy比較酷一點

COPY original_file file_in_image

他會把你現在這個Dockerfile所在的檔案系統的東西

根據你的設定塞進你要創建的image當中

並放在後者的路徑上 建議使用絕對路徑

COPY app/ /data/app

COPY

注意 / 的使用

對於後者

如果是 /data/

代表 data 是一個資料夾 你要把前述東西放進去

如果是 /data/app

代表 app 是一個檔名 你要把前述東西命名為app並放到data裡面

COPY app/ /data/app

也有很多人直接

COPY . .
COPY . .

全部砸進去 誰管他

RUN

就執行一個 cli 命令阿

RUN command

唯一要注意的是不能讓他跳出選擇畫面

不然永遠跑不完

最常見的就是apt install要加 -y

其它自己查

RUN apt install -y neofetch

EXPOSE

就打開一個port阿

EXPOSE port

阿對就這樣

但也不常用 在compose設定就好

EXPOSE 3000

ENTRYPOINT

可以幫你在正式開啟服務前做些事情

ENTRYPOINT ["command"...]

為甚麼不用 RUN: 那些東西在build時就跑完了

為甚麼不用 CMD: 太正式了 要先準備一下

通常會寫一個 shell script 去跑

ENTRYPOINT ["docker-setup.sh"]

沒用過

CMD

最終站

CMD ["command"...]

啟動最終程式

如果這個程式只跑一下就跑完了或是以daemon開啟就會直接關掉container

所以通常是用啟動伺服器的指令放在這裡

CMD ["rails", "s"]

有空格的話就要分開

話說我很久以前說我要來幹訓傳教 Ruby on Rails

為甚麼現在在講SA

Dockerfile

好懶得做簡報喔 by AaW in 2024

害我做簡報好累

Dockerfile

來看看例子

 

FROM ubuntu:latest

# Install Linux library dependency
RUN apt-get update
RUN apt-get install -y wget apt-transport-https gpg

# Install microsoft.gpg
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg
RUN mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
RUN wget -q https://packages.microsoft.com/config/ubuntu/18.04/prod.list
RUN mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
RUN chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
RUN chown root:root /etc/apt/sources.list.d/microsoft-prod.list

# Install .NET Core SDK
RUN apt-get update
RUN apt-get install -y dotnet-sdk-2.1

# Display Greeting
CMD [ "echo", "Ubuntu 18.04 LTS with .NET Core 2.1"]

Dockerfile

來看看例子

CMS Dockerfile by 成大某位電神

FROM ubuntu:20.04

RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y \
    build-essential \
    cgroup-lite \
    cppreference-doc-en-html \
    fp-compiler \
    git \
    haskell-platform \
    libcap-dev \
    libcups2-dev \
    libffi-dev \
    libpq-dev \
    libyaml-dev \
    mono-mcs \
    openjdk-8-jdk-headless \
    php7.4-cli \
    postgresql-client \
    python3-pip \
    python3.8 \
    python3.8-dev \
    rustc \
    sudo \
    wait-for-it \
    zip \
    vim

# Create cmsuser user with sudo privileges
RUN useradd -ms /bin/bash cmsuser && \
    usermod -aG sudo cmsuser
# Disable sudo password
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Set cmsuser as default user
USER cmsuser

COPY --chown=cmsuser:cmsuser . /home/cmsuser/cms

WORKDIR /home/cmsuser/cms
EXPOSE 8888-8890

RUN sudo pip3 install -r requirements.txt
RUN sudo pip3 install -r dev-requirements.txt
RUN sudo python3 setup.py install

RUN sudo python3 prerequisites.py --yes --cmsuser=cmsuser install

RUN sudo sed 's/cmsuser:0000@localhost/postgres@db/' ./config/cms.conf.sample \
    | sudo tee /usr/local/etc/cms.conf

ENV LANG C.UTF-8

CMD [""]

基本上 RUN 開頭的東西都是 CMS doc 裡面的安裝指令

build

使用這個指令可以把一個Dockerfile編譯成image

image name的設定是為了之後方便啟動

build dir 則是指定你的Dockerfile在哪個資料夾

通常使用 . 即可

docker build -t image_name build_dir
docker build -t nasa2025 .

run

把 image 啟動成 container 並開啟

這樣就開始跑了 但有一些常用的flag

docker run image_name
docker run -it image_name bash

這可以開起來並且連接一個shell進container

並使用bash

exec

連接shell進一個container

docker exec -it container_name bash

其他docker指令

列出所有container

把一些build完但不需要用到的image刪除

如果你亂build然後沒空間可能要用

docker ps -a
docker images

列出所有image

docker system prune

Docker Compose

舊版 docker compose 使用之指令為

docker-compose

新版為

docker compose

可是,有些時候,一個 Container 沒辦法完成所有事情

Docker-compose

一個指令,自動根據規則

完成 build image、啟動多個 container

並且操控多個 container

 

docker-compose.yml 就是 container 的管理文件,只是採用 code 形式描述

基本概念

 

  • 服務 (Service): 一個應用的容器,定義在 docker-compose.yml 文件中。
  • 網絡 (Network): 服務之間的通信方式,默認會為每個 Compose 應用創建一個網絡。
  • 卷 (Volume): 持久化數據存儲,服務可以共享和持久化數據。

看看例子

version: '3'
services:
  web:
    image: my-web-app:latest
    ports:
      - "5000:5000"
    depends_on:
      - redis
  redis:
    image: redis:alpine
  • 總共有兩個 container

  • 一個 Web 服務和一個 Redis 服務

  • image: 從哪裡抓 image

  • volumes: 映射資料夾進去容器

  • environment: 傳送變數

  • ports: 映射通訊阜口

看看例子

version: "3.7"

x-logging:
  &default-logging
  driver: "json-file"
  options:
    max-size: "100m"
    max-file: "2"

services:
  web:
    build:
      context: ./
      args:
        MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
        TIOJ_KEY: $FETCH_KEY
    environment:
      - RAILS_ENV=production
      - SMTP_USERNAME
      - SMTP_PASSWORD
      - SMTP_PORT
      - SMTP_ADDRESS
      - MAIL_HOSTNAME
      - MAIL_SENDER
    volumes:
      - /srv/tioj/web-td:/tioj/td
      - /srv/tioj/web-log:/tioj/log
      - /srv/tioj/public:/tioj/public
    depends_on:
      - db
    networks:
      - app
    ports:
      - "4000:4000"
    logging: *default-logging
  db:
    image: mysql:8.0
    volumes:
      - /srv/tioj/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
      - MYSQL_DATABASE=tioj_production
    networks:
      - app
    logging: *default-logging
  judge:
    build: https://github.com/TIOJ-INFOR-Online-Judge/tioj-judge.git#ddb2f430eb7b6a4561ed23c1ef2ad94fcafc5b22
    environment:
      TIOJ_URL: "http://localhost:4000"
      TIOJ_KEY: $FETCH_KEY
    volumes:
      - /srv/tioj/judge-td-pool:/var/lib/tioj-judge/td-pool
      - /srv/tioj/judge-testdata:/var/lib/tioj-judge/testdata
    tmpfs:
      - /tmp:exec
    privileged: true
    network_mode: host
    pid: host
    logging: *default-logging

networks:
  app:
  • 總共有三個 container

  • image: 從哪裡抓 image

    • web: 直接編譯 dockerfile

    • db: 從 docker hub 抓 image

    • judge: 從另一個github repo 的 dockerfile 編譯

  • volumes: 映射資料夾進去容器

  • environment: 傳送變數

  • ports: 映射通訊阜口

看看例子

version: "3"
services:

  oj-redis:
    image: redis:4.0-alpine
    container_name: oj-redis
    restart: always
    volumes:
      - ./data/redis:/data
  
  oj-postgres:
    image: postgres:10-alpine
    container_name: oj-postgres
    restart: always
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=onlinejudge
      - POSTGRES_USER=onlinejudge
      - POSTGRES_PASSWORD=onlinejudge

  judge-server:
    image: registry.cn-hangzhou.aliyuncs.com/onlinejudge/judge_server
    container_name: judge-server
    restart: always
    read_only: true
    cap_drop:
      - SETPCAP
      - MKNOD
      - NET_BIND_SERVICE
      - SYS_CHROOT
      - SETFCAP
      - FSETID
    tmpfs:
      - /tmp
    volumes:
      - ./data/backend/test_case:/test_case:ro
      - ./data/judge_server/log:/log
      - ./data/judge_server/run:/judger
    environment:
      - SERVICE_URL=http://judge-server:8080
      - BACKEND_URL=http://oj-backend:8000/api/judge_server_heartbeat/
      - TOKEN=CHANGE_THIS
      # - judger_debug=1
  
  oj-backend:
    image: registry.cn-hangzhou.aliyuncs.com/onlinejudge/oj_backend
    container_name: oj-backend
    restart: always
    depends_on:
      - oj-redis
      - oj-postgres
      - judge-server
    volumes:
      - ./data/backend:/data
    environment:
      - POSTGRES_DB=onlinejudge
      - POSTGRES_USER=onlinejudge
      - POSTGRES_PASSWORD=onlinejudge
      - JUDGE_SERVER_TOKEN=CHANGE_THIS
      - FORCE_HTTPS=1
      # - STATIC_CDN_HOST=cdn.oj.com
    ports:
      - "0.0.0.0:8000:8000"
      - "0.0.0.0:1443:1443"
  • 總共有四個 container
    oj-redis、oj-postgres
    judge-server、oj-backend
  • image: 從哪裡抓 image
  • volumes: 映射資料夾進去容器
  • environment: 傳送變數
  • ports: 映射通訊阜口
# 啟動服務
docker-compose up

# 在背景執行
docker-compose up -d

# 停止服務
docker-compose down

上機測驗 2

使用 docker-compose 部署社網

要求:

  1. 使用 docker-compose + nginx
  2. 目標:完成一個資料夾,裡面放三個東西
    1. data 資料夾裡面放社網原始碼
    2. nginx.conf 放 nginx 配置
    3. docker-compose.yml:
      1. 抓取 nginx:alpine image
      2. 映射 80 和 443 port
      3. 映射 data 到容器內
      4. 映射 nginx.conf 到 conf.d 資料夾中
  3. 不准看簡報答案

提示關鍵字:
docker nginx static
docker compose nginx static

其他沒提到的東東

如何 google ?

  • 要用到好的關鍵字:
    • 系統、應用程式、版本、錯誤訊息
  • 要會過濾資料來源
    • 多看幾個做法,確定他們在幹嘛,再操作
  • 認真抓到錯誤訊息關鍵字

這個錯誤如何 google ?

如何在 stackoverflow 問問題

用壞伺服器的道歉信 by AaW

雪溱老師您好:
我是建中電研的學術長吳亞倫,也是先前負責管理網頁伺服器的同學。

首先,我要對不慎刪除伺服器當中設定檔一事,向老師道歉。
當初7/30日時,當時虛擬機器被重啟之後,網站docker的啟動出了問題。但是,當我連線到伺服器時卻發覺伺服器的port被別的東西佔用了,導致我無法按照說明文件的方式啟用網站。而當時學長也不清楚伺服器的現況,但因為我太心急要處理暑訓的東西,因此便自己試著要嘗試解決,因此嘗試了一些網路上的做法,也因此可能不慎動到了不該動到的地方。最後我是將systemctl的nginx停掉之後才得以正常啟動網站。後來網站也一直以此方式運作到九月多。
對於此自行修理一事我感到非常的抱歉。我應該要在交接伺服器使用權之後便主動向老師您聯絡,並且了解伺服器的狀態。但是我卻反而單獨聽信網路上的說明文件與指令胡亂嘗試,才動到不該動的地方,造成了老師以及北資同學的麻煩。

其實,在老師將伺服器關機的這一段時間內,我一直對於此事感到十分的自責,同時,我也花了許多時間去研究伺服器相關的知識以及相同問題的處理方式,希望能夠挽救回伺服器,也為我闖的禍負責。

因此,我希望老師可以答應我的請求,讓我一同參與接下來伺服器修理或是轉移到新伺服器的工作,讓我有一次彌補過錯的機會,並且從老師的教導當中學習更多。我保證我皆會在經過老師允許之後才對伺服器進行修理及調整。
另外,為了避免再犯相同錯誤,我會詳細的紀錄下此修理過程,並且在明年詳細的交接給學弟妹,避免他們和我犯下一樣的錯誤。
此外,我也懇求老師能夠將原本伺服器暫時開機,讓我將原本的題目等資料備份下來,使得學弟妹能夠暫時以其他方式進行練習。

我知道我的錯誤使得老師十分生氣,實在不應該再提出這種請求。但是我非常希望能夠彌補我的錯誤,因此誠心希望老師能夠同意讓我協助北資的同學一起進行之後的修理工作。希望老師願意原諒我的錯誤。

以下是我的聯絡資訊:
email: xxxxxxxxxx
line ID: xxxxxxxxxxxx
希望能夠收到老師您的回覆

Sincerely, 
吳亞倫 敬上

難以想像這是在GPT出現之前用手寫的

Made with Slides.com