You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker Swarm中Go API无法从etcd获取gauth.rpc键,但etcdctl可查询到

问题:Docker Swarm部署go-zero应用时API无法连接ETCD中的RPC服务

场景描述

使用go-zero框架部署Docker Swarm应用栈,架构以RESTful API服务作为请求入口,API通过ETCD查询对应RPC服务的端点,再调用RPC服务实现业务逻辑。Swarm集群启动后,所有RPC服务和ETCD均处于健康状态,但API服务持续报错退出。

Docker Compose配置

version: '3.8'

services:
  g_api:
    image: <Host IP>:5000/g-api:1.0.5
    ports:
      - "8888:8888"
    environment:
      - TZ=Asia/Shanghai
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - g_etcd
      - g_authrpc
      - g_commentrpc
      - g_userrpc
      - g_msgrpc
      - g_postrpc
    networks:
      - gnet
  g_authrpc:
    image: <Host IP>:5000/g-authrpc:1.0.5
    ports:
      - "8080:8080"
    environment:
      - TZ=Asia/Shanghai
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - g_etcd
    networks:
      - gnet
  g_userrpc:
    image: <Host IP>:5000/g-userrpc:1.0.5
    ports:
      - "8081:8081"
    environment:
      - TZ=Asia/Shanghai
    command: "./main"
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - g_etcd
    networks:
      - gnet
  g_msgrpc:
    image: <Host IP>:5000/g-msgrpc:1.0.5
    ports:
      - "8086:8086"
    environment:
      - TZ=Asia/Shanghai
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - g_etcd
    networks:
      - gnet
  g_postrpc:
    image: <Host IP>:5000/g-postrpc:1.0.5
    ports:
      - "8082:8082"
    environment:
      - TZ=Asia/Shanghai
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - g_etcd
    networks:
      - gnet
  g_commentrpc:
    image: <Host IP>:5000/g-commentrpc:1.0.5
    ports:
      - "8152:8152"
    environment:
      - TZ=Asia/Shanghai
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - g_etcd
    networks:
      - gnet
  g_etcd:
    image: <Host IP>:5000/etcd:3.5
    ports:
      - "2379:2379"
    environment:
      - TZ=Asia/Shanghai
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_ADVERTISE_CLIENT_URLS=http://g_etcd:2379
    deploy:
      replicas: 1
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    networks:
      - gnet
networks:
  gnet:
    driver: overlay

注:<Host IP>为云服务器IP

API服务错误日志

global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | 2024/07/01 03:05:57 rpc dial: etcd://141.2.18.177:2379/gauth.rpc, error: context deadline exceeded, make sure rpc service "gauth.rpc" is already started
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | 
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | goroutine 1 [running]:
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | runtime/debug.Stack()
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    |   C:/Program Files/Go/src/runtime/debug/stack.go:24 +0x5e
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | github.com/zeromicro/go-zero/core/logx.Must({0x20288e0?, 0xc00038a480?})
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    |   C:/Users/vipda/go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/core/logx/logs.go:225 +0x4b
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | github.com/zeromicro/go-zero/zrpc.MustNewClient({{{0xc000403220, 0x1, 0x1}, {0xc000406810, 0x9}, 0x0, {0x0, 0x0}, {0x0, 0x0}, ...}, ...}, ...)
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    |   C:/Users/vipda/go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/zrpc/client.go:45 +0x65
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | globalfun/app/api/internal/svc.NewServiceContext({{{{0xc000406846, 0x3}, {{...}, {...}, {...}, {...}, {...}, {...}, 0x0, 0x0, ...}, ...}, ...}, ...})
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    |   C:/Users/person/projects/realworld/globalfun-go/globalfun/app/api/internal/svc/servicecontext.go:36 +0xc9
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | main.main()
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    |   C:/Users/person/projects/realworld/globalfun-go/globalfun/app/api/app.go:25 +0x137
global_g_api.1.yv6gvbm93sq7@b3saglvue9qgqseg    | {"@timestamp":"2024-07-01T03:05:57.451Z","content":"rpc dial: etcd://141.2.18.177:2379/gauth.rpc, error: context deadline exceeded, make sure rpc service \"gauth.rpc\" is already started\n\ngoroutine 1 [running]:\nruntime/debug.Stack()\n\tC:/Program Files/Go/src/runtime/debug/stack.go:24 +0x5e\ngithub.com/zeromicro/go-zero/core/logx.Must({0x20288e0?, 0xc00038a480?})\n\tC:/Users/vipda/go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/core/logx/logs.go:225 +0x4b\ngithub.com/zeromicro/go-zero/zrpc.MustNewClient({{{0xc000403220, 0x1, 0x1}, {0xc000406810, 0x9}, 0x0, {0x0, 0x0}, {0x0, 0x0}, ...}, ...}, ...)\n\tC:/Users/vipda/go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/zrpc/client.go:45 +0x65\nglobalfun/app/api/internal/svc.NewServiceContext({{{{0xc000406846, 0x3}, {{...}, {...}, {...}, {...}, {...}, {...}, 0x0, 0x0, ...}, ...}, ...}, ...})\n\tC:/Users/person/projects/realworld/globalfun-go/globalfun/app/api/internal/svc/servicecontext.go:36 +0xc9\nmain.main()\n\tC:/Users/person/projects/realworld/globalfun-go/globalfun/app/api/app.go:25 +0x137\n","level":"fatal"}

ETCD键查询结果

commentrpc.rpc/7587879719612179989
10.0.7.12:8152
commentrpc.rpc/7587879719612180006
10.0.7.13:8152
gauth.rpc/7587879719612179977
10.0.7.20:8080
gauth.rpc/7587879719612179981
10.0.7.21:8080
msg.rpc/7587879719612179993
10.0.7.7:8086
msg.rpc/7587879719612179997
10.0.7.6:8086
posts.rpc/7587879719612179973
10.0.7.9:8082
posts.rpc/7587879719612179985
10.0.7.10:8082
users.rpc/7587879719612180001
10.0.7.4:8081
users.rpc/7587879719612180011
10.0.0.3:8081

排查解决方案

1. 修正ETCD地址配置

API当前配置的ETCD地址是云服务器IP(141.2.18.177:2379),但在Docker Swarm的overlay网络中,容器应通过服务名g_etcd:2379访问ETCD服务。宿主机IP可能受防火墙规则、Swarm网络策略限制,导致容器内部无法连通。修改API的配置文件,将ETCD地址改为http://g_etcd:2379。

2. 验证RPC服务网络连通性

RPC服务注册到ETCD的是Swarm overlay网络的内部IP(如10.0.7.20:8080),进入API容器内部,执行以下命令验证网络连通性:

# ping RPC服务内部IP
ping 10.0.7.20
# telnet RPC端口
telnet 10.0.7.20 8080

如果无法连通,检查Swarm overlay网络gnet的配置,确保所有服务都加入了该网络,且无自定义网络策略限制流量。

3. 优化服务启动顺序

Docker Swarm的depends_on仅保证服务启动顺序,不保证服务完全就绪。API启动时,ETCD中可能还未完成RPC服务的注册,导致启动超时。可以:

  • 在API的启动脚本中添加轮询逻辑,用etcdctl查询gauth.rpc前缀的键,直到检测到存在再启动API程序:
    while ! etcdctl --endpoints=http://g_etcd:2379 get --prefix gauth.rpc; do
      sleep 2
    done
    ./main
    
  • 修改API的zrpc客户端配置,增加启动重试机制,避免因临时未就绪导致启动失败。

4. 延长ETCD客户端超时时间

go-zero的zrpc客户端默认超时时间可能过短,在Swarm网络环境中容易出现超时。在API的zrpc客户端配置中增加timeout参数,延长超时时间(例如设置为10s):

# 示例zrpc客户端配置
GauthRpc:
  Etcd:
    Hosts:
    - g_etcd:2379
    Key: gauth.rpc
  Timeout: 10s

内容的提问来源于stack exchange,提问作者AchillesHeel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:55:54