상세 컨텐츠

본문 제목

9주차 - EKS Blue/Green Upgrade 실습

K8S - EKS

by 존자르징어 2025. 4. 5. 00:10

본문

 

https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/blue-green-upgrade/

 

Blue/Green Upgrade - Amazon EKS Blueprints for Terraform

Blue/Green Migration This directory provides a solution based on EKS Blueprint for Terraform that shows how to leverage blue/green or canary application workload migration between EKS clusters, using Amazon Route 53 weighted routing feature. The workloads

aws-ia.github.io

본 글은 우 실습 가이드를 통해 실습한 내용을 정리하였습니다.
모든 이미지의 출처는 위 실습 가이드 입니다.

 

0. 사전 준비

- Terrform

- Git

- GitOps repository SSH 인증 설정 (AWS Secret Manager -> "github-blue-print-ssh-key" 또는 workload_repo_secret 테라폼 변수로 변경 가능) : keygen을 통해 key를 만들고 github에 public key 등록 후 연동 테스트 필요

- AWS CLI

- Administrator 역할이 부여된 AWS 계정

- Amazon Route 53 Hosted Zone (hosted_zone_name 변수로 테라폼 코드에서 사용)

- 도메인 등록 (https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html)

1. 프로젝트 구성

 

> 실습 샘플 구성

  - environment

    . VPC

    . Route53 sub domain hosted zone

    . wildcard 인증서 (TLS 엔드포인트 용)

    . SecreteManager 비밀번호 (ArgoCD UI 용)

  - module/eks_cluster

    . ArgoCD add-on

    . 데모 워크로드

  - eks-blue

    . blue eks cluster

  - eks-green

    . green eks cluster

 

=> 2개의 EKS 클러스터가 동일한 VPC를 공유하고, 각 클러스터는 중앙 GitOps 리포지토리에서 ArgoCD 애드온을 사용하여 워크로드 설치

=> GitOps 리포지토리에서 애플리케이션을 배포하고 AWS Load Balancers Controller annotation을 사용하여, AWS 로드 밸런서를 통해 노출하며, 클러스터마다 1개의 로드 밸런서 생성

=> 두 클러스터에 ExternalDNS 애드온을 설정하여 동일한 Route 53 호스팅 영역을 공유하고, AWS Route 53의 가중치 레코드를 사용하여 두 EKS 클러스터 간에 카나리아 마이그레이션을 구성

 

> GitOps 구성

 

  - Terraform 모듈

    . eks : EKS 클러스터 생성

    . eks_blueprints_teams : 팀별 네임스페이스, 권한(Quota, Role 등) 설정

    . eks_blueprints_addons : AWS 리소스(예: IRSA 역할) 생성. 단, Helm 차트는 직접 배포하지 않음

    . gitops-bridge-metadata : Terraform에서 생성한 리소스 정보를 메타데이터로 추출

    . gitops_bridge_bootstrap : ArgoCD와의 연계를 위한 초기 부트스트랩

 

  - GitHub 저장소

    . eks-blueprints-add-ons : ArgoCD에서 사용할 ApplicationSet 정의

    . eks-blueprints-workloads : 샘플 워크로드 저장소

 

2. 실습

> Terrafor aws eks blueprint

git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git
cd patterns/blue-green-upgrade/

 

1. 기본 설정

cp terraform.tfvars.example terraform.tfvars
ln -s ../terraform.tfvars environment/terraform.tfvars
ln -s ../terraform.tfvars eks-blue/terraform.tfvars
ln -s ../terraform.tfvars eks-green/terraform.tfvars

 

- hosted_zone_name : 본인의 hosted zone name으로 변경

- eks_admin_role_name : EKS 클러스터 관리자 권한을 가진 IAM Role 지정 (이번 실습에서 Admin 권한을 가지는 롤을 새로 만들고 적용)

- aws_region  : 본인이 사용하는 region으로 변경

# You should update the below variables

aws_region          = "ap-northeast-2"
environment_name     = "eks-blueprint"
hosted_zone_name    = "psalmist-study-aws.com" # your Existing Hosted Zone
eks_admin_role_name = "Admin" # Additional role admin in the cluster (usually the role I use in the AWS console)

#gitops_addons_org      = "git@github.com:aws-samples"
#gitops_addons_repo     = "eks-blueprints-add-ons"
#gitops_addons_path     = "argocd/bootstrap/control-plane/addons"
#gitops_addons_basepath = "argocd/"

# EKS Blueprint Workloads ArgoCD App of App repository
gitops_workloads_org      = "git@github.com:aws-samples"
gitops_workloads_repo     = "eks-blueprints-workloads"
gitops_workloads_revision = "main"
gitops_workloads_path     = "envs/dev"


#Secret manager secret for github ssk jey
aws_secret_manager_git_private_ssh_key_name = "github-blueprint-ssh-key"

 

2. environment 배포

❯ tree ../environment
../environment
├── README.md
├── main.tf
├── outputs.tf
├── terraform.tfstate
├── terraform.tfstate.backup
├── terraform.tfvars -> ../terraform.tfvars
├── variables.tf
└── versions.tf
cd environment
terraform init
terraform apply

 

 

- eks-blueprint VPC 1개 생성

- 6개(public 3개, private 3개) subnet 및 2개(public, private) 라우팅 테이블 생성

   . pod는 private subnet에 배포

- NAT G/W (eks-blueprint-ap-northeast-2a)

- Internet G/W (eks-blueprint)

- public hosted zone 생성

- ACM 인증서 생성

 

- argocd admin 비밀번호 (secret manager secret 생성) - argocd 배포 후 admin 비밀번호 (k8s scret 값으로 들어감)

 

3. blue cluster 배포 (약 15분 소요)

cd eks-blue
terraform init
terraform apply

 

- main.tf

rovider "aws" {
  region = var.aws_region
}

provider "kubernetes" {
  host                   = module.eks_cluster.eks_cluster_endpoint
  cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data)

  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    command     = "aws"
    args        = ["eks", "get-token", "--cluster-name", module.eks_cluster.eks_cluster_id]
  }
}

provider "helm" {
  kubernetes {
    host                   = module.eks_cluster.eks_cluster_endpoint
    cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data)

    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      command     = "aws"
      args        = ["eks", "get-token", "--cluster-name", module.eks_cluster.eks_cluster_id]
    }
  }
}

module "eks_cluster" {
  source = "../modules/eks_cluster"

  aws_region      = var.aws_region
  service_name    = "blue"
  cluster_version = "1.30" << blue cluster 버전

  argocd_route53_weight      = "100"
  route53_weight             = "100" << 초기 가중치 100% (blue로만 인입)
  ecsfrontend_route53_weight = "100"

  environment_name    = var.environment_name
  hosted_zone_name    = var.hosted_zone_name
  eks_admin_role_name = var.eks_admin_role_name

#  aws_secret_manager_git_private_ssh_key_name = var.aws_secret_manager_git_private_ssh_key_name
  argocd_secret_manager_name_suffix           = var.argocd_secret_manager_name_suffix
  ingress_type                                = var.ingress_type

  gitops_addons_org      = var.gitops_addons_org
  gitops_addons_repo     = var.gitops_addons_repo
  gitops_addons_basepath = var.gitops_addons_basepath
  gitops_addons_path     = var.gitops_addons_path
  gitops_addons_revision = var.gitops_addons_revision

  gitops_workloads_org      = var.gitops_workloads_org
  gitops_workloads_repo     = var.gitops_workloads_repo
  gitops_workloads_revision = var.gitops_workloads_revision
  gitops_workloads_path     = var.gitops_workloads_path

}

 

- 1.30 EKS 클러스터 생성

- Amazon VPC CNI : v1.19.3-eksbuild.1

- kube-proxy: 1.30.9-eksbuild.3

- CoreDNS: v1.11.4-eksbuild.2

 

 

- team-burnham 네임스페이스에 배포된 워크로드를 통해 마이그레이션 자동화 확인

  . reponse body에 실행 중인 클러스터 이름 표시

 

> blue cluster 확인

# kubeconfig update - 클러스터 생성한 사용자는 --role-arn 필요 없음
aws eks --region ${region} update-kubeconfig --name eks-blueprint-blue --role-arn ${arn of eks_admin_role_name}

#
kubectl cluster-info
kubectl get no
kubectl get deployment -n team-burnham -l app=burnham
kubectl get pods -n team-burnham -l app=burnham
kubectl logs -n team-burnham -l app=burnham

# cluster 이름 확인
URL=$(echo -n "https://" ; kubectl get ing -n team-burnham burnham-ingress -o json | jq ".spec.rules[0].host" -r)
curl -s $URL | grep CLUSTER_NAME | awk -F "<span>|</span>" '{print $4}'
eks-blueprint-blue

 

4. green cluster 배포 (약 15분 소요)

 

- main.tf

rovider "aws" {
  region = var.aws_region
}

provider "kubernetes" {
  host                   = module.eks_cluster.eks_cluster_endpoint
  cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data)

  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    command     = "aws"
    args        = ["eks", "get-token", "--cluster-name", module.eks_cluster.eks_cluster_id]
  }
}

provider "helm" {
  kubernetes {
    host                   = module.eks_cluster.eks_cluster_endpoint
    cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data)

    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      command     = "aws"
      args        = ["eks", "get-token", "--cluster-name", module.eks_cluster.eks_cluster_id]
    }
  }
}

module "eks_cluster" {
  source = "../modules/eks_cluster"

  aws_region      = var.aws_region
  service_name    = "blue"
  cluster_version = "1.30" << green cluster 버전

<< 초기 가중치 100% (blue로만 인입) >>
  argocd_route53_weight      = "100"
  route53_weight             = "100" 
  ecsfrontend_route53_weight = "100"

  environment_name    = var.environment_name
  hosted_zone_name    = var.hosted_zone_name
  eks_admin_role_name = var.eks_admin_role_name

#  aws_secret_manager_git_private_ssh_key_name = var.aws_secret_manager_git_private_ssh_key_name
  argocd_secret_manager_name_suffix           = var.argocd_secret_manager_name_suffix
  ingress_type                                = var.ingress_type

  gitops_addons_org      = var.gitops_addons_org
  gitops_addons_repo     = var.gitops_addons_repo
  gitops_addons_basepath = var.gitops_addons_basepath
  gitops_addons_path     = var.gitops_addons_path
  gitops_addons_revision = var.gitops_addons_revision

  gitops_workloads_org      = var.gitops_workloads_org
  gitops_workloads_repo     = var.gitops_workloads_repo
  gitops_workloads_revision = var.gitops_workloads_revision
  gitops_workloads_path     = var.gitops_workloads_path

}

 

- 1.30 EKS 클러스터 생성

- Amazon VPC CNI : v1.19.3-eksbuild.1

- kube-proxy: 1.30.9-eksbuild.3

- CoreDNS: v1.11.4-eksbuild.2

 

 

- team-burnham 네임스페이스에 배포된 워크로드를 통해 마이그레이션 자동화 확인

  . reponse body에 실행 중인 클러스터 이름 표시

 

> green cluster 확인

cd eks-blue
terraform init
terraform apply

 

- main.tf

rovider "aws" {
  region = var.aws_region
}

provider "kubernetes" {
  host                   = module.eks_cluster.eks_cluster_endpoint
  cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data)

  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    command     = "aws"
    args        = ["eks", "get-token", "--cluster-name", module.eks_cluster.eks_cluster_id]
  }
}

provider "helm" {
  kubernetes {
    host                   = module.eks_cluster.eks_cluster_endpoint
    cluster_ca_certificate = base64decode(module.eks_cluster.cluster_certificate_authority_data)

    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      command     = "aws"
      args        = ["eks", "get-token", "--cluster-name", module.eks_cluster.eks_cluster_id]
    }
  }
}

module "eks_cluster" {
  source = "../modules/eks_cluster"

  aws_region      = var.aws_region
  service_name    = "green"
  cluster_version = "1.31" << green cluster 버전

<< 초기 가중치 0% (blue로만 인입) >>
  argocd_route53_weight      = "0"
  route53_weight             = "0" 
  ecsfrontend_route53_weight = "0"

  environment_name    = var.environment_name
  hosted_zone_name    = var.hosted_zone_name
  eks_admin_role_name = var.eks_admin_role_name

#  aws_secret_manager_git_private_ssh_key_name = var.aws_secret_manager_git_private_ssh_key_name
  argocd_secret_manager_name_suffix           = var.argocd_secret_manager_name_suffix
  ingress_type                                = var.ingress_type

  gitops_addons_org      = var.gitops_addons_org
  gitops_addons_repo     = var.gitops_addons_repo
  gitops_addons_basepath = var.gitops_addons_basepath
  gitops_addons_path     = var.gitops_addons_path
  gitops_addons_revision = var.gitops_addons_revision

  gitops_workloads_org      = var.gitops_workloads_org
  gitops_workloads_repo     = var.gitops_workloads_repo
  gitops_workloads_revision = var.gitops_workloads_revision
  gitops_workloads_path     = var.gitops_workloads_path

}

 

- green cluster 확인

# kubeconfig update - 클러스터 생성한 사용자는 --role-arn 필요 없음
aws eks --region ${region} update-kubeconfig --name eks-blueprint-green --role-arn ${arn of eks_admin_role_name}

#
kubectl cluster-info
kubectl get no
kubectl get deployment -n team-burnham -l app=burnham
kubectl get pods -n team-burnham -l app=burnham
kubectl logs -n team-burnham -l app=burnham

# cluster 이름 확인
URL=$(echo -n "https://" ; kubectl get ing -n team-burnham burnham-ingress -o json | jq ".spec.rules[0].host" -r)
curl -s $URL | grep CLUSTER_NAME | awk -F "<span>|</span>" '{print $4}'
eks-blueprint-green

- 1.31 EKS 클러스터 생성

- Amazon VPC CNI : v1.19.3-eksbuild.1

- kube-proxy: 1.31.3-eksbuild.2

- CoreDNS: v1.11.4-eksbuild.2

 

5. 카나리아 배포 실습

- 시나리오

  . eks-blue: 100, eks-green: 0 => BLUE 100%
  . eks-blue: 100, eks-green: 100 => BLUE 50% / GREEN 50%

  . eks-blue: 0, eks-green: 100 => GREEN 100%

  .  추후 스텝

    - eks-blue 클러스터 삭제

    - eks-blue 클러스터 업그레이드 후 트래픽 다시 돌림

    - 롤백 대비용으로 eks-blue 유지

 

- Amazon Route 53 Hostes Zone 사용

  . main.tf에서 exteranl_dns 설정 동일한 구성

enable_external_dns = true
external_dns_route53_zone_arns = [data.aws_route53_zone.sub.arn]
addons_metadata = merge(
  ...
  external_dns_policy = "sync"
)

 . sync 모드 사용 : ExternalDNS가 Ingress에 따라 DNS 레코드 생성 및 삭제 가능

 

- Ingress에 set-identifier, aws-weight 설정 사용

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

  . self-identifier : 해당 클러스터의 externalDns가 레코드 관리

  . aws-weight : Helm values <- terraform이 주입 : 플랫폼 팀이 traffic 비율 조정 가능

 

> eks-blue: 100, eks-green: 0 => BLUE 100% (초기 배포 상태)

 

 

- 확인

URL=$(echo -n "https://" ; kubectl get ing -n team-burnham burnham-ingress -o json | jq ".spec.rules[0].host" -r)
repeat 10 curl -s $URL | grep CLUSTER_NAME | awk -F "<span>|</span>" '{print $4}'

 

> eks-blue: 100, eks-green: 100 => BLUE 50% / GREEN 50%

# eks-green/main.tf

cd eks-green

...
module "eks_cluster" {
  source = "../modules/eks_cluster"

  aws_region      = var.aws_region
  service_name    = "green"
  cluster_version = "1.31" # Here, we deploy the cluster with the N+1 Kubernetes Version

  argocd_route53_weight      = "100" # We control with theses parameters how we send traffic to the workloads in the new cluster
  ...
  
  # 적용
  terraform apply

- 확인

URL=$(echo -n "https://" ; kubectl get ing -n team-burnham burnham-ingress -o json | jq ".spec.rules[0].host" -r)
repeat 10 curl -s $URL | grep CLUSTER_NAME | awk -F "<span>|</span>" '{print $4}' && sleep 60

 

> eks-blue: 0, eks-green: 100 => BLUE 50% / GREEN 100%

# blue-eks/main.tf
cd blue-eks

module "eks_cluster" {
  source = "../modules/eks_cluster"

  aws_region      = var.aws_region
  service_name    = "blue"
  cluster_version = "1.30"

  argocd_route53_weight      = "0"
  route53_weight             = "0"
  ecsfrontend_route53_weight = "0"

terraform apply
URL=$(echo -n "https://" ; kubectl get ing -n team-burnham burnham-ingress -o json | jq ".spec.rules[0].host" -r)
repeat 10 curl -s $URL | grep CLUSTER_NAME | awk -F "<span>|</span>" '{print $4}' && sleep 60

 

 

- 모든 DNS TTL 갱신되면 트래픽은 전부 eks-green 클러스터로 이동함

 

> 추후 스텝

 - eks-blue 클러스터 삭제

 - eks-blue 클러스터 업그레이드 후 트래픽 다시 돌림

 - 롤백 대비용으로 eks-blue 유지

 

> 자원 삭제

cd ../eks-blue
terraform destroy

cd ../eks-green
terraform destroy

cd ../environment
terraform destroy

 

관련글 더보기