kubeadm 으로 만든 클러스터는 컨트롤 플레인 인증서의 유효 기간이 1년이다. 한 해를 넘기면 다음과 같은 로그와 함께 클러스터가 응답하지 않는다.
E0325 authentication.go:73] "Unable to authenticate the request"
err="[x509: certificate has expired or is not yet valid:
current time 2025-03-25T07:08:48Z is after 2025-03-21T04:38:42Z ...]"
couldn't get current server API group list: Unauthorized
컨트롤 플레인 노드에서 확인한다.
kubeadm certs check-expiration
admin.conf · apiserver · apiserver-kubelet-client · controller-manager.conf · scheduler.conf · front-proxy-client · etcd 관련 인증서와 남은 기간이 나온다. CA 자체는 10년이라 보통 문제가 되지 않는다.
파일을 직접 볼 수도 있다.
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates
kubelet.conf 나 admin.conf 는 인증서 파일이 아니라 kubeconfig 다. openssl x509 -in 에 그대로 넣으면 unable to load certificate ... Expecting: TRUSTED CERTIFICATE 가 난다. 안에 든 값을 꺼내서 본다.
grep client-certificate-data /etc/kubernetes/kubelet.conf | awk '{print $2}' \
| base64 -d | openssl x509 -noout -subject -dates
kubeadm certs renew all
컨트롤 플레인 구성 요소는 정적 파드라 매니페스트를 건드려 다시 뜨게 하거나 kubelet 을 재시작한다. 컨트롤 플레인이 여럿이면 노드마다 수행한다.
systemctl restart kubelet
kubeadm certs check-expiration
admin.conf 도 함께 갱신되므로 관리자 kubeconfig 를 다시 복사한다.
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
정기적으로 kubeadm upgrade 를 수행하면 그 과정에서 인증서가 갱신된다. 한 해에 한 번도 손대지 않는 클러스터에서 이 문제가 난다.
kubeadm certs 는 컨트롤 플레인 인증서만 다룬다. 워커 노드의 kubelet 클라이언트 인증서는 kubelet 이 스스로 갱신한다.
grep rotateCertificates /var/lib/kubelet/config.yaml # true 여야 한다
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
journalctl -u kubelet | grep -i cert | tail -30
rotateCertificates: true 인데도 갱신되지 않는 경우는 대개 셋이다.
이미 만료됐다. kubelet 은 기존 인증서로 API 서버에 붙어 갱신을 요청한다. 그 인증서가 이미 만료되면 요청 자체를 보낼 수 없다. 로그에 certificate rotation is enabled, but certificate is expired and cannot be rotated 가 남는다.
CSR 이 승인되지 않는다. 컨트롤 플레인에서 확인한다. Pending 이 쌓여 있으면 자동 승인이 동작하지 않는 것이다.
kubectl get csr
kubectl certificate approve <csr 이름>
bootstrap 자격이 없다. 만료된 뒤 복구하려면 새 bootstrap 토큰으로 다시 등록한다.
# 컨트롤 플레인에서
kubeadm token create --print-join-command
만료된 노드에서 /etc/kubernetes/kubelet.conf 와 /var/lib/kubelet/pki/kubelet-client* 를 백업해 치우고 위 join 명령의 자격으로 다시 받게 하거나, 노드를 비우고 재조인한다. 재조인 전에는 워크로드를 옮긴다.
kubectl drain <노드> --ignore-daemonsets --delete-emptydir-data
kubelet 인증서는 노드마다 달라야 한다. 주체가 CN=system:node:<노드명> 이라 API 서버가 이것으로 노드를 식별하고 그 노드의 리소스만 다루도록 제한한다. 한 노드의 인증서를 다른 노드에 복사하면 권한 판정이 어긋난다.
kubelet 이 뜨지 못하면 그 노드의 10250 포트가 닫힌다.
Error from server: Get "https://192.168.10.51:10250/containerLogs/...":
dial tcp 192.168.10.51:10250: connect: connection refused
kubectl logs 나 kubectl exec 만 실패한다면 kubelet 상태부터 본다.
systemctl status kubelet
ss -tnlp | grep 10250
journalctl -u kubelet -xe | tail -50
유효 기간을 길게 잡고 싶다면 클러스터를 만들 때 정한다. 이미 만들어진 클러스터의 인증서 기간을 늘리려면 CA 로 다시 발급해야 하므로, 정기 갱신을 운영 절차에 넣는 편이 현실적이다.