Kubernetes

EKS Blue/Green Migration

mokpolar 2025. 4. 5. 22:01
반응형

안녕하세요?
AEWS 9주차 과제로 EKS Blue/Green Migration 실습을 진행하겠습니다.

Blue/Green Upgrade - Amazon EKS Blueprints for Terraform
내용은 이 문서를 참고 하였습니다.

 

EKS의 업그레이드와 Blue/Green Migration에 대하여

 

쿠버네티스은 계속 업데이트되고, 예전 버젼은 지원을 하지 않게 되며 강제로 업그레이드를 시켜버리니 언젠가는 EKS의 버젼을 업그레이드해야 하는 순간이 옵니다.

 

때 어떤 방식이 가장 안전할지에 대해서 생각해 볼 필요가 있습니다.

 

특히 프로덕션 환경에서 애플리케이션을 안전하게 배포하려면, 안정성과 롤백 가능성을 보장할 수 있는 전략이 필요합니다.

블루-그린 배포는 기존의 버전(블루)과 새로운 버전(그린)을 동시에 존재하게 한 뒤, 트래픽을 스위칭하여 배포하는 전략입니다.

 

이 접근 방식은 다음과 같은 특징이 있습니다:

  • 배포 중 장애 발생 시 쉽게 롤백 가능
  • 무중단 배포 실현

그러면 블루 그린 배포가 왜 필요할까요?

 

전통적인 롤링 업데이트(Rolling Update)는 점진적으로 Pod를 교체하기 때문에, 문제가 생기면 일부 사용자에게 오류가 발생할 수 있습니다.

 

반면 블루-그린 방식은 기존 블루 환경은 그대로 둔 채, 그린 환경을 배포하고 테스트한 뒤 한 번에 트래픽을 전환하기 때문에 다음과 같은 장점이 있습니다:

  • 배포 중 문제 발생 시, 블루로 즉시 전환 가능
  • 고가용성(HA) 환경에서 안정적 전환
  • 실시간 검증 환경 제공

프로젝트 실습

아키텍쳐

 

위와 같은 구성이 됩니다.
같은 VPC내에 EKS Blue, Green을 생성해서 옮기는 구조가 될 것입니다.

코드 준비

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

 

terraform.tfvars.example를 terraform.tfvars에 복사하여 각 환경, eks-blue 및 eks-green 폴더에 심볼릭 링크하고 필요에 따라 region, hosted_zone_name, eks_admin_role_name을 변경합니다.

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

 

terrafrom.tfvars 파일을 살펴보면 아래와 같이,
region, environment_name, hosted_zone_name, eks_admin_role_name을 변경해주어야 합니다.

# You should update the below variables

aws_region          = "eu-west-1"
environment_name     = "eks-blueprint"
hosted_zone_name    = "eks.example.org" # 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"

 

시크릿 매니저를 추가해줍니다.
로컬에 있는 나의 public key를 가지고 만들 수 있습니다.

aws secretsmanager create-secret \
  --name github-blueprint-ssh-key \
  --secret-string "$(cat ~/.ssh/id_ed25519.pub)"
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-2:441758121212:secret:github-blueprint-ssh-key-bYcvSM",
    "Name": "github-blueprint-ssh-key",
    "VersionId": "057c0b26-9177-408d-b53d-53c6d772df81"
}

 

물론 이 키는 GitHub에 등록이 되어있어야 합니다.

배포

cd environment
terraform init
terraform apply

클러스터 생성

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

}

 

그린 클러스터 생성

cd eks-green
terraform init
terraform apply

클러스터가 생성되었습니다.

배포

클러스터는 각 클러스터에 동기화된 기존 ArgoCD Github 저장소로 구성됩니다.

마이그레이션 자동화를 시연하기 위해 워크로드 리포지토리에서 배포된 애플리케이션 중 하나인 팀-번햄 네임스페이스의 번햄 워크로드를 예로 살펴보겠습니다.

 

실행 중인 클러스터의 이름을 본문에서 응답할 수 있는 간단한 go 애플리케이션을 설정했습니다. 이를 통해 워크로드의 현재 마이그레이션을 쉽게 확인할 수 있습니다.

k get deployment -n team-burnham -l app=burnham
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
burnham   3/3     3            3           30m
nginx     1/1     1            1           30m


k get pods -n team-burnham -l app=burnham
NAME                       READY   STATUS    RESTARTS   AGE
burnham-9sk5e2tlks-l23ch   1/1     Running   0          30m
burnham-9sk5e2tlks-kltp5   1/1     Running   0          30m
burnham-9sk5e2tlks-5tfa1   1/1     Running   0          30m

Blue -> Green Migration

처음에는 번햄 트래픽의 100%가 eks-blue 클러스터로 설정되며, 이는 route53_weight = "100"이라는 매개변수를 사용하여 eks-blue/main.tf 및 eks-green/main.tf 파일에서 제어됩니다. 동일한 파라미터가 eks-green 클러스터에서는 0으로 설정됩니다.

 

아래가 100으로 되어 있어서 이런식으로 동작하게 됩니다.

cat eks-blue/main.tf
provider "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"

  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

}

 

DNS 레코드 가중치가 블루 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}' && sleep 60

 

green에는 반대로 0인걸 볼 수 있습니다.

cat eks-green/main.tf
provider "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" # Here, we deploy the cluster with the N+1 Kubernetes Version

  argocd_route53_weight      = "0" # We control with theses parameters how we send traffic to the workloads in the new cluster
  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의 DNS 레코드 가중치를 100으로 하면 아래와 같은 결과가 나오는 것을 볼 수 있습니다.

반응형