AWS 기술 블로그

AWS Graviton 으로 EKS 비용 최적화 하기

클라우드를 쓰는 모든 팀에게는 영원한 숙제가 하나 있습니다. 바로 “성능은 지키면서 비용은 줄이는 것”입니다. 트래픽이 늘면 인스턴스를 키우고, 청구서를 받아 들면 다시 줄일 궁리를 하는 일이 반복됩니다. 하지만 이런 조정만으로는 한계가 분명합니다. 같은 x86 아키텍처 안에 머무는 한, “더 쓰면 더 낸다”는 구조를 벗어나기 어렵기 때문입니다.

그래서 최근에는 이 구조 자체를 바꾸려는 움직임이 빠르게 늘고 있습니다. 인프라 비용 절감과 성능 향상을 동시에 누리기 위해, ARM64 기반의 AWS Graviton 인스턴스를 Amazon EKS(Amazon Elastic Kubernetes Service)에 도입하는 기업들이 빠르게 증가하고 있습니다. Graviton은 동급 x86 인스턴스 대비 최대 40%의 가격 대비 성능을 제공합니다. 이번 글에서는 EKS에 Graviton 인스턴스를 어떻게 적용할 수 있는지 살펴보겠습니다.

AWS Graviton이란

AWS Graviton은 클라우드 워크로드에 최적화하기 위해 AWS가 직접 설계한 ARM 기반 프로세서입니다. 우리에게 익숙한 Intel·AMD의 x86 칩 대신, 스마트폰부터 슈퍼컴퓨터까지 폭넓게 쓰이는 ARM 아키텍처, 그중에서도 서버·클라우드에 최적화된 ARM Neoverse 코어를 기반으로 만들어졌습니다. EC2는 물론 RDS, Lambda, ECS/EKS, ElastiCache 등 주요 관리형 서비스에서도 인스턴스 타입으로 선택할 수 있습니다. 범용 시장을 겨냥한 칩이 아니라 클라우드 워크로드라는 한 가지 목표에 집중해 설계했기 때문에, 같은 비용으로 더 많은 일을 처리할 수 있습니다. 참고로 Graviton은 세대를 거듭해 현재 5세대(Graviton 5)까지 출시돼 있습니다.

AWS Graviton 은 다음과 같은 특징을 가지고 있습니다.

  • 1 vCPU = 물리 코어 1개: x86은 하이퍼스레딩(SMT)으로 물리 코어 하나를 가상 CPU 두 개로 나눠 쓰기 때문에 코어 자원을 두고 경합이 생깁니다. 반면 Graviton은 vCPU 하나가 물리 코어 하나에 그대로 매핑됩니다. 스레드 간 자원 경합이 없어, 마이크로서비스나 웹 서버처럼 멀티스레드 환경에서 성능이 더 예측 가능하고 일관되게 유지됩니다.
  • 높은 메모리 대역폭: 세대를 거듭하며 메모리 대역폭이 크게 늘어(Graviton 3부터 DDR5 채택), 데이터 집약적인 작업을 더 빠르게 처리합니다. (Graviton3 – DDR5-4800, Graviton4 – DDR5-5600, Graviton5 – DDR5-8800)
  • 뛰어난 에너지 효율: 같은 성능을 낼 때 동급 x86 기반 EC2 인스턴스보다 에너지 사용량이 최대 60% 적습니다. 비용뿐 아니라 지속가능성(탄소 감축) 관점에서도 의미가 있습니다.

이처럼 Graviton의 이점은 분명하지만, 근본적으로 x86과 다른 ARM64 아키텍처라는 점은 기억해야 합니다. 애플리케이션과 컨테이너 이미지가 ARM64를 지원해야 이 이점을 온전히 누릴 수 있습니다.

EKS 비용은 어디서 발생하는가

먼저 EKS 비용이 어떻게 구성되는지부터 살펴보겠습니다. EKS 비용은 크게 네 갈래로 나뉩니다.

  • 컨트롤 플레인(Control plane): 클러스터마다 시간당 $0.10 고정 요금
  • 컴퓨트(Compute): 파드가 실제로 소비하는 CPU·메모리·스토리지. EC2 노드 또는 AWS Fargate 형태로 과금
  • 네트워킹(Network): 데이터 전송(egress), 로드 밸런서(ELB) 등
  • 옵저버빌리티(Observability): 로그·지표·추적(CloudWatch 등)

여기서 핵심은, 워크로드를 실행하는 컴퓨트 비용이 대부분의 EKS 청구서에서 가장 큰 부분을 차지한다는 점입니다. 컨트롤 플레인은 클러스터당 월 약 $73(시간당 $0.10)로 고정돼 있고, 네트워킹·옵저버빌리티는 상대적으로 작은 비중입니다. 노드가 조금만 늘어도 컴퓨트가 청구서의 대부분을 차지하는 구조입니다. 결국 EKS 비용 최적화의 출발점은 컴퓨트 비용을 줄이는 것입니다.

이러한 컴퓨트 비용을 가장 손쉽게 줄이는 방법이 바로 AWS Graviton(ARM64) 인스턴스 도입입니다. 앞서 언급한 Graviton의 가격 대비 성능 이점이 다름 아닌 이 컴퓨트 비용에 직접 작용하기 때문입니다. 애플리케이션 코드는 그대로 두더라도, 인프라 관점에서는 노드 그룹의 인스턴스 타입만 ARM64 계열로 바꾸는 것만으로 컴퓨트 비용을 의미 있게 낮출 수 있습니다.

다만 이 이점을 온전히 누리려면, 하나의 태그로 x86과 ARM64를 모두 지원하는 멀티 아키텍처(Multi-Architecture) 컨테이너 이미지를 먼저 준비해야 합니다.

멀티 아키텍처 이미지란?

멀티 아키텍처(Multi-Architecture) 이미지란, 하나의 이미지 태그(예: my-app:v1.0.0) 안에서 여러 CPU 아키텍처(예: amd64(x86_64), arm64)용 이미지를 함께 제공하는 컨테이너 이미지를 말합니다. 태그를 가리키는 매니페스트 리스트가 아키텍처별 이미지를 각각 참조하고, 노드는 자신의 아키텍처에 맞는 이미지를 자동으로 선택해 내려받습니다. 덕분에 x86 노드와 ARM64 노드가 섞인 클러스터에서도 동일한 태그 하나로 배포하면 각 노드가 알맞은 이미지를 받아 실행하게 됩니다.

이러한 멀티 아키텍처 이미지를 빌드하는 방식은 크게 세 가지입니다. 프로젝트의 언어, CI/CD 인프라, 안정성 요구 수준에 따라 적합한 방식이 달라집니다.

방식 장점 단점 적합한 경우
에뮬레이션 추가 인프라 불필요 최대 10배 느림 전환 초기, 빌드 빈도 낮음
네이티브 가장 빠르고 안정적 ARM 인프라 필요 엔터프라이즈, CI/CD 상시
크로스 컴파일 빠르고 인프라 불필요 Go·Rust 등 일부 언어만 현대적 컴파일 언어 사용 시

에뮬레이션 빌드 (Emulated Build)

기존 x86 환경을 그대로 유지하면서 빌드하고 싶다면 에뮬레이션 방식을 사용할 수 있습니다. Docker의 buildxQEMU를 활용해 x86 머신 위에서 ARM 명령어를 해석하며 빌드하는 방식입니다. 추가 인프라 구축 없이 기존 CI/CD 파이프라인을 그대로 활용할 수 있어, 전환 초기 단계에서 경제적입니다. 다만 에뮬레이션 과정에서 빌드 속도가 크게 느려지고(최대 10배까지 저하), 드물게 에뮬레이션 오버헤드로 인한 런타임 오류가 발생할 수 있다는 점에 유의해야 합니다.

에뮬레이션 방식은 다음 순서로 진행합니다. 먼저 QEMU를 설치해 x86 머신이 ARM 명령어를 해석할 수 있도록 하고, docker buildx로 빌더를 생성·활성화한 뒤, docker buildx build 명령으로 멀티 아키텍처 이미지를 빌드해 Amazon ECR(Elastic Container Registry) 레지스트리에 푸시합니다.

# 에뮬레이션 방식: QEMU 설치 *→* buildx 빌더 생성 *→* 멀티 아키텍처 빌드·푸시

# 1. QEMU 설치 (호스트당 한 번만 실행)
docker run --privileged --rm tonistiigi/binfmt --install all

# 2. buildx 빌더 생성 및 활성화
docker buildx create --name eks-builder --use

# 3. 멀티 아키텍처 이미지 빌드 및 푸시
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0 \
  --push .

네이티브 빌드 (Native Build)

가장 높은 신뢰도를 보장하는 방법은 대상 아키텍처인 ARM64 환경에서 직접 빌드하는 것입니다. CI/CD 파이프라인의 빌드 러너 자체를 Graviton 인스턴스로 구성해 실행하는 방식입니다. 별도의 ARM 기반 인프라를 구축해야 하지만, 빌드 환경과 실행 환경이 완벽히 일치하므로 아키텍처 불일치로 인한 오류가 없고 빌드 속도도 빠릅니다. 따라서 안정성이 최우선인 대규모 엔터프라이즈 서비스에 가장 권장됩니다. 네이티브 빌드는 각 아키텍처를 해당 아키텍처의 실제 하드웨어에서 빌드합니다. 즉 x86 이미지는 x86 머신에서, ARM64 이미지는 Graviton 머신에서 각각 빌드해 아키텍처를 구분한 태그로 푸시한 뒤, 두 이미지를 하나의 매니페스트 리스트로 묶어 배포용 태그(예: v1.0.0)를 완성합니다.

먼저 각 머신에서 자신의 아키텍처에 맞는 이미지를 빌드해 푸시합니다.

# 네이티브 빌드: 각 아키텍처의 실제 머신에서 이미지 빌드·푸시

# x86(amd64) 머신에서 실행
docker build -t <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0-amd64 .
docker push <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0-amd64

# ARM64(Graviton) 머신에서 실행
docker build -t <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0-arm64 .
docker push <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0-arm64

각 빌드가 자신의 네이티브 환경에서 실행되므로 에뮬레이션 오버헤드가 전혀 없습니다. 이제 아키텍처별로 나뉜 두 이미지를 배포에 사용할 단일 태그로 결합하기 위해서, docker manifest create 와 push 명령어로 하나의 매니페스트 리스트로 합칩니다.

# 아키텍처별 두 이미지를 하나의 매니페스트 리스트(단일 태그)로 결합·푸시
docker manifest create <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0 \
  <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0-amd64 \
  <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0-arm64

docker manifest push <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0

크로스 컴파일 (Cross-Compilation)

Go 나 Rust 같은 현대적인 언어를 사용 중이라면, 컴파일러 기능을 활용한 크로스 컴파일이 가장 효율적인 대안입니다. x86 환경에서 빌드 명령을 내릴 때 대상 아키텍처를 ARM64로 지정하기만 하면 됩니다. 별도의 ARM 인프라나 에뮬레이터 없이도 네이티브에 가까운 빠른 속도로 빌드를 마칠 수 있어 효율적입니다. 다만 프로젝트가 C로 작성된 외부 라이브러리(CGO 등)에 의존하는 경우, 해당 라이브러리까지 크로스 컴파일해야 하므로 설정 복잡도가 높아질 수 있습니다. 크로스 컴파일의 핵심은 “컴파일 작업은 빠른 x86 환경에서 그대로 수행하되, 결과물만 ARM64용으로 만든다”는 데 있습니다. 이를 Dockerfile에 담으면 이미지 빌드까지 한 번에 처리할 수 있습니다. Buildx는 빌드 시점에 대상 아키텍처 정보를 TARGETOS, TARGETARCH 인자로 자동 주입하므로, 이 값을 컴파일러에 그대로 넘기면 됩니다.

# 크로스 컴파일용 Dockerfile: x86에서 컴파일해 ARM64용 바이너리 생성 

# 빌드 스테이지는 항상 빌더의 네이티브 아키텍처(x86)에서 실행
FROM --platform=$BUILDPLATFORM golang:1.26 AS builder

# Buildx가 자동으로 채워주는 인자
ARG TARGETOS
ARG TARGETARCH

WORKDIR /src
COPY . .

# 대상 아키텍처용 바이너리를 크로스 컴파일
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
    go build -o /out/app .

# 실행 이미지는 대상 아키텍처 기반
FROM alpine:3.22
COPY --from=builder /out/app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]

여기서 핵심은 빌드 스테이지에 붙은 –platform=$BUILDPLATFORM 입니다. 이 설정 덕분에 컴파일 자체는 항상 빠른 네이티브(x86)에서 실행되고, 코드만 크로스 컴파일로 ARM64용이 됩니다. 즉 에뮬레이션을 전혀 거치지 않습니다. 이제 앞의 에뮬레이션 섹션과 동일한 Buildx 빌드 명령을 그대로 사용하면 됩니다.

# 크로스 컴파일 이미지 빌드·푸시
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0 \
  --push .

명령어는 에뮬레이션 방식과 같지만, Dockerfile이 크로스 컴파일로 동작하기 때문에 빌드 속도가 훨씬 빠릅니다.

EKS 노드그룹 Graviton으로 전환하기

Graviton(ARM64) 노드는 동일 성능 대비 비용을 크게 낮출 수 있어 EKS 워크로드 전환 대상으로 자주 사용됩니다. 아래는 노드그룹을 새로 만들고, 트래픽을 점진적으로 옮기는 전체 흐름입니다.

Graviton 노드그룹 생성

기존 x86 노드 그룹은 그대로 두고, Graviton 인스턴스(예: m7g, c7g, t4g 계열)로 새 노드그룹을 추가합니다. 롤백이 쉽도록 기존 노드그룹과 병렬로 두는 것이 핵심입니다.

`# Graviton 관리형 노드그룹 생성 예시`
`aws eks create-nodegroup \`
` --cluster-name "$CLUSTER_NAME" \`
` --region "$REGION" \`
` --nodegroup-name graviton-ng \`
` --node-role "$NODE_ROLE_ARN" \`
` --subnets $SUBNET_IDS \`
` --ami-type AL2023_ARM_64_STANDARD \`
` --instance-types m7g.large \`
` --scaling-config minSize=2,maxSize=6,desiredSize=2 \`
` --disk-size 200 \`
` --labels arch=arm64 \`
` --tags "team=platform,workload=graviton"`

Graviton 으로 트래픽 전환 하기

Graviton 노드와 x86 노드가 같은 클러스터에 공존하는 상태에서 트래픽을 옮기는 전략은 크게 두 가지입니다. 블루-그린(Blue-Green)은 Graviton 환경을 미리 갖춰 두고 트래픽을 한 번에 전량 전환하는 방식으로, 절차는 단순하지만 문제가 생겼을 때 영향 범위와 롤백 폭이 큽니다. 반면 카나리(Canary)는 전체 트래픽의 일부만 Graviton으로 보내고 지표에 이상이 없을 때만 비율을 단계적으로 늘리는 방식입니다. 아키텍처 전환처럼 런타임 호환성을 실제 트래픽으로 검증해야 하는 작업에서는, 소량의 트래픽으로 문제점을 조기에 검증하고 즉시 되돌릴 수 있는 카나리가 더 안전한 선택입니다.

쿠버네티스에서는 별도 도구 없이도 카나리 전환을 구현할 수 있습니다. 같은 Service 뒤에 Graviton 전용 Deployment를 새롭게 추가하고, 기존 Deployment와 함께 레플리카 수를 조정하면, 그 비율 만큼 트래픽이 자연스럽게 나뉘기 때문입니다.

아래와 같이 원본 Deployment는 그대로 두고, 카나리용 Deployment를 하나 추가합니다. nodeSelector를 이용해 이 Deployment의 파드는 Graviton(ARM64) 노드에만 스케줄링되도록 합니다. 레플리카 수는 1로 시작합니다.

# app-canary.yaml — 동일 Service의 라벨 셀렉터에 잡히도록 app 라벨은 동일하게 유지
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-canary
  labels:
    app: my-app
spec:
  replicas: 1                       # 전체 10개 중 1개 → 약 10% 트래픽
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app                 # Service가 이 라벨로 두 Deployment를 함께 로드밸런싱
    spec:
      nodeSelector:
        kubernetes.io/arch: arm64   # 이 파드만 Graviton 노드로
      containers:
      - name: app
        image: <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0

쿠버네티스 Service는 app: my-app 라벨로 양쪽 Deployment의 파드를 모두 엔드포인트로 잡기 때문에, 레플리카 개수 비율만큼 트래픽이 자연스럽게 분배됩니다. 전환 비율을 늘릴 때는 두 Deployment의 레플리카 수를 조정합니다.

`# 전환 비율 조정: 두 Deployment의 replica 수로 트래픽 배분 (여기선 80:20)
kubectl scale deployment my-app --replicas=8`
`kubectl scale deployment my-app-canary --replicas=2`

본 예제에서는 전체 레플리카 수를 10으로 가정했습니다. 원본 my-app Deployment의 레플리카는 줄이고 my-app-canary의 레플리카는 늘려서, Graviton이 받는 트래픽 비율을 10% → 20% → 50% → 100%처럼 단계적으로 키웁니다. 비율을 올리기 전, 에러율·응답 지연에 이상이 없는지 확인합니다.

특정 아키텍처에만 배치하기 (선택)

특정 워크로드를 Graviton 노드에만 배치하고 싶다면 쿠버네티스 내장 라벨 kubernetes.io/arch를 nodeSelector나 nodeAffinity로 지정하면 됩니다. 아래는 nodeSelector 를 사용하는 예제입니다.

# 특정 워크로드를 Graviton(arm64) 노드에만 배치 
spec:
  template:
    spec:
      # 가장 간단한 방법 — nodeSelector
      nodeSelector:
        kubernetes.io/arch: arm64
      containers:
      - name: app
        image: <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app:v1.0.0

성능 모니터링하기

카나리 전환의 핵심은 “비율을 올릴 때마다 지표를 확인하고, 이상이 없을 때만 다음 단계로 넘어가는 것”입니다. Graviton 파드로 트래픽이 조금씩 흘러 들어가는 동안, 아래 지표들을 x86 파드와 나란히 비교하며 관찰합니다. 같은 애플리케이션·같은 트래픽을 받는 두 아키텍처를 직접 비교할 수 있다는 점이 카나리 방식의 가장 큰 장점입니다.

  • 에러율 / HTTP 5xx — Graviton 파드에서만 에러가 발생한다면 아키텍처 비호환(예: ARM64 미지원 네이티브 라이브러리)일 가능성이 높습니다.
  • 응답 지연(latency) — p50/p90/p99를 x86 파드와 비교합니다. 동등하거나 개선되면 정상입니다.
  • 파드 상태 — CrashLoopBackOff나 exec format error는 이미지가 ARM64를 지원하지 않을 때 나타나는 대표적인 신호입니다.
  • 리소스 사용률(CPU·메모리) — Graviton에서의 사용 패턴을 확인합니다.

에러율, HTTP 응답 코드, 응답 지연(latency) 등의 지표는 별도의 모니터링 시스템을 구축해야 수집할 수 있습니다. 반면 파드 상태와 리소스 사용률은 PrometheusCloudWatch Container Insights를 통해 확인할 수 있습니다.

마치며

3년 연속으로 AWS에 새로 추가되는 CPU 용량의 절반 이상을 Graviton이 차지할 만큼 도입 속도가 가파릅니다. 상위 1,000개 EC2 고객 중 98%가 이미 Graviton을 도입했고, 전 세계 12만 곳이 넘는 고객이 Graviton 위에서 서비스를 운영하고 있습니다 (AWS re:Invent 2025, About Amazon). 최근에는 Meta가 대규모 에이전틱 AI 용으로 Graviton을 선택하여, 적용 범위 또한 넓어지고 있습니다 (About Amazon, Meta Newsroom).

국내에서도 규모와 업종을 가리지 않고 Graviton 전환이 활발합니다. AWS Summit Seoul 에서 발표된 대표 사례를 정리하면 다음과 같습니다.

  • 하이퍼커넥트 — Intel 기반 EC2/EKS 노드그룹을 AWS Graviton3로 전환해 비용을 절감했습니다. 10개 리전에 걸친 30개 AWS 계정에서 300개 서비스를 Graviton3로 마이그레이션했으며, 이는 전체 EC2 서버의 80% 이상에 해당합니다. 애플리케이션 스택은 Java / Kotlin / Python / Golang 입니다.
  • 카카오페이손해보험 — Intel 기반 EC2/EKS 노드그룹을 Graviton으로 전환해 약 20% 비용을 절감했고, 전체 애플리케이션의 95%를 Graviton으로 이전했습니다. 애플리케이션 스택은 Java / Kotlin / Python / Golang 입니다.
  • 삼성전자— C5.2xlarge에서 Graviton 3(C7g.2xlarge)로 전환해 약 15% 비용을 절감했습니다. 동시에 JDK를 8에서 11(최신 패치)로 업그레이드했으며, 응답 시간도 개선됐습니다 (P50 5.6%, P90 48.2% 향상). 애플리케이션 스택은 Java / Spring 입니다.

이처럼 규모를 막론하고 Graviton 도입이 빠르게 확산되는 배경에는, 전환이 생각보다 어렵지 않다는 점이 있습니다. Java, Python, Node.js 등 주요 웹 애플리케이션 런타임이 이미 ARM64를 폭넓게 지원하는 만큼, 개발자는 앞서 살펴본 대로 멀티 아키텍처 이미지를 준비하고, 노드그룹의 인스턴스 타입을 ARM64 계열로 바꾸기만 하면 됩니다.

EKS 비용의 가장 큰 부분은 컴퓨트 비용이고, 이를 Graviton으로 옮기는 것만으로 의미 있는 절감과 성능 향상을 동시에 얻을 수 있습니다. 트래픽이 적은 스테이징이나 스테이트리스 워크로드부터 ARM64 노드 그룹을 추가해 점진적으로 전환해 보시길 권합니다. 이번 글이 그 첫걸음에 도움이 되기를 바랍니다.

SoonBeom Kwon

SoonBeom Kwon

권순범 솔루션즈 아키텍트는 AWS Compute Specialist로서, 고객이 EC2, EKS 등 AWS 컴퓨팅 서비스를 효과적으로 활용하도록 돕습니다. AWS Graviton을 중심으로 웹 서비스·컨테이너·데이터 분석·HPC 등 다양한 워크로드에서 최적의 인스턴스를 선정·검증하고, GPU 기반의 분산 훈련과 추론, 나아가 Agentic AI 워크로드에 대한 기술 지원을 제공하여 고객이 대규모 AI/ML 환경을 효율적으로 구축·운영할 수 있도록 지원합니다

Dahyeon Kang

Dahyeon Kang

강다현 솔루션즈 아키텍트는 제조 산업 고객과 함께 클라우드 여정을 걸으며, 비즈니스 과제를 이해하고 AWS 서비스를 효과적으로 활용해 고객이 혁신과 성장을 이루어갈 수 있도록 돕고 있습니다.