在 Docker 環境中,設定 DNS 和郵件服務是常見的需求。本文將介紹如何使用 go-dnsmasq 建立自定義 DNS 伺服器,並透過 ssmtp 設定郵件傳送功能。同時,我們也會探討如何使用 Supervisord 和 PM2 管理容器內的應用程式行程,以及如何利用 Docker Swarm 建立叢集、佈署服務並進行擴充套件,最後搭配 HAProxy 進行負載平衡,確保服務的高用性。這些技術的整合,能有效提升 Docker 環境的管理效率和應用程式的穩定性。
自定義DNS與郵件傳送在Docker中的應用
在Docker容器中,有時需要強制使用自定義的內部DNS伺服器。Docker提供了--dns選項來指定容器使用的DNS伺服器。例如:
docker run -it --rm=true --dns 8.8.8.8 alpine:3.5 ping www.google.com
這將使容器使用Google的公共DNS伺服器(8.8.8.8)進行網域名稱解析。
使用go-dnsmasq進行開發環境DNS管理
專案中使用了janeczku/go-dnsmasq來執行開發環境下的DNS伺服器。這使得開發者能夠覆寫公共DNS條目,將特定的網域名稱解析到開發伺服器。
#!/bin/bash
NAME="dnsmasq"
DOCKERFILE="janeczku/go-dnsmasq:latest"
PARENT=$(dirname $(dirname $(realpath -s $0)))
docker rm -f $NAME
docker run --restart=always -d -h $NAME --name $NAME -p 53:53/udp -p 53:53 -v $PARENT/conf:/conf $DOCKERFILE -f /conf/hosts --verbose -n 8.8.8.8,8.8.4.4
內容解密:
docker run --restart=always -d -h $NAME --name $NAME: 啟動一個名為dnsmasq的容器,並設定為總是重啟。-p 53:53/udp -p 53:53: 將容器的53埠(UDP和TCP)對映到主機的53埠,使得外部能夠存取DNS服務。-v $PARENT/conf:/conf: 將主機上的組態目錄掛載到容器的/conf目錄,用於提供DNS解析規則。$DOCKERFILE -f /conf/hosts --verbose -n 8.8.8.8,8.8.4.4: 指定使用/conf/hosts檔案進行DNS解析,並設定上游DNS伺服器為Google的公共DNS。
自定義DNS條目與泛網域名稱解析
go-dnsmasq支援使用hosts檔案管理DNS條目,並且可以設定泛網域名稱解析規則。
10.1.1.2 *.scene-si.org scene-si.org
內容解密:
10.1.1.2 *.scene-si.org scene-si.org: 將scene-si.org及其所有子網域名稱解析到10.1.1.2。
從Docker容器傳送郵件
在Docker容器中傳送郵件有多種方式。最簡單的方法是利用主機上的郵件服務,或是在容器中安裝ssmtp。
使用ssmtp組態郵件傳送
- 安裝
ssmtp:
apk --update add ssmtp
內容解密:
apk --update add ssmtp: 在Alpine Linux容器中安裝ssmtp套件,用於傳送郵件。
- 組態
ssmtp:
cat << EOF > /etc/ssmtp/ssmtp.conf
mailhub=$SSMTP_SERVER:$SSMTP_PORT
AuthUser=$SSMTP_USER
AuthPass=$SSMTP_PASS
UseSTARTTLS=$SSMTP_TLS
hostname=$SSMTP_HOSTNAME
FromLineOverride=YES
EOF
內容解密:
mailhub=$SSMTP_SERVER:$SSMTP_PORT: 設定SMTP伺服器地址和埠。AuthUser=$SSMTP_USER和AuthPass=$SSMTP_PASS: 設定SMTP驗證的使用者名稱和密碼。UseSTARTTLS=$SSMTP_TLS: 設定是否使用STARTTLS加密連線。hostname=$SSMTP_HOSTNAME: 設定傳送郵件的主機名。FromLineOverride=YES: 允許覆寫發件人地址。
Docker中的程式管理與重啟策略
Docker提供了--restart選項來控制容器的重啟策略,確保應用在意外離開或主機重啟後能夠自動還原。
重啟策略選項:
no: 不重啟容器(預設值)。on-failure: 僅在容器以非零離開狀態終止時重啟。unless-stopped: 除非手動停止容器,否則總是在Docker守護程式重啟時重啟容器。always: 總是重啟容器,無論其離開狀態如何。
進階行程管理與擴充套件
在前面的章節中,我們討論了 Docker 的重啟策略以及如何利用它來確保服務的可用性。在本章節中,我們將探討更進階的行程管理工具,例如 Supervisord 和 PM2,並瞭解如何利用它們來管理和擴充套件我們的應用程式。
Supervisord:進階行程管理
Supervisord 是一個流行的行程管理工具,可以用來管理和監控多個行程。它提供了 Web 介面來重啟個別服務,並且有一些嘗試在它上面提供叢集層,以檢視和管理佈署中的行程。
設定 Supervisord
要使用 Supervisord 管理行程,需要建立一個設定區塊,定義要執行的應用程式。例如:
[program:nginx]
command=/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
directory = /etc/nginx
autostart=true
autorestart=true
priority=10
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
這個設定區塊定義了一個名為 nginx 的程式,並指定了要執行的命令、工作目錄、自動啟動和重啟等選項。
為何使用 Supervisord?
雖然 Docker 的重啟策略可以滿足基本需求,但 Supervisord 提供了更多的功能和彈性。特別是在需要將多個應用程式封裝到一個 Docker 映像檔中時,Supervisord 可以提供更好的管理。
然而,將多個應用程式封裝到一個映像檔中可能會違反單一責任原則,因此不建議這樣做。更好的做法是將每個服務個別封裝到自己的 Docker 映像檔中。
PM2:Node.js 的進階行程管理
PM2 是一個專為 Node.js 設計的行程管理器,由於 Node.js 是單執行緒的應用程式,因此 PM2 可以幫助擴充套件應用程式以利用多個 CPU 核心。
PM2 的優點
PM2 提供了一些 Docker Swarm 所沒有的功能,例如零停機時間的重啟(滾動更新)。此外,PM2 還可以擴充套件服務到多個 CPU 核心上。
使用 PM2 執行 Go 應用程式
雖然 PM2 主要用於 Node.js,但也可以用來執行 Go 應用程式。我們可以使用 keymetrics/pm2-docker-alpine 這個 Docker Hub 映像檔來執行我們的 Go 應用程式。
首先,需要建立一個 process.yml 檔案來定義 Go 應用程式的設定和執行方式。
# process.yml 設定範例
apps:
- script: ./main.go
instances: 4
exec_mode: cluster
建構和執行 Go 應用程式
要建構和執行 Go 應用程式,需要使用 golang:1.8-alpine 這個映像檔來建構應用程式,以確保所需的函式庫在執行時可用。
# 使用多階段建構來建構 Go 應用程式
FROM golang:1.8-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o main main.go
FROM keymetrics/pm2-docker-alpine
WORKDIR /app
COPY --from=builder /app/main .
COPY process.yml .
CMD ["pm2-runtime", "process.yml"]
VIII. Concurrency - Scale out via the process model
使用 PM2 組態檔案執行 Go 應用程式
apps:
- script: ./gotwitter
name: 'gotwitter'
interpreter: none
execute-command: true
instances: 2
exec_mode: fork
watch: false
out_file: /dev/stdout
error_file: /dev/stderr
內容解密:
exec_mode設定為fork,表示 PM2 將使用exec系統呼叫來執行行程。instances指定要執行的行程數量。- 當處理 Node 應用程式時,
exec_mode可以設定為cluster,以啟用 Node 應用程式的擴充套件功能,但 PM2 不支援在fork模式下動態調整行程數量。
使用 PM2 執行 Go 應用程式
當我們使用 PM2 啟動 Go 應用程式時,可以透過 PM2 命令列進行一些操作。我們建立了一個包裝指令碼 pm2_exec 用於在 Docker 中執行 PM2。例如,可以使用 ./pm2_exec status 列出應用程式的狀態。
# ./pm2_exec status
┌───────────┬────┬──────┬─────┬────────┬─────────┬────────┬─────┬──────────┬────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ watching │
├───────────┼────┼──────┼─────┼────────┼─────────┼────────┼─────┼──────────┼────┤
│ gotwitter │ 0 │ fork │ 20 │ online │ 0 │ 91s │ 0% │ 4.0 MB │ enabled │
│ gotwitter │ 1 │ fork │ 24 │ online │ 0 │ 91s │ 0% │ 4.1 MB │ enabled │
└───────────┴────┴──────┴─────┴────────┴─────────┴────────┴─────┴──────────┴────┘
內容解密:
- 每個應用程式例項讀取
NODE_APP_INSTANCE環境變數(id 列),並在預設埠號上加上此值來啟動應用程式。 - 由於是在
fork模式下執行,因此沒有提供負載平衡器給應用程式,且不支援擴充套件功能。
使用 HAProxy 進行負載平衡
由於 PM2 在 fork 模式下不支援動態擴充套件,我們可以使用 HAProxy 例項連線到個別的代理埠。
global
maxconn 10000
frontend public
bind *:80
mode http
timeout client 30s
default_backend gotwitter
backend gotwitter
balance roundrobin
mode http
timeout server 5s
timeout connect 5s
server app1 pm2-gotwitter:3000 check
server app2 pm2-gotwitter:3001 check
內容解密:
- 設定 HAProxy 的前端和後端組態,將流量分配到不同的應用程式例項。
Docker Swarm 的使用
Docker Swarm 是 Docker 的內建協調工具,可以輕鬆擴充套件服務到多個例項。
root@swarm1:~$ docker swarm init
Swarm initialized: current node (4i0lko1qdwqp4x1aqwn6o7obh) is now a manager.
內容解密:
- 初始化 Docker Swarm,並將當前節點設定為管理員。
- 可以新增工作節點或管理員節點以實作高用性。
Docker Swarm 叢集管理與服務擴充套件實務
Docker Swarm 為容器協調工具,能夠有效地管理多個 Docker 主機,並提供高用性與負載平衡功能。本文將探討 Docker Swarm 的基本操作、服務建立與擴充套件,以及容錯移轉機制。
建立 Docker Swarm 叢集
首先,我們需要建立一個 Docker Swarm 叢集。假設我們有三台主機:swarm1、swarm2 和 swarm3。我們將 swarm1 設定為初始的管理節點。
root@swarm1:~# docker swarm init --advertise-addr 10.55.0.248
接著,我們在 swarm2 上執行以下指令,將其加入叢整合為管理節點:
root@swarm2:~# docker swarm join \
> --token SWMTKN-1-0445f42yyhu8z4k3lgxsbnusnd8ws83urf56t02rv1vdh1zqlj-09swsjxdz8\
0bfxbc1aed6mack \
> 10.55.0.248:2377
This node joined a swarm as a manager.
內容解密:
docker swarm join:用於將節點加入現有的 Docker Swarm 叢集。--token:指定加入叢集所需的驗證令牌。10.55.0.248:2377:管理節點的位址和埠號。
檢視叢集中的節點狀態:
root@swarm2:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
4i0lko1qdwqp4x1aqwn6o7obh swarm1 Ready Active Leader
9gyk5t22ngndbwtjof80hpg54 * swarm2 Ready Active Reachable
內容解密:
docker node ls:列出叢集中的所有節點。STATUS:顯示節點的目前狀態(Ready 表示正常)。MANAGER STATUS:顯示節點在叢集中的角色(Leader 或 Reachable)。
建立與擴充套件服務
在 swarm2 上建立一個名為 helloworld 的服務,該服務會執行 alpine ping google.com,並啟動 5 個副本:
root@swarm2:~# docker service create --replicas 5 --name helloworld alpine ping google.com
31zloagja1dlkt4kaicvgeahn
內容解密:
docker service create:建立一個新的服務。--replicas 5:指定服務的副本數量為 5。--name helloworld:為服務命名為 helloworld。alpine ping google.com:指定容器執行的命令。
檢視服務的狀態:
root@swarm2:~# docker service ls
ID NAME REPLICAS IMAGE COMMAND
31zloagja1dl helloworld 5/5 alpine ping google.com
內容解密:
docker service ls:列出所有服務及其目前狀態。REPLICAS:顯示服務的副本數量(5/5 表示 5 個副本皆正常執行)。
檢視服務的詳細資訊,包括每個副本執行的節點:
root@swarm1:~# docker service ps helloworld
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
5fxtllouvmd helloworld.1 alpine swarm1 Running Running 7 min
cqvgixx3djh helloworld.2 alpine swarm2 Running Running 7 min
99425nw3r4r helloworld.3 alpine swarm2 Running Running 7 min
1dj3cs7v5ij helloworld.4 alpine swarm1 Running Running 7 min
0hy3yzwqzln helloworld.5 alpine swarm1 Running Running 7 min
內容解密:
docker service ps helloworld:顯示 helloworld 服務的詳細資訊,包括每個副本的狀態和執行節點。NODE:顯示每個副本執行的節點名稱。
容錯移轉測試
當我們關閉 swarm1(目前的 Leader 節點)後,預期 swarm2 會被提升為新的 Leader,並且服務會繼續執行。然而,實際情況卻並不如預期。
root@swarm1:~# poweroff
在 swarm1 關閉後,檢視叢集狀態:
root@swarm2:~# docker node ls
Error response from daemon: rpc error: code = 2 desc = raft: no elected cluster leader
內容解密:
- 由於只有兩個管理節點,當其中一個關閉後,叢集無法達成共識,因此出現錯誤。
在這種情況下,我們需要增加第三個管理節點(swarm3)以提高容錯能力。
增加額外管理節點
取得加入叢集的 token:
root@swarm2:~# docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-0445f42yyhu8z4k3lgxsbnusnd8ws83urf56t02rv1vdh1zqlj-09swsjxd\
z80bfxbc1aed6mack \
10.55.0.238:2377
在 swarm3 上執行上述指令,將其加入叢整合為新的管理節點:
root@swarm3:~# docker swarm join \
> --token SWMTKN-1-0445f42yyhu8z4k3lgxsbnusnd8ws83urf56t02rv1vdh1zqlj-09swsjxdz8\
0bfxbc1aed6mack \
> 10.55.0.238:2377
This node joined a swarm as a manager.
內容解密:
此步驟增強了叢集的容錯能力,確保在單一管理節點故障時仍能正常運作。