Kubernetesクラスタ(kubeadmで構築)の証明書期限切れ
1年以上前に作成したKubernetesクラスタを久しぶりに起動したら、「an error on the server ("") has prevented the request from succeeding」というエラーメッセージが出て使用できなくなっていた。
# kubectl get pod Error from server (InternalError): an error on the server ("") has prevented the request from succeeding
結果としては、クラスタ内の証明書有効期限切れが原因だった。
調査・対処結果を残しておく。
環境
・Kubernetes:1.18.9
・kubeadm:1.18.9
・Docker:19.03.8
・Calico:v3.16.1
・OS:CentOS7.7
Kubernetes構成
kubeadmで構築した高可用性クラスタ。
マスターノード3台、ワーカーノード3台の構成。
原因調査
状態の確認
kubectlを実行するとエラーが出る。
# kubectl get pod Error from server (InternalError): an error on the server ("") has prevented the request from succeeding # kubectl version Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.9", GitCommit:"94f372e501c973a7fa9eb40ec9ebd2fe7ca69848", GitTreeState:"clean", BuildDate:"2020-09-16T13:56:40Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"} Error from server (InternalError): an error on the server ("") has prevented the request from succeeding
kubeletの状態を確認すると、起動はしているがエラーメッセージ「Unable to write event: 'Post https://dev-klb-001:6443/api/v1/namespaces/default/events: EOF' (may retry after sleeping)」が出ている。
kube-apiserverに接続出来てなさそう。
※このクラスタは、Load Balancer(dev-klb-001)経由で各マスターノード上で稼働するkube-apiserverに接続する構成。
# systemctl status kubelet -l ● kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled) Drop-In: /usr/lib/systemd/system/kubelet.service.d mq10-kubeadm.conf Active: active (running) since 火 2022-03-29 23:20:04 JST; 2min 31s ago Docs: https://kubernetes.io/docs/ Main PID: 1393 (kubelet) Tasks: 28 Memory: 165.0M CGroup: /system.slice/kubelet.service tq1393 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=cgroupfs --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2 mq4838 /opt/cni/bin/calico 3月 29 23:22:34 dev-k8s-001 kubelet[1393]: E0329 23:22:34.726943 1393 event.go:269] Unable to write event: 'Post https://dev-klb-001:6443/api/v1/namespaces/default/events: EOF' (may retry after sleeping) 3月 29 23:22:34 dev-k8s-001 kubelet[1393]: E0329 23:22:34.780032 1393 kubelet.go:2270] node "dev-k8s-001" not found 3月 29 23:22:34 dev-k8s-001 kubelet[1393]: E0329 23:22:34.880300 1393 kubelet.go:2270] node "dev-k8s-001" not found 3月 29 23:22:34 dev-k8s-001 kubelet[1393]: E0329 23:22:34.980527 1393 kubelet.go:2270] node "dev-k8s-001" not found
kube-apiserverのコンテナ(k8s_kube-apiserver_kube-apiserver-dev-k8s-001_kube-system~)がダウンしている。
# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f368632b3aea e9585e7d0849 "kube-apiserver --ad…" 43 seconds ago Exited (2) 22 seconds ago k8s_kube-apiserver_kube-apiserver-dev-k8s-001_kube-system_6b98960f9130958088f75ebb9d60bf0b_43220 184d8db8a792 c53af2e3d068 "kube-scheduler --au…" About a minute ago Up About a minute k8s_kube-scheduler_kube-scheduler-dev-k8s-001_kube-system_0b616cf167c119acab298fb3401e5b0d_17 d7180e8f9c79 k8s.gcr.io/pause:3.2 "/pause" About a minute ago Up About a minute k8s_POD_kube-scheduler-dev-k8s-001_kube-system_0b616cf167c119acab298fb3401e5b0d_14 e4bc371d116f dacf3f247065 "kube-controller-man…" About a minute ago Up About a minute k8s_kube-controller-manager_kube-controller-manager-dev-k8s-001_kube-system_c3d93a3b59f1ca8301ff46a4830eeaa7_18 298d73338ee9 303ce5db0e90 "etcd --advertise-cl…" About a minute ago Up About a minute k8s_etcd_etcd-dev-k8s-001_kube-system_0eadebe3593196dc3f27d3e05cc6c3a9_34724 ea2d8a47539b k8s.gcr.io/pause:3.2 "/pause" About a minute ago Up About a minute k8s_POD_kube-apiserver-dev-k8s-001_kube-system_6b98960f9130958088f75ebb9d60bf0b_12 adc2f556a505 k8s.gcr.io/pause:3.2 "/pause" About a minute ago Up About a minute k8s_POD_etcd-dev-k8s-001_kube-system_0eadebe3593196dc3f27d3e05cc6c3a9_13 127994367f83 k8s.gcr.io/pause:3.2 "/pause" About a minute ago Up About a minute k8s_POD_kube-controller-manager-dev-k8s-001_kube-system_c3d93a3b59f1ca8301ff46a4830eeaa7_13
ダウンしていたkube-apiserverコンテナのログを確認するが、よく分からず。
# docker logs f368632b3aea W0329 12:01:55.559292 1 clientconn.go:1208] grpc: addrConn.createTransport failed to connect to {https://127.0.0.1:2379 <nil> 0 <nil>}. Err :connection error: desc = "transport: authentication handshake failed: context deadline exceeded". Reconnecting... panic: context deadline exceeded goroutine 1 [running]: k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition.NewREST(0xc000700a10, 0x50ebdc0, 0xc000157d40, 0xc000157f68) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go:56 +0x3e7 k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver.completedConfig.New(0xc000a53ca0, 0xc00044d408, 0x51aa780, 0x774b858, 0x10, 0x0, 0x0) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go:145 +0x14ef k8s.io/kubernetes/cmd/kube-apiserver/app.createAPIExtensionsServer(0xc00044d400, 0x51aa780, 0x774b858, 0x0, 0x50eb980, 0xc0009767a0) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-apiserver/app/apiextensions.go:102 +0x59 k8s.io/kubernetes/cmd/kube-apiserver/app.CreateServerChain(0xc00087f080, 0xc000030360, 0x455f9f4, 0xc, 0xc0006d3c48) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-apiserver/app/server.go:181 +0x2b8 k8s.io/kubernetes/cmd/kube-apiserver/app.Run(0xc00087f080, 0xc000030360, 0x0, 0x0) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-apiserver/app/server.go:150 +0x101 k8s.io/kubernetes/cmd/kube-apiserver/app.NewAPIServerCommand.func1(0xc00089a000, 0xc000875380, 0x0, 0x1a, 0x0, 0x0) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-apiserver/app/server.go:117 +0x104 k8s.io/kubernetes/vendor/github.com/spf13/cobra.(*Command).execute(0xc00089a000, 0xc00004c1d0, 0x1a, 0x1b, 0xc00089a000, 0xc00004c1d0) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/spf13/cobra/command.go:826 +0x460 k8s.io/kubernetes/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0xc00089a000, 0x16e0d84e6630895f, 0x772d680, 0xc000068750) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/spf13/cobra/command.go:914 +0x2fb k8s.io/kubernetes/vendor/github.com/spf13/cobra.(*Command).Execute(...) /workspace/anago-v1.18.9-rc.0.79+8147d851af540a/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/spf13/cobra/command.go:864 main.main() _output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-apiserver/apiserver.go:43 +0xcd
原因
この後、ノードのOS再起動や、kubelet、Dockerの再起動を試したが、状況は変わらなかった。
以前は問題なく動作していたクラスタが時間経過で動作しなくなったので、証明書まわりが怪しそうだが、ググった感じ証明書期限切れのときは「Unable to connect to the server: x509: certificate has expired or is not yet valid」とストレートなメッセージが出そう。
念のため、証明書の状態を確認したところ、とっくに期限が切れていた。
# date 2022年 3月 29日 火曜日 23:26:51 JST # kubeadm alpha certs check-expiration [check-expiration] Reading configuration from the cluster... [check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' [check-expiration] Error reading configuration from the Cluster. Falling back to default configuration W0329 23:24:42.384030 6270 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED admin.conf Sep 26, 2021 08:27 UTC <invalid> no apiserver Sep 26, 2021 08:27 UTC <invalid> ca no apiserver-etcd-client Sep 26, 2021 08:27 UTC <invalid> etcd-ca no apiserver-kubelet-client Sep 26, 2021 08:27 UTC <invalid> ca no controller-manager.conf Sep 26, 2021 08:27 UTC <invalid> no etcd-healthcheck-client Sep 26, 2021 08:27 UTC <invalid> etcd-ca no etcd-peer Sep 26, 2021 08:27 UTC <invalid> etcd-ca no etcd-server Sep 26, 2021 08:27 UTC <invalid> etcd-ca no front-proxy-client Sep 26, 2021 08:27 UTC <invalid> front-proxy-ca no scheduler.conf Sep 26, 2021 08:27 UTC <invalid> no CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED ca Sep 24, 2030 08:27 UTC 8y no etcd-ca Sep 24, 2030 08:27 UTC 8y no front-proxy-ca Sep 24, 2030 08:27 UTC 8y no
Kubernetesの公式ドキュメントによると、以下の通り。
・kubeadmで生成される証明書の期限は1年。
・コントロールプレーンのアップグレード時にkubeadmは証明書を更新する。
・kubeadm certs renew
コマンドで手動で証明書更新が可能。
→Kubernetesを随時バージョンアップすれば問題ない。
とはいえ何らかの理由で1年以上バージョンアップできないケースもあると思う。
せっかく期限切れしたので、そのようなケースを想定して手動更新をやってみる。
証明書を手動更新する
クラスタの証明書を更新(マスターノード#1での作業)
公式の手順に従い、kubeadm certs renew
コマンドで証明書を手動更新する。
まずは、マスターノード全台で作業が必要とのことなので、まずはマスターノード#1で作業をする。
# kubeadm alpha certs renew all [renew] Reading configuration from the cluster... [renew] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' [renew] Error reading configuration from the Cluster. Falling back to default configuration W0329 23:40:55.707534 19090 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself renewed certificate for serving the Kubernetes API renewed certificate the apiserver uses to access etcd renewed certificate for the API server to connect to kubelet renewed certificate embedded in the kubeconfig file for the controller manager to use renewed certificate for liveness probes to healthcheck etcd renewed certificate for etcd nodes to communicate with each other renewed certificate for serving etcd renewed certificate for the front proxy client renewed certificate embedded in the kubeconfig file for the scheduler manager to use renewed
証明書の期限を確認すると、更新されている。
# kubeadm alpha certs check-expiration [check-expiration] Reading configuration from the cluster... [check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' [check-expiration] Error reading configuration from the Cluster. Falling back to default configuration W0329 23:51:57.412900 29201 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED admin.conf Mar 29, 2023 14:40 UTC 364d no apiserver Mar 29, 2023 14:40 UTC 364d ca no apiserver-etcd-client Mar 29, 2023 14:40 UTC 364d etcd-ca no apiserver-kubelet-client Mar 29, 2023 14:40 UTC 364d ca no controller-manager.conf Mar 29, 2023 14:40 UTC 364d no etcd-healthcheck-client Mar 29, 2023 14:40 UTC 364d etcd-ca no etcd-peer Mar 29, 2023 14:40 UTC 364d etcd-ca no etcd-server Mar 29, 2023 14:40 UTC 364d etcd-ca no front-proxy-client Mar 29, 2023 14:40 UTC 364d front-proxy-ca no scheduler.conf Mar 29, 2023 14:40 UTC 364d no CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED ca Sep 24, 2030 08:27 UTC 8y no etcd-ca Sep 24, 2030 08:27 UTC 8y no front-proxy-ca Sep 24, 2030 08:27 UTC 8y no
kubectlの証明書を更新(マスターノード#1での作業)
公式ドキュメントには明記されていないが、kubectlが使用する証明書は古いままなので、差し替える必要がある。
kubectlが使用する証明書の有効期限を確認すると、古いまま。
# cat ~/.kube/config | grep "client-certificate-data:" | awk '{ print $2 }' | base64 --decode > /tmp/crt.txt # openssl x509 -enddate -noout -in /tmp/crt.txt | cut -d= -f 2 Sep 26 08:27:31 2021 GMT
更新された「/etc/kubernetes/admin.conf」を「~/.kube/config」にコピーすることで更新する。
# cp /etc/kubernetes/admin.conf ~/.kube/config # cat ~/.kube/config | grep "client-certificate-data:" | awk '{ print $2 }' | base64 --decode > /tmp/crt_new.txt # openssl x509 -enddate -noout -in /tmp/crt_new.txt | cut -d= -f 2 Mar 29 14:40:56 2023 GMT
残りのマスターノードでの作業
公式ドキュメントではこの後、/etc/kubernetes/manifests/配下のマニフェストファイルを一度退避し戻すことで、Static Podを再起動する手順となっているが、実施しても再起動しなかった。
マスターノード全台での作業を完了させる必要があるのかもしれないと考え、残りのマスターノード#2、#3でもマスターノード#1と同様の作業を実行した。
マスターノード#2、#3での作業後少し待つと、全マスターノード上でkube-apiserver Pod含むstatic Podが起動した。
作業後の確認
kubectlが問題無く実行出来るようになった。
ワーカーノードについてもSTATUSがReadyになっており、クラスタへの再登録などは不要だった。
# kubectl get node NAME STATUS ROLES AGE VERSION dev-k8s-001 Ready master 549d v1.18.9 dev-k8s-002 Ready master 549d v1.18.9 dev-k8s-003 Ready master 549d v1.18.9 dev-k8s-101 Ready <none> 549d v1.18.9 dev-k8s-102 Ready <none> 549d v1.18.9 dev-k8s-103 Ready <none> 549d v1.18.9
まとめ
・エラーメッセージが「Unable to connect to the server: x509: certificate has expired or is not yet valid」でなくても、証明書関連の問題の場合がある。
・公式ドキュメントにはざっくりした更新手順しか書いていないが、kubectlの証明書更新や、高可用性クラスタの場合の作業など、注意が必要な点がある。
・プロダクション環境導入時には、証明書の更新についての検討が必要。
参考文献
Kubernetes公式ドキュメント kubeadmによる証明書管理
TLS証明書のエラーでkubectlが使用できなくなった
Kubernetes証明書を10年間の有効期間で更新します
Kubernetes証明書の期限切れによりクラスタ全体の通信が停止する