Files

212 lines
10 KiB
YAML

services:
metrics:
image: python:3-slim@sha256:a7fb1e634c4a578f9e0bd6327f11a3cde11b7a9395f48e24360c0988bcc5c2bc
container_name: xiteng-site-metrics
restart: unless-stopped
command: ["python3", "/app/metrics.py"]
environment:
- PORT=8092
- HOST_PROC=/host/proc
- DISK_PATH=/host/disk
- CACHE_TTL_SECONDS=2
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=utility
volumes:
- ./metrics.py:/app/metrics.py:ro
- /proc:/host/proc:ro
- /etc/hostname:/host/hostname:ro
- ./:/host/disk:ro
read_only: true
security_opt:
- no-new-privileges:true
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8092/healthz', timeout=2)"]
interval: 15s
timeout: 5s
retries: 3
start_period: 5s
networks:
- homelab_net
labels:
- "traefik.enable=false"
- "xiteng.site.component.host-metrics.enabled=true"
- "xiteng.site.component.host-metrics.name=Host Metrics"
- "xiteng.site.component.host-metrics.description=采集并公开 CPU、内存、磁盘与 GPU 的实时汇总指标。"
- "xiteng.site.component.host-metrics.section=infrastructure"
- "xiteng.site.component.host-metrics.category=可观测性"
- "xiteng.site.component.host-metrics.access=public"
- "xiteng.site.component.host-metrics.access-label=公开只读指标"
- "xiteng.site.component.host-metrics.icon=HW"
- "xiteng.site.component.host-metrics.accent=green"
- "xiteng.site.component.host-metrics.order=590"
registry:
image: node:24-alpine
container_name: xiteng-site-registry
restart: unless-stopped
command: ["node", "/app/registry.mjs"]
environment:
- PORT=8091
- DOCKER_SOCKET=/var/run/docker.sock
- DATABASE_PATH=/data/registry.db
- DISCOVERY_INTERVAL_MS=5000
- MISSING_RETENTION_DAYS=30
- RAW_RETENTION_DAYS=30
- AGGREGATE_RETENTION_DAYS=365
- MONITOR_CONCURRENCY=4
volumes:
- ./registry.mjs:/app/registry.mjs:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data:/data
read_only: true
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8091/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
interval: 15s
timeout: 5s
retries: 3
start_period: 5s
networks:
- homelab_net
labels:
- "traefik.enable=false"
- "xiteng.site.component.site-registry.enabled=true"
- "xiteng.site.component.site-registry.name=Component Registry"
- "xiteng.site.component.site-registry.description=只读发现 Docker Label,并向门户输出白名单组件元数据。"
- "xiteng.site.component.site-registry.section=infrastructure"
- "xiteng.site.component.site-registry.category=门户"
- "xiteng.site.component.site-registry.access=internal"
- "xiteng.site.component.site-registry.access-label=仅容器网络"
- "xiteng.site.component.site-registry.icon=API"
- "xiteng.site.component.site-registry.accent=blue"
- "xiteng.site.component.site-registry.order=12"
xiteng-site:
image: node:24-alpine
container_name: xiteng-site
restart: unless-stopped
command: ["node", "/app/server.mjs"]
environment:
- PORT=8080
- REGISTRY_URL=http://xiteng-site-registry:8091/components
- REGISTRY_ADMIN_URL=http://xiteng-site-registry:8091/admin
- METRICS_URL=http://xiteng-site-metrics:8092/metrics
- KEY_VAULT_URL=http://ai-gateway:8093
- PROVIDER_TEST_URL=http://xiteng-chat:3000/api/provider-test
- KEY_VAULT_TOKEN_FILE=/run/secrets/portal_gateway_hmac
- ADMIN_USERNAME=liooil
- AUTHENTIK_ISSUER=https://auth.xiteng.site
- AUTHENTIK_URL=https://auth.xiteng.site
- AUTHENTIK_TOKEN_FILE=/run/authentik-secrets/portal_api_token
- IDENTITY_AUDIT_PATH=/data/identity-audit.jsonl
volumes:
- ./index.html:/app/index.html:ro
- ./styles.css:/app/styles.css:ro
- ./app.js:/app/app.js:ro
- ./sw.js:/app/sw.js:ro
- ./manifest.webmanifest:/app/manifest.webmanifest:ro
- ./favicon.svg:/app/favicon.svg:ro
- ./favicon.ico:/app/favicon.ico:ro
- ./icons:/app/icons:ro
- ./admin.html:/app/admin.html:ro
- ./admin.js:/app/admin.js:ro
- ./account.html:/app/account.html:ro
- ./account.js:/app/account.js:ro
- ./authentik.mjs:/app/authentik.mjs:ro
- ./server.mjs:/app/server.mjs:ro
- ../ai-gateway/secrets/portal_gateway_hmac:/run/secrets/portal_gateway_hmac:ro
- ../authentik/secrets/portal_api_token:/run/authentik-secrets/portal_api_token:ro
- ./data:/data
read_only: true
security_opt:
- no-new-privileges:true
depends_on:
registry:
condition: service_healthy
metrics:
condition: service_healthy
networks:
- homelab_net
labels:
# Traefik
- "traefik.enable=true"
- "traefik.http.routers.xiteng-site.rule=Host(`xiteng.site`)"
- "traefik.http.routers.xiteng-site.entrypoints=websecure"
- "traefik.http.routers.xiteng-site.tls=true"
- "traefik.http.routers.xiteng-site.tls.certresolver=cfresolver"
- "traefik.http.routers.xiteng-site.service=xiteng-site"
- "traefik.http.routers.xiteng-site.middlewares=xiteng-site-nocache"
- "traefik.http.services.xiteng-site.loadbalancer.server.port=8080"
- "traefik.http.routers.xiteng-site-http.rule=Host(`xiteng.site`)"
- "traefik.http.routers.xiteng-site-http.service=xiteng-site"
- "traefik.http.routers.xiteng-site-http.entrypoints=web"
- "traefik.http.routers.xiteng-site-http.middlewares=xiteng-site-nocache"
- "traefik.http.middlewares.xiteng-site-nocache.headers.customresponseheaders.Cache-Control=no-store, no-cache, must-revalidate"
- "traefik.http.middlewares.xiteng-site-nocache.headers.customresponseheaders.Pragma=no-cache"
- "traefik.http.middlewares.xiteng-site-nocache.headers.customresponseheaders.Expires=0"
# Authentik ForwardAuth protects all Portal administration and account routes.
- "traefik.http.middlewares.xiteng-admin-scheme.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.xiteng-admin-auth.forwardauth.address=http://authentik-outpost:9000/outpost.goauthentik.io/auth/traefik"
- "traefik.http.middlewares.xiteng-admin-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.xiteng-admin-auth.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid"
- "traefik.http.middlewares.xiteng-admin-context.headers.customrequestheaders.X-Portal-Authenticated=1"
- "traefik.http.routers.xiteng-site-admin.rule=Host(`xiteng.site`) && (PathPrefix(`/admin`) || PathPrefix(`/account`) || PathPrefix(`/api/admin`) || PathPrefix(`/api/account`))"
- "traefik.http.routers.xiteng-site-admin.entrypoints=websecure"
- "traefik.http.routers.xiteng-site-admin.tls=true"
- "traefik.http.routers.xiteng-site-admin.tls.certresolver=cfresolver"
- "traefik.http.routers.xiteng-site-admin.service=xiteng-site"
- "traefik.http.routers.xiteng-site-admin.middlewares=xiteng-admin-scheme,xiteng-admin-auth,xiteng-admin-context,xiteng-site-nocache"
- "traefik.http.routers.xiteng-site-admin.priority=200"
- "traefik.http.routers.xiteng-site-admin-http.rule=Host(`xiteng.site`) && (PathPrefix(`/admin`) || PathPrefix(`/account`) || PathPrefix(`/api/admin`) || PathPrefix(`/api/account`))"
- "traefik.http.routers.xiteng-site-admin-http.entrypoints=web"
- "traefik.http.routers.xiteng-site-admin-http.service=xiteng-site"
- "traefik.http.routers.xiteng-site-admin-http.middlewares=xiteng-admin-scheme,xiteng-admin-auth,xiteng-admin-context,xiteng-site-nocache"
- "traefik.http.routers.xiteng-site-admin-http.priority=200"
# The native Authentik admin UI is hidden; login, OAuth/OIDC and API endpoints remain available.
- "traefik.http.routers.authentik-admin-hidden.rule=Host(`auth.xiteng.site`) && PathPrefix(`/if/admin`)"
- "traefik.http.routers.authentik-admin-hidden.entrypoints=websecure"
- "traefik.http.routers.authentik-admin-hidden.tls=true"
- "traefik.http.routers.authentik-admin-hidden.tls.certresolver=cfresolver"
- "traefik.http.routers.authentik-admin-hidden.service=xiteng-site"
- "traefik.http.routers.authentik-admin-hidden.priority=5000"
- "traefik.http.routers.authentik-admin-hidden-http.rule=Host(`auth.xiteng.site`) && PathPrefix(`/if/admin`)"
- "traefik.http.routers.authentik-admin-hidden-http.entrypoints=web"
- "traefik.http.routers.authentik-admin-hidden-http.service=xiteng-site"
- "traefik.http.routers.authentik-admin-hidden-http.priority=5000"
- "xiteng.site.cache.portal-static.enabled=true"
- "xiteng.site.cache.portal-static.routers=xiteng-site,xiteng-site-http"
- "xiteng.site.cache.portal-static.paths=/icons/"
- "xiteng.site.cache.portal-static.edge-ttl=604800"
- "xiteng.site.cache.portal-static.stale-while-revalidate=86400"
# Dynamic catalog
- "xiteng.site.component.portal.enabled=true"
- "xiteng.site.component.portal.name=Xiteng Portal"
- "xiteng.site.component.portal.description=唯一公开入口、个人主页与 Authentik 保护的 Homelab 管理控制面。"
- "xiteng.site.component.portal.section=infrastructure"
- "xiteng.site.component.portal.category=门户"
- "xiteng.site.component.portal.url=https://xiteng.site"
- "xiteng.site.component.portal.access=public"
- "xiteng.site.component.portal.access-label=公开目录"
- "xiteng.site.component.portal.icon=XT"
- "xiteng.site.component.portal.icon-url=https://xiteng.site/favicon.svg"
- "xiteng.site.component.portal.accent=red"
- "xiteng.site.component.portal.order=10"
- "xiteng.site.component.portal.monitor.enabled=true"
- "xiteng.site.component.portal.monitor.url=http://xiteng-site:8080/healthz"
- "xiteng.site.component.portal.monitor.interval=60"
- "xiteng.site.component.portal.monitor.failures=3"
- "xiteng.site.component.portal.monitor.timeout=10"
- "xiteng.site.component.portal.monitor.accept=200-299"
networks:
homelab_net:
external: true