ECR 멀티 아키텍처 이미지를 활용하여 EKS Graviton 노드 그룹 관리하기
- -
이번 포스팅에서는 ECR 에서 제공하는 Multi Architecture Image 기능을 활용하여
EKS Cluster 의 Amazon Linux 노드 그룹과 Graviton 노드 그룹을 한 번에 관리하는 법에 대해 알아보겠습니다.
1. 개요
최근 서비스 개발에 EKS 를 도입하는 프로젝트가 눈에 띄게 많아졌습니다.
그 만큼 kubernetes 와 EKS 에 대한 관심과 이해도가 많이 증가한 것을 느낄 수 있습니다.
생각보다 EKS 를 서비스에 도입하고 활용하는 것에는 상당한 비용이 들기에 도입을 고려한 후 비용 때문에 도입을 철회하는 경우도 많이 있습니다.
하지만, 비교적 최근 AWS 에서 출시한 ARM 기반의 Graviton 인스턴스를 활용하면 기존 EC2 에 비해 가격을 20% 정도 절약할 수 있습니다.
ARM 프로세서 - 고성능 프로세서 - AWS EC2 Graviton - AWS
범용 M7g: 컴퓨팅, 메모리 및 네트워킹의 균형을 맞춘 범용 워크로드에 대한 최고의 가격 대비 성능 구동: AWS Graviton3 구축 대상: 애플리케이션 서버, 중간 규모의 데이터 스토어, 마이크로서비스
aws.amazon.com
그렇기 때문에, Graviton 을 이용한 EKS 활용도 흔히 볼 수 있는 Practice 가 되고 있습니다.
만약 이 글을 보고 있는 사람이 신규 EKS 구축을 Graviton 으로 하고자 할 때는 아마 크게 어려움이 없을 것입니다.
하지만 기존에 Amazon Linux 노드로 운영을 하고 있고 이를 비용 효율적인 Graviton 인스턴스로 Migration 하고 싶을 때,
Container Image 관리 측면에 어려움을 겪을 수 있습니다.
1.1. Multi Architecture
흔히, Container 는 프로세스가 격리되어 있기 때문에 어떠한 환경에서 띄운다 할 지라도 호환이 된다고 알고 있을 것입니다.
하지만, CPU Architecture 자체가 변경된 x86_64(amd) 와 ARM 환경의 Container 는 서로 호환이 되지 않습니다.
이러한 제약 때문에, 최근 업로드되는 Docker Hub 나 Public ECR 의 Container Image 는 Multi Architecture 를 지원하고 있습니다.
Multi Architecture Image 는 x86(amd), ARM 환경에서 동일하게 사용할 수 있는 Container Image 를 의미합니다.
실제 서비스에 활용되는 Container Image 는 보통 직접 빌드해서 사용하거나,
Private 한 환경에 보관된 Image 를 사용하기 때문에 어떻게 Multi Architecture Image 를 지원하는 환경을 구현할 것인지가 핵심 과제가 될 것입니다.
다행히도, 최근 ECR 에서 Multi Architecture Image 를 지원하는 기능을 발표했습니다.
이번 글에서는 이를 이용하여 Private ECR 을 이용하여
EKS 클러스터의 Amazon Linux Node 와 Graviton Node 이미지 배포를 관리하는 법에 대해 알아볼까 합니다.
1.2. 구성도

2. 테스트 환경
테스트 환경을 위해 사용하는 리소스는 다음과 같습니다.
- EKS Cluster
- Amazon Linux Node Group + Graviton Node Group
- Private ECR Repo
- EC2 2대 (amd + arm)

2.1. 사전 환경 검증
Private ECR 에 저장하기 위해서는 Public Image 를 내 환경에 다운 받은 후 ECR 로 다시 업로드 하는 과정을 거치게 됩니다.
이 때, amd와 arm 환경에서 각각 어떤 Image 를 다운받는 지 확인해볼 예정입니다.
각각의 Linux 환경에서 아래의 이미지를 가져옵니다. 해당 이미지는 fluent-bit 의 공식 이미지 주소입니다.
docker pull cr.fluentbit.io/fluent/fluent-bit

겉으로 보기에는 동일한 Image 로 보이지만, Image ID 를 보면 알 수 있듯이 전혀 다른 이미지입니다.
아래 명령어를 통해 무엇이 다른 지 명확하게 알 수 있습니다.
docker inspect cr.fluentbit.io/fluent/fluent-bit:latest | grep Architecture

보시다시피 amd 환경에서는 amd Image 를 다운받고, arm 환경에서는 arm Image 를 다운받습니다.
이것이 Multi Architecture Image 의 특징이라고 할 수 있습니다.
그렇다면, amd 환경에서 다운받은 Image 를 Private ECR 에 올리면 해당 Image 를 arm 환경에서 사용할 수 있을까요??

결론부터 말하자면 불가능합니다.
증명을 위해 Private ECR 을 생성 후 업로드 해보겠습니다.
Private ECR Repo 명은 multi-arch/fluent-bit 으로 진행했습니다.
- AMD64 리눅스에서 실행
# ACCOUNT ID & Region 설정 ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 # ECR Repo 생성 aws ecr create-repository --repository-name multi-arch/fluent-bit # ECR Login aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com # Image Tag 변경 docker tag cr.fluentbit.io/fluent/fluent-bit:latest ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:amd64 # Image Push docker push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:amd64
ECR 콘솔에서 해당 이미지를 확인합니다.

arm 환경에서 해당 이미지를 다운합니다.
# ACCOUNT ID & Region 설정 ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 # ECR Login aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com # Image Pull docker pull ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:amd64
이후 해당 이미지의 아키텍처를 확인해보면 변경되지 amd 환경이 변경되지 않은 것을 확인할 수 있습니다.
docker inspect ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:amd64 | grep Architecture

그럼 이런 환경이 왜 문제가 되는 지, 실제 EKS 에 배포해보겠습니다.
2.2. Private ECR 을 통한 Pod 배포
Private ECR 에 있는 Image 를 Helm 을 통해서 배포해 보겠습니다.
ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 helm install fluent-bit fluent/fluent-bit \ --set image.repository=${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit \ --set image.tag=amd64
Pod 배포 결과 확인
kubectl get nodes -o wide kubectl get pods -o wide

결과에서 보듯이, amd 환경에서 ECR 에 업로드 한 Image 는 Graviton 노드에서 활용할 수 없는 것을 확인할 수 있습니다.
그럼 반대로, arm 환경에서 올린 Image 는 어떨까요??
- ARM 리눅스에서 실행
# ACCOUNT ID & Region 설정 ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 # ECR Login aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com # Image Tag 변경 docker tag cr.fluentbit.io/fluent/fluent-bit:latest ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:arm64 # Image Push docker push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:arm64
Helm 의 Image 를 변경하여 다시 배포해봅니다.
ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 helm upgrade fluent-bit fluent/fluent-bit \ --set image.repository=${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit \ --set image.tag=arm64

이번엔 Amazon Linux 노드에서 사용할 수 없다는 것을 알 수 있습니다.
이런 환경은 저희가 원하는 것이 아닙니다.
amd 노드에서는 amd ECR Image 를, arm 노드에서는 arm ECR Image 를 다운받을 수 있게 하는 것을 원합니다.
3. ECR Multi Architecture Image 생성
이러한 문제점을 해결하기 위해 2개의 amd & arm 이미지를 1개로 묶어줘야 합니다.
ECR 에서는 이를 지원하고 있습니다.
3.1. Image Push
우선 ECR Repository 에 사용할 amd, arm 2개의 Image 를 업로드 해줍니다.

해당 이미지들을 통해 새로운 Multi Architecture Image 를 생성할 예정입니다.
3.2. Container Manifest 생성
Container 에는 Manifest 라고 하는 Container 구성 요소가 존재합니다.
Manifest 를 통해 Layer 와 OS 등의 구성요소를 지정하게 됩니다.
자세한 내용은 아래 공식 문서를 참고 바랍니다.
docker manifest
docs.docker.com
앞서 ECR 에 업로드 했던 2개의 Image 를 사용하여 Multi Architecture 를 지원하는 Manifest 를 생성하겠습니다.
# 사용법 docker manifest create <PRIVATE_ECR_REPO>:<TAG> \ <PRIVATE_ECR_REPO>:<ARM_TAG> \ <PRIVATE_ECR_REPO>:<x86_TAG>
ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 # Manifest 생성 docker manifest create ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:multi-image \ ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:amd64 \ ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:arm64 # Manifest 생성 확인 docker manifest inspect ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:multi-image

3.3. Container Manifest 업로드
Manifest 만 생성했을 뿐, 아직 Image 로 사용하지는 못합니다.
ECR 에 업로드하여 사용할 수 있도록 변경해줍니다.
docker manifest push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit:multi-image
이후 ECR 콘솔에서 확인해보면 실제 Image 가 올라가있는 것을 알 수 있습니다.

3.4. Multi Architecture Image 배포
그럼 실제 EKS 에 Image 를 배포하여 아키텍처 별로 잘 적용이 되는 지 확인해보겠습니다.
ACCOUNT_ID=$(aws sts get-caller-identity | jq .Account -r) REGION=ap-northeast-2 helm upgrade fluent-bit fluent/fluent-bit \ --set image.repository=${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/multi-arch/fluent-bit \ --set image.tag=multi-image
kubectl get nodes -o wide kubectl get pods -o wide

이제 ARM, AMD 환경 상관없이 이미지를 잘 가져오는 것을 확인할 수 있습니다.
가능한 구성 시나리오
해당 환경은 ARM, AMD 인스턴스를 혼합해서 사용하는 환경에 적합합니다.
Graviton 인스턴스로 Migration 과정에서 일시적으로 함께 사용할 때 적합합니다.
Public Image 외에 Private Image 를 활용해야 하는 보안 규정이 있을 경우 적합합니다.