PaddleOCR과 같은 딥러닝 라이브러리는 C++로 컴파일된 바이너리에 의존하기 때문에 CPU 아키텍처(x86 vs ARM)에 매우 민감합니다. 이 점을 강조하며 docker buildx가 왜 필요한지 실무에서 발생했던 문제 상황을 어떻게 해결했는지 정리했습니다.
최근 회사에서 운영 중인 RAG(Retrieval-Augmented Generation) 서비스를 위해 FastAPI와 PaddleOCR을 사용한 OCR 서버를 Docker 컨테이너로 띄워 사용하고 있었습니다.
서비스가 안정화되면서 서버를 이전해야 할 일이 생겼는데, 여기서 예상치 못한 치명적인 문제가 발생했습니다.
"기존 서버(Intel/AMD)에서 잘 돌아가던 도커 이미지가, 새 서버(ARM/AWS Graviton/Mac M1 등)에서는 실행조차 안 된다?"
로그를 까보니 exec format error 혹은 라이브러리 로딩 실패...
원인은 바로 CPU 아키텍처의 차이(AMD64 vs ARM64) 때문이었습니다.
오늘은 이 문제를 해결하기 위해 도입한 Docker Buildx에 대해, 그리고 왜 이것이 PaddleOCR 같은 딥러닝 서비스 배포에 필수적인지 아주 자세히 기록해 둡니다.
1. 문제 상황: 아키텍처의 벽 (AMD64 vs ARM64)
PaddleOCR이 왜 문제인가?
일반적인 Python 코드(순수 파이썬 스크립트)는 OS나 CPU를 크게 타지 않습니다. 하지만 PaddleOCR이나 PyTorch, TensorFlow 같은 딥러닝 라이브러리는 다릅니다. 이들은 성능을 위해 밑단에서 C/C++로 컴파일된 바이너리를 사용합니다.
- 기존 서버:
linux/amd64(Intel/AMD CPU) - 새로운 서버:
linux/arm64(AWS Graviton, Apple Silicon, Raspberry Pi 등)
기존의 docker build 명령어는 명령어를 입력하는 호스트 머신의 아키텍처에 맞춰 이미지를 생성합니다. 즉, Intel 맥북이나 일반 데스크탑에서 빌드한 이미지는 ARM 서버에 가져가면 "이게 무슨 언어지?" 하며 실행을 거부하게 됩니다.
2. 해결책: Docker Buildx란 무엇인가?
이 문제를 해결하기 위해 등장한 것이 바로 Docker Buildx입니다.

[그림 1] Docker Buildx의 멀티 아키텍처 빌드 흐름도
기존의 빌드 방식은 호스트의 아키텍처(예: Intel)만 따라가지만, Buildx는 QEMU 에뮬레이터를 통해 가상의 빌더 인스턴스를 생성합니다.
위 그림처럼 개발자가 한 번의 빌드 명령(docker buildx build)을 내리면:
- 빌더가 AMD64(Intel/AMD)용과 ARM64(Apple Silicon/Graviton)용 이미지를 병렬로 각각 빌드합니다.
- 이 두 이미지를 하나로 묶는 Manifest List(매니페스트 리스트)를 생성합니다.
- Docker Hub와 같은 레지스트리에 업로드합니다.
결과적으로, 나중에 서버가 이미지를 당겨갈 때(pull) 자신의 CPU 아키텍처에 맞는 버전을 자동으로 식별해서 가져가게 됩니다.
docker build vs docker buildx
docker build: 현재 내가 쓰고 있는 컴퓨터의 CPU 아키텍처에 맞는 이미지만 빌드합니다. (Single-Architecture)docker buildx: Docker 19.03부터 도입된 CLI 플러그인으로, QEMU라는 에뮬레이터를 이용해 하나의 명령어만으로 여러 아키텍처(Multi-Architecture)를 지원하는 이미지를 동시에 빌드할 수 있습니다.
즉, "나는 인텔 CPU를 쓰지만, 결과물은 ARM용과 인텔용 둘 다 만들어줘!" 가 가능해집니다.
3. 실전 가이드: Buildx로 PaddleOCR 멀티 아키텍처 이미지 만들기
이제 실제로 FastAPI + PaddleOCR 이미지를 amd64와 arm64 두 환경에서 모두 돌아가게 빌드해 보겠습니다.
Step 1. 현재 빌더(Builder) 확인하기
기본적으로 도커는 default 빌더를 사용합니다. 하지만 멀티 플랫폼 빌드를 위해서는 새로운 빌더 인스턴스를 생성하는 것이 좋습니다.
# 현재 사용 가능한 빌더 확인
docker buildx ls
Step 2. 새로운 빌더 생성 및 사용 설정
멀티 플랫폼 빌드를 지원하는 새로운 빌더를 만들고, 이를 사용하도록 설정합니다.
# 'mybuilder'라는 이름의 새로운 빌더 생성
docker buildx create --name mybuilder --use
# 빌더가 제대로 생성되었는지 확인 (Status가 running인지 확인)
docker buildx inspect --bootstrap
inspect 명령어를 쳤을 때 Platforms 항목에 linux/amd64, linux/arm64 등이 모두 보여야 정상입니다.
Step 3. Dockerfile 준비 (PaddleOCR 주의사항)
PaddleOCR을 사용할 때 주의할 점은 베이스 이미지나 의존성 라이브러리가 ARM64를 지원하는지 확인해야 한다는 점입니다. 다행히 최근 Python 공식 이미지와 PaddlePaddle은 대부분의 아키텍처를 지원합니다.
# Dockerfile 예시
FROM python:3.9-slim
WORKDIR /app
# 시스템 의존성 설치 (PaddleOCR/OpenCV 구동을 위해 필요)
RUN apt-get update && apt-get install -y \
libgomp1 \
libgl1-mesa-glx \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
# 패키지 설치
COPY requirements.txt .
# PaddlePaddle과 PaddleOCR 설치 (requirements.txt에 포함되어 있다고 가정)
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Step 4. Buildx로 빌드하고 푸시하기 (핵심!)
docker buildx의 가장 큰 특징은 빌드된 이미지가 로컬의 docker images에 바로 저장되지 않고, 레지스트리(Docker Hub, ECR 등)로 바로 푸시하거나 별도의 옵션을 줘야 한다는 점입니다. 멀티 아키텍처 이미지는 하나의 태그 아래 여러 아키텍처 manifest가 묶이는 형태이기 때문입니다.
가장 간편한 방법은 --push 옵션을 사용하여 레지스트리에 바로 올리는 것입니다.
# amd64와 arm64 두 가지 플랫폼을 동시에 빌드하여 푸시
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t my-docker-hub-id/my-fastapi-ocr:v1 \
--push \
.
--platform linux/amd64,linux/arm64: 이 옵션이 마법을 부립니다. 쉼표로 구분하여 원하는 아키텍처를 나열합니다.--push: 빌드 완료 후 레지스트리에 업로드합니다.
4. 결과 확인 및 검증
빌드가 성공적으로 끝나면 Docker Hub(혹은 사용 중인 레지스트리)에 이미지가 올라갑니다. 이제 이 이미지가 정말로 멀티 아키텍처를 지원하는지 확인해 봅시다.
# imagetools를 사용하여 이미지 매니페스트 확인
docker buildx imagetools inspect my-docker-hub-id/my-fastapi-ocr:v1
출력 (Example)
Name: docker.io/my-docker-hub-id/my-fastapi-ocr:v1
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256: ...
Manifests:
# 첫 번째 아키텍처: linux/amd64
Name: docker.io/my-docker-hub-id/my-fastapi-ocr:v1@sha256:...
Platform: linux/amd64
# 두 번째 아키텍처: linux/arm64
Name: docker.io/my-docker-hub-id/my-fastapi-ocr:v1@sha256:...
Platform: linux/arm64
이렇게 두 개의 플랫폼이 모두 뜬다면 성공입니다!
이제 이 이미지를 인텔 서버에서 docker run 하면 자동으로 amd64 이미지를 당겨오고, ARM 서버에서 실행하면 arm64 이미지를 당겨옵니다.
5. 마치며: PaddleOCR와 운영체제 독립성
RAG 파이프라인 구축을 위해 PaddleOCR을 사용하면서 가장 애를 먹었던 부분이 바로 이 아키텍처 호환성이었습니다. 로컬 개발 환경(Mac M1)과 배포 서버(Linux x86)가 다를 때, 혹은 비용 절감을 위해 ARM 기반 인스턴스로 서버를 옮길 때 docker buildx는 선택이 아닌 필수입니다.
요약:
- 딥러닝 모델(PaddleOCR)은 CPU 아키텍처를 탄다.
- 서버 이동 시
Exec format error가 뜬다면 아키텍처 불일치를 의심하자. docker buildx를 사용해--platform linux/amd64,linux/arm64옵션으로 한 방에 해결하자.
이제 서버 아키텍처가 바뀌어도 두렵지 않습니다.
'DevOps > Docker' 카테고리의 다른 글
| [Docker compose - Postgres 설정] 부팅 이슈 해결하기 (Internship : Infra) (0) | 2026.01.20 |
|---|---|
| [docker-compose]온프렘 도커 배포 실전 삽질기 (네트워크, 쿠키, 헬스체크 해결편) (0) | 2025.12.31 |
| [docker-compose]온프레미스 환경에서 Docker Compose로 서비스 구성하기 (0) | 2025.12.31 |
| [Docker] 실행 방법 (0) | 2025.12.11 |
| [docker] 개념 정리 (0) | 2025.12.10 |