cert-manager Webhook Pod 的權威除錯指南
上次驗證:2022 年 9 月 8 日
cert-manager webhook 是一個 pod,它作為您的 cert-manager 安裝的一部分執行。當使用 kubectl
套用資訊清單時,Kubernetes API 伺服器會透過 TLS 呼叫 cert-manager webhook 來驗證您的資訊清單。本指南可協助您除錯 Kubernetes API 伺服器和 cert-manager webhook pod 之間的通訊問題。
此頁面中列出的錯誤訊息會在安裝或升級 cert-manager 時,或在嘗試建立 Certificate、Issuer 或任何其他 cert-manager 自訂資源後不久遇到。
在下圖中,我們展示了除錯 cert-manager webhook 問題時的常見模式:當建立 cert-manager 自訂資源時,API 伺服器會透過 TLS 連接到 cert-manager webhook pod。紅色叉號表示 API 伺服器無法與 webhook 通訊。

本文件的其餘部分將介紹您可能遇到的錯誤訊息。
錯誤:connect: connection refused
此問題已在 4 個 GitHub 問題中回報 (#2736、#3133、#3445、#4425),在外部專案的 1 個 GitHub 問題中回報 (
aws-load-balancer-controller#1563
),在 Stack Overflow 上 (serverfault#1076563
),並且在 13 則 Slack 訊息中提到,可以使用搜尋in:#cert-manager in:#cert-manager-dev ":443: connect: connection refused"
列出。這個錯誤訊息也可能在其他正在建立 webhook 的專案中找到 (kubewarden-controller#110
)。
在安裝或升級 cert-manager 後不久,當您建立 Certificate、Issuer 或任何其他 cert-manager 自訂資源時,可能會遇到此錯誤。例如,使用以下命令建立 Issuer 資源
kubectl apply -f- <<EOFapiVersion: cert-manager.io/v1kind: Issuermetadata:name: examplespec:selfSigned: {}EOF
會顯示以下錯誤訊息
Error from server (InternalError): error when creating "STDIN":Internal error occurred: failed calling webhook "webhook.cert-manager.io": failed to call webhook:Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s":dial tcp 10.96.20.99:443: connect: connection refused
當使用 Helm 安裝或升級 cert-manager 1.5.0 及更高版本時,執行 helm install
或 helm upgrade
時,可能會出現非常相似的錯誤訊息
Error: INSTALLATION FAILED: Internal error occurred:failed calling webhook "webhook.cert-manager.io": failed to call webhook:Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s":dial tcp 10.96.20.99:443: connect: connection refused
當 API 伺服器嘗試與 cert-manager-webhook 建立 TCP 連線時,會發生「連線遭拒」訊息。在 TCP 術語中,API 伺服器傳送了 SYN
封包以啟動 TCP 交握,並收到 RST
封包作為回應。
如果我們要在執行 API 伺服器的控制平面節點內使用 tcpdump
,我們會看到傳回給 API 伺服器的封包
192.168.1.43 (apiserver) -> 10.96.20.99 (webhook pod) TCP 59466 → 443 [SYN]10.96.20.99 (webhook pod) -> 192.168.1.43 (apiserver) TCP 443 → 59466 [RST, ACK]
當沒有任何程式在接聽要求的連接埠時,Linux 核心會傳送 RST
封包。RST
封包也可能由其中一個 TCP 躍點傳回,例如,防火牆,如 Stack Overflow 頁面 連線遭拒錯誤的原因可能有哪些? 中所述。
請注意,防火牆通常不會傳回 RST
封包;它們通常會完全丟棄 SYN
封包,最終您會收到錯誤訊息 i/o timeout
或 context deadline exceeded
。如果是這種情況,請繼續查看 錯誤:i/o timeout
(連線問題) 和 錯誤:context deadline exceeded
部分進行調查。
讓我們從最接近 TCP 連線來源 (API 伺服器) 到其目的地 (pod cert-manager-webhook
) 的可能原因進行排除。
讓我們假設名稱 cert-manager-webhook.cert-manager.svc
已解析為 10.43.183.232。這是一個叢集 IP。執行 API 伺服器處理程序的控制平面節點會使用其 iptables 來使用 pod IP 重寫 IP 目的地。這可能是第一個問題:有時,沒有 pod IP 與給定的叢集 IP 相關聯,因為只要就緒探測器不起作用,kubelet 就不會使用 pod IP 填寫 Endpoint 資源。
讓我們首先檢查它是否是 Endpoint 資源的問題
kubectl get endpoints -n cert-manager cert-manager-webhook
有效的輸出會像這樣
NAME ENDPOINTS AGEcert-manager-webhook 10.244.0.2:10250 27d ✅
如果您有這個有效的輸出,並且有 connect: connection refused
,則問題會更深入網路堆疊。我們不會深入研究此案例,但您可能會想使用 tcpdump
和 Wireshark 來查看流量是否正確地從 API 伺服器流向節點的主機命名空間。從主機命名空間到 pod 命名空間的流量已經正常運作,因為 kubelet 已經能夠到達就緒端點。
常見問題包括防火牆丟棄從控制平面到工作站的流量;例如,GKE 上的 API 伺服器只允許透過連接埠 10250
與工作站節點 (執行 cert-manager webhook 的位置) 通訊。在 EKS 中,您的安全性群組可能會拒絕從您的控制平面 VPC 到您的工作站 VPC 透過 TCP 10250
的流量。
如果您看到 <none>
,表示 cert-manager webhook 正在正常執行,但無法到達其就緒端點
NAME ENDPOINTS AGEcert-manager-webhook <none> 236d ❌
若要修正 <none>
,您必須檢查 cert-manager-webhook 部署是否健康。當 cert-manager-webhook 未標示為 healthy
時,端點會保持在 <none>
。
kubectl get pod -n cert-manager -l app.kubernetes.io/name=webhook
您應該會看到 pod 正在 Running
,並且就緒的容器數目為 0/1
NAME READY STATUS RESTARTS AGEcert-manager-76578c9687-24kmr 0/1 Running 7 (8h ago) 28d ❌
我們不會詳細說明您取得 1/1
和 Running
的情況,因為這表示 Kubernetes 中的狀態不一致。
繼續使用 0/1
,這表示就緒端點沒有回應。當發生這種情況時,不會建立任何端點。下一步是找出為什麼就緒端點沒有回應。讓我們看看 kubelet 在點擊就緒端點時使用哪個連接埠
kubectl -n cert-manager get deploy cert-manager-webhook -oyaml | grep -A5 readiness
在我們的範例中,kubelet 將嘗試點擊的連接埠是 6080
readinessProbe:failureThreshold: 3httpGet:path: /healthzport: 6080 # ✨scheme: HTTP
現在,讓我們將連接埠轉送至該連接埠,看看 /healthz
是否正常運作。在 shell 工作階段中,執行
kubectl -n cert-manager port-forward deploy/cert-manager-webhook 6080
在另一個 shell 工作階段中,執行
curl -sS --dump-header - 127.0.0.1:6080/healthz
正常的輸出是
HTTP/1.1 200 OK ✅Date: Tue, 07 Jun 2022 17:16:56 GMTContent-Length: 0
如果就緒端點無法運作,您會看到
curl: (7) Failed to connect to 127.0.0.1 port 6080 after 0 ms: Connection refused ❌
此時,請驗證就緒端點是否在同一個連接埠上設定。讓我們看看記錄,以檢查我們的 webhook 是否正在接聽連接埠 6080 以取得其就緒端點
$ kubectl logs -n cert-manager -l app.kubernetes.io/name=webhook | head -10I0607 webhook.go:129] "msg"="using dynamic certificate generating using CA stored in Secret resource"I0607 server.go:133] "msg"="listening for insecure healthz connections" "address"=":6081" ❌I0607 server.go:197] "msg"="listening for secure connections" "address"=":10250"I0607 dynamic_source.go:267] "msg"="Updated serving TLS certificate"...
在上面的範例中,問題是就緒連接埠的設定錯誤。在 webhook 部署中,引數 --healthz-port=6081
與就緒設定不符。
錯誤:i/o timeout
(連線問題)
這個錯誤訊息在 Slack 上回報了 26 次。若要列出這些訊息,請使用
in:#cert-manager in:#cert-manager-dev "443: i/o timeout"
進行搜尋。這個錯誤訊息在 2 個 GitHub issue 中被回報 (#2811, #4073)
Error from server (InternalError): error when creating "STDIN": Internal error occurred:failed calling webhook "webhook.cert-manager.io": failed to call webhook:Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s":dial tcp 10.0.0.69:443: i/o timeout
當 API 伺服器嘗試與 cert-manager webhook 通訊時,SYN
封包永遠不會被回應,導致連線逾時。如果我們在 webhook 的網路命名空間內執行 tcpdump,我們會看到
192.168.1.43 (apiserver) -> 10.0.0.69 (webhook pod) TCP 44772 → 443 [SYN]192.168.1.43 (apiserver) -> 10.0.0.69 (webhook pod) TCP [TCP Retransmission] 44772 → 443 [SYN]192.168.1.43 (apiserver) -> 10.0.0.69 (webhook pod) TCP [TCP Retransmission] 44772 → 443 [SYN]192.168.1.43 (apiserver) -> 10.0.0.69 (webhook pod) TCP [TCP Retransmission] 44772 → 443 [SYN]
這個問題是由於 SYN
封包在某處被丟棄所造成的。
原因 1:GKE 私人叢集
預設的 Helm 設定應該適用於 GKE 私人叢集,但更改 securePort
可能會破壞它。
在上下文中,與公有 GKE 叢集(控制平面可以透過任何 TCP 連接埠自由地與 Pod 通訊)不同,私人 GKE 叢集中的控制平面只能透過 TCP 連接埠 10250
和 443
與工作節點中的 Pod 通訊。這兩個開放的連接埠是指 Pod 內的 containerPort
,而不是 Service 資源中稱為 port
的連接埠。
為了使其運作,Deployment 內的 containerPort
必須符合 10250
或 443
;containerPort
是由 Helm 值 webhook.securePort
所設定的。預設情況下,webhook.securePort
設定為 10250
。
若要查看 containerPort
是否有問題,讓我們先從 Service 資源開始查看
kubectl get svc -n cert-manager cert-manager-webhook -oyaml
查看輸出,我們看到 targetPort
設定為 "https"
apiVersion: v1kind: Servicemetadata:name: cert-manager-webhookspec:ports:- name: httpsport: 443 # ❌ This port is not the cause.protocol: TCPtargetPort: "https" # 🌟 This port might be the cause.
上述 port: 443
不可能是原因的原因是,kube-proxy (也在控制平面節點上運行) 會將 webhook 的叢集 IP 轉換為 pod IP,並將上述 port: 443
轉換為 containerPort
中的值。
若要查看目標連接埠 "https"
背後是什麼,我們查看 Deployment 資源
kubectl get deploy -n cert-manager cert-manager-webhook -oyaml | grep -A3 ports:
輸出顯示 containerPort
未設定為 10250
,這表示需要在 Google Cloud 中新增防火牆規則。
ports:- containerPort: 12345 # 🌟 This port matches neither 10250 nor 443.name: httpsprotocol: TCP
總結一下,如果上述 containerPort
不是 443
或 10250
,而且您不希望將 containerPort
變更為 10250
,則您必須新增防火牆規則。您可以閱讀 Google 文件中的 在 GKE 私人叢集中新增防火牆規則章節。
在上下文中,我們沒有將 securePort
預設為 443
的原因是,繫結到 443
需要一個額外的 Linux 功能 (NET_BIND_SERVICE
);另一方面,10250
不需要任何額外功能。
原因 2:在自訂 CNI 上使用 EKS
如果您在 EKS 上使用 Weave 或 Calico 等自訂 CNI,Kubernetes API 伺服器(位於自己的節點中)可能無法連線到 webhook Pod。發生這種情況的原因是,控制平面無法設定為在 EKS 上使用自訂 CNI 執行,這表示 CNI 無法啟用 API 伺服器與工作節點中運行的 Pod 之間的連線。
假設您正在使用 Helm,解決方法是在您的 values.yaml
檔案中新增以下值
webhook:hostNetwork: truesecurePort: 10260
或者,如果您從命令列使用 Helm,請使用以下標誌
--set webhook.hostNetwork=true --set webhook.securePort=10260
透過將 hostNetwork
設定為 true
,webhook Pod 將在主機的網路命名空間中運行。透過在主機的網路命名空間中運行,webhook Pod 可以透過節點的 IP 存取,這表示您將解決 kube-apiserver 無法連線到任何 Pod IP 或叢集 IP 的問題。
透過將 securePort
設定為 10260
,而不是依賴預設值 (為 10250
),您將防止 webhook 與 kubelet 之間發生衝突。kubelet 是在每個 Kubernetes 工作節點上直接在主機上運行的代理程式,它使用連接埠 10250
向 kube-apiserver 公開其內部 API。
若要了解 hostnetwork
和 securePort
如何互動,我們必須查看 TCP 連線如何建立。當 kube-apiserver 程序嘗試連線到 webhook Pod 時,kube-proxy(也在控制平面節點上運行,即使沒有 CNI)會啟動並將 webhook 的叢集 IP 轉換為 webhook 的主機 IP
https://cert-manager-webhook.cert-manager.svc:443/validate||Step 1: resolve to the cluster IPvhttps://10.43.103.211:443/validate||Step 2: send TCP packetvsrc: 172.28.0.1:43021dst: 10.43.103.211:443||Step 3: kube-proxy rewrite (cluster IP to host IP)vsrc: 172.28.0.1:43021dst: 172.28.0.2:10260|| control-plane node| (host IP: 172.28.0.1)------------|--------------------------------------------------| (host IP: 172.28.0.2)v worker node+-------------------+| webhook pod || listens on || 172.28.0.2:10260 |+-------------------+
使用 10250
作為預設 securePort
的原因是,它可以解決 GKE 私人叢集的另一個限制,如上述 GKE 私人叢集章節所述。
原因 3:網路原則,Calico
假設您正在使用 Helm 圖表,並且您正在使用 webhook.securePort
的預設值(為 10250
),並且您正在使用 Calico 等網路原則控制器,請檢查是否存在允許從 API 伺服器到 webhook Pod 的 TCP 連接埠 10250
的原則。
原因 4:EKS 和安全群組
假設您正在使用 Helm 圖表,並且您正在使用 webhook.securePort
的預設值(為 10250
),您可能需要檢查您的 AWS 安全群組是否允許從控制平面的 VPC 到工作程式 VPC 的 TCP 連接埠 10250
的流量。
其他原因
如果以上原因都不適用,您將需要找出 webhook 無法連線的原因。
若要偵錯連線問題(即封包被丟棄),我們建議在每個 TCP 跳躍點使用 tcpdump
以及 Wireshark。您可以參考文章 偵錯 Kubernetes 網路:我的 kube-dns
無法運作!,了解如何使用 tcpdump
與 Wireshark 偵錯網路問題。
錯誤:x509: 憑證對 xxx.internal 有效,而非 cert-manager-webhook.cert-manager.svc
(使用 Fargate Pod 的 EKS)
Internal error occurred: failed calling webhook "webhook.cert-manager.io":Post https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=30s:x509: certificate is valid for ip-192-168-xxx-xxx.xxx.compute.internal,not cert-manager-webhook.cert-manager.svc
此問題首次回報於 #3237。
這可能是因為您在啟用 Fargate 的 EKS 上執行。Fargate 為每個 Pod 建立一個微型 VM,而 VM 的核心用於在其自身的命名空間中執行容器。問題在於每個微型 VM 都有自己的 kubelet。對於任何 Kubernetes 節點,VM 的埠 10250
由 kubelet 程序監聽。並且 10250
也是 cert-manager webhook 監聽的埠。
但這不是問題:kubelet 程序和 cert-manager webhook 程序在兩個獨立的網路命名空間中執行,並且埠不會衝突。無論是在傳統的 Kubernetes 節點中,還是在 Fargate 微型 VM 內部,情況都是如此。
當 API 伺服器嘗試訪問 Fargate pod 時,問題就出現了:微型 VM 的主機網路命名空間被配置為轉發每個可能的埠,以最大限度地與傳統 pod 相容,如 Stack Overflow 頁面 EKS Fargate 連接到本地 kubelet 中所示。但是埠 10250
已被微型 VM 的 kubelet 使用,因此任何訪問此埠的內容都不會被轉發,而是會訪問 kubelet。
總而言之,cert-manager webhook 看起來是健康的,並且能夠根據其日誌監聽埠 10250
,但是微型 VM 的主機不會將 10250
轉發到 webhook 的網路命名空間。這就是為什麼在執行 TLS 交握時,您會看到有關出現意外網域的消息:雖然 cert-manager webhook 正在正確執行,但響應 API 伺服器的是 kubelet。
這是 Fargate 微型 VM 的限制:pod 的 IP 和節點的 IP 是相同的。它給您與傳統 pod 相同的體驗,但它帶來了網路方面的挑戰。
要解決此問題,訣竅是更改 cert-manager webhook 正在監聽的埠。使用 Helm,我們可以使用參數 webhook.securePort
helm install \cert-manager jetstack/cert-manager \--namespace cert-manager \--create-namespace \--version v1.16.1 \--set webhook.securePort=10260
錯誤:找不到服務 "cert-managercert-manager-webhook"
Error from server (InternalError): error when creating "test-resources.yaml": Internal error occurred:failed calling webhook "webhook.cert-manager.io": failed to call webhook:Post "https://cert-managercert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s":service "cert-managercert-manager-webhook" not found
我們不知道此錯誤的原因,如果您碰巧遇到此錯誤,請在上面的其中一個 GitHub 問題上發表評論。
錯誤:服務 "cert-manager-webhook" 沒有可用的端點
(OVHCloud)
Error: INSTALLATION FAILED: Internal error occurred:failed calling webhook "webhook.cert-manager.io":Post https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=30s:no endpoints available for service "cert-manager-webhook"
此問題首次在 Slack 中回報一次 (1)。
此錯誤很少見,只在 OVHcloud 管理的 Kubernetes 叢集中出現過,其中 etcd 資源配額相當低。etcd 是儲存 Kubernetes 資源 (例如 pod 和部署) 的資料庫。OVHCloud 限制了 etcd 中資源使用的磁碟空間。當達到限制時,整個叢集開始出現不穩定的行為,其中一個症狀是 kubelet 不會建立 Endpoint 資源。
若要驗證它是否確實是配額問題,您應該能夠在您的 kube-apiserver 日誌中看到以下訊息
rpc error: code = Unknown desc = ETCD storage quota exceededrpc error: code = Unknown desc = quota computation: etcdserver: not capablerpc error: code = Unknown desc = The OVHcloud storage quota has been reached
解決方法是移除一些資源 (例如 CertificateRequest 資源) 以低於限制,如 OVHCloud 的 ETCD 配額錯誤,疑難排解 頁面中所述。
錯誤:x509: 憑證已過期或尚未生效
此錯誤訊息在 Slack 中回報一次 (1)。
當使用 kubectl apply
時
Internal error occurred: failed calling webhook "webhook.cert-manager.io":Post https://kubernetes.default.svc:443/apis/webhook.cert-manager.io/v1beta1/mutations?timeout=30s:x509: certificate has expired or is not yet valid
此錯誤訊息在 Slack 中回報一次 (1)。
請回覆上述 Slack 訊息,因為我們仍然不確定是什麼原因導致此問題;若要存取 Kubernetes Slack,請造訪 https://slack.k8s.io/。
錯誤:net/http:等待連線時取消請求
Error from server (InternalError): error when creating "STDIN":Internal error occurred: failed calling webhook "webhook.cert-manager.io":Post https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=30s:net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
此錯誤訊息在 Slack 中回報一次 (1)。
錯誤:context deadline exceeded
此錯誤訊息在 GitHub 問題中回報 (2319、2706 5189、5004),以及一次 在 Stack Overflow 上。
當在安裝或升級 cert-manager 後嘗試套用發行者或任何其他 cert-manager 自訂資源時,此錯誤會出現在 cert-manager 0.12 及更高版本中
Error from server (InternalError): error when creating "STDIN":Internal error occurred: failed calling webhook "webhook.cert-manager.io":Post https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=30s:context deadline exceeded
ℹ️ 在較舊版本的 cert-manager (0.11 及更低版本) 中,webhook 依賴 APIService 機制,訊息看起來有點不同,但原因相同
Error from server (InternalError): error when creating "STDIN":Internal error occurred: failed calling webhook "webhook.certmanager.k8s.io":Post https://kubernetes.default.svc:443/apis/webhook.certmanager.k8s.io/v1beta1/mutations?timeout=30s:context deadline exceeded
ℹ️ 當使用
cmctl check api
時,也會出現訊息context deadline exceeded
。原因相同,您可以繼續閱讀本節以進行除錯。Not ready: Internal error occurred: failed calling webhook "webhook.cert-manager.io": failed to call webhook:Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s":context deadline exceeded
訊息 context deadline exceeded
的問題在於它模糊了超時的 HTTP 連線部分。當出現此訊息時,我們無法判斷 HTTP 互動的哪個部分超時。可能是 DNS 解析、TCP 交握、TLS 交握、傳送 HTTP 請求或接收 HTTP 回應。
ℹ️ 為了說明,您可以在上述錯誤訊息中看到的查詢參數
?timeout=30s
是 API 伺服器在呼叫 webhook 時決定的超時。它通常設定為 10 或 30 秒。
除錯此問題的第一步是確保 cert-manager 變更和驗證 webhook 配置上的 timeoutSeconds
欄位設定為 30 秒 (最大值)。預設情況下,它設定為 10 秒,這表示 context deadline exceeded
可能會隱藏其他超時訊息。若要檢查 timeoutSeconds
欄位的值,請執行
$ kubectl get mutatingwebhookconfigurations,validatingwebhookconfigurations cert-manager-webhook \-ojsonpath='{.items[*].webhooks[*].timeoutSeconds}'10 10
這表示兩個 webhook 都設定了 10 秒的內容超時。若要將其設定為 30 秒,請執行
kubectl patch mutatingwebhookconfigurations,validatingwebhookconfigurations cert-manager-webhook \--type=json -p '[{"op": "replace", "path": "/webhooks/0/timeoutSeconds", "value": 30}]'
下圖顯示了在 30 秒後擲出的全方位 context deadline exceeded
錯誤訊息 (由外框表示) 背後可能隱藏的三個錯誤
context deadline exceeded|30 seconds |timeout v+-------------------------------------------------------------------------+| || i/o timeout || | net/http: TLS handshake timeout || 10 seconds | | || timeout v | ||------------+ 30 seconds | net/http: request canceled ||TCP | timeout v while awaiting headers ||handshake +---------------------+ | ||------------| TLS | | || | handshake +------------+ 10 seconds | || +---------------------| sending | timeout v || | request +------------+ || +------------|receiving |------+ || |resp. header| recv.| || +------------+ resp.| || | body +-----+| +------|other|| |logic|| +-----++-------------------------------------------------------------------------+
在本節的其餘部分,我們將嘗試觸發三個「更具體」的錯誤之一
i/o timeout
是 TCP 握手逾時,來自 Kubernetes apiserver 中的DialTimeout
。名稱解析可能是原因,但通常,此訊息會在 API 伺服器發送SYN
封包並等待 10 秒以從 cert-manager webhook 接收SYN-ACK
封包後出現。net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
是 HTTP 回應逾時,來自 這裡,並設定為 30 秒。Kubernetes API 伺服器已經發送 HTTP 請求,並且正在等待 HTTP 回應標頭(例如,HTTP/1.1 200 OK
)。net/http: TLS handshake timeout
是在 TCP 握手完成時,且 Kubernetes API 伺服器已發送初始 TLS 握手封包(ClientHello
)並等待 10 秒,讓 cert-manager webhook 以ServerHello
封包回應。
我們可以將這三則訊息分為兩類:一種是連線問題(SYN
被丟棄),另一種是 webhook 問題(即 TLS 憑證錯誤,或 webhook 沒有回傳任何 HTTP 回應)。
逾時訊息 | 類別 |
---|---|
i/o timeout | 連線問題 |
net/http: TLS handshake timeout | webhook 端問題 |
net/http: request canceled while awaiting headers | webhook 端問題 |
第一步是排除 webhook 端問題。在您的 shell 工作階段中,執行以下命令
kubectl -n cert-manager port-forward deploy/cert-manager-webhook 10250
在另一個 shell 工作階段中,檢查您是否可以連線到 webhook
curl -vsS --resolve cert-manager-webhook.cert-manager.svc:10250:127.0.0.1 \--service-name cert-manager-webhook-ca \--cacert <(kubectl -n cert-manager get secret cert-manager-webhook-ca -ojsonpath='{.data.ca\.crt}' | base64 -d) \https://cert-manager-webhook.cert-manager.svc:10250/validate 2>&1 -d@- <<'EOF' | sed '/^* /d; /bytes data]$/d; s/> //; s/< //'{"kind":"AdmissionReview","apiVersion":"admission.k8s.io/v1","request":{"requestKind":{"group":"cert-manager.io","version":"v1","kind":"Certificate"},"requestResource":{"group":"cert-manager.io","version":"v1","resource":"certificates"},"name":"foo","namespace":"default","operation":"CREATE","object":{"apiVersion":"cert-manager.io/v1","kind":"Certificate","spec":{"dnsNames":["foo"],"issuerRef":{"group":"cert-manager.io","kind":"Issuer","name":"letsencrypt"},"secretName":"foo","usages":["digital signature"]}}}}EOF
正確的輸出看起來像這樣
POST /validate HTTP/1.1Host: cert-manager-webhook.cert-manager.svc:10250User-Agent: curl/7.83.0Accept: */*Content-Length: 1299Content-Type: application/x-www-form-urlencodedHTTP/1.1 200 OKDate: Wed, 08 Jun 2022 14:52:21 GMTContent-Length: 2029Content-Type: text/plain; charset=utf-8..."response": {"uid": "","allowed": true}
如果回應顯示 200 OK
,我們可以排除 webhook 端問題。由於初始錯誤訊息是 context deadline exceeded
,而不是 apiserver 端問題,例如 x509: certificate signed by unknown authority
或 x509: certificate has expired or is not yet valid
,我們可以得出結論,問題是連線問題:Kubernetes API 伺服器無法建立到 cert-manager webhook 的 TCP 連線。請依照上方「錯誤:i/o timeout
(連線問題)」章節中的指示繼續偵錯。
錯誤:net/http: TLS handshake timeout
此錯誤訊息曾在 1 個 GitHub 問題中回報 (#2602)。
Error from server (InternalError): error when creating "STDIN":Internal error occurred: failed calling webhook "webhook.cert-manager.io":Post https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=30s:net/http: TLS handshake timeout
查看以上圖表,此錯誤訊息表示 Kubernetes API 伺服器已成功建立到與 cert-manager webhook 關聯的 Pod IP 的 TCP 連線。TLS 握手逾時表示 cert-manager webhook 程序並非結束 TCP 連線的那方:中間有一些 HTTP Proxy,可能正在等待純 HTTP 請求而不是 ClientHello
封包。
我們不知道此錯誤的原因。如果您注意到此錯誤,請在上述 GitHub 問題中留言。
錯誤:HTTP probe failed with statuscode: 500
此錯誤訊息在 cert-manager webhook 上顯示為事件
Warning Unhealthy <invalid> (x13 over 15s) kubelet, node83Readiness probe failed: HTTP probe failed with statuscode: 500
我們不知道此錯誤的原因。如果您注意到此錯誤,請在上述 GitHub 問題中留言。
錯誤:Service Unavailable
此錯誤曾在 1 個 GitHub 問題中回報 (#4281)
Error from server (InternalError): error when creating "STDIN": Internal error occurred:failed calling webhook "webhook.cert-manager.io":Post "https://my-cert-manager-webhook.default.svc:443/mutate?timeout=10s":Service Unavailable
上述訊息會在使用 Weave CNI 的 Kubernetes 叢集中出現。
我們不知道此錯誤的原因。如果您注意到此錯誤,請在上述 GitHub 問題中留言。
錯誤:failed calling admission webhook: the server is currently unable to handle the request
Error from server (InternalError): error when creating "test-resources.yaml": Internal error occurred:failed calling admission webhook "issuers.admission.certmanager.k8s.io":the server is currently unable to handle the request
我們不知道此錯誤的原因。如果您能夠重現此錯誤,請在上述其中一個 GitHub 問題中留言。
錯誤:x509: certificate signed by unknown authority
在 GitHub 問題中回報 (2602)
當安裝或升級 cert-manager 並使用不是 cert-manager
的命名空間時
Error: UPGRADE FAILED: release core-l7 failed, and has been rolled back due to atomic being set:failed to create resource: conversion webhook for cert-manager.io/v1alpha3, Kind=ClusterIssuer failed:Post https://cert-manager-webhook.core-l7.svc:443/convert?timeout=30s:x509: certificate signed by unknown authority
當建立 Issuer 或任何其他 cert-manager 自訂資源時,可能會顯示非常相似的錯誤訊息
Internal error occurred: failed calling webhook "webhook.cert-manager.io":Post https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=30s:x509: certificate signed by unknown authority`
使用 cmctl install
和 cmctl check api
,您可能會看到以下錯誤訊息
2022/06/06 15:36:30 Not ready: the cert-manager webhook CA bundle is not injected yet(Internal error occurred: conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed:Post "https://<company_name>-cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s":x509: certificate signed by unknown authority)
如果您使用 cert-manager 0.14 及更早版本搭配 Helm,且您安裝在與 cert-manager
不同的命名空間中,則 CRD 清單的命名空間名稱會硬式編碼為 cert-manager
。您可以在以下註釋中看到硬式編碼的命名空間
kubectl get crd issuers.cert-manager.io -oyaml | grep inject
您將看到以下內容
cert-manager.io/inject-ca-from-secret: cert-manager/cert-manager-webhook-ca# ^^^^^^^^^^^^# hardcoded
注意 1: cert-manager Helm 圖表中的這個錯誤在 cert-manager 0.15 中已修復。
注意 2:自 cert-manager 1.6 起,由於不再需要轉換,此註釋已不再使用於 cert-manager CRD 上。
如果您仍在使用 cert-manager 0.14 或更早版本,解決方案是使用 helm template
呈現清單,然後編輯註釋以使用正確的命名空間,然後使用 kubectl apply
安裝 cert-manager。
如果您使用 cert-manager 1.6 及更早版本,問題可能是由於 cainjector 卡住,試圖將 cert-manager webhook 建立並儲存在 Secret 資源 cert-manager-webhook-ca
中的自簽憑證注入到 cert-manager CRD 的 spec.caBundle
欄位中。第一步是檢查 cainjector 是否正常執行
$ kubectl -n cert-manager get pods -l app.kubernetes.io/name=cainjectorNAME READY STATUS RESTARTS AGEcert-manager-cainjector-5c55bb7cb4-6z4cf 1/1 Running 11 (31h ago) 28d
查看日誌,您將能夠判斷領導者選舉是否成功。領導者選舉完成最多可能需要一分鐘。
I0608 start.go:126] "starting" version="v1.8.0" revision="e466a521bc5455def8c224599c6edcd37e86410c"I0608 leaderelection.go:248] attempting to acquire leader lease kube-system/cert-manager-cainjector-leader-election...I0608 leaderelection.go:258] successfully acquired lease kube-system/cert-manager-cainjector-leader-electionI0608 controller.go:186] cert-manager/secret/customresourcedefinition/controller/controller-for-secret-customresourcedefinition "msg"="Starting Controller"I0608 controller.go:186] cert-manager/certificate/customresourcedefinition/controller/controller-for-certificate-customresourcedefinition "msg"="Starting Controller"I0608 controller.go:220] cert-manager/secret/customresourcedefinition/controller/controller-for-secret-customresourcedefinition "msg"="Starting workers" "worker count"=1I0608 controller.go:220] cert-manager/certificate/customresourcedefinition/controller/controller-for-certificate-customresourcedefinition "msg"="Starting workers" "worker count"=1
正常的輸出會包含像這樣的行
I0608 sources.go:184] cert-manager/secret/customresourcedefinition/generic-inject-reconciler"msg"="Extracting CA from Secret resource" "resource_name"="issuers.cert-manager.io" "secret"="cert-manager/cert-manager-webhook-ca"I0608 controller.go:178] cert-manager/secret/customresourcedefinition/generic-inject-reconciler"msg"="updated object" "resource_name"="issuers.cert-manager.io"
現在,尋找任何表明由 cert-manager webhook 建立的 Secret 資源無法載入的訊息。可能會出現的兩個錯誤訊息是
E0608 sources.go:201] cert-manager/secret/customresourcedefinition/generic-inject-reconciler"msg"="unable to fetch associated secret" "error"="Secret \"cert-manager-webhook-caq\" not found"
以下訊息表示已略過給定的 CRD,因為缺少註釋。您可以忽略這些訊息
I0608 controller.go:156] cert-manager/secret/customresourcedefinition/generic-inject-reconciler"msg"="failed to determine ca data source for injectable" "resource_name"="challenges.acme.cert-manager.io"
如果 cainjector 日誌看起來沒有任何問題,您會想要檢查驗證、變更和轉換配置中的 spec.caBundle
欄位是否正確。Kubernetes API 伺服器會使用該欄位的內容來信任 cert-manager webhook。caBundle
包含 cert-manager webhook 啟動時建立的自簽 CA。
$ kubectl get validatingwebhookconfigurations cert-manager-webhook -ojson | jq '.webhooks[].clientConfig'{"caBundle": "LS0tLS1...LS0tLS0K","service": {"name": "cert-manager-webhook","namespace": "cert-manager","path": "/validate","port": 443}}
$ kubectl get mutatingwebhookconfigurations cert-manager-webhook -ojson | jq '.webhooks[].clientConfig'{"caBundle": "LS0tLS1...RFLS0tLS0K","service": {"name": "cert-manager-webhook","namespace": "cert-manager","path": "/validate","port": 443}}
讓我們看看 caBundle
的內容
$ kubectl get mutatingwebhookconfigurations cert-manager-webhook -ojson \| jq '.webhooks[].clientConfig.caBundle' -r | base64 -d \| openssl x509 -noout -text -in -Certificate:Data:Version: 3 (0x2)Serial Number:ee:8f:4f:c8:55:7b:16:76:d8:6a:a2:e5:94:bc:7c:6bSignature Algorithm: ecdsa-with-SHA384Issuer: CN = cert-manager-webhook-caValidityNot Before: May 10 16:13:37 2022 GMTNot After : May 10 16:13:37 2023 GMTSubject: CN = cert-manager-webhook-ca
讓我們檢查 caBundle
的內容是否可用於連接到 webhook
$ kubectl -n cert-manager get secret cert-manager-webhook-ca -ojsonpath='{.data.ca\.crt}' \| base64 -d | openssl x509 -noout -text -in -Certificate:Data:Version: 3 (0x2)Serial Number:ee:8f:4f:c8:55:7b:16:76:d8:6a:a2:e5:94:bc:7c:6bSignature Algorithm: ecdsa-with-SHA384Issuer: CN = cert-manager-webhook-caValidityNot Before: May 10 16:13:37 2022 GMTNot After : May 10 16:13:37 2023 GMTSubject: CN = cert-manager-webhook-ca
我們的最後一個測試是嘗試使用此信任套件連接到 webhook。讓我們將 port-forward 連接到 webhook pod
kubectl -n cert-manager port-forward deploy/cert-manager-webhook 10250
在另一個 shell 工作階段中,使用以下命令發送 /validate
HTTP 請求
curl -vsS --resolve cert-manager-webhook.cert-manager.svc:10250:127.0.0.1 \--service-name cert-manager-webhook-ca \--cacert <(kubectl get validatingwebhookconfigurations cert-manager-webhook -ojson | jq '.webhooks[].clientConfig.caBundle' -r | base64 -d) \https://cert-manager-webhook.cert-manager.svc:10250/validate 2>&1 -d@- <<'EOF' | sed '/^* /d; /bytes data]$/d; s/> //; s/< //'{"kind":"AdmissionReview","apiVersion":"admission.k8s.io/v1","request":{"requestKind":{"group":"cert-manager.io","version":"v1","kind":"Certificate"},"requestResource":{"group":"cert-manager.io","version":"v1","resource":"certificates"},"name":"foo","namespace":"default","operation":"CREATE","object":{"apiVersion":"cert-manager.io/v1","kind":"Certificate","spec":{"dnsNames":["foo"],"issuerRef":{"group":"cert-manager.io","kind":"Issuer","name":"letsencrypt"},"secretName":"foo","usages":["digital signature"]}}}}EOF
您應該會看到成功的 HTTP 請求和回應
POST /validate HTTP/1.1Host: cert-manager-webhook.cert-manager.svc:10250User-Agent: curl/7.83.0Accept: */*Content-Length: 1299Content-Type: application/x-www-form-urlencodedHTTP/1.1 200 OKDate: Wed, 08 Jun 2022 16:20:45 GMTContent-Length: 2029Content-Type: text/plain; charset=utf-8...
錯誤:cluster scoped resource "mutatingwebhookconfigurations/" is managed and access is denied
此訊息在 GitHub 問題 3717 中回報。
在 GKE Autopilot 上安裝 cert-manager 時,您會看到以下訊息
Error: rendered manifests contain a resource that already exists. Unable to continue with install:could not get information about the resource:mutatingwebhookconfigurations.admissionregistration.k8s.io "cert-manager-webhook" is forbidden:User "XXXX" cannot get resource "mutatingwebhookconfigurations" in API group "admissionregistration.k8s.io" at the cluster scope:GKEAutopilot authz: cluster scoped resource "mutatingwebhookconfigurations/" is managed and access is denied
當在 GKE Autopilot 上使用 Kubernetes 1.20 及更舊版本時,會出現此錯誤訊息。這是由於 GKE Autopilot 中對變更 admission webhooks 的限制所致。
截至 2021 年 10 月,「快速」Autopilot 發布通道已為 Kubernetes 主節點推出 1.21 版本。透過 Helm chart 安裝可能會導致錯誤訊息,但據某些使用者回報,cert-manager 運作正常。歡迎提供意見反應和 PR。
錯誤:the namespace "kube-system" is managed and the request's verb "create" is denied
當在 GKE Autopilot 上使用 Helm 安裝 cert-manager 時,您會看到以下錯誤訊息
Not ready: the cert-manager webhook CA bundle is not injected yet
在此失敗之後,您應該仍然會看到三個 pod 正常運行
$ kubectl get pods -n cert-managerNAME READY STATUS RESTARTS AGEcert-manager-76578c9687-24kmr 1/1 Running 0 47mcert-manager-cainjector-b7d47f746-4799n 1/1 Running 0 47mcert-manager-webhook-7f788c5b6-mspnt 1/1 Running 0 47m
但是查看任何一個日誌,您都會看到以下錯誤訊息
E0425 leaderelection.go:334] error initially creating leader election record:leases.coordination.k8s.io is forbidden: User "system:serviceaccount:cert-manager:cert-manager-webhook"cannot create resource "leases" in API group "coordination.k8s.io" in the namespace "kube-system":GKEAutopilot authz: the namespace "kube-system" is managed and the request's verb "create" is denied
這是由於 GKE Autopilot 的限制所致。無法在 kube-system
命名空間中建立資源,而 cert-manager 使用廣為人知的 kube-system
來管理領導者選舉。為了繞過此限制,您可以告訴 Helm 使用不同的命名空間進行領導者選舉
helm install cert-manager jetstack/cert-manager --version 1.8.0 \--namespace cert-manager --create-namespace \--set global.leaderElection.namespace=cert-manager