AWS Code Pipeline, Code Build, Code Deploy, ECR을 이용한 SpringBoot CI/CD를 구현해보려고 한다.
EC2와 Spring Boot 프로젝트는 미리 준비해준다.
아래 사진은 전체 아키텍처 플로우다.

배포 시나리오
- Github에 코드를 Push하면 Code Pipeline 동작
- Code Build를 통해 빌드해 Docker Image를 ECR에 저장
- S3에 Build Artifacts 업로드
- CodeDeploy를 통해 EC2에 배포
1. IAM 준비
IAM은 AWS에서 사용자를 인증하고 권한을 관리하는 서비스다.
CI/CD를 위해 EC2에 필요한 IAM과 Code Deploy에 필요한 IAM을 생성한다.
EC2 IAM
EC2에는 S3, Code Deploy, ECR에 접근할 수 있는 권한이 필요하다.
IAM > 역할 > 역할 생성을 클릭한다.

AWS 서비스, EC2를 선택한다.

아래 4개의 권한을 선택한다.
- AmazonEC2RoleforAWSCodeDeploy
- AmazonS3FullAccess
- AWSCodeDeployFullAccess
- AWSCodeDeployRole

역할 이름을 입력한 후 역할을 생성한다.

방금 생성한 역할 클릭한다.

권한 추가 > 인라인 정책 생성을 클릭한다.

ECR 관련 추가 정책을 추가해준다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:ListImages"
],
"Resource": "*"
}
]
}

Code Deploy IAM
AWS 서비스, CodeDeploy를 선택한다.

2. EC2 설정
EC2 인스턴스 > 작업 > 보안 > IAM 역할 수정을 클릭한다.

위에서 생성한 EC2 IAM을 선택한다.

EC2에 접속 후 아래와 같은 명령어를 입력해준다.
Code Deploy를 사용하기 위해서는 Agent가 필요하다. 이 Agent는 Ruby가 필요하기 때문에 Ruby 설치가 필요하다.
# 도커 설치
sudo yum install docker -y
sudo sysmtectl enable docker
sudo systemctl start docker
# Ruby 설치
sudo yum install ruby
# wget 설치
sudo yum install wget
# Code Deploy Agent 설치
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
sudo systemctl enable codedeploy-agent
sudo systemctl start codedeploy-agent
3. ECR 설정
ECR(Elastic Container Registry)는 AWS에서 제공하는 Docker 컨테이너 이미지 저장소이다.
ECR > 프라이빗 리포지토리 > 리포지토리 생성을 클릭한다.

리포지토리 이름을 입력해주고 이미지 태그 변경 가능성을 선택한다.

4. Dockerfile 준비
Spring Boot는 CodeBuild에서 빌드하는 것이 속도 면에서 유리하며, 여러 EC2에 배포할 때마다 빌드할 필요가 없다. 따라서 Dockerfile은 이미 빌드된 JAR 파일을 실행하는 역할만 수행한다.
FROM openjdk:17
COPY build/libs/<jar명>.jar /app.jar
CMD ["java", "-jar", "/app.jar"]
5. buildspec.yml
buildspec.yml은 CodeBuild가 할 행동을 정의하는 파일이다. 이 파일은 SpringBoot 프로젝트 최상위에 만들어준다.
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com
- REPOSITORY_URI=904233098323.dkr.ecr.ap-northeast-2.amazonaws.com/ci_cd
- IMAGE_TAG=dev
build:
commands:
- ./gradlew build
- docker build -t $REPOSITORY_URI:$IMAGE_TAG .
post_build:
commands:
- docker push $REPOSITORY_URI:$IMAGE_TAG
cache:
paths:
- '/root/.gradle/**/*'
- '/tmp/gradle/**/*'
artifacts:
files:
- appspec.yml
- scripts/*
코드를 살펴보면 AWS ECR에 로그인하는 부분이 있다. 저 명령어는 ECR Repository 생성 후 푸시 명령 보기를 누르면 아래와 같이 나온다.

6. appspec.yml
appspec.yml은 CodeDeploy의 행동을 정의하는 파일이다.
buildspec.yml과 마찬가지로 SpringBoot 프로젝트의 최상위에 만들어준다.
version: 0.0
os: linux
files:
- source: /
destination: /home
hooks:
ApplicationStart:
- location: scripts/deploy.sh
timeout: 300
runas: root
7. deploy.sh
SpringBoot 프로젝트 최상위 기준으로 /scripts/deploy.sh 경로로 만들어준다.
# 실패 시 즉시 종료
set -e
# AWS ECR 로그인
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com
# 기존 컨테이너 중지 및 삭제
docker stop aws_ci_cd || true
docker rm aws_ci_cd || true
# 최신 이미지 가져오기
docker pull 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com/ci_cd:dev
# 컨테이너 실행
docker run -d --name ci_cd -p 8080:8080 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com/ci_cd:dev
8. CodeBuild
CodeBuild > 빌드 프로젝트 > 프로젝트 생성을 클릭한다.

프로젝트 이름을 입력해주고, Github 리포지토리를 연동해준다.

우리는 CodePipeline을 통해 Webhook 트리거를 사용할거기 때문에 CodeBuild에서는 Webhook 트리거를 비활성화해준다.

빌드된 DockerImage를 ECR에 저장할거기 때문에 아티팩트 없음을 선택한다.
캐시를 선택해주면 더 빠른 빌드가 가능하다.
아래와 같이 선택하고 빌드 프로젝트를 생성한다.

CodeBuild 프로젝트를 생성한 후 IAM으로 이동해 위에서 생성된 역할에 AmazonEC2ContainerRegistryPowerUser 권한을 추가해준다.

9. CodeDeploy
CodeDeploy > 애플리케이션 > 애플리케이션 생성을 클릭한다.

EC2를 이용해 SpringBoot 배포를 진행하므로 EC2/온프레미스를 선택한다.

배포 그룹 생성을 클릭한다.

위에서 생성한 Code Deploy IAM을 선택해준다.
배포 유형 - 블루/그린을 선택하면 무중단 배포가 가능하다. 하지만 이번 글에서는 단순 배포 CI/CD를 구현하므로 현재 위치를 선택한다.
환경 구성은 Amazon EC2 인스턴스를 선택하고, 위에서 만든 EC2의 이름을 입력해준다.

로드 밸런싱도 사용하지 않으므로 비활성화하고 배포 그룹을 생성해준다.

10. CodePipeline
CodePipeline > 파이프라인 > 파이프라인 생성을 클릭한다.

사용자 지정 파이프라인 빌드를 선택한다.

실행 모드는 아래 3가지 중 상황에 맞게 선택하면 된다.
- 대체 : 새로운 파이프라인이 실행되면 이전 파이프라인이 종료됨
- 대기됨 : 실행 중인 파이프라인이 끝날 때까지 새로운 실행이 대기열(Queue)에서 기다림
- 병렬 : 여러 실행이 동시에 진행될 수 있음

소스 스테이지는 Github 리포지토리를 연동해 선택해준다.
우리는 Github에 코드를 푸시하면 Webhook을 이용해 CodePipeline을 트리거해야하므로 Webhook 사용을 체크해준다.

빌드 스테이지는 위에서 생성한 CodeBuild 프로젝트를 연동해준다.

테스트 스테이지는 별도로 사용하지 않으므로 건너뛴다.

배포 스테이지에는 위에서 생성한 CodeDeploy 애플리케이션과 배포 그룹을 설정해준다.

이제 CodePipeline 설정을 완료하면 자동으로 CodePipeline이 실행된다. 하지만 이때 대부분 실패할 것이다.
Github에 코드를 Push하고 다시 살펴보면 CI/CD가 정상적으로 수행된다.

AWS Code Pipeline, Code Build, Code Deploy, ECR을 이용한 SpringBoot CI/CD를 구현해보려고 한다.
EC2와 Spring Boot 프로젝트는 미리 준비해준다.
아래 사진은 전체 아키텍처 플로우다.

배포 시나리오
- Github에 코드를 Push하면 Code Pipeline 동작
- Code Build를 통해 빌드해 Docker Image를 ECR에 저장
- S3에 Build Artifacts 업로드
- CodeDeploy를 통해 EC2에 배포
1. IAM 준비
IAM은 AWS에서 사용자를 인증하고 권한을 관리하는 서비스다.
CI/CD를 위해 EC2에 필요한 IAM과 Code Deploy에 필요한 IAM을 생성한다.
EC2 IAM
EC2에는 S3, Code Deploy, ECR에 접근할 수 있는 권한이 필요하다.
IAM > 역할 > 역할 생성을 클릭한다.

AWS 서비스, EC2를 선택한다.

아래 4개의 권한을 선택한다.
- AmazonEC2RoleforAWSCodeDeploy
- AmazonS3FullAccess
- AWSCodeDeployFullAccess
- AWSCodeDeployRole

역할 이름을 입력한 후 역할을 생성한다.

방금 생성한 역할 클릭한다.

권한 추가 > 인라인 정책 생성을 클릭한다.

ECR 관련 추가 정책을 추가해준다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:ListImages"
],
"Resource": "*"
}
]
}

Code Deploy IAM
AWS 서비스, CodeDeploy를 선택한다.

2. EC2 설정
EC2 인스턴스 > 작업 > 보안 > IAM 역할 수정을 클릭한다.

위에서 생성한 EC2 IAM을 선택한다.

EC2에 접속 후 아래와 같은 명령어를 입력해준다.
Code Deploy를 사용하기 위해서는 Agent가 필요하다. 이 Agent는 Ruby가 필요하기 때문에 Ruby 설치가 필요하다.
# 도커 설치
sudo yum install docker -y
sudo sysmtectl enable docker
sudo systemctl start docker
# Ruby 설치
sudo yum install ruby
# wget 설치
sudo yum install wget
# Code Deploy Agent 설치
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
sudo systemctl enable codedeploy-agent
sudo systemctl start codedeploy-agent
3. ECR 설정
ECR(Elastic Container Registry)는 AWS에서 제공하는 Docker 컨테이너 이미지 저장소이다.
ECR > 프라이빗 리포지토리 > 리포지토리 생성을 클릭한다.

리포지토리 이름을 입력해주고 이미지 태그 변경 가능성을 선택한다.

4. Dockerfile 준비
Spring Boot는 CodeBuild에서 빌드하는 것이 속도 면에서 유리하며, 여러 EC2에 배포할 때마다 빌드할 필요가 없다. 따라서 Dockerfile은 이미 빌드된 JAR 파일을 실행하는 역할만 수행한다.
FROM openjdk:17
COPY build/libs/<jar명>.jar /app.jar
CMD ["java", "-jar", "/app.jar"]
5. buildspec.yml
buildspec.yml은 CodeBuild가 할 행동을 정의하는 파일이다. 이 파일은 SpringBoot 프로젝트 최상위에 만들어준다.
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com
- REPOSITORY_URI=904233098323.dkr.ecr.ap-northeast-2.amazonaws.com/ci_cd
- IMAGE_TAG=dev
build:
commands:
- ./gradlew build
- docker build -t $REPOSITORY_URI:$IMAGE_TAG .
post_build:
commands:
- docker push $REPOSITORY_URI:$IMAGE_TAG
cache:
paths:
- '/root/.gradle/**/*'
- '/tmp/gradle/**/*'
artifacts:
files:
- appspec.yml
- scripts/*
코드를 살펴보면 AWS ECR에 로그인하는 부분이 있다. 저 명령어는 ECR Repository 생성 후 푸시 명령 보기를 누르면 아래와 같이 나온다.

6. appspec.yml
appspec.yml은 CodeDeploy의 행동을 정의하는 파일이다.
buildspec.yml과 마찬가지로 SpringBoot 프로젝트의 최상위에 만들어준다.
version: 0.0
os: linux
files:
- source: /
destination: /home
hooks:
ApplicationStart:
- location: scripts/deploy.sh
timeout: 300
runas: root
7. deploy.sh
SpringBoot 프로젝트 최상위 기준으로 /scripts/deploy.sh 경로로 만들어준다.
# 실패 시 즉시 종료
set -e
# AWS ECR 로그인
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com
# 기존 컨테이너 중지 및 삭제
docker stop aws_ci_cd || true
docker rm aws_ci_cd || true
# 최신 이미지 가져오기
docker pull 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com/ci_cd:dev
# 컨테이너 실행
docker run -d --name ci_cd -p 8080:8080 904233098323.dkr.ecr.ap-northeast-2.amazonaws.com/ci_cd:dev
8. CodeBuild
CodeBuild > 빌드 프로젝트 > 프로젝트 생성을 클릭한다.

프로젝트 이름을 입력해주고, Github 리포지토리를 연동해준다.

우리는 CodePipeline을 통해 Webhook 트리거를 사용할거기 때문에 CodeBuild에서는 Webhook 트리거를 비활성화해준다.

빌드된 DockerImage를 ECR에 저장할거기 때문에 아티팩트 없음을 선택한다.
캐시를 선택해주면 더 빠른 빌드가 가능하다.
아래와 같이 선택하고 빌드 프로젝트를 생성한다.

CodeBuild 프로젝트를 생성한 후 IAM으로 이동해 위에서 생성된 역할에 AmazonEC2ContainerRegistryPowerUser 권한을 추가해준다.

9. CodeDeploy
CodeDeploy > 애플리케이션 > 애플리케이션 생성을 클릭한다.

EC2를 이용해 SpringBoot 배포를 진행하므로 EC2/온프레미스를 선택한다.

배포 그룹 생성을 클릭한다.

위에서 생성한 Code Deploy IAM을 선택해준다.
배포 유형 - 블루/그린을 선택하면 무중단 배포가 가능하다. 하지만 이번 글에서는 단순 배포 CI/CD를 구현하므로 현재 위치를 선택한다.
환경 구성은 Amazon EC2 인스턴스를 선택하고, 위에서 만든 EC2의 이름을 입력해준다.

로드 밸런싱도 사용하지 않으므로 비활성화하고 배포 그룹을 생성해준다.

10. CodePipeline
CodePipeline > 파이프라인 > 파이프라인 생성을 클릭한다.

사용자 지정 파이프라인 빌드를 선택한다.

실행 모드는 아래 3가지 중 상황에 맞게 선택하면 된다.
- 대체 : 새로운 파이프라인이 실행되면 이전 파이프라인이 종료됨
- 대기됨 : 실행 중인 파이프라인이 끝날 때까지 새로운 실행이 대기열(Queue)에서 기다림
- 병렬 : 여러 실행이 동시에 진행될 수 있음

소스 스테이지는 Github 리포지토리를 연동해 선택해준다.
우리는 Github에 코드를 푸시하면 Webhook을 이용해 CodePipeline을 트리거해야하므로 Webhook 사용을 체크해준다.

빌드 스테이지는 위에서 생성한 CodeBuild 프로젝트를 연동해준다.

테스트 스테이지는 별도로 사용하지 않으므로 건너뛴다.

배포 스테이지에는 위에서 생성한 CodeDeploy 애플리케이션과 배포 그룹을 설정해준다.

이제 CodePipeline 설정을 완료하면 자동으로 CodePipeline이 실행된다. 하지만 이때 대부분 실패할 것이다.
Github에 코드를 Push하고 다시 살펴보면 CI/CD가 정상적으로 수행된다.
