diff --git a/rxprod-compose/docker-compose.yml b/rxprod-compose/docker-compose.yml index b59b1e0..0958176 100644 --- a/rxprod-compose/docker-compose.yml +++ b/rxprod-compose/docker-compose.yml @@ -1,18 +1,3 @@ -# Простой (без profiles/override/prod) docker-compose для сервера с ролями -# control-api + admin-dashboard + prober. Реальный OpenStack (mode: real) — -# validator-agent сюда не входит, он работает отдельно на реальных -# ВМ-валидаторах в облаке и обращается к control-api по адресу этого хоста. -# -# Запуск: -# cp .env.example .env # заполнить реальными OS_*-креденшлами и PROBER_SITE_ID -# # поправить путь к control-api.yaml ниже, если реальный файл лежит не в -# # /etc/cloud-ip-validator/control-api.yaml -# docker compose up -d --build -# curl -s http://localhost:8080/healthz -# -# (из корня репозитория тот же стек можно поднять гибче, с profiles и -# dev/prod-разделением — см. deploy/docker/docker-compose.yml) - name: cloud-ip-validator networks: @@ -25,20 +10,15 @@ volumes: services: control-api: - build: - context: .. - dockerfile: deploy/docker/control-api/Dockerfile - image: cloud-ip-validator-control-api + image: civ-capi platform: linux/amd64 restart: unless-stopped networks: [backend] ports: - - "8080:8080" + - "8081:8081" volumes: - # Поправьте путь слева, если реальный control-api.yaml (mode: real, - # с настоящими validators/sites/targets/ip_addresses) лежит не здесь. - - /etc/cloud-ip-validator/control-api.yaml:/etc/cloud-ip-validator/control-api.yaml:ro - - control-api-db:/var/lib/cloud-ip-validator + - /opt/lvraid/claude/cloud_ip_validator/rxprod-compose/control-api.yaml:/etc/cloud-ip-validator/control-api.yaml:ro + - /opt/lvraid/claude/cloud_ip_validator/rxprod-compose/capi-db:/var/lib/cloud-ip-validator environment: OS_AUTH_URL: "${OS_AUTH_URL}" OS_PROJECT_ID: "${OS_PROJECT_ID}" @@ -49,40 +29,34 @@ services: OS_USER_DOMAIN_NAME: "${OS_USER_DOMAIN_NAME:-}" OS_PASSWORD: "${OS_PASSWORD:-}" healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8080/healthz"] + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8081/healthz"] interval: 5s timeout: 3s retries: 10 start_period: 5s admin-dashboard: - build: - context: .. - dockerfile: deploy/docker/admin-dashboard/Dockerfile - image: cloud-ip-validator-admin-dashboard + image: civ-adash platform: linux/amd64 restart: unless-stopped networks: [backend] ports: - "8090:8090" environment: - ADMIN_DASHBOARD_CONTROL_API_URL: http://control-api:8080 + ADMIN_DASHBOARD_CONTROL_API_URL: http://control-api:8081 depends_on: control-api: condition: service_healthy prober: - build: - context: .. - dockerfile: deploy/docker/prober/Dockerfile - image: cloud-ip-validator-prober + image: civ-prober platform: linux/amd64 restart: unless-stopped networks: [backend] cap_add: [NET_RAW] environment: PROBER_SITE_ID: "${PROBER_SITE_ID}" - PROBER_CONTROL_API_URL: http://control-api:8080 + PROBER_CONTROL_API_URL: http://control-api:8081 depends_on: control-api: condition: service_healthy diff --git a/rxprod-compose/.env.example b/rxprod-compose/sources/.env.example similarity index 100% rename from rxprod-compose/.env.example rename to rxprod-compose/sources/.env.example diff --git a/rxprod-compose/sources/admin-dashboard.example.yaml b/rxprod-compose/sources/admin-dashboard.example.yaml new file mode 100644 index 0000000..e2699f6 --- /dev/null +++ b/rxprod-compose/sources/admin-dashboard.example.yaml @@ -0,0 +1,18 @@ +server: + listen_addr: ":8090" + +control_api: + base_url: "http://control-api.internal:8080" + timeout_seconds: 10 + +# Настройки сводки на странице "Обзор" — см. docs/DASHBOARD.md. Оба поля +# влияют только на то, как дашборд группирует уже существующие данные +# control-api (GET /admin/status, GET /admin/ips); никакого нового +# состояния control-api не заводит. +overview: + # Сколько последних завершённых (done/failed) адресов показывать в + # сводке "последняя завершённая проверка" и учитывать в разбивке + # pass/partial/fail/cancelled. + last_completed_count: 20 + # Как часто браузер опрашивает /overview/fragment для live-обновления. + poll_interval_seconds: 5 diff --git a/rxprod-compose/sources/control-api.example.yaml b/rxprod-compose/sources/control-api.example.yaml new file mode 100644 index 0000000..d6c6669 --- /dev/null +++ b/rxprod-compose/sources/control-api.example.yaml @@ -0,0 +1,118 @@ +# Control API configuration. +# +# OpenStack credentials are never set here — only the *names* of the +# environment variables to read them from. The actual values must be +# supplied by the process environment (see deploy/systemd/control-api.service +# and its EnvironmentFile=). + +server: + listen_addr: ":8080" + +database: + path: "/var/lib/cloud-ip-validator/control-api.db" + +openstack: + mode: "real" # "mock" | "real" — mock uses an in-memory + # OpenStack stand-in for local dev/testing + auth_method: "token" # "token" (default) | "password" — see below + + auth_url_env: "OS_AUTH_URL" + project_id_env: "OS_PROJECT_ID" + region_env: "OS_REGION_NAME" + interface_env: "OS_INTERFACE" # optional; empty env value defaults to "public" + + # auth_method: "token" — an admin supplies an already project-scoped + # token directly; it's used as-is for every call, never exchanged for a + # new one. Simplest option, but it can't renew itself: when the token + # expires, control-api starts failing OpenStack calls until the operator + # reissues OS_TOKEN and restarts the process. + token_env: "OS_TOKEN" + + # auth_method: "password" — the client authenticates with a normal + # Keystone username/password and automatically re-authenticates + # (mints a fresh token) whenever the current one is rejected, for as + # long as the process runs. Trade-off: a long-lived password credential + # sits in the environment file instead of a token. + username_env: "OS_USERNAME" + user_domain_name_env: "OS_USER_DOMAIN_NAME" + password_env: "OS_PASSWORD" + +orchestrator: + poll_interval_seconds: 5 + self_check_timeout_seconds: 60 + max_self_check_retries: 3 + checking_window_seconds: 120 + max_retries: 3 + lease_ttl_seconds: 180 + heartbeat_timeout_seconds: 30 + # Pause (seconds) between FIP association and the start of self-check — + # gives the OpenStack data plane time to start forwarding traffic + # through the newly attached floating IP. 0 = no pause (default). + # This is only the one-time seed value used the first time control-api + # starts against an empty database; after that it's managed at runtime + # via PUT /api/v1/admin/config/orchestrator (or the dashboard's + # /settings page) and this field is ignored. Must satisfy + # fip_settle_seconds + self_check_timeout_seconds < lease_ttl_seconds. + fip_settle_seconds: 0 + +aggregation: + missing_counts_as_fail: true + +# Validators are VMs in the service project; os_port_id is the Neutron port +# ID of each validator's primary NIC, used when associating a floating IP. +validators: + - validator_id: "validator_01" + os_port_id: "REPLACE_WITH_NEUTRON_PORT_ID_1" + - validator_id: "validator_02" + os_port_id: "REPLACE_WITH_NEUTRON_PORT_ID_2" + +# Inbound (prober) checks are OPTIONAL: list here only the external sites +# you actually run a `prober` on — index is any integer >= 1, no cap on +# how many slots you configure. Leave this list empty to disable inbound +# checks entirely — the overall result is then based on egress checks +# alone, and aggregation doesn't wait on any prober. A partial list (e.g. +# just index 1) only waits on that one site. +sites: + - site_id: "site-1" + index: 1 + - site_id: "site-2" + index: 2 + - site_id: "site-3" + index: 3 + +# Outbound/egress check types the validator-agent runs, and which target +# group (below) each runs against. +check_types: + - name: "https" + enabled: true + targets: ["default-targets"] + - name: "icmp" + enabled: true + targets: ["default-targets"] + - name: "ssh" + enabled: false + targets: [] + +targets: + default-targets: + - "https://hub.docker.com" + - "https://github.com" + - "https://packages.ubuntu.com" + +# Inbound checks the 3 external-site probers run directly against each +# validator's currently-assigned floating IP. Like validators/sites/targets/ +# check_types above, this is only a bootstrap seed for a fresh, empty +# database; after that it's managed at runtime via PUT +# /api/v1/admin/config/inbound-checks (or the dashboard's /settings page) +# and this field is ignored. +inbound_checks: + ports: [22, 80, 443, 8080] + icmp: true + +# The pool of public IPv4 addresses to validate, in the order they'll be +# processed (ip_queue.sequence). Every address here is checked through to +# the end of the list. +ip_addresses: + - "203.0.113.10" + - "203.0.113.11" + - "203.0.113.12" diff --git a/rxprod-compose/sources/prober.example.yaml b/rxprod-compose/sources/prober.example.yaml new file mode 100644 index 0000000..441b1d0 --- /dev/null +++ b/rxprod-compose/sources/prober.example.yaml @@ -0,0 +1,11 @@ +# Prober configuration. Runs on one of the 3 external test sites. site_id +# must match one of the control-api config's `sites[].site_id`. + +site_id: "site-1" +control_api_url: "http://control-api.internal:8080" +poll_interval_seconds: 5 + +checks: + tcp_timeout_seconds: 5 + icmp_timeout_seconds: 5 + icmp_count: 3 diff --git a/rxprod-compose/sources/validator-agent.example.yaml b/rxprod-compose/sources/validator-agent.example.yaml new file mode 100644 index 0000000..5f3bd66 --- /dev/null +++ b/rxprod-compose/sources/validator-agent.example.yaml @@ -0,0 +1,28 @@ +# Validator-agent configuration. Runs on each validator VM. validator_id +# must match one of the control-api config's `validators[].validator_id`. + +validator_id: "validator_01" +control_api_url: "http://control-api.internal:8080" +poll_interval_seconds: 5 + +self_check: + timeout_seconds: 10 + # Must be a resource genuinely outside the cloud project — OpenStack only + # applies floating-IP SNAT to traffic leaving via the external network, + # so anything reachable over the project's internal network (including + # control-api itself, if it's on the same internal network) would report + # this validator's private address instead, regardless of whether the + # floating IP is correctly attached. Tried in order; falls through to the + # next URL only on error/timeout, never on a genuine mismatch. Defaults + # to these two public services if omitted. + ip_echo_urls: + - "https://api.ipify.org" + - "https://ifconfig.me/ip" + +checks: + https_timeout_seconds: 10 + icmp_timeout_seconds: 5 + icmp_count: 3 + ssh: + enabled: false + timeout_seconds: 5