Модули стали основным способом управления зависимостями в Go: go.mod фиксирует требования, go.sum — контрольные суммы, а команды go mod и go build работают одинаково на Linux, macOS и Windows. Отдельная тема — кросс-компиляция: один и тот же исходник собирается под другую архитектуру без виртуальной машины, если не задействован cgo.
Установка и окружение
Дистрибутивный пакет golang часто отстаёт. Для свежей версии скачайте архив с go.dev/dl, сверьте контрольную сумму и распакуйте в /usr/local. Номер версии подставьте актуальный: в примере он вынесен в переменную, чтобы не редактировать команды по нескольку раз.
VERSION=1.25.0
curl -fsSLO "https://go.dev/dl/go${VERSION}.linux-amd64.tar.gz"
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf "go${VERSION}.linux-amd64.tar.gz"
/usr/local/go/bin/go version
Добавьте /usr/local/go/bin в PATH. Затем проверьте каталоги: GOROOT — установка Go, GOPATH — рабочая область, GOMODCACHE — скачанные модули, GOCACHE — кеш сборки. Для воспроизводимых сборок в CI полезно запретить автоматическую подкачку тулчейна: GOTOOLCHAIN=local.
Если на машине несколько версий, не смешивайте их в одном PATH: проверьте, какой бинарник запускается через command -v go, и при необходимости задайте абсолютный путь в сборочном скрипте.
Модуль и зависимости
mkdir -p ~/src/hello && cd ~/src/hello
go mod init example.com/hello
go get github.com/spf13/pflag@latest
go mod tidy
cat go.mod
В go.mod остаются модуль, версия языка и требования; косвенные зависимости помечаются // indirect. Файл go.sum хранит хеши и должен лежать в репозитории вместе с go.mod — иначе сборка на другой машине будет проверять их заново и может отказаться работать при несовпадении.
Команда go mod download заранее заполняет кеш модулей — это удобно в CI, где сеть может быть недоступна во время сборки. Версию зависимости фиксируйте в go.mod, а не в скрипте установки.
Полезные команды: go mod verify сверяет содержимое модулей с go.sum, go list -m all показывает дерево, go mod why объясняет, зачем нужна зависимость. Обновление всей ветки зависимостей через go get -u ./… делайте отдельным коммитом и прогоняйте тесты: мажорные версии так не обновляются, но минорные могут изменить поведение.
Vendoring и приватные репозитории
go mod vendor
go build -mod=vendor ./...
go env -w GOPRIVATE=git.example.com
du -sh vendor
Каталог vendor делает сборку автономной: код зависимостей лежит рядом, сеть не нужна. Go автоматически использует vendor, если он создан и modules.txt согласован с go.mod. Для приватных модулей задайте GOPRIVATE: он отключает прокси и проверку публичных сумм для указанных доменов, а доступ настраивается через SSH-ключ или токен в git config.
Сборка
go build -trimpath \
-ldflags "-s -w -X main.version=$(git describe --tags --always)" \
-o bin/app ./cmd/app
file bin/app
./bin/app --version
go version -m bin/app
Флаг -trimpath убирает абсолютные пути из бинарника, -s -w уменьшает размер за счёт отладочной информации. Параметр -X работает только со строковой переменной уровня пакета, например var version = «dev». Команда go version -m показывает встроенные сведения о модулях и ревизии VCS — удобно для проверки, что на сервер попал нужный коммит.
Если сборка идёт из архива без Git, добавьте -buildvcs=false. Кеш сборки ускоряет повторные компиляции; в CI его сохраняют между запусками, а при странных ошибках чистят через go clean -cache.
Кросс-компиляция
GOOS=linux GOARCH=arm64 go build -o dist/app-linux-arm64 ./cmd/app
GOOS=windows GOARCH=amd64 go build -o dist/app.exe ./cmd/app
GOOS=darwin GOARCH=arm64 go build -o dist/app-darwin-arm64 ./cmd/app
go tool dist list | grep -E '^(linux|windows|darwin)/'
Для чистого Go достаточно пары переменных: компилятор не требует целевой системы. Флаг CGO_ENABLED=0 отключает cgo и даёт статический бинарник, который проверяется через file и ldd. Если зависимость использует cgo, понадобится кросс-тулчейн, sysroot и переменная CC — это заметно сложнее, а иногда проще собрать в контейнере нужной архитектуры.
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o dist/app-static ./cmd/app
file dist/app-static
ldd dist/app-static
GOARM=7 GOOS=linux GOARCH=arm go build -o dist/app-armv7 ./cmd/app
Для ARM32 значение GOARM задаёт версию набора инструкций, для amd64 существует GOAMD64. Не ставьте экзотические значения без необходимости: бинарник может не запуститься на целевом железе. Проверяйте артефакты на реальной машине или в эмуляторе.
Файлы с суффиксами _linux.go, _windows.go и _arm64.go подключаются автоматически по целям сборки, поэтому один каталог может содержать разные реализации без внешних условий.
Контейнер и CI
FROM golang:alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /out/app ./cmd/app
FROM alpine:3.22
RUN apk add --no-cache ca-certificates
COPY --from=build /out/app /usr/local/bin/app
ENTRYPOINT ["app"]
Многоступенчатая сборка оставляет в финальном образе только бинарник и корневые сертификаты. Тег alpine лучше фиксировать по digest, а версию Go — по конкретному образу, иначе сборка изменится без изменения кода. Для полностью статического бинарника финальной стадией может быть scratch, но тогда добавьте сертификаты и, при необходимости, tzdata вручную. Если приложению нужны часовые пояса, установите tzdata в финальный образ или скопируйте /usr/share/zoneinfo из подготовленного образа.
Диагностика
- «updates to go.mod needed» — выполните go mod tidy и закоммитьте результат.
- checksum mismatch — проверьте GOPROXY и go.sum, при необходимости go clean -modcache.
- Сборка работает локально, но не в CI — сравните версию Go, GOOS, CGO_ENABLED и регистр путей.
- Бинарник слишком большой — -s -w и -trimpath, но не упаковывайте антивирусными упаковщиками.
- Ошибка VCS — -buildvcs=false или сборка из каталога с Git.
go env GOOS GOARCH CGO_ENABLED GOPROXY
go list -m -u all
go clean -modcache
go test ./...
Локальная ошибка «cannot find package» чаще всего означает, что команда запущена вне модуля или в другом каталоге. Проверьте go env GOMOD: он должен указывать на существующий go.mod, а не на /dev/null.
Перед релизом прогоните go vet ./... и тесты с флагом -race на amd64: проверка гонок поддерживается не на всех архитектурах, а ошибки лучше ловить до кросс-компиляции.
Итог
Рабочий процесс в Go укладывается в несколько привычек: фиксировать зависимости через go.mod и go.sum, проверять сборку через go build ./…, собирать релизы с -trimpath и -ldflags, а кросс-компиляцию начинать с CGO_ENABLED=0. Для сложных зависимостей с cgo заранее готовьте кросс-тулчейн или контейнерную сборку, чтобы не выяснять это в день релиза.