在現代軟體開發中,容器化技術已成為不可或缺的一部分。PeerSpace 公司利用 Docker 和 systemd 建立了一套高效的 Web 服務佈署和管理系統,實作了零停機佈署、簡化組態管理和提高系統穩定性。他們將所有服務容器化,並使用 systemd 管理容器的生命週期,透過環境變數檔案區分叢集設定、服務組態和敏感資訊,確保不同環境的一致性。佈署流程則透過指令碼自動化執行組態更新、容器映像下載、systemd 重新載入和服務重啟,同時結合日誌聚合和監控工具,提升系統的可觀測性。
利用 systemd 管理服務
在 PeerSpace 的系統架構中,每個服務都由 systemd 進行管理。Systemd 是一種服務管理工具,其設計理念源自於 macOS 的 launchd。它使用簡單的資料檔案(稱為單元檔案,unit files)來定義每個服務的生命週期,而非像傳統的服務管理工具那樣使用 shell 指令碼進行管理。
systemd 的基本架構與組態
PeerSpace 的服務僅依賴 Docker 程式作為其執行時的唯一依賴。Systemd 的依賴管理功能僅用於確保 Docker 正在執行,而不涉及服務啟動順序的管理。這些服務被設計為可以按任意順序啟動。
每個服務由以下部分組成:
- 容器映像檔(Container Image):定義了服務的執行環境。
- systemd 單元檔案(Systemd Unit File):定義了服務如何被 systemd 管理。
- 特定於該容器的環境變數檔案(Environment Variable File):儲存了特定於該服務的組態。
- 分享的環境變數檔案(Shared Environment Variable Files):包含全域性的組態引數。
systemd 單元檔案結構
所有的 systemd 單元檔案遵循相同的結構。首先,它們會在服務啟動前載入一系列環境變數檔案:
EnvironmentFile=/usr/etc/service-locations.env
EnvironmentFile=/usr/etc/service-config.env
EnvironmentFile=/usr/etc/cluster.env
EnvironmentFile=/usr/etc/secrets.env
EnvironmentFile=/usr/etc/%n.env
內容解密:
- 這段組態確保每個服務都會載入一組通用的環境變數檔案,包括
service-locations.env、service-config.env、cluster.env和secrets.env。 - 此外,還會載入一個特定於該服務的環境變數檔案,檔案名稱與單元檔案的名稱相同(例如,
docker-search.service.env)。
接下來,單元檔案中包含了一些指令,以確保容器在啟動前被正確清理:
ExecStartPre=-/bin/docker kill %n
ExecStartPre=-/bin/docker rm -f %n
內容解密:
ExecStartPre指令用於在服務啟動前執行特定的命令。- 這裡使用了 Docker 命令來殺死並移除同名的容器(如果存在)。
%n會被替換為單元檔案的全名。 - 在 Docker 命令前加上
-表示即使命令失敗也不會影響服務的啟動。
啟動容器
主要的 ExecStart 指令定義瞭如何啟動容器:
ExecStart=/bin/docker \
run \
-p "${APP_PORT}:${APP_PORT}" \
-e "APP_PORT=${APP_PORT}" \
-e "SERVICE_C_HOST=${SERVICE_C_HOST}" \
-e "SERVICE_D_HOST=${SERVICE_D_HOST}" \
-e "SERVICE_M_HOST=${SERVICE_M_HOST}" \
--add-host docker01:${DOCKER01_IP} \
--add-host docker02:${DOCKER02_IP} \
--volume /usr/local/docker-data/%n/db:/data/data \
--volume /usr/local/docker-data/%n/logs:/data/logs \
--name %n \
${IMAGE_NAME}:${IMAGE_TAG}
內容解密:
- 使用
EnvironmentFile載入的環境變數來組態容器,例如透過-p匯出的埠號。 - 使用
--add-host將叢集中其他主機的位址新增到容器的/etc/hosts檔案中。 - 將日誌和資料目錄對映到主機上的特定路徑,以便檢查是否有未預期的寫入操作。
- 容器的映像檔名稱和版本來自於
/usr/etc/%n.env檔案中定義的環境變數。
停止容器與生命週期管理
最後,單元檔案中還定義瞭如何停止容器以及其他生命週期相關的組態:
ExecStop=-/bin/docker stop %n
Restart=on-failure
RestartSec=1s
TimeoutStartSec=120
TimeoutStopSec=30
內容解密:
ExecStop指令定義了停止容器的方式。Restart=on-failure表示當容器異常離開時,systemd 會自動重啟它。RestartSec=1s設定重啟的間隔時間為 1 秒。TimeoutStartSec和TimeoutStopSec分別設定了啟動和停止操作的超時時間。
叢集範圍內的組態管理
PeerSpace 將叢集的組態分成兩種型別:環境變數檔案和 systemd 單元檔案。環境變數檔案根據其變更頻率和敏感性被進一步劃分為不同的類別:
service-locations.env:包含叢集中所有服務的主機名稱,通常在不同叢集之間保持一致。service-config.env:包含與服務本身相關的組態,理想情況下在執行相同版本服務的不同叢集之間保持一致。secrets.env:包含敏感資訊,如金鑰等,在每個叢集上都不同。cluster.env:包含叢集特有的組態,如資料函式庫字首、是否為測試或生產環境、外部位址等。
範例:cluster.env 與 service-locations.env
以下是 cluster.env 的範例內容:
CLUSTER_ID=alpha
CLUSTER_TYPE="test"
DOCKER01_IP=x.x.x.226
DOCKER02_IP=x.x.x.144
EXTERNAL_ADDRESS=https://somethingorother.com
LOG_STORE_HOST=x.x.x.201
LOG_STORE_PORT=9200
MONGODB_PREFIX=alpha
MONGODB_HOST_01=x.x.x.177
MONGODB_HOST_02=x.x.x.299
MONGODB_REPLICA_SET_ID=rs001
以下是 service-locations.env 的範例內容:
SERVICE_A_HOST=docker01
SERVICE_B_HOST=docker03
CLIENTLOG_HOST=docker02
SERVICE_D_HOST=docker01
...
SERVICE_Y_HOST=docker03
SERVICE_Z_HOST=docker01
圖表說明:
graph LR;
A[cluster.env] -->|包含叢集特有組態|> B[叢集組態];
C[service-locations.env] -->|包含服務主機名稱|> B;
D[secrets.env] -->|包含敏感資訊|> B;
E[service-config.env] -->|包含服務相關組態|> B;
B -->|被 systemd 單元檔案參照|> F[服務啟動];
圖表翻譯: 此圖示展示了不同的環境變數檔案如何被用於組態叢集和服務。它們最終被 systemd 單元檔案參照,以正確地啟動和管理服務。
容器佈署與管理的最佳實踐:PeerSpace 的經驗分享
在現代化的軟體開發與佈署流程中,容器技術已經成為不可或缺的一環。PeerSpace,一家專注於創新技術的公司,透過採用 Docker 容器技術,建立了一套極簡且高效的佈署與管理系統。本文將探討 PeerSpace 如何利用 Docker 實作開發、測試和生產環境的一致性,並分享他們在容器佈署與管理方面的寶貴經驗。
組態管理的重要性
PeerSpace 的系統架構圍繞著組態管理展開。他們將所有的組態資訊儲存在 Git 倉函式庫中,這不僅確保了組態的可追蹤性,還大大簡化了故障排查的過程。這種做法的核心優勢在於:
- 版本控制:所有的組態變更都透過 Git 進行版本控制,使得每一次變更都有跡可循。
- 一致性:透過統一的組態管理,確保了開發、測試和生產環境之間的一致性。
- 可回溯性:當出現問題時,可以快速定位到具體的組態變更,進行有針對性的修復。
組態管理的技術實作
PeerSpace 的組態管理依賴於以下幾個關鍵技術要素:
- 環境變數:透過環境變數來控制服務的行為,使得同一套程式碼可以在不同的環境中靈活執行。
-e "SERVICE_D_HOST=${SERVICE_D_HOST}"
這條命令展示瞭如何透過環境變數傳遞主機資訊給容器。
- 主機資訊注入:利用
--add-host引數,將 Docker 主機的 IP 位址注入到容器中,實作容器與主機之間的網路連通。
--add-host docker01:${DOCKER01_IP}
這種方式使得容器可以透過主機名稱直接存取對應的主機。
佈署流程的最佳實踐
PeerSpace 的佈署流程分為三個主要步驟:
- 組態變更:首先在 Git 倉函式庫中進行組態變更,並提交更新。
- 組態推播:透過指令碼將更新後的組態推播到各個主機的暫存區。
- 佈署生效:在各個主機上執行佈署指令碼,將新的組態生效。
佈署流程的詳細步驟
在每次佈署過程中,PeerSpace 會執行以下具體操作:
- 複製組態檔案:將暫存區的組態檔案複製到最終位置,包括 systemd 單元檔案、分享組態檔案、服務特定組態檔案以及解密後的金鑰檔案。
- 下載映象:根據服務組態檔案中的定義,下載必要的 Docker 映象。
- 重新載入 systemd 組態:確保 systemd 能夠識別最新的單元檔案。
- 重啟服務:重啟對應的 systemd 單元,以使新的組態生效。
# 示例指令碼片段
for service in ${SERVICES_LIST}; do
# 複製組態檔案
cp /staging/${service}/config/* /etc/${service}/
# 下載映象
docker pull ${REGISTRY}/${service}:${VERSION}
# 過載 systemd 組態
systemctl daemon-reload
# 重啟服務
systemctl restart ${service}
done
內容解密:
此指令碼片段展示了 PeerSpace 如何自動化佈署流程。首先,遍歷需要更新的服務列表;然後,將組態檔案從暫存區複製到目標位置;接著,下載指定的 Docker 雡像;之後,過載 systemd 組態,以確保最新的單元檔案被識別;最後,重啟對應的服務,以使新的組態生效。這一流程保證了佈署的自動化和一致性。
開發與生產環境的差異管理
PeerSpace 對開發環境和生產環境採取了不同的管理策略:
- 開發環境:使用
latest標籤的 Docker 映象,便於快速迭代和測試。 - 生產環境:使用帶有版本號的標籤,確保生產環境的穩定性和可追溯性。
版本管理的最佳實踐
在生產環境中,PeerSpace 採用了嚴格的版本管理機制:
- 釋出指令碼:執行釋出指令碼,對 Git 倉函式庫進行標籤化,並構建和推播帶有版本號的 Docker 映象。
- 更新組態:更新服務的環境變數檔案,參照新的映象標籤。
- 推播組態:將新的組態推播到生產主機。
- 執行佈署:在生產主機上執行佈署指令碼,完成新版本的佈署。
輔助服務的支援
PeerSpace 使用了一系列輔助服務來支援其核心業務,包括:
- 日誌聚合:透過 Fluentd + Kibana 的組合,實作日誌的收集和分析。同時,利用 docker-gen 動態生成 Fluentd 的組態檔案,確保每個容器的日誌都能被正確收集。
- 監控服務:採用 Datadog 作為監控服務,提供效能指標、API 使用情況以及業務事件的監控。Datadog 的強大標籤功能,使得資料的多維度分析成為可能。
日誌聚合與監控的技術細節
PeerSpace 的日誌聚合系統根據以下技術堆疊構建:
- Fluentd:作為日誌收集代理,負責從各個容器中收集日誌。
- Kibana:提供日誌的視覺化介面,方便開發人員進行日誌分析。
- docker-gen:自動生成 Fluentd 的組態檔案,確保新啟動的容器能夠被納入日誌收集範圍。
# docker-compose.yml 示例
version: '3'
services:
fluentd:
image: fluent/fluentd:v1.12-debian-1
volumes:
- ./fluentd.conf:/fluentd/etc/fluent.conf
ports:
- "24224:24224"
內容解密:
此 docker-compose.yml 檔案定義了一個 Fluentd 服務,用於收集日誌。透過掛載自定義的 fluentd.conf 組態檔案,Fluentd 能夠根據 PeerSpace 的需求進行靈活組態。同時,開放 24224 連線埠,用於接收來自其他容器的日誌資料。這種方式實作了日誌收集的標準化和自動化。
- 零停機佈署:透過引入反向代理等技術,實作服務的零停機佈署,提升使用者經驗。
- 高層級組態抽象:開發更高層級的組態描述語言,自動生成組態檔案,並計算需要重啟的容器,降低人工干預的需求。
- 叢集管理工具:評估採用 Mesos 或 Kubernetes,以進一步簡化叢集管理和提升系統的可擴充套件性。
透過不斷最佳化和創新,PeerSpace 不僅提升了自身的技術能力,也為同行業的其他企業提供了寶貴的參考經驗。隨著容器技術和相關工具的不斷發展,相信會有更多企業能夠從中受益,實作更高效、更穩定的軟體開發和佈署流程。
使用Docker實作零停機佈署的Web環境實戰
在現代化的軟體開發與佈署流程中,Docker已經成為眾多企業的首選方案。許多成功的企業透過Docker實作了高效且穩定的生產環境佈署。本文將探討一個真實的生產環境案例,展示如何利用Docker實作零停機佈署、集中化日誌管理、監控以及JVM效能分析等功能。
環境概述
該Web環境執行在Amazon Web Services(AWS)上,使用Ubuntu作業系統,並透過Docker容器化技術佈署其CRM網頁應用程式。選擇Docker的主要原因在於它能夠提供零停機佈署、版本隔離以及快速回復等功能。整個環境透過bash指令碼進行協調和管理,無需依賴複雜的容器協調工具如Apache Mesos或Kubernetes。
單機佈署詳解
在單台AWS伺服器上,執行著四個Docker容器。這些容器之間透過Docker橋接網路進行通訊,並將多個埠暴露給主機,以實作HTTP服務和JVM監控。此外,該環境還使用了Amazon ELB(Elastic Load Balancer)進行負載平衡和健康檢查。所有容器的日誌都被儲存到主機上,以便與現有的日誌解決方案(如SumoLogic)整合。
協調與佈署
協調是Docker生產環境中的關鍵環節,主要包含兩個部分:
-
準備Docker主機:使用Chef組態管理工具安裝Docker並組態必要的操作工具,如監控和日誌代理。
- 伺服器啟動後,Chef會自動安裝Docker、組態監控代理(如Datadog)、設定日誌代理(如SumoLogic)等。
- 最後,Chef會建立一個bash指令碼,用於管理和佈署容器。
-
佈署容器:透過一個定時執行的bash指令碼來管理和佈署容器。
- 該指令碼會檢查容器是否正在執行,如果沒有,則佈署hipache和redis容器,並將它們連結起來。
- 提取最新的Web伺服器映像並啟動新的容器。
- 在將新容器加入負載平衡器之前,進行健康檢查。
- 更新負載平衡器的組態,以包含新容器的資訊。
- 保留舊容器以便需要時進行回復。
程式碼範例:佈署指令碼
#!/bin/bash
# 檢查hipache容器是否正在執行
STATE=$(docker inspect hipache | jq ".[0].State.Running")
if [[ "$STATE" != "true" ]]; then
set +e
docker rm hipache > /dev/null 2>&1
set -e
mkdir -p /logs/hipache/
docker run -p 80:80 -p 6379:6379 --name hipache -v /logs/hipache:/logs -d repo.com/hipache
echo "$(date +"%Y-%m-%d %H:%M:%S %Z") lpush frontend:* default"
sleep 5
(echo -en "lpush frontend:* default\r\n"; sleep 1) | nc localhost 6379
fi
# 提取最新的映像
IMAGE_ID=$(docker images | grep ${IMAGE_NAME} | grep $REMOTE_VERSION | head -n 1 | awk '{print $3}')
if [ -z $IMAGE_ID ]; then
docker pull $DOCKER_IMAGE_NAME
fi
# 啟動新的Web應用容器
echo "$(date +"%Y-%m-%d %H:%M:%S %Z") launching $DOCKER_IMAGE_NAME, logging to $LOG_DIR"
mkdir -p $LOG_DIR
NEW_WEBAPP_ID="abcdefghijklmnopqrstuvwxyz"
MAX_TIMEOUT=5
set +e
until [ $MAX_TIMEOUT -le 0 ] || NEW_WEBAPP_ID=$(docker run -P -h $(hostname) --link hipache:hipache $(dockerParameters $BRANCH) -d -v $LOG_DIR:/logs $DOCKER_IMAGE_NAME); do
echo -n "."
sleep 1
let MAX_TIMEOUT-=1
done
set -e
# 檢查新容器是否啟動成功
NEW_WEBAPP_IP_ADDR=$(docker inspect $NEW_WEBAPP_ID | jq '.[0].NetworkSettings.IPAddress' -r)
if [ -z "$NEW_WEBAPP_IP_ADDR" -o "$NEW_WEBAPP_IP_ADDR" = "null" ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S %Z") no new webapp ip, failed to start"
# 傳送佈署失敗通知
fi
內容解密:
此指令碼首先檢查hipache容器是否正在執行。如果不是,則移除舊容器並啟動一個新的hipache容器,同時設定必要的組態。接著,它會提取最新的Web應用映像,並啟動一個新的容器,將其日誌掛載到主機上。最後,指令碼會檢查新容器的IP位址,以確認它是否成功啟動。如果啟動失敗,則會傳送通知。
隨著技術的不斷發展,未來可以考慮採用Kubernetes等更先進的容器協調工具,以進一步簡化容器的佈署和管理,提高系統的可擴充套件性和彈性。此外,還可以探索更多的監控和日誌分析工具,以提升系統的可觀測性和故障排查能力。