AWS Outpost EKS 로컬 클러스터의 노드 Kubelet Client 인증서 자동 갱신 활성화 방안

7분 분량
콘텐츠 수준: 중급
4

본 기사는 EKS on Outpost 로컬 클러스터 환경에서 워커 노드의 Kubelet Client 인증서 만료(기한 1년)에 따른 인증서 자동 갱신 설정을 설명합니다. 또한 Kubelet Client 인증서의 자동 갱신 설정으로 노드 리소스에 영향을 미치는 부분은 고려하지 않습니다.

본 기사에서 설명하는 노드 그룹은 AWS Outpost의 EKS 로컬 클러스터의 노드 그룹만 해당됩니다.

Amazon EKS는 AWS 및 온프레미스에서 Kubernetes를 쉽게 실행할 수 있도록 지원하는 관리형 Kubernetes 서비스입니다.
AWS Outpost 서비스는 AWS 인프라, 서비스, API 및 도구를 온프레미스로 확장하는 완전 관리형 서비스이며 Outpost 내 컨트롤 플레인 노드와 워커 노드를 구성할 수 있습니다. AWS Outpost 서비스에 대한 자세한 설명은 문서를 통해 참조할 수 있습니다.

Amazon EKS의 컨트롤 플레인의 위치는 AWS 리전을 사용하는 확장 클러스터와 Outpost 내 컨트롤 플레인을 직접 배포하는 로컬 클러스터 환경으로 서비스를 제공합니다.
각 배포 옵션에 대한 필요성과 옵션은 문서를 통해 자세히 확인할 수 있습니다.

일반적인 Amazon EKS 서비스를 통해 생성된 관리형 노드 그룹의 노드 생성 시 API 서버가 Kubelet과 통신하기 위한 Kubelet 서버 인증서를 생성합니다. Kubelet이 API 서버와 통신하기 위한 Client 구성은 노드의 kubeconfig의 설정을 따르며 IAM의 인증을 통해서 API 서버와 통신을 하게 됩니다. Outpost 확장 클러스터를 사용할 때에도 동일하며 IAM을 통해서 노드는 API 서버와 통신을 수행합니다. 확장 클러스터의 노드가 AWS 리전과의 네트워크 연결성이 좋지 않을 경우에는 통신이 불가할 수 있습니다.

EKS 관리형 노드 그룹의 노드

$ kubectl get csr
NAME        AGE   SIGNERNAME                      REQUESTOR                                                 REQUESTEDDURATION   CONDITION
csr-6cr88   28m   kubernetes.io/kubelet-serving   system:node:ip-10-X-Y-Z.ap-northeast-2.compute.internal   <none>              Approved,Issued

하지만, 일반적인 Kubernetes에서 Kubelet은 API 서버와 통신하기 위한 아래의 두 가지 인증서를 필요로 합니다.

  • kubelet에서 API 서버와 통신하기 위한 kubelet Client 인증서
  • API 서버가 kubelet과 통신하기 위한 kubelet Server 인증서

EKS on Outpost 로컬 클러스터의 워커 노드는 자체 관리형 노드로 구성되어 있으며 x509 인증서를 통해 API 서버와 통신하게 됩니다. 즉, Outpost 로컬 클러스터에서는 kubelet이 API 서버와 통신하기 위해 x509 Client 인증서가 필요하며 이는 자체 관리형 노드의 특성상 사용자가 관리를 해야 합니다.

AWS Outpost EKS 로컬 클러스터 워커 노드

$ kubectl get csr
NAME        AGE   SIGNERNAME                                    REQUESTOR                                            REQUESTEDDURATION   CONDITION
csr-hgxp6   38s   kubernetes.io/kube-apiserver-client-kubelet   system:node:ip-10-X-Y-Z.us-west-2.compute.internal   <none>              Approved,Issued
csr-mdbtm   36s   kubernetes.io/kubelet-serving                 system:node:ip-10-X-Y-Z.us-west-2.compute.internal   <none>              Approved,Issued

API 서버와 통신하기 위해 생성된 Kubelet Client 인증서는 1년 뒤에 만료가 됩니다. 이에 따라 Outpost 로컬 클러스터 환경에서의 노드는 1년 이내 정기적인 노드의 재배포 혹은 교체 등의 패치를 권장하고 있습니다.
Client 인증서가 만료될 경우, 워커 노드는 NotReady 상태가 되며 Kubelet에서 아래와 같은 로그가 발생하게 됩니다.

Feb 17 09:53:35 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[13449]: E0217 09:53:35.456533   13449 bootstrap.go:266] part of the existing bootstrap client certificate in /var/lib/kubelet/kubeconfig is expired: 2025-02-16 09:51:41 +0000 UTC

이 외에도 아래와 같은 로그 메시지가 식별될 수 있습니다.

  • "certificate has expired or is not yet valid" 메시지
  • "Unauthorized" 또는 "UnauthorizedOperation" 에러
  • "authentication handshake failed" 관련 메시지

이해를 돕기 위해 AWS Outpost EKS 로컬 클러스터를 생성해 보겠습니다. 본 기사는 다음과 같이 설명을 진행합니다.

1. EKS on Outpost 로컬 클러스터 생성

2. 워커 노드 생성 시 Client 인증서 Rotation 활성화

3. 기존 노드의 Client 인증서 Rotation 활성화 방안


우선 EKS on Outpost 로컬 클러스터를 생성하기 위해서 Outpost 계정이 필요합니다. 해당 계정에서 Outpost 콘솔을 통해 자원을 확인한 후에 생성할 수 있습니다.
AWS Outpost를 통해 EKS를 생성할 수 있는 방법으로는 웹 콘솔과 eksctl이 있으며 이와 관련하여 자세한 설명은 공식 문서를 통해 확인해보실 수 있습니다. 본 기사에서는 eksctl을 통한 클러스터 및 노드를 생성합니다.
Enter image description here

1. EKS on Outpost 로컬 클러스터 생성

# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: <ClusterName>
  region: <Region-Code>
  version: "1.27"

vpc:
  clusterEndpoints:
    privateAccess: true
  id: "<VPC-ID>"
  subnets:
    private:
      outpost-subnet-1:
        id: "<Subnet-ID>"

outpost:
  controlPlaneOutpostARN: <Outpost-ARN>
  controlPlaneInstanceType: <Instance-Type>

yaml의 변수 내용을 다음 설명을 참고하여 변경하십시오.

  • <ClusterName>: 클러스터 이름을 설정합니다.
  • <Region-Code>: 클러스터를 생성할 Region 코드를 입력합니다. (ex, ap-northeast-2)
  • <VPC-ID>: Outpost 장비가 속한 VPC를 설정합니다.
  • <Subnet-ID>: Outpost 컨트롤 플레인 노드가 배포될 Subnet을 설정합니다.
  • <Outpost-ARN>: Outpost의 ARN을 설정합니다. (ex, arn:aws:outposts:region-code:1234567890:outpost/op-111111111111)
  • <Instance-Type>: 컨트롤 플레인 노드의 인스턴스 유형을 설정합니다. (ex, m5.xlarge)

eksctl 명령어를 통해 클러스터를 생성합니다.

$ eksctl create cluster -f cluster.yaml --region ap-northeast-2

클러스터가 생성되었다면 아래와 같이 3개의 컨트롤 플레인 노드가 동작하게 됩니다.

$ kubectl get nodes
NAME                                       STATUS     ROLES           AGE   VERSION
ip-10-X-Y-Z.us-west-2.compute.internal   NotReady   control-plane   29h   v1.27.16
ip-10-X-Y-Z.us-west-2.compute.internal   NotReady   control-plane   29h   v1.27.16
ip-10-X-Y-Z.us-west-2.compute.internal   NotReady   control-plane   29h   v1.27.16

2. 워커 노드 생성 시 Client 인증서 Rotation 활성화

API 서버가 kubelet과 통신하기 위한 kubelet 서버 인증서의 경우에는 RotateKubeletServerCertificate: true를 통해 활성화하지 않더라도 Kubernetes Feature gate의 디폴트 옵션으로 활성화되게 됩니다. 이를 통해 Server 인증서는 만료되기 전 자동으로 갱신이 이루어지게 됩니다. 하지만, kubelet이 API 서버와 통신하기 위해 사용하는 Client 인증서는 Kubelet 설정에서 rotateCertificates가 비활성화 되어 있기 때문에 자동으로 갱신되지 않으며, 만료되기 전 자동으로 갱신이 필요할 경우에는 rotateCertificates: true을 통해 Client 인증서에 대한 자동 갱신 기능을 활성화해야 합니다. 그렇지 않다면 1년 후 Client 인증서는 만료되게 됩니다.

# node.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: <ClusterName>
  region: <Region-Code>

nodeGroups:
  - name: <NodegroupName>
    instanceType: <Instance-Type>
    desiredCapacity: 1
    privateNetworking: true # Required Parameters
    subnets:
      - <Subnet-ID>
    kubeletExtraConfig:
        rotateCertificates: true # enable client certificate rotation.
        featureGates:
            RotateKubeletServerCertificate: true # has to be enabled, otherwise it will be disabled

yaml의 변수 내용을 다음 설명을 참고하여 변경하십시오.

  • <ClusterName>: 클러스터 이름을 설정합니다.
  • <Region-Code>: 클러스터를 생성할 Region 코드를 입력합니다. (ex, ap-northeast-2)
  • <NodegroupName>: 노드 그룹의 이름을 설정합니다.
  • <Instance-Type>: 컨트롤 플레인 노드의 인스턴스 유형을 설정합니다. (ex, m5.xlarge)
  • <Subnet-ID>: Outpost 노드 그룹이 배포될 Subnet을 설정합니다.

eksctl 명령어를 통해 워커 노드를 생성합니다.

$ eksctl create nodegroup -f node.yaml

워커 노드를 생성하면 아래와 같이 2개의 인증서가 생성되며 아래와 같습니다.

-rw------- 1 root root 1159 Feb  5 06:25 kubelet-client-2025-02-05-06-25-23.pem
lrwxrwxrwx 1 root root   59 Feb  5 06:25 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2025-02-05-06-25-23.pem
-rw------- 1 root root 1236 Feb  5 06:25 kubelet-server-2025-02-05-06-25-55.pem
lrwxrwxrwx 1 root root   59 Feb  5 06:25 kubelet-server-current.pem -> /var/lib/kubelet/pki/kubelet-server-2025-02-05-06-25-55.pem

위와 같이 Client 인증서에 대한 Rotation을 활성화하였다면, 노드가 생성된 후 kubelet 로그를 통해 활성화 여부와 다음 갱신 날짜를 확인할 수 있습니다.

$ journalctl -u kubelet | grep -i "certificate\|rotation\|csr"
Feb 06 00:02:09 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[361850]: I0206 00:02:09.980216  361850 server.go:837] "Client rotation is on, will bootstrap in background"
Feb 06 00:02:09 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[361850]: I0206 00:02:09.988285  361850 certificate_store.go:130] Loading cert/key pair from "/var/lib/kubelet/pki/kubelet-client-current.pem".
Feb 06 00:02:09 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[361850]: I0206 00:02:09.988631  361850 server.go:894] "Starting client certificate rotation"
Feb 06 00:02:09 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[361850]: I0206 00:02:09.988644  361850 certificate_manager.go:356] kubernetes.io/kube-apiserver-client-kubelet: Certificate rotation is enabled
Feb 06 00:02:09 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[361850]: I0206 00:02:09.994574  361850 certificate_manager.go:356] kubernetes.io/kube-apiserver-client-kubelet: Certificate expiration is 2026-02-05 05:20:08 +0000 UTC, rotation deadline is 2025-11-08 09:05:32.489280387 +0000 UTC
Feb 06 00:02:09 ip-10-X-Y-Z.us-west-2.compute.internal kubelet[361850]: I0206 00:02:09.994640  361850 certificate_manager.go:356] kubernetes.io/kube-apiserver-client-kubelet: Waiting 6609h3m22.494643581s for next certificate rotation

추가로, kubelet-config.json 내 설정이 포함되어 있는 것을 확인할 수 있습니다.

$ cat /etc/kubernetes/kubelet/kubelet-config.json
...(중략)
  "rotateCertificates": true,
...

3. 기존 노드의 Client 인증서 Rotation 활성화 방안

앞서 설명한 바와 같이 Client 인증서 Rotation 활성화는 kubelet-config.json에서 설정이 되기 때문에 노드에 직접 접속하여 kubelet-config.json을 수정한 후에 kubelet 서비스를 재시작하여도 동작하게 됩니다. 하지만 Auto-Scaling 혹은 노드의 교체 등이 이루어진다면 기존에 비활성화 상태로 노드가 다시 생성되기 때문에 올바른 방법이 아닙니다. 로컬 클러스터 환경에서의 노드 그룹은 CloudFormation을 통해 생성되어 관리되게 됩니다. CloudFormation에서 시작 템플릿의 UserData를 직접 수정한 후에 노드를 재배포 하거나 기존 노드를 유지하기 위해 UpdatePolicy를 수정하고 이 후 노드에 대해서는 새로운 시작 템플릿의 버전을 따르게 설정을 고려할 수도 있으나 잘못된 수정으로 인해 노드가 정상적으로 생성되지 않을 수 있습니다. 이에 따라 eksctl 문서에서 설명하는 바와 같이 설정 변경이 필요한 경우에는 새로운 노드 그룹을 생성한 이후에 기존 노드 그룹은 제거하여 적용하는 것을 권장합니다.

eksctl create nodegroup --config-file=<path>

고려사항

Kubelet 설정에서 rotateCertificates: true을 활성화하지 않고 1년 이상 노드를 운영하실 경우에는 API 서버와 통신하기 위한 Client 인증서를 수동으로 갱신해야 합니다. 인증서가 만료될 경우에는 워커 노드는 API 서버와 통신할 수 없기 때문에 NotReady 상태가 됩니다. 노드의 NotReady 상태는 동작 중인 Pod의 볼륨 마운트 변경, Pod의 재시작, 리소스 제한에 대한 모니터링 등이 불가하게 되며 컨트롤 플레인 노드에 배포된 'kube-controller-manger'을 통해 노드의 상태 체크를 수행합니다. 이때 pod-eviction-timeout으로 인해 NotReady 상태의 노드에서 Pod가 eviction 처리될 수 있습니다.

결론

EKS on Outpost 로컬 클러스터를 운영할 때 1년 이내 워커 노드의 정기적인 패치 혹은 교체를 권장하고 있습니다.
운영 환경 특성상 워커 노드의 1년 이상의 장기적인 운영이 필요한 환경일 경우에는 Kubelet 설정에서 'rotateCertificates'을 활성화하여 Client 인증서에 대한 자동 갱신 설정을 할 수 있습니다.
Client 인증서에 대한 자동 갱신 설정을 별도로 하지 않은 경우에는 1년 후 Client 인증서는 만료되기 때문에 만료 전 인증서를 수동으로 갱신해야 합니다.

참고 문서

[1] AWS Outposts를 사용한 Amazon EKS 온프레미스 배포 - https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/eks-outposts.html
[2] PKI 인증서 및 요구 사항- https://kubernetes.io/ko/docs/setup/best-practices/certificates/
[3] https://kubernetes.io/ko/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/
[4] 기능 게이트 - https://kubernetes.io/ko/docs/reference/command-line-tools-reference/feature-gates/
[5] Kubelet Configuration (v1beta1) - https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/

profile pictureAWS
지원 엔지니어
게시됨 한 달 전80회 조회
댓글 없음

관련 콘텐츠