Kubernetes(이하 k8s)에서는 애플리케이션/서비스의 보안 강화를 위한 TLS(Transport Layer Security) 인증서를 반드시 사용하게 된다.
TLS는 네트워크 상에서 데이터가 안전하게(암호화) 전달되도록 해주는 프로토콜이다.
이 때 사용하는 인증서를 통해 "서버의 신원 보장"과 "암호화 통신"이 가능하다.
| 명칭 | 역할 | 확장자 |
|---|---|---|
| CA(Certificate Authority) | 인증서 발급을 보증하는 기관(공개키) | .crt, .pem |
| TLS 인증서 | 서버의 공개키 + 서버 정보 + CA의 서명 | .crt, .pem |
| TLS 개인키 | 서버만 보관하는 비밀키 (암호화·복호화, 전자서명용) | .key, .pem |
apiVersion: v1
kind: Secret
metadata:
name: my-tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64 인코딩된 인증서>
tls.key: <base64 인코딩된 개인키>
해커가 개인키를 소유하면 다음이 가능하다.
1) Kubectl suddenly stops responding to your commands. Check it out! Someone recently modified the /etc/kubernetes/manifests/etcd.yaml file.
You are asked to investigate and fix the issue. Once you fix the issue wait for sometime for kubectl to respond. Check the logs of the ETCD container.
controlplane ~ ➜ kubectl get pods
Error from server (Timeout): the server was unable to return a response in the time allotted, but may still be processing the request (get pods)
crictl ps -a | grep kube-apiserver
crictl logs container-id
controlplane /var/log/containers ➜ crictl ps -a | grep kube-apiserver
c785c7c0161d5 6ba9545b2183e 9 seconds ago Running kube-apiserver 6 68f0eae2c4a26 kube-apiserver-controlplane kube-system
623e291a0c21c 6ba9545b2183e 2 minutes ago Exited kube-apiserver 5 68f0eae2c4a26 kube-apiserver-controlplane kube-system
controlplane /var/log/containers ➜ crictl logs c785c7c0161d5
I0909 04:39:25.739014 1 options.go:249] external host was not specified, using 192.168.138.245
I0909 04:39:25.741163 1 server.go:147] Version: v1.33.0
I0909 04:39:25.741193 1 server.go:149] "Golang settings" GOGC="" GOMAXPROCS="" GOTRACEBACK=""
W0909 04:39:26.389211 1 logging.go:55] [core] [Channel #1 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: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:26.389226 1 logging.go:55] [core] [Channel #2 SubChannel #3]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
I0909 04:39:26.391363 1 shared_informer.go:350] "Waiting for caches to sync" controller="node_authorizer"
I0909 04:39:26.401472 1 shared_informer.go:350] "Waiting for caches to sync" controller="*generic.policySource[*k8s.io/api/admissionregistration/v1.ValidatingAdmissionPolicy,*k8s.io/api/admissionregistration/v1.ValidatingAdmissionPolicyBinding,k8s.io/apiserver/pkg/admission/plugin/policy/validating.Validator]"
I0909 04:39:26.409434 1 plugins.go:157] Loaded 14 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,PodTopologyLabels,MutatingAdmissionPolicy,MutatingAdmissionWebhook.
I0909 04:39:26.409467 1 plugins.go:160] Loaded 13 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,ClusterTrustBundleAttest,CertificateSubjectRestriction,ValidatingAdmissionPolicy,ValidatingAdmissionWebhook,ResourceQuota.
I0909 04:39:26.409735 1 instance.go:233] Using reconciler: lease
W0909 04:39:26.411022 1 logging.go:55] [core] [Channel #5 SubChannel #6]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:27.390468 1 logging.go:55] [core] [Channel #1 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: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:27.390492 1 logging.go:55] [core] [Channel #2 SubChannel #3]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:27.411878 1 logging.go:55] [core] [Channel #5 SubChannel #6]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:29.063283 1 logging.go:55] [core] [Channel #1 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: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:29.114788 1 logging.go:55] [core] [Channel #5 SubChannel #6]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:29.130175 1 logging.go:55] [core] [Channel #2 SubChannel #3]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:31.253953 1 logging.go:55] [core] [Channel #5 SubChannel #6]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:31.435044 1 logging.go:55] [core] [Channel #2 SubChannel #3]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:31.476465 1 logging.go:55] [core] [Channel #1 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: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:34.977045 1 logging.go:55] [core] [Channel #5 SubChannel #6]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:35.535661 1 logging.go:55] [core] [Channel #2 SubChannel #3]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:36.244217 1 logging.go:55] [core] [Channel #1 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: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:41.513983 1 logging.go:55] [core] [Channel #2 SubChannel #3]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:41.655901 1 logging.go:55] [core] [Channel #5 SubChannel #6]grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:2379", ServerName: "127.0.0.1:2379", }. Err: connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
W0909 04:39:43.069154 1 logging.go:55] [core] [Channel #1 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: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
F0909 04:39:46.410711 1 instance.go:226] Error creating leases: error creating storage factory: context deadline exceeded
-> crictl로 에러메시지 확인 : "transport: Error while dialing: dial tcp 127.0.0.1:2379: connect: connection refused"
-> 127.0.0.1:2379는 ETCD 서버
-> kube-apiserver가 etcd 서버에 연결되지 못한 이슈로 kubectl이 작동하지 않음
controlplane /var/log/containers ✖ crictl ps -a | grep etcd
3fce5c7b4ab1c 499038711c081 2 minutes ago Exited etcd 6 b5365a1abe03b etcd-controlplane kube-system
controlplane /var/log/containers ➜ crictl logs 3fce5c7b4ab1c
{"level":"warn","ts":"2025-09-09T04:40:32.688192Z","caller":"embed/config.go:689","msg":"Running http and grpc server on single port. This is not recommended for production."}
{"level":"info","ts":"2025-09-09T04:40:32.688313Z","caller":"etcdmain/etcd.go:73","msg":"Running: ","args":["etcd","--advertise-client-urls=https://192.168.138.245:2379","--cert-file=/etc/kubernetes/pki/etcd/server-certificate.crt","--client-cert-auth=true","--data-dir=/var/lib/etcd","--experimental-initial-corrupt-check=true","--experimental-watch-progress-notify-interval=5s","--initial-advertise-peer-urls=https://192.168.138.245:2380","--initial-cluster=controlplane=https://192.168.138.245:2380","--key-file=/etc/kubernetes/pki/etcd/server.key","--listen-client-urls=https://127.0.0.1:2379,https://192.168.138.245:2379","--listen-metrics-urls=http://127.0.0.1:2381","--listen-peer-urls=https://192.168.138.245:2380","--name=controlplane","--peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt","--peer-client-cert-auth=true","--peer-key-file=/etc/kubernetes/pki/etcd/peer.key","--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt","--snapshot-count=10000","--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt"]}
{"level":"info","ts":"2025-09-09T04:40:32.688435Z","caller":"etcdmain/etcd.go:116","msg":"server has been already initialized","data-dir":"/var/lib/etcd","dir-type":"member"}
{"level":"warn","ts":"2025-09-09T04:40:32.688455Z","caller":"embed/config.go:689","msg":"Running http and grpc server on single port. This is not recommended for production."}
{"level":"info","ts":"2025-09-09T04:40:32.688466Z","caller":"embed/etcd.go:140","msg":"configuring peer listeners","listen-peer-urls":["https://192.168.138.245:2380"]}
{"level":"info","ts":"2025-09-09T04:40:32.688503Z","caller":"embed/etcd.go:528","msg":"starting with peer TLS","tls-info":"cert = /etc/kubernetes/pki/etcd/peer.crt, key = /etc/kubernetes/pki/etcd/peer.key, client-cert=, client-key=, trusted-ca = /etc/kubernetes/pki/etcd/ca.crt, client-cert-auth = true, crl-file = ","cipher-suites":[]}
{"level":"info","ts":"2025-09-09T04:40:32.690427Z","caller":"embed/etcd.go:148","msg":"configuring client listeners","listen-client-urls":["https://127.0.0.1:2379","https://192.168.138.245:2379"]}
{"level":"info","ts":"2025-09-09T04:40:32.690604Z","caller":"embed/etcd.go:323","msg":"starting an etcd server","etcd-version":"3.5.21","git-sha":"a17edfd","go-version":"go1.23.7","go-os":"linux","go-arch":"amd64","max-cpu-set":16,"max-cpu-available":16,"member-initialized":true,"name":"controlplane","data-dir":"/var/lib/etcd","wal-dir":"","wal-dir-dedicated":"","member-dir":"/var/lib/etcd/member","force-new-cluster":false,"heartbeat-interval":"100ms","election-timeout":"1s","initial-election-tick-advance":true,"snapshot-count":10000,"max-wals":5,"max-snapshots":5,"snapshot-catchup-entries":5000,"initial-advertise-peer-urls":["https://192.168.138.245:2380"],"listen-peer-urls":["https://192.168.138.245:2380"],"advertise-client-urls":["https://192.168.138.245:2379"],"listen-client-urls":["https://127.0.0.1:2379","https://192.168.138.245:2379"],"listen-metrics-urls":["http://127.0.0.1:2381"],"cors":["*"],"host-whitelist":["*"],"initial-cluster":"","initial-cluster-state":"new","initial-cluster-token":"","quota-backend-bytes":2147483648,"max-request-bytes":1572864,"max-concurrent-streams":4294967295,"pre-vote":true,"initial-corrupt-check":true,"corrupt-check-time-interval":"0s","compact-check-time-enabled":false,"compact-check-time-interval":"1m0s","auto-compaction-mode":"periodic","auto-compaction-retention":"0s","auto-compaction-interval":"0s","discovery-url":"","discovery-proxy":"","downgrade-check-interval":"5s"}
{"level":"info","ts":"2025-09-09T04:40:32.700627Z","caller":"etcdserver/backend.go:81","msg":"opened backend db","path":"/var/lib/etcd/member/snap/db","took":"9.614598ms"}
{"level":"info","ts":"2025-09-09T04:40:32.707387Z","caller":"etcdserver/server.go:534","msg":"No snapshot found. Recovering WAL from scratch!"}
{"level":"info","ts":"2025-09-09T04:40:32.713441Z","caller":"etcdserver/raft.go:541","msg":"restarting local member","cluster-id":"5c709c924160fd6d","local-member-id":"590f24c3d8737b02","commit-index":1491}
{"level":"info","ts":"2025-09-09T04:40:32.714397Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 switched to configuration voters=()"}
{"level":"info","ts":"2025-09-09T04:40:32.714473Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became follower at term 8"}
{"level":"info","ts":"2025-09-09T04:40:32.714489Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"newRaft 590f24c3d8737b02 [peers: [], term: 8, commit: 1491, applied: 0, lastindex: 1491, lastterm: 8]"}
{"level":"warn","ts":"2025-09-09T04:40:32.714839Z","caller":"auth/store.go:1241","msg":"simple token is not cryptographically signed"}
{"level":"info","ts":"2025-09-09T04:40:32.714906Z","caller":"mvcc/kvstore.go:348","msg":"restored last compact revision","meta-bucket-name":"meta","meta-bucket-name-key":"finishedCompactRev","restored-compact-revision":849}
{"level":"info","ts":"2025-09-09T04:40:32.716130Z","caller":"mvcc/kvstore.go:425","msg":"kvstore restored","current-rev":1334}
{"level":"info","ts":"2025-09-09T04:40:32.716174Z","caller":"etcdserver/server.go:628","msg":"restore consistentIndex","index":1489}
{"level":"info","ts":"2025-09-09T04:40:32.716299Z","caller":"etcdserver/quota.go:94","msg":"enabled backend quota with default value","quota-name":"v3-applier","quota-size-bytes":2147483648,"quota-size":"2.1 GB"}
{"level":"info","ts":"2025-09-09T04:40:32.716743Z","caller":"etcdserver/corrupt.go:96","msg":"starting initial corruption check","local-member-id":"590f24c3d8737b02","timeout":"7s"}
{"level":"info","ts":"2025-09-09T04:40:32.717024Z","caller":"etcdserver/corrupt.go:177","msg":"initial corruption checking passed; no corruption","local-member-id":"590f24c3d8737b02"}
{"level":"info","ts":"2025-09-09T04:40:32.717056Z","caller":"etcdserver/server.go:875","msg":"starting etcd server","local-member-id":"590f24c3d8737b02","local-server-version":"3.5.21","cluster-version":"to_be_decided"}
{"level":"info","ts":"2025-09-09T04:40:32.717148Z","caller":"etcdserver/server.go:775","msg":"starting initial election tick advance","election-ticks":10}
{"level":"info","ts":"2025-09-09T04:40:32.717209Z","caller":"fileutil/purge.go:50","msg":"started to purge file","dir":"/var/lib/etcd/member/snap","suffix":"snap.db","max":5,"interval":"30s"}
{"level":"info","ts":"2025-09-09T04:40:32.717331Z","caller":"fileutil/purge.go:50","msg":"started to purge file","dir":"/var/lib/etcd/member/snap","suffix":"snap","max":5,"interval":"30s"}
{"level":"info","ts":"2025-09-09T04:40:32.717378Z","caller":"fileutil/purge.go:50","msg":"started to purge file","dir":"/var/lib/etcd/member/wal","suffix":"wal","max":5,"interval":"30s"}
{"level":"info","ts":"2025-09-09T04:40:32.717434Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 switched to configuration voters=(6417388417594915586)"}
{"level":"info","ts":"2025-09-09T04:40:32.717467Z","caller":"v3rpc/health.go:61","msg":"grpc service status changed","service":"","status":"SERVING"}
{"level":"info","ts":"2025-09-09T04:40:32.717509Z","caller":"membership/cluster.go:421","msg":"added member","cluster-id":"5c709c924160fd6d","local-member-id":"590f24c3d8737b02","added-peer-id":"590f24c3d8737b02","added-peer-peer-urls":["https://192.168.138.245:2380"],"added-peer-is-learner":false}
{"level":"info","ts":"2025-09-09T04:40:32.717631Z","caller":"membership/cluster.go:587","msg":"set initial cluster version","cluster-id":"5c709c924160fd6d","local-member-id":"590f24c3d8737b02","cluster-version":"3.5"}
{"level":"info","ts":"2025-09-09T04:40:32.717668Z","caller":"api/capability.go:75","msg":"enabled capabilities for version","cluster-version":"3.5"}
{"level":"info","ts":"2025-09-09T04:40:32.720709Z","caller":"embed/etcd.go:762","msg":"starting with client TLS","tls-info":"cert = /etc/kubernetes/pki/etcd/server-certificate.crt, key = /etc/kubernetes/pki/etcd/server.key, client-cert=, client-key=, trusted-ca = /etc/kubernetes/pki/etcd/ca.crt, client-cert-auth = true, crl-file = ","cipher-suites":[]}
{"level":"info","ts":"2025-09-09T04:40:32.720861Z","caller":"embed/etcd.go:633","msg":"serving peer traffic","address":"192.168.138.245:2380"}
{"level":"info","ts":"2025-09-09T04:40:32.720892Z","caller":"embed/etcd.go:603","msg":"cmux::serve","address":"192.168.138.245:2380"}
{"level":"info","ts":"2025-09-09T04:40:32.721118Z","caller":"embed/etcd.go:292","msg":"now serving peer/client/metrics","local-member-id":"590f24c3d8737b02","initial-advertise-peer-urls":["https://192.168.138.245:2380"],"listen-peer-urls":["https://192.168.138.245:2380"],"advertise-client-urls":["https://192.168.138.245:2379"],"listen-client-urls":["https://127.0.0.1:2379","https://192.168.138.245:2379"],"listen-metrics-urls":["http://127.0.0.1:2381"]}
{"level":"info","ts":"2025-09-09T04:40:32.721172Z","caller":"embed/etcd.go:908","msg":"serving metrics","address":"http://127.0.0.1:2381"}
{"level":"info","ts":"2025-09-09T04:40:33.714981Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 is starting a new election at term 8"}
{"level":"info","ts":"2025-09-09T04:40:33.715055Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became pre-candidate at term 8"}
{"level":"info","ts":"2025-09-09T04:40:33.715079Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 received MsgPreVoteResp from 590f24c3d8737b02 at term 8"}
{"level":"info","ts":"2025-09-09T04:40:33.715091Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became candidate at term 9"}
{"level":"info","ts":"2025-09-09T04:40:33.715131Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 received MsgVoteResp from 590f24c3d8737b02 at term 9"}
{"level":"info","ts":"2025-09-09T04:40:33.715146Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became leader at term 9"}
{"level":"info","ts":"2025-09-09T04:40:33.715160Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"raft.node: 590f24c3d8737b02 elected leader 590f24c3d8737b02 at term 9"}
{"level":"info","ts":"2025-09-09T04:40:33.715593Z","caller":"etcdserver/server.go:2144","msg":"published local member to cluster through raft","local-member-id":"590f24c3d8737b02","local-member-attributes":"{Name:controlplane ClientURLs:[https://192.168.138.245:2379]}","request-path":"/0/members/590f24c3d8737b02/attributes","cluster-id":"5c709c924160fd6d","publish-timeout":"7s"}
{"level":"info","ts":"2025-09-09T04:40:33.715630Z","caller":"embed/serve.go:124","msg":"ready to serve client requests"}
{"level":"info","ts":"2025-09-09T04:40:33.715646Z","caller":"embed/serve.go:124","msg":"ready to serve client requests"}
{"level":"error","ts":"2025-09-09T04:40:33.715991Z","caller":"embed/serve.go:142","msg":"registerGateway failed","error":"open /etc/kubernetes/pki/etcd/server-certificate.crt: no such file or directory","stacktrace":"go.etcd.io/etcd/server/v3/embed.(*serveCtx).serve\n\tgo.etcd.io/etcd/server/v3/embed/serve.go:142\ngo.etcd.io/etcd/server/v3/embed.(*Etcd).serveClients.func1\n\tgo.etcd.io/etcd/server/v3/embed/etcd.go:818"}
{"level":"info","ts":"2025-09-09T04:40:33.716068Z","caller":"etcdmain/main.go:44","msg":"notifying init daemon"}
{"level":"info","ts":"2025-09-09T04:40:33.716194Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
{"level":"error","ts":"2025-09-09T04:40:33.716149Z","caller":"embed/serve.go:142","msg":"registerGateway failed","error":"open /etc/kubernetes/pki/etcd/server-certificate.crt: no such file or directory","stacktrace":"go.etcd.io/etcd/server/v3/embed.(*serveCtx).serve\n\tgo.etcd.io/etcd/server/v3/embed/serve.go:142\ngo.etcd.io/etcd/server/v3/embed.(*Etcd).serveClients.func1\n\tgo.etcd.io/etcd/server/v3/embed/etcd.go:818"}
{"level":"fatal","ts":"2025-09-09T04:40:33.716205Z","caller":"etcdmain/etcd.go:219","msg":"listener failed","error":"open /etc/kubernetes/pki/etcd/server-certificate.crt: no such file or directory","stacktrace":"go.etcd.io/etcd/server/v3/etcdmain.startEtcdOrProxyV2\n\tgo.etcd.io/etcd/server/v3/etcdmain/etcd.go:219\ngo.etcd.io/etcd/server/v3/etcdmain.Main\n\tgo.etcd.io/etcd/server/v3/etcdmain/main.go:40\nmain.main\n\tgo.etcd.io/etcd/server/v3/main.go:31\nruntime.main\n\truntime/proc.go:272"}
-> 에러메시지 확인 : {"level":"fatal","ts":"2025-09-09T04:40:33.716205Z","caller":"etcdmain/etcd.go:219","msg":"listener failed","error":"open /etc/kubernetes/pki/etcd/server-certificate.crt: no such file or directory","stacktrace":"go.etcd.io/etcd/server/v3/etcdmain.startEtcdOrProxyV2\n\tgo.etcd.io/etcd/server/v3/etcdmain/etcd.go:219\ngo.etcd.io/etcd/server/v3/etcdmain.Main\n\tgo.etcd.io/etcd/server/v3/etcdmain/main.go:40\nmain.main\n\tgo.etcd.io/etcd/server/v3/main.go:31\nruntime.main\n\truntime/proc.go:272"}
-> /etc/kubernetes/pki/etcd/server-certificate.crt 파일을 못 찾고 있음
-> yaml 파일에서 경로를 올바르게 수정해줘야 함
controlplane ~ ➜ cat /etc/kubernetes/manifests/etcd.yaml | grep "\-\-cert"
- --cert-file=/etc/kubernetes/pki/etcd/server-certificate.crt
-> 현재 etcd.yaml 파일에서는 위와 같이 작성하였음 확인
-> /etc/kubernetes/pki/etcd/server-certificate.crt 는 에러 로그에 나온 못 찾겠다는 파일 명칭임
controlplane ~ ➜ ls /etc/kubernetes/pki/etcd
ca.crt ca.key healthcheck-client.crt healthcheck-client.key peer.crt peer.key server.crt server.key
-> 실제 파일 etcd 인증서 파일 경로를 들어가보니 yaml 파일에 기재된 인증서 명과 다르게 server.crt 가 맞는 이름임을 확인
controlplane ~ ➜ vi /etc/kubernetes/manifests/etcd.yaml
-> vi etcd.yaml 로 server.crt 로 명칭 수정해주고 편집
controlplane ~ ➜ crictl ps -a | grep etcd
6ace31bb46baf 499038711c081 18 seconds ago Running etcd 0 05e9a899698c8 etcd-controlplane kube-system
controlplane ~ ➜ crictl logs 6ace31bb46baf
{"level":"warn","ts":"2025-09-09T04:58:30.190350Z","caller":"embed/config.go:689","msg":"Running http and grpc server on single port. This is not recommended for production."}
{"level":"info","ts":"2025-09-09T04:58:30.190431Z","caller":"etcdmain/etcd.go:73","msg":"Running: ","args":["etcd","--advertise-client-urls=https://192.168.138.245:2379","--cert-file=/etc/kubernetes/pki/etcd/server.crt","--client-cert-auth=true","--data-dir=/var/lib/etcd","--experimental-initial-corrupt-check=true","--experimental-watch-progress-notify-interval=5s","--initial-advertise-peer-urls=https://192.168.138.245:2380","--initial-cluster=controlplane=https://192.168.138.245:2380","--key-file=/etc/kubernetes/pki/etcd/server.key","--listen-client-urls=https://127.0.0.1:2379,https://192.168.138.245:2379","--listen-metrics-urls=http://127.0.0.1:2381","--listen-peer-urls=https://192.168.138.245:2380","--name=controlplane","--peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt","--peer-client-cert-auth=true","--peer-key-file=/etc/kubernetes/pki/etcd/peer.key","--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt","--snapshot-count=10000","--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt"]}
{"level":"info","ts":"2025-09-09T04:58:30.190506Z","caller":"etcdmain/etcd.go:116","msg":"server has been already initialized","data-dir":"/var/lib/etcd","dir-type":"member"}
{"level":"warn","ts":"2025-09-09T04:58:30.190523Z","caller":"embed/config.go:689","msg":"Running http and grpc server on single port. This is not recommended for production."}
{"level":"info","ts":"2025-09-09T04:58:30.190533Z","caller":"embed/etcd.go:140","msg":"configuring peer listeners","listen-peer-urls":["https://192.168.138.245:2380"]}
{"level":"info","ts":"2025-09-09T04:58:30.190561Z","caller":"embed/etcd.go:528","msg":"starting with peer TLS","tls-info":"cert = /etc/kubernetes/pki/etcd/peer.crt, key = /etc/kubernetes/pki/etcd/peer.key, client-cert=, client-key=, trusted-ca = /etc/kubernetes/pki/etcd/ca.crt, client-cert-auth = true, crl-file = ","cipher-suites":[]}
{"level":"info","ts":"2025-09-09T04:58:30.192745Z","caller":"embed/etcd.go:148","msg":"configuring client listeners","listen-client-urls":["https://127.0.0.1:2379","https://192.168.138.245:2379"]}
{"level":"info","ts":"2025-09-09T04:58:30.192922Z","caller":"embed/etcd.go:323","msg":"starting an etcd server","etcd-version":"3.5.21","git-sha":"a17edfd","go-version":"go1.23.7","go-os":"linux","go-arch":"amd64","max-cpu-set":16,"max-cpu-available":16,"member-initialized":true,"name":"controlplane","data-dir":"/var/lib/etcd","wal-dir":"","wal-dir-dedicated":"","member-dir":"/var/lib/etcd/member","force-new-cluster":false,"heartbeat-interval":"100ms","election-timeout":"1s","initial-election-tick-advance":true,"snapshot-count":10000,"max-wals":5,"max-snapshots":5,"snapshot-catchup-entries":5000,"initial-advertise-peer-urls":["https://192.168.138.245:2380"],"listen-peer-urls":["https://192.168.138.245:2380"],"advertise-client-urls":["https://192.168.138.245:2379"],"listen-client-urls":["https://127.0.0.1:2379","https://192.168.138.245:2379"],"listen-metrics-urls":["http://127.0.0.1:2381"],"cors":["*"],"host-whitelist":["*"],"initial-cluster":"","initial-cluster-state":"new","initial-cluster-token":"","quota-backend-bytes":2147483648,"max-request-bytes":1572864,"max-concurrent-streams":4294967295,"pre-vote":true,"initial-corrupt-check":true,"corrupt-check-time-interval":"0s","compact-check-time-enabled":false,"compact-check-time-interval":"1m0s","auto-compaction-mode":"periodic","auto-compaction-retention":"0s","auto-compaction-interval":"0s","discovery-url":"","discovery-proxy":"","downgrade-check-interval":"5s"}
{"level":"info","ts":"2025-09-09T04:58:30.197051Z","caller":"etcdserver/backend.go:81","msg":"opened backend db","path":"/var/lib/etcd/member/snap/db","took":"3.805703ms"}
{"level":"info","ts":"2025-09-09T04:58:30.203678Z","caller":"etcdserver/server.go:534","msg":"No snapshot found. Recovering WAL from scratch!"}
{"level":"info","ts":"2025-09-09T04:58:30.211340Z","caller":"etcdserver/raft.go:541","msg":"restarting local member","cluster-id":"5c709c924160fd6d","local-member-id":"590f24c3d8737b02","commit-index":1499}
{"level":"info","ts":"2025-09-09T04:58:30.211652Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 switched to configuration voters=()"}
{"level":"info","ts":"2025-09-09T04:58:30.211691Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became follower at term 12"}
{"level":"info","ts":"2025-09-09T04:58:30.211704Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"newRaft 590f24c3d8737b02 [peers: [], term: 12, commit: 1499, applied: 0, lastindex: 1499, lastterm: 12]"}
{"level":"warn","ts":"2025-09-09T04:58:30.212150Z","caller":"auth/store.go:1241","msg":"simple token is not cryptographically signed"}
{"level":"info","ts":"2025-09-09T04:58:30.212220Z","caller":"mvcc/kvstore.go:348","msg":"restored last compact revision","meta-bucket-name":"meta","meta-bucket-name-key":"finishedCompactRev","restored-compact-revision":849}
{"level":"info","ts":"2025-09-09T04:58:30.221630Z","caller":"mvcc/kvstore.go:425","msg":"kvstore restored","current-rev":1334}
{"level":"info","ts":"2025-09-09T04:58:30.221690Z","caller":"etcdserver/server.go:628","msg":"restore consistentIndex","index":1497}
{"level":"info","ts":"2025-09-09T04:58:30.221781Z","caller":"etcdserver/quota.go:94","msg":"enabled backend quota with default value","quota-name":"v3-applier","quota-size-bytes":2147483648,"quota-size":"2.1 GB"}
{"level":"info","ts":"2025-09-09T04:58:30.222165Z","caller":"etcdserver/corrupt.go:96","msg":"starting initial corruption check","local-member-id":"590f24c3d8737b02","timeout":"7s"}
{"level":"info","ts":"2025-09-09T04:58:30.222443Z","caller":"etcdserver/corrupt.go:177","msg":"initial corruption checking passed; no corruption","local-member-id":"590f24c3d8737b02"}
{"level":"info","ts":"2025-09-09T04:58:30.222486Z","caller":"etcdserver/server.go:875","msg":"starting etcd server","local-member-id":"590f24c3d8737b02","local-server-version":"3.5.21","cluster-version":"to_be_decided"}
{"level":"info","ts":"2025-09-09T04:58:30.222570Z","caller":"etcdserver/server.go:775","msg":"starting initial election tick advance","election-ticks":10}
{"level":"info","ts":"2025-09-09T04:58:30.222667Z","caller":"fileutil/purge.go:50","msg":"started to purge file","dir":"/var/lib/etcd/member/snap","suffix":"snap.db","max":5,"interval":"30s"}
{"level":"info","ts":"2025-09-09T04:58:30.222727Z","caller":"fileutil/purge.go:50","msg":"started to purge file","dir":"/var/lib/etcd/member/snap","suffix":"snap","max":5,"interval":"30s"}
{"level":"info","ts":"2025-09-09T04:58:30.222736Z","caller":"fileutil/purge.go:50","msg":"started to purge file","dir":"/var/lib/etcd/member/wal","suffix":"wal","max":5,"interval":"30s"}
{"level":"info","ts":"2025-09-09T04:58:30.222948Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 switched to configuration voters=(6417388417594915586)"}
{"level":"info","ts":"2025-09-09T04:58:30.222992Z","caller":"membership/cluster.go:421","msg":"added member","cluster-id":"5c709c924160fd6d","local-member-id":"590f24c3d8737b02","added-peer-id":"590f24c3d8737b02","added-peer-peer-urls":["https://192.168.138.245:2380"],"added-peer-is-learner":false}
{"level":"info","ts":"2025-09-09T04:58:30.223052Z","caller":"v3rpc/health.go:61","msg":"grpc service status changed","service":"","status":"SERVING"}
{"level":"info","ts":"2025-09-09T04:58:30.223076Z","caller":"membership/cluster.go:587","msg":"set initial cluster version","cluster-id":"5c709c924160fd6d","local-member-id":"590f24c3d8737b02","cluster-version":"3.5"}
{"level":"info","ts":"2025-09-09T04:58:30.223105Z","caller":"api/capability.go:75","msg":"enabled capabilities for version","cluster-version":"3.5"}
{"level":"info","ts":"2025-09-09T04:58:30.225380Z","caller":"embed/etcd.go:762","msg":"starting with client TLS","tls-info":"cert = /etc/kubernetes/pki/etcd/server.crt, key = /etc/kubernetes/pki/etcd/server.key, client-cert=, client-key=, trusted-ca = /etc/kubernetes/pki/etcd/ca.crt, client-cert-auth = true, crl-file = ","cipher-suites":[]}
{"level":"info","ts":"2025-09-09T04:58:30.225531Z","caller":"embed/etcd.go:633","msg":"serving peer traffic","address":"192.168.138.245:2380"}
{"level":"info","ts":"2025-09-09T04:58:30.225574Z","caller":"embed/etcd.go:603","msg":"cmux::serve","address":"192.168.138.245:2380"}
{"level":"info","ts":"2025-09-09T04:58:30.225690Z","caller":"embed/etcd.go:292","msg":"now serving peer/client/metrics","local-member-id":"590f24c3d8737b02","initial-advertise-peer-urls":["https://192.168.138.245:2380"],"listen-peer-urls":["https://192.168.138.245:2380"],"advertise-client-urls":["https://192.168.138.245:2379"],"listen-client-urls":["https://127.0.0.1:2379","https://192.168.138.245:2379"],"listen-metrics-urls":["http://127.0.0.1:2381"]}
{"level":"info","ts":"2025-09-09T04:58:30.225734Z","caller":"embed/etcd.go:908","msg":"serving metrics","address":"http://127.0.0.1:2381"}
{"level":"info","ts":"2025-09-09T04:58:31.713145Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 is starting a new election at term 12"}
{"level":"info","ts":"2025-09-09T04:58:31.713222Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became pre-candidate at term 12"}
{"level":"info","ts":"2025-09-09T04:58:31.713262Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 received MsgPreVoteResp from 590f24c3d8737b02 at term 12"}
{"level":"info","ts":"2025-09-09T04:58:31.713281Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became candidate at term 13"}
{"level":"info","ts":"2025-09-09T04:58:31.713325Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 received MsgVoteResp from 590f24c3d8737b02 at term 13"}
{"level":"info","ts":"2025-09-09T04:58:31.713336Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"590f24c3d8737b02 became leader at term 13"}
{"level":"info","ts":"2025-09-09T04:58:31.713345Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"raft.node: 590f24c3d8737b02 elected leader 590f24c3d8737b02 at term 13"}
{"level":"info","ts":"2025-09-09T04:58:31.713603Z","caller":"etcdserver/server.go:2144","msg":"published local member to cluster through raft","local-member-id":"590f24c3d8737b02","local-member-attributes":"{Name:controlplane ClientURLs:[https://192.168.138.245:2379]}","request-path":"/0/members/590f24c3d8737b02/attributes","cluster-id":"5c709c924160fd6d","publish-timeout":"7s"}
{"level":"info","ts":"2025-09-09T04:58:31.713625Z","caller":"embed/serve.go:124","msg":"ready to serve client requests"}
{"level":"info","ts":"2025-09-09T04:58:31.713675Z","caller":"embed/serve.go:124","msg":"ready to serve client requests"}
{"level":"info","ts":"2025-09-09T04:58:31.713983Z","caller":"etcdmain/main.go:44","msg":"notifying init daemon"}
{"level":"info","ts":"2025-09-09T04:58:31.714017Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
{"level":"info","ts":"2025-09-09T04:58:31.716170Z","caller":"v3rpc/health.go:61","msg":"grpc service status changed","service":"","status":"SERVING"}
{"level":"info","ts":"2025-09-09T04:58:31.716150Z","caller":"v3rpc/health.go:61","msg":"grpc service status changed","service":"","status":"SERVING"}
{"level":"info","ts":"2025-09-09T04:58:31.717057Z","caller":"embed/serve.go:275","msg":"serving client traffic securely","traffic":"grpc+http","address":"192.168.138.245:2379"}
{"level":"info","ts":"2025-09-09T04:58:31.717057Z","caller":"embed/serve.go:275","msg":"serving client traffic securely","traffic":"grpc+http","address":"127.0.0.1:2379"}
controlplane ~ ➜ k get pods
No resources found in default namespace.
-> 이후 etcd가 static pod이므로 자동으로 반영되어 etcd 서버 정상화, kubectl 명령어를 정상적으로 사용 가능함을 확인
2) The kube-api server stopped again! Check it out. Inspect the kube-api server logs and identify the root cause and fix the issue.
Run crictl ps -a command to identify the kube-api server container. Run crictl logs container-id command to view the logs.
controlplane ~ ➜ k get pods
The connection to the server controlplane:6443 was refused - did you specify the right host or port?
-> 오류 메세지 확인
controlplane ~ ✖ crictl ps -a | grep kube-api
037030b867bfb 6ba9545b2183e 5 seconds ago Running kube-apiserver 2 2244560041c77 kube-apiserver-controlplane kube-system
d144ccb8aed5f 6ba9545b2183e 42 seconds ago Exited kube-apiserver 1 2244560041c77 kube-apiserver-controlplane kube-system
controlplane ~ ➜ crictl logs 037030b867bfb
I0909 05:02:00.831961 1 options.go:249] external host was not specified, using 192.168.138.245
I0909 05:02:00.833844 1 server.go:147] Version: v1.33.0
I0909 05:02:00.833870 1 server.go:149] "Golang settings" GOGC="" GOMAXPROCS="" GOTRACEBACK=""
I0909 05:02:01.448673 1 shared_informer.go:350] "Waiting for caches to sync" controller="node_authorizer"
W0909 05:02:01.451769 1 logging.go:55] [core] [Channel #3 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 signed by unknown authority"
W0909 05:02:01.452589 1 logging.go:55] [core] [Channel #1 SubChannel #2]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 signed by unknown authority"
I0909 05:02:01.459149 1 shared_informer.go:350] "Waiting for caches to sync" controller="*generic.policySource[*k8s.io/api/admissionregistration/v1.ValidatingAdmissionPolicy,*k8s.io/api/admissionregistration/v1.ValidatingAdmissionPolicyBinding,k8s.io/apiserver/pkg/admission/plugin/policy/validating.Validator]"
I0909 05:02:01.467286 1 plugins.go:157] Loaded 14 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,PodTopologyLabels,MutatingAdmissionPolicy,MutatingAdmissionWebhook.
I0909 05:02:01.467314 1 plugins.go:160] Loaded 13 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,ClusterTrustBundleAttest,CertificateSubjectRestriction,ValidatingAdmissionPolicy,ValidatingAdmissionWebhook,ResourceQuota.
I0909 05:02:01.467603 1 instance.go:233] Using reconciler: lease
W0909 05:02:01.471717 1 logging.go:55] [core] [Channel #5 SubChannel #6]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 signed by unknown authority"
W0909 05:02:02.455774 1 logging.go:55] [core] [Channel #1 SubChannel #2]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 signed by unknown authority"
W0909 05:02:02.458327 1 logging.go:55] [core] [Channel #3 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 signed by unknown authority"
W0909 05:02:02.474858 1 logging.go:55] [core] [Channel #5 SubChannel #6]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 signed by unknown authority"
W0909 05:02:04.030069 1 logging.go:55] [core] [Channel #1 SubChannel #2]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 signed by unknown authority"
W0909 05:02:04.120255 1 logging.go:55] [core] [Channel #3 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 signed by unknown authority"
W0909 05:02:04.274151 1 logging.go:55] [core] [Channel #5 SubChannel #6]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 signed by unknown authority"
W0909 05:02:06.617976 1 logging.go:55] [core] [Channel #5 SubChannel #6]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 signed by unknown authority"
W0909 05:02:06.630857 1 logging.go:55] [core] [Channel #3 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 signed by unknown authority"
W0909 05:02:07.025750 1 logging.go:55] [core] [Channel #1 SubChannel #2]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 signed by unknown authority"
W0909 05:02:10.675670 1 logging.go:55] [core] [Channel #3 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 signed by unknown authority"
W0909 05:02:11.296970 1 logging.go:55] [core] [Channel #5 SubChannel #6]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 signed by unknown authority"
W0909 05:02:11.929960 1 logging.go:55] [core] [Channel #1 SubChannel #2]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 signed by unknown authority"
W0909 05:02:17.723838 1 logging.go:55] [core] [Channel #3 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 signed by unknown authority"
W0909 05:02:17.868372 1 logging.go:55] [core] [Channel #1 SubChannel #2]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 signed by unknown authority"
W0909 05:02:18.135735 1 logging.go:55] [core] [Channel #5 SubChannel #6]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 signed by unknown authority"
F0909 05:02:21.468573 1 instance.go:226] Error creating leases: error creating storage factory: context deadline exceeded
-> 에러메시지 확인 : "transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority"
-> 인증서 이슈
controlplane /etc/kubernetes/manifests ➜ cat kube-apiserver.yaml | grep "crt"
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --etcd-cafile=/etc/kubernetes/pki/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
-> 현재 kube-apiserver.yaml에 기재된 인증서 목록 확인
-> --etcd-cafile=/etc/kubernetes/pki/ca.crt 값의 경로가 /etcd 하위에 있는 값으로 변경 필요함 확인
controlplane kubernetes/pki/etcd ➜ ll
total 40
drwxr-xr-x 2 root root 4096 Sep 9 04:21 ./
drwxr-xr-x 3 root root 4096 Sep 9 04:21 ../
-rw-r--r-- 1 root root 1094 Sep 9 04:21 ca.crt
-rw------- 1 root root 1675 Sep 9 04:21 ca.key
-rw-r--r-- 1 root root 1123 Sep 9 04:21 healthcheck-client.crt
-rw------- 1 root root 1675 Sep 9 04:21 healthcheck-client.key
-rw-r--r-- 1 root root 1208 Sep 9 04:21 peer.crt
-rw------- 1 root root 1675 Sep 9 04:21 peer.key
-rw-r--r-- 1 root root 1208 Sep 9 04:21 server.crt
-rw------- 1 root root 1679 Sep 9 04:21 server.key
-> 실제 파일 경로 및 이름 확인
controlplane /etc/kubernetes/manifests ➜ vi kube-apiserver.yaml
controlplane /etc/kubernetes/manifests ➜ k get pod
The connection to the server controlplane:6443 was refused - did you specify the right host or port?
controlplane /etc/kubernetes/manifests ✖ k get pods
The connection to the server controlplane:6443 was refused - did you specify the right host or port?
controlplane /etc/kubernetes/manifests ✖ crictl ps -a | grep api-server
controlplane /etc/kubernetes/manifests ✖ crictl ps -a | grep api
4169824c8dbdf 6ba9545b2183e 12 seconds ago Running kube-apiserver 0 835ad386e4060 kube-apiserver-controlplane kube-system
controlplane /etc/kubernetes/manifests ➜ crictl logs 4169824c8dbdf
-> kube-apiserver.yaml 파일 편집 이후 로그에 에러 없음 확인
controlplane /etc/kubernetes/manifests ➜ k get pods
No resources found in default namespace.
-> kube-apiserver 서버 정상화 및 kubectl 사용 가능 확인
Certificates API는 Kubernetes에서 인증서 발급 신청(Request), 승인(Approval), 발급(Issue) 를 원활하게 처리하기 위한 리소스(오브젝트)와 메커니즘을 제공한다.
기본적으로 사용자 또는 Pod가 인증서를 신청하면, Kubernetes가 이를 관리해서 자동으로 인증서를 만들고 교체(갱신)할 수 있도록 돕는다.
csr라고도 부름아래 명령어로 개인키와 CSR 파일을 만든다.
openssl genrsa -out my-user.key 2048
openssl req -new -key my-user.key -out my-user.csr -subj "/CN=my-user"
my-user.key : 개인키 파일my-user.csr : 인증서 서명 요청 파일 (CSR, Certificate Signing Request)아래 명령어로 파일을 base64로 변환한다. (한 줄로 출력)
base64 < my-user.csr | tr -d '\n'
결과값(길게 출력된 한 줄)을 복사한다.
복사한 base64 문자열을 아래 YAML의 spec.request 필드에 붙여넣는다.
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: my-cert-request
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJ... # base64로 인코딩한 CSR 한 줄
signerName: kubernetes.io/kube-apiserver-client
usages:
- digital signature
- key encipherment
- client auth
spec.request에는 반드시 따옴표 없는 한 줄 base64 데이터를 쓴다.CSR 오브젝트를 클러스터에 생성한다.
kubectl create -f my-cert-request.yaml
CSR 목록을 확인한다.
kubectl get csr
CSR을 승인한다.
kubectl certificate approve my-cert-request
승인 후 아래 명령어로 발급된 인증서를 파일로 저장한다.
kubectl get csr my-cert-request -o jsonpath='{.status.certificate}' | base64 --decode > my-user.crt
my-user.crt : 발급된 인증서 파일my-user.key : 처음 만든 개인키 파일모든 인증서 관련 작업은 모두 kube-apiserver 내 controller manager가 담당한다!


1) Create a CertificateSigningRequest object with the name akshay with the contents of the akshay.csr file
참고 : https://kubernetes.io/docs/tasks/tls/certificate-issue-client-csr/
controlplane ~ ➜ ls
akshay.csr akshay.key
controlplane ~ ➜ cat akshay.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICVjCCAT4CAQAwETEPMA0GA1UEAwwGYWtzaGF5MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAxbJSPM/DuAXV5VciwNzbzGBZbNuNZNFyIcgZvRwJLweN
y7eEPzunacHXtL2zThl+OuxqinKUBEkdo5uiRevGTSlG7UzotXDSrx2vdsRmzxdS
NeQOtyC1llFlWQvkCbtJczfvgCN8OumeIGnnKei8hksq2A2EaXM7L0Ivkg4uScaY
S9xZarbsVllON2zNU+ig/8LjcjIV9m6wRf6+Pr57XSmnvW0E+J+YV+wJ2Af/jU9K
Sh4EpgNLzjuE3zJd+fvJL912m9L6n524sbCE7HQzY0WrLmRU7aw/++0k+vBlX2h1
F7ygMe7OLur3jtcQa0B1bRCJ8osBNe31c0ICnB7XxQIDAQABoAAwDQYJKoZIhvcN
AQELBQADggEBAHCakEEJ1R05Cb7z9P3nbJAD0/YbJr1fEbAIhSWlehvq5sXzot1r
OImYR2Ty+kMF1aIb3r8TAYWsej83c0Od/9/TUaa869ETdbJ6ihgWkCLHJlQbQze3
X2oaPdKO1P2pauIexu8MPmoNNzRyauA2vMSNppvBKru3y7CwaRuS/q8szKC9V7IM
VZhlIbu1phcWD0GEVDV0zWz6COcvHXKEV9r2oPyKlsF/M9qhLSupOuHndqIZjpii
6hglUQj750UI8afPciH1cWlwn9a1Q10TjPZMtkU1Di8An2lJRDWmWUS6CZkzbQBv
08PCSPb7aDclgoPmEAsVu8ImJ9sJv3fK/aw=
-----END CERTIFICATE REQUEST-----
controlplane ~ ➜ cat akshay.csr | base64 | tr -d "\n"
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZV3R6YUdGNU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQXhiSlNQTS9EdUFYVjVWY2l3TnpiekdCWmJOdU5aTkZ5SWNnWnZSd0pMd2VOCnk3ZUVQenVuYWNIWHRMMnpUaGwrT3V4cWluS1VCRWtkbzV1aVJldkdUU2xHN1V6b3RYRFNyeDJ2ZHNSbXp4ZFMKTmVRT3R5QzFsbEZsV1F2a0NidEpjemZ2Z0NOOE91bWVJR25uS2VpOGhrc3EyQTJFYVhNN0wwSXZrZzR1U2NhWQpTOXhaYXJic1ZsbE9OMnpOVStpZy84TGpjaklWOW02d1JmNitQcjU3WFNtbnZXMEUrSitZVit3SjJBZi9qVTlLClNoNEVwZ05Memp1RTN6SmQrZnZKTDkxMm05TDZuNTI0c2JDRTdIUXpZMFdyTG1SVTdhdy8rKzBrK3ZCbFgyaDEKRjd5Z01lN09MdXIzanRjUWEwQjFiUkNKOG9zQk5lMzFjMElDbkI3WHhRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBSENha0VFSjFSMDVDYjd6OVAzbmJKQUQwL1liSnIxZkViQUloU1dsZWh2cTVzWHpvdDFyCk9JbVlSMlR5K2tNRjFhSWIzcjhUQVlXc2VqODNjME9kLzkvVFVhYTg2OUVUZGJKNmloZ1drQ0xISmxRYlF6ZTMKWDJvYVBkS08xUDJwYXVJZXh1OE1QbW9OTnpSeWF1QTJ2TVNOcHB2QktydTN5N0N3YVJ1Uy9xOHN6S0M5VjdJTQpWWmhsSWJ1MXBoY1dEMEdFVkRWMHpXejZDT2N2SFhLRVY5cjJvUHlLbHNGL005cWhMU3VwT3VIbmRxSVpqcGlpCjZoZ2xVUWo3NTBVSThhZlBjaUgxY1dsd245YTFRMTBUalBaTXRrVTFEaThBbjJsSlJEV21XVVM2Q1premJRQnYKMDhQQ1NQYjdhRGNsZ29QbUVBc1Z1OEltSjlzSnYzZksvYXc9Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
-> 인증서 내용 base64 인코딩
controlplane ~ ➜ cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: akshay
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZV3R6YUdGNU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQXhiSlNQTS9EdUFYVjVWY2l3TnpiekdCWmJOdU5aTkZ5SWNnWnZSd0pMd2VOCnk3ZUVQenVuYWNIWHRMMnpUaGwrT3V4cWluS1VCRWtkbzV1aVJldkdUU2xHN1V6b3RYRFNyeDJ2ZHNSbXp4ZFMKTmVRT3R5QzFsbEZsV1F2a0NidEpjemZ2Z0NOOE91bWVJR25uS2VpOGhrc3EyQTJFYVhNN0wwSXZrZzR1U2NhWQpTOXhaYXJic1ZsbE9OMnpOVStpZy84TGpjaklWOW02d1JmNitQcjU3WFNtbnZXMEUrSitZVit3SjJBZi9qVTlLClNoNEVwZ05Memp1RTN6SmQrZnZKTDkxMm05TDZuNTI0c2JDRTdIUXpZMFdyTG1SVTdhdy8rKzBrK3ZCbFgyaDEKRjd5Z01lN09MdXIzanRjUWEwQjFiUkNKOG9zQk5lMzFjMElDbkI3WHhRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBSENha0VFSjFSMDVDYjd6OVAzbmJKQUQwL1liSnIxZkViQUloU1dsZWh2cTVzWHpvdDFyCk9JbVlSMlR5K2tNRjFhSWIzcjhUQVlXc2VqODNjME9kLzkvVFVhYTg2OUVUZGJKNmloZ1drQ0xISmxRYlF6ZTMKWDJvYVBkS08xUDJwYXVJZXh1OE1QbW9OTnpSeWF1QTJ2TVNOcHB2QktydTN5N0N3YVJ1Uy9xOHN6S0M5VjdJTQpWWmhsSWJ1MXBoY1dEMEdFVkRWMHpXejZDT2N2SFhLRVY5cjJvUHlLbHNGL005cWhMU3VwT3VIbmRxSVpqcGlpCjZoZ2xVUWo3NTBVSThhZlBjaUgxY1dsd245YTFRMTBUalBaTXRrVTFEaThBbjJsSlJEV21XVVM2Q1premJRQnYKMDhQQ1NQYjdhRGNsZ29QbUVBc1Z1OEltSjlzSnYzZksvYXc9Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400 # one day
usages:
- client auth
EOF
certificatesigningrequest.certificates.k8s.io/akshay created
controlplane ~ ➜ k get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
akshay 6s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Pending
csr-zjjck 9m46s kubernetes.io/kube-apiserver-client-kubelet system:node:controlplane <none> Approved,Issued
-> CertificateSigningRequest 파일 생성, 그러나 현재 pending 상태
2) Approve the CSR Request
controlplane ~ ✖ k certificate approve akshay
certificatesigningrequest.certificates.k8s.io/akshay approved
controlplane ~ ➜ k get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
akshay 2m30s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Approved,Issued
csr-zjjck 12m kubernetes.io/kube-apiserver-client-kubelet system:node:controlplane <none> Approved,Issued
3) What groups is this CSR requesting access to?
controlplane ~ ➜ kubectl get csr agent-smith -o yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
creationTimestamp: "2025-09-09T05:38:29Z"
name: agent-smith
resourceVersion: "1411"
uid: b5d942d3-0caf-4bbe-bb00-6137407ae4f2
spec:
extra:
authentication.kubernetes.io/credential-id:
- X509SHA256=544c737883894d1e5a8fa4dfdcb83c25ad05ca5936bc1e1fd8a04d784edc8e3b
groups:
- system:masters
- system:authenticated
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1dEQ0NBVUFDQVFBd0V6RVJNQThHQTFVRUF3d0libVYzTFhWelpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRE8wV0pXK0RYc0FKU0lyanBObzV2UklCcGxuemcrNnhjOStVVndrS2kwCkxmQzI3dCsxZUVuT041TXVxOTlOZXZtTUVPbnJEVU8vdGh5VnFQMncyWE5JRFJYall5RjQwRmJtRCs1eld5Q0sKeTNCaWhoQjkzTUo3T3FsM1VUdlo4VEVMcXlhRGtuUmwvanYvU3hnWGtvazBBQlVUcFdNeDRCcFNpS2IwVSt0RQpJRjVueEF0dE1Wa0RQUTdOYmVaUkc0M2IrUVdsVkdSL3o2RFdPZkpuYmZlek90YUF5ZEdMVFpGQy93VHB6NTJrCkVjQ1hBd3FDaGpCTGt6MkJIUFI0Sjg5RDZYYjhrMzlwdTZqcHluZ1Y2dVAwdEliT3pwcU52MFkwcWRFWnB3bXcKajJxRUwraFpFV2trRno4MGxOTnR5VDVMeE1xRU5EQ25JZ3dDNEdaaVJHYnJBZ01CQUFHZ0FEQU5CZ2txaGtpRwo5dzBCQVFzRkFBT0NBUUVBUzlpUzZDMXV4VHVmNUJCWVNVN1FGUUhVemFsTnhBZFlzYU9SUlFOd0had0hxR2k0CmhPSzRhMnp5TnlpNDRPT2lqeWFENnRVVzhEU3hrcjhCTEs4S2czc3JSRXRKcWw1ckxaeTlMUlZyc0pnaEQ0Z1kKUDlOTCthRFJTeFJPVlNxQmFCMm5XZVlwTTVjSjVURjUzbGVzTlNOTUxRMisrUk1uakRRSjdqdVBFaWM4L2RoawpXcjJFVU02VWF3enlrcmRISW13VHYybWxNWTBSK0ROdFYxWWllKzBIOS9ZRWx0K0ZTR2poNUw1WVV2STFEcWl5CjRsM0UveTNxTDcxV2ZBY3VIM09zVnBVVW5RSVNNZFFzMHFXQ3NiRTU2Q0M1RGhQR1pJcFVibktVcEF3a2ErOEUKdndRMDdqRytocGtueG11RkFlWHhnVXdvZEFMYUo3anUvVERJY3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K
signerName: kubernetes.io/kube-apiserver-client
usages:
- digital signature
- key encipherment
- server auth
username: agent-x
status: {}
-> system:masters 가 정답
-> 모든 그룹에 권한이 들어가있으므로 다음 문제에서 reject
3) Reject that request.
controlplane ~ ➜ kubectl certificate deny agent-smith
certificatesigningrequest.certificates.k8s.io/agent-smith denied
controlplane ~ ➜ k get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
agent-smith 2m1s kubernetes.io/kube-apiserver-client agent-x <none> Denied
akshay 4m59s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Approved,Issued
csr-zjjck 14m kubernetes.io/kube-apiserver-client-kubelet system:node:controlplane <none> Approved,Issued
4) Delete the new CSR object
controlplane ~ ➜ kubectl delete csr agent-smith
certificatesigningrequest.certificates.k8s.io "agent-smith" deleted
controlplane ~ ➜ k get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
akshay 5m23s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Approved,Issued
csr-zjjck 15m kubernetes.io/kube-apiserver-client-kubelet system:node:controlplane <none> Approved,Issued
(환경변수 KUBECONFIG 로 다른 경로 지정 가능)
vi ~/.bashrc
export KUBECONFIG="/path/to/your/kube-config-file"
source ~/.bashrc
즉, “어떤 클러스터에, 어떤 사용자 권한으로, 어떤 context에서 접근할지”를 정의한 설정 모음.
kubeconfig는 보통 아래 3가지 구성 요소를 가지고 있음
1) clusters
• 연결할 쿠버네티스 API 서버 정보
• API server의 주소(server), CA 인증서(certificate-authority) 등이 포함
clusters:
2) users
• 어떤 사용자 인증 정보를 쓸지 정의
• 클라이언트 인증서, 토큰, exec plugin(OIDC 등) 사용 가능
users:
3) contexts
• “클러스터 + 사용자 + 네임스페이스”의 조합
• 여러 개의 클러스터를 관리할 때 현재 어떤 조합을 쓸지 정하는 역할
contexts:
현재 context 확인
kubectl config current-context
context 리스트 확인
kubectl config get-contexts
context 전환
kubectl config use-context admin-context
기본 파일 변경
mv /root/my-kube-config /root/.kube/config
때로는 여러 개의 config 파일을 써야 할 때가 있다.
export KUBECONFIG=~/.kube/config:~/.kube/dev-config
kubectl config view --merge --flatten > ~/.kube/merged-config
• 시험 환경에서 기본 제공되는 ~/.kube/config 를 반드시 활용
• 여러 클러스터가 있을 때 kubectl config use-context <context-name> 으로 전환
• 인증/접속 문제 발생 시 clusters / users / contexts 세 부분을 확인
👉 요약하면, kubeconfig는 쿠버네티스 접속을 위한 “주소록 + 계정정보 + 환경설정”
1) context란?
2) 왜 context 전환이 필요할까?
예를 들어:
이때 kubeconfig에 두 클러스터와 두 사용자 정보가 모두 들어있음
👉 그런데 kubectl get pods 를 입력했을 때, 어떤 클러스터에서 어떤 권한으로 실행할지를 정해야 함
그걸 결정하는 게 context이고, context 전환을 통해 kubectl이 바라보는 클러스터를 바꿔주는 것임
3) 실제 예시
현재 context 확인
kubectl config current-context
-> 출력: dev-context
pod 조회 → dev-cluster 기준으로 조회됨
kubectl get pods -n default
만약 운영 클러스터로 바꾸고 싶다면, prod 클러스터로 context 전환
kubectl config use-context prod-context
이제 같은 명령어를 쳐도 prod-cluster에서 실행됨
kubectl get pods -n default
4) 정리