Когда в проекте много сервисов, один хороший Dockerfile уже не решает все проблемы. В этот момент на первый план выходят две темы: ускорение сборки через golden builder image и аккуратная публикация образов в registry.
Если базовый Dockerfile ещё не выстроен, начните со статьи про multi-stage сборку, pinning и runtime-безопасность. А если нужна именно дисциплина вокруг CI, secrets, proxy и HEALTHCHECK, рядом есть отдельный материал про production-сборку Docker-образов.
В статье
- Когда нужен golden builder image
- Что важно не перепутать
- CI-паттерн для builder image
- Как логиниться в registry
- Тегирование и push
- Перенос образов между registry и через архив
- Копирование через
skopeo
Когда нужен golden builder image
Golden builder image особенно уместен, если:
- У вас много сервисов на одном стеке и они собираются одинаковым набором инструментов.
- CI тормозит из-за повторной установки SDK, компиляторов, package managers и системных зависимостей.
- Нужны стабильные и воспроизводимые сборки с одинаковым builder-окружением между репозиториями.
Для небольшого проекта с одним-двумя сервисами это может быть лишним оверхедом: отдельный образ, отдельный pipeline и сопровождение не всегда окупаются.
Что важно не перепутать
- Golden builder image используется только на этапе сборки, а не как runtime-образ.
- Образ должен быть пинован по версиям и регулярно обновляться, включая security patches.
- Для него нужен отдельный pipeline: сборка, сканирование, подпись и публикация.
- Ускорение достигается за счёт предустановленных инструментов и кэшей, но секреты нельзя запекать внутрь образа.
Пример Dockerfile для прикладного сервиса:
# Пинуем golden builder по версии и digest — гарантия воспроизводимости
FROM registry.company.local/platform/go-builder:1.26.3@sha256:<digest> AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server ./cmd/server
# Runtime-стадия минимальна — toolchain остался в builder
FROM alpine:3.23
RUN apk --no-cache add ca-certificates && addgroup -S app && adduser -S app -G app
COPY --from=builder /src/server /usr/local/bin/server
USER app
CMD ["server"]Важно не превращать golden image в “помойку” из инструментов. Он должен быть минимальным, версионируемым и обновляться по расписанию.
CI-паттерн для golden builder image
Обычно это отдельный репозиторий или отдельная директория с Dockerfile builder-образа:
name: golden-builder
on:
push:
branches: [main]
paths:
- '.github/workflows/golden-builder.yml'
- 'build-images/go-builder/**'
schedule:
- cron: '0 3 * * 1' # еженедельная пересборка по понедельникам — подхватывает security patches
jobs:
build-scan-sign-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build builder image
run: docker build -t registry.company.local/platform/go-builder:1.26.3 ./build-images/go-builder
- name: Scan builder image # блокируем публикацию при уязвимостях
run: trivy image --exit-code 1 --severity HIGH,CRITICAL registry.company.local/platform/go-builder:1.26.3
- name: Push builder image
run: docker push registry.company.local/platform/go-builder:1.26.3
- name: Sign builder image # cosign подписывает образ — можно верифицировать при pull
run: cosign sign --yes registry.company.local/platform/go-builder:1.26.3После этого прикладные репозитории используют образ только на этапе сборки и не дублируют установку toolchain в каждом CI job.
Docker Hub не обязателен
Рабочая схема одинакова для любых registry: Harbor, Nexus, GitLab Registry, GHCR, ECR и других. Важна не конкретная платформа, а дисциплина публикации и именования образов.
Как логиниться в registry
Базовый вариант:
# --password-stdin: пароль не попадает в историю shell и ps
echo "$REGISTRY_TOKEN" | docker login registry.company.local -u "$REGISTRY_USER" --password-stdinПароль или токен передавайте через stdin, а не в явном виде в командной строке.
Примеры:
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GITHUB_USER" --password-stdin
echo "$HARBOR_TOKEN" | docker login registry.company.local -u "$HARBOR_USER" --password-stdinТегирование и push
# Immutable тег — конкретная версия, не перезаписывается
docker tag app:multi registry.company.local/team/app:1.4.2
# Плавающий тег — указывает на текущий рекомендуемый образ
docker tag app:multi registry.company.local/team/app:stable
docker push registry.company.local/team/app:1.4.2
docker push registry.company.local/team/app:stableПрактика простая: immutable тег, например 1.4.2 или commit sha, плюс удобный плавающий тег вроде stable или latest.
Если образ публикуется в несколько registry, лучше сохранять единый version tag и единый контроль digest в pipeline.
Как перенести образ из одного registry в другой
Самый понятный вариант — через локальный pull, tag и push:
# Простой перенос: pull → переименование → push в другой registry
docker pull registry-a.local/team/app:1.4.2
docker tag registry-a.local/team/app:1.4.2 registry-b.local/team/app:1.4.2
docker push registry-b.local/team/app:1.4.2Это рабочий вариант, когда нужен простой способ забрать и переложить образ без отдельного инструмента репликации.
Как сохранить образ в архив и перенести в другое окружение
# Экспорт в tar-архив — для переноса на изолированные площадки
docker save registry-a.local/team/app:1.4.2 -o app_1.4.2.tar
# На целевом хосте: загрузка из архива
docker load -i app_1.4.2.tar
docker tag registry-a.local/team/app:1.4.2 registry-b.local/team/app:1.4.2
docker push registry-b.local/team/app:1.4.2Подход полезен для изолированных контуров, air-gapped окружений или ручного переноса между площадками.
Копирование между registry без локальной загрузки
Для больших образов и автоматизации удобнее использовать skopeo:
# Прямое копирование между registry — без промежуточного docker pull/push
skopeo copy docker://registry-a.local/team/app:1.4.2 docker://registry-b.local/team/app:1.4.2Это особенно удобно, когда нужно синхронизировать registry без прогонки образа через локальный Docker daemon.
Вывод
Golden builder image и нормальная работа с registry становятся полезны не в момент “первого контейнера”, а когда проект начинает расти. Если сервисов мало, это может быть лишней сложностью. Но для командной разработки и длинных CI-сборок такие практики быстро окупаются.
Чтобы эти практики действительно работали, их лучше строить поверх уже нормальной базы: multi-stage Dockerfile и production-процесса со сканированием, secrets и healthcheck.
Комментарии