Docker-сборки
Эта страница описывает структуру, сборку и хранение Docker-образов для сервисов.
Registry
Все образы хранятся в отдельном registry рядом с кластером:
registry.amztop.ioПолный путь к образу имеет следующую структуру:
registry.amztop.io/<группа>/<проект>/<сервис>:<тег>Например:
registry.amztop.io/integrations/hub/backend:dev
registry.amztop.io/integrations/hub/frontend:dev
registry.amztop.io/integrations/auth_service/backend:prodПервые сегменты пути — это путь проекта в GitLab, и придумать их нельзя: токен на запись выдаётся только на путь существующего проекта. Всё, что после пути проекта, свободно — один проект может держать сколько угодно образов.
Аутентификация в registry
Собственной базы учёток у registry нет: токены ему подписывает GitLab, поэтому годятся любые GitLab-креды. CI-задачи аутентифицируются автоматически через $CI_REGISTRY_USER и $CI_REGISTRY_PASSWORD. Для ручной работы с локальной машины:
docker login registry.amztop.io -u <username> -p <token>Какой токен брать
- В пайплайне — ничего заводить не нужно, работает эфемерный токен джобы.
- Ноды, ansible, внешние сервисы — deploy-токен со скоупом
read_registry; имя пользователя берётся из карточки токена (gitlab+deploy-token-N). - Push извне пайплайна — тот же deploy-токен, но со скоупом
write_registry. Толькоread_registryдаст рабочий pull иdeniedна push. - Руками со своей машины — personal access token со скоупами
read_registry/write_registry.
Образов не видно в интерфейсе GitLab
GitLab пока знает о своём старом registry, поэтому вкладка Packages & Registry показывает не то, что лежит в registry.amztop.io. Содержимое смотрите через API:
curl -su <логин>:<токен> https://registry.amztop.io/v2/_catalog
curl -su <логин>:<токен> https://registry.amztop.io/v2/integrations/hub/backend/tags/listСтратегия тегирования образов
Образ получает один тег — по имени ветки, из которой собран:
| Тег | Ветка | Назначение |
|---|---|---|
dev | dev | Последняя сборка для dev-окружения |
stage | stage | Последняя сборка для stage-окружения |
prod | main | Последняя сборка для продакшена |
Теги перезаписываются при каждой сборке, и Nomad-джобы ссылаются именно на них. Чтобы Nomad увидел новую сборку под неизменившимся тегом, в джобу передаётся переменная deploy_ts с номером пайплайна — она и делает каждую выкатку уникальной.
# Всегда указывает на последнюю сборку из ветки dev:
docker pull registry.amztop.io/integrations/hub/backend:devОткат — это пересборка
Отдельного неизменяемого тега по commit SHA у образов нет, поэтому «вернуть предыдущий образ» по тегу не получится. Штатный откат — автоматический: если канареечная копия не прошла health-чеки, Nomad сам возвращает прошлую версию (auto_revert). Ручной откат делается ревертом коммита и новой сборкой.
Паттерны Dockerfile
Сервисы обычно делятся на две категории: только бэкенд и полный стек с фронтендом. Оба используют многоэтапные сборки для минимизации размера итоговых образов.
Бэкенд-сервис (Python / Go)
# --- Build stage ---
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
COPY . .
# --- Runtime stage ---
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY --from=builder /app .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]# --- Build stage ---
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/server .
# --- Runtime stage ---
FROM alpine:3.19
COPY --from=builder /bin/server /bin/server
EXPOSE 8080
ENTRYPOINT ["/bin/server"]Фронтенд (Vite + Node)
Фронтенд-сборки компилируют статические ресурсы на этапе сборки и раздают их из легковесного образа.
# --- Build stage ---
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
ARG VITE_API_URL
ENV VITE_API_URL=${VITE_API_URL}
RUN pnpm build
# --- Runtime stage ---
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80Аргументы сборки для фронтенда
Специфичные для окружения значения, такие как VITE_API_URL, должны передаваться во время сборки, поскольку Vite вшивает их в статический JS-бандл. Передавайте их через BUILD_ARGS в CI-задаче:
BUILD_ARGS: "--build-arg VITE_API_URL=https://api.amzhub.ai"Полный стек (бэкенд + фронтенд в одном репозитории)
Когда бэкенд и фронтенд находятся в одном репозитории, каждый получает отдельную задачу сборки с указанием своего SERVICE_DIR:
build:backend:
extends: .docker_build
tags:
- new-server
variables:
SERVICE_NAME: backend
SERVICE_DIR: back
build:frontend:
extends: .docker_build
tags:
- new-server
variables:
SERVICE_NAME: frontend
SERVICE_DIR: front
BUILD_ARGS: "--build-arg VITE_API_URL=https://api.amzhub.ai"В результате создаются два отдельных образа:
registry.amztop.io/integrations/hub/backend:dev
registry.amztop.io/integrations/hub/frontend:devОба собраны из одного коммита и получают одинаковый тег окружения.
Ограничение платформы
Все образы собираются с флагом --platform linux/amd64:
docker build --platform linux/amd64 -t ... .Это обеспечивается шаблоном .docker_build. Ноды кластера, на которых работает Nomad, используют архитектуру amd64, поэтому даже если CI-раннеры или машины разработчиков основаны на ARM (Apple Silicon), итоговые образы будут корректно работать на целевой инфраструктуре.
Локальное тестирование на ARM Mac
Если вы собираете локально на Mac с чипом M-серии без флага --platform linux/amd64, образ может работать на вашей машине, но упасть на ноде кластера. Всегда указывайте флаг платформы при локальной сборке для тестирования деплоя:
docker build --platform linux/amd64 -t my-service:test .Контекст сборки и .dockerignore
Каждый SERVICE_DIR должен содержать собственный .dockerignore для уменьшения контекста сборки:
node_modules
.git
.env
.env.*
*.md
dist
__pycache__
.venvКомпактный контекст сборки ускоряет этап docker build в CI, поскольку меньше данных передаётся демону Docker.