4

自動升級更新執行中的 Docker 容器解決方案 - watchtower

 1 year ago
source link: https://blog.wu-boy.com/2023/02/automating-docker-container-base-image-updates-cht/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
CI

現在大家在部署服務肯定都已經容器化,而如何有效管理及升級容器不影響現有的服務,這就是一個重要的議題,然而在 CI/CD 的流程內,肯定有兩個步驟是必須的,第一就是將環境打包成 Docker Image 並上傳到公司內私有的 Docker Registry,以及上傳完畢後,也許透過 SSH 方式連上機器,並且拉取新的映像檔,再透過 Graceful Shutdown 機制重新啟動正在執行的服務。可以參考這篇了解什麼是 Graceful Shutdown。本篇就是要帶給大家一個全新的工具 Watchtower 用來自動升級更新執行中的容器,讓 CD 流程可以再簡化一步,開發者只要上傳完 Docker Image,遠方的伺服器就可以自動更新。

架構圖就會變成底下

CI

什麼是 Watchtower

Watchtower 是一個用 Go 語言開發的應用程序,它會監視正在運行的 Docker 容器,並觀察這些容器最初啟動時所使用的映像檔 (Docker Image) 是否有更改。如果 watchtower 檢測到映像檔已更改,它將自動使用新映像檔重新啟動容器。

透過 watchtower,開發者可以通過將新的映像檔推送到 Docker Hub 或您自己的 Docker Registry,簡單地更新容器化應用程序的運行版本。Watchtower 將下載您的新映像,優雅地關閉現有容器,然後使用最初部署時使用的相同選項重新啟動它。

例如,假設您正在運行 watchtower 以及一個名為 ghcr.io/go-training/example53 的映像實例:

每隔幾分鐘,watchtower 將下載最新的 ghcr.io/go-training/example53 映像檔並將其與用於運行 “example53” 容器的映像進行比較。如果它發現映像檔已更改,它將停止/刪除 “example53” 容器,然後使用新映像和最初啟動容器時使用的相同 docker run 選項重新啟動它。

Watchtower 本身被打包成 Docker 容器,因此安裝非常簡單,只需拉取 containrrr/watchtower 映像即可。如果您正在使用基於 ARM 的架構,請從 Docker Hub 拉取適當的 containrrr/watchtower:armhf-tag 映像。

由於 watchtower 代碼需要與 Docker API 進行交互以監視運行的容器,因此在運行容器時需要使用 -v 標誌將 /var/run/docker.sock 挂載到容器中。

使用以下命令運行 watchtower 容器:

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower

如果從私有 Docker 註冊表中拉取映像檔,請使用環境變數 REPO_USERREPO_PASS 或將主機的 docker 配置文件掛載到容器中(位於容器文件系統的根目錄 /)。

docker run -d \
  --name watchtower \
  -e REPO_USER=username \
  -e REPO_PASS=password \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower container_to_watch --debug

另外,如果您在 Docker Hub 上設置了 2FA 身份驗證,提供帳號和密碼將不足夠。相反,可以運行 docker login 命令將憑證存在 $HOME/.docker/config.json 文件中,然後掛載此配置文件以使其對 Watchtower 容器可用:

docker run -d \
  --name watchtower \
  -v $HOME/.docker/config.json:/config.json \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower container_to_watch --debug

底下我們用 docker-compose 方式來測試正在執行的容器

version: "3"
services:
  example53:
    image: ghcr.io/go-training/example53:latest
    restart: always
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    ports:
      - "8080:8080"

  watchtower:
    image: containrrr/watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 5

啟動後可以看到底下 Log 訊息

example53_1   | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
example53_1   |
example53_1   | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
example53_1   |  - using env: export GIN_MODE=release
example53_1   |  - using code: gin.SetMode(gin.ReleaseMode)
example53_1   |
example53_1   | [GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
example53_1   | [GIN-debug] GET    /                         --> main.main.func2 (3 handlers)
example53_1   | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
example53_1   | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
example53_1   | [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
example53_1   | [GIN-debug] Listening and serving HTTP on :8080
watchtower_1  | time="2023-03-02T01:13:07Z" level=info msg="Watchtower 1.5.3"
watchtower_1  | time="2023-03-02T01:13:07Z" level=info msg="Using no notifications"
watchtower_1  | time="2023-03-02T01:13:07Z" level=info msg="Checking all containers (except explicitly disabled with label)"
watchtower_1  | time="2023-03-02T01:13:07Z" level=info msg="Scheduling first run: 2023-03-02 01:13:12 +0000 UTC"
watchtower_1  | time="2023-03-02T01:13:07Z" level=info msg="Note that the first check will be performed in 4 seconds"
watchtower_1  | time="2023-03-02T01:13:14Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
watchtower_1  | time="2023-03-02T01:13:19Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
watchtower_1  | time="2023-03-02T01:13:24Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no

你可以根據需要監控的時間間隔調整 --interval 5 參數,這邊先設定 5 秒,watchtower 預設監控 Host 全部容器,故如果有些容器不想更新,可以在設定 label

labels:
  - "com.centurylinklabs.watchtower.enable=false"

另外每次升級後,舊有的容器或映像檔都還是會存在主機內,會佔用一定空間,這時可以透過 --cleanup 參數讓 watchtower 會在使用新的映像檔案重新啟動容器後刪除舊的映像檔。

  watchtower:
    image: containrrr/watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 5 --cleanup

過程中拉到新的 Image 就可以看到底下錯誤訊息,會先送 SIGTERM 訊號給容器做 Gracefully Shutdown。

watchtower_1  | time="2023-03-02T01:35:15Z" level=info msg="Found new ghcr.io/go-training/example53:latest image (040d01951ee2)"
watchtower_1  | time="2023-03-02T01:35:17Z" level=info msg="Stopping /root_example53_1 (57fc95adf8cd) with SIGTERM"

如果要改 Stop Signals 可以透過 Label 方式轉換,請改 Dockerfile

LABEL com.centurylinklabs.watchtower.stop-signal="SIGHUP"

或者是在啟動容器時加上

docker run -d --label=com.centurylinklabs.watchtower.stop-signal=SIGHUP someimage

未來團隊在 CI/CD 流程,就可以專注在打包 Image,並且上傳到 Docker Registry 即可,機器上面所有的服務全部透過 Watchtower 來監控,上傳的 Image 也遵循 semver 原則。減輕不少撰寫 Shell Script 工作流程。



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK