你的Docker部署能更簡單嗎?GitLab整合與多程序管理又怎麼做?

xiaoxi666發表於2024-12-09

前言

文章開始前,先嚐試回答幾個問題:
  1. 在日常工作中,程式的打包和部署方式流程是怎樣的?

  2. 如果使用Docker容器部署方式,是用原生Docker命令,還是Kaniko這類工具?

  3. 除了業務服務外,如果還需要同時執行其他程序,應該怎麼辦?舉個例子:除了主服務程序(比如Web伺服器),還需要同時啟動Nginx程序(用於代理轉發部分前端流量,解決跨域問題)。

以上幾個問題,拆解:
  1. 屬於 CI 整合的概念:GitLab和Docker的整合是一種主流方式,其他工具也大同小異。

  2. 與核心態和使用者態有關。Kaniko是一種常用的容器映象構建工具。

  3. 需要用到程序管理工具,在Docker容器中同時管理多個程序。

一個個來說。

GitLab 和 Docker 的整合

兩者的整合主要有三種方式,手段大同小異,但核心目的都是把Docker打包映象的流程放到GitLab的流水線中執行。可參見官 https://docs.gitlab.com/ee/ci/docker/using_docker_build.html:

以 shell executor 為例:

可見,做法很簡單:將Docker命令作為流水線指令碼執行即可,映象打包出來後可用來部署。以近期熱度較高的大模型服務為例,經典的整合架構如下圖所示:

其他CI整合的主流程萬變不離其宗,不同之處在於在此基礎上做一些最佳化,比如快取加速等。

常用的容器映象構建工具:Kaniko

同樣,先看官網 https://github.com/GoogleContainerTools/kaniko 定義:

可見,Kaniko 是一個用於構建容器映象的工具,它允許我們從Dockerfile構建映象,且不需要Docker守護程序。Kaniko executor 構建映象的詳細步驟如下:
  1. Kaniko Executor Image:Kaniko執行器映象是一個特殊的容器映象,它包含了所有必要的工具和依賴,以便在容器內部構建新的Docker映象。

  2. Building an Image from a Dockerfile:使用者提供一個Dockerfile,Kaniko執行器映象將根據這個Dockerfile來構建一個新的映象。Dockerfile包含了一系列的指令,這些指令定義瞭如何構建一個新Docker映象,例如FROM, RUN, COPY, ADD等。

  3. Extracting the Filesystem of the Base Image:在Dockerfile中,FROM 指令指定了基礎映象,Kaniko 首先需要提取這個基礎映象的檔案系統。這個檔案系統被複制到 Kaniko 執行器映象的臨時目錄中,以便後續的構建過程可以在使用者空間中對其進行操作。

  4. Executing Commands in the Dockerfile:Kaniko逐個執行Dockerfile中的指令。對於每個指令,Kaniko都會在使用者空間中模擬該指令的效果。例如,如果指令是RUN apt-get update,Kaniko會在容器內部執行這個命令,就像在普通的Docker構建過程中一樣。

  5. Snapshotting the Filesystem in Userspace:在每個指令執行之後,Kaniko 會在使用者空間中對檔案系統進行快照。這個快照捕獲了自上一個快照以來檔案系統的所有變化,這些變化包括新建立的檔案、修改過的檔案和刪除的檔案。

  6. Appending a Layer of Changed Files:如果在執行某個指令後檔案系統發生了變化,Kaniko 會將這些變化作為一個新層新增到基礎映象上。這個過程是逐層進行的,每個指令可能對應一個或多個層,這取決於檔案系統的變化。

  7. Updating Image Metadata:除了新增檔案系統的變化之外,Kaniko還會更新映象的後設資料,包括標籤、環境變數、工作目錄等。這些後設資料資訊也是 Dockerfile 中指令的一部分,Kaniko會確保這些資訊被正確地應用到新構建的映象上。

  8. Pushing the Image to a Registry:一旦所有的指令都被執行完畢,並且所有的變化都被新增到映象中,Kaniko 會將這個新構建的映象推送到指定的容器映象倉庫(registry)。這個步驟需要提供倉庫的認證資訊,以便Kaniko能夠成功地將映象推送到倉庫。
總的來說,Kaniko與原生Docker最大的不同之處在於:Kaniko在使用者空間中模擬 Dockerfile中的指令,並逐層捕獲檔案系統的變化,最終構建出一個新的Docker映象,而不需要Docker守護程序的參與。這種方法提高了安全性,並且可以在多種環境中靈活地構建容器映象(也即:可以在沒有root許可權的環境中執行)。另外,在GitLab官網 https://docs.gitlab.com/ee/ci/docker/using_kaniko.html 也有 Kaniko 的使用方法描述:

在 Docker 容器中管理多個程序

現實中,大部分情況下容器中僅會有一個程序,也即1號程序。但總會有例外發生,以上文中的例子來說,某個容器中不僅需要主服務程序提供Web服務,還需要Nginx程序用於代理轉發部分前端流量,以解決跨域問題。此時就需要同時啟動並管理多個程序。有需求就有實現,目前多程序管理工具有很多了,一種常見的方案是使用Python編寫的程序管理工具supervisord,它可以管理多個程序,包括啟動、停止、重啟等操作。針對上述例子,整體整合方式如下:
  1. 製作基礎映象:基於centos系統映象,安裝supervisord 和nginx。
/bin/sh -c yum install -y epel-release
/bin/sh -c yum install -y supervisor
/bin/sh -c yum install -y nginx-1.10.3
/bin/sh -c yum clean all

# 注意只列出了本文涉及到的幾個命令,還有很多其他功能需要安裝
# 可參考倉庫 https://github.com/CentOS/sig-cloud-instance-images
# 該倉庫包括了centos系統的各種映象的構建命令,囊括了很多實用命令安裝指令碼


2. 編寫 /etc/supervisord.conf,它是supervisord的主配置檔案,定義了supervisord 程序管理器的全域性設定和行為,確保所有被管理的程序都能按照預期的方式啟動和執行。透過這個配置檔案,可以集中管理多個服務,使得服務的部署和維護更加方便。

[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700

[supervisord]
logfile=/var/log/supervisor/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
pidfile=/var/run/supervisord.pid
nodaemon=false
minfds=1024
minprocs=200

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

[include]
files = /etc/supervisor/conf.d/*.ini

[unix_http_server]:定義了 supervisord 的 Unix 套接字 HTTP 伺服器的配置,用於 supervisorctl 命令列工具或其他客戶端與 supervisord 進行通訊。

[supervisord]:包含了 supervisord 守護程序的全域性配置,如日誌檔案位置、日誌大小限制、日誌備份數量、程序檔案位置、最小檔案描述符和程序數量等。

[rpcinterface:x]:定義了 RPC 介面的配置,允許遠端管理 supervisord。這通常用於 supervisorctl 命令列工具。

[supervisorctl]:定義了 supervisorctl 命令列工具的配置,如連線到 supervisord 的 URL。

[include] :允許 supervisord 包含其他配置檔案。這使得你可以將不同的程式配置在不同的檔案中,然後由主配置檔案統一管理。通常,這個節會包含 /etc/supervisor/conf.d/*.ini,這樣 supervisord 就會自動載入 /etc/supervisor/conf.d/ 目錄下的所有 .ini 檔案。

  1. 為各工程使用方便,將 /etc/supervisor/conf.d/nginx.ini 提前打包到映象中,後續只需配置 nginx.conf 即可。

 
[program:nginx]
command=/etc/nginx/sbin/nginx
user=xiaoxi666
priority=999
numprocs=1
autostart=true
autorestart=true
startsecs=1
startretries=3
stopsignal=KILL
stopwaitsecs=10
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.err
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=5
stopasgroup=true

[program:nginx]:定義了一個名為 nginx 的程式。supervisord 會根據這個名稱來識別和控制這個程序。各個欄位含義:

command:指定啟動程序的命令。

user:指定以哪個使用者身份執行 Nginx 程序。通常建議使用非 root 使用者來執行服務以提高安全性。

priority:設定程式的啟動優先順序。較小的數字表示更高的優先順序,即 supervisord 會先啟動優先順序數值較小的程式。

numprocs:指定應該啟動多少個程序。這裡設定為1,意味著只啟動一個 Nginx 程序。

autostart:設定為 true 時,supervisord 會在啟動時自動啟動這個程式。

autorestart:設定為 true 時,如果 nginx 程序意外退出,supervisord 會自動重啟它。

startsecs:指定 Nginx 程序啟動後多少秒內被認為是穩定的。如果在這個時間內程序退出,supervisord 會認為啟動失敗。

startretries:如果 Nginx 程序在 startsecs 時間內退出,supervisord 會嘗試重啟它。這個值指定了最大重啟次數。

stopsignal:指定停止 Nginx 程序時傳送的訊號。KILL 訊號是一個強制終止程序的訊號。

stopwaitsecs:指定 supervisord 等待 Nginx 程序停止的時間(以秒為單位)。如果超時,supervisord 會傳送 KILL 訊號。

stdout_logfile:指定 Nginx 程序的標準輸出日誌檔案的位置。

stderr_logfile:指定 Nginx 程序的標準錯誤日誌檔案的位置。

stdout_logfile_maxbytes:設定標準輸出日誌檔案的最大大小。當檔案達到這個大小後,會滾動切分輸出。

stdout_logfile_backups:設定標準輸出日誌檔案的備份數量。當日志檔案滾動切分時,舊的日誌檔案會被保留的備份數量。

stopasgroup:設定為 true 時,supervisord 會向整個程序組傳送停止訊號,這可以確保所有子程序都被正確終止。

做完這一步,該映象就可以提供給“同時需要Web服務和Nginx服務”的工程使用了。

  1. 各工程編寫自己的Dockerfile檔案,將自定義的服務ini檔案(假設名字為supervisor-app.ini)和 nginx.conf 檔案複製到指定目錄,然後啟動supervisord。
# 定義環境變數
ENV SUPERVISOR_CONF_DIR=/etc/supervisor/conf.d
ENV NGINX_CONF_DIR=/etc/nginx/conf.d
ENV APP_DIR=/app
ENV PROJECT_NAME=xiaoxi666_demo_project

# 複製supervisord的配置檔案到supervisord的配置目錄
COPY supervisor-app.ini ${SUPERVISOR_CONF_DIR}/supervisor-app.ini

# 複製nginx配置檔案到指定的nginx配置目錄
COPY nginx.conf ${NGINX_CONF_DIR}/nginx.conf

# 複製當前目錄下的所有檔案到APP_DIR
COPY . ${APP_DIR}

# 建立專案目錄並移動target目錄下的內容到專案目錄
RUN mkdir -p ${APP_DIR}/${PROJECT_NAME} \
&& mv ${APP_DIR}/target/* ${APP_DIR}/${PROJECT_NAME} \
&& chown -R xiaoxi666:xiaoxi666 ${APP_DIR}

# 暴露埠
EXPOSE 8080 其他要暴露的埠

# 啟動supervisord
CMD ["/usr/bin/supervisord", "-n"]
其中,supervisor-app.ini檔案內容為主程序的啟動命令,一個示例:
[program:xiaoxi666-demo-project]
command=java -jar 其他虛擬機器引數 xiaoxi666-demo-project.jar
user=root
priority=999
numprocs=1
autostart=true
autorestart=true
startsecs=1
startretries=3
stopsignal=KILL
stopwaitsecs=20
stdout_logfile=%(ENV_LOG_DIR)/supervisor/xiaoxi666-demo-project.log
stderr_logfile=%(ENV_LOG_DIR)/supervisor/xiaoxi666-demo-project.err
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=5
stopasgroup=true

而 nginx.conf 根據實際代理需求配置即可。

整體來看,檔案引用層級是這樣的:

後記

本文以實際工程中的例子為切入點,圍繞“Docker容器映象構建”、“GitLab整合部署”,以及“容器多程序管理”這幾個側重點做了一些探討和梳理,同時給出了對應的官方參考資料連結,有興趣的讀者可進一步深入學習。也歡迎大家留言,談談你所經歷的容器整合部署方式是怎樣的。

相關文章