[Docker] Multi-architecture build

2023. 3. 7. 17:02Tools/Docker

1. 멀티 플랫폼을 지원하는 이미지 생성하기

 

 

Multi-platform images

 

docs.docker.com

 

 

[Docker] Buildx로 cross-platform 이미지 빌드하기

Buildx Docker는 multi-architecture 빌드 등, 다양한 빌드 옵션을 지원하는 CLI 플러그인을 제공합니다. Buildx는 19.03 이후 버전부터 사용이 가능하다고 하니 버전 확인이 필요합니다. 공식 문서에 따르면,

velog.io

 

 

멀티 플랫폼 빌드를 위한 Docker Buildx

개요 최근 Apple M1 기반의 맥북의 등장, AWS 그라비톤 서버군의 등장으로 ARM64 기반의 서버들을 지원하기 위한 도커 이미지 빌드가 필수로 되가고 있다. 따라서 이를 위한 방법으론 크게 2가지가 있

gurumee92.tistory.com

 


2. 문제

 

Macbook air m1에서 빌드한 이미지는 다른 CPU 아키텍처를 사용하는 컴퓨터에서는 동작하지 않는다.

 

그래서 이미지를 빌드할 때부터 다양한 플랫폼에서 지원되도록 빌드해야 한다.

 

이때 사용할 수 있는 것이 buildx다.

 


3. Build platform

 

먼저 Build instance을 만든다.

 

다음을 입력한다.

docker buildx create --name multiarch-builder --use

docker buildx inspect --bootstrap 명령어를 치면, 현재 buildxuse하는 빌더가 지원하는 플랫폼들을 확인할 수 있다.

 

docker buildx inspect --bootstrap
Name:   multiarch-builderDriver: docker-containerNodes:Name:      multiarch-builder0Endpoint:  unix:///var/run/docker.sockStatus:    runningPlatforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6

 


4. 부족한 Platform 추가하기

 

 

[Docker] Buildx로 cross-platform 이미지 빌드하기

Buildx Docker는 multi-architecture 빌드 등, 다양한 빌드 옵션을 지원하는 CLI 플러그인을 제공합니다. Buildx는 19.03 이후 버전부터 사용이 가능하다고 하니 버전 확인이 필요합니다. 공식 문서에 따르면,

velog.io

추천 자료 : https://rlxuc0ppd.toastcdn.net/presentation/[NHN FORWARD 2020]Docker ARM 되고 말고.pdf ← 꼭 읽어보기를 권합니다.

이래서 좋은 회사에 취직해야 해.

 

도커에서는 멀티 플랫폼 이미지를 빌드하는 3가지 방법을 제공한다.

  1. Using the QEMU emulation support in the kernel
  2. Building on multiple native nodes using the same builder instance
  3. Using a stage in Dockerfile to cross-compile to different architectures

 

공홈 기준 QEMU을 사용한 방법을 권장한다.

 

Dockerfile을 별다른 수정 없이 BuildKit이 자동으로 사용가능한 secondary architectures를 찾아 사용할 것이다.

 

BuildKit은 빌드하기 원하는 secondary architectures의 바이너리가 필요하다.

 

이 바이너리는 binfmt_misc handler에 등록된 경우 자동으로 로드된다.

 

Docker Decktop을 사용 중이라면 docker buildx ls 명령어를 실행해 Docker Desktop에서 제공하는 플랫폼들의 종류를 확인할 수 있다.

 

docker buildx ls

추가적으로 필요한 플랫폼의 바이너리는 binfmt을 사용하여 binfmt_misc handler에 추가할 수 있습니다.

 

docker run --privileged --rm tonistiigi/binfmt --install all
# all : 모두 설치

 

추가 설명

 

 

ARM64를 Target arch를 해서 빌드하는 상황이다.

 

QEMU는 크로스플랫폼을 지원하는 하이퍼바이저의 일종이다.

 

도커는 QEMU를 사용해서 내부적으로 빌드 환경을 emulate한다.

 

Target의 Arch와 동일하게 에뮬레이션 한 뒤에 그 안에서 빌드를 수행한다.

 

  • Hipervisor : 호스트 컴퓨터에서 다수의 운영 체제를 동시에 실행하기 위한 논리적 플랫폼(platform)을 말한다. virtual machine monitor 또는 virtual machine manager, 줄여서 VMM라고도 부른다.

 

이때 QEMU가 실제 애플리케이션의 수행 범위와 상관없이 모든 시스템을 에뮬레이션 하면 리소스를 과하게 사용한다.

 

이를 방지하기 위해서 binfmt_misc를 활용한다.

 

binfmt_misc는 Binary Format Miscellaneous(기타 잡다한 이진 형식?)의 약자로 이기종 포맷의 바이너리가 실행될 때 적절한 인터프리터를 등록한다.

 

binfmt_misc는 리눅스 커널의 기능으로 임의의 실행 파일 형식을 인식하여 에뮬레이터 및 가상 머신과 같은 특정 사용자 공간 응용 프로그램에 전달할 수 있다.

 

현재 호스트 환경에서 해석할 수 없는 명령어(ex, 다른 아키텍처)를 만났을 때, 유저 스페이스에 해당 명령어를 처리할 수 있는 QEMU에게 해당 명령어를 처리하도록 한다. (필요할 때 필요한 만큼만)

 

이로서 전체를 elmulate하는 것보다 효율적으로 build를 수행할 수 있다.

 

 

  • BuildKit : BuildKit는 기존 빌더를 대체하기 위한 개선된 백엔드입니다. 이는 Dockerfile의 재사용성과 빌드 성능을 향상하는 새로운 기능을 제공한다.

 

출처 : https://ko.wikipedia.org/wiki/QEMU

출처 : https://rlxuc0ppd.toastcdn.net/presentation/[NHN FORWARD 2020]Docker ARM 되고 말고.pdf

출처 : https://en.wikipedia.org/wiki/Binfmt_misc

출처 : https://docs.docker.com/build/buildkit/

 


5. Build 수행

 

 

docker buildx build

docker buildx build: The `buildx build` command starts a build using BuildKit. This command is similar to the UI of `docker build` command and takes the same flags and arguments....

docs.docker.com

docker buildx build --tag <registry>/<image> --output type=<TYPE> .
  • image: exports the build result to a container image.
  • registry: exports the build result into a container image, and pushes it to the specified registry.

 

--output 은 옵션이라 없어도 빌드는 수행한다.

 

하지만 어디에 이미지가 위치하는지 찾기 어려울 거다.

 

아래는 대표적인 <TYPE>들이다.

  • local: exports the build root filesystem into a local directory.
  • tar: packs the build root filesystem into a local tarball.
  • oci: exports the build result to the local filesystem in the OCI image layout format.
  • docker: exports the build result to the local filesystem in the Docker image format.
  • cacheonly: doesn’t export a build output, but runs the build and creates a cache.

 

 

Local and tar exporters

 

docs.docker.com

 

 

OCI and Docker exporters

 

docs.docker.com

 


가. —platform=?

 

 

docker buildx build

docker buildx build: The `buildx build` command starts a build using BuildKit. This command is similar to the UI of `docker build` command and takes the same flags and arguments....

docs.docker.com

우리의 목표는 단순히 이미지를 빌드하는 것을 넘어서 다양한 아키텍처에서 실행가능한 이미지를 빌드하는 것이다.

지원할 architecture들을 설정하는 옵션은 --platform이다.

 

# error 발생합니다. 사용하지 마세요!
docker buildx build --platform=linux/amd64,linux/arm64 -t <registry>/<image> --load .
  • --platform=linux/arm64,linux/amd64 띄어쓰지 말자.

 

자… 상식적인 선에서 멀티 플랫폼을 지원하는 이미지를 빌드해서 로컬에 바로 이미지를 생성하고자 했다.

 

하지만 위의 명령어를 그대로 사용하면 에러가 발생할 것이다.

 

$ docker buildx build --platform=linux/amd64,linux/arm64 -t studynode:1.1-multiarch --output=type=docker .
ERROR: docker exporter does not currently support exporting manifest lists

 

빌드한 이미지는 docker images로 보기 위해서는 build를 수행할 때 --load 옵션이 필요한데 이것은 멀티 플랫폼 이미지와는 사용할 수 없다.

 

--load: This flag instructs docker to load the resulting image into the local docker images list. However, this currently only works for single-architecture images. If you try this with multi-architecture images you’ll get an export error:

 

 

docker buildx create

docker buildx create: Create makes a new builder instance pointing to a docker context or endpoint, where context is the name of a context from `docker context ls` and endpoint...

docs.docker.com

 

결과적으로 멀티 플랫폼 이미지는 특정 registrypush하거나 로컬에 파일로 export할 수밖에 없다.

 

docker buildx build --platform=linux/amd64,linux/arm64 -t studynode:1.1-multiarch --output type=local,dest=/workspaces/Study_nodeJS/ .

로컬 파일로 export하는 것은 엄청 오래 걸린다.

 

The local and tar exporters output the root filesystem of the build result into a local directory. They’re useful for producing artifacts that aren’t container images.

  • local exports files and directories.
  • tar exports the same, but bundles the export into a tarball.

 

진짜 말 그대로 그냥 파일이다.

 

 

굳이? 사용하지 말자.

 

특정한 container registry로 output해야 하는데 dockerhub이 가장 많이 사용된다.

 

하지만 굳이 Github container registry로 push해보자.

 

docker login ghcr.io -u ramen4598
docker buildx build --platform=linux/amd64,linux/arm64 -t ghcr.io/ramen4598/studynode:1.1-multiarch --push .
ERROR: failed to solve: failed to push ghcr.io/ramen4598/studynode:1.1-multiarch: unexpected status: 403 Forbidden

오류가 발생한다.

아 제발

 

 

Unable to push to ghcr.io from Github Actions · community · Discussion #26274

Hi, following the documentation at Publishing Docker images - GitHub Docs I have set up an action for a private (team) respository name: Create and publish a Docker image on: push: branches: ['...

github.com

 

 

raise `ghcr unexpected status: 403 Forbidden` when push ghcr image · Issue #687 · docker/build-push-action

Troubleshooting Behaviour We didn't change any configuration before and after this error happened, same ghcr container image had permisson issue. Steps to reproduce this issue Expected behaviou...

github.com

 

package setting을 변경해야 한다.

 

  • package settings → Actions


 


6. Bug

 

자 사실 여기서부터는 multi platform build와는 상관없다.

 

그냥 한 명의 개발자가 버그 잡는 내용이다.

 

가. bug : npm ERR! code ENOENT

npm ERR! code ENOENT
study_nodejs-studynode-1  | npm ERR! syscall open
study_nodejs-studynode-1  | npm ERR! path /app/package.json
study_nodejs-studynode-1  | npm ERR! errno -2
study_nodejs-studynode-1  | npm ERR! enoent ENOENT: no such file or directory, open '/app/package.json'
study_nodejs-studynode-1  | npm ERR! enoent This is related to npm not being able to find a file.
study_nodejs-studynode-1  | npm ERR! enoent

 

 

How to solve npm ERR! enoent This is related to npm not being able to find a file

Learn how to solve npm error code ENOENT: no such file or directory

sebhastian.com

 

Here are the steps to resolve this issue:

  • Make sure you are using the latest npm version
  • Clean your npm cache
  • Delete node_modules folder and package-lock.json
  • Run npm install again

 

# 👇 update npm to the latest version
npm install -g npm@latest

# 👇 clean npm cache
npm cache clean --force

# 👇 delete node modules and package-lock.json 
npm rm -rf node_modules && rm package-lock.json

# 👇 retry installing dependencies
npm install

 

나는 이 유형의 문제는 아니었고 docker-compose.yml에서 volumes 경로를 잘못 설정해서 발생했다.

 


나. Bug : pm2 log가 멈춘다.

 

study_nodejs-studynode-1  | 2023-03-04T08:19:16: PM2 log: [--no-daemon] Continue to stream logs
study_nodejs-studynode-1  | 2023-03-04T08:19:16: PM2 log: [--no-daemon] Exit on target PM2 exit pid=19

 

pm2는 애플리케이션을 백그라운드에 보내고 실행한다.

 

도커를 지속적으로 동작시키기 위해서 --no-daemon 옵션을 사용했다.

 

하지만 구글링을 결과 도커에서 pm2를 동작시킬 때 pm2-runtime을 사용해야 한다는 사실을 배웠다.

 

pm2-runtime : 도커 컨테이너에서 사용하는 용도로, 애플리케이션을 foreground에 유지하고 컨테이너를 계속 실행하게 함.

 

pm2-runtime start ecosystem.config.js --no-daemon
# error: unknown option `--no-daemon'

pm2-runtime으로 로그를 보기 위해서는 따로 옵션을 넣을 필요가 없다.

 

그 자체로 foreground에 위치하기에 별도의 옵션 없이도 로그를 볼 수 있다.

 

 

docker container에서 pm2가 시작하자마자 종료될 때

도커 컨테이너에서 pm2를 실행하는 데 바로 꺼질 때 node 프로젝트를 프로덕션 환경에 pm2로 올려보도록 한다. node docker image를 이용해서 docker container에서 로 시작하도록 올려본다. 하지만 갑자기

dotorimook.github.io

 


다. Bug :WORKDIR와 bind mount 위치

 

아무 생각 없이 DockerfileCOPY 작업을 수행한 WORKDIRdocker-compose.ymlvolumes (bind mount)한 디렉터리를 같게 했다.

 

조금만 생각해 보면 이렇게 하면 안 된다는 것을 알 수 있다.

 

compose를 수행하면서 로컬의 파일이 컨테이너의 파일을 완전히 덮어쓴다.

 

#Dockerfile

FROM node:18.14.2

RUN apt update && apt install -y git zsh vim

RUN npm install pm2 -g -y

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm ci -y

COPY ecosystem.config.js src ./

EXPOSE 3000

ENTRYPOINT npm start

CMD /bin/bash

기껏 수행한 WORKDIR /app, COPY package.json package-lock.json ./, RUN npm ci -y, COPY ecosystem.config.js src ./이 쓸모없어졌다.

 

되도록 DockerfileWORKDIR 하위의 어느 디렉터리에 volumes(bind mount)를 하기를 추천한다.

 


힘들어 살려줘…

'Tools > Docker' 카테고리의 다른 글

[Docker] MySQL 설치  (0) 2023.03.10
[Docker] Github와의 연동  (0) 2023.02.28
[Docker] 연습하기  (2) 2023.02.27
[Docker] Compose  (0) 2023.02.27
[Docker] 가상화된 Docker host에 접근하기  (0) 2023.02.27