My Note on Tiny Go Container with Scratch Image

วันนี้นั่งคุยกับลุงเชน (Shane เป็น Developer ชาวออสเตรเลียที่อายุมากกว่าผมราวๆ 10 ปี++ ที่ทำงานอยู่ทีมเดียวกัน) เลยนั่งคุยกับลุงแกเรื่อง Go ที่ลุงใช้เป็นหนึ่งในภาษาที่ใช้ทำ Hobby Project ของแก เลยถามว่า Image เล็กสุดที่แกทำเนี่ยไซส์เท่าไหร่ แกบอกมาว่า 5–6 MB ผมนี่เลยต้องถามแกว่าแกทำได้ยังไง ลุงแกให้ keyword มาว่า ให้ลองไปทำ fully static linked binary แล้วเอาไปรันกับ Scratch ดู

พอกลับมาบ้านปุ๊ปเลยกลับมาลองทำเลยดูละกัน เพราะว่าถ้าทำได้ก็น่าจะสนุกมากเลย เอาไปรันบนอุปกรณ์ได้หลายอย่างเลย พอเริ่มปุ๊บก็สร้าง Go server ก่อนเลยแบบง่ายสุดๆ ตามนี้

package main

import (
	"fmt"
	"log"
	"net/http"
)

func homePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Homepage !!")
	fmt.Println("Endpoint Hit: homePage")
}

func handleRequests() {
	http.HandleFunc("/", homePage)
	log.Fatal(http.ListenAndServe(":8088", nil))
}

func main() {
	handleRequests()
}

ทีนี้ก็มาถึง keyword ของลุงเชนว่าทำยังไงจะ build ให้ได้ go binary แบบ static linked ก็เปิดดูใน docs กับ stackoverflow เลยออกมาเป็นคำสั่งนี้

CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags '-w' .

โดย flag ต่างๆมีความหมายตามนี้

CGO_ENABLED=0
อันนี้เพื่อ disable ตัว CGO ซึ่งปกติจะ enable มากับตัว go binary ที่ distribute มากับแต่ละ platform เพื่อให้สามารถใช้งานกับ psuedo-package ที่เป็น C library ได้ ปกติตรงนี้ผมไม่เคยใช้อยู่แล้ว เลยไม่มีประสบการณ์กับมันเท่าไหร่

GOOS=linux
อันนี้เพื่อ cross compile เป็น binary สำหรับ linux เพราะว่าผมทำการ compile บน macOS และจะเอา binary ไปรันบน linux

-a -tags netgo
ตรงนี้เราใช้ -a เพื่อ force rebuild และใช้ tag netgo เพื่อ force ให้ใช้ net package จากตัว built-in package เท่านั้น (ใครมีข้อมูลเพิ่มว่าทำไมใช้ system package ไม่ได้ช่วยขยายความทีนะครับ)

-ldflags '-w'
และตรงนี้ เรา disable debug symbols ไปซะเพื่อให้ได้ binary ที่เล็กลง

จากนั้นด้วยความมั่นใจว่าทำงานได้แน่ๆ เราก็ไปสร้าง Dockerfile เลย

FROM scratch
MAINTAINER Mahasak Pijittum <[email protected]>
ADD mini-go-container mini-go-container
EXPOSE 8088
ENTRYPOINT ["/mini-go-container"]

เสร็จแล้วก็ build docker image ด้วยคำสั่ง

docker build -t mahasak/mini-go-container .

ด้วยความร้อนใจ ก็อยากเห็นขนาดของ image ว่าจะเล็กขนาดไหน ก็ลองดูกันนะครับ

1_pnTq2A57OahWCpqyziYdng

ปรากฎว่าออกมาดูดีทีเดียว ขนาดเพียงแค่ 4.6 MB เท่านั้น ถือว่าใช้ได้เลย ทีนี้ลองรัยดูหน่อยละกัน

docker run -d -p 8088:8088 mahasak/mini-go-container

1_rFkgPWQ6pettIMik2AccPw

Happy Coding ครับ !!!

References: