똑같은 삽질은 2번 하지 말자

자동배포 vol.1 github의 repo에 push되면 CodeBuild를 이용해 ECR에 자동으로 docker image업로드하기 본문

카테고리 없음

자동배포 vol.1 github의 repo에 push되면 CodeBuild를 이용해 ECR에 자동으로 docker image업로드하기

곽빵 2023. 5. 6. 12:12

개요

자동배포를 구축하기 위한 첫 스텝으로 github의 repo에 push되면 CodeBuild를 이용해 ECR에 자동으로 docker image 업로드가 되게끔 인프라를 구축해보고자 한다.

 

ECR이란?

Amazon Elastic Container Registry (ECR): 이 서비스는 Docker 이미지를 저장하고 관리하기 위한 완전관리형 컨테이너 레지스트리입니다. ECR을 사용하면 컨테이너 이미지를 푸시, 풀 및 관리할 수 있으며, 이를 ECS, EKS 또는 Kubernetes와 같은 컨테이너 서비스와 통합할 수 있다. (docker hub와 비슷한 역할을 하는 친구이다.)

CodeBuild란?

이 서비스는 소스 코드에서 컨테이너 이미지를 빌드하고 테스트하는 데 사용되는 완전관리형 빌드 서비스이다. CodeBuild를 사용하면 도커 이미지를 빌드하고 ECR에 푸시하는 과정을 자동화할 수 있습니다.

 

우선 CodeBuild의 설정부터 해보자. 되도록 미니멈하게 할 예정이라 안쓰는 옵션들이나 잘 모르겠는 옵션들은 일단 default 설정을 그대로 두었다.

빌드 프로젝트 생성 Section

  • 프로젝트 이름만 설정해줬다. 나머지 옵션들에 대해서는 추후 필요할 경우 조사해보고 추가해보자

 

소스 Section

  • 소스공급자는 Github로 선택
  • 본인의 github Access Token을 넣으면 내 Github 계정의 리포지토리에서 내가 원하는 리포 지정이 가능하다.
  • 소스 버전은 develop 브랜치를 지정

Webhook 이벤트 Section

  • 푸시될 때마다 빌드되도록 체크를 한다.
  • 이벤트 유형으로 PUSH 를 선택했다.
  • 단, CodePipeLine에 따로 Webhook이벤트를 등록해서 CodeBuild를 연결시킬때는 이 CodeBuild 자체에서 등록한 Webhook은 삭제해야한다.

환경 Section

  • 관리형 이미지로 선택
  • 운영체제는 Amazon Linux
  • 런타임, 이미지, 이미지 버전, 환경유형등은 대충 선택해줬다.
  • 도커 이미지를 빌드하거나 빌드의 권한을 승격하려면 이 플래그를 활성화 라는 체크박스는 체크를 넣어줘야 한다. docker pull이나 hub로 부터 이미지를 가져오기 위해서는 이 권한이 필요하기 때문이다.
  • 역할이름도 본인 마음대로 작성해도 되지만 추후에 역할에 추가로 권한을 넣어줘야 하므로 이름을 잘 기억해두자

Buildspec Section

 

  • 여긴 default의 값 그대로이다. buildspec파일을 이용하기 때문에 나는 이대로 선택해줬다.

codebuild의 설정은 여기서 끝!

IAM에서 권한설정

IAM -> 역할로 들어온 뒤,

이전 단계의 환경 section에서 설정한 역할에 AmazonEC2ContainerRegistryFullAccess의 권한을 추가한다. 이를 추가하는 이유는 codebuild가 ECR에 접근할 수 있게 하기 위해서이다.

 

ECR에서 리포지토리 생성

이제 ECR 서비스로 이동해 리포지토리를 하나 생성해 준다.

리포지토리가 생성되면 위의 URI도 같이 설정되는데 이를 위해 CodeBuild의 환경변수를 설정해준다.

CodeBuild 환경변수 설정

이 환경변수들은 추후에 buildspec.yml 파일에서 사용되어지는 친구들이다.

  • IMAGE_REPO_NAME: 생성했던 ECR의 repo이름을 설정
  • AWS_DEFAULT_REGION: 현재 ECR을 생성한 지역을 설정 (필자는 도쿄에 거주중이므로 도쿄 region)
  • AWS_ACCOUNT_ID: 이건 ECR 리포지토리의 ${ID}.dkr.ecr의 ID 부분에 있는 숫자를 넣어주면 된다.

이제 buildspec.yml 파일을 작성해보자..!

우선 나의 docker-compose.yml 파일을 보면 총 4개의 이미지를 사용하고 있다.

두개의 이미지는 별도의 Dockerfile을 이용해 생성하고 있고 나머지 2개의 이미지는 docker hub에서 바로 땡겨와 사용중이다.

 

docker-compose.yml

version: "3.7"

services:
  frontend-proxy:
    image: nginx:1.18.0
    ports:
      - "80:80"
    restart: always
    volumes:
      - "./nginx/frontend-proxy.conf:/etc/nginx/nginx.conf"
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      target: prod
    ports:
      - "3000:3000"
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: 
      MYSQL_USER: 
      MYSQL_PASSWORD:
    volumes:
      - .dbdata:/var/lib/mysql
      - ./my.cnf:/etc/mysql/conf.d/my.cnf
    ports:
      - 3306:3306
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
      target: prod
    ports:
      - "5050:5050"
    volumes:
      - ./backend:/app
    depends_on:
      - db
    command:
      - bash
      - -c
      - |
        chmod +x /wait-for-it.sh
        /wait-for-it.sh db:3306 -t 10
        go run main.go

 

buildspec.yml

version: 0.2
phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}
      - aws ecr get-login-password | docker login --username AWS --password-stdin $REPOSITORY_URI
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}

  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker images...
      
      # Frontend
      - cd frontend
      - docker build -t $REPOSITORY_URI:frontend-$IMAGE_TAG .
      - cd ..

      # Backend
      - cd backend
      - docker build -t $REPOSITORY_URI:backend-$IMAGE_TAG .
      - cd ..

      # DB
      - docker pull mysql:5.7
      - docker tag mysql:5.7 $REPOSITORY_URI:db-$IMAGE_TAG

      # Frontend-proxy
      - docker pull nginx:1.18.0
      - docker tag nginx:1.18.0 $REPOSITORY_URI:frontend-proxy-$IMAGE_TAG

  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:frontend-$IMAGE_TAG
      - docker push $REPOSITORY_URI:backend-$IMAGE_TAG
      - docker push $REPOSITORY_URI:db-$IMAGE_TAG
      - docker push $REPOSITORY_URI:frontend-proxy-$IMAGE_TAG
  • 이미지의 태그에 commit 아이디를 붙여 어떤 커밋으로 이미지가 빌드됬는지 알 수 있게 해준다.
  • ECR에 push하는건 어디까지나 이미지! 이다. 그러므로 DB와 Frontend-proxy는 그냥 이미지만 Pull해서 가져온다. 이후에 ECS를 이용한 배포를 할때 docker-compose에서 셋팅한 env나 nginx의 설정변경등등을 적용시킬 예정이다.

이제 develop 브랜치에 push를 하면 이렇게 업로드 된다.

트러블 슈팅 했던 이야기

1. codebuild에서 이하의 에러가 발생했던적이 있는데 워낙 많이 실패를 하다보니 docker hub에서 pull하는 limit에 걸렸었다. 이건 시간이 지나면 자동으로 풀리는듯? 하다.

 

2. docker push하는 곳에서 그냥 error만 나고 딱히 원인은 적혀있지 않은 부분이었는데 이는 docker push를 할 수 있는 적절한 인증정보가 설정이 안되었을때 발생하는 에러일 가능성이 높다고 한다. 그래서 buildspec.yml에 docker login을 하게끔 스크립트를 추가해줬다.

- aws ecr get-login-password | docker login --username AWS --password-stdin $REPOSITORY_URI

 

3. ECR에 대한 이해가 충분하지 못했다. 처음에는 docker-compose로 build한 이미지 그대로 업로드 해야한다는 생각에 사로잡혀서 빌드된 이미지를 태깅해서 업로드하려고 했지만 ChatGPT 선생님의 가르침덕에 ECR에서 업로드한 이미지를 ECS에서 컨테이너를 만들기 때문에 image만 필요하면 된다는 사실을 뒤늦게 깨달았었다. 

Comments