Giới thiệu
Build docker image là một thành phần rất cần thiết mà chúng ta cần làm khi muốn containerize ứng dụng Golang. Build một docker image lớn nghĩa là chúng ta cần nhiều data hơn để di chuyển giữa image repository, CI/CD platform, và deployment server. Vì vậy việc tạo một docker image nhỏ hơn là việc làm cần thiết để tiết kiệm thời gian. Không quá khó khăn để chúng ta có thể giảm kích thước của các docker image. Đặc biệt với các ứng dụng Go, bời vì chúng đã đi kèm với file binary có nghĩa là chúng không cần bất kỳ environmental server như Nginx, Node, ...
Trong bài viết này, chúng ta sẽ học cách làm sao để có thể giảm kích thước của docker image cho ứng dụng Go bằng Docker
Golang app
Trong ví dụ này chúng ta có sample code Golang như sau:
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/julienschmidt/httprouter"
)
func main() {
r := httprouter.New()
r.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
m := map[string]interface{}{
"time": time.Now().UnixMilli(),
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(m)
})
fmt.Println("Run on port :8000")
http.ListenAndServe(":8000", r)
}
Chúng ta có thể import package "httprouter
" bằng cách sử dụng câu lệnh go mod download
trong Dockerfile
Viết Dockerfile
Giả sử đây là Dockerfile ban đầu của chúng ta
FROM golang:1.17.3-alpine3.14
WORKDIR /app
ENV GO111MODULE=on CGO_ENABLED=0
COPY go.mod go.sum /app/
RUN go mod download
COPY . .
RUN go build -o /app/main /app/main.go
CMD [ "/app/main" ]
Típ: Chúng ta nên sử dụng
alpine image
để giảm kích thước của image khi build và tiết kiệm data. Và tận dụng cache bằng cách đặt các dòng có ít sự thay đổi lên trên đầu để giảm thiểu thời gian build image.
Bây giờ chúng ta sẽ build image và gán tag cho image dựa trên Dockerfile
$ docker build -t example:initial .
$ docker images example
REPOSITORY TAG IMAGE ID CREATED SIZE
example initial 0d1bfb281019 37 seconds ago 337MB
Kích thước của image ban đầu là 337MB
, bây giờ chúng ta sẽ cải thiện nó
Dockerfile với Multistage
# base image
FROM golang:1.17.3-alpine3.14 as base
WORKDIR /builder
ENV GO111MODULE=on CGO_ENABLED=0
COPY go.mod go.sum /builder/
RUN go mod download
COPY . .
RUN go build -o /builder/main /builder/main.go
# runner image
FROM gcr.io/distroless/static:latest
WORKDIR /app
COPY --from=base /builder/main main
EXPOSE 8000
CMD ["/app/main"]
Multistage build giúp chúng ta để loại bỏ các thành phần không cần thiết trong base image và sử dụng image mới để chạy ứng dụng.
FROM golang:1.17.3-alpine3.14 as base
Có thể thấy, chúng ta có thể đặt tên (alias) cho các stage, Do đó nếu chúng ta muốn copy thứ gì từ stage đó, chúng ta có thể cần ghi tên của stage đó ở dòng COPY
.
...
# runner image
FROM gcr.io/distroless/static:latest
WORKDIR /app
COPY --from=base /builder/main main
EXPOSE 8000
CMD ["/app/main"]
Bây giờ chúng ta sẽ build lại image, và quan sát kết quả
$ docker build -t example:multistage .
$ docker images example
REPOSITORY TAG IMAGE ID CREATED SIZE
example multistage 6c54eb031f69 2 seconds ago 8.63MB
example initial 0d1bfb281019 20 minutes ago 337MB
Bây giờ kích thước của image giảm xuống còn 8.63MB
, rất đáng kể phải không nào
UPX Dockerfile
# base image
FROM golang:1.17.3-alpine3.14 as base
WORKDIR /builder
RUN apk add upx
ENV GO111MODULE=on CGO_ENABLED=0
COPY go.mod go.sum /builder/
RUN go mod download
COPY . .
RUN go build -o /builder/main /builder/main.go
RUN upx -9 /builder/main
# runner image
FROM gcr.io/distroless/static:latest
WORKDIR /app
COPY --from=base /builder/main main
EXPOSE 8000
CMD ["/app/main"]
UPX là một công cụ giúp chúng ta thu nhỏ kích thước của file binary, không chỉ dành riêng cho các ứng dụng Go. Chúng có thể cài đặt UPX ở dòng 4
và chạy lệnh UPX ở dòng 13
để tận dụng builder cache. upx -9
có nghĩa là chúng ta muốn nén tốt hơn. Ngoài ra có thể sử dụng lệnh upx -h
để xem các flags liên quan
Build lại image với tag là example:with-upx
$ docker build -t example:with-upx .
$ docker images example
REPOSITORY TAG IMAGE ID CREATED SIZE
example with-upx 0831b4ee8d1a 2 seconds ago 5.91MB
example multistage 6c54eb031f69 12 minutes ago 8.63MB
example initial 0d1bfb281019 33 minutes ago 337MB
Lần này kết quả là 5.91MB
Tận dụng Go Flags
...
RUN go build \
-ldflags "-s -w" \
-o /builder/main /builder/main.go
...
Trong Dockerfile cuối cùng này, chúng ta sẽ sửa lại câu lệnh go build
. Thêm flag -ldflags "-s -w"
để disable symbol table và DWARF generation để hỗ trợ việc tạo debugging data. Chúng ta có thể xem các tùy chọn khác thông qua lệnh go tool link -h
Build lại image với tag là example:latest
$ docker build -t example:latest .
$ docker images example
REPOSITORY TAG IMAGE ID CREATED SIZE
example latest fd81bd6268bd 1 second ago 4.16MB
example with-upx 0831b4ee8d1a 9 minutes ago 5.91MB
example multistage 6c54eb031f69 22 minutes ago 8.63MB
example initial 0d1bfb281019 42 minutes ago 337MB
Trong trường hợp này, docker image đã giảm đáng kể kích thước, chỉ còn 4.16MB
Bình luận