#!/bin/sh set -eu : "${SERVICE_NAME:?need SERVICE_NAME}" : "${SERVICE_ADDR:?need SERVICE_ADDR}" : "${SERVICE_PORT:?need SERVICE_PORT}" : "${ROUTE_HOST:?need ROUTE_HOST}" CONSUL="${CONSUL_HTTP_ADDR:?need CONSUL_HTTP_ADDR}" SERVICE_PROTOCOL="${SERVICE_PROTOCOL:-http}" # http | tcp CHECK_TYPE="${CHECK_TYPE:-tcp}" # http | tcp CHECK_PATH="${CHECK_PATH:-/}" CHECK_INTERVAL="${CHECK_INTERVAL:-10s}" CHECK_TIMEOUT="${CHECK_TIMEOUT:-2s}" DEREG_AFTER="${DEREG_AFTER:-1m}" TRAEFIK_HTTP_ENTRYPOINT="${TRAEFIK_HTTP_ENTRYPOINT:-websecure}" TRAEFIK_TCP_ENTRYPOINT="${TRAEFIK_TCP_ENTRYPOINT:-tcp}" TRAEFIK_CERT_RESOLVER="${TRAEFIK_CERT_RESOLVER:-alidns}" TLS_MODE="${TLS_MODE:-terminating}" # terminating | passthrough | plaintext # TRAEFIK_CERT_RESOLVER="${TRAEFIK_CERT_RESOLVER:-cf}" echo "[registrar] consul: $CONSUL, service: $SERVICE_NAME@$SERVICE_ADDR:$SERVICE_PORT" # 等云端 Consul Server 可用 for i in $(seq 1 90); do if wget -qO- "$CONSUL/v1/status/leader" >/dev/null 2>&1; then break fi sleep 1 done ID="${SERVICE_NAME}-${SERVICE_ADDR}-${SERVICE_PORT}" # 组装 Traefik tags(按“行”累加,避免值中逗号被拆) NL=' ' TAGS="traefik.enable=true" if [ "$SERVICE_PROTOCOL" = "http" ]; then TAGS="$TAGS${NL}traefik.http.routers.${SERVICE_NAME}.rule=Host(\`${ROUTE_HOST}\`)" TAGS="$TAGS${NL}traefik.http.routers.${SERVICE_NAME}.entrypoints=${TRAEFIK_HTTP_ENTRYPOINT}" TAGS="$TAGS${NL}traefik.http.routers.${SERVICE_NAME}.tls=true" TAGS="$TAGS${NL}traefik.http.services.${SERVICE_NAME}.loadbalancer.server.scheme=http" TAGS="$TAGS${NL}traefik.http.services.${SERVICE_NAME}.loadbalancer.server.port=${SERVICE_PORT}" # 抢占路由:给当前 Host 的 router 设置更高优先级 TAGS="$TAGS${NL}traefik.http.routers.${SERVICE_NAME}.priority=10000" # 可选中间件(注意:值里有逗号也安全) TAGS="$TAGS${NL}traefik.http.routers.${SERVICE_NAME}.middlewares=gzip-all@file,sec-headers@file" # 如需 ACME 证书解析器可再加一行(取消注释) # TAGS="$TAGS${NL}traefik.http.routers.${SERVICE_NAME}.tls.certresolver=${TRAEFIK_CERT_RESOLVER}" elif [ "$SERVICE_PROTOCOL" = "tcp" ]; then case "$TLS_MODE" in # A:Traefik 终止 TLS(推荐公网) terminating) TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.rule=HostSNI(\`${ROUTE_HOST}\`)" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.entrypoints=${TRAEFIK_TCP_ENTRYPOINT}" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.tls=true" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.tls.certresolver=${TRAEFIK_CERT_RESOLVER}" TAGS="$TAGS${NL}traefik.tcp.services.${SERVICE_NAME}.loadbalancer.server.port=${SERVICE_PORT}" ;; # A-备用:后端自己终止 TLS(需要给 woodpecker-server 配 cert/key) passthrough) TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.rule=HostSNI(\`${ROUTE_HOST}\`)" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.entrypoints=${TRAEFIK_TCP_ENTRYPOINT}" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.tls.passthrough=true" TAGS="$TAGS${NL}traefik.tcp.services.${SERVICE_NAME}.loadbalancer.server.port=${SERVICE_PORT}" ;; # B:明文 TCP(仅内网/Tailscale,用 * 兜底) plaintext) TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.rule=HostSNI(\`*\`)" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.entrypoints=${TRAEFIK_TCP_ENTRYPOINT}" TAGS="$TAGS${NL}traefik.tcp.routers.${SERVICE_NAME}.priority=1" TAGS="$TAGS${NL}traefik.tcp.services.${SERVICE_NAME}.loadbalancer.server.port=${SERVICE_PORT}" ;; *) echo "unsupported TLS_MODE=$TLS_MODE" >&2; exit 2;; esac else echo "unsupported SERVICE_PROTOCOL=$SERVICE_PROTOCOL" >&2; exit 2 fi # 转 JSON 数组(按“行”解析) to_json_array() { # 逐行 -> trim -> "..." -> [ ... ] awk 'BEGIN{RS="\n"} NF {gsub(/^[ \t]+|[ \t]+$/,""); printf "\"%s\",\n",$0}' | sed '1s/^/[/' | sed '$s/,\s*$/]/' } TAGS_JSON="$(printf "%s" "$TAGS" | to_json_array)" # 健康检查 JSON if [ "$CHECK_TYPE" = "http" ]; then CHECK_JSON=$(cat < /tmp/svc.json < ${CONSUL}" consul services register -http-addr="$CONSUL" /tmp/svc.json term() { echo "[registrar] deregister ${ID}" consul services deregister -http-addr="$CONSUL" /tmp/svc.json || true exit 0 } trap term TERM INT tail -f /dev/null