在分散式系統中,無狀態應用程式設計至關重要,它讓服務能夠水平擴充套件並提升可靠性。本文以Sonyflake ID生成器為例,展示如何在Docker Swarm叢集中佈署無狀態服務,並探討Docker網路模式的應用,涵蓋bridge、overlay等模式,以及連線埠繫結和DNS解析等關鍵技術。同時,文章也提供實用的網路除錯技巧,例如使用netstat、strace等工具,並推薦Grafana、Netdata、Prometheus等監控工具,幫助開發者快速診斷和解決網路問題,確保服務的穩定執行。
無狀態應用程式的設計與實作
在現代的雲端運算和容器化技術中,無狀態應用程式(Stateless Application)是一種重要的設計模式。這種模式允許應用程式在不依賴本地狀態的情況下執行,從而提高可擴充套件性、可靠性和容錯能力。本文將探討無狀態應用程式的設計原則,並以Sonyflake為例,展示如何在Docker Swarm環境中實作無狀態服務。
無狀態應用程式的優勢
無狀態應用程式的主要優勢在於其可擴充套件性和可靠性。由於應用程式不依賴本地狀態,因此可以輕易地在多個容器或節點之間進行擴充套件和負載平衡。這使得應用程式能夠更好地應對高流量和大規模的使用者需求。
Sonyflake:一個無狀態ID生成器的例子
Sonyflake是一個由Sony開發的64位元ID生成器,類別似於Twitter的Snowflake。它透過結合當前時間戳、序列號和機器ID來生成唯一的ID。由於Sonyflake是無狀態的,因此可以輕易地在Docker Swarm環境中進行擴充套件和佈署。
Sonyflake的工作原理
Sonyflake使用以下元件來生成ID:
- 39位元的時間戳(以10毫秒為單位)
- 8位元的序列號
- 16位元的機器ID
機器ID是根據容器的網路介面生成的,這確保了在同一網路中的容器具有唯一的機器ID。
在Docker Swarm中佈署Sonyflake
要在Docker Swarm中佈署Sonyflake,可以使用以下命令:
# docker service create \
--replicas 5 \
--network party-swarm \
--update-parallelism 5 \
--name sonyflake \
-p 80:80 titpetric/sonyflake
這個命令建立了一個名為sonyflake的服務,具有5個副本,並將其釋出到埠80。
#### 內容解密:
此命令用於在Docker Swarm中建立一個新的服務。--replicas 5 指定了服務的副本數量為5,--network party-swarm 指定了服務所使用的網路,--update-parallelism 5 指定了更新服務時的平行度,--name sonyflake 指定了服務的名稱,-p 80:80 將主機的80埠對映到容器的80埠,titpetric/sonyflake 是Sonyflake服務的Docker映像。
測試Sonyflake服務
可以使用curl命令來測試Sonyflake服務:
# curl -s 127.0.0.1
{"id":134164003092955146,"machine-id":10,"msb":0,"sequence":0,"time":7996797746}
#### 內容解密:
此命令用於測試Sonyflake服務是否正常執行。curl -s 127.0.0.1 向本地主機的80埠傳送一個HTTP請求,並傳回Sonyflake生成的ID。
效能測試
可以使用wrk工具來測試Sonyflake服務的效能:
# docker run --net=party-swarm --rm williamyeh/wrk -t 6 -c 30 http://sonyflake
#### 內容解密:
此命令用於測試Sonyflake服務的效能。docker run --net=party-swarm --rm williamyeh/wrk 在party-swarm網路中執行一個臨時的wrk容器,-t 6 -c 30 http://sonyflake 指定了測試的執行緒數、連線數和目標URL。
負載平衡
在預設的VIP(Virtual-IP)模式下,Docker Swarm會將請求均勻地分配到不同的容器中。但是,當使用keep-alive連線時,負載可能會變得不均勻。在這種情況下,可以使用DNS輪詢(DNS Round-Robin)來實作更智慧的負載平衡。
# docker service create \
--replicas 5 \
--network party-swarm \
--endpoint-mode dnsrr \
--name sonyflake \
-p 80:80 titpetric/sonyflake
#### 內容解密:
此命令用於建立一個使用DNS輪詢模式的Sonyflake服務。--endpoint-mode dnsrr 指定了服務的端點模式為DNS輪詢。
Docker 網路模式與 Port Binding 實務解析
Docker 的網路功能提供了多種模式,讓容器能夠以不同的方式與外界通訊。這些模式包括 none、bridge、host 和 overlay 等網路驅動程式,能夠滿足各種不同的應用場景需求。
None 網路模式:完全隔離的容器網路
在 none 網路模式下,容器不會被分配任何網路介面,這意味著容器內的應用程式無法存取網路,也無法對外提供任何服務。這種模式適合用於執行不受信任的程式碼,或者是某些不需要網路存取的 CLI 應用程式,例如影片處理任務。
程式碼範例:驗證 none 網路模式
docker run --rm -it --network=none alpine:3.5 ip addr
內容解密:
docker run:啟動一個新的容器。--rm:容器停止後自動刪除。-it:互動模式,允許使用者與容器內的 shell 互動。--network=none:指定容器使用 none 網路模式。alpine:3.5:使用的 Docker 映像檔。ip addr:顯示容器的網路介面資訊。
輸出結果顯示,只有 lo(loopback)介面可用,證明容器沒有外部網路存取能力。
Bridge 網路模式:預設的單機容器網路
bridge 網路模式是 Docker 預設的網路模式,適合單一主機上的容器互通。在此模式下,容器可以透過 IP 位址互相連線,但預設無法透過 DNS 解析彼此的主機名稱。
程式碼範例:驗證 bridge 網路模式下的容器互連
# 啟動兩個容器
docker run -itd --name sh1 alpine:3.5 sh
docker run -itd --name sh2 alpine:3.5 sh
# 檢視容器 IP 位址
docker exec -it sh1 ip addr | grep global
docker exec -it sh2 ip addr | grep global
# 使用 IP 位址進行 ping 測試
docker exec -it sh2 ping 172.17.0.3
內容解密:
docker run -itd:以 detached 模式啟動互動式容器。--name:為容器指定名稱。docker exec:在執行中的容器內執行命令。ip addr | grep global:過濾出容器的全球 IP 位址。ping:測試容器間的網路連通性。
自訂 Bridge 網路:增強的容器 DNS 解析
自訂 bridge 網路允許容器透過主機名稱互相存取,這是預設 bridge 網路所不具備的功能。
程式碼範例:建立自訂 bridge 網路並測試 DNS 解析
# 建立自訂 bridge 網路
docker network create -d bridge --subnet 172.25.0.0/24 party
# 在自訂網路上啟動容器
docker run -itd --name sh1 --network party alpine:3.5 sh
docker run -itd --name sh2 --network party alpine:3.5 sh
# 測試 DNS 解析
docker exec -it sh2 ping sh1
內容解密:
docker network create:建立新的 Docker 網路。-d bridge:指定網路驅動程式為 bridge。--subnet:定義網路的子網路範圍。--network party:將容器連線到自訂的 party 網路。
Overlay 網路模式:跨主機容器通訊
overlay 網路驅動程式讓 Docker Swarm 中的容器能夠跨多個主機進行通訊。建立 overlay 網路時,需要在 manager 節點上執行,並可透過 --attachable 引數允許非 Swarm 服務的容器連線。
程式碼範例:建立 overlay 網路
docker network create \
--driver overlay \
--subnet 10.0.0.0/20 \
--attachable \
party-swarm
內容解密:
--driver overlay:指定使用 overlay 網路驅動程式。--subnet 10.0.0.0/20:定義一個較大的 IP 範圍(4096 個 IP 位址)。--attachable:允許非 Swarm 服務的容器連線到該網路。
VII. 連線埠繫結 - 透過連線埠繫結匯出服務
在 Docker Swarm 中,當我們建立服務並將其連線到自定義的網路時,容器之間可以互相通訊,而無需將服務連線埠暴露給外部網路。讓我們探討 Docker 的連線埠繫結機制,以及如何安全地將服務暴露給外部存取。
Docker 網路架構
首先,我們在兩個不同的 Swarm 節點(swarm2 和 swarm3)上啟動了兩個 Alpine 容器,分別命名為 sh1 和 sh2,並將它們連線到名為 party-swarm 的網路。
# ssh swarm2 docker run -itd --name sh1 --network party-swarm alpine:3.5 sh
6367bce89b0e255214456f4063c29b21bd8e573f86dc1c15ee0d5c6ce7114b99
# ssh swarm3 docker run -itd --name sh2 --network party-swarm alpine:3.5 sh
d84267c6b277216d0a4363fc2a731d56af5b480b028a6816d68941eda13c3d41
內容解密:
- 我們使用
docker run命令在 swarm2 和 swarm3 上分別啟動了兩個容器 sh1 和 sh2。 --name引數為容器命名,而--network party-swarm將容器連線到party-swarm網路。alpine:3.5是使用的 Docker 映像檔版本,而sh是容器啟動後執行的命令。
接著,我們使用 nslookup 命令檢查了容器 sh1 和 sh2 的 DNS 解析結果,確認它們在 party-swarm 網路中能夠被正確解析。
# docker run -it --rm --network party-swarm alpine:3.5 nslookup sh1
Name: sh1
Address 1: 10.0.0.8 sh1.party-swarm
# docker run -it --rm --network party-swarm alpine:3.5 nslookup sh2
Name: sh2
Address 1: 10.0.0.9 sh2.party-swarm
內容解密:
- 我們啟動了臨時的 Alpine 容器,並將其連線到
party-swarm網路,以執行nslookup命令。 nslookup命令用於查詢容器 sh1 和 sh2 的 IP 位址,結果顯示它們的 IP 位址分別為 10.0.0.8 和 10.0.0.9。
此外,每個容器實際上是連線到兩個網路:party-swarm 網路和另一個系統網路 docker_gwbridge。這個額外的網路是 Docker Swarm 自動建立的,用於提供叢集外部的連線。
# ssh swarm2 docker exec sh1 ip addr | grep global
inet 10.0.0.8/20 scope global eth0
inet 172.18.0.5/16 scope global eth1
內容解密:
- 我們使用
docker exec命令進入容器 sh1,並執行ip addr命令檢視其網路介面組態。 - 結果顯示容器有兩個網路介面:eth0 連線到
party-swarm網路(IP 位址為 10.0.0.8),而 eth1 連線到docker_gwbridge網路(IP 位址為 172.18.0.5)。
主機網路模式
當我們以主機網路模式執行服務時,容器可以直接存取主機的網路堆積疊,這意味著容器可以控制主機網路介面的建立、修改和刪除。這種模式對於某些特定的應用程式(如 VPN 客戶端或網路監控工具)非常有用。
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 主機網路模式
rectangle "存取" as node1
rectangle "控制" as node2
node1 --> node2
@enduml
此圖示說明瞭容器在主機網路模式下與主機網路堆積疊的關係。
內容解密:
- 圖表展示了容器如何直接存取主機的網路堆積疊。
- 這種模式下,容器具備對主機網路介面的控制權。
然而,這種模式也帶來了安全風險,因為容器的網路隔離被關閉,任何在容器內監聽的連線埠都會被公開暴露。
暴露服務連線埠
建議的做法是將容器連線到自定義的橋接或覆寫網路,並根據需要使用 -p 選項暴露個別連線埠。
-p=[] : Publish a container's port or a range of ports to the host
格式如下:
ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
例如,若應用程式監聽在連線埠 3000,但我們想對外暴露連線埠 80,可以使用 -p 80:3000。
內容解密:
-p選項用於將容器的連線埠對映到主機的連線埠。- 可以指定單一連線埠或連線埠範圍進行對映。
- 這種方式使得應用程式可以靈活地暴露服務,而無需修改其預設監聽連線埠。
對於遵循 Twelve-Factor App(12FA)原則的應用程式,應該從環境變數中讀取監聽的連線埠,但可以提供一個合理的預設值。在生產環境中,通常需要使用反向代理伺服器來監聽標準的 HTTP 連線埠(80 或 443),並將流量轉發到後端服務。
總之,Docker 的連線埠繫結機制提供了靈活的方式來暴露服務,同時也需要注意安全性和遵循最佳實踐。
除錯 Docker 網路連線問題
在除錯網路連線問題時,通常會檢查幾個關鍵點。當遇到問題時,我會遵循特定的步驟來排查。
使用 netstat 檢查服務埠
在 Debian 系統中,可以使用 net-tools 套件中的 netstat 工具來檢查服務是否正在監聽特定的埠。例如,若要檢視 Docker 應用程式繫結的內部服務埠,可以執行以下命令:
docker exec -it apt-cacher netstat -pan
輸出範例:
Active Internet connections (servers and established)
Proto Local Address Foreign Address State PID/Program name
tcp 127.0.0.11:37391 0.0.0.0:* LISTEN -
tcp 0.0.0.0:3142 0.0.0.0:* LISTEN 7/apt-cacher-ng
tcp6 :::3142 :::* LISTEN 7/apt-cacher-ng
udp 127.0.0.11:32837 0.0.0.0:* -
內容解密:
docker exec -it apt-cacher netstat -pan:在名為apt-cacher的 Docker 容器中執行netstat -pan命令,以顯示所有監聽和已建立的連線。Proto:顯示協定型別(TCP 或 UDP)。Local Address:顯示本地地址和埠。Foreign Address:顯示遠端地址和埠。State:顯示連線的狀態,如LISTEN表示正在監聽。PID/Program name:顯示相關程式的 PID 和名稱。
若要僅檢視正在監聽的埠,可以附加 | grep LISTEN 到命令中。在上述範例中,apt-cacher-ng 正在 IPv4 和 IPv6 的 3142 埠上監聽。
Docker 內部 DNS 解析
Docker 的內部 DNS 服務執行在 127.0.0.11。可以透過以下命令確認:
docker exec -it apt-cacher cat /etc/resolv.conf
輸出範例:
nameserver 127.0.0.11
options ndots:0
內容解密:
nameserver 127.0.0.11:指定 Docker 內部 DNS 伺服器的地址。options ndots:0:設定 DNS 解析的相關選項。
除錯工具介紹
Linux 中有多種工具可用於除錯網路連線和程式執行,包括:
telnet和netcat:用於開啟到特定 IP 或主機的原始連線。curl和wget:用於傳送 HTTP 請求。netcat和ss:用於列出開啟的連線和監聽的通訊端。lsof:用於列出程式開啟的檔案。strace:用於列印程式的系統呼叫軌跡。
使用 strace 除錯網路延遲問題
曾經遇到過連線到 Docker 中執行的資料函式庫例項時出現延遲的問題。使用 strace 可以捕捉到系統呼叫的詳細資訊,幫助定位問題。
telnet db1 3306 | grep socket
輸出範例中包含:
socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 3
內容解密:
socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 3:建立了一個 UDP 通訊端,用於 DNS 解析。
進一步分析發現,問題是由於 Docker 容器內的 /etc/resolv.conf 檔案中殘留了過時的 DNS 組態,導致了 DNS 解析延遲。重啟 Docker 服務後,問題得到解決。
重啟 Docker 服務以解決 DNS 解析問題
重啟 Docker 服務可以更新容器內的 DNS 組態:
service docker restart
網路監控工具推薦
為了預防類別似問題,設定網路監控是非常重要的。推薦使用以下工具:
- Grafana:用於資料視覺化和監控。
- Netdata:提供實時系統和應用監控。
- Prometheus:用於系統和服務監控及報警。
這些工具可以幫助及時發現和解決網路連線問題。