개발하는 자몽

CI/CD에 블루-그린 무중단 배포 적용하기: AWS ELB, AWS Auto Scaling Groups(ASG) 본문

Architecture & Tool/AWS

CI/CD에 블루-그린 무중단 배포 적용하기: AWS ELB, AWS Auto Scaling Groups(ASG)

jaamong 2025. 8. 9. 21:44

꽤... 늦었는데, 지난번에 구축한 CI/CD에 블루-그린 무중단 배포를 적용해 보자.

📌지난 글: 2025.07.28 - [Architecture & Tool/AWS] - CI/CD 구축하기: GitHub Actions (OIDC) + Amazon S3 + Amazon CodeDeploy

 

 

보안 그룹

ALB 보안 그룹 생성

ALB를 생성하기 전에 보안 그룹을 만들자. EC2 > 보안 그룹으로 이동하여 보안 그룹 생성을 클릭한다.

아웃바운드 규칙은 건들지 않고 인바운드 규칙만 편집한다. 

  • 유형: HTTP
  • 소스 유형: 사용자 지정
  • 소스: 0.0.0.0/0

EC2 보안 그룹 수정

애플리케이션에 대한 요청을 ALB를 통해서만 받을 수 있도록 이전 포스트에서 생성했던 EC2 보안그룹을 수정해야 한다.

이전 포스트에서 두 개의 인바운드 규칙을 추가했었다. 

  1. 외부에서 EC2 내부로 접속하기 위한 SSH 22번 포트에 관한 규칙
  2. 외부에서 EC2 내부에서 실행되고 있는 애플리케이션에게 요청을 보내기 위한 사용자 지정 TCP 8080(애플리케이션 별 다름)번 포트에 관한 규칙

이번에는 애플리케이션 실행 포트 번호에 대해서 수정해야 한다. 아래와 같이 수정하자. 

  • 유형: 사용자 지정 TCP
  • 포트 범위: 8080 (애플리케이션이 실행되는 포트 번호)
  • 소스 유형: 사용자 지정
  • 소스: 앞서 생성한 ALB 보안 그룹을 선택 (ALB를 통해서만 요청을 받을 수 있게 됨)

SSH 관련 인바운드 규칙은 유지한다.

 

대상 그룹

보안 그룹은 말 그대로 보안을 위한, 리소스를 둘러싼 벽(방화벽, firewall)같은 거라 ALB가 받은 요청이 마법처럼 EC2에게 전달될 수 없다. 따라서 ALB와 EC2 인스턴스를 이어주는 다리가 필요하다. 이 다리 역할을 대상 그룹(Target Group)이 수행한다. 

대상 그룹 생성

EC2 > 대상 그룹으로 이동하여 대상 그룹 생성을 클릭한다. 

 

1단계: 그룹 세부 정보 지정

  • 기본 구성: 인스턴스 ; ALB가 받은 트래픽을 인스턴스 단위로 전달(분산)할 것이므로
  • 대상 그룹 이름: 자유롭게 입력(어떤 서비스를 위한 대상 그룹인지, 그리고 대상 그룹임을 명시하는 이름이 좋은 것 같다)
  • 프로토콜: HTTP
  • 포트: 8080 ; 앞서 수정한 EC2의 보안그룹의 포트와 동일해야 한다. 이 포트를 통해 ALB가 받은 트래픽이 라우팅 된다.
  • VPC: 트래픽을 받을 EC2 인스턴스가 존재하는 VPC를 선택한다. 
  • 상태 검사: 애플리케이션에 헬스 체크 API가 별도로 있다면, 상태 검사 경로에 해당 API의 엔드포인트를 입력한다. 
  • 언급하지 않은 나머지 항목들은 기본값을 유지하고, 다음으로 넘어간다. 

2단계: 대상 등록

  • 사용 가능한 인스턴스: 트래픽을 받을 인스턴스 선택 (앞서 보안그룹을 수정한 EC2를 선택하면 된다)
  • 선택한 인스턴스를 위한 포트: 8080
  • 아래에 보류 중인 것으로 포함 클릭
  • 대상 그룹 생성 클릭

Application Load Balancer

EC2 인스턴스 앞에서 트래픽을 받아줄 로드밸런서를 생성할 차례다. EC2 > 로드 밸런서로 이동하고, 로드 밸런서 생성을 클릭한다. 로드 밸런서 유형은 Application Load Balancer를 선택한다. 

  • 로드 밸런서 이름: 자유롭게 입력
  • 체계(schema): 인터넷 경계(internet-facing)
  • VPC: 대상 그룹과 EC2 인스턴스가 놓인 VPC와 동일해야 함
  • 가용 영역 및 서브넷: 2개 이상 선택해야 한다. EC2 인스턴스와 다른 서브넷에 위치해도 상관없다. 
    • 이런 경우에서의 ALB 사용 사례를 잘 생각해보자.  보통 EC2 인스턴스를 프라이빗 서브넷에 놓는데(보안), 트래픽을 앞에서 먼저 받아 전달해 주는 ALB는 퍼블릭 서브넷에 놓는 편 → 둘은 다른 서브넷에 위치해도 OK.
  • 보안 그룹: 처음에 ALB를 위해 생성했던 보안그룹을 선택
  • 리스너 및 라우팅(다음 프로토콜 및 포트로 트래픽을 받아 대상 그룹으로 트래픽을 전달(라우팅))
    • 프로토콜 - 포트: HTTP - 80
    • 대상 그룹: 앞서 만들었던 대상 그룹을 선택한다. 
  • 언급하지 않은건 기본값으로 남겨둔다. 
  • 이제 요약을 잘 확인해 보고 이상이 없다면 생성을 완료한다. 

AMI

AMI(Amazon Machin Image)인스턴스를 생성할 때 사용되는 이미지로, OS처럼 생각하면 될 것 같다. AMI가 있으면 다른 인스턴스를 생성해도 동일한 OS 및 환경을 구성할 수 있다. 우리는 이미 생성된 인스턴스가 있으므로, 그 인스턴스를 이용해 AMI를 생성할 것이다. 

오토 스케일링은 인스턴스를 트래픽에 따라 자동으로 생성 및 삭제를 수행하는 기능으로, 이 기능을 사용하려면 시작 템플릿(Launch Template)이 필요하고, 이를 생성하려면 AMI가 필요하다. 우리가 새로운 인스턴스 만들 때 AMI를 선택하는 것처럼 자동으로 인스턴스 생성 시 사용될 AMI가 있어야 한다. 

 

EC2 > 인스턴스로 이동하여 오토 스케일링을 적용할 인스턴스를 선택하고, 작업 > 이미지 및 템플릿 > 이미지 생성을 클릭한다. 

  • 이미지 이름: 자유롭게 입력하나, 어떤 이미지인지 잘 식별되도록 하자.
  • 이미지 설명: 넘기지 말고, 어떤 목적으로 생성했는지 작성하자.
  • 나머지는 기본값으로 두고 이미지 생성을 완료한다. 

바로 생성이 완료되지는 않고, 몇 분 기다리다보면 사용 가능이라고 상태가 뜬다. 

 

시작 템플릿

우리는 인스턴스를 생성할 때 AMI 선택 외에도 인스턴스에 관한 스펙(인스턴스 유형, 네트워크 설정 등)을 설정한다. 오토 스케일링으로 만들어질 인스턴스의 스펙은 시작 템플릿(Launch Template)을 통해 설정된다. 

 

EC2 > 시작 템플릿으로 이동하여 시작 템플릿 생성을 클릭한다. 

  • 시작 템플릿 이름: 자유롭게 입력
  • 템플릿 버전 설명: 보통 애플리케이션 이름과 함께 템플릿(Template)을 같이 적어주는 것 같다 (예시: HelloWorld Web Server Template)
  • 애플리케이션 및 OS 이미지: 내 AMI > 내 소유 > 앞서 생성한 AMI 선택
  • 인스턴스 유형: 애플리케이션 스펙에 맞춰서 선택하기. 연습용이거나 작은 프로젝트라면 `t3.micro`나 `t3.small`로 충분하다(docker로 서버를 띄운다면 small 권장)
  • 키 페어 이름: 시작 템플릿에 포함하지 않음 
  • 서브넷/가용 영역: 시작 템플릿에 포함하지 않음
  • 방화벽(보안 그룹): 오토 스케일링을 적용할(현재 CI/CD 되고 있는) EC2 인스턴스와 동일한 보안 그룹을 선택한다. 
  • 고급 세부 정보 > 사용자 데이터: 여기에 Java17 및 CodeDeploy 에이전트 설치와 애플리케이션을 실행할 디렉토리를 생성하는 명령어를 작성한다. 이 과정에서 인스턴스의 AMI는 Ubuntu라서 이 OS에 맞춰서 명령어를 사용했다. (AMI에서 하는 것이 더 적절해 보이는데 일단 여기에서)
    #!/bin/bash
    sudo apt update -y
    sudo apt install -y openjdk-17-jdk
    
    sudo apt install ruby-full
    sudo apt install wget 
    cd /home/ubuntu
    wget https://bucket-name.s3.region-identifier.amazonaws.com/latest/install
    chmod +x ./install
    sudo ./install auto
    
    mkdir -p /home/ec2-user/hello
  • 언급하지 않은 나머지는 기본값으로 두고 생성을 완료한다. 

오토 스케일링 그룹(ASG)

AWS ASG는 인스턴스를 그룹 단위로 관리하며, 트래픽 변화에 따라 스케일링 정책을 기반으로 자동으로 인스턴스 수를 증가시키거나 감소시킬 수 있다. 

 

EC2 > Auto Scaling 그룹으로 이동, Auto Scaling 그룹 생성을 클릭한다. 

1단계: 시작 템플릿 선택 

  • 이름: 자유롭게 입력
  • 시작 템플릿: 앞서 생성한 템플리 선택

이후 다음 클릭

 

2단계: 인스턴스 시작 옵션 선택

  • VPC: EC2 인스턴스와 동일한 VPC 선택
  • 가용 영역 및 서브넷: 자유롭게 선택. 모두 선택해도 상관없다. 단순하게만 보자면 많이 선택할 수록 가용성이 높아진다고 생각하면 될 것 같다. 최소한의 가용성을 고려했을 때 적어도 두 개는 선택하는 게 좋다. 
  • 가용 영역 배포: 균형 잡힌 최선 노력

이후 다음 클릭

 

3단계: 다른 서비스와 통합

  • 로드 밸런싱: 기존 로드 밸런서에 연결
  • 기존 로드 밸런서에 연결: 로드 밸런서 대상 그룹에서 선택
  • 기존 로드밸런서 대상 그룹: 앞서 만든 대상 그룹 선택
  • 상태 확인: Elastic Load Balancer 상태 확인 켜기 활성화 
    • 로드 밸런서가 인스턴스의 상태를 확인하고, 문제가 있는 인스턴스가 있으면 오토 스케일링이 이를 확인하고 인스턴스를 교체함

4단계: 그룹 크기 및 크기 조정 구성

  • 원하는 용량(Desired capacity): 1 (상황에 맞춰서 진행, 여기서는 간단히 기본값으로 진행)
    • 생성 시에 만들어지는 인스턴스 개수로, ASG는 이 수를 유지하려고 한다. 
    • 1로 설정한 경우를 예시로 보자. 실행되고 있는 한 개의 인스턴스가 예상치 못하게 종료되면, 원하는 용량값인 1을 유지하기 위해 ASG가 한 개를 새롭게 생성한다. 
    • *원하는 최소 용량 ≤ 원하는 용량 ≤ 원하는 최대 용량
  • 원하는 최소 용량: 1 (상황에 맞춰서 진행)
    • 스케일링 정책이 설정된 경우, 그룹의 원하는 용량을 이 값보다 더 작게 감소시키지 못함
  • 원하는 최대 용량: 2 (상황에 맞춰서 진행)
    • 스케일링 정책이 설정된 경우, 그룹의 원하는 용량을 이 값보다 더 크게 증가시키지 못함
  • Automatic scaling: 크기 조정 정책 없음
    • 이 기능이 실제로 "오토 스케일링"이라고 부르는 부분이다. 지금은 정책 없음을 선택해서 ASG가 트래픽 변화에 따라 자동으로 스케일링 할 수 없다. 우리가 수동으로 원하는 용량을 변경하여 그룹 크기를 변경해야 한다. 따라서 `원하는 최소 용량`으로만 유지된다. 
    • 지금은 인스턴스 개수가 유지만 되도록 정책 없음을 선택했다. 
  • 인스턴스 유지 관리 정책: 정책 없음

나머지는 손대지 않고 다음으로 넘어간다. 

 

5단계: 알림 추가

이 기능을 사용하려면 AWS의 다른 기능도 설정해야 해서 이 글에서는 넘어간다.

 

6단계: 태그 추가

이 단계도 그냥 넘어가도 된다. 태그를 설정하면 새로 생성되는 인스턴스의 이름을 지정할 수 있다. 보통 키는 `Name`을 유지하고 값에 애플리케이션/프로젝트 이름을 넣는 것 같다. 

 

7단계: 검토

설정값들을 마지막으로 확인하고 생성을 완료한다.

 

ASG 생성이 완료되면 원하는 용량을 유지하기 위해 새 인스턴스를 생성한다. → 지금까지의 과정을 통해 생성된 인스턴스는 총 2개(이전 글에서 처음에 1개만 생성했다면)

 

CodeDeploy

CodeDeploy도 수정해야 한다. 우선 CodeDeploy의 IAM 역할부터 수정한다.

 

IAM 역할 - 권한 추가

IAM > 역할로 이동해서 전에 CodeDeploy를 위해 생성한 IAM 역할을 클릭한다. 이전 글에서는  `CodeDepoyServiceRoleForEC2`라는 이름으로 생성했다. 이동한 화면에서 권한 > 권한 추가 > 인라인 정책 생성을 클릭한다.

 

정책 편집기에서 JSON을 선택하고 아래와 같이 작성한다. (시작 템플릿으로 ASG을 생성한 경우, 다음 권한들이 필요함)

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "VisualEditor0",
			"Effect": "Allow",
			"Action": [
				"iam:PassRole",
				"ec2:CreateTags",
				"ec2:RunInstances"
			],
			"Resource": "*"
		}
	]
}

 

작성 후 다음으로 넘어가고, 권한 추가를 완료한다. 

 

CodeDeploy 배포 그룹 편집

CodeDeploy > 애플리케이션 > 앞서 생성한 애플리케이션(SpringBootApp) > 앞서 생성한 배포 그룹(production)까지 이동하자. 이동한 화면에서 편집을 클릭한다.

 

이동하면 이전에 배포 그룹을 생성했을 때 보았던 화면이 나온다.

  • 배포 유형: 블루/그린 (현재 위치 → 블루/그린)
  • 환경 구성: Amazon EC2 Auto Scaling 그룹 자동 복사
  • ASG 선택: 앞서 생성한 ASG 선택
  • 배포 설정 - 새 배포가 생성되면 바로 트래픽을 라우팅 하고, 기존 인스턴스는 15분 후 종료되게 설정한다.  
    • 트래픽 재 라우팅: 즉시 트래픽 라우팅
    • 배포 성공 후 원본 환경 인스턴스 종료 여부 및 종료 전 대기 시간 선택: 배포 그룹의 원본 인스턴스 종료
    • 일: 0
    • 시간: 0
    • 분: 15
    • 배포 구성: CodeDeployDefault.AllAtOnce
  • 로드 밸런서 유형: Application Load Balancer 또는 Network Load Balancer 활성화
  • 대상 그룹 선택: 앞서 생성한 대상 그룹 선택

변경 사항 저장을 클릭하여 편집을 완료한다. 

 

이제 CodeDeploy를 사용하여 ASG의 EC2 인스턴스에 배포할 수 있다!

 

블루/그린 무중단 배포 과정 정리

새로운 버전을 배포하기 전 상태

  • 인스턴스 총 개수: 직접 생성한 EC2 인스턴스 1개(A라고 지칭) + ASG로 생성된 EC2 인스턴스 1개(B라고 지칭)
    • 이 둘을 Blue 그룹이라고 하자.

배포 과정(단순하게 표현함, 실제로는 더 많은 단계가 있음)

  1. 코드 변경 사항이 발생하여 GitHub 레포지토리에 commit/push
  2. GitHub Actions에서 감지(push trigger) → 배포 스크립트(.github/workflows/deploy.yml) 실행
  3. GitHub Actions에서 CI를 진행하고 CodeDeploy를 통해 CD 진행
  4. CodeDeploy를 통해 다음의 과정이 일어난다.
    1. Green 그룹 생성: 2개의 인스턴스를 새롭게 생성
      • 이때 ASG 그룹이 하나 더 늘어나고, 인스턴스 목록도 보면 2개에서 3개로 늘어나있는 것을 볼 수 있다. 
    2. Green 그룹에 새로운 버전의 배포를 진행
    3. Green 그룹으로 트래픽 라우팅
    4. 배포가 성공하고 15분 후에 Blue 그룹 제거
      • 새로 생겼던 ASG 그룹이 유지되고, 기존 그룹은 제거된다. (그룹 수 다시 1개)
      • ASG로 인해 생겼던 기존 인스턴스도 제거되고, 새로운 인스턴스가 유지된다. (인스턴스 수 다시 2개: 수동 생성했던 인스턴스 1 + 지금 새로 생긴 인스턴스 1)
  5. 배포 완료

 

후기

어느 리소스를 생성하든, 그 리소스가 명시되도록 이름을 잘 짓는게 중요하다. 어떤 목적으로 리소스를 생성했는지 설명을 잘 작성하는 것도 무척 중요하다. 

AWS  리소스를 생성하는 과정에서 어떤 옵션을 선택할 때 이 옵션이 무엇인지 작게 설명이 쓰여있는데, 잘 읽어보면 꽤나 도움이 된다. (사실 아리송할 때가 더 많은 것 같기도... 이때 이해 못 해서 공식 문서를 찾아봐도 궁금증이 완전히 해결되는 경우는 아직까지 많지 않은 듯)

 

 

 

 

 

🔖

Comments