version: "3.9" services: # 1) 本机 Consul agent(client) consul-agent: image: hashicorp/consul:1.21 container_name: consul-agent network_mode: "host" # 避免 8301/udp/lan gossip 的端口映射问题 command: > agent -server=false -client=0.0.0.0 -bind=${LOCAL_TS_IP} -advertise=${LOCAL_TS_IP} -retry-join=${CONSUL_SERVER_IP} -datacenter=${CONSUL_DC} -data-dir=/consul/data -leave-on-terminate volumes: - ./consul-data:/consul/data healthcheck: test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8500/v1/agent/self >/dev/null"] interval: 3s timeout: 2s retries: 60 restart: unless-stopped # 2) 你的服务(示例:Python http.server) api: image: python:3.12-slim container_name: api command: ["python", "-m", "http.server", "${SERVICE_PORT}"] # 关键:只绑定到本机 Tailscale IP,避免暴露到 0.0.0.0 ports: - "${LOCAL_TS_IP}:${SERVICE_PORT}:${SERVICE_PORT}" depends_on: consul-agent: condition: service_healthy restart: unless-stopped # 3) registrar 旁车:把服务注册到"云端" Consul(server) registrar: image: hashicorp/consul:1.21 container_name: registrar network_mode: "host" depends_on: consul-agent: condition: service_healthy api: condition: service_started environment: SERVICE_NAME: "${SERVICE_NAME}" SERVICE_ADDR: "${LOCAL_TS_IP}" # 共用宿主 Tailscale IP SERVICE_PORT: "${SERVICE_PORT}" SERVICE_PROTOCOL: "http" # http | tcp ROUTE_HOST: "${ROUTE_HOST}" # 要暴露的域名 CONSUL_HTTP_ADDR: "http://${CONSUL_SERVER_IP}:8500" CHECK_TYPE: "${CHECK_TYPE}" CHECK_PATH: "${CHECK_PATH}" CHECK_INTERVAL: "${CHECK_INTERVAL}" CHECK_TIMEOUT: "${CHECK_TIMEOUT}" DEREG_AFTER: "${DEREG_AFTER}" TRAEFIK_HTTP_ENTRYPOINT: "${TRAEFIK_HTTP_ENTRYPOINT}" TRAEFIK_TCP_ENTRYPOINT: "${TRAEFIK_TCP_ENTRYPOINT}" TRAEFIK_CERT_RESOLVER: "${TRAEFIK_CERT_RESOLVER}" volumes: - ./registrar.sh:/registrar.sh:ro entrypoint: ["/bin/sh","-lc","/registrar.sh"] restart: unless-stopped