Как собирать проект на Go в CI/CD пайплайне?

«Как собирать проект на Go в CI/CD пайплайне?» — вопрос из категории Скриптинг и автоматизация, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В DevOps-практике сборка Go-проекта — это этап CI-пайплайна, который должен быть воспроизводимым, быстрым и безопасным. Я строю его так:

1. Этап сборки (в CI, например, GitLab CI / GitHub Actions):

# .gitlab-ci.yml пример этапа
build:
  stage: build
  image: golang:1.21-alpine
  variables:
    GOOS: linux
    GOARCH: amd64
    CGO_ENABLED: 0
  script:
    - go mod download
    - go vet ./...
    - go test ./... -short
    - go build 
        -trimpath 
        -ldflags="-s -w -X main.Version=$CI_COMMIT_SHORT_SHA -X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" 
        -o bin/myapp 
        ./cmd/app
  artifacts:
    paths:
      - bin/myapp

Пояснение ключевых флагов и практик:

  • -trimpath: Удаляет пути к файловой системе из бинарника, делая сборки детерминированными и безопасными.
  • -ldflags="-s -w": Удаляет таблицу символов и отладочную информацию, уменьшая размер бинарника.
  • -X main.Version=...: Инжектит информацию о версии (хэш коммита, тег) прямо в бинарник для последующей идентификации.
  • CGO_ENABLED=0: Компиляция в статический бинарник, который можно запустить в любом scratch- или alpine-контейнере.
  • Кэширование зависимостей: В CI настраиваю кэширование директории $GOMODCACHE (по умолчанию ~/go/pkg/mod), чтобы этап go mod download не выполнялся каждый раз с нуля.

2. Создание Docker-образа (мульти-стейдж билд):

# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /myapp ./cmd/app

# Final stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
COPY --from=builder /myapp .
CMD ["./myapp"]

Такой подход дает минимальный итоговый образ (часто ~10-20 МБ).

3. Дополнительные шаги в пайплайне:

  • Статический анализ: go fmt, go vet, gosec (security scanner), staticcheck.
  • Юнит-тесты с покрытием: go test -race -coverprofile=coverage.out ./... с последующей загрузкой отчета в SonarQube или Codecov.
  • Создание SBOM (Software Bill of Materials): Использую cyclonedx-gomod для генерации списка зависимостей, что важно для security-сканирования уязвимостей (Trivy, Grype).

Для монолитов или сложных проектов использую Makefile как единую точку входа для локальной разработки, который затем повторяется в CI: make build, make test, make docker-build.