
Docker 이미지는 기본적으로 특정 CPU 아키텍처에 따라 만들어지기 때문에(ex: x86_64, ARM64 등) 서로 다른 환경에서 동일한 이미지를 실행하려고 하면 아키텍처 호환성 문제가 생길 수 있습니다. 최근에는 클라우드 환경, IoT 디바이스, 그리고 Apple Silicon처럼 여러 가지 플랫폼이 함께 사용되는 시대여서 이런 제약이 더욱 뚜렷하게 드러납니다.
이 글에서는 이러한 문제를 해결할 수 있도록 Docker의 멀티 아키텍처 이미지 구성 방법을 설명합니다. buildx를 사용해 여러 플랫폼용 이미지를 빌드하는 방법부터 manifest를 활용한 통합 이미지 관리 방법까지, 실제 따라 해볼 수 있는 예제를 중심으로 다룰 예정입니다. 이를 통해 하나의 이미지 태그만으로 다양한 아키텍처를 지원하는 과정을 이해하는 것이 목표입니다.
즉, 하나의 이미지를 여러 아키텍처 환경에서 그대로 사용하기 위해서는 멀티 아키텍처 이미지 구성이 필요합니다.
package main
import (
"fmt"
"log"
"net/http"
"runtime"
)
func arch(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Application Running\nARCH=%s\n", runtime.GOARCH)
}
func healthcheck(w http.ResponseWriter, req *http.Request) {
fmt.Fprint(w, "OK")
}
func main() {
http.HandleFunc("/arch", arch)
http.HandleFunc("/healthcheck", healthcheck)
log.Fatal(http.ListenAndServe(":8080", nil))
}
해당 애플리케이션은 실행 중인 컨테이너의 CPU 아키텍처를 확인하기 위한 테스트용 코드입니다.
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
WORKDIR /build
ARG TARGETOS
ARG TARGETARCH
ENV CGO_ENABLED=0
COPY ./main.go ./
RUN go mod init noah.io/ark/rest && go mod tidy
RUN --mount=type=cache,target=/root/.cache/go-build \
GOOS=$TARGETOS \
GOARCH=$TARGETARCH \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o main main.go
FROM alpine:3.20
WORKDIR /app
RUN apk add --no-cache curl
COPY --from=builder /build/main .
EXPOSE 8080
ENTRYPOINT ["/app/main"]
--platform=$BUILDPLATFORM : 빌드 환경 기준으로 builder 이미지 선택TARGETOS, TARGETARCH : buildx가 자동으로 전달하는 타겟 플랫폼 정보GOOS, GOARCH : 크로스 컴파일을 통해 다른 아키텍처용 바이너리 생성ECR에 업로드 하는 Layer는 하나의 ECR Repository에서만 동작합니다.
Amazon ECR Registry를 생성합니다.

buildx Package를 설치합니다.
# AMD64
mkdir -p ~/.docker/cli-plugins
curl -L https://github.com/docker/buildx/releases/download/v0.33.0/buildx-v0.33.0.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
# ARM64
mkdir -p ~/.docker/cli-plugins
curl -L https://github.com/docker/buildx/releases/download/v0.33.0/buildx-v0.33.0.linux-arm64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
buildx Version을 확인합니다.
docker buildx version
환경변수를 입력합니다.
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
REGION_CODE="ap-northeast-2"
ECR_NAME="demo-ecr"
ECR_URI="$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com/$ECR_NAME"
IMAGE_TAG="v1.0.0"
서로 다른 CPU 아키텍처 이미지를 빌드하기 위해서 tonistiigi/binfmt 컨테이너를 실행합니다.
docker run --privileged --rm tonistiigi/binfmt --install all
멀티 아키텍처 이미지를 빌드하기 위한 전용 빌더 환경을 생성합니다.
docker buildx create --name multi-builder --driver docker-container --use --bootstrap
ECR Registry에 로그인합니다.
aws ecr get-login-password --region $REGION_CODE | docker login --username AWS --password-stdin "$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com"
CPU 아키텍처별로 Docker 이미지를 각각 따로 빌드합니다.
docker buildx build --platform linux/amd64 -t $ECR_URI:$IMAGE_TAG-amd64 --load .
docker buildx build --platform linux/arm64 -t $ECR_URI:$IMAGE_TAG-arm64 --load .
Amazon ECR에 Docker 이미지를 Push합니다.
docker push $ECR_URI:$IMAGE_TAG-amd64
docker push $ECR_URI:$IMAGE_TAG-arm64
Amazon ECR에 업로드한 Docker 이미지를 조회합니다. 조회 시 CPU 아키텍처별로 이미지가 업로드된 것을 확인할 수 있습니다.
aws ecr describe-images --repository-name $ECR_NAME --region $REGION_CODE

CPU 아키텍처 이미지를 하나로 만들기 위해서 멀티 아키텍처 이미지로 묶습니다.
docker manifest create $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG-amd64 $ECR_URI:$IMAGE_TAG-arm64
manifest에 각 이미지의 정확한 플랫폼 정보(OS/아키텍처)를 명시해줍니다.
docker manifest annotate $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG-amd64 --os linux --arch amd64
docker manifest annotate $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG-arm64 --os linux --arch arm64
멀티 아키텍처 manifest 안에 어떤 이미지들이 들어있는지 확인합니다.
docker manifest inspect $ECR_URI:$IMAGE_TAG

로컬에서 만든 멀티 아키텍처 manifest를 Amazon ECR에 업로드합니다.
docker manifest push $ECR_URI:$IMAGE_TAG
docker run --platform=linux/amd64 -d -p 8080:8080 $ECR_URI:$IMAGE_TAG

docker run --platform=linux/arm64 -d -p 8080:8080 $ECR_URI:$IMAGE_TAG

동일한 이미지 태그를 사용하더라도 --platform 옵션에 따라 서로 다른 아키텍처의 컨테이너가 실행되는 것을 확인할 수 있습니다.
이번 실습을 통해 Docker에서 멀티 아키텍처 이미지를 구성하는 전체 과정을 확인할 수 있었습니다.
기존에는 CPU 아키텍처(x86, ARM 등)가 다른 환경에서는 동일한 이미지를 사용할 수 없었지만, buildx와 manifest를 활용하면 하나의 이미지 태그로 여러 플랫폼을 동시에 지원할 수 있습니다.
이러한 방식은 클라우드 환경, CI/CD 파이프라인, 그리고 다양한 디바이스를 대상으로 하는 서비스에서 매우 중요한 요소이며, 단일 이미지 태그로 다양한 실행 환경을 지원할 수 있다는 점에서 운영 효율성과 확장성을 크게 향상시킬 수 있습니다.