도커 컨테이너 기반의 프로젝트에서 CI/CD 적용하면서 겪은 과정을 정리하려고 한다.
name: Deploy To EC2
on:
push:
branches:
- main
- dev
jobs:
deploy:
runs-on: ubuntu-latest
env:
APP_NAME: liveblog-server
steps:
- name: Github Repository 파일 불러오기
uses: actions/checkout@v4
- name: JDK 17버전 설치
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: application.yml 파일 만들기
run: echo "${{ secrets.APPLICATION_PROPERTIES }}" > ./src/main/resources/application.yml
- name: 테스트 및 빌드하기
run: ./gradlew clean build
- name: AWS Resource에 접근할 수 있게 AWS credentials 설정
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-2
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: ECR에 로그인하기
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Docker 이미지 생성
run: docker build -t $APP_NAME .
- name: Docker 이미지에 Tag 붙이기
run: docker tag $APP_NAME ${{ steps.login-ecr.outputs.registry }}/$APP_NAME:latest
- name: ECR에 Docker 이미지 Push하기
run: docker push ${{ steps.login-ecr.outputs.registry }}/$APP_NAME:latest
- name: SSH로 EC2에 접속하기
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script_stop: true
script: |
APP_NAME=${{ env.APP_NAME }}
docker stop $APP_NAME || true
docker rm $APP_NAME || true
docker pull ${{ steps.login-ecr.outputs.registry }}/$APP_NAME:latest
docker run -d --name $APP_NAME -p 80:8080 ${{ steps.login-ecr.outputs.registry }}/$APP_NAME:latest
대략적인 Flow는 다음과 같다.
Git Push
application.yml 파일을 GitHub Secrets에서 복사한다.
Docker 이미지를 생성한다. (docker build -t .)
ECR에 Docker 이미지를 push
SSH로 EC2 접속, ECR에서 도커 이미지를 다운 후 동작
빌드 과정에서 application.yml을 GitHub Secrets에 저장한 후 복사해 사용하는 방식은 민감한 정보를 코드베이스에 노출하지 않아 보안을 강화할 수 있다.
SSH로 EC2 접속하기 부분에서
no basic auth credentials
에러가 지속적으로 발생하면서 실패하였다.
결과적으로 아래 3번 권한 설정이 누락되어 발생한 문제임을 깨닫고 해결하였다.
위 flow가 성공하려면 세가지 권한 설정이 필요하다.
Github Actions가 AWS ECR에 접근 가능해야한다. (이미지 푸시)
Github Actions가 EC2 인스턴스에 접근 가능해야한다. (SSH 접속)
EC2 인스턴스가 AWS ECR에 접근 가능해야한다. (이미지 다운)
3의 권한 설정이 필요한 이유는 ECR의 Private Repository에 이미지를 저장하는 방식을 선택했기 때문이다.
Github Actions가 내 AWS Resource에 접근하려면
적절하게 권한이 설정된 IAM 유저의 Access Key
와 Secret Access Key
가 필요하다.
Access Key
는 유저를 식별하는 ID, Secret Access Key
는 Password 같은 개념이다.
이를 위해 CI-CD 라는 이름의 User Group을 만들고, IAM User 하나를 생성해 연결하였다.
CI-CD 유저 그룹에는 AmazonEC2ContainerRegistryFullAccess
(ECR) 정책을 설정한다.
name: AWS Resource에 접근할 수 있게 AWS credentials 설정
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-2
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
name: ECR에 로그인하기
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
이렇게 생성한 유저의 Access Key, Secret Access Key를 통해 Github Actions에서 ECR에 로그인한다.
AWS는 관리 효율성, 일관성 보장, 보안 강화 측면에서 유저 그룹에 정책을 할당하고, 유저를 해당 그룹에 추가하는 방식을 사용하는 것을 강력히 권장한다.
EC2 인스턴스는 .pem
키를 통한 SSH 방식으로 접속하기 때문에 따로 권한 설정이 필요하지 않다.
따라서 CI-CD 유저 그룹에도 EC2 인스턴스 관련 정책은 포함되지 않는다.
EC2 인스턴스가 ECR의 Private Repository에 접근하려면 적절한 정책이 설정된 Role 을 부여해야한다.
EC2 인스턴스는 유저가 아니기 때문에 Role을 사용해야한다.
유저 그룹과 비슷하게 EC2-ECR 라는 Role을 만들고 AmazonEC2ContainerRegistryFullAccess
정책을 설정한다.
EC2 인스턴스에 생성한 Role을 부여한다.
또한 EC2 내부에서 amazon-ecr-credential-helper
설정을 추가적으로 해주어야한다.
참조 https://github.com/awslabs/amazon-ecr-credential-helper?tab=readme-ov-file
이제 EC2 에서 ECR의 Private Repository 로 접근이 가능하다.
앞서 언급한 no basic auth credentials
에러는 (3)의 권한을 제대로 부여하지 않아 생긴 문제였다.
EC2에 Role을 부여하니 해결되었다.
ECR에 Docker 이미지를 업로드하면, latest가 아닌 이전 버전의 이미지들이 지속적으로 남아있게된다.
ECR은 사용하는 용량만큼 요금이 청구되기 때문에 이를 주기적으로 삭제해야 한다.
Life Cycle Policy를 통해 자동으로 삭제가 가능하며,
untagged 이미지는 삭제하는 정책을 할당하였다.
정책을 할당해도 곧바로 삭제되지는 않았는데, 이는 AWS에서 주기적으로 처리하기 때문에 시간이 걸린다고 한다.
시간이 지나자 이전 버전의 이미지들이 자동으로 삭제되는 것을 확인할 수 있다!