cert-manager Webhook Podの決定版デバッグガイド
最終確認日: 2022年9月8日
cert-manager webhookは、cert-managerのインストールの一部として実行されるPodです。 kubectl
でマニフェストを適用すると、Kubernetes APIサーバーはTLS経由でcert-manager webhookを呼び出してマニフェストを検証します。このガイドでは、Kubernetes APIサーバーとcert-manager webhook Pod間の通信問題をデバッグする方法について説明します。
このページに記載されているエラーメッセージは、cert-managerのインストールまたはアップグレード中、またはcert-managerのインストールまたはアップグレード直後にCertificate、Issuer、またはその他のcert-managerカスタムリソースを作成しようとしたときに発生します。
以下の図では、cert-manager webhookの問題をデバッグする際の一般的なパターンを示しています。cert-managerカスタムリソースを作成する際、APIサーバーはTLS経由でcert-manager webhook Podに接続します。赤いバツ印は、APIサーバーがwebhookとの通信に失敗することを示しています。

このドキュメントの残りの部分では、発生する可能性のあるエラーメッセージを紹介します。
エラー: connect: connection refused
この問題は、4つのGitHub issue(#2736, #3133, #3445, #4425)、外部プロジェクトの1つのGitHub issue(
aws-load-balancer-controller#1563
)、Stack Overflow(serverfault#1076563
)で報告されており、Slackメッセージではin:#cert-manager in:#cert-manager-dev ":443: connect: connection refused"
という検索で一覧表示できる13件のメッセージで言及されています。このエラーメッセージは、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
"connection refused"メッセージは、APIサーバーがcert-manager-webhookとのTCP接続を確立しようとしたときに発生します。TCPの用語では、APIサーバーはTCPハンドシェイクを開始するために SYN
パケットを送信し、代わりに 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]
RST
パケットは、要求されたポートをリッスンしているものがない場合に、Linuxカーネルによって送信されます。RST
パケットは、Stack Overflowのページ What can be the reasons of connection refused errors? で詳しく説明されているように、ファイアウォールなどのTCPホップのいずれかによっても返される可能性があります。
ファイアウォールは通常、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宛先を書き換えます。それが最初の問題である可能性があります。kubeletは、準備プローブが機能している限り、Pod IPでEndpointリソースを埋めないため、特定のクラスターIPにPod IPが関連付けられていない場合があります。
まず、Endpointリソースに問題があるかどうかを確認しましょう。
kubectl get endpoints -n cert-manager cert-manager-webhook
有効な出力は次のようになります
NAME ENDPOINTS AGEcert-manager-webhook 10.244.0.2:10250 27d ✅
この有効な出力があり、connect: connection refused
が発生する場合は、ネットワークスタックの奥深くで問題が発生しています。このケースについては詳しく説明しませんが、APIサーバーからノードのホスト名前空間へのトラフィックが適切に流れているかどうかを確認するために、tcpdump
と Wiresharkを使用するとよいでしょう。kubeletはすでに準備エンドポイントに到達できたため、ホスト名前空間からPod名前空間へのトラフィックはすでに正常に機能しています。
一般的な問題としては、コントロールプレーンからワーカーへのトラフィックをドロップするファイアウォールが挙げられます。たとえば、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が 正常
とマークされるまで、エンドポイントは <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
が機能するかどうかを確認します。シェルセッションで、次を実行します。
kubectl -n cert-manager port-forward deploy/cert-manager-webhook 6080
別のシェルセッションで、次を実行します。
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ポートでポッドと自由に通信できるのに対し、プライベートGKEクラスターでは、コントロールプレーンはワーカーノードのポッドとTCPポート10250
と443
でのみ通信できます。これらの2つの開いているポートは、Serviceリソースのport
というポートではなく、ポッド内のcontainerPort
を参照します。
動作させるには、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をポッド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
)が1つ必要になるためです。一方、10250
には追加の機能は必要ありません。
原因2:カスタムCNI上のEKS
EKSを使用しており、WeaveやCalicoなどのカスタムCNIを使用している場合、Kubernetes APIサーバー(独自のノードにある)がwebhookポッドに到達できない可能性があります。これは、EKSでコントロールプレーンをカスタムCNIで実行するように構成できないためです。つまり、CNIはAPIサーバーとワーカーノードで実行されているポッド間の接続を有効にできません。
Helmを使用していると仮定すると、回避策はvalues.yaml
ファイルに次の値を追加することです。
webhook:hostNetwork: truesecurePort: 10260
または、コマンドラインからHelmを使用している場合は、次のフラグを使用します。
--set webhook.hostNetwork=true --set webhook.securePort=10260
hostNetwork
をtrue
に設定すると、webhookポッドはホストのネットワーク名前空間で実行されます。ホストのネットワーク名前空間で実行することにより、webhookポッドはノードのIP経由でアクセス可能になります。つまり、kube-apiserverがポッドIPにもクラスターIPにも到達できないという事実を回避できます。
securePort
をデフォルト値(10250
)に頼るのではなく、10260
に設定すると、webhookとkubeletの間の競合を防ぐことができます。すべてのKubernetesワーカーノードで実行され、ホスト上で直接実行されるエージェントであるkubeletは、ポート10250
を使用して、内部APIをkube-apiserverに公開します。
hostnetwork
とsecurePort
がどのように相互作用するかを理解するには、TCP接続がどのように確立されるかを確認する必要があります。kube-apiserverプロセスがwebhookポッドに接続しようとすると、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などのネットワークポリシーコントローラーを使用している場合は、TCPポート10250
を介してAPIサーバーからwebhookポッドへのトラフィックを許可するポリシーが存在することを確認してください。
原因4:EKSとセキュリティグループ
Helmチャートを使用しており、webhook.securePort
のデフォルト値(10250
)を使用している場合は、AWSセキュリティグループが、コントロールプレーンのVPCからワーカーのVPCへの10250
経由のTCPトラフィックを許可していることを確認してください。
その他の原因
上記の原因のいずれも当てはまらない場合は、webhookが到達不能な理由を突き止める必要があります。
到達可能性の問題(つまり、パケットがドロップされる問題)をデバッグするには、すべてのTCPホップでtcpdump
とWiresharkを使用することをお勧めします。ネットワークの問題をデバッグするためにtcpdump
とWiresharkをどのように使用するかについては、Debugging Kubernetes Networking: my kube-dns
is not working! という記事を参照してください。
エラー: x509: certificate is valid for xxx.internal, not cert-manager-webhook.cert-manager.svc
(Fargateポッドを使用した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はポッドごとにマイクロVMを作成し、VMのカーネルを使用して、独自の名前空間でコンテナを実行します。問題は、各マイクロVMが独自のkubeletを取得することです。Kubernetesノードと同様に、VMのポート10250
はkubeletプロセスによってリッスンされます。そして、10250
は、cert-manager webhookがリッスンするポートでもあります。
しかし、それは問題ではありません。kubeletプロセスとcert-manager webhookプロセスは、2つの異なるネットワーク名前空間で実行されており、ポートが衝突することはありません。これは、従来のKubernetesノード内でも、FargateマイクロVM内でも同様です。
問題は、APIサーバーがFargateポッドにアクセスしようとするときに発生します。マイクロVMのホストネットワーク名前空間は、従来のポッドとの最大の互換性のために、すべての可能なポートをポート転送するように構成されています。これは、Stack OverflowのページEKS Fargate connect to local kubeletで説明されています。ただし、ポート10250
はマイクロVMのkubeletですでに使用されているため、このポートにアクセスするものはポート転送されず、代わりにkubeletにアクセスすることになります。
要約すると、cert-manager webhookは正常に見え、ログどおりポート10250
をリッスンできますが、マイクロVMのホストは10250
をwebhookのネットワーク名前空間にポート転送しません。TLSハンドシェイク時に予期しないドメインが表示されるというメッセージが表示されるのはそのためです。cert-manager webhookは正しく実行されていますが、APIサーバーに応答しているのはkubeletです。
これは、FargateのマイクロVMの制限です。ポッドのIPとノードのIPは同じです。これにより、従来のポッドと同じエクスペリエンスが得られますが、ネットワークに関する課題が発生します。
この問題を解決するには、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
エラー: service "cert-managercert-manager-webhook" not found
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 issueのいずれかにコメントしてください。
エラー: no endpoints available for service "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回(1)最初に報告されました。
このエラーはまれで、etcdリソースクォータが非常に低いOVHcloudマネージドKubernetesクラスターでのみ発生しました。etcdは、Kubernetesリソース(ポッドやデプロイメントなど)が保存されるデータベースです。OVHCloudは、etcd内のリソースが使用するディスクスペースを制限しています。制限に達すると、クラスター全体が不安定な動作を開始し、1つの症状として、kubeletによってエンドポイントリソースが作成されなくなります。
それが実際にクォータの問題であることを確認するには、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
回避策は、OVHCloudのETCD Quotas error, troubleshootingページで説明されているように、CertificateRequestリソースなどの一部のリソースを削除して、制限を下回ることです。
エラー: x509: certificate has expired or is not yet valid
このエラーメッセージは、Slackで1回(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回(1)報告されました。
この問題の原因をまだ特定できていないため、上記のSlackメッセージに回答してください。Kubernetes Slackにアクセスするには、https://slack.k8s.io/にアクセスしてください。
エラー: net/http: request canceled while waiting for connection
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回(1)報告されました。
エラー: context deadline exceeded
このエラーメッセージは、GitHub issue(2319、2706 5189、5004)およびStack Overflowで1回報告されています。
このエラーは、cert-managerをインストールまたはアップグレードした後、Issuerまたはその他の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
ℹ️
context deadline exceeded
というメッセージは、cmctl check api
を使用する場合にも表示されます。原因は同じであり、このセクションを読み進めてデバッグできます。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
エラーメッセージの背後に隠れている可能性のある 3 つのエラーを示しています。
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|| +-----++-------------------------------------------------------------------------+
このセクションの残りの部分では、3 つの「より具体的な」エラーのいずれかをトリガーしようとします。
i/o timeout
は TCP ハンドシェイクタイムアウトであり、Kubernetes API サーバーのDialTimeout
に由来します。名前解決が原因である可能性がありますが、通常、このメッセージは、API サーバーがSYN
パケットを送信し、cert-manager Webhook からSYN-ACK
パケットを受信するのを 10 秒間待った後に表示されます。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
)を送信し、cert-manager Webhook がServerHello
パケットで応答するのを 10 秒間待った場合に発生します。
これらの 3 つのメッセージは、接続性の問題(SYN
がドロップされた)か、Webhook の問題(つまり、TLS 証明書が間違っているか、Webhook が HTTP レスポンスを返していない)の 2 つのカテゴリに分類できます。
タイムアウトメッセージ | カテゴリ |
---|---|
i/o timeout | 接続性の問題 |
net/http: TLS handshake timeout | Webhook側の問題 |
net/http: request canceled while awaiting headers | Webhook側の問題 |
まず、Webhook側の問題を排除します。シェルセッションで、以下を実行します。
kubectl -n cert-manager port-forward deploy/cert-manager-webhook 10250
別のシェルセッションで、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
であり、x509: certificate signed by unknown authority
や x509: certificate has expired or is not yet valid
などの apiserver 側の問題ではないため、問題は接続性の問題であると結論付けることができます。つまり、Kubernetes API サーバーは cert-manager Webhook への TCP 接続を確立できません。上記にある エラー:i/o timeout
(接続性の問題) のセクションの手順に従って、デバッグを続けてください。
エラー:net/http: TLS handshake timeout
このエラーメッセージは、1 件の GitHub issue(#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 接続を終了するものではないことを意味します。おそらく、ClientHello
パケットではなく、プレーンな HTTP リクエストを待機している HTTP プロキシが間にある可能性があります。
このエラーの原因は不明です。このエラーが発生した場合は、上記の GitHub issue にコメントしてください。
エラー: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 issue にコメントしてください。
エラー:Service Unavailable
このエラーは、1 件の GitHub issue(#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 issue にコメントしてください。
エラー: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 issue にコメントしてください。
エラー:x509: certificate signed by unknown authority
GitHub issue(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)
Helm で cert-manager 0.14 以下を使用しており、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 以下を使用している場合、問題は、cert-manager Webhook が作成し、Secret リソース cert-manager-webhook-ca
に保存した自己署名証明書を、cert-manager CRD の spec.caBundle
フィールドに挿入しようとして、cainjector がスタックしていることが原因である可能性があります。最初のステップは、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
ログを見ると、リーダー選出が機能したかどうかを判断できます。リーダー選出の完了には最大 1 分かかる場合があります。
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リソースがロードできないことを示すメッセージを探してください。表示される可能性のあるエラーメッセージは次の2つです。
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のログに問題がないようであれば、validation、mutation、conversionの設定にある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への接続を試みます。webhookポッドにポートフォワードしましょう。
kubectl -n cert-manager port-forward deploy/cert-manager-webhook 10250
別のシェルセッションで、次のコマンドを使用して/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 issue 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
このエラーメッセージは、Kubernetes 1.20以下のバージョンでGKE Autopilotを使用している場合に表示されます。これは、GKE Autopilotにおけるmutating admission webhookの制限によるものです。
2021年10月現在、「rapid」Autopilotリリースチャネルでは、Kubernetesマスターのバージョン1.21が展開されています。Helmチャートによるインストールはエラーメッセージで終わる可能性がありますが、cert-managerは一部のユーザーによって動作することが報告されています。フィードバックとPRを歓迎します。
エラー: the namespace "kube-system" is managed and the request's verb "create" is denied
Helmを使用してGKE Autopilotにcert-managerをインストールすると、次のエラーメッセージが表示されます。
Not ready: the cert-manager webhook CA bundle is not injected yet
この失敗の後も、3つのポッドは正常に実行されているはずです。
$ 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