티스토리 뷰

개요

  • 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

artifacts:
  files:
    - imagedefinitions.json
  • buildspec.yml 파일은 런타임에 소스 코드를 내려 받은 후 빌드하는 시점에 실행될 명령어 목록을 정의한 파일이다. pre_build, build, post_build 3단계에 걸쳐 실행될 명령어 목록을 작성해야 한다.
  • artifacts는 빌드의 결과로 생성된 파일 목록을 의미한다. imagedefinitions.json에는 빌드 완료 후 배포 대상이 되는 컨테이너의 정보를 보관하는데, 이 정보를 기반으로 Amazon ECS 서비스에 소속된 작업 정의를 개정하고, 서비스를 업데이트하는 과정을 자동으로 진행한다.

파이프라인 생성

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

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

# 소스
→ 소스 공급자: [GitHub(버전 2)] 선택
→ 연결: [GitHub에 연결] 클릭
→ 연결 이름: foobar (입력)
→ [GitHub에 연결] 클릭
→ [새 앱 설치] 클릭
→ [연결] 클릭
→ 리포지토리 이름: some-company/foobar (선택)
→ 브랜치 이름: master (선택)
→ 출력 아티팩트 형식: [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 파일이 존재하는 것으로 간주한다.

빌드 역할에 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] 입력
→ [정책 생성] 클릭

트러블슈팅: 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] 체크
→ [정책 연결] 클릭

참고 글

댓글
댓글쓰기 폼