Docker-образи
У кожного проєкту — власний Dockerfile у корені, що запускається на Kubernetes. CI збирає кожен образ, пушить у registry, а GitOps бампає тег у Helm-values (див. GitOps).
| застосунок | база | збирає | запускається як | k8s-об'єкт | порт |
|---|---|---|---|---|---|
api | node:22-alpine | NestJS → dist | node dist/main.js | Deployment + Service + HPA + Ingress (пул core) | 3333 |
app | node:22-alpine | Nuxt → .output | node .output/server/index.mjs | Deployment + Service + Ingress | 3000 |
admin | node:22-alpine | Nuxt → .output | node .output/server/index.mjs | Deployment + Service + Ingress | 3001 |
worker | Playwright/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 + ефемерний мозок
# 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
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 на задачу.
# Важка база з готовим 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 репозиторію.