
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> |
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 이 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 |
STATE 가 Exited 이고 ATTEMPT 숫자가 계속 증가하고 있습니다. kube-apiserver 가 시작하자마자 계속 죽고 있는 상태입니다.
컨테이너 ID 로 로그를 확인합니다.
> sudo crictl logs b2d1ea08a99ab |
WARN[0000] runtime connect using default endpoints: ... |
이 로그만으로는 인증서 문제인지 바로 파악하기 어렵습니다. 더 최근의 컨테이너 로그를 확인합니다.
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: |
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... |
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... |
클러스터가 완전히 중단된 상태여서
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 |
kubeconfig 갱신
인증서를 갱신하면 /etc/kubernetes/admin.conf 도 새로운 인증서로 업데이트됩니다.
홈 디렉토리의 kubeconfig 파일을 사용하고 있다면 이 파일도 교체해줘야 합니다.
mkdir -p $HOME/.kube |
이후 kubectl get nodes 등의 명령이 정상적으로 동작하는지 확인합니다.
5. 인증서 만료 방지
주기적으로 만료일 모니터링
sudo kubeadm certs check-expiration |
만료 30일 전부터 미리 갱신하는 것을 권장합니다.
kubeadm upgrade 시 자동 갱신
kubeadm upgrade apply 를 실행하면 인증서도 함께 자동으로 갱신됩니다.
1년에 한 번 이상 쿠버네티스 버전을 업그레이드하고 있다면 인증서 만료 문제를 자연스럽게 피할 수 있습니다.
cron 으로 자동 갱신 등록
버전 업그레이드 없이 장기 운영하는 클러스터라면 cron 으로 연간 자동 갱신을 등록할 수 있습니다.
# /etc/cron.d/kubeadm-cert-renew |
위 예시는 매년 1월 1일 새벽 3시에 인증서를 갱신하고 kubelet 을 재시작합니다.