From 19ad30e8c488868455c760008bef52012a3c7296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Fri, 28 Nov 2025 09:31:21 +0100 Subject: [PATCH] revert: tailscale docker sidecar --- ai/compose.yaml | 167 ++++++++++++++++++++++-------------------------- arty.yml | 54 ++++++++++++++++ 2 files changed, 129 insertions(+), 92 deletions(-) diff --git a/ai/compose.yaml b/ai/compose.yaml index e2bdfed..301f2f0 100644 --- a/ai/compose.yaml +++ b/ai/compose.yaml @@ -96,7 +96,6 @@ services: image: ghcr.io/berriai/litellm:main-latest container_name: ${AI_COMPOSE_PROJECT_NAME}_litellm restart: unless-stopped - network_mode: "service:tailscale" environment: TZ: ${TIMEZONE:-Europe/Berlin} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} @@ -122,9 +121,21 @@ services: ] depends_on: - ai_postgres - - tailscale healthcheck: disable: true + networks: + - compose_network + labels: + - "traefik.enable=true" + - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)" + - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.entrypoints=web" + - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)" + - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.tls.certresolver=resolver" + - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.entrypoints=web-secure" + - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.middlewares=security-headers@file" + - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-litellm.loadbalancer.server.port=4000" + - "traefik.docker.network=${NETWORK_NAME}" + - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}" # Facefusion - AI face swapping and enhancement facefusion: @@ -168,130 +179,99 @@ services: image: nginx:alpine container_name: ${AI_COMPOSE_PROJECT_NAME}_comfyui restart: unless-stopped - network_mode: "service:tailscale" environment: TZ: ${TIMEZONE:-Europe/Berlin} - GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST} + GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator} GPU_SERVICE_PORT: ${COMFYUI_BACKEND_PORT:-8188} volumes: - ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" - depends_on: - - tailscale - - audiocraft: - image: nginx:alpine - container_name: ${AI_COMPOSE_PROJECT_NAME}_audiocraft - restart: unless-stopped - network_mode: "service:tailscale" - environment: - TZ: ${TIMEZONE:-Europe/Berlin} - GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST} - GPU_SERVICE_PORT: ${AUDIOCRAFT_BACKEND_PORT:-7860} - volumes: - - ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro - command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" - depends_on: - - tailscale - - upscale: - image: nginx:alpine - container_name: ${AI_COMPOSE_PROJECT_NAME}_upscale - restart: unless-stopped - network_mode: "service:tailscale" - environment: - TZ: ${TIMEZONE:-Europe/Berlin} - GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST} - GPU_SERVICE_PORT: ${UPSCALE_BACKEND_PORT:-8080} - volumes: - - ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro - command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" - depends_on: - - tailscale - - # Supervisor UI - Modern web interface for RunPod process management - supervisor: - image: dev.pivoine.art/valknar/supervisor-ui:latest - container_name: ${AI_COMPOSE_PROJECT_NAME}_supervisor_ui - restart: unless-stopped - network_mode: "service:tailscale" - environment: - TZ: ${TIMEZONE:-Europe/Berlin} - NODE_ENV: production - # Connect to RunPod Supervisor via Tailscale - SUPERVISOR_HOST: ${GPU_TAILSCALE_HOST} - SUPERVISOR_PORT: ${SUPERVISOR_BACKEND_PORT:-9001} - # No auth needed - Supervisor has auth disabled (protected by Authelia) - depends_on: - - tailscale - - # Tailscale VPN - Provides network access to RunPod GPU services - tailscale: - image: tailscale/tailscale:latest - hostname: vps - container_name: ${AI_COMPOSE_PROJECT_NAME}_tailscale - cap_add: - - NET_ADMIN - - SYS_MODULE - volumes: - - tailscale_state:/var/lib/tailscale - - /dev/net/tun:/dev/net/tun - environment: - - TS_AUTHKEY=${TAILSCALE_AUTHKEY} - - TS_STATE_DIR=/var/lib/tailscale - - TS_USERSPACE=true - - TS_ACCEPT_DNS=true - restart: unless-stopped networks: - compose_network labels: - "traefik.enable=true" - - "traefik.docker.network=${NETWORK_NAME}" - # === ComfyUI (port 80 - nginx proxy) === - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.rule=Host(`${AI_COMFYUI_TRAEFIK_HOST:-comfy.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.entrypoints=web" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.rule=Host(`${AI_COMFYUI_TRAEFIK_HOST:-comfy.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.tls.certresolver=resolver" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.entrypoints=web-secure" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.middlewares=net-authelia,security-headers@file" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.service=${AI_COMPOSE_PROJECT_NAME}-comfyui" - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-comfyui.loadbalancer.server.port=80" - # === AudioCraft (port 7860 via nginx on 80) === + - "traefik.docker.network=${NETWORK_NAME}" + + audiocraft: + image: nginx:alpine + container_name: ${AI_COMPOSE_PROJECT_NAME}_audiocraft + restart: unless-stopped + environment: + TZ: ${TIMEZONE:-Europe/Berlin} + GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator} + GPU_SERVICE_PORT: ${AUDIOCRAFT_BACKEND_PORT:-7860} + volumes: + - ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro + command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" + networks: + - compose_network + labels: + - "traefik.enable=true" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web.rule=Host(`${AI_AUDIOCRAFT_TRAEFIK_HOST:-audiocraft.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web.entrypoints=web" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.rule=Host(`${AI_AUDIOCRAFT_TRAEFIK_HOST:-audiocraft.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.tls.certresolver=resolver" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.entrypoints=web-secure" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.middlewares=net-authelia,security-headers@file" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-audiocraft-web-secure.service=${AI_COMPOSE_PROJECT_NAME}-audiocraft" - - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-audiocraft.loadbalancer.server.port=7860" - # === Upscale (port 8080 via nginx on 80) === + - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-audiocraft.loadbalancer.server.port=80" + - "traefik.docker.network=${NETWORK_NAME}" + + upscale: + image: nginx:alpine + container_name: ${AI_COMPOSE_PROJECT_NAME}_upscale + restart: unless-stopped + environment: + TZ: ${TIMEZONE:-Europe/Berlin} + GPU_SERVICE_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator} + GPU_SERVICE_PORT: ${UPSCALE_BACKEND_PORT:-8080} + volumes: + - ./nginx.conf.template:/etc/nginx/nginx.conf.template:ro + command: /bin/sh -c "envsubst '$${GPU_SERVICE_HOST},$${GPU_SERVICE_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" + networks: + - compose_network + labels: + - "traefik.enable=true" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web.rule=Host(`${AI_UPSCALE_TRAEFIK_HOST:-upscale.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web.entrypoints=web" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.rule=Host(`${AI_UPSCALE_TRAEFIK_HOST:-upscale.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.tls.certresolver=resolver" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.entrypoints=web-secure" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.middlewares=net-authelia,security-headers@file" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-upscale-web-secure.service=${AI_COMPOSE_PROJECT_NAME}-upscale" - - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-upscale.loadbalancer.server.port=8080" - # === Supervisor UI (port 3000) === + - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-upscale.loadbalancer.server.port=80" + - "traefik.docker.network=${NETWORK_NAME}" + + # Supervisor UI - Modern web interface for RunPod process management + supervisor: + image: dev.pivoine.art/valknar/supervisor-ui:latest + container_name: ${AI_COMPOSE_PROJECT_NAME}_supervisor_ui + restart: unless-stopped + environment: + TZ: ${TIMEZONE:-Europe/Berlin} + NODE_ENV: production + # Connect to RunPod Supervisor via Tailscale (host Tailscale provides DNS) + SUPERVISOR_HOST: ${GPU_TAILSCALE_HOST:-runpod-ai-orchestrator} + SUPERVISOR_PORT: ${SUPERVISOR_BACKEND_PORT:-9001} + # No auth needed - Supervisor has auth disabled (protected by Authelia) + networks: + - compose_network + labels: + - "traefik.enable=true" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.rule=Host(`${AI_SUPERVISOR_TRAEFIK_HOST:-supervisor.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.entrypoints=web" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.rule=Host(`${AI_SUPERVISOR_TRAEFIK_HOST:-supervisor.ai.pivoine.art}`)" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.tls.certresolver=resolver" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.entrypoints=web-secure" - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.middlewares=net-authelia,security-headers@file" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.service=${AI_COMPOSE_PROJECT_NAME}-supervisor" - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-supervisor.loadbalancer.server.port=3000" - # === LiteLLM (port 4000) === - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.entrypoints=web" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.tls.certresolver=resolver" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.entrypoints=web-secure" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.middlewares=security-headers@file" - - "traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.service=${AI_COMPOSE_PROJECT_NAME}-litellm" - - "traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-litellm.loadbalancer.server.port=4000" + - "traefik.docker.network=${NETWORK_NAME}" + - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}" volumes: ai_postgres_data: @@ -300,5 +280,8 @@ volumes: name: ${AI_COMPOSE_PROJECT_NAME}_webui_data ai_facefusion_data: name: ${AI_COMPOSE_PROJECT_NAME}_facefusion_data - tailscale_state: - name: ${AI_COMPOSE_PROJECT_NAME}_tailscale_state + +networks: + compose_network: + name: ${NETWORK_NAME} + external: true diff --git a/arty.yml b/arty.yml index 819c219..8a3c088 100644 --- a/arty.yml +++ b/arty.yml @@ -262,3 +262,57 @@ scripts: docker restart sexy_api && echo "✓ Directus API restarted" net/create: docker network create "$NETWORK_NAME" + # Setup iptables NAT for Docker containers to reach Tailscale network + # Requires Tailscale installed on host: curl -fsSL https://tailscale.com/install.sh | sh + tailscale/setup: | + echo "Setting up iptables for Docker-to-Tailscale routing..." + + # Enable IP forwarding + sudo sysctl -w net.ipv4.ip_forward=1 + grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf + + # Get Docker network CIDR + DOCKER_CIDR=$(docker network inspect ${NETWORK_NAME} --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "172.18.0.0/16") + echo "Docker network CIDR: $DOCKER_CIDR" + + # Add NAT rule (check if already exists) + if ! sudo iptables -t nat -C POSTROUTING -s "$DOCKER_CIDR" -o tailscale0 -j MASQUERADE 2>/dev/null; then + sudo iptables -t nat -A POSTROUTING -s "$DOCKER_CIDR" -o tailscale0 -j MASQUERADE + echo "✓ iptables NAT rule added" + else + echo "✓ iptables NAT rule already exists" + fi + + # Persist rules + sudo netfilter-persistent save 2>/dev/null || echo "Install iptables-persistent to persist rules: sudo apt install iptables-persistent" + + echo "✓ Tailscale routing configured" + + # Install and configure Tailscale on host with persistent state + tailscale/install: | + echo "Installing Tailscale..." + + # Install Tailscale if not present + if ! command -v tailscale &> /dev/null; then + curl -fsSL https://tailscale.com/install.sh | sh + else + echo "✓ Tailscale already installed" + fi + + # Create state directory for persistence + TAILSCALE_STATE="/var/lib/tailscale" + sudo mkdir -p "$TAILSCALE_STATE" + + # Start and enable tailscaled service + sudo systemctl enable --now tailscaled + + # Connect to Tailscale network + echo "Connecting to Tailscale..." + sudo tailscale up --authkey="$TAILSCALE_AUTHKEY" --hostname=vps + + # Show status + echo "" + tailscale status + echo "" + echo "✓ Tailscale installed and connected" + echo " Run 'arty tailscale/setup' to configure iptables routing for Docker"