ในบทความนี้
เลิกติดตั้ง Go บนเครื่องโฮสต์ ไม่มี
GOPATHรก ไม่มีปัญหา "เครื่องผมใช้ได้" และไม่มีเวอร์ชันคลาดเคลื่อน นี่คือรูปแบบเดียวกับที่เราใช้เขียนเอเจนต์ Java ใหม่สำหรับรายงาน BigBlueButton Learning Analytics และสร้าง Piler Go Agent ที่ปลอดภัย ต่อไปนี้คือวิธีส่งมอบไบนารีแบบสแตติกด้วย Docker เพียงอย่างเดียว
โดย Ghazi Triki, RIADVICE
ปัญหาของคำแนะนำว่า "ติดตั้ง Go ไปเลย"
ขณะที่เราดูแลเอเจนต์ที่พัฒนาด้วย Java สำหรับรายงาน BigBlueButton Learning Analytics เราเผชิญกับ "นรกของการพึ่งพา" (dependency hell) แบบคลาสสิก การย้ายมาใช้ Go ให้คำมั่นว่าจะเรียบง่ายขึ้น แต่จะเป็นจริงได้ก็ต่อเมื่อเราไม่ทำผิดซ้ำรอยเดิม เราต้องการกระบวนการบิลด์ที่แยกขาดจากสภาพแวดล้อมภายนอก (hermetic) ได้ไม่แพ้ไบนารีที่สร้างขึ้น
ทุกทีมย่อมต้องเจออุปสรรคเหล่านี้ในที่สุด
- เวอร์ชันคลาดเคลื่อน Ahmed ใช้ 1.24, Omar ใช้ 1.26 และเซิร์ฟเวอร์ใช้งานจริงใช้ 1.25 การเปลี่ยนแปลงเล็กน้อยในคอมไพเลอร์ทำให้บิลด์ล้มเหลว แต่เกิดขึ้นกับ Ahmed เท่านั้น
- เครื่องโฮสต์รก
GOPATH,GOMODCACHEและเครื่องมือที่ติดตั้งแบบโกลบอล (staticcheck,gofumpt) แตกต่างกันไปในแต่ละเครื่อง - การรับคนใหม่เข้าทีมติดขัด วิศวกรใหม่ใช้เวลาวันแรกทั้งวันไปกับตัวแปรสภาพแวดล้อมและชุดเครื่องมือ C สำหรับ
cgo
ที่ RIADVICE เราให้คุณค่ากับวินัยและความเป็นระเบียบ เราต้องการกระบวนการบิลด์ที่สะท้อนสิ่งนั้น คือสะอาด คาดการณ์ได้ และใช้ร่วมกันได้
คำตอบมาตรฐานคือ "ใช้ Docker" แต่บทแนะนำส่วนใหญ่สอนเพียงวิธีสร้าง อิมเมจคอนเทนเนอร์ ไม่ได้สอนวิธีได้มาซึ่ง ไบนารีธรรมดาที่ลิงก์แบบสแตติกบนเครื่องโฮสต์ ซึ่งเป็นสิ่งที่คุณต้องใช้จริงสำหรับบริการ systemd หรือการปรับใช้ในเครือข่ายที่แยกขาด (air-gapped) โดยไม่ต้องติดตั้งคอมไพเลอร์ Go ไว้ในเครื่องเลย
docker run --rm -v "$(pwd):/app" -w /app golang:1.26.5-trixie \
go build -o dist/myapp ./cmd/myappอิมเมจ golang อย่างเป็นทางการทำหน้าที่เป็นคอมไพเลอร์ ซอร์สโค้ดของคุณถูกเมานต์แบบ bind ไว้ที่ /app ไบนารีจะถูกเขียนลงที่ dist/myapp บนเครื่องโฮสต์ เมื่อคำสั่งทำงานเสร็จ คอนเทนเนอร์จะถูกทำลาย (--rm) เครื่องโฮสต์ของคุณจึงสะอาดเหมือนเดิม
ประสบการณ์จริง: จาก BigBlueButton สู่ Piler
นี่ไม่ใช่แค่ทฤษฎี เราใช้รูปแบบนี้กับโครงการสำคัญสองโครงการ
- รายงาน BigBlueButton Learning Analytics: เราเขียนเอเจนต์ Java ขนาดใหญ่ใหม่เป็นไบนารี Go ที่มีขนาดเล็ก การบิลด์ด้วย Docker ทำให้เราคอมไพล์ให้ตรงกับสภาพแวดล้อม Debian ของเซิร์ฟเวอร์ BBB ได้ทุกประการ โดยไม่ต้องมีสภาพแวดล้อมนั้นบนแล็ปท็อปของเรา
- Piler Go Agent: สำหรับโซลูชันจัดเก็บอีเมลถาวรของเรา เราสร้างเอเจนต์ที่เสริมความแข็งแกร่งด้านความปลอดภัย กระบวนการบิลด์ต้องปลอดภัยพอ ๆ กับตัวโค้ด ด้วยการตรึงคอมไพเลอร์ไว้กับ SHA ที่ระบุ เราจึงมั่นใจได้ว่าทุกบิตในไบนารีตรวจสอบที่มาได้
เหตุใดวิธีนี้จึงดีกว่าการติดตั้ง Go
| ประเด็น | go install บนเครื่องโฮสต์ | docker run golang:... |
|---|---|---|
| เวอร์ชัน Go | เวอร์ชันที่ติดตั้งไว้ล่าสุด | ตรึงไว้ และเหมือนกันสำหรับทุกคน |
| GOPATH / แคช | ทำให้ ~/go รก | เลเยอร์คอนเทนเนอร์ที่แยกขาด |
| ชุดเครื่องมือ cgo | ต้องตั้งค่าบนเครื่องโฮสต์ | รวมอยู่ในอิมเมจอย่างเป็นทางการ |
| ความสอดคล้องกับ CI | "เครื่องผมใช้ได้" | อิมเมจเดียวกัน ได้ไบนารีเดียวกัน |
BINARY := mini-api
DIST := dist
# Version metadata derived from git
VERSION := $(shell tmp=$$(git describe --tags --always --dirty 2>/dev/null) && echo "$${tmp#v}" || echo dev)
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)
BUILD_DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -X example.com/mini-api/internal/version.Version=$(VERSION) \
-X example.com/mini-api/internal/version.Commit=$(COMMIT) \
-X example.com/mini-api/internal/version.Branch=$(BRANCH) \
-X example.com/mini-api/internal/version.BuildDate=$(BUILD_DATE)
# Pinned toolchain image
GO_IMAGE := golang:1.26.5-trixie
# Run any command inside the Go toolchain container
define run_in_docker
docker run --rm -v "$(PWD):/app" -w /app $(GO_IMAGE)
endef
.PHONY: build test lint
build:
@mkdir -p $(DIST)
$(call run_in_docker) bash -c 'CGO_ENABLED=0 go build -buildvcs=false -ldflags="$(LDFLAGS)" -o $(DIST)/$(BINARY) ./cmd/$(BINARY)'
@echo "Built $(DIST)/$(BINARY) (version $(VERSION))"
test:
$(call run_in_docker) go test -race ./...
lint:
$(call run_in_docker) go vet ./...Makefile: ต้นทางของวินัย
เราใช้ Makefile เพื่อกำหนดขั้นตอนการบิลด์ให้เป็นลายลักษณ์อักษร สังเกตว่า GO_IMAGE ถูกตรึงไว้กับเวอร์ชันที่ระบุ เราไม่ใช้ latest เด็ดขาด
รายละเอียดทางเทคนิคที่สำคัญ
1. CGO_ENABLED=0 สำหรับไบนารีแบบสแตติก
สำหรับเอเจนต์ของ Piler เราต้องการไบนารีที่รันได้ทุกที่ ตั้งแต่ Debian รุ่นใหม่ไปจนถึงเซิร์ฟเวอร์รุ่นเก่า การปิด CGO ทำให้ไบนารีมีทุกสิ่งที่ต้องใช้ในตัว โดยไม่พึ่งพาไลบรารีของเครื่องโฮสต์แบบไดนามิก
2. -ldflags -X เพื่อระบุที่มา
ไบนารีของคุณควรบอกได้อย่างแม่นยำว่ามาจากที่ใด เราแทรกคอมมิต git และวันที่บิลด์ไว้ในขั้นตอนการลิงก์ ซึ่งมีความสำคัญอย่างยิ่งในการดีบักเอเจนต์ BigBlueButton บนคลัสเตอร์ต่าง ๆ หลายสิบแห่ง
3. การตรึงอิมเมจคอมไพเลอร์
วัฒนธรรมของเราเชื่อมั่นในความสม่ำเสมอ การตรึง golang:1.26.5-trixie แทนการใช้ latest หมายความว่า หาก Ahmed บิลด์ไบนารีในวันนี้ และ Omar บิลด์อีกครั้งในอีกหกเดือน ผลลัพธ์จะเหมือนกันทุกบิต
บทสรุป
คอมไพเลอร์เป็นเครื่องมือ และเช่นเดียวกับเครื่องมือทุกชนิด ควรถูกเก็บไว้ในที่ของมัน การย้ายชุดเครื่องมือ Go เข้าไปไว้ใน Docker ช่วยให้เรากำจัดข้อบกพร่องประเภท "เครื่องผมใช้ได้" ไปได้ทั้งหมด และทำให้การรับวิศวกรใหม่ทุกคนที่เข้าร่วม RIADVICE ง่ายขึ้น
ไม่ว่าคุณจะเขียนเอเจนต์ Java ใหม่หรือสร้าง API ที่ปลอดภัยสำหรับ Piler รูปแบบนี้มอบวินัยและความน่าเชื่อถือที่ระบบใช้งานจริงต้องการ
Ghazi Triki เป็นผู้ก่อตั้ง RIADVICE เราสร้างระบบเชื่อมต่อที่มีประสิทธิภาพสูงและปลอดภัยสำหรับ BigBlueButton, Piler และอื่น ๆ มาร่วมสร้างสิ่งที่ยอดเยี่ยมไปด้วยกัน





