쿠버네티스 인증서의 만료 기한은 kubeadm certs check-expiration 명령으로 확인할 수 있다.
현재 테스트 환경에서는 인증서들의 만료 기한은 1년으로 설정되어 있다.
이번 포스팅에서는 인증서의 구성 요소와 재발급 방법에 대해 살펴보자.
쿠버네티스는 클러스터 구성소요 간에 안전한 통신을 위해 Public Key 기반 구조(PKI)를 사용한다. CA(Certificate Authority, 인증 기관)는 클러스터 내 모든 인증서를 발급하고 서명하는 역할을 한다. CA가 서명한 인증서는 클러스터 내에서 신뢰할 수 있는 것으로 간주된다.
예를 들어, 클러스터 내에서 apiserver, kubelet, scheduler 등이 서로를 인증할 때, 각 구성 요소의 인증서는 CA가 서명했기 때문에 서로를 신뢰할 수 있다.
테스트 클러스터 환경에서는 apiserver, kubelet, scheduler 인증서는 같은 CA를 사용하여 발급된 것을 확인할 수 있다.
root@kjh-m1:~# 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'
W0209 23:45:46.495110 1159802 utils.go:69] The recommended value for "clusterDNS" in "KubeletConfiguration" is: [10.233.0.10]; the provided value is: [169.254.25.10]
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Feb 07, 2026 00:26 UTC 362d ca no
apiserver Feb 07, 2026 00:26 UTC 362d ca no
apiserver-kubelet-client Feb 07, 2026 00:26 UTC 362d ca no
controller-manager.conf Feb 07, 2026 00:26 UTC 362d ca no
front-proxy-client Feb 07, 2026 00:26 UTC 362d front-proxy-ca no
scheduler.conf Feb 07, 2026 00:26 UTC 362d ca no
super-admin.conf Feb 07, 2026 00:26 UTC 362d ca no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Dec 03, 2034 05:08 UTC 9y no
front-proxy-ca Dec 03, 2034 05:08 UTC 9y no
etcd는 자체 CA를 사용하여 독립적으로 인증서를 발급되었는데, 그 이유는 kubespray 의 스크립트에 의해서 Etcd만 독립적으로 인증서를 발급한 것을 알 수 있다.
root@kjh-m1:~/kubespray/roles/etcd# pwd
/root/kubespray/roles/etcd
root@kjh-m1:~/kubespray/roles/etcd# grep -r ssl |head -1
tasks/main.yml: command: "openssl x509 -in {{ etcd_cert_dir }}/node-{{ inventory_hostname }}.pem -noout -serial"
etcd 데이터 스냅샷은 현재 etcd 클러스터에 저장된 모든 키-값 데이터를 백업하는 파일이다. 즉, Kubernetes 클러스터의 상태를 저장하는 데이터베이스의 백업본이라고 보면 된다.
etcd는 모든 노드가 동일한 데이터를 가지므로, 한 개의 노드에서만 스냅샷을 생성하면 된다. 가능하면 리더 노드에서 생성하는 것이 가장 안전하다.
# etcd 리더 확인 -> master node1 번에 있는 Etcd가 리더이다.
root@kjh-m1:~# ETCDCTL_API=3 etcdctl endpoint status \
--write-out=table \
--endpoints=https://192.168.122.21:2379,https://192.168.122.22:2379,https://192.168.122.23:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/admin-kjh-m1.pem \
--key=/etc/ssl/etcd/ssl/admin-kjh-m1-key.pem
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.122.21:2379 | e99611c964d08e01 | 3.5.16 | 11 MB | true | false | 172 | 16432203 | 16432203 | |
| https://192.168.122.22:2379 | aac23c77cb18b0cc | 3.5.16 | 11 MB | false | false | 172 | 16432202 | 16432202 | |
| https://192.168.122.23:2379 | 83b998bb779c8db9 | 3.5.16 | 11 MB | false | false | 172 | 16432204 | 16432203 | |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
# 스냅샷 생성
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem --cert=/etc/ssl/etcd/ssl/admin-kjh-m1.pem \
--key=/etc/ssl/etcd/ssl/admin-kjh-m1-key.pem \
snapshot save ~/etcd_snapshot.db
## 스냅샷 생성 결과 확인
ll ~/etcd_snapshot.db
ETCDCTL_API=3 etcdutl snapshot status ~/etcd_snapshot.db --write-out=table
k8s 각 구성요소들은 인증서들은 만료 기한이 1년 CA는 10년이다.
# 저장 위치
root@kjh-m1:~# ls -al /etc/kubernetes/pki
lrwxrwxrwx 1 root root 19 12월 5 13:45 /etc/kubernetes/pki -> /etc/kubernetes/ssl
root@kjh-m1:~# ls -al /etc/kubernetes/pki/
total 80
drwxr-xr-x 2 root root 4096 12월 5 14:46 .
drwxr-xr-x 5 kube root 4096 2월 7 04:18 ..
-rw-r--r-- 1 root root 1452 2월 7 09:26 apiserver.crt
-rw-r--r-- 1 root root 1452 12월 5 14:08 apiserver.crt.old
-rw------- 1 root root 1679 2월 7 09:26 apiserver.key
-rw------- 1 root root 1675 12월 5 14:08 apiserver.key.old
-rw-r--r-- 1 root root 1176 2월 7 09:26 apiserver-kubelet-client.crt
-rw-r--r-- 1 root root 1176 12월 5 14:08 apiserver-kubelet-client.crt.old
-rw------- 1 root root 1675 2월 7 09:26 apiserver-kubelet-client.key
-rw------- 1 root root 1675 12월 5 14:08 apiserver-kubelet-client.key.old
-rw-r--r-- 1 root root 1107 12월 5 14:08 ca.crt
-rw------- 1 root root 1679 12월 5 14:08 ca.key
-rw-r--r-- 1 root root 1123 12월 5 14:08 front-proxy-ca.crt
-rw------- 1 root root 1675 12월 5 14:08 front-proxy-ca.key
-rw-r--r-- 1 root root 1119 2월 7 09:26 front-proxy-client.crt
-rw-r--r-- 1 root root 1119 12월 5 14:08 front-proxy-client.crt.old
-rw------- 1 root root 1679 2월 7 09:26 front-proxy-client.key
-rw------- 1 root root 1675 12월 5 14:08 front-proxy-client.key.old
-rw------- 1 root root 1679 12월 5 14:08 sa.key
-rw------- 1 root root 451 12월 5 14:08 sa.pub
# 만료 기한 확인
root@kjh-m1:~# 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'
W0210 00:14:58.418948 1182414 utils.go:69] The recommended value for "clusterDNS" in "KubeletConfiguration" is: [10.233.0.10]; the provided value is: [169.254.25.10]
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Feb 07, 2026 00:26 UTC 362d ca no
apiserver Feb 07, 2026 00:26 UTC 362d ca no
apiserver-kubelet-client Feb 07, 2026 00:26 UTC 362d ca no
controller-manager.conf Feb 07, 2026 00:26 UTC 362d ca no
front-proxy-client Feb 07, 2026 00:26 UTC 362d front-proxy-ca no
scheduler.conf Feb 07, 2026 00:26 UTC 362d ca no
super-admin.conf Feb 07, 2026 00:26 UTC 362d ca no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Dec 03, 2034 05:08 UTC 9y no
front-proxy-ca Dec 03, 2034 05:08 UTC 9y no
etcd는 만료 기한이 100년이다.
# 저장 위치
root@kjh-m1:~# ls -al /etc/ssl/etcd/ssl
total 88
drwx------ 2 etcd root 4096 12월 5 15:26 .
drwx------ 4 etcd root 4096 2월 7 04:18 ..
-rwx------ 1 etcd root 1675 12월 5 15:26 admin-kjh-m1-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 admin-kjh-m1.pem
-rwx------ 1 etcd root 1675 12월 5 15:26 admin-kjh-m2-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 admin-kjh-m2.pem
-rwx------ 1 etcd root 1675 12월 5 15:26 admin-kjh-m3-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 admin-kjh-m3.pem
-rwx------ 1 etcd root 1675 12월 5 14:06 ca-key.pem
-rwx------ 1 etcd root 1111 12월 5 14:06 ca.pem
-rwx------ 1 etcd root 1679 12월 5 15:26 member-kjh-m1-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 member-kjh-m1.pem
-rwx------ 1 etcd root 1675 12월 5 15:26 member-kjh-m2-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 member-kjh-m2.pem
-rwx------ 1 etcd root 1679 12월 5 15:26 member-kjh-m3-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 member-kjh-m3.pem
-rwx------ 1 etcd root 1675 12월 5 15:26 node-kjh-m1-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 node-kjh-m1.pem
-rwx------ 1 etcd root 1679 12월 5 15:26 node-kjh-m2-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 node-kjh-m2.pem
-rwx------ 1 etcd root 1679 12월 5 15:26 node-kjh-m3-key.pem
-rwx------ 1 etcd root 1415 12월 5 15:26 node-kjh-m3.pem
# 만료 기한 확인
root@kjh-m1:~# openssl x509 -in /etc/ssl/etcd/ssl/ca.pem -noout -dates
notBefore=Dec 5 05:06:00 2024 GMT
notAfter=Nov 11 05:06:00 2124 GMT
root@kjh-m1:~# openssl x509 -in /etc/ssl/etcd/ssl/admin-kjh-m1.pem -noout -dates
notBefore=Dec 5 06:26:53 2024 GMT
notAfter=Nov 11 06:26:53 2124 GMT
root@kjh-m1:~# openssl x509 -in /etc/ssl/etcd/ssl/member-kjh-m1.pem -noout -dates
notBefore=Dec 5 06:26:53 2024 GMT
notAfter=Nov 11 06:26:53 2124 GMT
인증서를 재발급하기 전에 기존 인증서를 백업한다.
kubeadm certs renew 로 인증서를 갱신하면 기본값으로 /etc/kubernetes/pki 아래에 저장된 인증서가 갱신된다. (--cert-dir string Default: "/etc/kubernetes/pki")따라서 /etc/kubernetes/pki 경로 아래에 있는 인증서를 백업하면 된다.
## PKI 인증서 백업
cp -rp /etc/kubernetes/ssl /etc/kubernetes/ssl_bkup_$(date +%y%m%d)
ls -al /etc/kubernetes/ssl_bkup_$(date +%y%m%d)/
kubeadm certs renew all
# PKI 인증서 확인 -> 인증서만 재발급됨. ca는 재발급 안됨.
kubeadm certs check-expiration
# etcd 인증서 확인 -> 재발급 안됨.
openssl x509 -in /etc/ssl/etcd/ssl/ca.pem -noout -dates
openssl x509 -in /etc/ssl/etcd/ssl/admin-kjh-m1.pem -noout -dates
openssl x509 -in /etc/ssl/etcd/ssl/member-kjh-m1.pem -noout -dates
kube-apiserver, kube-controller-manager, kube-scheduler가 갱신된 인증서를 사용하기 위해 재시작이 필요하다. 파드가 재기동하는 동안 서비스 연속성을 위해 Rolling 방식으로 마스터 노드 하나씩 작업하는 것이 좋다.
# static pod 재기동을 위해 설정 파일 이동
ls -al /etc/kubernetes/manifests/kube-*
mv /etc/kubernetes/manifests/kube-* /root/
ls -al /root/kube-*
# 상태 확인
kubectl get pods -n kube-system |egrep ^kube-
kubectl get nodes
# 복구
mv /root/kube-* /etc/kubernetes/manifests/
# 상태 확인
kubectl get pods -n kube-system |egrep ^kube-
kubectl get nodes
admin.conf(/etc/kubernetes/admin.conf)는 kubeadm certs renew all 실행 시 자동으로 갱신된다. 반면 /root/.kube/config는 자동으로 업데이트되지 않으므로 수동으로 변경해야 한다. 갱신된 인증서를 포함한 admin.conf를 ~/.kube/config로 복사하면 된다.
# 기존 kubeconfig 백업
mv /root/.kube/config /root/.kube/config-$(date +%y%m%d)
ls -al /root/.kube/config-$(date +%y%m%d)
# 갱신된 admin.conf를 사용하여 새로운 kubeconfig 적용
cp /etc/kubernetes/admin.conf ~/.kube/config
ls -al ~/.kube/config
# kubelet 재시작하여 적용
systemctl restart kubelet.service
systemctl status kubelet.service
[1] https://kubernetes.io/docs/setup/best-practices/certificates/#certificate-paths
[2] https://sickrov.github.io/
[3] https://danykde0til.tistory.com/165