Оди на содржината
Сите статии

Градење Go-апликации со Docker: пренослив, херметички комплет алатки

од Ghazi Triki · 9 мин. читање

Градење Go-апликации со Docker
Во оваа статија

Престанете да инсталирате Go на својот домаќин. Без загадување на GOPATH, без „кај мене работи“ и без никакво отстапување меѓу верзиите. Ова е истиот образец со кој ги препишавме Java-агентите за извештаите BigBlueButton Learning Analytics и го изградивме безбедниот Piler Go Agent. Еве како да испорачувате статички бинарни датотеки само со Docker.

Од Ghazi Triki, RIADVICE

Проблемот со „само инсталирајте Go“

Додека управувавме со Java-агентите за извештаите BigBlueButton Learning Analytics, се соочивме со класичниот „пекол на зависности“. Преминот на Go ветуваше едноставност, но само ако не ги повториме истите грешки. Ни требаше процес на градење што е подеднакво херметички како и бинарните датотеки што ги создава.

Секој тим порано или подоцна удира во истите ѕидови:

  • Отстапување меѓу верзиите. Ahmed користи 1.24, Omar користи 1.26, а продукцискиот сервер е на 1.25. Мала промена во компајлерот го крши градењето, но само кај Ahmed.
  • Загадени домаќини. GOPATH, GOMODCACHE и глобално инсталирани алатки (staticcheck, gofumpt) што се разликуваат од работна станица до работна станица.
  • Тешко воведување во работата. Новиот инженер првиот ден го поминува борејќи се со променливи на околината и со C-алатки за cgo.

Во RIADVICE ги цениме дисциплината и редот. Сакавме процес на градење што ги одразува: чист, предвидлив и заеднички.

Стандардниот одговор е „користете Docker“, но повеќето упатства покажуваат само како да изградите слика на контејнер. Не покажуваат како да добиете обична, статички поврзана бинарна датотека на домаќинот, односно артефактот што навистина ви треба за systemd-услуга или за распоредување во изолирана мрежа, без воопшто да го инсталирате компајлерот за Go локално.

Shell
docker run --rm -v "$(pwd):/app" -w /app golang:1.26.5-trixie \
    go build -o dist/myapp ./cmd/myapp

Официјалната слика golang е компајлерот. Изворниот код е монтиран во /app. Бинарната датотека се запишува во dist/myapp на домаќинот. Кога командата ќе заврши, контејнерот се уништува (--rm), а домаќинот останува чист како и претходно.

Искуство од практиката: од BigBlueButton до Piler

Ова не е само теорија. Овој образец го применивме во два клучни проекти:

  1. BigBlueButton Learning Analytics Reports: Тешките Java-агенти ги препишавме во лесни бинарни датотеки на Go. Градењето засновано на Docker ни овозможи да ја таргетираме точната Debian-околина на BBB-серверите, без таа околина да ни треба на лаптопите.
  2. Piler Go Agent: За нашето решение за архивирање е-пошта изградивме агент со засилена безбедност. Процесот на градење мораше да биде безбеден колку и кодот. Со фиксирање на компајлерот на одреден SHA, обезбедивме секој бит во бинарната датотека да има познато потекло.

Зошто ова е подобро од инсталирање Go

Аспектgo install на домаќинотdocker run golang:...
Верзија на GoКоја и да сте инсталирале последнаФиксирана, иста за сите
GOPATH / кешГо загадува ~/goХерметички слоеви на контејнерот
cgo-алаткиБара поставување на домаќинотВклучени во официјалната слика
Усогласеност со CI„Кај мене работи“Иста слика, иста бинарна датотека
Makefile
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 и други системи. Ајде заедно да изградиме нешто одлично.

🔗 Преземете го golang-docker-boilerplate на GitHub