在分散式系統中,有效管理 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

內容解密:

  1. [program:nginx] 定義了一個名為 nginx 的程式組態區塊。
  2. command=/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf 指定了執行Nginx的命令,其中 -g 'daemon off;' 確保Nginx以前台模式執行,而 -c 指定了組態檔案的路徑。
  3. directory = /etc/nginx 設定了執行命令的工作目錄。
  4. autostart=trueautorestart=true 分別確保了Nginx在Supervisord啟動時自動啟動,並且在Nginx離開時自動重新啟動,這與Docker的 --restart=always 類別似。
  5. stdout_events_enabled=truestderr_events_enabled=true 允許Supervisord捕捉Nginx的標準輸出和錯誤輸出的事件。
  6. stdout_logfile=/dev/stdoutstderr_logfile=/dev/stderr 將Nginx的輸出重定向到Supervisord的標準輸出和錯誤輸出,這樣可以在Docker日誌中看到Nginx的日誌。

諸如 autostartautorestart 之類別的選項相當於Docker中的 --restart=alwaysdirectory 類別似於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 lsdocker 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 提供了一個強大的容器協調工具,能夠實作服務的擴充套件、管理和容錯。透過合理的組態和管理,可以確保應用服務的高用性和可靠性。