grep

DevOps

Docker Buildx로 Multi-Archtecture Image 빌드하기

NHN

2020년 10월 5일

원문에서 보기 ↗

뭘 하려고 했나?

1.png 최근 구글 클라우드 프로젝트에 쿠버네티스 클러스터를 생성했다가 과금 폭탄을 맞고 나서, 금전적 비용없이 멀티 노드 클러스터를 실습할 수 있는 환경을 구성하기 위해 집에서 놀고있는 네 대의 개발 보드 ordroid-xu4 활용해보기로 했습니다.

뭐가 안되었나?

Docker를 세팅하는 과정에서 문제가 있었습니다. Docker를 설치하고 테스트하기 위해 간단히 빌드한 이미지를 pull 받아서 컨테이너를 실행하면 아래와 같이 실패했습니다.

jaeeunyoo@skynet-201:~$ docker pull frontiersofme/py-app
Using default tag: latest
latest: Pulling from frontiersofme/py-app
...
Status: Image is up to date for frontiersofme/py-app:latest
docker.io/frontiersofme/py-app:latest
jaeeunyoo@skynet-201:~$ docker run -it -p 8081:8081 frontiersofme/py-app
standard_init_linux.go:211: exec user process caused "exec format error"

왜 안되었나?

위 에러 메세지를 검색하면, shebang을 쓰지 않아서, 실행 권한이 없어서 등 여러 견해가 있으나 제 상황에서는 빌드된 이미지의 실행 환경(cpu 아키텍쳐)가 다르기 때문이었습니다.(xu4는 arm32v7)

2.png

hello-world는 왜 될까?

jaeeunyoo@skynet-201:~$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

그런데 hello-world 예제는 아래와 같이 잘 실행이 되었는데요. github에 공개되어있는 hello-world 프로젝트를 살펴보면, Dockerfile에 major한 아키텍쳐에 대해 cross-compile 하는 것을 확인했습니다,

# explicitly use Debian for maximum cross-architecture compatibility
FROM debian:buster-slim

RUN set -eux; \
	apt-get update; \
	apt-get install -y --no-install-recommends \
		ca-certificates \
		gnupg dirmngr \
		wget \
		\
		gcc \
		libc6-dev \
		make \
		\
		libc6-dev-arm64-cross \
		libc6-dev-armel-cross \
		libc6-dev-armhf-cross \
		libc6-dev-i386-cross \
		libc6-dev-mips64el-cross \
		libc6-dev-ppc64el-cross \
		libc6-dev-s390x-cross \
		\
		gcc-aarch64-linux-gnu \
		gcc-arm-linux-gnueabi \
		gcc-arm-linux-gnueabihf \
		gcc-i686-linux-gnu \
		gcc-mips64el-linux-gnuabi64 \
		gcc-powerpc64le-linux-gnu \
		gcc-s390x-linux-gnu \
		\
		file \
	; \
	rm -rf /var/lib/apt/lists/*
    ...

이 밖에도 이기종 디바이스에서 실행시키기 위한 빌드 방법은 이 밖에도 여러가지 있었습니다만 임베디드 SW개발자가 아닌 이상 주된 관심사가 아닐 뿐만 아니라 더 나은 빌드 및 배포를 위해 Docker를 사용하려는 입장에서는 꽤나 번거롭다는 생각이 들었습니다.

Docker Buildx 소개

포기하려던 찰나에 Docker Buildx를 알게되었는데요, Buildx는 여러 다른 플랫폼 용으로 빌드하는 기능 등을 포함하는 CLI 확장 플러그인으로 19.03 버전 부터 사용할 수 있습니다. 아직은 experimental feature로 제공되고 있기 때문에 사용하려면 해당 기능을 직접 활성화 해야합니다.

실제 사용

MacOS 기준으로 작성되었습니다.

Docker Version 확인

NHNEntui-MacBook-Pro-45:~ nhnent$ docker --version
Docker version 19.03.12, build 48a66213fe

19.03 부터 적용되므로 이하 버전에서는 사용할 수 없습니다.

Docker CLI Experimental 활성화

MacOS 3.png

# 활성화 전
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx
docker: 'buildx' is not a docker command.
See 'docker --help'

# 활성화 후
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx

Usage:	docker buildx COMMAND

Build with BuildKit

Management Commands:
  imagetools  Commands to work on images in registry

Commands:
  bake        Build from a file
  build       Start a build
  create      Create a new builder instance
  inspect     Inspect current builder instance
  ls          List builder instances
  rm          Remove a builder instance
  stop        Stop builder instance
  use         Set the current builder instance
  version     Show buildx version information

Run 'docker buildx COMMAND --help' for more information on a command.

Builder Instance 확인

NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS  PLATFORMS
default * docker
  default default         running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6 들에 대해 빌드가 제공됩니다.

Builder 생성 및 사용 설정

docker buildx create --name [builder instance 명] --driver [드라이버 이름] --use

# multi-arch-builder로 사용 설정
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx create --name multi-arch-builder --driver docker-container --use
multi-arch-builder

# 현재 Builder Instance 정보
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx inspect --bootstrap
[+] Building 6.0s (1/1) FINISHED
 => [internal] booting buildkit                                                                                                                                                                                                                                            6.0s
 => => pulling image moby/buildkit:buildx-stable-1                                                                                                                                                                                                                         5.0s
 => => creating container buildx_buildkit_multi-arch-builder0                                                                                                                                                                                                              1.0s
Name:   multi-arch-builder
Driver: docker-container

Nodes:
Name:      multi-arch-builder0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

Buildx로 Image Build & Push

NHNEntui-MacBook-Pro-45:multiarch nhnent$ docker buildx build --platform linux/arm/v7 -t frontiersofme/multiarch-py-app --push .
[+] Building 105.2s (8/8) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                       0.0s
 => => transferring dockerfile: 125B                                                                                                                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                            0.0s
 => [internal] load metadata for docker.io/library/python:3.8                                                                                                                                                                                                             12.7s
 => [internal] load build context                                                                                                                                                                                                                                          0.0s
 => => transferring context: 28B                                                                                                                                                                                                                                           0.0s
 => [1/3] FROM docker.io/library/python:3.8@sha256:2c1045587e4452d49544c6dce92efe21c3b4b33864cfb56fdee66a2c8585c769                                                                                                                                                       39.4s
 => => resolve docker.io/library/python:3.8@sha256:2c1045587e4452d49544c6dce92efe21c3b4b33864cfb56fdee66a2c8585c769                                                                         ... 

target 보드 Image Pull & 컨테이너 실행

# Image Pull
jaeeunyoo@skynet-201:~$ docker pull frontiersofme/multiarch-py-app
Using default tag: latest
latest: Pulling from frontiersofme/multiarch-py-app
e7cf402ee4b1: Already exists
cff6bd4a89fc: Already exists
e48f5852a753: Already exists
e58a93b33dd4: Already exists
a3c3db37cc57: Already exists
2c8297f1a358: Already exists
6d14eca818f3: Already exists
704121b65f11: Already exists
6cdf5a94358e: Already exists
9f9eec9ff6d5: Pull complete
a54ba2fcc3f8: Pull complete
Digest: sha256:3b46e7ef76d3512d31ddef962b07a5d46f999a9e10b7367d3548112b34af2cd7
Status: Downloaded newer image for frontiersofme/multiarch-py-app:latest
docker.io/frontiersofme/multiarch-py-app:latest

# 컨테이너 실행
jaeeunyoo@skynet-201:~$ docker run -it -p 10001:10001 frontiersofme/multiarch-py-app
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:10001/ (Press CTRL+C to quit)
192.168.0.3 - - [27/Aug/2020 23:47:25] "GET / HTTP/1.1" 200 -
192.168.0.3 - - [27/Aug/2020 23:47:25] "GET / HTTP/1.1" 200 -
192.168.0.3 - - [27/Aug/2020 23:47:25] "GET /favicon.ico HTTP/1.1" 404 -
..

마무리

지금은 실험적 기능정도로 제공되고 있으나 해당 기능에 대한 수요가 매우 많고, 실제로 저전력 고성능 ARM 64bit 기반 인스턴스를 제공하는 서비스도 늘어나고 있기 때문에 곧 정식 기능으로 포함되길 기대해봅니다.