From dce7cb6fc9a85c5cbdb8d26bbdb32ad1b6c8c9fb Mon Sep 17 00:00:00 2001 From: hotwa Date: Thu, 2 Oct 2025 21:02:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E7=94=A8=E6=96=B0=E7=89=88=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=E6=B3=A8=E5=86=8C=EF=BC=8C=E4=BA=91=E7=AB=AF=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E9=80=9A=E9=85=8D=E7=AC=A6=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/.env | 25 +++---- docker/docker-compose.yml | 134 ++++++++++++++++---------------------- docker/registrar.sh | 103 ++++++++++++++++------------- 3 files changed, 122 insertions(+), 140 deletions(-) diff --git a/docker/.env b/docker/.env index 21fe3d0..7e728ac 100644 --- a/docker/.env +++ b/docker/.env @@ -1,21 +1,14 @@ +# 本机(边缘节点)的 Tailscale IP +LOCAL_TS_IP=100.64.0.27 -# 这台业务节点在 Tailscale 上的 IP -SERVICE_IP=100.64.0.27 - -# 端口 -PORT_RUSTFS=9000 -PORT_MCP=9009 - -# Consul(主集群)信息 +# 云端 Consul Server 的 Tailscale IP 与 DC CONSUL_SERVER_IP=100.64.0.1 CONSUL_DC=dc1 -# 服务名(建议分开,避免混入) -SVC_RUSTFS=rustfs -SVC_MCP=rustfs-toolkit +# 可选:云端 Traefik entrypoint 名称(默认 websecure/tcp) +TRAEFIK_HTTP_ENTRYPOINT=websecure +TRAEFIK_TCP_ENTRYPOINT=tcp -# 域名(Caddy 用) -DOMAIN_RUSTFS=rfs.jmsu.top -DOMAIN_MCP=mcp.jmsu.top - -NODE_NAME=rustfs-100-64-0-27 +# RustFS 凭据(不要硬编码在 compose) +RUSTFS_ACCESS_KEY=lingyuzeng +RUSTFS_SECRET_KEY=rust@Hotwa2020 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 258cff5..b2d3153 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,109 +1,87 @@ version: "3.9" services: + # -------- RustFS 主服务 -------- rustfs: image: rustfs/rustfs:1.0.0-alpha.60 - container_name: rustfs_container - restart: always - ports: - - "${SERVICE_IP}:${PORT_RUSTFS}:${PORT_RUSTFS}" + container_name: rustfs + restart: unless-stopped volumes: - /vol2/1000/rustfs_vol2:/data - ./data:/app/data:rw environment: RUSTFS_VOLUMES: /data/rustfs0 - RUSTFS_ADDRESS: ":${PORT_RUSTFS}" - RUSTFS_SERVER_DOMAINS: ${DOMAIN_RUSTFS} - RUSTFS_ACCESS_KEY: lingyuzeng - RUSTFS_SECRET_KEY: rust@Hotwa2020 + RUSTFS_ADDRESS: ":9000" + RUSTFS_SERVER_DOMAINS: rfs.jmsu.top + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} RUSTFS_CONSOLE_ENABLE: "true" + # **仅绑定到本机 Tailscale IP**,供云端 Traefik 反代 + ports: + - "${LOCAL_TS_IP}:9000:9000" + # -------- RustFS MCP 接口(假设走 HTTP on :9009)-------- rustfs-s3-toolkit: image: hotwa/rustfs-s3-toolkit:latest - build: - context: .. - dockerfile: docker/Dockerfile - container_name: rustfs-s3-toolkit - restart: always + container_name: rustfs-mcp + restart: unless-stopped + environment: + MCP_PORT: "9009" volumes: - ./data:/app/data:rw ports: - - "${SERVICE_IP}:${PORT_MCP}:${PORT_MCP}" + - "${LOCAL_TS_IP}:9009:9009" - consul-agent: + # -------- Registrar:把 :9000 注册到 rfs.jmsu.top -------- + registrar-rustfs: image: hashicorp/consul:1.21 - stop_signal: SIGTERM - stop_grace_period: 60s - command: - - agent - - -server=false - - -node=${NODE_NAME} - - -client=0.0.0.0 - - -bind=0.0.0.0 - - -advertise=${SERVICE_IP} - - -retry-join=${CONSUL_SERVER_IP} - - -datacenter=${CONSUL_DC} - - -data-dir=/consul/data - environment: - CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}' - ports: - - "${SERVICE_IP}:8500:8500/tcp" - - "${SERVICE_IP}:8600:8600/tcp" - - "${SERVICE_IP}:8600:8600/udp" - - "${SERVICE_IP}:8301:8301/tcp" - - "${SERVICE_IP}:8301:8301/udp" - healthcheck: - test: ["CMD", "consul", "info"] - interval: 5s - timeout: 3s - retries: 30 - start_period: 10s + container_name: registrar-rustfs restart: unless-stopped - - # 注册 rustfs(9000) - registrar_rustfs: - image: hashicorp/consul:1.21 depends_on: - consul-agent: - condition: service_healthy - rustfs: - condition: service_started + - rustfs + volumes: + - ./registrar.sh:/registrar.sh:ro environment: - CONSUL_HTTP_ADDR: "http://consul-agent:8500" - SERVICE_NAME: "${SVC_RUSTFS}" - SERVICE_ADDR: "${SERVICE_IP}" - SERVICE_PORT: "${PORT_RUSTFS}" - SERVICE_TAGS: "console" - CHECK_TYPE: "tcp" + # 指向“云端” Consul Server(通过 Tailscale) + CONSUL_HTTP_ADDR: "http://${CONSUL_SERVER_IP}:8500" + # 下面这 4 个由 registrar.sh 必填 + SERVICE_NAME: "rustfs" + SERVICE_ADDR: "${LOCAL_TS_IP}" + SERVICE_PORT: "9000" + ROUTE_HOST: "rfs.jmsu.top" + # 可选项(HTTP/TCP、健康检查、入口等) + SERVICE_PROTOCOL: "http" + CHECK_TYPE: "http" + CHECK_PATH: "/" CHECK_INTERVAL: "10s" CHECK_TIMEOUT: "2s" DEREG_AFTER: "1m" + TRAEFIK_HTTP_ENTRYPOINT: "websecure" + TRAEFIK_TCP_ENTRYPOINT: "tcp" + # TRAEFIK_CERT_RESOLVER 可在云端用 file/dynamic 统一配置,这里不强制 + command: ["/bin/sh","/registrar.sh"] + + # -------- Registrar:把 :9009 注册到 mcprfs.jmsu.top -------- + registrar-mcprfs: + image: hashicorp/consul:1.21 + container_name: registrar-mcprfs + restart: unless-stopped + depends_on: + - rustfs-s3-toolkit volumes: - ./registrar.sh:/registrar.sh:ro - entrypoint: ["/bin/sh","-lc","/registrar.sh"] - restart: unless-stopped - - # 注册 MCP(9009) - registrar_mcp: - image: hashicorp/consul:1.21 - depends_on: - consul-agent: - condition: service_healthy - rustfs-s3-toolkit: - condition: service_started environment: - CONSUL_HTTP_ADDR: "http://consul-agent:8500" - SERVICE_NAME: "${SVC_MCP}" - SERVICE_ADDR: "${SERVICE_IP}" - SERVICE_PORT: "${PORT_MCP}" - SERVICE_TAGS: "toolkit" - CHECK_TYPE: "tcp" + CONSUL_HTTP_ADDR: "http://${CONSUL_SERVER_IP}:8500" + SERVICE_NAME: "mcprfs" + SERVICE_ADDR: "${LOCAL_TS_IP}" + SERVICE_PORT: "9009" + ROUTE_HOST: "mcprfs.jmsu.top" + SERVICE_PROTOCOL: "http" # 如果 MCP 走 TCP,请改成 "tcp" 并把 CHECK_TYPE 改为 tcp + CHECK_TYPE: "http" + CHECK_PATH: "/" CHECK_INTERVAL: "10s" CHECK_TIMEOUT: "2s" DEREG_AFTER: "1m" - volumes: - - ./registrar.sh:/registrar.sh:ro - entrypoint: ["/bin/sh","-lc","/registrar.sh"] - restart: unless-stopped - -networks: {} + TRAEFIK_HTTP_ENTRYPOINT: "websecure" + TRAEFIK_TCP_ENTRYPOINT: "tcp" + command: ["/bin/sh","/registrar.sh"] diff --git a/docker/registrar.sh b/docker/registrar.sh index 6469820..d43d8c4 100755 --- a/docker/registrar.sh +++ b/docker/registrar.sh @@ -1,71 +1,82 @@ #!/bin/sh set -eu -: "${CONSUL_HTTP_ADDR:?need CONSUL_HTTP_ADDR}" : "${SERVICE_NAME:?need SERVICE_NAME}" : "${SERVICE_ADDR:?need SERVICE_ADDR}" : "${SERVICE_PORT:?need SERVICE_PORT}" +: "${ROUTE_HOST:?need ROUTE_HOST}" -SERVICE_ID="${SERVICE_ID:-${SERVICE_NAME}-${SERVICE_ADDR}-${SERVICE_PORT}}" -SERVICE_TAGS="${SERVICE_TAGS:-}" -CHECK_TYPE="${CHECK_TYPE:-tcp}" # tcp|http -CHECK_PATH="${CHECK_PATH:-/healthz}" # http 模式才用 +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:-cf}" -# 组装 Tags 的 JSON 数组 -if [ -n "$SERVICE_TAGS" ]; then - # 逗号分隔转 JSON 数组 - TAGS_JSON=$(printf '%s' "$SERVICE_TAGS" | awk -F, ' - BEGIN{printf "["} - {for(i=1;i<=NF;i++){gsub(/^ *| *$/, "", $i); printf "%s\"%s\"", (i>1?",":""), $i}} - END{printf "]"} - ') +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(ConsulCatalog) +TAGS="traefik.enable=true" +if [ "$SERVICE_PROTOCOL" = "http" ]; then + TAGS="$TAGS,traefik.http.routers.${SERVICE_NAME}.rule=Host(\`${ROUTE_HOST}\`)" + TAGS="$TAGS,traefik.http.routers.${SERVICE_NAME}.entrypoints=${TRAEFIK_HTTP_ENTRYPOINT}" + TAGS="$TAGS,traefik.http.routers.${SERVICE_NAME}.tls=true" + TAGS="$TAGS,traefik.http.services.${SERVICE_NAME}.loadbalancer.server.scheme=http" + TAGS="$TAGS,traefik.http.services.${SERVICE_NAME}.loadbalancer.server.port=${SERVICE_PORT}" + # 可选:应用云端 dynamic.yml 的中间件 + TAGS="$TAGS,traefik.http.routers.${SERVICE_NAME}.middlewares=gzip-all@file,security-headers@file" +elif [ "$SERVICE_PROTOCOL" = "tcp" ]; then + TAGS="$TAGS,traefik.tcp.routers.${SERVICE_NAME}.rule=HostSNI(\`${ROUTE_HOST}\`)" + TAGS="$TAGS,traefik.tcp.routers.${SERVICE_NAME}.entrypoints=${TRAEFIK_TCP_ENTRYPOINT}" + TAGS="$TAGS,traefik.tcp.services.${SERVICE_NAME}.loadbalancer.server.port=${SERVICE_PORT}" else - TAGS_JSON="[]" + echo "unsupported SERVICE_PROTOCOL=$SERVICE_PROTOCOL" >&2; exit 2 fi -# 组装 Check JSON +# 转 JSON 数组(按逗号拆分) +to_json_array() { echo "$1" | awk -v RS=, 'NF{print "\""$0"\""}' | paste -sd, - | sed 's/^/[/' | sed 's/$/]/'; } +TAGS_JSON="$(to_json_array "$TAGS")" + +# 健康检查 JSON if [ "$CHECK_TYPE" = "http" ]; then CHECK_JSON=$(cat < /tmp/service.json < /tmp/svc.json < ${SERVICE_ADDR}:${SERVICE_PORT} ..." -curl -fsS -X PUT -d @/tmp/service.json "${CONSUL_HTTP_ADDR}/v1/agent/service/register" -echo "[registrar] done." -# 阻塞防退出(可选) -tail -f /dev/null +echo "[registrar] register ${ID} -\u003e ${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 \ No newline at end of file