Cloud

EKS Version Upgrade

Hanhorang31 2025. 4. 2. 23:21
 

EKS Version Upgrade 워크샵 내용을 정리합니다.
Amazon EKS Upgrades: Strategies and Best Practices

워크샵 제공해주신 AWS 관계자님 & 가시다님 감사합니다.

 

 

 

Version Upgrade

EKS는 쿠버네티스 프로젝트 릴리스 주기를 따르며, 평균 4개월에 한번씩 마이너 버전이 출시됩니다.

EKS는 마이너 버전 출시 후 14개월동안 EKS 표준 지원을 받습니다.

표준 지원이 끝지나면 12개월동안 확장지원이 지원되나 비용이 6배 증가합니다.

  • 표준 지원 : 클러스터 별 시간당 0.1$, 확장지원 0.6$

25년 4월 기준 버전 출시일정은 다음과 같습니다.

 

Kubernetes 클러스터의 보안, 안정성, 성능 및 호환성을 유지하는 데 중요하며, 플랫폼에서 제공하는 최신 기능과 역량을 활용할 수 있도록 보장합니다.

쿠버네티스 버전은 컨트롤 플레인과 데이터 플레인를 모두 포함합니다.

AWS EKS의 경우 공동 책임 모델에 의하여 AWS가 컨트롤 플레인을 관리하게 되며 , 데이터 플레인은 클러스터 소유자가 업그레이드를 해야할 책임이 있습니다.

 

업그레이드 버전 순서

클러스터 버전 업그레이드 순서는 다음과 같습니다.

  1. EKS 업그레이드 준비
  2. 클러스터 백업(선택사항)
  3. 컨트롤 플레인 업그레이드
  4. Addon 업그레이드
  5. 데이터 플레인 업그레이드(In -Plcae, Blue-Green)

 

참고 사항

  • version skew policy에 따라 컨트롤 플레인과 데이터 플레인간 3개의 마이너 버전 호환성을 가집니다.
  • 버전 순서를 바꾸는 것은 예상치 못한 동작을 발생합니다.

 

실습 환경 확인

EKS 컨트롤 플레인, 데이터 플레인 Version 1.25 Terraform EKS BluePrint 로 구성

 

# 클러스터 구성 확인 
eksctl get cluster
NAME                    REGION          EKSCTL CREATED
eksworkshop-eksctl      us-west-2       False

# 컨트롤 플레인 버전 확인
aws eks describe-cluster --name $EKS_CLUSTER_NAME | jq
..
"cluster": {
  "version": "1.25",
 ..


# addon 구성 확인 1. AWS addon 
eksctl get addon --cluster $CLUSTER_NAME
NAME                    VERSION                 STATUS  ISSUES  IAMROLE                                                                                         UPDATE AVAILABLE              CONFIGURATION VALUES
aws-ebs-csi-driver      v1.41.0-eksbuild.1      ACTIVE  0       arn:aws:iam::773588311077:role/eksworkshop-eksctl-ebs-csi-driver-2025033005471319150000001d
coredns                 v1.8.7-eksbuild.10      ACTIVE  0                                                                                                       v1.9.3-eksbuild.22,v1.9.3-eksbuild.21,v1.9.3-eksbuild.19,v1.9.3-eksbuild.17,v1.9.3-eksbuild.15,v1.9.3-eksbuild.11,v1.9.3-eksbuild.10,v1.9.3-eksbuild.9,v1.9.3-eksbuild.7,v1.9.3-eksbuild.6,v1.9.3-eksbuild.5,v1.9.3-eksbuild.3,v1.9.3-eksbuild.2
kube-proxy              v1.25.16-eksbuild.8     ACTIVE  0
vpc-cni                 v1.19.3-eksbuild.1      ACTIVE  0

# addon 구성 확인 2. 서드 파티
helm list -A 
NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                   APP VERSION
argo-cd                         argocd          1               2025-03-30 05:52:33.912269919 +0000 UTC deployed        argo-cd-5.55.0                          v2.10.0    
aws-efs-csi-driver              kube-system     1               2025-03-30 05:53:03.366112976 +0000 UTC deployed        aws-efs-csi-driver-2.5.6                1.7.6      
aws-load-balancer-controller    kube-system     1               2025-03-30 05:53:03.311299928 +0000 UTC deployed        aws-load-balancer-controller-1.7.1      v2.7.1     
karpenter                       karpenter       1               2025-03-30 05:53:02.547414863 +0000 UTC deployed        karpenter-0.37.0                        0.37.0     
metrics-server                  kube-system     1               2025-03-30 05:52:33.909528959 +0000 UTC deployed        metrics-server-3.12.0                   0.7.0 

# 데이터 플레인 버전 확인 1. 노드 그룹 
eksctl get nodegroup --cluster $CLUSTER_NAME
CLUSTER                 NODEGROUP                               STATUS  CREATED                 MIN SIZE        MAX SIZE        DESIRED CAPACITY        INSTANCE TYPE                   IMAGE ID       ASG NAME                                                                        TYPE
eksworkshop-eksctl      blue-mng-2025033005474785250000002b     ACTIVE  2025-03-30T05:47:49Z    1               2               1                       m5.large,m6a.large,m6i.large    AL2_x86_64     eks-blue-mng-2025033005474785250000002b-f2caf2cb-c39d-eadb-7113-a366a35c0387    managed
eksworkshop-eksctl      initial-20250330054747829400000029      ACTIVE  2025-03-30T05:47:49Z    2               10              2                       m5.large,m6a.large,m6i.large    AL2_x86_64     eks-initial-20250330054747829400000029-68caf2cb-c38c-dd8d-d589-e617df1c2afd     managed


# 데이터 플레인 버전 확인 2. fargate profile 구성 확인 
eksctl get fargateprofile --cluster $CLUSTER_NAME 
NAME            SELECTOR_NAMESPACE      SELECTOR_LABELS POD_EXECUTION_ROLE_ARN                                                  SUBNETS                                                       TAGS                                                                                                                                     STATUS
fp-profile      assets                  <none>          arn:aws:iam::773588311077:role/fp-profile-20250330054742331800000021    subnet-00b8866f562019542,subnet-03d62f35a7786e72f,subnet-083e8dff66d967d83     Blueprint=eksworkshop-eksctl,GithubRepo=github.com/aws-ia/terraform-aws-eks-blueprints,karpenter.sh/discovery=eksworkshop-eksctl        ACTIVE

# 데이터 플레인 버전 확인 3. Karpenter 
kubectl get nodepools
NAME      NODECLASS
default   default

# 데이터 플레인 버전 확인 3. Karpenter
kubectl get nodeclaims
NAME            TYPE        ZONE         NODE                                       READY   AGE
default-cl56j   m6i.large   us-west-2a   ip-10-0-13-94.us-west-2.compute.internal   True    2d8h


# 노드 그룹 구성 확인 
kubectl get nodes \
  --label-columns=eks.amazonaws.com/nodegroup,karpenter.sh/nodepool,eks.amazonaws.com/capacityType,node.kubernetes.io/lifecycle,karpenter.sh/capacity-type,eks.amazonaws.com/compute-type,node.kubernetes.io/instance-type,kubernetes.io/arch,kubernetes.io/os,topology.kubernetes.io/zone
NAME                                                STATUS   ROLES    AGE    VERSION                NODEGROUP                             NODEPOOL   CAPACITYTYPE   LIFECYCLE      CAPACITY-TYPE   COMPUTE-TYPE   INSTANCE-TYPE   ARCH    OS      ZONE
fargate-ip-10-0-46-241.us-west-2.compute.internal   Ready    <none>   2d8h   v1.25.16-eks-2d5f260                                                                                                  fargate                        amd64   linux   us-west-2c
ip-10-0-13-94.us-west-2.compute.internal            Ready    <none>   2d8h   v1.25.16-eks-59bf375                                         default                                  spot                           m6i.large       amd64   linux   us-west-2a
ip-10-0-16-54.us-west-2.compute.internal            Ready    <none>   2d8h   v1.25.16-eks-59bf375                                                                   self-managed                                  m5.large        amd64   linux   us-west-2b
ip-10-0-17-142.us-west-2.compute.internal           Ready    <none>   2d8h   v1.25.16-eks-59bf375   initial-20250330054747829400000029               ON_DEMAND                                                    m5.large        amd64   linux   us-west-2b
ip-10-0-42-186.us-west-2.compute.internal           Ready    <none>   2d8h   v1.25.16-eks-59bf375   initial-20250330054747829400000029               ON_DEMAND                                                    m5.large        amd64   linux   us-west-2c
ip-10-0-45-187.us-west-2.compute.internal           Ready    <none>   2d8h   v1.25.16-eks-59bf375                                                                   self-managed                                  m5.large        amd64   linux   us-west-2c
ip-10-0-6-32.us-west-2.compute.internal             Ready    <none>   2d8h   v1.25.16-eks-59bf375   blue-mng-2025033005474785250000002b              ON_DEMAND                                                    m5.large        amd64   linux   us-west-2a

# 노드 테인트 확인 
kubectl get nodes -o custom-columns='NODE:.metadata.name,TAINTS:.spec.taints[*].key,VALUES:.spec.taints[*].value,EFFECTS:.spec.taints[*].effect'
NODE                                                TAINTS                           VALUES        EFFECTS
fargate-ip-10-0-46-241.us-west-2.compute.internal   eks.amazonaws.com/compute-type   fargate       NoSchedule
ip-10-0-13-94.us-west-2.compute.internal            dedicated                        CheckoutApp   NoSchedule
ip-10-0-16-54.us-west-2.compute.internal            <none>                           <none>        <none>
ip-10-0-17-142.us-west-2.compute.internal           <none>                           <none>        <none>
ip-10-0-42-186.us-west-2.compute.internal           <none>                           <none>        <none>
ip-10-0-45-187.us-west-2.compute.internal           <none>                           <none>        <none>
ip-10-0-6-32.us-west-2.compute.internal             dedicated    
  • fargete 전용 노드
  • CheckoutApp, OrderApp 전용 노드 Toleration이 없다면 해당 파드로 못 들어감

# crd 확인 
kubectl get crd
NAME                                         CREATED AT
applications.argoproj.io                     2025-03-30T05:52:36Z
applicationsets.argoproj.io                  2025-03-30T05:52:37Z
appprojects.argoproj.io                      2025-03-30T05:52:36Z
cninodes.vpcresources.k8s.aws                2025-03-30T05:43:32Z
ec2nodeclasses.karpenter.k8s.aws             2025-03-30T05:53:02Z
eniconfigs.crd.k8s.amazonaws.com             2025-03-30T05:45:21Z
ingressclassparams.elbv2.k8s.aws             2025-03-30T05:53:03Z
nodeclaims.karpenter.sh                      2025-03-30T05:53:02Z
nodepools.karpenter.sh                       2025-03-30T05:53:02Z
policyendpoints.networking.k8s.aws           2025-03-30T05:43:32Z
securitygrouppolicies.vpcresources.k8s.aws   2025-03-30T05:43:32Z
targetgroupbindings.elbv2.k8s.aws            2025-03-30T05:53:03Z

# argoCD 확인 
kubectl get applications -n argocd
NAME        SYNC STATUS   HEALTH STATUS
apps        Synced        Healthy
assets      Synced        Healthy
carts       Synced        Healthy
catalog     Synced        Healthy
checkout    Synced        Healthy
karpenter   Synced        Healthy
orders      Synced        Healthy
other       Synced        Healthy
rabbitmq    Synced        Healthy
ui          OutOfSync     Healthy

# 구성 파드 확인
kubectl get pod -A
NAMESPACE     NAME                                                        READY   STATUS    RESTARTS       AGE
argocd        argo-cd-argocd-application-controller-0                     1/1     Running   0              2d8h
argocd        argo-cd-argocd-applicationset-controller-74d9c9c5c7-dhkpv   1/1     Running   0              2d8h
argocd        argo-cd-argocd-dex-server-6dbbd57479-csnkh                  1/1     Running   0              2d8h
argocd        argo-cd-argocd-notifications-controller-fb4b954d5-d5fsq     1/1     Running   0              2d8h
argocd        argo-cd-argocd-redis-76b4c599dc-gs47l                       1/1     Running   0              2d8h
argocd        argo-cd-argocd-repo-server-6b777b579d-5wxgm                 1/1     Running   0              2d8h
argocd        argo-cd-argocd-server-86bdbd7b89-klvk6                      1/1     Running   0              2d8h
assets        assets-7ccc84cb4d-ngfjf                                     1/1     Running   0              2d8h
carts         carts-7ddbc698d8-xqffb                                      1/1     Running   0              2d8h
carts         carts-dynamodb-6594f86bb9-l9q5g                             1/1     Running   0              2d8h
catalog       catalog-857f89d57d-mzllb                                    1/1     Running   3 (2d8h ago)   2d8h
catalog       catalog-mysql-0                                             1/1     Running   0              2d8h
checkout      checkout-558f7777c-hggz4                                    1/1     Running   0              2d8h
checkout      checkout-redis-f54bf7cb5-dds5x                              1/1     Running   0              2d8h
karpenter     karpenter-5c7d586576-kf2kr                                  1/1     Running   0              2d8h
karpenter     karpenter-5c7d586576-zpsfw                                  1/1     Running   1 (2d8h ago)   2d8h
kube-system   aws-load-balancer-controller-5d7b54456d-9mbrp               1/1     Running   0              2d8h
kube-system   aws-load-balancer-controller-5d7b54456d-bx7nt               1/1     Running   0              2d8h
kube-system   aws-node-bqmkt                                              2/2     Running   0              2d8h
kube-system   aws-node-c7svq                                              2/2     Running   0              2d8h
kube-system   aws-node-d9zzw                                              2/2     Running   0              2d8h
kube-system   aws-node-srv48                                              2/2     Running   0              2d8h
kube-system   aws-node-xmwkj                                              2/2     Running   0              2d8h
kube-system   aws-node-zgvnn                                              2/2     Running   0              2d8h
kube-system   coredns-98f76fbc4-46jrv                                     1/1     Running   0              2d8h
kube-system   coredns-98f76fbc4-m82wn                                     1/1     Running   0              2d8h
kube-system   ebs-csi-controller-6b575b5f4d-7bvw7                         6/6     Running   0              2d8h
kube-system   ebs-csi-controller-6b575b5f4d-mlwhh                         6/6     Running   0              2d8h
kube-system   ebs-csi-node-62nsv                                          3/3     Running   0              2d8h
kube-system   ebs-csi-node-fc7vb                                          3/3     Running   0              2d8h
kube-system   ebs-csi-node-fqzcm                                          3/3     Running   0              2d8h
kube-system   ebs-csi-node-kz9d6                                          3/3     Running   0              2d8h
kube-system   ebs-csi-node-l5rd9                                          3/3     Running   0              2d8h
kube-system   ebs-csi-node-tvxx7                                          3/3     Running   0              2d8h
kube-system   efs-csi-controller-5d74ddd947-9h9nz                         3/3     Running   0              2d8h
kube-system   efs-csi-controller-5d74ddd947-lxftq                         3/3     Running   0              2d8h
kube-system   efs-csi-node-657hw                                          3/3     Running   0              2d8h
kube-system   efs-csi-node-9wxp7                                          3/3     Running   0              2d8h
kube-system   efs-csi-node-bfq44                                          3/3     Running   0              2d8h
kube-system   efs-csi-node-fjzqx                                          3/3     Running   0              2d8h
kube-system   efs-csi-node-gvdg6                                          3/3     Running   0              2d8h
kube-system   efs-csi-node-s4h6l                                          3/3     Running   0              2d8h
kube-system   kube-proxy-62c2l                                            1/1     Running   0              2d8h
kube-system   kube-proxy-6q5ht                                            1/1     Running   0              2d8h
kube-system   kube-proxy-7n7z6                                            1/1     Running   0              2d8h
kube-system   kube-proxy-9mzh7                                            1/1     Running   0              2d8h
kube-system   kube-proxy-c2wdz                                            1/1     Running   0              2d8h
kube-system   kube-proxy-jddxw                                            1/1     Running   0              2d8h
kube-system   metrics-server-785cd745cd-6n9qn                             1/1     Running   0              2d8h
orders        orders-5b97745747-b9vtf                                     1/1     Running   2 (2d8h ago)   2d8h
orders        orders-mysql-b9b997d9d-8nwm2                                1/1     Running   0              2d8h
rabbitmq      rabbitmq-0                                                  1/1     Running   0              2d8h
ui            ui-5dfb7d65fc-nr2cr                                         1/1     Running   0              2d8h

# pdb 확인 
kubectl get pdb -A
NAMESPACE     NAME                           MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
karpenter     karpenter                      N/A             1                 1                     2d8h
kube-system   aws-load-balancer-controller   N/A             1                 1                     2d8h
kube-system   coredns                        N/A             1                 1                     2d8h
kube-system   ebs-csi-controller             N/A             1                 1                     2d8h

# argocd svc 확인 
kubectl get svc -n argocd argo-cd-argocd-server
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP                                                                   PORT(S)                      AGE
argo-cd-argocd-server   LoadBalancer   172.20.159.26   k8s-argocd-argocdar-039ae6c012-8a23ea8f8b1c8c57.elb.us-west-2.amazonaws.com   80:31453/TCP,443:31616/TCP   2d8h


# arogcd 계정 정보 확인
export ARGOCD_USER="admin"
export ARGOCD_PWD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
echo "Username: ${ARGOCD_USER}"
echo "Password: ${ARGOCD_PWD}"
 

# statefulSet 확인 
kubectl get sts -A
NAMESPACE   NAME                                    READY   AGE
argocd      argo-cd-argocd-application-controller   1/1     2d8h
catalog     catalog-mysql                           1/1     2d8h
rabbitmq    rabbitmq                                1/1     2d8h

# 스토리지클래스 확인
kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
efs             efs.csi.aws.com         Delete          Immediate              true                   2d8h
gp2             kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  2d8h
gp3 (default)   ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   2d8h

# efs 볼륨 확인
kubectl describe sc efs
Name:                  efs
IsDefaultClass:        No
Annotations:           <none>
Provisioner:           efs.csi.aws.com
Parameters:            basePath=/dynamic_provisioning,directoryPerms=755,ensureUniqueDirectory=false,fileSystemId=fs-09c7449db5761f1c3,gidRangeEnd=200,gidRangeStart=100,provisioningMode=efs-ap,reuseAccessPoint=false,subPathPattern=${.PVC.namespace}/${.PVC.name}
AllowVolumeExpansion:  True
MountOptions:
  iam
ReclaimPolicy:      Delete
VolumeBindingMode:  Immediate
Events:             <none>

# PV, PVC 확인
kubectl get pv,pvc -A
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   REASON   AGE
persistentvolume/pvc-3a8a338f-a30f-4bd7-9aa2-251593f40c2f   4Gi        RWO            Delete           Bound    catalog/catalog-mysql-pvc     gp3                     2d8h
persistentvolume/pvc-a33898ed-3a5b-41f0-934f-8e74977a3b22   4Gi        RWO            Delete           Bound    orders/order-mysql-pvc        gp3                     2d8h
persistentvolume/pvc-f2535625-698f-48e2-b2c8-b172a1b440ec   4Gi        RWO            Delete           Bound    checkout/checkout-redis-pvc   gp3                     2d8h

NAMESPACE   NAME                                       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
catalog     persistentvolumeclaim/catalog-mysql-pvc    Bound    pvc-3a8a338f-a30f-4bd7-9aa2-251593f40c2f   4Gi        RWO            gp3            2d8h
checkout    persistentvolumeclaim/checkout-redis-pvc   Bound    pvc-f2535625-698f-48e2-b2c8-b172a1b440ec   4Gi        RWO            gp3            2d8h
orders      persistentvolumeclaim/order-mysql-pvc      Bound    pvc-a33898ed-3a5b-41f0-934f-8e74977a3b22   4Gi        RWO            gp3            2d8h

# 클러스터 내 접근가능한 IAM 권한 
aws eks list-access-entries --cluster-name $CLUSTER_NAME
{
    "accessEntries": [
        "arn:aws:iam::773-:role/aws-service-role/eks.amazonaws.com/AWSServiceRoleForAmazonEKS",
        "arn:aws:iam::773-:role/blue-mng-eks-node-group-2025033005375015570000000e",
        "arn:aws:iam::773-:role/default-selfmng-node-group-20250330053750304900000010",
        "arn:aws:iam::773-:role/fp-profile-20250330054742331800000021",
        "arn:aws:iam::773-:role/initial-eks-node-group-2025033005375015740000000f",
        "arn:aws:iam::773-:role/karpenter-eksworkshop-eksctl",
        "arn:aws:iam::773-:role/workshop-stack-ekstfcodebuildStackDeployProjectRole-NHlxMh6z4HgI"
    ]
}
# 권한 매핑 확인
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN                                                                                     USERNAME                                GROUPS                                                  ACCOUNT
arn:aws:iam::77-:role/WSParticipantRole                                        admin                                   system:masters
arn:aws:iam::77-:role/blue-mng-eks-node-group-2025033005375015570000000e       system:node:{{EC2PrivateDNSName}}       system:bootstrappers,system:nodes
arn:aws:iam::77-:role/fp-profile-20250330054742331800000021                    system:node:{{SessionName}}             system:bootstrappers,system:nodes,system:node-proxier
arn:aws:iam::77-:role/initial-eks-node-group-2025033005375015740000000f        system:node:{{EC2PrivateDNSName}}       system:bootstrappers,system:nodes
arn:aws:iam::77-:role/workshop-stack-IdeIdeRoleD654ADD4-FsCM43iYJfVE           admin                                   system:masters

# RBAC 확인
kubectl describe cm -n kube-system aws-auth
..
- groups:
  - system:bootstrappers
  - system:nodes
  rolearn: arn:aws:iam::773-:role/blue-mng-eks-node-group-2025033005375015570000000e
  username: system:node:{{EC2PrivateDNSName}}
- groups:
  - system:bootstrappers
  - system:nodes
  rolearn: arn:aws:iam::773-:role/initial-eks-node-group-2025033005375015740000000f
  username: system:node:{{EC2PrivateDNSName}}
- groups:
  - system:bootstrappers
  - system:nodes
  - system:node-proxier
  rolearn: arn:aws:iam::773-:role/fp-profile-20250330054742331800000021
  username: system:node:{{SessionName}}
- groups:
  - system:masters
  rolearn: arn:aws:iam::773-:role/WSParticipantRole
  username: admin
- groups:
  - system:masters
  rolearn: arn:aws:iam::773-:role/workshop-stack-IdeIdeRoleD654ADD4-FsCM43iYJfVE
  username: admin
  ..
  
# IRSA 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
No iamserviceaccounts found

ec2-user:~/environment:$ kubectl describe sa -A | grep role-arn 
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::773588311077:role/karpenter-20250330055301070200000039
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::773588311077:role/alb-controller-2025033005530108620000003a
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::773588311077:role/eksworkshop-eksctl-ebs-csi-driver-2025033005471319150000001d
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::773588311077:role/aws-efs-csi-driver-2025033005530109790000003e
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::773588311077:role/aws-efs-csi-driver-2025033005530109790000003e

# pod identity 없음 
eksctl get podidentityassociation --cluster $CLUSTER_NAME
No podidentityassociations found

# OIDC
aws eks describe-cluster --name $CLUSTER_NAME --query cluster.identity.oidc.issuer --output text
https://oidc.eks.us-west-2.amazonaws.com/id/492AE2DC69F87A8BD4CBDACD8C43A003..

# s3 버킷 확인
aws s3 ls
2025-03-30 05:34:08 workshop-stack-tfstatebackendbucketf0fc9a9d-2hmno5ruguo3

aws s3 ls s3://workshop-stack-tfstatebackendbucketf0fc9a9d-2hmno5ruguo3
2025-03-30 05:55:47     933696 terraform.tfstate

# state 확인 
aws s3 cp s3://workshop-stack-tfstatebackendbucketf0fc9a9d-2hmno5ruguo3/terraform.tfstate .

# 테라폼 확인
terraform state list 
data.aws_availability_zones.available
data.aws_caller_identity.current
data.aws_ecrpublic_authorization_token.token
data.aws_partition.current
aws_codecommit_repository.gitops_repo_cc
aws_eks_access_entry.karpenter_node_access_entry
aws_iam_service_specific_credential.argocd_codecommit_credential
aws_iam_user.argocd_user
aws_iam_user_policy.argocd_user_codecommit_rw
aws_secretsmanager_secret.argocd_user_creds_secret
aws_secretsmanager_secret_version.argocd_user_creds_secret_version
..

# 환경 변수 확인
cat ~/.bashrc

1. 업그레이드 준비

1.1 업그레이드 인사이트

릴리스 노트를 검토하여 더 이상 사용되지 않거나 제거된 Kubernetes API를 확인한 다음 해당 API를 사용하는 애플리케이션을 확인하는 작업

aws eks list-insights --filter kubernetesVersions=1.26 --cluster-name $CLUSTER_NAME | jq .
 
 

매니페스트 버전 마이그레이션 도구인 kubectl-covert 로 마이그레이션할 수 있습니다.

# kubectl-convert 플러그인 설치 
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert"

# 사용 동작
kubectl convert -f  --output-version /

 

 

1.2 업그레이드 체크리스트 확인

1.2.1 버전 업그레이드 변동 사항은 AWS 문서를 통해 참고 1.31 1.32

1.2.2 addon 및 서드 파티 도구들 호환성 확인 - 각 문서 확인

  • AWS addon 들은 AWS 콘솔에서 버전 별로 업그레이드가 가능하며, 서드 파티들은 각 홈페이지에서 버전 호환성을 확인해야함
  • 테스트 환경에서 충분히 사전 테스트를 해보며, velero 와 같은 백업 툴로 중요 데이터를 백업하는 것을 추천
  1. Amazon VPC CNI: Upgrades for Amazon EKS Add-on deployments are limited to single minor version bumps at a time.
  2. kube-proxy: Find upgrade instructions in Updating the Kubernetes kube-proxy self-managed add-on .
  3. CoreDNS: Upgrade guidance is available in Updating the CoreDNS self-managed add-on .
  4. AWS Load Balancer Controller: Compatibility with your EKS version is crucial. Refer to the installation guide  for details.
  5. Amazon EBS/EFS CSI Drivers: Installation and upgrade information can be found in Managing the Amazon EBS CSI driver as an Amazon EKS add-on  and Amazon EFS CSI driver  respectively.
  6. Metrics Server: For more information, see metrics-server on GitHub .
  7. Cluster Autoscaler: Upgrade by modifying the image version within the deployment definition. Given its tight coupling with the scheduler, the Cluster Autoscaler typically requires upgrading alongside the cluster itself. Consult the GitHub releases  to locate the latest image compatible with your Kubernetes minor version.
  8. Karpenter: Refer to the Karpenter documentation for installation and upgrade instructions .

 

1.3 EKS 컨트롤 플레인 업그레이드 전 기본 요구 사항 확인

1.3.1 사용 가능한 IP주소 확인 : 클러스터 생성 시 지정한 서브넷 내에 최소 5개의 여유 IP 주소가 필요

 

aws ec2 describe-subnets --subnet-ids \
  $(aws eks describe-cluster --name ${CLUSTER_NAME} \
  --query 'cluster.resourcesVpcConfig.subnetIds' \
  --output text) \
  --query 'Subnets[*].[SubnetId,AvailabilityZone,AvailableIpAddressCount]' \
  --output table
  
----------------------------------------------------
|                  DescribeSubnets                 |
+---------------------------+--------------+-------+
|  subnet-00b8866f562019542 |  us-west-2b  |  4049 |
|  subnet-03d62f35a7786e72f |  us-west-2c  |  4048 |
|  subnet-083e8dff66d967d83 |  us-west-2a  |  4050 |
+---------------------------+--------------+-------+
  • 서브넷이 IP가 없는 경우 새로운 서브넷을 추가
  • VPC 자체 사용할 서브넷이 없다면 추가 CIDR 블록을 추가(Add Private CIDR Blocks, Subnet Update based on New CIDR)

1.3.2 IAM Role 확인

ROLE_ARN=$(aws eks describe-cluster --name ${CLUSTER_NAME} \
  --query 'cluster.roleArn' --output text)
aws iam get-role --role-name ${ROLE_ARN##*/} \
  --query 'Role.AssumeRolePolicyDocument'
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EKSClusterAssumeRole",
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com"
            },
            "Action": [
                "sts:TagSession",
                "sts:AssumeRole"
            ]
        }
    ]
}

1.3.3 보안 그룹 확인

클러스터를 생성할 때 EKS는 eks-cluster-sg-my-cluster-uniqueID 라는 보안그룹을 가짐.

보안 그룹 안에는 다음의 기본 룰이 존재

 
  1. 자동 태그: EKS는 클러스터에 대해 만든 보안 그룹에 특정 태그를 주입합니다. 이러한 태그는 작동에 필수적이며 제거하면 다시 추가됩니다.
  2. 리소스 연결 : 이 보안 그룹은 여러 리소스에 자동으로 연결됩니다.
  3. 기본 보안 규칙 : 처음에 보안 그룹은 제한 없는 통신을 허용합니다.
  4. 사용자 지정 보안 그룹(선택 사항) : 클러스터 생성 중에 선택적으로 자체 보안 그룹을 지정할 수 있습니다. 그렇게 하는 경우:

기본적으로 EKS는 광범위한 권한을 가진 기본 보안 그룹을 제공하지만, 사용자가 사용자 정의 그룹을 사용하여 더 엄격한 보안 정책을 구현할 수 있도록 합니다.

 

 

추가 고려사항

  • 아웃바운드 인터넷 액세스 (선택사항)
  • 컨테이너 이미지 액세스
  • IPv4/IPv6에 대한 별도 규칙

 

1.4 데이터 플레인 워크로드 보장

중요한 워크로드의 경우 가용성을 보장하기 위해 적절한 옵션을 설정해야 합니다.

  • Pod disruption budgets(PDB) : 설정된 파드가 사용 가능한 상태로 유지되도록하는 옵션
  • ToplologySpreadConstraints : 특정 노드나 가용영역에 쏠리지 않게 퍼지도록 강제하는 옵션

1.4.1 PDB 설정

kubectl get pdb 

cd ~/environment
git clone codecommit::${REGION}://eks-gitops-repo 

# 예제 파드 PDB 설정
cat << EOF > ~/environment/eks-gitops-repo/apps/orders/pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: orders-pdb
  namespace: orders
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: service
      app.kubernetes.io/instance: orders
      app.kubernetes.io/name: orders
EOF

# 변경사항 커밋
echo "  - pdb.yaml" >> ~/environment/eks-gitops-repo/apps/orders/kustomization.yaml
cd ~/environment/eks-gitops-repo/
git add apps/orders/kustomization.yaml
git add apps/orders/pdb.yaml
git commit -m "Add PDB to orders"
git push

# argoCD 싱크 
export ARGOCD_SERVER=$(kubectl get svc argo-cd-argocd-server -n argocd -o json | jq --raw-output '.status.loadBalancer.ingress[0].hostname')
echo "ArgoCD URL: http://${ARGOCD_SERVER}"
export ARGOCD_USER="admin"
export ARGOCD_PWD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
echo "Username: ${ARGOCD_USER}"
echo "Password: ${ARGOCD_PWD}"

argocd login ${ARGOCD_SERVER} --username ${ARGOCD_USER} --password ${ARGOCD_PWD} --insecure --skip-test-tls --grpc-web 
argocd app sync orders 
..
GROUP   KIND                   NAMESPACE  NAME             STATUS  HEALTH   HOOK  MESSAGE
        Namespace                         orders           Synced                 namespace/orders unchanged
policy  PodDisruptionBudget    orders     orders-pdb       Synced                 poddisruptionbudget.policy/orders-pdb created
        ServiceAccount         orders     orders           Synced                 serviceaccount/orders unchanged
        Secret                 orders     orders-db        Synced                 secret/orders-db unchanged
        ConfigMap              orders     orders           Synced                 configmap/orders unchanged
        PersistentVolumeClaim  orders     order-mysql-pvc  Synced  Healthy        persistentvolumeclaim/order-mysql-pvc unchanged
        Service                orders     orders           Synced  Healthy        service/orders unchanged
        Service                orders     orders-mysql     Synced  Healthy        service/orders-mysql unchanged
apps    Deployment             orders     orders           Synced  Healthy        deployment.apps/orders unchanged
apps    Deployment             orders     orders-mysql     Synced  Healthy        deployment.apps/orders-mysql unchanged 
 

1.4.2 토폴리지 설정 확인

# 토폴로지 설정 확인 
kubectl get deploy -A -o json | jq '.items[] | select(.spec.template.spec.topologySpreadConstraints != null) | {namespace: .metadata.namespace, name: .metadata.name, constraints: .spec.template.spec.topologySpreadConstraints}'
{
  "namespace": "karpenter",
  "name": "karpenter",
  "constraints": [
    {
      "labelSelector": {
        "matchLabels": {
          "app.kubernetes.io/instance": "karpenter",
          "app.kubernetes.io/name": "karpenter"
        }
      },
      "maxSkew": 1,
      "topologyKey": "topology.kubernetes.io/zone",
      "whenUnsatisfiable": "ScheduleAnyway"
    }
  ]
}
  • labelSelector.matchLabels - app.kubernetes.io/instance=karpenter, app.kubernetes.io/name=karpenter 라벨을 가진 파드에만 적용됨
  • maxSkew: 1 - 각 Zone 간 파드 개수 차이가 최대 1개 이하로 유지되도록 함
  • topologyKey: topology.kubernetes.io/zone - Zone 단위로 파드를 고르게 분산시킴
  • whenUnsatisfiable: ScheduleAnyway - 조건을 만족 못 해도 파드는 스케줄됨 (경고만 남김)

토폴리지가 변경되는 경우 파드가 재배포됩니다.

운영 환경에서 적용시 주의가 필요합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout
spec:
  template:
    spec:
      containers:
        - name: checkout
          image: ...
          # 생략
      topologySpreadConstraints:   # 추가
        - labelSelector:
            matchLabels:
              app.kubernetes.io/instance: checkout
              app.kubernetes.io/name: checkout
          maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: ScheduleAnyway
 

업그레이드 전략 선택

전략
장점
단점
In-Place 업그레이드
- 기존 VPC, 서브넷, 보안 그룹 유지 - 동일한 API 엔드포인트 유지 - 여러 클러스터를 관리할 필요 없음 - 상태 저장 앱과 데이터 마이그레이션 최소화
- 가동 중지 최소화를 위한 사전 계획 필요 - 여러 버전 건너뛸 경우 단계별 업그레이드 필요 - 실패 시 롤백 어려움 (Control Plane은 롤백 불가) - 구성 요소 및 종속성 호환성 테스트 필요
Blue-Green 업그레이드
- 새 클러스터에서 충분히 테스트 가능 - 여러 버전 건너뛰기 가능 - 문제 발생 시 빠르게 롤백 가능 - 업그레이드 중에도 무중단 운영 가능
- 두 개의 클러스터를 유지해야 하므로 리소스/비용 증가 - 트래픽 전환 및 리소스 관리 복잡 - 외부 도구(CI/CD, 모니터링 등) 설정 변경 필요 - 상태 저장 앱은 데이터 동기화가 어려울 수 있음
  • In-Place 업그레이드는 다운타임 허용 범위가 낮은 부 버전 업그레이드에 적합한 반면, Blue-Green 업그레이드는 주요 버전 업그레이드나 상태 복잡도가 높은 애플리케이션에 권장됩니다.
업그레이드는 테라폼을 기반으로 진행합니다.

In-place 업그레이드 (1.25 > 1.26)

 

컨트롤 플레인 업그레이드

준비 사항

  • EKS 클러스터를 업데이트하기 위해 클러스터를 생성할 때 지정한 서브넷에서 최대 5개의 사용 가능한 IP 주소가 필요합니다.
  • 업그레이드 인사이트 확인(API 호환성 확인)
  • 클러스터에 비밀 암호화가 활성화된 경우 클러스터 IAM 역할에 AWS Key Management Service(AWS KMS) 키를 사용할 수 있는 권한이 있는지 확인 필요
  • 컨테이너 이미지 확인
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c > 1.25.txt
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.19.3-eksbuild.1
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-network-policy-agent:v1.2.0-eksbuild.1
  8 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/aws-ebs-csi-driver:v1.41.0
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/coredns:v1.8.7-eksbuild.10
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-attacher:v4.8.1-eks-1-32-7
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-node-driver-registrar:v2.13.0-eks-1-32-7
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-provisioner:v5.2.0-eks-1-32-7
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-resizer:v1.13.2-eks-1-32-7
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-snapshotter:v8.2.1-eks-1-32-7
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/kube-proxy:v1.25.16-minimal-eksbuild.8
  8 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/livenessprobe:v2.14.0-eks-1-32-7
  8 amazon/aws-efs-csi-driver:v1.7.6
  1 amazon/dynamodb-local:1.13.1
  1 ghcr.io/dexidp/dex:v2.38.0
  1 hjacobs/kube-ops-view:20.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-assets:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-cart:0.7.0
  1 public.ecr.aws/aws-containers/retail-store-sample-catalog:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-checkout:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-orders:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-ui:0.4.0
  1 public.ecr.aws/bitnami/rabbitmq:3.11.1-debian-11-r0
  2 public.ecr.aws/docker/library/mysql:8.0
  1 public.ecr.aws/docker/library/redis:6.0-alpine
  1 public.ecr.aws/docker/library/redis:7.0.15-alpine
  2 public.ecr.aws/eks-distro/kubernetes-csi/external-provisioner:v3.6.3-eks-1-29-2
  8 public.ecr.aws/eks-distro/kubernetes-csi/livenessprobe:v2.11.0-eks-1-29-2
  6 public.ecr.aws/eks-distro/kubernetes-csi/node-driver-registrar:v2.9.3-eks-1-29-2
  2 public.ecr.aws/eks/aws-load-balancer-controller:v2.7.1
  2 public.ecr.aws/karpenter/controller:0.37.0@sha256:157f478f5db1fe999f5e2d27badcc742bf51cc470508b3cebe78224d0947674f
  5 quay.io/argoproj/argocd:v2.10.0
  1 registry.k8s.io/metrics-server/metrics-server:v0.7.0
 

컨트롤 플레인 코드 확인 후 버전 업데이트

 
terraform state list

# 결과 텍스트 확인
terraform plan -out=tfplan.out
terraform show tfplan.out > plan.txt

텍스트가 깨져서 확인되지만, plan 결과를 확인할 수 있습니다.

 
terraform apply -auto-approve # 자동 적용 옵션 활성화 

# 8분 정도 소요
aws eks describe-cluster --name $EKS_CLUSTER_NAME | jq 
  "cluster": {
    "name": "eksworkshop-eksctl",
    "arn": "arn:aws:eks:us-west-2:773588311077:cluster/eksworkshop-eksctl",
    "createdAt": "2025-03-30T05:38:01.163000+00:00",
    "version": "1.26", # 업그레이드 확인
    "endpoint": "https://492AE2DC69F87A8BD4CBDACD8C43A003.gr7.us-west-2.eks.amazonaws.com",
    "roleArn": "arn:aws:iam::773588311077:role/eksworkshop-eksctl-cluster-20250330053737244700000003",
    "resourcesVpcConfig": {
    
    
# 컨테이너 이미지 확인 
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c > 1.26 

  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.19.3-eksbuild.1
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-network-policy-agent:v1.2.0-eksbuild.1
  8 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/aws-ebs-csi-driver:v1.41.0
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/coredns:v1.8.7-eksbuild.10
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-attacher:v4.8.1-eks-1-32-7
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-node-driver-registrar:v2.13.0-eks-1-32-7
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-provisioner:v5.2.0-eks-1-32-7
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-resizer:v1.13.2-eks-1-32-7
  2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/csi-snapshotter:v8.2.1-eks-1-32-7
  6 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/kube-proxy:v1.25.16-minimal-eksbuild.8
  8 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/livenessprobe:v2.14.0-eks-1-32-7
  8 amazon/aws-efs-csi-driver:v1.7.6
  1 amazon/dynamodb-local:1.13.1
  1 ghcr.io/dexidp/dex:v2.38.0
  1 public.ecr.aws/aws-containers/retail-store-sample-assets:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-cart:0.7.0
  1 public.ecr.aws/aws-containers/retail-store-sample-catalog:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-checkout:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-orders:0.4.0
  1 public.ecr.aws/aws-containers/retail-store-sample-ui:0.4.0
  1 public.ecr.aws/bitnami/rabbitmq:3.11.1-debian-11-r0
  2 public.ecr.aws/docker/library/mysql:8.0
  1 public.ecr.aws/docker/library/redis:6.0-alpine
  1 public.ecr.aws/docker/library/redis:7.0.15-alpine
  2 public.ecr.aws/eks-distro/kubernetes-csi/external-provisioner:v3.6.3-eks-1-29-2
  8 public.ecr.aws/eks-distro/kubernetes-csi/livenessprobe:v2.11.0-eks-1-29-2
  6 public.ecr.aws/eks-distro/kubernetes-csi/node-driver-registrar:v2.9.3-eks-1-29-2
  2 public.ecr.aws/eks/aws-load-balancer-controller:v2.7.1
  2 public.ecr.aws/karpenter/controller:0.37.0@sha256:157f478f5db1fe999f5e2d27badcc742bf51cc470508b3cebe78224d0947674f
  5 quay.io/argoproj/argocd:v2.10.0
  1 registry.k8s.io/metrics-server/metrics-server:v0.7.0 
  
# 컨테이너 이미지 변경 사항 확인
diff 1.26.txt 1.25.txt
      

AWS Addon & 서드파티 업그레이드

AWS Addon 과 서드파티에 대해 호환서을 체크 후 업그레이드를 진행해야 합니다.

AWS addon는 AWS CLI 명령어를 통해 관리(버전 확인 및 업데이트 등)를 할 수 있습니다.

# 구성 addon 확인
eksctl get addon --cluster $CLUSTER_NAME 
NAME                    VERSION                 STATUS  ISSUES  IAMROLE                                                                                         UPDATE AVAILABLE              CONFIGURATION VALUES
aws-ebs-csi-driver      v1.41.0-eksbuild.1      ACTIVE  0       arn:aws:iam::773588311077:role/eksworkshop-eksctl-ebs-csi-driver-2025033005471319150000001d
coredns                 v1.8.7-eksbuild.10      ACTIVE  0                                                                                                       v1.9.3-eksbuild.22,v1.9.3-eksbuild.21,v1.9.3-eksbuild.19,v1.9.3-eksbuild.17,v1.9.3-eksbuild.15,v1.9.3-eksbuild.11,v1.9.3-eksbuild.10,v1.9.3-eksbuild.9,v1.9.3-eksbuild.7,v1.9.3-eksbuild.6,v1.9.3-eksbuild.5,v1.9.3-eksbuild.3,v1.9.3-eksbuild.2
kube-proxy              v1.25.16-eksbuild.8     ACTIVE  0                                                                                                       v1.26.15-eksbuild.24,v1.26.15-eksbuild.19,v1.26.15-eksbuild.18,v1.26.15-eksbuild.14,v1.26.15-eksbuild.10,v1.26.15-eksbuild.5,v1.26.15-eksbuild.2,v1.26.13-eksbuild.2,v1.26.11-eksbuild.4,v1.26.11-eksbuild.1,v1.26.9-eksbuild.2,v1.26.7-eksbuild.2,v1.26.6-eksbuild.2,v1.26.6-eksbuild.1,v1.26.4-eksbuild.1,v1.26.2-eksbuild.1
vpc-cni                 v1.19.3-eksbuild.1      ACTIVE  0

# coreDNS 1.26 버전 호환성 확인
aws eks describe-addon-versions --addon-name coredns --kubernetes-version 1.26 --output table \
    --query "addons[].addonVersions[:10].{Version:addonVersion,DefaultVersion:compatibilities[0].defaultVersion}"
------------------------------------------
|          DescribeAddonVersions         |
+-----------------+----------------------+
| DefaultVersion  |       Version        |
+-----------------+----------------------+
|  False          |  v1.9.3-eksbuild.22  |
|  False          |  v1.9.3-eksbuild.21  |
|  False          |  v1.9.3-eksbuild.19  |
|  False          |  v1.9.3-eksbuild.17  |
|  False          |  v1.9.3-eksbuild.15  |
|  False          |  v1.9.3-eksbuild.11  |
|  False          |  v1.9.3-eksbuild.10  |
|  False          |  v1.9.3-eksbuild.9   |
|  True           |  v1.9.3-eksbuild.7   |
|  False          |  v1.9.3-eksbuild.6   |
+-----------------+----------------------+

위 결과는 Default Version 입니다.

EKS 추가 기능에서 addon 관리 호환성을 확인할 수 있으니 상황에 따라 맞춰 가면 됩니다.

 

addon.tf에서 addon 코드값을 변경하겠습니다.

terraform plan
terraform apply -auto-approve

데이터 플레인 업그레이드

데이터 플레인에 따라 업그레이드 방식과 인프레이스 내 전략이 존재합니다.

# 노드 그룹 구성 확인 
kubectl get nodes \
  --label-columns=eks.amazonaws.com/nodegroup,karpenter.sh/nodepool,eks.amazonaws.com/capacityType,node.kubernetes.io/lifecycle,karpenter.sh/capacity-type,eks.amazonaws.com/compute-type,node.kubernetes.io/instance-type,kubernetes.io/arch,kubernetes.io/os,topology.kubernetes.io/zone
NAME                                                STATUS   ROLES    AGE    VERSION                NODEGROUP                             NODEPOOL   CAPACITYTYPE   LIFECYCLE      CAPACITY-TYPE   COMPUTE-TYPE   INSTANCE-TYPE   ARCH    OS      ZONE
fargate-ip-10-0-46-241.us-west-2.compute.internal   Ready    <none>   2d8h   v1.25.16-eks-2d5f260                                                                                                  fargate                        amd64   linux   us-west-2c
ip-10-0-13-94.us-west-2.compute.internal            Ready    <none>   2d8h   v1.25.16-eks-59bf375                                         default                                  spot                           m6i.large       amd64   linux   us-west-2a
ip-10-0-16-54.us-west-2.compute.internal            Ready    <none>   2d8h   v1.25.16-eks-59bf375                                                                   self-managed                                  m5.large        amd64   linux   us-west-2b
ip-10-0-17-142.us-west-2.compute.internal           Ready    <none>   2d8h   v1.25.16-eks-59bf375   initial-20250330054747829400000029               ON_DEMAND                                                    m5.large        amd64   linux   us-west-2b
ip-10-0-42-186.us-west-2.compute.internal           Ready    <none>   2d8h   v1.25.16-eks-59bf375   initial-20250330054747829400000029               ON_DEMAND                                                    m5.large        amd64   linux   us-west-2c
ip-10-0-45-187.us-west-2.compute.internal           Ready    <none>   2d8h   v1.25.16-eks-59bf375                                                                   self-managed                                  m5.large        amd64   linux   us-west-2c
ip-10-0-6-32.us-west-2.compute.internal             Ready    <none>   2d8h   v1.25.16-eks-59bf375   blue-mng-2025033005474785250000002b              ON_DEMAND                                                    m5.large        amd64   linux   us-west-2a

데이터플레인 1. 관리 노드 그룹 업그레이드 (인플레이스)

EKS 관리 노드 업그레이드는 5단계로 진행됩니다.

단계
설명
주요 설정 / 조건
1. 설정 단계
새로운 AMI 또는 시작 템플릿 버전 생성 후, ASG에 반영
- 새 Launch Template 버전 생성 - updateConfig로 최대 병렬 업그레이드 노드 수 설정 (기본 1, 최대 100)
2. 확장 단계
ASG의 크기를 일시적으로 증가시켜 새 노드 추가
- ASG 최대/원하는 크기 증가(자동) - 최대 확장은 AZ 수의 2배까지 가능
3. 준비 단계
새 노드 준비 및 기존 노드 예약 불가 처리
- 새 노드 준비 상태 확인 - 기존 노드에 NoSchedule taint 부여- node.kubernetes.io/exclude-from-external-load-balancers=true 레이블 설정
4. 업그레이드 단계
기존 노드를 순차적으로 교체
- 최대 병렬 업그레이드 수만큼 노드 선택 - Pod 퇴거 (15분 제한, 실패 시 force 플래그 사용) - 퇴거 후 60초 대기 및 ASG 종료 요청 - 이전 노드 모두 교체될 때까지 반복
5. 축소 단계
ASG 크기를 원래대로 복원
- ASG의 maxSize, desiredSize를 원래대로 감소
  • 업그레이드 단계(15분 제한)이 끝나면 데이터 플레인은 원복됩니다. 파드 또한 다시 마이그레이션되니 파드 스케쥴링 검증이 필요합니다.

업그레이드 방법은 두 가지입니다.

  • Using default AMI for given K8s version - 버전만 기입하여 업그레이드, AMI는 AWS 제공하는 최적화된 이미지로 설정됩니다.
  • Using specific AMI ID (like custom AMIs) - 사용자 정의 버전 AMI를 지정하여 업그레이드

Using default AMI for given K8s version 은 테라폼 코드에서 버전만 수정하면 동작합니다.

 
 

Using specific AMI ID (like custom AMIs)은 AMI를 검색하여 기입해야 합니다.

확인한 AMI를 가지고 신규 그룹을 생성하여 적용하겠습니다.

# AMI 확인 
 aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
  --region $AWS_REGION --query "Parameter.Value" --output text
  
 ami-086414611b43bb691 
 
  terraform plan 
  terraform apply -auto-approve

데이터플레인 2. 관리 노드 그룹 업그레이드 (블루/그린)

쿠버네티스 skew 정책에 따라 버전 호환 범위 내에서 노드 그룹을 신규 생성하여 블루/그린 방식으로 이주가 가능합니다. 상태 저장 워크로드(order) 를 가지고 업그레이드를 진행하겠습니다.

기존 블루 그룹을 확인하겠습니다.

kubectl get nodes -l type=OrdersMNG
NAME                                      STATUS   ROLES    AGE     VERSION
ip-10-0-6-32.us-west-2.compute.internal   Ready    <none>   2d23h   v1.25.16-eks-59bf375

blue-mng={
  instance_types = ["m5.large", "m6a.large", "m6i.large"]
  cluster_version = "1.25"
  min_size     = 1
  max_size     = 2
  desired_size = 1
  update_config = {
    max_unavailable_percentage = 35
  }
  labels = {
    type = "OrdersMNG"
  }
  subnet_ids = [module.vpc.private_subnets[0]]
  taints = [
    {
      key    = "dedicated"
      value  = "OrdersApp"
      effect = "NO_SCHEDULE"
    }
  ]
}

주목할 점은 노드 Taint 입니다.

 

신규 노드 그룹 생성시 위와 같이 그대로 구성하여 추가해야 합니다.

 
terraform plan
terraform apply -auto-approve

노드 그룹 생성 확인 후 파드를 마이그레이션 해야합니다.

PDB가 설정된 replica 수를 늘려 해당 노드에 파드를 배치하겠습니다.


kubectl get nodes -l type=OrdersMNG -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints[?(@.effect=='NoSchedule')]}{\"\n\"}{end}"


# 애플리케이션 파드 설정
cd ~/environment/eks-gitops-repo/
sed -i 's/replicas: 1/replicas: 2/' apps/orders/deployment.yaml
git add apps/orders/deployment.yaml
git commit -m "Increase orders replicas 2"
git push

# arogoCD 싱크
argocd app sync orders

파드 확인 후 이전 노드 그룹을 삭제하겠습니다.

terraform plan
terraform apply -auto-approve

데이터플레인 2. 카펜터 (Karpenter)

Drift나 타이머(expireAfter) 와 같은 기능은 Karpenter를 통해 프로비저닝된 Kubernetes 데이터 플레인에 적용합니다.

기능
설명
예시
Drift (드리프트)
노드가 "원래 의도한 상태"와 다르게 된 경우 자동으로 교체
태그, 보안 설정, AMI 등이 변경되는 경우
expireAfter (만료 타이머)
노드를 일정 시간 후 자동 종료
TTL 설정 값이 지나면 변경

드리프트 방식은 다음과 같이 나뉘어 분류됩니다.

  • amiSelectorTerms를 사용하여 AMI ID, AMI 이름 또는 특정 태그로 명시적으로 지정
  • amiSelectorTerms가 지정되지 않은 경우 Karpenter는 Amazon EKS 최적화 AMI에 대해 게시된 SSM 매개변수를 모니터링하여 최신 버전이 있는 경우 교체

expireAfter는 노드 풀에서 spec.template.spec.expireAfter 를 통해 설정 가능합니다.

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  # Template section that describes how to template out NodeClaim resources that Karpenter will provision
  # Karpenter will consider this template to be the minimum requirements needed to provision a Node using this NodePool
  # It will overlay this NodePool with Pods that need to schedule to further constrain the NodeClaims
  # Karpenter will provision to launch new Nodes for the cluster
  template:
      # The amount of time a Node can live on the cluster before being removed
      # Avoiding long-running Nodes helps to reduce security vulnerabilities as well as to reduce the chance of issues that can plague Nodes with long uptimes such as file fragmentation or memory leaks from system processes
      # You can choose to disable expiration entirely by setting the string value 'Never' here

      # Note: changing this value in the nodepool will drift the nodeclaims.
      expireAfter: 720h | Never

expireAfter 의 경우 NodePool의 spec.disruption.budgets를 통해 Karpenter의 중단을 제한할 수 있습니다.

예를 들어 "30일 뒤에 만료” 만료 후 동작 스케쥴링을 아래와 같이 변경할 수 있습니다.


    apiVersion: karpenter.sh/v1beta1
    kind: NodePool
    metadata:
      name: default
    spec: # This is not a complete NodePool Spec.
      disruption:
        consolidationPolicy: WhenEmpty
        expireAfter: 720h # 30 * 24h = 720h
        budgets:
        - schedule: "0 9 * * mon-fri"
          duration: 8h
          nodes: 0
        - nodes: 10

Karpenter는 평일 업무 시간 동안 노드를 프로비저닝 해제할 수 없고, 다른 시간에는 10개의 노드만 동시에 프로비저닝 해제할 수 있도록 허용합니다.

구성된 노드 풀은 다음과 같이 확인할 수 있습니다.


kubectl describe nodepool
Name:         default
Namespace:    
Labels:       argocd.argoproj.io/instance=karpenter
Annotations:  karpenter.sh/nodepool-hash: 12028663807258658692
              karpenter.sh/nodepool-hash-version: v2
API Version:  karpenter.sh/v1beta1
Kind:         NodePool
Metadata:
  Creation Timestamp:  2025-03-30T05:58:00Z
  Generation:          1
  Resource Version:    6627
  UID:                 e0394e0b-dfc2-40a6-b8e9-d4d6865b588b
Spec:
  Disruption:
    Budgets:
      Nodes:               10%
    Consolidation Policy:  WhenUnderutilized
    Expire After:          Never # 없음 
  Limits:
    Cpu:  100
  Template:
    Metadata:
      Labels:
        Env:   dev
        Team:  checkout
    Spec:
      Node Class Ref:
        Name:  default
      Requirements:
        Key:       karpenter.k8s.aws/instance-family
        Operator:  In
        Values:
          c5
          m5
          m6i
          m6a
          r4
          c4
        Key:       kubernetes.io/arch
        Operator:  In
        Values:
          amd64
        Key:       karpenter.sh/capacity-type
        Operator:  In
        Values:
          on-demand
          spot
        Key:       kubernetes.io/os
        Operator:  In
        Values:
          linux
      Taints:
        Effect:  NoSchedule
        Key:     dedicated
        Value:   CheckoutApp
Status:
  Resources:
    Attachable - Volumes - Aws - Ebs:  39
    Cpu:                               2
    Ephemeral - Storage:               20959212Ki
    Memory:                            7981952Ki
    Pods:                              29
Events:                                <none>

# 구성 정보 확인 
kubectl describe ec2nodeclass
Name:         default
Namespace:    
Labels:       argocd.argoproj.io/instance=karpenter
Annotations:  karpenter.k8s.aws/ec2nodeclass-hash: 5256777658067331158
              karpenter.k8s.aws/ec2nodeclass-hash-version: v2
API Version:  karpenter.k8s.aws/v1beta1
Kind:         EC2NodeClass
Metadata:
  Creation Timestamp:  2025-03-30T05:58:00Z
  Finalizers:
    karpenter.k8s.aws/termination
  Generation:        1
  Resource Version:  1857182
  UID:               e448bee4-cae6-43d9-a3bf-cce39966f9c1
Spec:
  Ami Family:  AL2
  Ami Selector Terms:
    Id:  ami-0ee947a6f4880da75
  Metadata Options:
    Http Endpoint:                enabled
    httpProtocolIPv6:             disabled
    Http Put Response Hop Limit:  2
    Http Tokens:                  required
  Role:                           karpenter-eksworkshop-eksctl
  Security Group Selector Terms:
    Tags:
      karpenter.sh/discovery:  eksworkshop-eksctl
  Subnet Selector Terms:
    Tags:
      karpenter.sh/discovery:  eksworkshop-eksctl
  Tags:
    Intent:        apps
    Managed - By:  karpenter
    Team:          checkout
Status:
  Amis:
    Id:    ami-0ee947a6f4880da75
    Name:  amazon-eks-node-1.25-v20250123
    Requirements:
      Key:       kubernetes.io/arch
      Operator:  In
      Values:
        amd64
  Conditions:
    Last Transition Time:  2025-03-30T05:58:01Z
    Message:               
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Instance Profile:        eksworkshop-eksctl_4067990795380418201
  Security Groups:
    Id:    sg-02c20fac7d05ee8bd
    Name:  eksworkshop-eksctl-node-20250330053748685300000009
    Id:    sg-0e679282c4b01346d
    Name:  eksworkshop-eksctl-cluster-2025033005374871870000000b
    Id:    sg-0ff5221c9c93d17ce
    Name:  eks-cluster-sg-eksworkshop-eksctl-1769733817
  Subnets:
    Id:       subnet-00b8866f562019542
    Zone:     us-west-2b
    Zone ID:  usw2-az1
    Id:       subnet-03d62f35a7786e72f
    Zone:     us-west-2c
    Zone ID:  usw2-az3
    Id:       subnet-083e8dff66d967d83
    Zone:     us-west-2a
    Zone ID:  usw2-az2
Events:       <none>


# 노드 구성 확인 
kubectl get nodes -l team=checkout
NAME                                       STATUS   ROLES    AGE     VERSION
ip-10-0-13-94.us-west-2.compute.internal   Ready    <none>   2d23h   v1.25.16-eks-59bf375

파드 수를 늘려서 노드를 늘려 가용성을 보장하는 작업을 진행합니다.


apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout
  labels:
    app.kubernetes.io/created-by: eks-workshop
    app.kubernetes.io/type: app
spec:
  replicas: 10 # 1 - > 10 
  
..

# 코드 반영 
git add apps/checkout/deployment.yaml
git commit -m "scale checkout app"
git push --set-upstream origin main

# 싱크 
argocd app sync checkout

# 노드 수 증가 
kubectl get nodes -l team=checkout
NAME                                        STATUS     ROLES    AGE   VERSION
ip-10-0-13-94.us-west-2.compute.internal    Ready      <none>   3d    v1.25.16-eks-59bf375
ip-10-0-25-183.us-west-2.compute.internal   NotReady   <none>   6s    v1.25.16-eks-59bf375

AMI 를 검색 후 드리프트로 설정하겠습니다.


aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
    --region ${AWS_REGION} --query "Parameter.Value" --output text
    
ami-0685d6ff76087efe1

노드 클래스에서 AMI를 설정하고, 노드 풀에서 노드 변경 수를 지정합니다.

아래 명령어 방식으로 수정하였지만, 원복되었고 ArgoCD를 통해 정상 적용하였습니다.

싱크 작업으로 AMI가 변경된 것으로 확인됩니다.


kubectl edit nodepool
kubectl edit ec2nodeclass
 
 

# 위 반영이 안되어 코드 푸쉬 후 싱크 
git add apps/karpenter/default-ec2nc.yaml apps/karpenter/default-np.yaml
git commit -m "disruption changes"
git push --set-upstream origin main

argocd app sync karpenter

# 로그 확인 
kubectl -n karpenter logs deployment/karpenter -c controller --tail=33 

# 노드 버전 확인 
kubectl get nodes -l team=checkout
NAME                                        STATUS   ROLES    AGE     VERSION
ip-10-0-15-185.us-west-2.compute.internal   Ready    <none>   3m11s   v1.26.15-eks-1552ad0
ip-10-0-31-160.us-west-2.compute.internal   Ready    <none>   4m23s   v1.26.15-eks-1552ad0
 

expireAfter: Never 였지만 AMI를 명시적으로 변경하여 노드 그룹이 교체됨을 확인하였습니다.

 

 

데이터플레인 3. Fargate

Fargate 는 서버리스 컨테이너 관리 서비스로 사용자가 노드 그룹을 확인할 수 없습니다.

클러스터에서 실행 중인 Fargate 컨트롤러가 Pod를 인식하고 업데이트하며 Fargate에 예약합니다.

AWS Fargate 노드를 업그레이드하려면 K8s 배포를 다시 시작하여 새로운 Pod가 자동으로 최신 Kubernetes 버전에 예약되도록 할 수 있습니다.

# farate 파드 확인
kubectl get pod -A -o wide | grep fargate
assets        assets-7ccc84cb4d-ngfjf                                     1/1     Running   0            3d      10.0.46.241   fargate-ip-10-0-46-241.us-west-2.compute.internal   <none>           <none>

# 노드 버전 확인 
kubectl get node $(kubectl get pods -n assets -o jsonpath='{.items[0].spec.nodeName}') -o wide
NAME                                                STATUS   ROLES    AGE   VERSION                INTERNAL-IP   EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
fargate-ip-10-0-46-241.us-west-2.compute.internal   Ready    <none>   3d    v1.25.16-eks-2d5f260   10.0.46.241   <none>        Amazon Linux 2   5.10.234-225.910.amzn2.x86_64   containerd://1.7.25


# Faragte 파드 재배포 
kubectl rollout restart deployment assets -n assets 

# 노드 확인 
kubectl get node $(kubectl get pods -n assets -o jsonpath='{.items[0].spec.nodeName}') -o wide

 

 

Blue/Green 업그레이드(1.26 > 1.30)

Blue-green 클러스터 업그레이드 전략을 사용하면 한 번에 여러 K8s 버전을 건너뛰거나 여러 업그레이드를 하나씩 수행할 수 있습니다.

EKS 클러스터는 이전 클러스터에서 만든 것과 동일한 VPC 내에 만들어집니다.

항목
설명
네트워크 연결
두 클러스터가 같은 VPC에 있어 리소스 간 통신이 원활하고, 워크로드 및 데이터 마이그레이션이 쉬움
공유 리소스
NAT Gateway, VPN, Direct Connect 등 기존 VPC 리소스를 재사용하여 복잡성과 비용 절감
보안 그룹
동일한 보안 그룹 규칙을 재사용함으로써 보안 정책을 일관되게 유지 가능
서비스 검색
AWS Cloud Map 등으로 클러스터 간 서비스 검색이 쉬워짐
서브넷 활용도
기존 서브넷을 재사용함으로써 네트워크 재설정 없이 자원 할당 가능
VPC 피어링
피어링된 외부 VPC에 대한 접근 권한을 두 클러스터 모두 유지 가능
일관된 DNS
Route 53 등 내부 DNS 설정을 일관되게 유지 가능
IAM 및 리소스 정책
IAM 정책 및 리소스 권한이 동일 VPC 기준으로 설정돼 있어 권한 관리가 단순함

ls -lrt eksgreen-terraform/
total 24
-rw-r--r--. 1 ec2-user ec2-user  542 Sep  1  2024 versions.tf
-rw-r--r--. 1 ec2-user ec2-user   15 Sep  1  2024 README.md
-rw-r--r--. 1 ec2-user ec2-user  464 Sep  8  2024 variables.tf
-rw-r--r--. 1 ec2-user ec2-user 4849 Sep  8  2024 addons.tf
-rw-r--r--. 1 ec2-user ec2-user 4010 Sep  8  2024 base.tf


cd eksgreen-terraform
terraform init
terraform plan -var efs_id=$EFS_ID
terraform apply -var efs_id=$EFS_ID
    
  • 테라폼 코드 실행시 EFS 스토리지를 전달하는 이유는 StorageClass 와 StatefulSet 리소스를 생성할 때 해당 값을 사용할 것이므로 해당 EFS 파일 시스템의 ID를 기록해 두어야 합니다.
  • 그린 클러스터 구성까지 약 30분정도 소요됩니다.

# 클러스터 확인
eksctl get cluster 
NAME                    REGION          EKSCTL CREATED
eksworkshop-eksctl      us-west-2       False
eksworkshop-eksctl-gr   us-west-2       False

# 클러스터 컨텍스트 등록
aws eks --region ${AWS_REGION} update-kubeconfig --name ${EKS_CLUSTER_NAME} --alias blue && \
  kubectl config use-context blue

aws eks --region ${AWS_REGION} update-kubeconfig --name ${EKS_CLUSTER_NAME}-gr --alias green && \
  kubectl config use-context green 
 

# 그린 클러스터 노드 확인, 1.30 
kubectl get nodes --context green
NAME                                        STATUS   ROLES    AGE   VERSION
ip-10-0-15-131.us-west-2.compute.internal   Ready    <none>   36m   v1.30.9-eks-5d632ec
ip-10-0-17-2.us-west-2.compute.internal     Ready    <none>   36m   v1.30.9-eks-5d632ec
ip-10-0-24-139.us-west-2.compute.internal   Ready    <none>   36m   v1.30.9-eks-5d632ec
ip-10-0-42-2.us-west-2.compute.internal     Ready    <none>   36m   v1.30.9-eks-5d632ec


# 그린 클러스터 배포 패키지 확인 
helm list -A --kube-context green
NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                   APP VERSION
argo-cd                         argocd          1               2025-04-02 06:49:16.963339726 +0000 UTC deployed        argo-cd-5.55.0                          v2.10.0    
aws-efs-csi-driver              kube-system     1               2025-04-02 06:49:45.623111902 +0000 UTC deployed        aws-efs-csi-driver-2.5.6                1.7.6      
aws-load-balancer-controller    kube-system     1               2025-04-02 06:49:48.447800098 +0000 UTC deployed        aws-load-balancer-controller-1.7.1      v2.7.1     
karpenter                       karpenter       1               2025-04-02 06:49:45.425515154 +0000 UTC deployed        karpenter-0.37.0                        0.37.0     
metrics-server                  kube-system     1               2025-04-02 06:49:16.948759521 +0000 UTC deployed        metrics-server-3.12.0                   0.7.0      

상태 비저장 워크로드 마이그레이션

상태 비저장 애플리케이션은 클러스터에 영구 데이터를 보관할 필요가 없으므로 업그레이드하는 동안 새 그린 클러스터에 이를 배포하고 트래픽을 라우팅할 수 있습니다.

ArgoCD에 신규 클러스터 대상을 등록하고 배포를 진행하겠습니다.

사용하고 있는 repo는 1.26 AMI가 등록되어 있으므로 1.30 AMI를 확인하여 수정하고 푸쉬하는 과정입니다.

cd ~/environment/eks-gitops-repo
git status

git switch -c green
git branch -a

Switched to a new branch 'green'
* green
  main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
 
 # AMI 확인 
 export AL2023_130_AMI=$(aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.30/amazon-linux-2023/x86_64/standard/recommended/image_id --region ${AWS_REGION} --query "Parameter.Value" --output text)
 echo $AL2023_130_AMI
 
 ami-08eb2eb81143e2902
 
cat << EOF > ~/environment/eks-gitops-repo/apps/karpenter/default-ec2nc.yaml
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiFamily: AL2023
  amiSelectorTerms:
  - id: "${AL2023_130_AMI}" # Latest EKS 1.30 AMI
  role: karpenter-eksworkshop-eksctl-gr
  securityGroupSelectorTerms:
  - tags:
      karpenter.sh/discovery: eksworkshop-eksctl-gr
  subnetSelectorTerms:
  - tags:
      karpenter.sh/discovery: eksworkshop-eksctl
  tags:
    intent: apps
    managed-by: karpenter
    team: checkout
EOF

또한, pluto 를 통해 버전 API 확인 진행하고 수정합니다.

pluto 는 GitOps 저장소에서 더 이상 사용되지 않는 API 사용을 찾는 데 도움이 되는 유틸리티입니다.

아래 결과 확인시 autoscaling/v2beta2 은 지원되지 않으므로, convert를 통해 API를 전환하도록 하겠습니다.

pluto detect-files -d ~/environment/eks-gitops-repo/ 
NAME   KIND                      VERSION               REPLACEMENT      REMOVED   DEPRECATED   REPL AVAIL  
ui     HorizontalPodAutoscaler   autoscaling/v2beta2   autoscaling/v2   false     true         false       


Want more? Automate Pluto for free with Fairwinds Insights!
 🚀 https://fairwinds.com/insights-signup/pluto 🚀 
 
# API 전환
cd ~/environment/eks-gitops-repo/
kubectl convert -f apps/ui/hpa.yaml --output-version autoscaling/v2 -o yaml > apps/ui/tmp.yaml && mv apps/ui/tmp.yaml apps/ui/hpa.yaml
cat apps/ui/hpa.yaml
 

마지막으로 ArgoCD 애플리케이션이 green 브랜치를 사용하도록 설정하고 그린 브랜치의 yaml들을 그린 클러스터에 배포할 수 있도록 설정하겠습니다.


# 브랜치 설정
sed -i 's/targetRevision: main/targetRevision: green/' app-of-apps/values.yaml

# 브랜치 푸쉬
git add .  && git commit -m "1.30 changes"
git push -u origin green

# ARgoCD 정보 지정
export ARGOCD_SERVER_GR=$(kubectl get svc argo-cd-argocd-server -n argocd -o json --context green | jq --raw-output '.status.loadBalancer.ingress[0].hostname')
echo "ArgoCD URL: http://${ARGOCD_SERVER_GR}"
export ARGOCD_USER_GR="admin"
export ARGOCD_PWD_GR=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" --context green | base64 -d)
echo "Username: ${ARGOCD_USER_GR}"
echo "Password: ${ARGOCD_PWD_GR}"

# 로그인
argocd login --name green ${ARGOCD_SERVER_GR} --username ${ARGOCD_USER_GR} --password ${ARGOCD_PWD_GR} --insecure --skip-test-tls --grpc-web
'admin:login' logged in successfully
Context 'green' updated

# 키 반환
argo_creds=$(aws secretsmanager get-secret-value --secret-id argocd-user-creds --query SecretString --output text)

# repo 등록 
argocd repo add $(echo $argo_creds | jq -r .url) --username $(echo $argo_creds | jq -r .username) --password $(echo $argo_creds | jq -r .password) --server ${ARGOCD_SERVER_GR}

Repository 'https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo' added

# repo에 등록된 yaml 배포 
argocd app create apps --repo $(echo $argo_creds | jq -r .url) --path app-of-apps \
  --dest-server https://kubernetes.default.svc --sync-policy automated --revision green --server ${ARGOCD_SERVER_GR}
  

# 구성 확인 
argocd app list --server ${ARGOCD_SERVER_GR} 
NAME              CLUSTER                         NAMESPACE  PROJECT  STATUS  HEALTH   SYNCPOLICY  CONDITIONS  REPO                                                                     PATH            TARGET
argocd/apps       https://kubernetes.default.svc             default  Synced  Healthy  Auto              https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  app-of-apps     green
argocd/assets     https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/assets     green
argocd/carts      https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/carts      green
argocd/catalog    https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/catalog    green
argocd/checkout   https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/checkout   green
argocd/karpenter  https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/karpenter  green
argocd/orders     https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/orders     green
argocd/other      https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/other      green
argocd/rabbitmq   https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/rabbitmq   green
argocd/ui         https://kubernetes.default.svc             default  Synced  Healthy  Auto-Prune        https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-gitops-repo  apps/ui         green

구성이 완료되었다면 트래픽 전환이 필요합니다.

트래픽 전환에는 Amazon Route 53의 가중 레코드, AWS Application Load Balancer의 가중 대상 그룹 등 여러 가지 기술을 사용할 수 있습니다.

본 워크샵에서는 Route53 가중 레코드를 통한 이론적 지침만 제공합니다.

 

Route53 라우팅은 external-DNS annotation을 통해 설정할 수 있습니다.

필자가 테스트한 결과를 공유드리니, 정책 설정 및 카나리 배포에 참고 부탁드립니다.


external-dns.alpha.kubernetes.io/set-identifier: {{ .Values.spec.clusterName }}
external-dns.alpha.kubernetes.io/aws-weight: '{{ .Values.spec.ingress.route53_weight }}'
 
 

 

상태 저장 워크로드 마이그레이션

Kubernetes에서 실행되는 상태 저장 애플리케이션을 위한 영구 저장소를 호스팅하는 데는 Amazon EBS 볼륨이나 클러스터 간에 동기화해야 하는 데이터베이스 컨테이너를 포함하여 다양한 저장소 옵션이 있습니다.

이 워크숍의 단순성을 위해 Amazon EFS 파일 시스템을 사용하지만, 블루 그린 프로세스를 사용하여 상태 저장 애플리케이션을 업그레이드하는 일반적인 개념은 동일합니다.

 

영구 저장소가 있는 상태 저장 애플리케이션의 업그레이드를 시뮬레이션하려면 다음 단계를 순서대로 따릅니다.

  1. Blue 클러스터에서 Kubernetes StatefulSet 리소스를 만들고 정적 html 파일을 수동으로 만듭니다. 이 파일은 공유 EFS 파일 시스템에 저장됩니다.
  2. nginx를 실행하는 포드를 호스팅하고 Blue 포드와 동일한 파일 저장소에 액세스하는 Green 클러스터에서 StatefulSet 리소스를 만듭니다.
  3. (선택 사항) Blue 클러스터에서 원래 StatefulSet을 삭제합니다.

 

[1단계] Blue 클러스터 내 EFS 볼륨 기반 nginx 애플리케이션 배포

# 블루 클러스터 컨텍스트 설정
kubectl config use-context blue

# EFS 구성 확인 
kubectl get storageclass efs -o yaml 
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  creationTimestamp: "2025-03-30T05:54:16Z"
  name: efs
  resourceVersion: "3908"
  uid: 9deecea9-ce8c-4007-a25c-3dbb1ef97ace
mountOptions:
- iam
parameters:
  basePath: /dynamic_provisioning
  directoryPerms: "755"
  ensureUniqueDirectory: "false"
  fileSystemId: fs-09c7449db5761f1c3
  gidRangeEnd: "200"
  gidRangeStart: "100"
  provisioningMode: efs-ap
  reuseAccessPoint: "false"
  subPathPattern: ${.PVC.namespace}/${.PVC.name}
provisioner: efs.csi.aws.com
reclaimPolicy: Delete
volumeBindingMode: Immediate


# StatefulSet 매니패스트 배포
cat <<EOF | kubectl apply -f -
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: efs-example
  namespace: default
spec:
  serviceName: "efs-example"
  replicas: 1
  selector:
    matchLabels:
      app: efs-example
  template:
    metadata:
      labels:
        app: efs-example
    spec:
      containers:
      - name: app
        image: nginx:latest
        volumeMounts:
        - name: efs-storage
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: efs-storage
    spec:
      accessModes: ["ReadWriteMany"]
      storageClassName: efs # efs 볼륨 사용 설정 
      resources:
        requests:
          storage: 1Gi
EOF


# 상태 저장 볼륨 확인 
kubectl exec $(kubectl get pods -o jsonpath='{.items[0].metadata.name}' -l app=efs-example) -- bash -c 'touch /usr/share/nginx/html/index.html'

# 파일 삭제 후 이전 파드 동작 확인 
kubectl delete pods -l app=efs-example && \
kubectl exec $(kubectl get pods -o jsonpath='{.items[0].metadata.name}' -l app=efs-example) -- bash -c 'ls -lh /usr/share/nginx/html/index.html'

pod "efs-example-0" deleted
-rw-r--r-- 1 100 users 0 Apr  2 07:55 /usr/share/nginx/html/index.html

[2단계] Green 클러스터 내 StatefulSet 애플리케이션 배포

# 그린 클러스터 컨텍스트 설정 
kubectl config use-context green

# 스토리지 클래스 확인, fileSystemID가 동일함
kubectl get storageclass efs -o yaml 
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  creationTimestamp: "2025-04-02T06:50:59Z"
  name: efs
  resourceVersion: "3334"
  uid: dfea7abf-3872-4a21-8abd-f45f4ed3ba40
mountOptions:
- iam
parameters:
  basePath: /dynamic_provisioning
  directoryPerms: "755"
  ensureUniqueDirectory: "false"
  fileSystemId: fs-09c7449db5761f1c3
  gidRangeEnd: "2000"
  gidRangeStart: "1000"
  provisioningMode: efs-ap
  reuseAccessPoint: "false"
  subPathPattern: ${.PVC.namespace}/${.PVC.name}
provisioner: efs.csi.aws.com
reclaimPolicy: Delete

# 배포 
cat <<EOF | kubectl apply -f -
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: efs-example
  namespace: default
spec:
  serviceName: "efs-example"
  replicas: 1
  selector:
    matchLabels:
      app: efs-example
  template:
    metadata:
      labels:
        app: efs-example
    spec:
      containers:
      - name: app
        image: nginx:latest
        volumeMounts:
        - name: efs-storage
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: efs-storage
    spec:
      accessModes: ["ReadWriteMany"]
      storageClassName: efs
      resources:
        requests:
          storage: 1Gi
EOF


# 동작 확인 
kubectl exec $(kubectl get pods -o jsonpath='{.items[0].metadata.name}' -l app=efs-example) -- bash -c 'ls -lh /usr/share/nginx/html/index.html' 
-rw-r--r-- 1 100 users 0 Apr  2 07:55 /usr/share/nginx/html/index.html

[3단계] 블루 클러스터 내 StatefulSet 애플리케이션 삭제

kubectl delete statefulset efs-example --context blue

위 상태 저장 워크로드는 EFS 스토리지 기반의 시나리오입니다.

EBS 로 동작하는 워크로드인 경우 시나리오에 따라 처리할 수 있도록 합시다.

시나리오
처리 방법
StatefulSet에서 EBS 사용하는 경우
기존 볼륨을 새 클러스터로 이동해야 함
임시 볼륨이거나 삭제해도 되는 경우
새 클러스터에서 새로 생성하면 됨 (볼륨 복사 불필요)
EBS-backed PVC를 그대로 유지하고 싶은 경우
EBS Volume을 Detach → Attach 또는 스냅샷 복원 필요

 



'Cloud' 카테고리의 다른 글

EKS 파드로 노드 관리하기  (0) 2025.04.12
EKS Fargate & AutoMode  (0) 2025.03.23
EKS Security  (2) 2025.03.16
EKS Karpenter  (0) 2025.03.09
EKS Autoscaling  (0) 2025.03.09