在分散式系統中,有效管理 Go 應用程式的平行處理和佈署至關重要。本文將介紹如何使用 PM2 管理 Go 應用程式,並探討 Docker Swarm 的叢集管理和服務擴充套件機制,以確保應用程式在高負載環境下的穩定性和可用性。透過 PM2,可以簡化 Go 應用程式的佈署和監控,實作零停機時間重啟和負載平衡等功能。同時,Docker Swarm 提供了容器協調能力,方便建立和管理 Docker 叢集,並實作服務的自動擴充套件和容錯。結合 PM2 和 Docker Swarm,可以有效提升 Go 應用程式在分散式環境下的可靠性和擴充套件性,滿足現代應用程式對高用性和高效能的需求。
VIII. 平行性 - 透過處理程式模型擴充套件
雖然Go程式由於其強大的平行模型和能夠將處理擴充套件到多個CPU核心,不直接適用於此選項,但值得探討此選項作為透過處理程式模型提供冗餘的一種方式,並且在佈署新應用程式版本時提供優雅的升級路徑。透過使用Docker,我們也可以滿足12FA中提出的基本處理程式管理需求 - 回應當機的程式並處理使用者啟動的重新啟動和關閉。
基本處理程式管理
在確保應用程式保持活動狀態方面,Docker具有內建的--restart開關,可以控制程式意外離開或VPS或伺服器意外重新啟動後的生命週期。
執行應用程式時,您有多種重新啟動策略可供選擇:
no- 當應用程式離開時不重新啟動容器(預設值)on-failure- 僅當應用程式以非零離開狀態離開時重新啟動容器on-failure[:max-retries]- 可選設定應用程式重新啟動的最大次數unless-stopped- 如果您手動停止容器,則在Docker啟動時不會重新啟動always- 當應用程式離開或Docker啟動時(例如在重新啟動後)重新啟動應用程式
顯然,對於任何應用程式,預設值都可以是always,除非您有非常好的理由保持應用程式不可用。玄貓主張使用docker stop && docker rm,這將完全刪除容器,因此即使採用激進的重新啟動策略,也不會有副作用 - 例如在Docker主機重新啟動後啟動未受保護的管理容器。
在特殊情況下,重新啟動策略可以保護您免受例項的意外當機。通常,人們會從在主機上透過init.d(系統V init工具)執行Redis等服務開始。這確保了Redis在主機重新啟動後會啟動,但不能確保Redis在意外當機的情況下會自動重新啟動。玄貓曾經遇到過Redis因OOM(記憶體不足)錯誤而當機的情況,而當時仍有大量的RAM可用。雖然在GitHub上提交了問題,但最終玄貓將Redis移到了Docker中,可以立即利用此重新啟動策略。
測試重新啟動策略
我們甚至可以相當有效地使用Redis測試我們的重新啟動策略。Redis提供了一個命令DEBUG SEGFAULT,將模擬當機,我們可以看到Docker如何回應它。我們的Redis服務(參見第V章)已經以--restart=always啟動,因此我們只需要發出命令使其當機。
$ ./redis-cli debug segfault
Error: Server closed the connection
在一個終端中,您可以使用docker events檢視Docker事件。在另一個終端中,您可以執行services/redis中提供的redis-cli輔助工具,如下所示:
root@verdana:~# docker events
2017-02-06T20:11:30+01:00 container exec_create: redis-cli debug segfault 557...81a (image=redis:3.2-alpine, name=redis)
2017-02-06T20:11:30+01:00 container exec_start: redis-cli debug segfault 557...81a (image=redis:3.2-alpine, name=redis)
2017-02-06T20:11:30+01:00 container die 557...81a (exitCode=139, image=redis:3.2-alpine, name=redis)
2017-02-06T20:11:30+01:00 container start 557...81a (image=redis:3.2-alpine, name=redis)
為了簡潔起見,玄貓縮短了輸出。如您所見 - Docker在Redis容器內執行redis-cli,並且在記錄die事件後立即重新啟動容器。耗時不到一秒。當我們每幾天遇到一次當機時,這將是一個足夠好的臨時解決方案。
可用性計算
考慮到如果服務當機大約需要2秒,並且每3天發生一次,那麼系統的可用性將為99.993%。假設我們有3個例項,並且它們不會同時當機,那麼可用性將略高於99.998%。為了留存記錄,報告的OOM當機問題可在GitHub上找到。
高階處理程式管理
Supervisord
還有外部軟體可用於提供與Docker --restart策略類別似的功能。一個流行的用於管理程式的軟體稱為Supervisord。在與Docker相關的場景中,玄貓大多見到它被用來將多個應用程式組合到一個Docker映像中,最值得注意的是Nginx網路伺服器和PHP指令碼語言(php-fpm)。這違背了單一責任原則,因此違背了良好的實踐(每個容器一個應用程式)。
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
內容解密:
[program:nginx]定義了一個名為nginx的程式組態區塊。command=/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf指定了執行Nginx的命令,其中-g 'daemon off;'確保Nginx以前台模式執行,而-c指定了組態檔案的路徑。directory = /etc/nginx設定了執行命令的工作目錄。autostart=true和autorestart=true分別確保了Nginx在Supervisord啟動時自動啟動,並且在Nginx離開時自動重新啟動,這與Docker的--restart=always類別似。stdout_events_enabled=true和stderr_events_enabled=true允許Supervisord捕捉Nginx的標準輸出和錯誤輸出的事件。stdout_logfile=/dev/stdout和stderr_logfile=/dev/stderr將Nginx的輸出重定向到Supervisord的標準輸出和錯誤輸出,這樣可以在Docker日誌中看到Nginx的日誌。
諸如 autostart 和 autorestart 之類別的選項相當於Docker中的 --restart=always。 directory 類別似於Docker的 -w 開關,還有很多其他可用的選項已在Supervisord檔案中記錄。
玄貓認為,將Nginx和PHP結合在一起是可以的,主要是因為玄貓尚未見到為每個服務使用專用伺服器的Nginx/php佈署。這裡還有一定的效能優勢,因為Nginx可以使用Unix通訊端與PHP通訊。在Go(或NodeJS)中,很容易建立一個HTTP伺服器,因此不太需要緊密耦合一個網路伺服器,因為應用程式已經同時執行這兩項功能。經驗法則是將服務個別封裝到Docker映像中 - 這樣,在某些時候,您可以從Apache遷移到Lighttpd,然後再遷移到Nginx - 所有這些都可以獨立完成。
使用 PM2 進行 Go 應用程式的平行處理與擴充套件
在現代軟體開發中,特別是在雲端運算和微服務架構中,如何有效地管理和擴充套件應用程式的平行處理能力,是一個重要的議題。對於使用 Go 語言開發的應用程式來說,雖然 Go 語言本身提供了強大的平行處理能力(透過 goroutines),但在某些情況下,使用程式管理工具來管理和擴充套件應用程式仍然是有用的。本文將探討使用 PM2 這個程式管理工具來執行和管理 Go 應用程式。
為什麼選擇 PM2?
PM2 是一個流行的 Node.js 程式管理工具,它提供了諸如零停機時間重啟、負載平衡等功能。雖然它主要是為 Node.js 應用程式設計的,但它也可以用來執行其他語言的應用程式,包括 Go。
PM2 的主要功能
- 零停機時間重啟:允許在不中斷服務的情況下重啟應用程式。
- 負載平衡:可以將請求分配到多個例項,提高應用程式的處理能力。
組態 PM2 執行 Go 應用程式
要使用 PM2 執行 Go 應用程式,需要建立一個 process.yml 組態檔案。這個檔案定義瞭如何執行 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
組態解說
script: 指定要執行的可執行檔案。name: 為應用程式例項指定名稱。interpreter: 設定為none表示直接執行可執行檔案,不使用直譯器。instances: 指定要執行的例項數量。exec_mode: 設定為fork表示使用exec系統呼叫建立新程式。
執行和管理 Go 應用程式
使用 PM2 執行 Go 應用程式後,可以透過 PM2 的命令列工具來管理和監控應用程式。
檢視應用程式狀態
./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 環境變數,並根據該變數的值決定監聽的埠。
負載平衡和反向代理
由於 PM2 在 fork 模式下不提供內建的負載平衡功能,因此需要使用額外的工具(如 HAProxy 或 Nginx)來實作負載平衡。
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
Docker Swarm 叢集管理與服務擴充套件
Docker Swarm 是 Docker 官方提供的容器協調工具,允許使用者將多個 Docker 主機組成一個虛擬的 Docker 主機,實作容器的叢集管理和服務擴充套件。隨著 Docker 1.12 版本的發布,Swarm 功能已經內建於 Docker 中,使得建立和管理叢集變得更加容易。
初始化 Swarm 叢集
建立 Swarm 叢集的第一步是初始化叢集。假設我們從一個乾淨的 Docker 1.12.0 安裝開始,可以使用以下命令初始化 Swarm:
docker swarm init
此命令將當前節點設定為管理員節點,並提供加入叢集所需的 Token。
內容解密:
docker swarm init初始化 Swarm 叢集,將當前節點設定為管理員。- 初始化完成後,會顯示加入叢集所需的命令,包括工作節點和新增管理員的 Token。
加入節點到 Swarm 叢集
要建立一個高用性的叢集,我們需要新增多個管理員節點。可以使用以下命令將新節點加入叢集:
docker swarm join --token <TOKEN> <MANAGER-IP>:2377
內容解密:
docker swarm join命令用於將新節點加入現有的 Swarm 叢集。--token引數指定加入叢集所需的驗證 Token。<MANAGER-IP>:2377指定管理員節點的 IP 地址和埠號。
建立服務
在 Swarm 叢集中建立服務,可以使用 docker service create 命令。例如,建立一個名為 helloworld 的服務,使用 alpine 映象,並執行 ping google.com 命令:
docker service create --replicas 5 --name helloworld alpine ping google.com
內容解密:
--replicas 5指定服務的副本數量為 5。--name helloworld為服務命名。alpine ping google.com指定服務執行的命令。
檢視服務狀態
可以使用 docker service ls 和 docker service ps 命令檢視服務的狀態和詳細資訊。
docker service ls
docker service ps helloworld
內容解密:
docker service ls列出所有服務及其狀態。docker service ps <SERVICE-NAME>檢視指定服務的詳細執行狀態。
測試容錯能力
當 Swarm 叢集中的某個管理員節點失效時,其他管理員節點應該能夠接管其工作。為了測試容錯能力,我們可以關閉其中一個管理員節點,並觀察服務的狀態。
內容解密:
- 當管理員節點失效時,Swarm 會嘗試在其他可用節點上重新啟動服務。
- 如果只有兩個管理員節點,當其中一個失效時,叢集可能無法正常運作,因為無法達成共識(Quorum)。
新增管理員節點以提高容錯能力
為了提高容錯能力,可以新增更多管理員節點到 Swarm 叢集中。根據 RAFT 共識演算法的要求,為了容忍 $N$ 個節點的故障,需要至少 $2N+1$ 個管理員節點。因此,對於需要容忍一個節點故障的情況,至少需要三個管理員節點。
docker swarm join-token manager
此命令提供將新節點加入為管理員所需的 Token 和命令。
內容解密:
docker swarm join-token manager取得將新節點加入為管理員的 Token 和命令。- 新增管理員節點可以提高 Swarm 叢集的容錯能力和高用性。
綜上所述,Docker Swarm 提供了一個強大的容器協調工具,能夠實作服務的擴充套件、管理和容錯。透過合理的組態和管理,可以確保應用服務的高用性和可靠性。