[Golang] 외부 라이브러리 의존없이 Go 정적 빌드(Static Build)하기

go build 명령어 실행 시 아래와 같이 지정해주면, 시스템 라이브러리 (libstdc 등)에 의존하지 않는 정적 프로그램으로 빌드할 수 있다.

CGO_ENABLED=0 GOOS=linux GOARCH=$(dpkg --print-architecture) go build -a -ldflags '-w -extldflags "-static"' -o executable_name *.go

Environment Variables

  • CGO_ENABLED=0: [필수] 외부 C 라이브러리에 의존하는 CGO를 비활성화한다.
  • GOOS=linux: linux OS를 타겟으로 빌드한다. (가능한 값 보기)
  • GOARCH=$(dpkg --print-architecture): 현재 빌드하는 시스템 아키텍쳐를 대상으로 빌드한다 (가능한 값 보기)
    • dpkg 명령어에 의존하므로 빌드하는 시스템은 Debian-based여야 한다. Ubuntu OS나 일반적인 golang latest Docker image의 경우 Debian-based이므로 문제가 없다.

Build Arguments

  • -a: 이미 빌드된 패키지라고 해도 다시 빌드한다.
  • -ldflags '-w': DWARF 디버깅 정보를 삭제(strip)한다.
  • -ldflags '-extldflags "-static"': [필수] 외부 링커 (ld)에게 정적 빌드를 진행함을 알린다.

(Optional) Extra Arguments

  • -tags netgo: 시스템 Network 라이브러리 (C-based)를 사용하지 않고 Go 내부의 Network 구현을 사용한다. CGO_ENABLED=0을 세팅하지 않을 경우 개별 라이브러리를 비활성화할 수 있는듯 하다.

Advantages

정적 프로그램으로 빌드할 경우 libc를 포함한 외부 라이브러리에 대한 의존이 없어서 단독으로 실행이 가능하다.

예를 들어, 위에서 빌드한 프로그램 ./main을 아래와 같이 Docker scratch 이미지에 넣고 단독으로 실행이 가능하다 (scratch 이미지의 경우 내용물이 빈 이미지이다.)

mkdir build
cd build
CGO_ENABLED=0 GOOS=linux GOARCH=$(dpkg --print-architecture) \
  go build -a -ldflags '-w -extldflags "-static"' -o main ../main.go

cat > Dockerfile <<EOF
FROM scratch

COPY main /main
ENTRYPOINT /main
EOF

docker build . -t my-image:latest
docker run -it --rm my-image:latest