[K8S] 프론트엔드 개발자의 EKS 경험기 2️⃣

2024-03-04T14:04:00.000Z

⬆️ 이미지 AWS ECR에 업로드하기

이전 글에서 내 개발블로그 프로젝트를 도커라이징하는 데 성공하였다. 이번에는 이 도커 이미지를 AWS ECR에 업로드하는 과정을 정리해보겠다.

도커 이미지를 빌드하는 것은 성공하였고, 이를 ECR에 업로드해야 하는 상황인데, 이미지를 빌드할 때마다 개발자가 일일히 수동으로 ECR에 업로드하는 것은 매우 비효율적일 것이다.

이러한 번거로움을 해소하기 위해 Github Actions를 사용할 것이다. Github Actions를 사용하면 코드를 푸시할 때마다 자동으로 도커 이미지를 빌드하고 ECR에 업로드할 수 있다.

1. AWS ECR 레지스트리 생성

AWS ECR은 Public, Private 두 종류의 레지스트리를 제공한다. 나의 경우 Private 레지스트리를 사용하였는데, 월 요금이 1달러 미만으로 나왔기 때문에 실습할 때 굳이 Public을 사용하지 않아도 될 것 같다.

Amazon ECR > 프라이빗 레지스트리 > 리포지토리 항목으로 들어가서 리포지토리 생성 버튼을 클릭한다.

리포지토리 타입이 프라이빗으로 선택되어 있는지 확인하고, 리포지토리 이름을 입력한다.

나의 경우 깃허브 레포지토리 이름과 동일하게 greenhead-blog로 입력하였다.

그 외 나머지 설정은 기본값으로 두고 생성하도록 한다.

그렇다면 정상적으로 greenhead-blog라는 이름의 ECR 프라이빗 레포지토리가 생성된 것을 확인할 수 있다.


2. Github Actions를 위한 AWS IAM 계정 생성

Github Actions가 1️⃣AWS 리소스에 접근할 수 있도록 하고, 2️⃣AWS ECR에 이미지를 업로드할 수 있도록 하기 위해, 2개의 AWS 공식 Actions를 사용할 것이다.

configure-aws-credentials는 다음 3가지 값을 with 필드 내부에 정의하여야 한다.

  • aws-access-key-id
  • aws-secret-access-key
  • aws-region

나의 경우 서울 리전을 사용하고 있으므로 aws-regionap-northeast-2를 입력해주면 된다.

그렇다면 aws-access-key-id, aws-secret-access-key는 어디서 가져올까? 🤔

이 값을 생성하기 위해, Github Actions를 위한 IAM 계정을 생성할 필요가 있다.

🤔 IAM 계정이란?

IAM은 Identity and Access Management의 약자로, 말 그대로 신원과 권한을 관리하는 방식을 의미한다.


AWS는 1️⃣root 계정2️⃣IAM 계정, 2개 유형의 계정을 제공한다.

root 계정은 모든 권한을 소유한 계정으로, 계정 정보를 변경한다든지, 조직 내 IAM 계정의 권한을 수정한다든지 등의 작업은 오직 root 계정만이 가능하다. 하지만 그만큼 권한이 막강하기 때문에, 만에 하나 탈취되는 경우 아주 큰 문제가 된다.


이를 해결하기 위해 IAM 계정을 사용한다. IAM 계정은 오직 부여받은 권한에 해당하는 작업만 수행할 수 있기 때문에, 조직 내 특정 IAM 계정이 탈취당하더라도 root 계정이 탈취당하는 경우에 비해서는 피해 규모가 훨씬 작다.


같은 이유로, 이번 포스트에서도 Github Actions가 정상적으로 동작하기 위해 필요한 최소한의 권한만을 갖는 IAM 계정을 생성하여 사용한다면 보안 측면에서 더 안전하다.

AWS 콘솔에 접속하여 IAM 서비스로 이동 후, 사용자 생성 버튼을 클릭한다.

IAM 계정 사용자 이름을 입력하고, AWS Management Console에 대한 사용자 액세스 권한 제공 옵션은 체크하지 않는다.

AWS Management Console, 즉 GUI에 대한 접근 권한은 단순 스크립트 실행만 수행하는 Github Actions에게는 필요 없는 권한이기 때문이다.

이후 해당 IAM 계정에게 부여할 권한을 설정해야 한다. 이를 위해 아까 위에서 언급하였던 AWS Actions 중 amazon-ecr-loginREADME.md를 참고해보자.

문서를 보면 GetAuthorizationToken, AllowPushPull 2개로 그룹으로 나누긴 했지만 전부 ECR에 관련된 권한이며, 이를 생성할 IAM 계정에게 부여하면 될 것이다.

하지만 AWS에서 기본적으로 제공하는 권한정책 중에서는 위에서 찾아본 권한만 알맞게 포함하고 있는 정책이 없기 때문에, 직접 정책을 생성해야 한다.

amazon-ecr-login 공식문서에서 예시로 든 정책의 포맷이 JSON 형식이었기 때문에, 여기서도 편집기 타입을 JSON에 맞춰주자.

거의 그대로 복사해 붙여넣되, Resource 필드에는 내가 생성했던 ECR 리포지토리의 정보에 맞게 수정해주면 된다.

이후 정책 이름을 입력하고, 정책을 생성하면 된다. 나는 AWSServiceRoleForAmazonECRPrivate라는 이름으로 생성하였다.

생성한 정책을 IAM 계정에 연결하면 최종적으로 IAM 계정이 생성된다. 이제, 액세스 키 만들기 버튼을 클릭하여 configure-aws-credentials에 필요한 aws-access-key-id, aws-secret-access-key를 생성하면 된다.

AWS 외부에서 실행되는 애플리케이션 항목을 체크하고 다음으로 넘어가자.

설명 태그를 작성하고 액세스 키 만들기 버튼을 누르면 생성된다. 나의 경우, IAM 계정 사용자 이름과 동일하게 greenhead-blog를 설명 태그로 입력하였다.

그려면 1️⃣액세스 키aws-access-key-id2️⃣비밀 액세스 키aws-secret-access-key가 생성된 것을 확인할 수 있다.

⚠️ 해당 페이지를 벗어나면 더 이상 비밀 액세스 키를 확인할 수 없으니, 복사해서 어딘가 안전한 곳에 저장해 두도록 하자.


3. AWS IAM 계정 액세스 키 -> Github Secrets 저장

이제 Github Actions 스크립트를 작성하…기 전에, aws-access-key-id, aws-secret-access-key를 Github에 환경변수로 저장해둘 필요가 있다.

이 레포지토리는 프라이빗이 아닌 퍼블릭이고, 사실 굳이 퍼블릭이 아니어도 이러한 민감한 정보를 직접 코드에 작성하는 것은 대단히 좋지 못하다.

Github는 이러한 민감한 정보를 Actions에서 안전하게 사용할 수 있도록 Secrets라는 기능을 제공한다. 이를 사용하여 환경변수를 저장하고, 이를 Github Actions 스크립트에서 사용할 수 있다.

레포지토리에서 Settings > Security > Secrets and Variables > Actions 항목으로 들어간 후, New repository secret 버튼을 클릭한다.

📝 Repository Secrets와 Environment Secrets의 차이점

Repository Secrets특정 리포지토리에 한해서 사용할 수 있는 환경변수를 정의할 때 사용하며, 이번 포스트에서도 이를 사용할 것이다.


Environment Secrets환경 별로(ex: develop, staging, production) 서로 다른 비밀정보를 정의할 때 사용한다. 이를 통해 동일한 리포지토리 내에서도 환경별로 다른 비밀 정보를 사용할 수 있게 되므로 Repository Secrets보다 더 유연하게 사용할 수 있다.

이전에 생생한 액세스 키, 시크릿 키를 가져와서 각각 다음의 이름으로 Secret을 생성한다.

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

작성 후 위 사진과 같이 2개의 Secret가 생성된 것을 확인하였다면, 이제 정말로 Github Actions 스크립트를 작성할 차례이다.


4. Github Actions 워크플로우 작성

사진과 같이 블로그 프로젝트 루트 경로로부터 .github/workflows/main.yaml 파일을 생성한다.

이 파일은 Github Actions 워크플로우를 정의하는 파일로, 이 파일 내부에 작성한 코드가 Github Actions를 통해 실행될 것이다.

전체적인 스크립트는 Github에서 기본 제공하는 workflow 템플릿 중에서 Deploy to Amazon ECS를 참고하여 작성하였다.

우선, 전체 코드를 먼저 보자.

name: Build image and push to Amazon ECR

on:
  push:
    branches: [ "master" ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - 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: ap-northeast-2

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to Amazon ECR
        env:
          REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          REPOSITORY: greenhead-blog
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
          docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG

전역 필드는 3가지 필드로 구성되어 있다.

  • name : 워크플로우의 이름. 기능에 부합하는 적절한 이름을 사용한다.
  • on : 워크플로우가 실행될 조건.
    • 나의 경우 (git) push 이벤트가 발생하고, 브랜치가 master일 때 실행되도록 하였다.
  • jobs : 워크플로우가 수행할 job.
    • 나의 경우 build-and-push라는 이름의 job을 정의하였다.


build-and-push는 다시 runs-on, steps 2개의 필드로 구성되어 있다.

  • runs-on : job을 실행시킬 때 OS 환경을 정의한다.
    • 나의 경우 ubuntu-latest를 사용하였는데, 이는 Github Actions가 제공하는 ubuntu 환경 중 가장 최신 버전을 사용하겠다는 의미이다.
  • steps : 해당 job이 수행할 단계를 정의한다.

steps 필드에 작성된 단계가 가장 중요한데, 이 단계들이 순차적으로 실행되면서 워크플로우가 수행된다.

하나씩 알아보도록 하자.


1️⃣ step - Checkout

- name: Checkout
  uses: actions/checkout@v3

2줄의 코드로 이루어져 있지만 매우 중요한 단계이다. 이 단계는 Github Actions가 실행되는 환경에 현재 레포지토리의 코드를 가져오는 역할을 한다.

만약 이 단계가 없다면, Github Actions가 실행되는 환경에는 아무런 코드도 없기 때문에, 이후의 단계들은 모두 실패하게 된다.

코드가 없다면 아무것도 못하므로 이 단계는 필수적으로 정의되어야 하며, 또한 steps 필드 내에서 가장 먼저 정의되어야 한다.


2️⃣ step - Configure AWS Credentials

- 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: ap-northeast-2

AWS Actions인 configure-aws-credentials를 사용하여, Github Actions가 AWS 리소스에 접근할 수 있도록 한다.

with 문에서는 3개의 필드가 정의되어 있는데, 각각의 역할은 다음과 같다.

  • aws-access-key-id : AWS IAM 계정의 액세스 키
  • aws-secret-access-key: AWS IAM 계정의 비밀 액세스 키
  • aws-region : 사용할 AWS 리소스가 위치한 리전을 정의한다.

Github Secrets에 저장한 환경변수를 워크플로우에서 사용하기 위해서는 ${{ secrets.변수명 }} 형식으로 사용하면 된다.


3️⃣ step - Login to Amazon ECR

- name: Login to Amazon ECR
  id: login-ecr
  uses: aws-actions/amazon-ecr-login@v2

amazon-ecr-login을 사용하여, Github Actions가 ECR에 이미지를 업로드할 수 있도록 로그인한다.

id 필드는 해당 단계를 참조할 때 사용할 수 있는 식별자로, 이후의 단계에서 이 단계의 출력값을 사용할 때 사용된다.


4️⃣ step - Build, tag, and push image to Amazon ECR

- name: Build, tag, and push image to Amazon ECR
  env:
    REGISTRY: ${{ steps.login-ecr.outputs.registry }}
    REPOSITORY: greenhead-blog
    IMAGE_TAG: ${{ github.sha }}
  run: |
    docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
    docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG

마지막 단계는 실제로 도커 이미지를 빌드하고 ECR에 업로드하는 단계이다.

env 필드에는 환경변수를 정의하는데, 이 환경변수는 run 필드에서 사용된다.

  • REGISTRY : ECR 리포지토리의 주소.
    • login-ecr 단계의 반환값outputsregistry 필드를 사용한다.
  • REPOSITORY : ECR 리포지토리의 이름.
  • IMAGE_TAG : 이미지 태그.
    • Github Actions가 실행되는 환경에는 github.sha라는 환경변수가 존재하는데, 이는 Actions를 트리거한 커밋의 SHA 해시값이다.
    • 이를 사용하여 유니크한 이미지 태그를 정의하였다.

run 필드에는 실제로 실행할 명령어를 작성하는데, 여기서는 다음 2가지 명령어를 실행한다.

  • docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG . : 도커 이미지를 빌드한다.
  • docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG : 빌드한 도커 이미지를 ECR에 업로드한다.

✅ AWS ECR 업로드!

작성한 워크플로우가 정상적으로 동작하는지 확인하기 위해 master 브랜치에 코드를 푸시해보자.

✅ 아이콘이 표시되었음은 워크플로우가 성공적으로 실행되었음을 의미한다.

AWS ECR greenhead-blog 레포지토리에 실제로 이미지가 업로드 된 것을 확인할 수 있다!


🤔 다음으로 할 것은?

지금까지 도커 이미지를 빌드하고, 이를 AWS ECR에 업로드하는 과정을 정리해보았는데, 사실 이는 다음에 다룰 쿠버네티스에 비하면 정말 간단한 내용들이다..

다음에는 이렇게 업로드한 이미지를 쿠버네티스 클러스터에 가져와 사용할 수 있도록 하는 과정을 자세히! 정리해 보겠다. 🥹


Greenhead
Written by@Greenhead
프론트엔드 개발자 윤녹두입니다 :)

GitHub