# Повний стек NetPulse: БД, кеш, міграції, API з інтерфейсом, колектор # зондів і зворотний проксі з автоматичним TLS. # # Швидкий старт: # cp deploy/.env.example .env # і заповнити секрети # docker compose up -d # docker compose run --rm api netpulse-user -tenant default -login admin -role owner # # Тільки стенд для розробки (БД і кеш, решта — з go run): # docker compose up -d db cache name: netpulse x-server-env: &server-env NETPULSE_DSN: postgres://netpulse:${POSTGRES_PASSWORD:?потрібен POSTGRES_PASSWORD}@db:5432/netpulse?sslmode=disable NETPULSE_DEK: ${NETPULSE_DEK:?потрібен NETPULSE_DEK — див. deploy/README.md} NETPULSE_LOG_LEVEL: ${NETPULSE_LOG_LEVEL:-info} TZ: ${TZ:-Europe/Kyiv} services: db: image: timescale/timescaledb:2.17.2-pg16 environment: POSTGRES_USER: netpulse POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?потрібен POSTGRES_PASSWORD} POSTGRES_DB: netpulse TIMESCALEDB_TELEMETRY: "off" command: - postgres - -c - shared_preload_libraries=timescaledb - -c - max_connections=200 - -c - shared_buffers=${PG_SHARED_BUFFERS:-512MB} - -c - timescaledb.max_background_workers=8 volumes: - db-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U netpulse -d netpulse"] interval: 5s timeout: 5s retries: 20 restart: unless-stopped cache: image: docker.dragonflydb.io/dragonflydb/dragonfly:v1.25.5 ulimits: memlock: -1 volumes: - cache-data:/data restart: unless-stopped # Міграції окремою службою, а не на старті API. # # API запускається в кількох примірниках, і накочування схеми зі старту # означало б гонку між ними. Тут же — один запуск, який мусить # завершитись успіхом, перш ніж піднімуться API й колектор. migrate: build: &server-build context: . dockerfile: deploy/Dockerfile.server args: VERSION: ${NETPULSE_VERSION:-dev} COMMIT: ${NETPULSE_COMMIT:-none} image: netpulse/server:${NETPULSE_VERSION:-dev} entrypoint: ["netpulse-migrate"] environment: *server-env depends_on: db: condition: service_healthy restart: "no" api: build: *server-build image: netpulse/server:${NETPULSE_VERSION:-dev} entrypoint: ["netpulse-api"] command: - -listen=:8080 - -alert-interval=${NETPULSE_ALERT_INTERVAL:-30s} environment: <<: *server-env NETPULSE_JWT_SECRET: ${NETPULSE_JWT_SECRET:?потрібен NETPULSE_JWT_SECRET — не менше 32 байтів} # Історія конфігів у Git. Спільний том для API й колектора: # колектор туди пише під час бекапу, API звідти читає для порівняння # версій. Різні томи означали б, що половина історії видна одному # процесу, а половина другому. volumes: - git-data:/var/lib/netpulse depends_on: migrate: condition: service_completed_successfully cache: condition: service_started healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"] interval: 10s timeout: 3s retries: 6 start_period: 15s restart: unless-stopped # Колектор зондів. TLS знімає Caddy, всередині мережі — h2c, тому # -insecure. Порт назовні сам не публікує: до нього ходять через # проксі, який має справжній сертифікат. collector: build: *server-build image: netpulse/server:${NETPULSE_VERSION:-dev} entrypoint: ["netpulse-server"] command: - -listen=:9443 - -insecure environment: *server-env # Історія конфігів у Git. Спільний том для API й колектора: # колектор туди пише під час бекапу, API звідти читає для порівняння # версій. Різні томи означали б, що половина історії видна одному # процесу, а половина другому. volumes: - git-data:/var/lib/netpulse depends_on: migrate: condition: service_completed_successfully restart: unless-stopped proxy: image: caddy:2.8-alpine environment: NETPULSE_DOMAIN: ${NETPULSE_DOMAIN:?потрібен NETPULSE_DOMAIN} # Порожній рядок Caddy не приймає: директива email без значення — # помилка розбору, і проксі не підніметься взагалі. Тому підстановка # спрацьовує і на незадану змінну, і на задану порожньою. ACME_EMAIL: ${ACME_EMAIL:-admin@${NETPULSE_DOMAIN}} volumes: - ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config ports: - "80:80" - "443:443" # Окремий порт для зондів: вони говорять gRPC, а не HTTP, і # ділити з ним 443 означало б розрізняти протоколи за шляхом — # зайва крихкість там, де порт коштує нічого. - "9443:9443" depends_on: - api - collector restart: unless-stopped # Зонд на тому ж хості, що й сервер: базовий моніторинг самої # інсталяції. Профіль, а не звичайна служба: у типовому розгортанні # зонди стоять у мережах клієнтів, а не поруч із сервером. agent: build: context: . dockerfile: deploy/Dockerfile.agent args: VERSION: ${NETPULSE_VERSION:-dev} COMMIT: ${NETPULSE_COMMIT:-none} image: netpulse/agent:${NETPULSE_VERSION:-dev} profiles: ["agent"] command: - -server=collector:9443 - -insecure - -modules=icmp,snmp,topology,ncm environment: NETPULSE_ENROLL: ${NETPULSE_ENROLL:-} NETPULSE_NAME: ${NETPULSE_AGENT_NAME:-локальний зонд} TZ: ${TZ:-Europe/Kyiv} cap_add: - NET_RAW volumes: - agent-identity:/var/lib/netpulse depends_on: - collector restart: unless-stopped volumes: db-data: git-data: cache-data: caddy-data: caddy-config: agent-identity: