返回文章列表

容器故障排除與健康檢查監控

本文探討容器技術的故障排除和健康檢查監控方法。首先介紹了在 Dockerfile 中使用 `--no-log-init` 選項的最佳實踐,接著深入解析 Podman

容器技術 系統管理

在容器化應用程式管理中,故障排除和監控是不可或缺的環節。本文除了提供 Podman 的健康檢查設定指引外,也涵蓋了容器建置過程中可能遇到的常見錯誤,例如映像檔儲存函式庫名稱錯誤、標籤錯誤以及私人儲存函式庫驗證失敗等問題。這些錯誤的產生原因和解決方法,對於確保容器化應用程式的穩定性和可靠性至關重要。透過理解 Podman 的健康檢查機制和常見錯誤的處理方式,開發者可以更有效地管理和維護容器化應用程式,提升整體系統的健壯性。

容器故障排除與監控

在建立基礎容器映像時,若需要新增具有高UID編號的全新使用者,最佳做法是在Dockerfile指令中加入--no-log-init選項,如下所示:

RUN useradd --no-log-init -u 99999000 -g users myuser

此選項指示useradd指令停止建立lastlog檔案,解決可能遇到的問題。

如同本文早期段落所提及,Podman團隊已建立一份長但非詳盡的常見問題清單。強烈建議在遇到任何問題時參考該清單: https://github.com/containers/podman/blob/main/troubleshooting.md

故障排除可能很棘手,但第一步始終是識別問題。因此,監控工具可以在問題出現時盡快發出警示。讓我們在下一節中瞭解如何使用健康檢查來監控容器。

使用健康檢查監控容器

從版本1.2開始,Podman支援為容器新增健康檢查。本文將探討這些健康檢查及其使用方法。

健康檢查是Podman的一個功能,可以幫助確定容器中執行的程式的健康狀態或就緒狀態。它可以像檢查容器的程式是否正在執行一樣簡單,也可以更複雜,例如使用網路連線驗證容器及其應用程式是否具有回應性。

健康檢查由五個核心元件組成。第一個是主要元素,用於指示Podman執行特定的檢查;其他元件用於組態健康檢查的排程。讓我們詳細瞭解這些元素:

  • 指令:這是Podman將在目標容器內執行的指令。容器的健康狀態將透過等待成功(傳回碼0)或失敗(其他離開碼)來確定。 若我們的容器提供一個Web伺服器,例如,我們的健康檢查指令可以是非常簡單的curl指令,嘗試連線到Web伺服器埠以確保其具有回應性。
  • 重試次數:這定義了Podman必須連續執行失敗的指令次數,才會將容器標記為不健康。如果指令執行成功,Podman將重置重試計數器。
  • 間隔:此選項定義了Podman執行健康檢查指令的時間間隔。 找到合適的間隔時間可能很困難,需要一些試錯。如果我們將其設定為較小的值,那麼我們的系統可能會花費大量時間執行健康檢查。但如果我們將其設定為較大的值,我們可能會難以捕捉逾時。此值可以使用廣泛使用的時間格式定義:30s1h5m
  • 啟動期間:這描述了Podman開始執行健康檢查的時間。在此期間,Podman將忽略健康檢查失敗。 我們可以將其視為一個寬限期,應允許我們的應用程式成功啟動並正確回應任何客戶端以及我們的健康檢查。
  • 逾時:這定義了健康檢查本身必須完成之前被視為不成功的時間。

讓我們來看一個實際範例,假設我們要為容器定義一個健康檢查並手動執行該健康檢查:

$ podman run -dt --name healthtest1 --healthcheck-command \
'CMD-SHELL curl http://localhost || exit 1' \
--healthcheck-interval=0 quay.io/libpod/alpine_nginx:latest

內容解密:

  • podman run指令用於啟動一個新的容器。
  • -dt選項表示以分離模式(背景)執行容器,並分配一個偽TTY。
  • --name healthtest1指定容器的名稱為healthtest1
  • --healthcheck-command定義了健康檢查指令,在此範例中為curl http://localhost || exit 1,用於檢查容器內的Web伺服器是否具有回應性。
  • --healthcheck-interval=0停用自動健康檢查,使其成為手動執行。
$ podman healthcheck run healthtest1
$ echo $?
0

內容解密:

  • podman healthcheck run healthtest1指令用於手動執行對healthtest1容器的健康檢查。
  • echo $?輸出上一個指令的離開碼,在此範例中為0,表示健康檢查成功,容器是健康的。

Podman使用systemd計時器來排程健康檢查。因此,如果我們想要為我們的容器排程健康檢查,則必須使用systemd。當然,如果我們的某些系統不使用systemd作為預設的守護程式管理器,我們可以使用不同的工具,例如cron,來排程健康檢查,但這些需要手動設定。

讓我們透過建立一個具有間隔的健康檢查來檢視此自動整合與systemd的工作原理:

$ podman run -dt --name healthtest2 --healthcheck-command \
'CMD-SHELL curl http://localhost || exit 1' \
--healthcheck-interval=10s quay.io/libpod/alpine_nginx:latest

內容解密:

  • 此指令與前一個範例類別似,但新增了--healthcheck-interval=10s選項,用於每10秒執行一次健康檢查。
$ podman ps
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS                   PORTS     NAMES
70e7d3f0b436   quay.io/libpod/alpine_nginx:latest   nginx -g daemon o...   7 seconds ago   Up 7 seconds ago (healthy)         healthtest2

內容解密:

  • podman ps指令用於列出正在執行的容器。
  • 輸出顯示容器的狀態為(healthy),表示健康檢查成功。

那麼,這種整合是如何工作的?讓我們取得容器ID並在以下目錄中搜尋它:

$ ls /run/user/$UID/systemd/transient/70e*
/run/user/1000/systemd/transient/70e7d3f0b4363759fc66ae4903625e5f451d3af6795a96586bc1328c1b149ce5.service

內容解密:

  • 此指令列出了指定目錄中的檔案,該目錄包含與容器ID相關的systemd服務檔案。
  • 輸出顯示了一個與容器ID相對應的服務檔案,表明Podman已建立了一個systemd服務來排程健康檢查。

使用健康檢查監控容器

當我們使用排程間隔啟動具有健康檢查的容器時,Podman 將執行 systemd 服務和計時器單元檔案的暫時設定。這意味著這些單元檔案不是永久的,可能會在重啟時遺失。

讓我們檢查這些檔案中定義的內容:

$ cat /run/user/$UID/systemd/transient/70e7d3f0b4363759fc66ae4903625e5f451d3af6795a96586bc1328c1b149ce5.service
# This is a transient unit file, created programmatically via the systemd API. Do not edit.
[Unit]
Description=/usr/bin/podman healthcheck run 70e7d3f0b4363759fc66ae4903625e5f451d3af6795a96586bc1328c1b149ce5
[Service]
Environment="PATH=/home/alex/.local/bin:/home/alex/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/var/lib/snapd/snap/bin"
ExecStart="/usr/bin/podman" "healthcheck" "run" "70e7d3f0b4363759fc66ae4903625e5f451d3af6795a96586bc1328c1b149ce5"

$ cat /run/user/$UID/systemd/transient/70e7d3f0b4363759fc66ae4903625e5f451d3af6795a96586bc1328c1b149ce5.timer
# This is a transient unit file, created programmatically via the systemd API. Do not edit.
[Unit]
Description=/usr/bin/podman healthcheck run 70e7d3f0b4363759fc66ae4903625e5f451d3af6795a96586bc1328c1b149ce5
[Timer]
OnUnitInactiveSec=10s
AccuracySec=1s
RemainAfterElapse=no

內容解密:

  • 第一條命令顯示了與健康檢查相關的 systemd 服務單元檔案內容。該檔案定義了執行的命令和環境變數。
  • 第二條命令顯示了與健康檢查相關的 systemd 計時器單元檔案內容。該檔案定義了健康檢查的排程間隔。
  • OnUnitInactiveSec=10s 表示健康檢查每 10 秒執行一次。
  • AccuracySec=1s 表示計時器的精確度為 1 秒。
  • RemainAfterElapse=no 表示計時器在觸發後不會保留。

從前面的程式碼區塊中,我們可以看到服務單元檔案包含了 Podman 健康檢查命令,而計時器單元檔案定義了排程間隔。

為了快速識別健康或不健康的容器,我們可以使用以下命令:

$ podman ps -a --filter health=healthy
CONTAINER ID   IMAGE                                  COMMAND                  CREATED         STATUS                    PORTS     NAMES
1faae6c46839   quay.io/libpod/alpine_nginx:latest     nginx -g daemon o...     36 minutes ago   Exited (137) 19 minutes ago (healthy)         healthtest1
70e7d3f0b436   quay.io/libpod/alpine_nginx:latest     nginx -g daemon o...     13 minutes ago   Up 13 minutes ago (healthy)         healthtest2

內容解密:

  • --filter health=healthy 選項用於篩選出健康的容器。
  • podman ps 命令用於列出容器。
  • 該命令輸出了健康的容器列表,包括容器 ID、映像、命令、建立時間、狀態和名稱。

檢查容器建置結果

在之前的章節中,我們詳細討論了容器建置流程,並學習瞭如何使用 Dockerfiles/Containerfiles 或 Buildah 原生命令建立自定義映像。

本文將提供一些最佳實踐,以檢查建置結果並瞭解相關問題。

從 Dockerfiles 疑難排解建置問題

當使用 Podman 或 Buildah 根據 Dockerfile/Containerfile 執行建置時,建置流程會將所有指令的輸出和相關錯誤列印到終端機 stdout。

對於所有 RUN 指令,執行的命令產生的錯誤會被傳播並列印出來,以便進行偵錯。

讓我們嘗試測試一些可能的建置問題。以下範例並非錯誤的詳盡清單;目的是提供一種方法來分析根本原因。

建置範例:RUN 指令錯誤

第一個範例顯示了一個最小化的建置,其中 RUN 指令由於執行的命令中的錯誤而失敗。RUN 指令中的錯誤可以涵蓋廣泛的情況,但一般規則如下:執行的命令傳回一個離開碼,如果是非零值,則建置失敗,並列印出錯誤和離開狀態。

在下一個範例中,我們使用 yum 命令安裝 httpd 軟體包,但我們故意在軟體包名稱中輸入錯誤,以產生錯誤。以下是 Dockerfile 的內容:

FROM registry.access.redhat.com/ubi8
# Update image and install httpd
RUN yum install -y htpd && yum clean all –y
# Expose the default httpd port 80
EXPOSE 80
# Run the httpd
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

內容解密:

  • FROM 指令指定了基礎映像。
  • RUN 指令用於安裝 httpd 軟體包,但由於軟體包名稱錯誤而失敗。
  • EXPOSE 指令暴露了容器的埠號。
  • CMD 指令定義了容器啟動時執行的命令。

如果我們嘗試執行建置命令,將會得到由 yum 命令產生的錯誤,因為它找不到遺失的 htpd 軟體包:

$ buildah build -t custom_httpd .
STEP 1/4: FROM registry.access.redhat.com/ubi8
STEP 2/4: RUN yum install -y htpd && yum clean all –y
...
No match for argument: htpd
Error: Unable to find a match: htpd
error building at STEP "RUN yum install -y htpd && yum clean all -y": error while running runtime: exit status 1
ERRO[0004] exit status 1

內容解密:

  • 前兩行列印了由 yum 命令產生的錯誤訊息。
  • Buildah(以及 Podman)產生了一條訊息,通知我們哪一步驟產生了錯誤。
  • 該訊息包含了 Dockerfile 指令和產生的錯誤,以及離開狀態。
  • 最後一行包含了 ERRO[0004] 錯誤碼和最終的離開狀態 1,與 buildah 命令執行相關。

解決方案:使用錯誤訊息來找出包含失敗命令的 RUN 指令,並修復或疑難排解命令錯誤。

容器建置錯誤處理與除錯

容器建置過程中可能出現多種錯誤,包括映像檔儲存函式庫名稱錯誤、標籤錯誤以及私人儲存函式庫驗證失敗等。瞭解這些錯誤的來源和解決方法對於順利佈署容器化應用程式至關重要。

Dockerfile 建置錯誤分析

在 Dockerfile 中,FROM 指令用於指定基礎映像檔。如果指定的映像檔不存在或標籤錯誤,建置過程將會失敗。

儲存函式庫名稱錯誤

FROM registry.access.redhat.com/ubi_8
# 更新映像檔並安裝 httpd
RUN dnf install -y httpd && dnf clean all –y
# 暴露預設的 httpd 連線埠 80
EXPOSE 80
# 執行 httpd
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

建置時出現錯誤:

$ buildah build -t custom_httpd .
STEP 1/4: FROM registry.access.redhat.com/ubi_8
Trying to pull registry.access.redhat.com/ubi_8:latest...
error creating build container: initializing source docker://registry.access.redhat.com/ubi_8:latest: reading manifest latest in registry.access.redhat.com/ubi_8: name unknown: Repo not found
ERRO[0001] exit status 125

標籤錯誤處理

FROM docker.io/library/fedora:sometag
# 更新映像檔並安裝 httpd
RUN dnf install -y httpd && dnf clean all –y
# 暴露預設的 httpd 連線埠 80
EXPOSE 80
# 執行 httpd
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

建置時出現錯誤:

$ buildah build -t custom_httpd .
STEP 1/4: FROM docker.io/library/fedora:sometag
Trying to pull docker.io/library/fedora:sometag...
error creating build container: initializing source docker://fedora:sometag: reading manifest sometag in docker.io/library/fedora: manifest unknown: manifest unknown
ERRO[0001] exit status 125

私人儲存函式庫驗證失敗

FROM local-registry.example.com/ubi8
# 更新映像檔並安裝 httpd
RUN yum install -y httpd && yum clean all –y
# 暴露預設的 httpd 連線埠 80
EXPOSE 80
# 執行 httpd
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

建置時出現錯誤:

$ buildah build -t test3 .
STEP 1/4: FROM local-registry.example.com/ubi8
Trying to pull local-registry.example.com/ubi8:latest...
error creating build container: initializing source docker://local-registry.example.com/ubi8:latest: reading manifest latest in local-registry.example.com/ubi8: unauthorized: authentication required
ERRO[0000] exit status 125

使用 Buildah 原生指令進行除錯

當使用 Buildah 原生指令建置容器時,將指令嵌入 shell 指令碼中是一種常見的做法。為避免因中間錯誤導致意外結果,建議在指令碼開頭新增 set -euo pipefail

#!/bin/bash
set -euo pipefail
# 試圖提取不存在的 Fedora 官方映像檔標籤
container=$(buildah from docker.io/library/fedora:non-existing-tag)
buildah run $container -- dnf install -y httpd; dnf clean all –y
buildah config --cmd "httpd -DFOREGROUND" $container
buildah config --port 80 $container
buildah commit $container custom-httpd
buildah tag custom-httpd registry.example.com/custom-httpd:v0.0.1

執行指令碼後出現錯誤:

$ ./custom-httpd.sh
Trying to pull docker.io/library/fedora:non-existing-tag...
initializing source docker://fedora:non-existing-tag: reading manifest non-existing-tag in docker.io/library/fedora: manifest unknown: manifest unknown
ERRO[0001] exit status 125

常見錯誤解決方案

  1. 修正儲存函式庫名稱:確認 FROM 指令中的儲存函式庫名稱正確無誤。
  2. 查詢有效標籤:使用 skopeo list-tags 命令查詢儲存函式庫中可用的標籤。
  3. 驗證私人儲存函式庫:使用 podman loginbuildah login 對私人儲存函式庫進行身份驗證。