diff --git a/deploy/compose/.env.example b/deploy/compose/.env.example index 03e8106..fe567b0 100644 --- a/deploy/compose/.env.example +++ b/deploy/compose/.env.example @@ -4,5 +4,17 @@ EVOBGP_REGISTRY=git.shx.one/denozord EVOBGP_IMAGE_TAG=latest +# Stable ID of the local BIRD node for telemetry. Set uniquely per actual BIRD node +# when several control-plane deployments share one PostgreSQL database. +# EVOBGP_TELEMETRY_LOCAL_ID=bird2-node-a + +# Prometheus: ограничение TSDB по размеру (WAL и head требуют отдельного запаса). +# EVOBGP_PROMETHEUS_RETENTION_SIZE=2GB + # Для защищенного Web UI (Traefik + Let's Encrypt + Cloudflare DNS challenge) # используйте второй файл рядом: .env.web-sec (шаблон: .env.web-sec.example) + +# Reference Compose hardcodes EVOBGP_SCHEDULER_MODE=db (trusted, all tenant scopes). +# Setting HTTP variables in this .env DOES NOT switch the scheduler mode. +# To use a single-tenant HTTP scheduler, explicitly override its service environment +# with MODE=http plus URL, bearer and tenant; see docs/quickstart.md. diff --git a/deploy/compose/.env.production.example b/deploy/compose/.env.production.example index c264cf2..f494b07 100644 --- a/deploy/compose/.env.production.example +++ b/deploy/compose/.env.production.example @@ -4,6 +4,13 @@ EVOBGP_REGISTRY=git.shx.one/denozord EVOBGP_IMAGE_TAG=latest +# Уникальный стабильный ID локального BIRD-узла в общей БД; не меняйте при рестарте. +# EVOBGP_TELEMETRY_LOCAL_ID=bird2-node-a + +# Optional NATS для внешних клиентов: COMPOSE_PROFILES=nats +# Prometheus: TSDB retention size; WAL/head требуют дополнительного места. +# EVOBGP_PROMETHEUS_RETENTION_SIZE=2GB + # Имя compose-проекта (должно совпадать с name: в docker-compose.yaml) COMPOSE_PROJECT_NAME=evobgp-microvps-full diff --git a/deploy/compose/.env.stack.microvps-full.example b/deploy/compose/.env.stack.microvps-full.example index f7efb33..45b75fc 100644 --- a/deploy/compose/.env.stack.microvps-full.example +++ b/deploy/compose/.env.stack.microvps-full.example @@ -7,6 +7,13 @@ EVOBGP_REGISTRY=git.shx.one/denozord EVOBGP_IMAGE_TAG=latest +# Уникальный стабильный ID локального BIRD-узла в общей БД; не меняйте при рестарте. +# EVOBGP_TELEMETRY_LOCAL_ID=bird2-node-a + +# Optional NATS для внешних клиентов: COMPOSE_PROFILES=nats +# Prometheus: TSDB retention size; WAL/head требуют дополнительного места. +# EVOBGP_PROMETHEUS_RETENTION_SIZE=2GB + # Защищенный Web UI (Traefik + Let's Encrypt + Cloudflare DNS challenge) WEBUI_DOMAIN=bgp.shz.su WEBUI_IP_WHITELIST=109.174.26.78/32,87.103.241.8/32 diff --git a/deploy/compose/docker-compose.microvps-full.yaml b/deploy/compose/docker-compose.microvps-full.yaml index fd2af17..0435f4f 100644 --- a/deploy/compose/docker-compose.microvps-full.yaml +++ b/deploy/compose/docker-compose.microvps-full.yaml @@ -1,4 +1,4 @@ -# Дополнительный файл Compose: профиль microvps-full + ужатые лимиты под хост ~1 ГиБ RAM. +# Дополнительный файл Compose: профиль microvps-full + ресурсные лимиты. # # Запуск (PowerShell, из каталога deploy\compose): сначала .env из .env.example, docker login git.shx.one, docker compose pull # docker compose -f docker-compose.yaml -f docker-compose.microvps-full.yaml --profile microvps-full up -d @@ -6,13 +6,13 @@ # Остановка: # docker compose -f docker-compose.yaml -f docker-compose.microvps-full.yaml --profile microvps-full down # -# Лимиты под хост ~1 ГиБ: сумма лимитов контейнеров ~800 МиБ + запас ОС/Docker. При OOM увеличьте RAM VPS или уберите prometheus/nats из профиля (отдельная правка compose). +# Сумма memory limits с edge и без optional NATS — 984M, это не замер потребления. +# Пригодность для хоста ~1 ГиБ требует workload-теста с запасом ОС/Docker и BIRD peaks. # Postgres ниже ~240–256 МиБ часто не успевает пройти healthcheck / падает по памяти — не уменьшайте postgres без tuned postgresql.conf. services: evobgp-all: environment: - EVOBGP_BROKER_URL: nats://nats:4222 EVOBGP_SERVICE: evobgp-all EVOBGP_RUNTIME_LOGS_DIR: /opt/evobgp/runtime-logs volumes: diff --git a/deploy/compose/docker-compose.production.example.yaml b/deploy/compose/docker-compose.production.example.yaml index 819d230..4d4a310 100644 --- a/deploy/compose/docker-compose.production.example.yaml +++ b/deploy/compose/docker-compose.production.example.yaml @@ -62,6 +62,7 @@ services: max-file: "3" nats: + profiles: ["nats"] image: nats:2.10-alpine restart: unless-stopped command: ["-js", "-m", "8222"] @@ -104,7 +105,7 @@ services: - bird_etc:/etc/bird - bird_run:/run/bird entrypoint: ["/usr/local/bin/evobgp-agent"] - command: ["watch", "-socket=/run/bird/bird.ctl", "-watch-interval=30s"] + command: ["-socket=/run/bird/bird.ctl", "-watch-interval=30s", "watch"] logging: driver: json-file options: @@ -117,20 +118,18 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started bird2: condition: service_started ports: - "8080:8080" environment: EVOBGP_DATABASE_URL: postgres://evobgp:evobgp@postgres:5432/evobgp?sslmode=disable - # EVOBGP_BROKER_URL: nats://nats:4222 EVOBGP_NODE_DISPATCH_ENABLED: "1" EVOBGP_BUNDLE_SEED_HEX: "bd8fbcd31545aacfdd228203beca8e945ab9a752f2ce5624cf42d9f316389a9d" EVOBGP_HTTP_ADDR: ":8080" EVOBGP_SEED_DEMO: "1" EVOBGP_BIRDC_SOCKET: /run/bird/bird.ctl + EVOBGP_TELEMETRY_LOCAL_ID: ${EVOBGP_TELEMETRY_LOCAL_ID:-} EVOBGP_BIRDC_INTERVAL: 30s EVOBGP_BIRD_ACTIVE_DIR: /etc/bird EVOBGP_BIRD_STAGING_DIR: /tmp/evobgp-bird-staging @@ -233,10 +232,13 @@ services: target: /etc/prometheus/prometheus.yml - source: prometheus_alerts_yml target: /etc/prometheus/alerts.yml + volumes: + - prometheus_data:/prometheus command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus - --storage.tsdb.retention.time=7d + - --storage.tsdb.retention.size=${EVOBGP_PROMETHEUS_RETENTION_SIZE:-2GB} - --web.enable-lifecycle logging: driver: json-file @@ -285,6 +287,7 @@ services: volumes: pgdata: + prometheus_data: bird_etc: bird_run: traefik_letsencrypt: diff --git a/deploy/compose/docker-compose.yaml b/deploy/compose/docker-compose.yaml index 5a22109..585e2e7 100644 --- a/deploy/compose/docker-compose.yaml +++ b/deploy/compose/docker-compose.yaml @@ -9,10 +9,10 @@ # docker compose --profile microvps up -d # microVPS ~1 ГиБ + Web UI — docs/quickstart.md и docker-compose.microvps-full.yaml # -# reference: 5× Go (api + scheduler + ingest + render + deploy) + postgres + NATS JetStream + bird2 + agent + web. -# Async jobs выполняются in-process в evobgp-api (jobs.Registry); NATS — задел под брокер (см. docs/architecture.md). +# reference: 5× Go (api + scheduler + ingest + render + deploy) + postgres + bird2 + agent + web. +# Async jobs выполняются in-process в evobgp-api (jobs.Registry); NATS — опция `--profile nats` (см. docs/architecture.md). # microvps: evobgp-all + postgres + bird2 + agent (без брокера и без UI). -# microvps-full: как microvps + evobgp-web-microvps (порт 3000) + nats + prometheus-microvps (порт 9090). +# microvps-full: как microvps + evobgp-web-microvps (порт 3000) + prometheus-microvps (порт 9090). # # Опция microVPS_sqlite из плана (без контейнера Postgres): требует реализации store на SQLite в приложении; # пока используйте microvps с контейнерным Postgres (единые миграции). @@ -35,7 +35,6 @@ x-logging: &default-logging x-env-ref: &env-ref EVOBGP_DATABASE_URL: postgres://evobgp:evobgp@postgres:5432/evobgp?sslmode=disable - EVOBGP_BROKER_URL: nats://nats:4222 x-env-micro: &env-micro EVOBGP_DATABASE_URL: postgres://evobgp:evobgp@postgres:5432/evobgp?sslmode=disable @@ -67,7 +66,7 @@ services: memory: 384M nats: - profiles: ["reference", "microvps-full"] + profiles: ["nats"] image: nats:2.10-alpine restart: unless-stopped command: ["-js", "-m", "8222"] @@ -86,8 +85,6 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started bird2: condition: service_started ports: @@ -99,6 +96,7 @@ services: # DEV ONLY — не для production. Bearer dev + слабые demo-секреты (см. docs/access.md). EVOBGP_DEV_INSECURE: "1" EVOBGP_BIRDC_SOCKET: /run/bird/bird.ctl + EVOBGP_TELEMETRY_LOCAL_ID: ${EVOBGP_TELEMETRY_LOCAL_ID:-} EVOBGP_BIRDC_INTERVAL: 30s EVOBGP_BIRD_ACTIVE_DIR: /etc/bird EVOBGP_BIRD_STAGING_DIR: /tmp/evobgp-bird-staging @@ -120,16 +118,13 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started evobgp-api: condition: service_started environment: <<: *env-ref - # Split deploy: scheduler must enqueue jobs via API, not in-process Registry (ARCH-04). + # Trusted DB scheduler enqueues into the shared durable queue; API workers claim jobs. EVOBGP_SCHEDULER_STANDALONE: "1" - EVOBGP_CONTROL_PLANE_URL: http://evobgp-api:8080 - EVOBGP_SCHEDULER_BEARER: dev + EVOBGP_SCHEDULER_MODE: db logging: *default-logging deploy: resources: @@ -144,8 +139,6 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started environment: <<: *env-ref logging: *default-logging @@ -162,8 +155,6 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started environment: <<: *env-ref logging: *default-logging @@ -180,8 +171,6 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started environment: <<: *env-ref EVOBGP_BIRD_ACTIVE_DIR: /etc/bird @@ -225,6 +214,7 @@ services: EVOBGP_HTTP_ADDR: ":8080" EVOBGP_SEED_DEMO: "1" EVOBGP_BIRDC_SOCKET: /run/bird/bird.ctl + EVOBGP_TELEMETRY_LOCAL_ID: ${EVOBGP_TELEMETRY_LOCAL_ID:-} EVOBGP_BIRDC_INTERVAL: 30s EVOBGP_BIRD_ACTIVE_DIR: /etc/bird EVOBGP_BIRD_STAGING_DIR: /tmp/evobgp-bird-staging @@ -315,10 +305,12 @@ services: volumes: - ../prometheus/prometheus-microvps.yml:/etc/prometheus/prometheus.yml:ro - ../prometheus/alerts.yml:/etc/prometheus/alerts.yml:ro + - prometheus_data:/prometheus command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus - --storage.tsdb.retention.time=7d + - --storage.tsdb.retention.size=${EVOBGP_PROMETHEUS_RETENTION_SIZE:-2GB} - --web.enable-lifecycle logging: *default-logging deploy: @@ -359,7 +351,7 @@ services: - bird_etc:/etc/bird - bird_run:/run/bird entrypoint: ["/usr/local/bin/evobgp-agent"] - command: ["watch", "-socket=/run/bird/bird.ctl", "-watch-interval=30s"] + command: ["-socket=/run/bird/bird.ctl", "-watch-interval=30s", "watch"] logging: *default-logging deploy: resources: @@ -377,10 +369,12 @@ services: volumes: - ../prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ../prometheus/alerts.yml:/etc/prometheus/alerts.yml:ro + - prometheus_data:/prometheus command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus - --storage.tsdb.retention.time=15d + - --storage.tsdb.retention.size=${EVOBGP_PROMETHEUS_RETENTION_SIZE:-2GB} - --web.enable-lifecycle logging: *default-logging deploy: @@ -390,6 +384,7 @@ services: volumes: pgdata: + prometheus_data: bird_etc: bird_run: traefik_letsencrypt: diff --git a/deploy/compose/stack.microvps-full.yaml b/deploy/compose/stack.microvps-full.yaml index a799d66..02b0d0f 100644 --- a/deploy/compose/stack.microvps-full.yaml +++ b/deploy/compose/stack.microvps-full.yaml @@ -1,4 +1,5 @@ -# Standalone stack (без profiles): Portainer / `docker compose -f stack.microvps-full.yaml up -d` +# Standalone stack: Portainer / `docker compose -f stack.microvps-full.yaml up -d`. +# Основные сервисы запускаются без профиля; NATS доступен только в профиле `nats`. # Эквивалент microvps-full из docker-compose.yaml + docker-compose.microvps-full.yaml, # с встроенными configs для Prometheus (как у вас). # @@ -66,6 +67,7 @@ services: max-file: "3" nats: + profiles: ["nats"] image: nats:2.10-alpine restart: unless-stopped command: ["-js", "-m", "8222"] @@ -108,7 +110,7 @@ services: - bird_etc:/etc/bird - bird_run:/run/bird entrypoint: ["/usr/local/bin/evobgp-agent"] - command: ["watch", "-socket=/run/bird/bird.ctl", "-watch-interval=30s"] + command: ["-socket=/run/bird/bird.ctl", "-watch-interval=30s", "watch"] logging: driver: json-file options: @@ -121,18 +123,16 @@ services: depends_on: postgres: condition: service_healthy - nats: - condition: service_started bird2: condition: service_started ports: - "8080:8080" environment: EVOBGP_DATABASE_URL: postgres://evobgp:evobgp@postgres:5432/evobgp?sslmode=disable - EVOBGP_BROKER_URL: nats://nats:4222 EVOBGP_HTTP_ADDR: ":8080" EVOBGP_SEED_DEMO: "1" EVOBGP_BIRDC_SOCKET: /run/bird/bird.ctl + EVOBGP_TELEMETRY_LOCAL_ID: ${EVOBGP_TELEMETRY_LOCAL_ID:-} EVOBGP_BIRDC_INTERVAL: 30s EVOBGP_BIRD_ACTIVE_DIR: /etc/bird EVOBGP_BIRD_STAGING_DIR: /tmp/evobgp-bird-staging @@ -237,10 +237,13 @@ services: target: /etc/prometheus/prometheus.yml - source: prometheus_alerts_yml target: /etc/prometheus/alerts.yml + volumes: + - prometheus_data:/prometheus command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus - --storage.tsdb.retention.time=7d + - --storage.tsdb.retention.size=${EVOBGP_PROMETHEUS_RETENTION_SIZE:-2GB} - --web.enable-lifecycle logging: driver: json-file @@ -361,6 +364,7 @@ services: volumes: pgdata: + prometheus_data: bird_etc: bird_run: traefik_letsencrypt: diff --git a/deploy/docker/README.md b/deploy/docker/README.md index 8fd11df..ef8b2e7 100644 --- a/deploy/docker/README.md +++ b/deploy/docker/README.md @@ -14,20 +14,20 @@ BuildKit кэширует `/go/pkg/mod`, `~/.cache/go-build` и pnpm store че | Образ | 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`; демон не запускается | +| scheduler, ingest, render, deploy | `gcr.io/distroless/static-debian12:nonroot` | static Go (`CGO_ENABLED=0`), без shell; deploy только сверяет drift | +| api, all, 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). +Сборка Go: `golang:1.24-alpine`. Для Go Dockerfile действует отдельный [Dockerfile.dockerignore](gobinary/Dockerfile.dockerignore): в контексте остаются только Go-модуль, исходники, миграции, встроенные shell-скрипты и сборочный скрипт BIRD. Web-исходники не входят в Go-контекст. Кэш Go build также зависит от `GIT_SHA` и `BUILD_TIME`, поэтому эффект для web-only коммитов требует замера. Web context режется корневым [.dockerignore](../../.dockerignore). ### Кэш registry - `git.shx.one//evobgp-buildcache:go-buildcache` — **запись** только из `go-build-all` - `git.shx.one//evobgp-buildcache:web-buildcache` — **запись** только из `web-build` - `git.shx.one//evobgp-buildcache:birdc-buildcache` — **запись** только из `go-birdc` -- `git.shx.one//evobgp-buildcache:base-*` — зеркало FROM с **Docker Hub** (`docker.io/library/…`; distroless — `gcr.io`). Только `linux/amd64`; skip если тег уже в Gitea. Неуспешный copy не валит CD — bake берёт Docker Hub FROM для этой базы. +- `git.shx.one//evobgp-buildcache:base-*` — зеркало FROM с **Docker Hub** (`docker.io/library/…`; distroless — `gcr.io`). Только `linux/amd64`; существующий тег не копируется повторно, а в bake передаётся `repo@sha256:…`. `MIRROR_REFRESH=1` принудительно обновляет зеркало и завершает скрипт с ошибкой, если copy или digest lookup не удались. Без refresh неуспешный copy или digest lookup оставляет исходный upstream FROM для этой базы. `pull = false` в bake: не перекачивать FROM, если слой уже в builder. Не запускайте `docker system prune -a` на runner. diff --git a/deploy/docker/docker-bake.hcl b/deploy/docker/docker-bake.hcl index e9bfce8..3351afd 100644 --- a/deploy/docker/docker-bake.hcl +++ b/deploy/docker/docker-bake.hcl @@ -236,7 +236,7 @@ target "evobgp-render" { } target "evobgp-deploy" { - inherits = ["_go-runtime-birdc"] + inherits = ["_go-runtime"] args = { BIN = "evobgp-deploy" } tags = image-tags("evobgp-deploy") } diff --git a/deploy/docker/gobinary/Dockerfile b/deploy/docker/gobinary/Dockerfile index a19888b..c7d210f 100644 --- a/deploy/docker/gobinary/Dockerfile +++ b/deploy/docker/gobinary/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 # Универсальная сборка бинарей cmd/* (ARG BIN) или всех сразу (target build-all). -# Воркеры (runtime): distroless static. api/all/deploy/node (runtime-birdc): debian-slim + bird + birdc. +# Воркеры, включая deploy (runtime): distroless static. api/all/node (runtime-birdc): debian-slim + bird + birdc. # CI: docker buildx bake -f deploy/docker/docker-bake.hcl ARG BASE_GOLANG=docker.io/library/golang:1.24-alpine ARG BASE_DEBIAN=docker.io/library/debian:bookworm-slim @@ -64,7 +64,7 @@ COPY --from=build-all /out/${BIN} /usr/local/bin/evobgp EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/evobgp"] -# api / all / deploy / node — bird -p (parse-check) + birdc configure. +# api / all / node — bird -p (parse-check) + birdc configure. # Демон BIRD в этом контейнере не запускается; процесс bird — в образе evobgp-bird2. FROM ${BASE_DEBIAN} AS runtime-birdc RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ diff --git a/deploy/docker/gobinary/Dockerfile.dockerignore b/deploy/docker/gobinary/Dockerfile.dockerignore new file mode 100644 index 0000000..27f62c7 --- /dev/null +++ b/deploy/docker/gobinary/Dockerfile.dockerignore @@ -0,0 +1,17 @@ +# Go builds use only the Go module and assets embedded into the binaries. +# This file takes precedence over the repository .dockerignore for this Dockerfile. +* +!go.mod +!go.sum +!cmd/ +!cmd/** +!internal/ +!internal/** +!migrations/ +!migrations/** +!deploy/ +!deploy/docker/ +!deploy/docker/bird/ +!deploy/docker/bird/bird-from-source.sh +**/*_test.go +**/*.md diff --git a/deploy/docker/mirror-base-images.sh b/deploy/docker/mirror-base-images.sh index b299e18..915bbad 100644 --- a/deploy/docker/mirror-base-images.sh +++ b/deploy/docker/mirror-base-images.sh @@ -4,7 +4,8 @@ # REGISTRY=git.shx.one/ (без суффикса /evobgp-buildcache) # # Источник library-образов — Docker Hub (docker.io), не public.ecr.aws (429 Too Many Requests). -# Если тег уже есть в Gitea — skip (без повторного pull). +# Если тег уже есть в Gitea — skip. MIRROR_REFRESH=1 принудительно обновляет зеркала. +# В bake передаётся immutable digest зеркала, а не его изменяемый tag. # Копируем только linux/amd64 — bake platforms совпадает, multi-arch индекс не нужен. # Шаг CD не должен падать: неуспешный copy оставляет bake на Docker Hub FROM для этой базы. set -eu @@ -13,6 +14,12 @@ CACHE_REPO="${REGISTRY}/evobgp-buildcache" ENV_FILE="${MIRROR_ENV_FILE:-}" PLATFORM="${MIRROR_PLATFORM:-linux/amd64}" RETRIES="${MIRROR_RETRIES:-4}" +REFRESH="${MIRROR_REFRESH:-0}" + +case "$REFRESH" in + 0|1) ;; + *) echo "MIRROR_REFRESH must be 0 or 1" >&2; exit 2 ;; +esac tmp="$(mktemp)" cleanup() { rm -f "$tmp"; } @@ -22,6 +29,14 @@ dest_exists() { docker buildx imagetools inspect "$1" >/dev/null 2>&1 } +dest_digest() { + digest="$(docker buildx imagetools inspect --format '{{.Manifest.Digest}}' "$1")" || return 1 + case "$digest" in + sha256:????????????????????????????????????????????????????????????????) printf '%s\n' "$digest" ;; + *) echo "invalid mirror digest for $1: $digest" >&2; return 1 ;; + esac +} + copy_retry() { src="$1" dest="$2" @@ -31,9 +46,11 @@ copy_retry() { if docker buildx imagetools create --platform "$PLATFORM" --tag "$dest" "$src"; then return 0 fi - delay=$((n * 25)) - echo "mirror retry ${n}/${RETRIES}, sleep ${delay}s: ${dest}" - sleep "$delay" + if [ "$n" -lt "$RETRIES" ]; then + delay=$((n * 25)) + echo "mirror retry ${n}/${RETRIES}, sleep ${delay}s: ${dest}" + sleep "$delay" + fi done return 1 } @@ -43,30 +60,36 @@ mirror() { tag="$2" var="$3" dest="${CACHE_REPO}:${tag}" - if dest_exists "$dest"; then + if [ "$REFRESH" = 0 ] && dest_exists "$dest"; then echo "skip (already in registry): ${dest}" - printf '%s=%s\n' "$var" "$dest" >>"$tmp" + else + echo "mirror ${src} -> ${dest} (${PLATFORM})" + if ! copy_retry "$src" "$dest"; then + if [ "$REFRESH" = 1 ]; then + echo "error: refresh failed for ${dest}" >&2 + return 1 + fi + echo "warn: ${dest} not mirrored — bake uses upstream FROM for ${var}" + return 0 + fi + fi + if digest="$(dest_digest "$dest")"; then + printf '%s=%s@%s\n' "$var" "$CACHE_REPO" "$digest" >>"$tmp" return 0 fi - echo "mirror ${src} -> ${dest} (${PLATFORM})" - if copy_retry "$src" "$dest"; then - printf '%s=%s\n' "$var" "$dest" >>"$tmp" - return 0 + if [ "$REFRESH" = 1 ]; then + echo "error: cannot resolve refreshed digest for ${dest}" >&2 + return 1 fi - echo "warn: ${dest} not mirrored — bake uses Docker Hub FROM for ${var}" + echo "warn: cannot resolve digest for ${dest} — bake uses upstream FROM for ${var}" return 0 } mirror "docker.io/library/golang:1.24-alpine" "base-golang-1.24-alpine" BASE_GOLANG -sleep 8 mirror "docker.io/library/debian:bookworm-slim" "base-debian-bookworm-slim" BASE_DEBIAN -sleep 8 mirror "docker.io/library/node:22-alpine" "base-node-22-alpine" BASE_NODE -sleep 8 mirror "docker.io/library/nginx:1.27-alpine" "base-nginx-1.27-alpine" BASE_NGINX -sleep 8 mirror "docker.io/library/ubuntu:noble" "base-ubuntu-noble" BASE_UBUNTU -sleep 8 mirror "gcr.io/distroless/static-debian12:nonroot" "base-distroless-static-debian12-nonroot" BASE_DISTROLESS echo "----- mirrored BASE_* -----" diff --git a/deploy/prometheus/alerts.yml b/deploy/prometheus/alerts.yml index 11f294d..019a6c4 100644 --- a/deploy/prometheus/alerts.yml +++ b/deploy/prometheus/alerts.yml @@ -3,7 +3,7 @@ groups: - name: evobgp-controlplane rules: - alert: EvoBGPMaterializedPrefixesZero - expr: evobgp_materialized_prefixes_max == 0 + expr: evobgp_stored_revision_materialized_prefixes_max == 0 and evobgp_store_metrics_collection_success == 1 for: 2h labels: severity: warning @@ -11,6 +11,15 @@ groups: summary: "В сторе control-plane нет ни одной ревизии с ненулевым materialized_prefix_count." description: "Проверьте render/ingest и наличие ревизий." + - alert: EvoBGPStoreMetricsCollectionFailed + expr: evobgp_store_metrics_collection_success == 0 + for: 5m + labels: + severity: warning + annotations: + summary: "Не удалось прочитать агрегаты хранилища для /metrics." + description: "Проверьте PostgreSQL и ошибки API; отсутствие агрегатов не означает нуль." + - alert: EvoBGPBirdEstablishedMissing expr: | evobgp_bird_protocols_scrape_success == 1