Go Docker 경량화
•
기존 Dockerfile의 경우 우분투 이미지를 사용하고 있으며 go build후 실행 파일을 우분투에 올려 실행합니다.
•
하지만 이렇게 할 경우 우분투의 기능을 사용하지 않음에도 무거운 우분투 이미지를 이용해 빌드하기에 비효율적이라고 생각되었습니다.
Image Options
•
Golang의 docker 이미지는 대표적으로 아래와 같이 5가지 정도입니다.
◦
Debian 11 (Bullseye)
◦
Alpine 3.16
◦
Distroless
◦
BusyBox
◦
Scratch
이미지 용량 비교
•
기본적으로 scratch는 timezone과 CA certificates가 포함되어 있지 않기에 정확한 비교가 될 수 없습니다.
•
BusyBox, Scratch-advanced 및 Distroless는 용량이 3.06MB와 4.17MB 사이에 위치하여 매우 비슷합니다. 특히 컨테이너에 연결하여 다른 도구를 설치하지 않을 경우(예: 디버깅 목적) 안전한 이미지들 입니다.
•
Debian은 가장 큰 이미지이며 개발 목적과 테스트, 컨테이너 내부에 다른 도구를 배치하려는 경우에 좋을 수 있습니다.
•
Alpine은 가장 작은 세 개의 이미지와 Debian 사이에 좋은 절충안을 제공합니다.
scratch
•
여러 이미지들 중에서 가장 경량화가 잘 되어 있는 scratch를 선택했습니다.
◦
scratch - 텅 비어있는 이미지로, 베이스 이미지 또는 super minimal image를 만들기에 유용하다고 합니다.
•
An explicitly empty image, especially for building images "FROM scratch".
This image is most useful in the context of building base images (such as debian and busybox) or super minimal images (that contain only a single binary and whatever it requires, such as hello-world).
Golang Build 환경 변수
•
GO11MODULE=on
◦
빌드 중에 $GOPATH 대신 모듈(go.mod)에 있는 패키지를 사용합니다.
•
CGO_ENABLED=0
◦
cgo를 사용하지 않습니다. Scratch 이미지에는 C 바이너리조차 없기 때문에, 반드시 cgo를 비활성화 후 빌드해야합니다.
•
GOOS=linux GOARCH=amd64
◦
OS와 아키텍쳐 설정입니다.
•
a
◦
모든(all) 의존 패키지를 cgo를 사용하지 않도록 재빌드합니다.
•
Dockerfile
기존 Dockerfile
FROM ubuntu:latest
ADD ./deploy/ /
RUN apt-get update \
&& apt-get install -y apt-transport-https curl
ARG COMMIT_SHA
ENV COMMIT_SHA ${COMMIT_SHA}
RUN chmod +x /product
EXPOSE 8080
ENTRYPOINT ["/product"]
Docker
복사
경량화된 Dockerfile
•
go.mod 파일과 main.go 파일의 위치에 따라 경로 수정이 필요합니다.
############################
# STEP 1 build executable binary
############################
FROM golang:1.20-alpine AS build
# Install git + SSL ca certificates.
## Git is required for fetching the dependencies.
## Ca-certificates is required to call HTTPS endpoints.
RUN apk update && apk add --no-cache git ca-certificates tzdata && update-ca-certificates
# Set build environment
ARG wavve_env
ARG GITLAB_USER
ARG GITLAB_PASSWORD
# Set Golang Env
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
wavve_env=${wavve_env} \
GITLAB_USER=${GITLAB_USER} \
GITLAB_PASSWORD=${GITLAB_PASSWORD}
RUN echo "machine gitlab.wavve.com login ${GITLAB_USER} password ${GITLAB_PASSWORD}" > ~/.netrc && chmod 600 ~/.netrc
WORKDIR /go-app
COPY . .
# Fetch dependencies.
## Using go get.
## RUN go get -d -v
## Using go mod.
## go.mod must be in WORKDIR
## WORKDIR /go-app/{go.mod path}
RUN go mod download
RUN go mod verify
# Build the binary.
## main.go must be in WORKDIR
## WORKDIR /go-app/{main.go path}
RUN go build -a -ldflags="-w -s" -o demo .
RUN rm -rf ~/.netrc
############################
# STEP 2 build a small image
############################
FROM scratch
# Import from builder.
COPY /usr/share/zoneinfo /usr/share/zoneinfo
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
WORKDIR /go-app
# Copy our static executable
COPY /go-app .
# Open Port
EXPOSE 8080
# Run the binary.
ENTRYPOINT ["./demo"]
Docker
복사
경량화 결과
•
member - 기존 Dockerfile로 빌드한 결과입니다.
•
member-light - 경량화한 Dockerfile로 빌드한 결과입니다.
•
기존 대비 약 83% 경량화 되었습니다.
◦
166.04 MB → 28.17 MB
Docker에서 Private Repo에 있는 Go Modules 설치하기
•
Private Repo를 가져오기 위해 설정하는 부분입니다. 아래 부분만 수정해주시면 됩니다!
◦
gitlab-ci-token와 $CI_JOB_TOKEN 는 Gitlab CI에서 삽입해주는 정보입니다.
docker build --build-arg GITLAB_USER=gitlab-ci-token --build-arg GITLAB_PASSWORD=$CI_JOB_TOKEN -t $COMMIT_IMAGE_TAG .
Bash
복사
Makefile
•
Go 실행과 Docker 실행의 편의성을 위해 작성한 Makefile입니다.
# import config.
# You can change the default config with `make cnf="config_special.config" build`
cnf ?= docker.config.env
include $(cnf)
export $(shell sed 's/=.*//' $(cnf))
# HELP
# This will output the help for each task
# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
.PHONY: help
help: ## This help.
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.DEFAULT_GOAL := help
# DOCKER TASKS
# Build the container
build-dev: ## Build the container
docker build --build-arg wavve_env=dev --build-arg GITLAB_USER=${GITLAB_USER} --build-arg GITLAB_PASSWORD=${GITLAB_PASSWORD} -t $(APP_NAME) .
build-prod: ## Build the container
docker build --build-arg wavve_env=prod --build-arg GITLAB_USER=${GITLAB_USER} --build-arg GITLAB_PASSWORD=${GITLAB_PASSWORD} -t $(APP_NAME) .
build-nc: ## Build the container without caching
docker build --no-cache -t $(APP_NAME) .
run: ## Run container on port configured in `docker.config.env`
docker run -i -t --rm --env-file=./docker.config.env -p=$(PORT):$(PORT) --name="$(APP_NAME)" $(APP_NAME)
up-dev: build-dev run ## Run container on port configured in `docker.config.env` (Alias to run)
up-prod: build-prod run ## Run container on port configured in `docker.config.env` (Alias to run)
stop: ## Stop and remove a running container
docker stop $(APP_NAME); docker rm $(APP_NAME)
# LOCAL RUN
local-dev:
wavve_env=dev go run . ### Run go application in local with dev configure
local-prod:
wavve_env=prod g go run . ### Run go application in local with prod configure
# RUN TEST
clean-test-cache:
go clean -testcache
test-run:
wavve_env=dev go test -v ./app/service/
test-e2e-run:
wavve_env=dev go test -v ./__test__
test: clean-test-cache test-run
test-e2e: clean-test-cache test-e2e-run
# HELPERS
# generate script to login to aws docker repo
CMD_REPOLOGIN := "eval $$\( aws ecr"
ifdef AWS_CLI_PROFILE
CMD_REPOLOGIN += " --profile $(AWS_CLI_PROFILE)"
endif
ifdef AWS_CLI_REGION
CMD_REPOLOGIN += " --region $(AWS_CLI_REGION)"
endif
CMD_REPOLOGIN += " get-login --no-include-email \)"
# login to AWS-ECR
repo-login: ## Auto login to AWS-ECR unsing aws-cli
@eval $(CMD_REPOLOGIN)
version: ## Output the current version
@echo $(VERSION)
Makefile
복사
docker.config.env
•
Makefile에서 사용될 환경 변수들입니다.
•
CI를 통해 Gitlab 인증정보를 넣을 수 없는 경우 GITLAB_USER과 GITLAB_PASSWORD를 입력해주세요.
# You have to define the values in {}
APP_NAME=member-voc
# optional aws-cli options
# AWS_CLI_PROFILE={aws-cli-profile}
# AWS_CLI_REGION={aws-cli-region}
# Gitlab authorization options
# GITLAB_USER={GITLAB_USER}
# GITLAB_PASSWORD={GITLAB_PASSWORD}
# Port to run the container
PORT=8080
Makefile
복사
•
make 명령어를 통해 간단하게 빌드 및 실행을 할 수 있습니다.
◦
make build-dev
◦
make up-dev
◦
… 등등
◦
자세한건 위 Makefile 스크립트 확인해주세요.