# Docker-образы EvoBGP

## CI (Gitea Actions)

Сборка образов — **`docker buildx bake`** ([docker-bake.hcl](docker-bake.hcl)), не отдельные `docker build`.

**Publish:** push в `main` после quality gates — workflow [CD](../../.gitea/workflows/cd.yaml) job **publish**: semantic-release + зеркало base-образов + bake с `VERSION` из релиза.

Bake читает переменные из **окружения** (`REGISTRY`, `IMAGE_TAG`, `CACHE_REF_*`, `BASE_*`). Скрипт [write-bake-override.sh](write-bake-override.sh) — опциональный helper для локальной отладки.

BuildKit кэширует `/go/pkg/mod`, `~/.cache/go-build` и pnpm store через `RUN --mount=type=cache`. На CI mounts живут, пока named builder `evobgp` не удаляют (`cleanup: false`).

### Слои и runtime

| Образ | Runtime base | Заметка |
|-------|----------------|---------|
| scheduler, ingest, render | `gcr.io/distroless/static-debian12:nonroot` | static Go (`CGO_ENABLED=0`), без shell |
| api, all, deploy, node | `debian:bookworm-slim` + `bird` + `birdc` | `bird -p` (parse-check) и `birdc`; демон не запускается |
| agent | тот же Ubuntu+bird2, что bird2 | общие слои с `evobgp-bird2` |
| bird2 | Ubuntu Noble + пакет bird2 | |
| web, web-all | `nginx:1.27-alpine` | `worker_processes 1` |

Сборка Go: `golang:1.24-alpine`. Context режется корневым [.dockerignore](../../.dockerignore).

### Кэш registry

- `git.shx.one/<owner>/evobgp-buildcache:go-buildcache` — **запись** только из `go-build-all`
- `git.shx.one/<owner>/evobgp-buildcache:web-buildcache` — **запись** только из `web-build`
- `git.shx.one/<owner>/evobgp-buildcache:birdc-buildcache` — **запись** только из `go-birdc`
- `git.shx.one/<owner>/evobgp-buildcache:base-*` — зеркало FROM с **Docker Hub** (`docker.io/library/…`; distroless — `gcr.io`). Только `linux/amd64`; skip если тег уже в Gitea. Неуспешный copy не валит CD — bake берёт Docker Hub FROM для этой базы.

`pull = false` в bake: не перекачивать FROM, если слой уже в builder. Не запускайте `docker system prune -a` на runner.

Параллельный `cache-to` в один ref ломает manifest (`content descriptor … not found`).

Если CI падает на «not found» после смены схемы кэша — один раз удалите теги `evobgp-buildcache:*` в registry и пересоберите.

## Локальная сборка

Из корня репозитория:

```bash
cd deploy/docker
export REGISTRY=git.shx.one/<owner>
export IMAGE_TAG=latest
export SHORT_SHA=$(git rev-parse --short HEAD)
export VERSION=dev
export BUILD_TIME=
docker buildx bake --allow=fs.read=../.. -f docker-bake.hcl go-images
docker buildx bake --allow=fs.read=../.. -f docker-bake.hcl web-images
```

Проверка манифеста без сборки:

```bash
docker buildx bake --allow=fs.read=../.. -f docker-bake.hcl --print default
```

В `docker-bake.hcl`: `context = "../.."` резолвится **от cwd** (каталог `deploy/docker/`). `dockerfile` — путь от этого context (корень репо). Из корня репо не вызывать bake с `-f deploy/docker/docker-bake.hcl`: получится `lstat ../../deploy`. Можно задать `BUILDX_BAKE_FILE_RELATIVE_PATHS=1`.

Один образ (legacy, target `build-all`):

```bash
docker build -f deploy/docker/gobinary/Dockerfile \
  --target build-all \
  -t evobgp-build-all:local .
```

Runtime-образы в bake ожидают stage `build-all` (через bake contexts).
