배포 자동화: Go + Docker + ECS + Fargate + ECR + Github Action
1. Intro
인프라에 대해 공부해보다가 Go + Docker + ECS + Fargate + ECR + Github Action으로 CI/CD를 구성했다는 블로그 글을 보고, 따라해 보았다.
2. Dockerfile 만들기
Dockerfile은 Docker 이미지를 생성하기 위한 설정 파일입니다. Go + gin backend를 위한 Dockerfile을 만들어 보겠습니다.
FROM golang:alpine AS builder
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
WORKDIR /build
COPY go.mod go.sum main.go ./
RUN go mod download
RUN go build -o main .
WORKDIR /dist
RUN cp /build/main .
FROM scratch
COPY --from=builder /dist/main .
ENTRYPOINT ["/main"]
3. ECS 만들기 (Fargate vs EC2)
Amazon ECS(Elastic Container Service)는 컨테이너화된 애플리케이션을 쉽게 실행하고 관리할 수 있는 완전 관리형 컨테이너 오케스트레이션 서비스입니다. ECS를 사용할 때 두 가지 주요 실행 모드가 있습니다 (Fargate, EC2). 생성할 때 두 개 중 하나를 선택할 수 있습니다. 저는 Fargate를 선택했습니다.
Fargate
서버리스 옵션으로, 인프라 관리 없이 컨테이너를 실행할 수 있습니다. 확장성이 뛰어나고 관리가 쉽습니다.
EC2
사용자가 직접 EC2 인스턴스를 관리하며, 더 많은 제어와 커스터마이징이 가능합니다.
예전에는 EC2 + Code Deploy 조합으로 배포 인프라 구성을 하곤 했는데, 이번에는 Fargate를 시도해보고자 합니다.
Fargate는 인프라 관리 부담을 줄이고 싶은 경우에 적합하며, EC2는 더 세밀한 제어가 필요한 경우에 선택한다고 합니다.
클러스터 (Cluster) 개념
ECS의 최상위 리소스 그룹입니다.
여러 개의 EC2 인스턴스 또는 Fargate 작업을 논리적으로 그룹화합니다. (저는 Fargate를 사용했습니다.)
한 클러스터 내에서 여러 서비스와 태스크를 실행할 수 있습니다.
태스크 (Task) 개념
애플리케이션의 최소 배포 단위입니다.
하나 이상의 컨테이너로 구성됩니다.
태스크 정의(Task Definition)를 통해 실행할 컨테이너, 사용할 리소스 등을 정의합니다.
서비스 (Service)
지정된 수의 태스크를 클러스터에서 동시에 실행하고 관리합니다.
태스크가 중지되면 자동으로 새 태스크를 시작하여 지정된 개수를 유지합니다. (저는 1개를 지정해서 최대 1개만 실행됩니다.)
로드 밸런서와 연결하여 트래픽을 분산할 수 있다고 합니다. (요금 주의)
컨테이너 (Container)
Docker 이미지를 기반으로 실행되는 격리된 실행 환경입니다. (ECR에 push하고 태그로 관리됩니다)
go + gin 으로 구성된 프로젝트를 Docker로 빌드했습니다.
태스크 내에서 하나 이상의 컨테이너가 함께 실행될 수 있습니다.
4. 인바운드 규칙 편집
8080 포트 열기 (인바운드 규칙 편집)
ECS 태스크가 실행되는 Fargate 태스크에 연결된 보안 그룹의 인바운드 규칙을 편집해야 합니다.
ECS -> 클러스터 -> 서비스 -> 보안 그룹으로 이동합니다.
해당 보안 그룹을 선택하고 "인바운드 규칙 편집"을 클릭합니다.
유형: 사용자 지정 TCP
포트 범위: 8080
소스: 필요에 따라 특정 IP 범위 또는 모든 트래픽(0.0.0.0/0)을 선택
저는 8080포트를 열었으므로, 8080포트를 외부에서 접근 가능하도록 개방했습니다.
이렇게 설정하면 외부에서 8080 포트를 통해 ECS에서 실행 중인 go backend app에 접근할 수 있게 됩니다. 단, 보안을 위해 가능한 한 접근을 제한적으로 설정하는 것이 좋습니다.
또한, 태스크 정의에서도 컨테이너 포트 매핑을 설정해야 합니다. 태스크 정의의 컨테이너 설정에서 8080 포트를 호스트 포트와 매핑해야 합니다.
4. ECR 만들기 (Docker 이미지 저장소)
Amazon ECR(Elastic Container Registry)은 Docker 컨테이너 이미지를 안전하게 저장, 관리 및 배포할 수 있는 완전 관리형 Docker 컨테이너 레지스트리입니다. ECR을 사용하면 프라이빗 레포지토리를 생성하여 Docker 이미지를 저장하고, ECS와 쉽게 통합할 수 있습니다.
5. Github Action (develop branch에 머지 시 워크플로우 실행)
develop 브랜치에 머지가 되었을 때 (이벤트 발생), Docker 이미지가 빌드되고 ECR에 push(업로드)된 다음에, 해당 도커 이미지를 ECS에서 실행되게 할 것입니다. (8080포트로 접근 가능하게)
GitHub Actions를 사용하면 이러한 CI/CD 파이프라인을 구축할 수 있습니다. develop 브랜치에 머지될 때 자동으로 빌드, 테스트, 배포 과정을 실행하도록 설정할 수 있습니다.
aws.yml 코드
name: Deploy to Amazon ECS
on:
push:
branches: [ "develop" ]
env:
AWS_REGION: ap-northeast-2 # set this to your preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: fye-backend-repository # set this to your Amazon ECR repository name
ECS_SERVICE: fye-backend-service # set this to your Amazon ECS service name
ECS_CLUSTER: fye-cluster # set this to your Amazon ECS cluster name
ECS_TASK_DEFINITION: ./fye-task.json # set this to the path to your Amazon ECS task definition
# file, e.g. .aws/task-definition.json
CONTAINER_NAME: fye-backend-container # set this to the name of the container in the
# containerDefinitions section of your task definition
permissions:
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
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 "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- 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: ${{ env.ECS_TASK_DEFINITION }}
container-name: ${{ env.CONTAINER_NAME }}
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: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
상단에 있는 env: 부분에서 지역과 ecs, ecr 관련 정보들을 넣습니다. json은 태스크 탭에서 다운로드할 수 있습니다. 그리고 iam을 생성해서 github secrets에 access key와 secret key를 등록하여 github action이 aws의 다양한 서비스를 조작할 수 있게 설정합니다. 저는 iam에 아래의 6개 권한을 추가했습니다. 그리고 .csv로 추출하여 github의 secret에 키를 등록했습니다.
6. 마무리 및 소감
No Docker + Code Deploy로 했을 때는 1~2분이면 서버 배포가 끝났는데, Docker + ECS로 해보니 생각보다 오래 걸려서 당황했다..
Docker Build 하는데만 48s, Deploy ECS Task Definition 하는 데만 4m 16s가 걸렸다. 총 5분..
다수가 협업할 때는 장점이 충분히 있을 것 같지만, 1인이나 소규모로 할 때는 No Docker + Code Deploy를 사용하는 게 속도면에서 좋을 것 같다.
'개발 > CI & CD' 카테고리의 다른 글
ssh config, shell script로 배포 시간 1/10으로 단축하기 (0) | 2024.07.18 |
---|---|
Docker 내에서의 'exec format error' 오류 해결하기 (0) | 2023.10.01 |