[쿠버네티스] Kubernetes 인증서들의 만료로 인한 접속 불가 및 해결

1. 원인 - kubeadm 인증서의 유효기간

kubeadm 으로 구성한 쿠버네티스 클러스터는 내부 통신에 사용하는 TLS 인증서들을 자동으로 생성합니다.
그런데 이 인증서들의 기본 유효기간이 1년 으로 설정되어 있습니다.

반면 CA (Certificate Authority) 인증서는 10년 유효기간으로 발급되기 때문에, 매년 하위 인증서들만 갱신해줘야 합니다.

클러스터를 운영하다 보면 이 1년이라는 만료 시점을 놓쳐서 갑자기 클러스터 접속이 불가능해지는 상황이 발생할 수 있습니다.

2. 상황 파악 - 쿠버네티스 클러스터 접속 안됨

외부에서 쿠버네티스 클러스터로 접속이 안돼 쿠버네티스 control plane 이 설치된 node 로 가서 kubelet 로그를 확인해 보니 다음과 같이 TLS 핸드셰이크시 오류가 발생하는 로그가 찍히고 있었습니다.

kubelet 로그 확인

> sudo systemctl status kubelet
kubelet[1435]: I0222 08:40:59.656171 1435 log.go:245] http: TLS handshake error from 101.198.0.183:19517: tls: client requested unsupported application protocols ([http/0.9 http/1.0 spdy/1 spdy/2 spdy/3>
kubelet[1435]: I0222 08:40:59.758452 1435 log.go:245] http: TLS handshake error from 101.198.0.135:34088: tls: client requested unsupported application protocols ([hq h2c spdy/3 spdy/2 spdy/1 http/1.0 h>
kubelet[1435]: I0222 08:40:59.857735 1435 log.go:245] http: TLS handshake error from 101.198.0.172:36328: tls: client offered only unsupported versions: [302 301]

TLS 핸드셰이크 오류가 발생하고 있습니다. 클라이언트와 서버 간에 TLS 통신 자체가 성립되지 않는 상태입니다.

kubelet[11937]: E0222 14:39:14.689141 11937 kubelet_node_status.go:96] "Unable to register node with API server" err="Post \"https://x.x.x.x:6443/api/v1/nodes\": dial tcp x.x.x.x:6443: co>
kubelet[11937]: E0222 14:39:15.701380 11937 controller.go:145] "Failed to ensure lease exists, will retry" err="Get \"https://x.x.x.x:6443/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/l>

kubelet 이 API 서버에 노드 등록을 시도하고 있으나 계속 실패하고 있습니다.
kube-node-lease 는 kubelet 이 주기적으로 heartbeat 를 보내 노드의 생존 여부를 알리는 리소스인데, 이것도 갱신에 실패하고 있습니다.

containerd 로그 확인

> sudo systemctl status containerd
containerd[14153]: time="2026-02-22T15:11:52.036751485+09:00" level=error msg="StopPodSandbox for \"baf53c...\" failed" error="failed to destroy network for sandbox \"...\": plugin type=\"calico\" failed (delete): error getting ClusterInformation: Get \"https://10.96.0.1:443/apis/crd.projectcalico.org/v1/clusterinformations/default\": dial tcp 10.96.0.1:443: connect: no route to host"

CNI 플러그인인 Calico 가 API 서버(10.96.0.1 은 kubernetes service 의 ClusterIP)와 통신하지 못하고 있습니다.
API 서버가 제대로 동작하지 않으니 네트워크 플러그인도 연쇄적으로 실패하고 있는 상태입니다.

kube-apiserver 컨테이너 상태 확인

kube-apiserver 가 계속 재시작되고 있는지 확인합니다.

sudo crictl ps -a | grep kube-apiserver
CONTAINER           IMAGE               CREATED             STATE               NAME                              ATTEMPT             POD ID              POD
b2d1ea08a99ab 41955df92b279 47 seconds ago Exited kube-apiserver 8 07a998d525e5a kube-apiserver
ffed6161e7c84 41955df92b279 10 minutes ago Exited kube-apiserver 7 ae5cb5a56b83c kube-apiserver
41bb608cefd93 41955df92b279 25 minutes ago Exited kube-apiserver 41 aed1231d5192d kube-apiserver

STATEExited 이고 ATTEMPT 숫자가 계속 증가하고 있습니다. kube-apiserver 가 시작하자마자 계속 죽고 있는 상태입니다.

컨테이너 ID 로 로그를 확인합니다.

> sudo crictl logs b2d1ea08a99ab
WARN[0000] runtime connect using default endpoints: ...
ERRO[0000] validate service connection: ...
E0222 06:08:08.552909 1 run.go:74] "command failed" err="oidc-issuer-url and oidc-client-id must be specified together when any oidc-* flags are set"

이 로그만으로는 인증서 문제인지 바로 파악하기 어렵습니다. 더 최근의 컨테이너 로그를 확인합니다.

3. 원인 확인 - x509 인증서 만료

> sudo crictl logs c70cce04c44dd
W0222 06:12:12.365718 1 logging.go:59] [core] [Channel #2 SubChannel #4] grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2026-02-22T06:12:12Z is after 2026-02-20T16:16:45Z"

핵심 에러 메시지는 다음과 같습니다.

tls: failed to verify certificate: x509: certificate has expired or is not yet valid:
current time 2026-02-22T06:12:12Z is after 2026-02-20T16:16:45Z

kube-apiserver 가 etcd(127.0.0.1:2379)에 접속하려 했는데, 인증서가 이미 만료되어 TLS 연결 자체가 실패하고 있습니다.
2026-02-20T16:16:45Z 에 만료되었는데, 현재 시각(2026-02-22T06:12:12Z)은 그보다 이후임을 알 수 있습니다.

전체 인증서 만료 현황 확인

> sudo kubeadm 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 -o yaml'

[check-expiration] Error reading configuration from the Cluster. Falling back to default configuration

CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Feb 20, 2026 16:16 UTC <invalid> ca no
apiserver Feb 20, 2026 16:16 UTC <invalid> ca no
apiserver-etcd-client Feb 20, 2026 16:16 UTC <invalid> etcd-ca no
apiserver-kubelet-client Feb 20, 2026 16:16 UTC <invalid> ca no
controller-manager.conf Feb 20, 2026 16:16 UTC <invalid> ca no
etcd-healthcheck-client Feb 20, 2026 16:16 UTC <invalid> etcd-ca no
etcd-peer Feb 20, 2026 16:16 UTC <invalid> etcd-ca no
etcd-server Feb 20, 2026 16:16 UTC <invalid> etcd-ca no
front-proxy-client Feb 20, 2026 16:16 UTC <invalid> front-proxy-ca no
scheduler.conf Feb 20, 2026 16:16 UTC <invalid> ca no
super-admin.conf Feb 20, 2026 16:16 UTC <invalid> ca no

CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Feb 18, 2035 16:16 UTC 8y no
etcd-ca Feb 18, 2035 16:16 UTC 8y no
front-proxy-ca Feb 18, 2035 16:16 UTC 8y no

RESIDUAL TIME<invalid> 로 표시된 인증서들이 모두 만료된 상태입니다.
CERTIFICATE AUTHORITY 에 해당하는 CA 인증서들(ca, etcd-ca, front-proxy-ca)은 유효기간이 10년이라 아직 정상입니다.

각 인증서의 역할은 다음과 같습니다.

인증서 설명
apiserver kube-apiserver 가 클라이언트 요청을 받을 때 사용하는 서버 인증서
apiserver-etcd-client kube-apiserver 가 etcd 에 접속할 때 사용하는 클라이언트 인증서
apiserver-kubelet-client kube-apiserver 가 kubelet 에 접속할 때 사용하는 클라이언트 인증서
etcd-server etcd 서버 인증서
etcd-peer etcd 노드 간 통신에 사용하는 인증서
etcd-healthcheck-client etcd liveness probe 에 사용하는 인증서
front-proxy-client API aggregation layer 에서 사용하는 인증서
admin.conf kubectl 의 기본 관리자 kubeconfig 에 포함된 인증서
controller-manager.conf kube-controller-manager 가 API 서버에 인증할 때 사용하는 인증서
scheduler.conf kube-scheduler 가 API 서버에 인증할 때 사용하는 인증서
super-admin.conf 클러스터 bootstrapping 시 사용하는 최고 권한 kubeconfig 인증서

4. 문제 해결

인증서 일괄 갱신

kubeadm certs renew all 명령으로 만료된 인증서들을 한 번에 갱신할 수 있습니다.

sudo kubeadm 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 -o yaml'
[renew] Error reading configuration from the Cluster. Falling back to default configuration

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
certificate embedded in the kubeconfig file for the super-admin renewed

Done renewing certificates. You must restart the kube-apiserver, kube-controller-manager, kube-scheduler and etcd, so that they can use the new certificates.

클러스터가 완전히 중단된 상태여서 Error reading configuration from the Cluster 가 출력되지만, Falling back to default configuration 으로 기본 설정을 사용해 갱신을 진행합니다.

kubelet 및 containerd 재시작

인증서 갱신 후 새 인증서를 적용하려면 kube-apiserver, kube-controller-manager, kube-scheduler, etcd 를 재시작해야 합니다.
이 컴포넌트들은 Static Pod 로 동작하므로, kubelet 을 재시작하면 자동으로 재시작됩니다.

sudo systemctl restart containerd
sudo systemctl restart kubelet

kubeconfig 갱신

인증서를 갱신하면 /etc/kubernetes/admin.conf 도 새로운 인증서로 업데이트됩니다.
홈 디렉토리의 kubeconfig 파일을 사용하고 있다면 이 파일도 교체해줘야 합니다.

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

이후 kubectl get nodes 등의 명령이 정상적으로 동작하는지 확인합니다.

5. 인증서 만료 방지

주기적으로 만료일 모니터링

sudo kubeadm certs check-expiration

만료 30일 전부터 미리 갱신하는 것을 권장합니다.

kubeadm upgrade 시 자동 갱신

kubeadm upgrade apply 를 실행하면 인증서도 함께 자동으로 갱신됩니다.
1년에 한 번 이상 쿠버네티스 버전을 업그레이드하고 있다면 인증서 만료 문제를 자연스럽게 피할 수 있습니다.

cron 으로 자동 갱신 등록

버전 업그레이드 없이 장기 운영하는 클러스터라면 cron 으로 연간 자동 갱신을 등록할 수 있습니다.

# /etc/cron.d/kubeadm-cert-renew
0 3 1 1 * root kubeadm certs renew all && systemctl restart kubelet

위 예시는 매년 1월 1일 새벽 3시에 인증서를 갱신하고 kubelet 을 재시작합니다.

Share