Motivation
•
개발자의 CPU architecture가 amd64(e.g. intel, AMD CPU)와 arm64(e.g. M1 CPU)로 나뉘어 있어 두 platform 모두에서 build / run이 되도록 해야하는 상황
•
KrakenD API Gateway가 실행되는 CPU architecture는 arm64 기반임(graviton). 그간 사용되던 amd64와는 달리
Summary
•
Cross platform build를 위한 방법으로는 Host machine에서의 build 또는 해당 platform 기반 Docker에서의 build 의 두 가지 방법이 있음
•
Host machine에서의 build는 빠른 반면, 빌드환경 이식성이 떨어지며 Docker에서의 build는 이와는 반대임.
•
그런데, docker의 신규 기능인 buildx 를 통해 docker 자체 기능만으로 ‘빠르게’ cross platform 빌드 가능. 따라서, ‘빌드환경 이식성’과 ‘빠른 빌드’의 장점 모두를 얻을 수 있음
•
하지만 Go언어의 plugin build는 (현재까지는) cross platform build 검토 진행 중
Host machine에서의 build
•
방법 : host machine에서 그대로 build를 이루되, GOOS=${GOOS} GOARCH=${GOARCH} go build ... 와 같이 go build 명령 앞에 GOOS, GOARCH Go 언어 환경변수를 추가. 다음은 linux/arm64 대상 krakend 빌드 명령 예
#in host machine makefile...
GOOS=linux GOARCH=arm64 go build -ldflags="-X github.com/luraproject/lura/v2/core.KrakendVersion=${VERSION} \
-X github.com/luraproject/lura/v2/core.GoVersion=${GOLANG_VERSION} \
-X github.com/luraproject/lura/v2/core.GlibcVersion=${GLIBC_VERSION} ${EXTRA_LDFLAGS}" \
-o krakend ./cmd/krakend-ce
Bash
복사
•
장점 : cross platform 빌드에도 빠른 build 가능
•
단점 : build 환경이 해당 host machine에 종속되어, docker 상 build 대비 빌드환경 이식성이 저하됨
Docker에서의 build
•
방법 : build를 이루는 Docker container를 해당 platform용으로 사용. 다음은 linux/arm64 platform을 지정한 Dockerfile 예 (go언어가 설치된 alpine linux 사용)
#in Dockerfile...
FROM golang:1.19.3-alpine:3.16
...
Docker
복사
•
장점 : docker에서 수행하므로 Host machine에서 build 대비 ‘빌드환경 이식성’이 좋음
•
단점 : build 수행 성능이 ‘상당히’ 떨어짐. 5분 이내에 완료된 Host machine 상에서의 빌드 대비 50분 이상 걸리는 경우 발생(krakend CE 빌드의 경우)
Fast build in Docker : Cross-compilation build
Docker 상에서의 build 옵션의 단점을 제거한 방법으로, 해당 옵션의 장점인 ‘빌드환경 이식성’과 ‘빠른 빌드’ 모두를 가능하게 함
Cross-compilation build를 적용하면, 그래프 상 1/4 이상으로 build duration이 줄어들며(중앙의 그래프 참조), cache까지 적용하면 극단적으로 build time이 줄어듦(마지막 그래프 참조). cache 적용은 여기서 다루지 않음(논의 범위 밖).
이미지 출처 : https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
빠른 빌드 어떻게 : Docker 상에서의 build 가 느린 원인 및 해결안
•
cross platform 기반 Docker에서의 build는 QEMU 란 emulator 기반하에서 동작. 따라서, build 역시 emulation 기반으로 동작하므로 태생적으로 느릴 수밖에 없음
•
Cross-compilation build : 이 때, Docker에 새로 추가된 Moby Buildkit 엔진 기반의 buildx instruction과 Dockerfile로의 특정 환경 변수 설정을 통해, host machine에서 동작하는 binary를 사용할 수 있으며 해당 binary는 주로 compiler이기에 이를 활용
x86 host machine에서의 emulation 기반과 Cross-compilation의 차이. Cross-compilation의 경우, build를 x86기반 binary로 진행
이미 출처 : https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
Cross-compilation build 적용 방법
1.
docker build 시 buildx instruction 적용
•
기존 docker instruction을 그대로 사용하되, buildx를 추가
•
동작 내용 : linux/arm64, linux/amd64 두 platform에 대한 image 둘을 동시 생성 후, 단일 tag로 병합하여 해당 tag(krakend-ce:2.1.3)에 대한 manifest를 생성 및 remote repository에 PUSH(—push)
•
다음은 krakend-ce의 예로, makefile 중 일부
docker buildx build --push --platform linux/amd64,linux/arm64 -f Dockerfile-ce ... -t krakend-ce:2.1.3
Bash
복사
2.
Dockerfile 내에 Docker 자체의 build용 argument 적용
•
•
다음은 krakend-ce의 예로, Dockerfile 중 일부. BUILDPLATFORM, TARGETOS, TARGETARCH 가 사용됨.
◦
host machine이 linux/amd64 platform이며, 생성 대상 platform이 linux/arm64일 경우,
◦
BUILDPLATFORM은 linux/amd64, TARGETOS는 linux, TARGETARCH는 arm64로 대치됨
#multi stage 기반으로 build stage에서는 host machine의 platform으로 동작,
FROM golang:1.19.3-alpine:3.16 AS builder
...
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build ...
...
#runtime stage에서는 생성 대상 platform으로 동작
FROM alpine:3.16
...
Docker
복사
Go 언어의 plugin build에 관하여
•
Go 언어 plugin의 cross platform build는 go build 명령 시 C cross compiler 지정이 필요하며(e.g. CC=gcc), 이는 go build 엔진이 C code를 호출하는 C-Go를 요구하기 때문임(CGO_ENABLED=1)
Conclusion
•
Docker의 Cross-compilation build를 사용하면 docker의 ‘빌드환경 이식성’의 장점과 host machine에서의 ‘빠른 빌드’ 장점을 모두 취할 수 있음.