본문 바로가기

About/Docker

[Docker] Docker Buildx를 통한 Multi-architecture 이미지 빌드(x86, ARM)

최근 ARM 기반 프로세서(AWS Graviton, Apple M1 등) 사용이 증가로 인하여 Multi-architecture 빌드가 중요해지고 있습니다. (특히 Apple M1 칩의 등장으로 ARM 기반 프로세서에서 동작하는 프로그램을 개발하는 엔지니어가 증가할 것이라 예측됩니다..)

기존의 서버는 보통 x86 또는 AMD CPU를 사용하여 실행하였고, 동일한 환경에서 개발된 프로그램이라면 배포하는 것에는 아무 문제가 없습니다. 하지만 ARM 환경에서 개발된 프로그램을 AMD 환경에 배포하거나, AMD 환경에서 개발된 프로그램을 ARM 환경에 배포한다면 문제가 생길 수 있습니다. 

 

Docker buildx를 이용하여 Multi-architecture에 배포할 수 있는 컨테이너 이미지를 빌드하는 방법에 대하여 알아보겠습니다. 

 

기존의 Multi-Architecture Image Build

기존의 Docker를 이용한 Multi-architecture 이미지의 빌드 절차는 다음과 같습니다.

- x86에서 동작하는 도커 이미지 작성

1
2
3
4
$ docker build \
 -f Dockerfile.x86 \
 -t my-image:linux-x86 \
 .​
cs

 

- arm에서 동작하는 도커 이미지 작성

1
2
3
4
$ docker build \
 -f Dockerfile.arm \
 -t my-image:linux-arm \
 .​
cs

 

- docker manifest

1
2
3
$ docker manifest create my-image:tag \
  my-image:linux-x86 \
  my-image:linux-arm64​
cs

 

위 작업은 꽤나 번거롭고 귀찮습니다..(시간이 오래걸리겠죠..)

 

Docker Buildx

docker buildx

Docker에서는 다중 아키텍처를 빌드하는 새로운 방법을 발표하였고 이것이 docker buildx 입니다. 
아직은 실험적인 단계이나 ARM을 사용한 서비스가 늘어나고 있어 더욱더 중요해질 것이라 여겨집니다.

 

Buildx는 여러 다른 플랫폼 용으로 빌드하는 기능 등을 포함하는 CLI 확장 플로그인으로 docker 19.03버전 부터 사용할 수 있습니다.

맥북 에어(M1), 도커 20.10.8 버전에서 실행하였습니다.

 

 

Buildx를 이용한 Multi Architecutre 이미지 빌드

우선 간단한 node 서버를 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// server.js
const http = require('http');
 
const hostname = '0.0.0.0';
const port = 8080;
 
const server = http.createServer((req, res) =>{
    res.statusCode = 200;
    res.setHeader('Content-Type''text/plain');
    res.end(`Hello from node.js in ${process.arch}!`);
});
 
server.listen(port, hostname, () =>{
    console.log(`Server ruuning at http://${hostname}:${port}/`) ;
})
cs

 

http 모듈을 이용한 간단한 서버로, response의 ${process.arch} 를 이 어플리케이션 컨테이너를 실행하는데 어떤 프로세서 아키텍처가 사용되었는지 알려줍니다.

그 후 Dockerfile을 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
# Dockerfile
 
FROM node:12
WORKDIR /usr/src/app
 
COPY package*.json ./
RUN npm install
 
COPY server.js ./
 
CMD ["server.js"]
cs

 

Dockerfile에서는 server.js(위의 서버파일)을 복사하여 실행합니다.

(server.js와 Dockerfile을 같은 디렉토리에 위치시킵니다.)

이후  CLI에서 docker login을 통하여 로그인을 먼저합니다.

1
2
3
4
$ docker login
Username: ...
Password:...
Login Succeeded
cs

 

docker buildx ls 를 입력하면 기본 빌더가 출력되고, PLATFORMS에 사용 가능한 플랫폼이 출력됩니다.

1
2
3
4
5
6
$ docker buildx ls
NAME/NODE       DRIVER/ENDPOINT STATUS  PLATFORMS
desktop-linux   docker
  desktop-linux desktop-linux   running linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default *       docker
  default       default         running linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
cs

 

docker buildx create를 이용하여 새로운 빌더를 생성합니다.

1
$ docker buildx create --name mybuilder
cs

 

그 후 docker buildx use를 통해 새로운 빌더를 사용하도록 합니다.

1
$ docker buildx use mybuilder
cs

 

마지막으로 docker buildx inspect를 이용하여 빌더를 조사하여 올바르게 구성되었는지 확인합니다.

1
$ docker buildx inspect --bootstrap
cs

 

--bootstrap옵션은 빌더를 검사하기전에 빌더가 부팅되었는지 확인하는 옵션입니다.

docker buildx inspect 결과

 

그 후 Docker Hub에서 빌드한 이미지를 저장할 저장소를 하나 만들어줍니다.

저는 multi-arch-test라는 이름으로 생성하였습니다.

이미지를 저장할 리포지토리 생성

 

이제 buildx를 이용하여 이미지를 빌드합니다.

1
2
3
4
$ docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
-t mhmhm3/multi-arch-test:latest \
--push .
cs

 

--platform 옵션을 통하여 빌드할 아키텍처를 지정합니다(위에서는 amd64, arm64, arm/v7)

--push 옵션은 이미지와 매니페스트를 자동으로 푸시한다는 의미입니다.

docker buidx build 경과 (빨간색 네모에서 병렬로 이미지 생성되는 것을 확인)

 

이미지 빌드가 완료되면 도커 허브에 다음과 같이 다양한 아키텍처에 대하여 이미지가 생성된 것을 확인할 수 있습니다.

 

생성된 이미지들

 

이제 도커 컨테이너를 실행합니다.

호스트의 아키텍처를 도커가 자동으로 감지하여 적절한 이미지를 다운로드 받아 실행됩니다.

1
$ docker run --8084:8080 mhmhm3/multi-arch-test
cs

 

컨테이너 실행 후 해당 포트에 요청한 결과

 

node.js에서 arm64에서 실행되었다고 하네요. 성공입니다. 

docker buildx를 이용하여 3가지의 플랫폼의 이미지를 생성하였고, M1 칩에 맞는 이미지 파일이 실행된 것입니다.

DIGEST를 이용하여 다른 이미지들도 다운로드 받아보았는데,

1
2
3
4
5
6
$ docker run -d \
-8080:8080 \
--name multi-arch-test-arm-v7 \
mhmhm3/multi-arch-test@sha256:39ef9a8102b6d791339754d98394220875f0cf6ea59db90247b2fc307da4eaa3
WARNING: The requested image's platform (linux/arm/v7) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
43b942a254dafdd51c6423a59586b3465c5c64d79483b30f1f39575e4579235e
cs

 

위의 경고문구 보이시나요? 

WARNING: The requested image's platform (linux/arm/v7) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 43b942a254dafdd51c6423a59586b3

465c5c64d79483b30f1f39575e4579235e

 

host(arm64/v8)와 이미지(arm/v7)의 아키텍처이 일치하지 않는다고 경고합니다. 꽤나 친절하네요..

Buildx의 주의할 점 

  • 아직은 실험단계에 가깝다고 합니다.
  • 모든 base image가 멀티 아키텍처 빌드를 지원하지 않습니다. 
    위에 사용된 node 이미지의 경우 멀티 아키텍처 빌드를 지원하였기 때문에 실행이 가능했던 것입니다.
  • 멀티 아키텍처 빌드를 지원하더라도 모든 아키텍처에 대해 구축되는 것은 아닙니다. 
    원하는 아키텍처를 지원하지 않는 경우 직접 구축해야 합니다.
  • buildx 보다 기존의 방법이 더 빠를 수도 있습니다..(정보의 부족, 지원 이미지의 부족 등등..)

Docker buildx에 대하여 알아보았습니다.

참고