AWS ECR을 사용하게된 계기
작은 프로젝트에서 CI/CD 파이프라인으로 github action을 사용하여 docker hub에 docker image를 저장했었습니다.
그러다가 간헐적으로 이미지를 받지 못하는 일이 발생했습니다.
docker hub 무료 계정의 월 200회 pull 제한에 걸려서 그런 것 같습니다.
프로젝트는 최대 하루에 10번 배포했던 적도 있어서 docker hub를 사용하기에 부적합했습니다.
그리고 docker hub는 private 레포지토리에는 1개의 이미지만 저장할 수 있었고, 이미 다른 이미지를 private 레포지토리에 저장했던 상태여서 public 레포지토리를 사용했습니다. 그래서 보안상 문제가 있었습니다.
장점 및 단점
docker hub의 대안을 찾아보던 중, AWS에서 완전 관리형 컨테이너 레지스트리인 ECR (Elastic Container Registry)을 사용해서 CI/CD를 구축하는 방법을 알게되었습니다.
일단 장점은
1. 컨테이너 이미지 저장소를 직접 관리할 수 있다.
2. 이용 횟수에 제한이 없다.
3. 안정적으로 push/pull할 수 있다.
단점은
1. 적용 방법이 조금 복잡하다.
2. 비용이 든다.
AWS로 배포해본 경험이 있는 분이라면 충분히 할만 합니다.
비용
데이터 전송 비용은 2024년 8월 기준 다음과 같습니다.
- private 리포지토리에 모든 수신 비용은 무료입니다.
- 스토리지는 모든 종류의 리포지토리에 저장된 데이터의 경우에 GB/월당 0.10 USD입니다.
- 동일한 리전의 Amazon ECR 및 기타 서비스 사이에서 전송된 데이터(예: Amazon EC2, AWS Lambda, AWS App Runner 또는 AWS Fargate)는 무료입니다.
- 프리티어 계정은 1년간 private 리포지토리 저장공간 500MB를 무료로 이용할 수 있습니다.
- 기본적으로 public 리포지토리는 월 50GB의 무료 스토리지를 이용할 수 있습니다.
- AWS 계정이 아니면 매월 public 리포지토리에서 500GB의 데이터를 인터넷으로 무료로 전송할 수 있고, AWS 계정을 이용하면 매월 5TB의 데이터를 public 리포지토리에서 인터넷으로 무료로 전송할 수 있습니다.
- public 리포지토리에서 AWS 컴퓨팅리소스 간에는 무제한 대역폭을 무료로 사용할 수 있습니다.
private 리포지토리에서
모든 이미지들의 총 용량이 400MB이고,
30일 동안 이 모든 이미지를 매일 10번 push, 10번 pull한다면,
400 * 30 * 10(push) -> 120GB -> 12.00 USD가 대략적인 저장 비용입니다.
400 * 30 * 10(pull) -> 120GB ->15.12 USD가 대략적인 데이터 전송 비용입니다.
생각보다 만만찮은 비용인데, 다행히도 레포지토리 설정에서 Lifecycle Policy를 통해 저장 비용을 줄일 수 있습니다.
그리고 동일한 리전의 AWS 서비스(EC2, Lambda 등)로 데이터 전송하는 건 무료이므로, 리전마다 ECR을 만들면 데이터 전송 비용을 줄일 수 있을 것입니다.
사용방법
1. ECR private 리포지토리 만들기
이름만 설정하고 나머지는 기본값 그대로 만들어 줍니다.
2. IAM 정책(Policy) 생성하기
"정책 생성"을 클릭합니다.
이렇게 검색해서 정책에 작업을 추가할 수 있지만 시간이 없는 우리는 JSON으로 한방에 적을 겁니다.
"정책 편집기"에서 "JSON"을 클릭합니다.
그리고 아래 내용을 복붙합니다.
Github OIDC를 위해 GetAuthorizationToken 액션을 모든 Resource에서 사용할 수 있도록 합니다.
Resource는 생성한 private 리포지토리의 ARN입니다. public을 포함한 모든 리포지토리에 이 정책이 적용되길 원한다면 "*"만 작성하면 됩니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "GetAuthorizationToken",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Sid": "AllowPushPull",
"Effect": "Allow",
"Action": [
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
],
"Resource": "arn:aws:ecr:us-east-1:123456789012:repository/my-ecr-repo"
}
]
}
리포지토리의 ARN은 아래 사진처럼 '요약' 메뉴에서 볼수 있습니다. 리포지토리를 클릭하면 좌측 메뉴 중 "Summary"가 보일텐데, 여기로 들어가도 같은 페이지가 나옵니다. 참고로 ARN은 Amazon Resource Name입니다.
3. IAM 역할(Role) 생성하기
이제 정책을 연결할 역할을 만들어야 합니다. "역할 생성"을 클릭합니다.
엔터티 유형에서 "웹 자격 증명"을 선택하고, ID 제공업체로 "token.actions.githubusercontent.com"을 선택합니다.
Audience는 "sts.amazonaws.com"을 선택합니다.
organization에는 적용할 organization이 있다면 그 이름을 적거나, 개인 레포지토리에서 사용할 거라면 본인 github 닉네임을 적습니다.
이 IAM 역할을 사용할 github repository, branch를 적어서 세세하게 설정할 수 있습니다.
나머지는 그대로 두고 역할을 생성합니다.
다음으로 넘어가서 아까 만든 정책을 검색으로 찾아 선택합니다.
다음으로 넘어가서 역할 이름을 입력하고 신뢰 정책을 작성합니다.
사실 이미 적혀있어서 건들건 없습니다만, 저는 경험적 추정에 근거하여 repo:<github name or organization>/*:*로 수정했습니다.
4. 자격 증명 공급자(Identity providers) 생성하기
AWS IAM의 자격 증명 공급자 항목에 들어가서 자격 증명 공급자를 만들어줍니다.
공급자 유형은 OpenID Connect를 선택합니다.
공급자 URL(Provider URL)은 https://token.actions.githubusercontent.com
대상(Audience)은 sts.amazonaws.com
입니다.
5. github action workflow 작성하기
- AWS_REGION 환경변수를 설정하면 ECR public 리포지토리를 사용할 수 없습니다.
- ECR public은 us-east-1에서만 로그인할 수 있습니다. 즉, GetAuthorizationToken 명령이 us-east-1만 지원합니다.
- Access Key와 Secret Key대신 github action 공식 문서에서 추천하는 OIDC를 사용하여 AWS에 로그인하겠습니다.
여기서는 필수적인 것만 작성하겠습니다.
나머지는 본인의 CI/CD 파이프라인에 맞게 작성하면 됩니다.
env:
ECR_REPOSITORY: ddolboghi/my-repo
permissions:
contents: "read"
id-token: "write"
jobs:
deploy:
name: deploy with AWS ECR
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_GITHUB_ACTION_ROLE_ARN }}
aws-region: ap-northeast-2
- name: Login to Amazon ECR Private
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
mask-password: "false"
- name: Push to Amazon ECR Private
shell: bash
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker push $REGISTRY/${{ env.ECR_REPOSITORY }}:latest
- name: Deploy to Server
uses: appleboy/ssh-action@master
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_PASSWORD: ${{ steps.login-ecr.outputs.docker_password_123451234512_dkr_ecr_ap_northeast_2_amazonaws_com }}
with:
host: ${{ secrets.DEPLOY_SERVER_HOST }}
username: ${{ secrets.DEPLOY_SERVER_USER }}
key: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
port: ${{ secrets.DEPLOY_SERVER_PORT }}
envs: ECR_REGISTRY, ECR_PASSWORD
script: |
echo "$ECR_PASSWORD" | docker login --username AWS --password-stdin "$ECR_REGISTRY"
- permissions.id-token: "write" 로 지정해야 AWS에서 토큰을 가져올 수 있습니다. 원격 서버에 접속해서 AWS ECR에 로그인해야 할때 이 토큰으로 로그인할 수 있습니다.
- permissions.content: "read" 로 지정해야 acions/checkout을 할 수 있습니다.
- aws-actions/configure-aws-credentials@v4 를 사용해서
- role-to-assume 필드에 아까 만든 IAM Role의 ARN을 넣습니다.
- aws-region은 본인의 지역을 넣습니다.
- aws-actions/amazon-ecr-login@v2 를 사용해서 ECR에 로그인합니다.
- mask-password: "false"로 지정해야 AWS 토큰을 다른 step에서 사용할 수 있습니다.
- 이 step의 outputs 중 registry로 ECR 레지스트리 URI를 가져올 수 있습니다.
- 이 step의 outputs 중 docker_password_<AWS 계정 ID>_dkr_ecr_<region>_amazonaws_com로 ECR 로그인에 필요한 비밀번호를 가져올 수 있습니다. 이때 <region>의 "-"를 "_"로 바꿔줘야합니다.
+ 원격서버에 AWS CLI 설치할 필요 없이 AWS ECR 로그인하는 방법
permissions.id-token: "write"
permissions.content: "read"
mask-password: "false"
위 셋을 모두 설정했다면, AWS ECR 로그인 step의 outputs에서 가져온 docker_password를 사용해서 원격 서버에서 AWS ECR에 로그인할 수 있습니다.
저는 원격 서버에 접속하기 위해 appleboy/ssh-action@master를 사용했습니다.
env에 docker_password를 할당한 변수를 추가합니다.
그리고 with.envs에 추가한 변수명을 넣어줘야 script에서 정상적으로 인식됩니다.
이제 다음 스크립트를 작성합니다.
echo "$ECR_PASSWORD" | docker login --username AWS --password-stdin "$ECR_REGISTRY"
ECR_PASSWORD 값을 출력하지 않고 파이프라인(|)으로 연결된 뒤 명령어의 입력으로 넘깁니다.
--password-stdin은 입력을 바로 받습니다.
--username은 AWS여야 ECR에 정상적으로 로그인됩니다.
이러면 원격 서버에 AWS CLI를 설치하여 시크릿키를 등록할 필요 없이 ECR에 로그인할 수 있습니다.
++ Repository에 저장된 이미지 수명주기 설정
Lifecycle Policy를 사용해서 비용을 아낄 수 있습니다.
리포지토리를 선택하고 작업에서 수명 주기 정책을 클릭합니다.
규칙 생성을 클릭합니다.
다음과 같이 설정하면 태그가 없는 이미지들은 최근 10개만 유지됩니다. 1로 해야 저장 요금을 더 많이 줄일 수 있습니다.
매치 범위로 '이미지가 푸시된 후'를 선택하면 며칠동안 저장할지 설정할 수 있습니다.
이 외에도 이미지 태그를 지정하면 특정 이미지들에만 Lifecycle policy를 적용할 수 있습니다.
참고
https://github.com/aws-actions/amazon-ecr-login?tab=readme-ov-file#building-and-pushing-an-image
GitHub - aws-actions/amazon-ecr-login: Logs into Amazon ECR with the local Docker client.
Logs into Amazon ECR with the local Docker client. - aws-actions/amazon-ecr-login
github.com
https://docs.aws.amazon.com/ko_kr/AmazonECR/latest/userguide/security_iam_service-with-iam.html
Amazon Elastic Container Registry가 IAM과 작동하는 방식 - Amazon ECR
Amazon ECR 리포지토리 정책에서 정책 요소 Sid는 IAM 정책에서 지원되지 않는 추가 문자 및 공백을 지원합니다.
docs.aws.amazon.com
Configuring OpenID Connect in Amazon Web Services - GitHub Docs
Use OpenID Connect within your workflows to authenticate with Amazon Web Services.
docs.github.com
Use OpenID Connect to Authenticate AWS Account in GitHub Actions
In my recent personal project, I decided to use GitHub Actions workflow to push my docker image to AWS Elastic Container Registry as part…
medium.com