Skip to content

Docker-образы

У каждого проекта — свой Dockerfile в корне, запускаемый на Kubernetes. CI собирает каждый образ, пушит в registry, а GitOps бампает тег в Helm-values (см. GitOps).

приложениебазасобираетзапускается какk8s-объектпорт
apinode:22-alpineNestJS → distnode dist/main.jsDeployment + Service + HPA + Ingress (пул core)3333
appnode:22-alpineNuxt → .outputnode .output/server/index.mjsDeployment + Service + Ingress3000
adminnode:22-alpineNuxt → .outputnode .output/server/index.mjsDeployment + Service + Ingress3001
workerPlaywright/Chromium (heavy)цикл агента + тулыодна задача, потом выходk8s Job (без Service) — на задачу

Общие принципы

  • Multi-stage сборки — стадия build (полные зависимости + компиляция) и тонкая runtime (только prod-зависимости + артефакты). Маленький финальный образ, быстрые пуллы.
  • Non-root юзер; пинованные теги базовых образов; .dockerignore (без node_modules, .git, dist).
  • Healthcheck для долгоживущих сервисов (Deployments); k8s readinessProbe/livenessProbe.
  • Конфиг через env (12-factor); секреты не вшиваем — инжектит k8s в рантайме. Инфра-креды приходят из HashiCorp Vault через External Secrets Operator, а не из Sealed Secrets (см. GitOps); секреты агента резолвятся из Postgres.
  • Один образ на папку репо — с двумя поправками к строке, которая тут была раньше. У app/ нет Dockerfile, хотя helm/agentfy-app в Agentfy/gitops фиксирует реальный тег ghcr.io/agentfy/agentfy-app, то есть этот образ собирается там, где не записано ни в одном из репозиториев. А у worker/ их три: Dockerfile (shell-воркер), Dockerfile.browser (тот же образ плюс Chromium, AGNT2-212) и Dockerfile.control — намеренно незахищённый образ, который существует только чтобы можно было показать scripts/verify-image.sh падающим. Контрольный образ никогда не собирается для деплоя.

api — Core + эфемерный мозг

dockerfile
# build
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx prisma generate && npm run build
# runtime
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules/.prisma ./node_modules/.prisma
USER node
EXPOSE 3333
CMD ["node", "dist/main.js"]

api — это stateless, горизонтально масштабируемый Deployment (HPA скейлит реплики по нагрузке). «Эфемерный агент = api» значит нет пода на агента — каждый турн это stateless-запрос, который грузит стейт агента из Postgres и отвечает; общий флот api скейлится по трафику. (Литеральный под-на-задачу — это Job воркера.) Prisma-миграции не в образе: их планировали как PreSync-хук GitOps, а сегодня они выполняются в initContainer у Deployment api (helm/agentfy-api/templates/deployment.yaml в Agentfy/gitops).

app / admin — Nuxt

dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build               # → .output (Nitro server build)
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/.output ./.output
USER node
EXPOSE 3000                     # admin: 3001
CMD ["node", ".output/server/index.mjs"]

SSR (Nitro Node-сервер) за Ingress. Если набор страниц полностью статичен — альтернатива nuxt generate → образ nginx:alpine, отдающий статику — та же форма Deployment.

worker — руки агента (heavy-образ)

Отличается от остальных: тяжёлая база с настоящей ОС — bash, файловая система и Chromium (Playwright) — плюс цикл агента + тул-исполнители + WS-клиент. Не долгоживущий сервер; образ выполняет одну задачу и выходит, используется k8s Job на задачу.

dockerfile
# Тяжёлая база с готовым Chromium + deps (образ Playwright, либо
# oven/bun:alpine + chromium, как в Dockerfile cleanslice/runtime).
FROM mcr.microsoft.com/playwright:v1.49.0-jammy
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER pwuser
# Подключается к Core по WS через SESSION_ID/CORE_WS_URL/SESSION_TOKEN, тянет
# задачу, гоняет bash/fs/browser тулы, выгружает результаты, затем выходит.
ENTRYPOINT ["node", "dist/worker.js"]
  • Берёт цикл агента + тул-исполнители из cleanslice/runtime.
  • Запускается в Job: restartPolicy: Never, ttlSecondsAfterFinished, emptyDir-workspace, read-only root fs (запись только /workspace), короткоживущий projected secret. См. Worker на Kubernetes.
  • Не управляется GitOps как Deployment — Argo держит только ref образа воркера + RBAC + шаблон NetworkPolicy + пресеты RuntimeProfile; экземпляры Job создаются в рантайме.

Поток сборки и доставки

git push → docker build <app>/Dockerfile → push ghcr.io/agentfy/<app>:<sha>
        → бамп image.tag в Agentfy/gitops helm/<chart>/values.dev.yaml → ArgoCD синкает

Вторая половина настоящая, и путь точный. Первая не подтверждена: ни в одном из репозиториев нет workflow, который собирал бы или пушил образ приложения, а в этом репозитории нет .github/ вообще, — при этом values-файлы несут реальные теги. См. Два репозитория.

Локальная разработка — те же Dockerfile через compose; прод — на Hetzner k8s. Через среды двигается единый артефакт (образ) — воспроизводимо и откатываемо.

Одно исключение из контекста <app>/ (AGNT2-248). Образ api собирается из КОРНЯ репозитория — docker build -f api/Dockerfile . — потому что каталог инструментов воркера, который api показывает модели до подъёма пода, генерируется из worker/src во время сборки. Исходники воркера здесь вход сборки и ни в один слой готового образа не попадают; .dockerignore в корне не даёт контексту стать всеми node_modules репозитория.