티스토리 뷰

개요

  • AWS CodePipeline은 아마존의 서버리스 매니지드 CD(지속적 배포) 상품이다. 이를 이용하여 인스턴스를 필요로 했던 기존의 Jenkins와 같은 CD 도구를 대체하여 서비 관리 유지 부담을 줄이고, 개발자는 온전히 비지니스 로직 구현에 집중할 수 있다.
  • 이번 글에서는 AWS CodePipeline 상품을 이용하여 Amazon ECS에서 운영 중인 서비스에 대해 빌드 및 배포 파이프라인을 만드는 과정을 설명하고자 한다.

배포 시나리오

  • 배포 대상 프로젝트는 Gradle 기반으로 제작된 Spring Boot 프로젝트이다.
  • 프로젝트 루트에 빌드시 실행될 명령어 목록을 저장하는 buildspec.yml 파일이 존재한다.
  • 프로젝트 저장소는 GitHub Enterprise Cloud에 위치하며, 배포 대상 브랜치는 master로 설정한다.
  • Amazon ECRDocker 이미지를 보관하는 리파지터리가 생성되어 있다. [관련 링크]
  • Amazon ECS에 해당 이미지를 기반으로 서비스가 생성되어 있다. [관련 링크]
  • AWS CodePipelineGitHub Enterprise Cloud에서 소스 코드를 내려 받아 Docker 이미지로 빌드하여 Amazon ECR에 푸시 후, Amazon ECS에 배포한다.

buildspec.yml 작성

  • 파이프라인 생성에 앞서 가장 먼저 빌드 대상이 되는 프로젝트 루트에 buildspec.yml 파일을 작성해야 한다. (반드시 루트 경로와 파일명을 일치시킬 필요는 없다. 아래 빌드 프로젝트 생성 단계에서 커스터마이징이 가능하기 때문이다.)
  • 파일이 존재하지 않거나 형식이 맞지 않을 경우 빌드시 DOWNLOAD_SOURCE 단계에서 YAML_FILE_ERROR가 발생한다.
version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto11
  pre_build:
    commands:
      - REGION=ap-northeast-2
      - REPOSITORY_URI=xxxxx.dkr.ecr.ap-northeast-2.amazonaws.com
      - IMAGE_NAME=foobar
      - IMAGE_TAG=latest
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - BUILD_TAG=${COMMIT_HASH:=latest}
      - CONTAINER_NAME=foobar
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region $REGION | docker login -u AWS --password-stdin $REPOSITORY_URI
  build:
    commands:
      - echo Building the Docker image...
      - chmod +x gradlew
      - ./gradlew bootBuildImage --imageName=$IMAGE_NAME
      - docker tag $REPOSITORY_URI/$IMAGE_NAME:$IMAGE_TAG $REPOSITORY_URI/$IMAGE_NAME:$BUILD_TAG
  post_build:
    commands:
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI/$IMAGE_NAME:$IMAGE_TAG
      - docker push $REPOSITORY_URI/$IMAGE_NAME:$BUILD_TAG
      - printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $REPOSITORY_URI/$IMAGE_NAME:$BUILD_TAG > imagedefinitions.json
      - cat imagedefinitions.json

cache:
  paths:
    - '/root/.gradle/caches/**/*'
    - '/root/.m2/**/*'
    - '/root/.cache/pip/**/*'
    - '/root/.npm/**/*'

artifacts:
  files:
    - imagedefinitions.json
  • buildspec.yml 파일은 런타임에 소스 코드를 내려 받은 후 빌드하는 시점에 실행될 명령어 목록을 정의한 파일이다. pre_build, build, post_build 3단계에 걸쳐 실행될 명령어 목록을 작성해야 한다.
  • cache는 빌드 후 어떤 대상 폴더 및 파일을 캐시에 저장할지를 식별하는 역할을 한다. 특히, Gradle, Maven 빌드시 미리 관련 라이브러리가 캐시되어 있으면 재빌드시 속도가 10분 이상 단축되기 때문에 cache 지정은 필수라고 할 수 있다. 식별된 캐시를 어디에 얼마동안 저장할지를 결정하는 부분은 아래에서 설명한다.
  • artifacts는 빌드의 결과로 생성된 파일 목록을 의미한다. imagedefinitions.json에는 빌드 완료 후 배포 대상이 되는 컨테이너의 정보를 보관하는데, 이 정보를 기반으로 Amazon ECS 서비스에 소속된 작업 정의를 개정하고, 서비스를 업데이트하는 과정을 자동으로 진행한다.

파이프라인 생성

  • 이제 파이프라인을 생성할 차례이다. 아래는 GitHub Enterprise Cloud에 프로젝트가 존재한다는 가정 하에 소스 코드 빌드 후 Amazon ECS의 서비스를 배포하는 예이다.
AWS CodePipeline 콘솔 접속
→ [파이프라인 생성] 클릭

# 파이프라인 설정
→ 파이프라인 이름: foobar-prod (입력)
→ 서비스 역할: [새 서비스 역할] 선택
→ 역할 이름: AWSCodePipelineServiceRole-ap-northeast-2-foobar (입력)
→ [다음] 클릭

# 소스
→ 소스 공급자: [GitHub(버전 2)] 선택
→ 연결: [GitHub에 연결] 클릭
→ 연결 이름: foobar (입력)
→ [GitHub에 연결] 클릭
→ [새 앱 설치] 클릭
→ [연결] 클릭
→ 리포지토리 이름: some-company/foobar (선택)
→ 브랜치 이름: main (선택)
→ 출력 아티팩트 형식: [Codepipeline 기본값] 선택
→ [다음] 클릭

# 빌드
→ 빌드 공급자: [AWS CodeBuild]
→ 리전: [아시아 태평양 (서울)]
→ [프로젝트 생성] 클릭

# 빌드 > 프로젝트 생성 > 프로젝트 구성
→ 프로젝트 이름: foobar (입력)

# 빌드 > 프로젝트 생성 > 환경
→ 환경 이미지: [관리형 이미지] 선택
→ 운영 체제: [Amazon Linux 2] 선택
→ 런타임: [Standard] 선택
→ 이미지: [aws/codebuild/amazonlinux2-x86_64-standard:3.0] 선택
→ 이미지 버전: [이 런타임에 항상 최신 이미지 사용] 선택
→ 환경 유형: [Linux] 선택
→ [도커 이미지를 빌드하거나 빌드의 권한을 승력하려면 이 플래그를 활성화합니다.] 선택
→ 서비스 역할: [새 서비스 역할] 선택
→ 역할 이름: codebuild-foobar-service-role (입력)

# 빌드 > 프로젝트 생성 > Buildspec
→ 빌드 사양: [buildspec 파일 사용] 사용
→ buildspec 이름: buildspec (입력)

# 빌드 > 프로젝트 생성
→ [Code Pipeline으로 계속] 클릭

# 빌드
→ [다음] 클릭

# 배포
→ 배포 공급자: [Amazon ECS] 선택
→ 리전: [아시아 태평양 (서울) 선택)
→ 클러스터 이름: (생성한 클러스터 이름 선택)
→ 서비스 이름: (생성한 서비스 이름 선택)
→ 이미지 정의 파일: imagedefinitions.json (입력)
→ [다음] 클릭

# 검토
→ [파이프라인 생성] 클릭
  • GitHub 연결시 앱을 설치할 계정은 빌드할 프로젝트의 계정명이 되어야 한다.
  • Buildspec 이름에는 프로젝트 루트를 기준으로 미리 존재하는 파일명을 입력한다. 미입력시 프로젝트 루트에 buildspec.yml 파일이 존재하는 것으로 간주한다.

AWS CodeBuild 빌드 캐싱 활성화

  • 빌드 캐싱 없이 매번 CodePipeline으로 빌드할 경우, 빌드에만 10분이 넘는 시간이 소요된다. 매번 빌드 시점마다 수백 메가바이트의 같은 라이브러리를 다시 다운로로드하는 것보다는, 한번 캐시된 것을 재사용하여 빌드 속도를 비약적으로 향상시킬 수 있다. (내 경우, 운영 환경에서 빌드 캐싱 적용 전에는 빌드에 12분이 소요되던 것이 빌드 캐싱 적용 후에는 3분으로 줄어들었다. 즉, 4배의 시간 단축 효과를 보았다.)
  • AWS CodeBuild는 이러한 상황에 대비하여 로컬 캐싱과 S3 2가지 방법의 빌드 캐싱을 제공한다. 로컬 캐싱은 제일 빠르지만 캐시 유효 시간이 수분 정도로 굉장히 짧기 때문에, 범용적으로 쓰기에는 적합하지 않다. 아래는 S3를 이용하여 빌드 캐싱을 활성화하는 방법이다. 먼저 빌드 캐싱을 전담할 S3 버킷을 생성한다.
Amazon S3 콘솔 접속
→ [버킷 만들기] 클릭
→ 버킷 이름: foobar-build-cache
→ AWS 리전: [아시아 태평양 (서울) us-east-1] 선택
→ [버킷 만들기] 클릭
  • 다음으로 AWS Codebuild에 빌드 캐싱을 활성화하면 캐시를 생성하고 사용할 준비가 완료된다.
AWS CodeBuild 콘솔 접속
→ [빌드] → [프로젝트 빌드]
→ [foobar-build-cache] 선택 → [편집] → [아티팩트]

# 아티팩트 편집
→ [추가 구성] 클릭
→ 캐시 유형: [Amazon S3] 선택
→ 캐시 버킷: [foobar-build-cache] 선택
→ 캐시 경로 접두사: foobar-prod (입력)
→ 캐시 수명 주기(일): 7 (입력)
→ [아티팩트 업데이트] 클릭

GitHub 풀링 권한 부여

  • 앞서의 작업 후에 빌드 역할에 대해 GitHub 풀링 권한을 추가해야 한다. (권한 미부여시 authorization failed for primary source and source version 오류로 빌드가 실패한다.) [관련 링크]
AWS CodePipeline 콘솔 접속
→ [파이프라인] 클릭 → [파이프라인] 클릭
→ [foobar-prod] 클릭
→ Source 스테이지의 [i] 클릭 → [ConnectionArn] 값 복사 (ex: arn:aws:codestar-connections:us-east-2:115255558787:connection/687cad53-33d7-48bd-b558-ceed6ee1ce39)

→ [빌드] 클릭 → [프로젝트 빌드] 클릭
→ foobar-prod 클릭

# 빌드 프로젝트
→ [빌드 세부 정보] 클릭
→ [서비스 역할] → codebuild-foobar-prod-service-role 클릭

# IAM 콘솔 > 역할 > 요약
→ [정책 연결] 클릭

# 권한 연결
→ [정책 생성] 클릭
→ [JSON] 클릭 후 아래 내용 입력
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "codestar-connections:UseConnection",
            "Resource": "{앞서 복사한 [ConnectionArn] 값 입력}"
        }
    ]
}
→ [정책 검토] 클릭

# 정책 생성
→ 이름: [GitHubPullAccess] 입력
→ [정책 생성] 클릭
  • 앞서 생성된 GitHubPullAccess 권한은 여러 프로젝트가 같이 사용할 수 있다. 아래와 같이 생성한 권한을 빌드 포르젝트에 연결하면 권한 부여가 완료된다.
AWS CodePipeline 콘솔 접속
→ [빌드] 클릭 → [프로젝트 빌드] 클릭
→ foobar-prod 클릭

# 빌드 프로젝트
→ [빌드 세부 정보] 클릭
→ [서비스 역할] → codebuild-foobar-prod-service-role 클릭

# IAM 콘솔 > 역할 > 권한
→ [정책 연결] 클릭

# 권한 연결
→ [GitHubPullAccess] 체크
→ [정책 연결] 클릭

Amazon ECR 다운로드 권한 부여

  • Amzon ECR에서 컨테이너 이미지를 다운로드 받아야 하므로 아래와 같이 빌드 역할에 다운로드 권한을 부여한다.
AWS CodePipeline 콘솔 접속
→ [빌드] 클릭 → [프로젝트 빌드] 클릭
→ foobar-prod 클릭

# 빌드 프로젝트
→ [빌드 세부 정보] 클릭
→ [서비스 역할] → codebuild-foobar-prod-service-role 클릭

# IAM 콘솔 > 역할 > 권한
→ [정책 연결] 클릭

# 권한 연결
→ [AmazonEC2ContainerRegistryFullAccess] 체크
→ [정책 연결] 클릭

트러블슈팅: Submodules를 가진 프로젝트 빌드 오류 해결

  • AWS CodePipeline은 현재 Submodules를 가진 프로젝트의 빌드를 자체적으로 지원하고 있지 않다. 서브모듈이 빠진 채로 빌드되기 때문에 빌드 단계에서 오류가 발생한다.
  • 해결책은 서브모듈 부분에 대한 풀링을 직접 buildspec.yml에 명시하는 것이다. 꽤나 번거롭지만 확실한 해결책이다.
version: 0.2

env:
  parameter-store:
    GITHUB_SSH_KEY: GITHUB_SSH_KEY

phases:
  install:
    runtime-versions:
      java: corretto11
    commands:
      - mkdir -p ~/.ssh
      - echo "$GITHUB_SSH_KEY" > ~/.ssh/id_rsa
      - chmod 600 ~/.ssh/id_rsa
      - eval "$(ssh-agent -s)"
      - ssh-add ~/.ssh/id_rsa
      - git config --global url."git@github.com:".insteadOf "https://github.com/"
      - echo Pooling Git Submodules...
      - git submodule init
      - git submodule update --recursive
  pre_build:
    commands:
      - REGION=ap-northeast-2
      - REPOSITORY_URI=xxxxx.dkr.ecr.ap-northeast-2.amazonaws.com
      - IMAGE_NAME=foobar
      - IMAGE_TAG=latest
      - DEPLOY_TAG=prod
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - BUILD_TAG=${COMMIT_HASH:=prod}
      - CONTAINER_NAME=foobar-prod
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region $REGION | docker login -u AWS --password-stdin $REPOSITORY_URI
  build:
    commands:
      - echo Building the Docker image...
      - chmod +x ./gradlew
      - ./gradlew api:bootBuildImage --imageName=$IMAGE_NAME
      - docker tag $IMAGE_NAME:$IMAGE_TAG $REPOSITORY_URI/$IMAGE_NAME:$DEPLOY_TAG
  post_build:
    commands:
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI/$IMAGE_NAME:$DEPLOY_TAG
      - printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $REPOSITORY_URI/$IMAGE_NAME:$DEPLOY_TAG > imagedefinitions.json
      - cat imagedefinitions.json

artifacts:
  files:
    - imagedefinitions.json
  • 필요한 작업이 더 있다. 서브모듈 풀링을 SSH Key를 이용하기 때문에 해당 파이프라인에 종속되어 생성된 코드 빌드 역할에 AWS Parameter Store 접근 권한을 추가해야 한다. (권한 미부여시 AccessDeniedException 오류로 빌드가 실패한다.)
AWS CodePipeline 콘솔 접속
→ [빌드] 클릭 → [프로젝트 빌드] 클릭
→ foobar-prod 클릭

# 빌드 프로젝트
→ [빌드 세부 정보] 클릭
→ [서비스 역할] → codebuild-foobar-prod-service-role 클릭

# IAM 콘솔 > 역할 > 권한
→ [정책 연결] 클릭

# 권한 연결
→ [AmazonSSMReadOnlyAccess] 체크
→ [정책 연결] 클릭

참고 글

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함