회사에서는 다양한 플랫폼을 운영하고 있어, 해당 플랫폼의 어드민 사이트도 여러 개 운영 중이다. 그 중 호텔 시스템을 관리하는 서비스는 Nuxt.js(vue)로 개발되어 있었는데, 선임 책임님께서 Next.js(react)로 마이그레이션하는 작업을 하던 중 퇴사하셨다. 작업 초기에 나가셔서 배포 프로세스가 구축되지 않은 상황이며, 기획, 디자인, 개발 등 추가 요청 사항이 생길 때마다 일일이 수동 배포를 해야하거나, 자리로 불러서 확인시켜 드려야 하는 불편한 상황이 발생했다. 따라서 이번에는 배포 자동화를 구축하여, 작업을 보다 원활하게 진행하고자 한다.
AS-IS
# Dockerfile
FROM node:16.13.2-alpine
ENV HOST 0.0.0.0
WORKDIR /app
COPY package.json ./
COPY yarn.lock ./
RUN echo "BASE_URL=process.env.BASE_URL" >> .env
RUN echo "API_KEY=process.env.API_KEY" >> .env
RUN yarn
COPY . .
RUN yarn build
EXPOSE 3000
CMD [ "yarn", "start" ]
# deploy.sh
# sh ./deploy.sh
# ECR 배포 완료 후 ECS 클러스터 업데이트 및 태스크 재실행
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 292885173959.dkr.ecr.ap-northeast-2.amazonaws.com
branch=$(git symbolic-ref --short HEAD)
docker build -t staypia-admin-web/${branch} .
docker tag staypia-admin-web/${branch}:latest 292885173959.dkr.ecr.ap-northeast-2.amazonaws.com/staypia-admin-web/${branch}:latest
docker push 292885173959.dkr.ecr.ap-northeast-2.amazonaws.com/staypia-admin-web/${branch}:latest
docker rmi --force $(docker images -q 292885173959.dkr.ecr.ap-northeast-2.amazonaws.com/staypia-admin-web/${branch}:latest)
최종적으로 배포 서비스는 ECS와 EB 중에 ECS를 사용하기로 했다.
AWS나 컨테이너화 개념을 처음 접하는 경우, 또는 Docker
를 막 시작하거나 새로운 애플리케이션을 개발하는 경우 EB
를 통해 컨테이너를 지원하면 좋다.
EB
는 간단한 인터페이스를 제공하고, AWS에 Docker
컨테이너를 배포하는 것을 매우 간단하게 진행할 수 있다.
그러나 현재 수동 배포를 ECS
서비스에 업데이트 하고 있어 이미 레거시 클러스터가 존재하기도 하고, Docker
Container Registry 이력을 유지하는게 좋겠다고 생각했다.
또한, Github Actions
워크플로우도 제로부터 작성해보고 싶었고, 파트별로 브랜치를 나눠 수시로 커밋을 하다보니 PR만 해도 자동으로 배포되는 프로세스가 좀 더 편할 것 같았다.
기능적으로도 EB
와 비교하여 Docker
컨테이너의 아키텍처 및 오케스트레이션에 대해 더 많은 제어를 제공한다.
스케줄링, CPU 및 메모리 활용에 있어 높은 유연성과 사용자 정의를 제공하여, 다른 AWS 서비스와 통합이 필요한 마이크로 서비스를 실행하거나 사용자 지정 또는 관리형 스케줄러를 사용해 EC2 온디맨드, 예약 또는 배치 워크로드를 실행해야 할 때 좋다.
무엇보다 레거시 코드를 컨테이너화 하고 코드를 다시 작성할 필요 없이 AWS로 마이그레이션 하려는 경우에는 ECS
를 선택해야 쉽게 마이그레이션이 가능하다는 글을 보았다 :)
이러한 이유에서 ECS를 선택하게 되었다.
일반적으로 아래의 배포절차를 거쳐 서비스(개발) 웹 애플리케이션을 배포한다.
ECR
푸쉬 (build Docker, 이미지 태깅)ECS
태스크 개정ECS
신규 태스크 배포CloudFront
캐시 무효화서비스 태스크 개정에 필요한 json은 태스크를 생성할 때 받을 수 있다.
해당 JSON을 프로젝트 루트 디렉토리에 json파일로 세팅하고, 깃액션 워크플로우 태스크 개정 단계에서 활용한다.
# 예시 (dev-task-def.json)
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: dev-task-def.json
container-name: MatsSsoDevContainer
image: ${{ steps.build-image.outputs.image }}
구글링과 다른 프로젝트 레거시 코드를 참고하여 .yml 파일을 작성하였고, 깃허브 develop 브랜치에 코드가 병합될 때 개발버전 배포 워크플로우가 동작하도록 설계했다.
to-be
name: Deploy Development
on:
push:
branches: [develop]
# 해당 Workflow의 하나 이상의 Job 목록
jobs:
deploy_dev:
name: Deploy Dev
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- name: Checkout Repository
uses: actions/checkout@v3.0.0
with:
fetch-depth: 0
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.FE_STAYPIA_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.FE_STAYPIA_AWS_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Generate dotenv
run: |
echo "BASE_URL=https://admin.api.staypia.com/prod" >> .env
echo "API_KEY=FprmqLkwCoaBclWAOjqLF9gych2AJ0g818jLhQq4" >> .env
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: staypia-admin-web/refactor
IMAGE_TAG: ${{ github.sha }}
run: |
# Build a docker container and
# push it to ECR so that it can
# be deployed to ECS.
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: staypia-admin-web-task.json
container-name: StaypiaAdminContainer
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: StaypiaAdminRefactor
cluster: StaypiaAdminWebCluster
# 기본값 false, 최종확인시 true
wait-for-service-stability: false
# CF 캐시 무효화 # cf에서 캐시 설정하지 않도록 변경해서 주석처리
# - name: Invalidate CloudFront Cache
# run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} --paths "/*"
- name: build result to slack
uses: 8398a7/action-slack@v3.12.0
with:
job_name: 스테이피아어드민 개발버전 배포
status: ${{job.status}}
fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
author_name: Frontend CI
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.FE_SLACK_WEBHOOK_URL }}
if: always()
배포 성공유무에 대한 피드백은 슬랙채널(fe-github-actions)로 받는다.
Workflow
: 여러 Job으로 구성되고, Event에 의해 트리거될 수 있는 자동화된 프로세스Event
: 특정 브랜치로 Push, Pull Request 하거나 특정 시간대 반복(Cron) 등Job
: Job은 여러 Step으로 구성되고, 다른 Job에 의존 관계를 갖거나 독립적으로 병렬 실행도 가능Step
: Task들의 집합으로, 커맨드를 날리거나 Action을 실행할 수 있음Action
: Workflow의 가장 작은 블럭이고, Job을 만들기 위해 Step들을 연결할 수 있음Runner
: Github Actions Runner 애플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스