본문 바로가기
DevOps/EKS

[AEWS4기] EKS Blue/Green 업그레이드 실습

by 서어켜엉 2026. 5. 4.

새로운 EKS 클러스터 생성

새 클러스터는 기존 EKS 클러스터와 같은 VPC 에 생성한다.

  • Network connectivity
    • 리소스 간 원활한 통신이 보장되어, 워크로드와 데이터 마이그레이션이 한층 수월하다.
  • Shared resources
    • NAT 게이트웨이, VPN 연결, Direct Connect 등 기존 VPC 리소스를 재사용할 수 있어 복잡성과 비용을 줄일 수 있다.
  • Security groups
    • 두 클러스터에 걸쳐 일관된 보안 그룹 규칙을 유지할 수 있으므로 보안 관리가 단순해진다.
  • Service discovery
    • AWS Cloud Map이나 유사한 서비스 디스커버리 메커니즘을 사용하는 경우, 클러스터 간에 서비스들이 서로를 더 쉽게 찾을 수 있다.
  • Subnet utilization
    • 기존 서브넷을 효율적으로 활용할 수 있어 새로운 네트워크 대역을 추가로 프로비저닝할 필요가 없다.
  • VPC peering
    • 해당 VPC가 다른 VPC와 피어링되어 있다면, 그 연결은 두 클러스터 모두에서 그대로 유효하게 사용된다.
    • Transit Gateway를 사용하고 있다고 해도 비슷한 효과를 볼 수 있다.
  • Consistent DNS
    • 동일한 VPC를 사용함으로써 프라이빗 DNS 영역과 Route 53 구성을 일관되게 적용할 수 있다.
  • IAM and resource policies
    • 다수의 IAM 역할과 리소스 정책이 VPC 단위로 범위가 지정되어 있기 때문에, 동일한 VPC를 사용하면 권한 관리가 단순해진다.

[기존 사용하던 클러스터로 실습을 진행. 모듈은 Custom 모듈을 사용했기 때문에, 공개된 모듈과는 조금 차이가 있음]

module "eks_green" {
  source = "./modules/terraform-aws-eks"

  name               = "${local.project_code}-eks"
  eks_version        = "1.35"
  role_arn           = module.eks_cluster_role.arn
  subnet_ids         = local.sbn_app_ids
  security_group_ids = [module.eks_cp_sg.id]
  endpoint_public_access = true
  tags               = { Environment = var.environment }
}

# --------------------
# EKS Node Group
# --------------------

module "eks_ng_green" {
  source = "./modules/terraform-aws-eks-ng"

  cluster_name    = module.eks_green.name
  node_group_name = "${local.project_code}-eks-ng-1"
  node_role_arn   = module.eks_node_role.arn
  subnet_ids      = local.sbn_app_ids

  min_size     = 1
  max_size     = 3
  desired_size = 2

  launch_template_name    = module.eks_node_lt.name
  launch_template_version = module.eks_node_lt.latest_version

  update_config = { max_unavailable = 1 }

  labels = { tier = "platform" }
  taints = [{
    key    = "tier"
    value  = "platform"
    effect = "NO_SCHEDULE"
  }]

  tags = {
    Environment                                = var.environment
    "k8s.io/cluster-autoscaler/enabled"        = "true"
    "k8s.io/cluster-autoscaler/${local.project_code}-eks" = "owned"
  }

  depends_on = [module.eks_addon_pre_node, kubectl_manifest.eniconfig_az1, kubectl_manifest.eniconfig_az3]
}

 

 

Stateless Application Upgrade Process

Stateless 애플리케이션은 클러스터 내에 영구 데이터를 보관할 필요가 없기 때문에, 업그레이드 시 새로운 Green 클러스터에 단순히 배포한 뒤 트래픽을 라우팅하기만 하면 된다.

이번 실습환경에서는 Gitlab에 구성된 개인 repository 에 Source of Truth 를 구축해 두었고, FluxCD를 사용해 blue 클러스터에 배포했다. 

 

Green 배포 시에는 새로운 branch (green)을 따서 업그레이드 하고자 하는 버젼(1.35)에 호환되도록 필요한 변경사항을 적용한다. 

 

FluxCD Bootstrap (Terraform)

resource "helm_release" "flux_green" {
  name             = "flux2"
  repository       = "https://fluxcd-community.github.io/helm-charts"
  chart            = "flux2"
  namespace        = "flux-system"
  create_namespace = true
  timeout          = 600

  values = [yamlencode({
    # pre-install hook Job (CRD 설치)에 toleration 필요
    cli = {
      tolerations  = [{ key = "tier", value = "platform", effect = "NoSchedule" }]
      nodeSelector = { tier = "platform" }
    }
    helmController            = { tolerations = [{ key = "tier", value = "platform", effect = "NoSchedule" }], nodeSelector = { tier = "platform" } }
    kustomizeController       = { tolerations = [{ key = "tier", value = "platform", effect = "NoSchedule" }], nodeSelector = { tier = "platform" } }
    sourceController          = { tolerations = [{ key = "tier", value = "platform", effect = "NoSchedule" }], nodeSelector = { tier = "platform" } }
    notificationController    = { tolerations = [{ key = "tier", value = "platform", effect = "NoSchedule" }], nodeSelector = { tier = "platform" } }
    imageAutomationController = { tolerations = [{ key = "tier", value = "platform", effect = "NoSchedule" }], nodeSelector = { tier = "platform" } }
    imageReflectionController = { tolerations = [{ key = "tier", value = "platform", effect = "NoSchedule" }], nodeSelector = { tier = "platform" } }
  })]

  depends_on = [module.eks_green, module.eks_ng_green]
}

 

 

최대한 자동화를 시키기 위해서 FluxCD만 Terraform 으로 부트스트랩하고 그 외 매니페스트들은 전부 GitOps 구성을 해뒀더니, Git 주소만 제대로 입력해줘도 모든 stateless app 들이 마이그레이션 완료가 됐다.

 

Traffic Routing

애플리케이션이 배포된 후에는, Blue 클러스터에서 Green 클러스터로 트래픽을 어떻게 전환할 것인지가 중요한 과제이다. 이를 위해 Amazon Route 53의 가중치 레코드(weighted records)나 AWS Application Load Balancer의 가중치 대상 그룹(weighted target groups) 등 다양한 기법을 활용할 수 있다.

 

Amazon Route 53의 가중치 리소스 레코드를 사용하면, 각 클러스터에 정의된 인그레스(ingress) 리소스를 가리키는 DNS 레코드의 가중치를 변경함으로써 Blue-Green 업그레이드 또는 카나리(canary) 방식의 마이그레이션 시 트래픽을 점진적으로 전환할 수 있다.

 

Route 53 가중치 레코드는 도메인 이름(example.com)으로 향하는 트래픽을 여러 엔드포인트로 분산시킬 수 있게 해준다. 동일한 이름과 타입을 가진 여러 레코드에 0부터 255 사이의 가중치를 할당할 수 있다. Route 53은 가중치 집합 내 모든 레코드의 가중치 합계를 계산한 후, 각 레코드의 가중치가 전체에서 차지하는 비율에 따라 트래픽을 라우팅한다. 예를 들어 가중치가 각각 1과 3인 두 개의 레코드가 있다면, 첫 번째 레코드는 전체 트래픽의 25%(1/4)를, 두 번째 레코드는 75%(3/4)를 받게 된다.

 

  • Blue 클러스터 DNS 레코드 = 100, Green 클러스터 DNS 레코드 = 0인 경우, Route 53은 모든 요청을 Blue 클러스터로 라우팅한다.
  • Blue 클러스터 DNS 레코드 = 0, Green 클러스터 DNS 레코드 = 100인 경우, Route 53은 모든 요청을 Green 클러스터로 라우팅한다.
  • 그 사이의 값을 지정할 수도 있다. 예를 들어 Blue 클러스터 = 50, Green 클러스터 = 50으로 설정하면, 두 클러스터 사이에서 요청이 균등하게 분산된다.

Amazon Route 53에 DNS 레코드 가중치를 설정하기 위해 인그레스 리소스에 다음과 같은 어노테이션을 활용할 수 있다.

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

 

 

  • set-identifier 어노테이션에는 생성하려는 클러스터의 이름이 들어가며, 이 값은 external-dns의 txtOwnerId 테라폼(Terraform) 구성에 정의된 값과 반드시 일치해야 합니다.
  • aws-weight 어노테이션은 가중치 레코드의 값을 구성하는 데 사용됩니다. 이 값은 Helm values를 통해 배포되며, Terraform에 의해 주입됩니다. 이를 통해 플랫폼 팀은 EKS 클러스터 간에 워크로드를 언제, 어떻게 마이그레이션할지를 자율적으로 제어할 수 있습니다.
  • Terraform의 관련 값을 변경하고 구성을 적용하기만 하면 워크로드 마이그레이션을 트리거할 수 있습니다.

 

 

Stateful Application Upgrade Process

(Stateful application 예시가 없어서 워크샵 내용을 정리함)

 

Blue 및 Green EKS 클러스터를 모두 배포해 둔 상태에서 진행한다. 쿠버네티스에서 실행되는 Stateful 애플리케이션의 영구 스토리지를 호스팅하는 방법에는 Amazon EBS 볼륨, 또는 클러스터 간 동기화가 필요한 데이터베이스 컨테이너 등 다양한 선택지가 있다. 

이 워크샵 내 실습은 클러스터 간 데이터를 실제로 동기화하는 대신 EFS 를 공유스토리지로 사용해서 두 클러스터에 마운트하는 방식을 사용했다.

 

  • Blue 클러스터에 쿠버네티스 StatefulSet 리소스 생성
  • Blue 클러스터에서 정적 HTML 파일을 수동으로 생성 — 이 파일은 최종적으로 공유 EFS 파일 시스템에 저장됩니다.
  • Green 클러스터에 nginx를 실행하는 파드를 호스팅하는 StatefulSet 리소스를 생성하고, Blue 클러스터의 파드와 동일한 파일 스토리지에 접근하도록 구성
  • (선택 사항) Blue 클러스터에서 기존 StatefulSet 삭제

 

 

 


let textNodes = document.querySelectorAll("div.tt_article_useless_p_margin.contents_style > *:not(figure):not(pre)"); textNodes.forEach(function(a) { a.innerHTML = a.innerHTML.replace(/`(.*?)`/g, '$1'); });