Docker Compose网络中Go容器无法连接MongoDB问题排查
问题根因
连接失败是两个核心问题叠加导致的,depends_on 没有起到你预期的等待作用,同时配置里还存在容易忽略的挂载隐患。
depends_on只会保证依赖容器的启动顺序,不会等待容器内的服务真正完成初始化、开始监听端口。MongoDB容器刚启动时,还在做内存映射、加载存储引擎,27017端口还没对外提供服务,这时候你的Go服务就已经开始发起连接,自然会报no reachable servers错误。- Dockerfile中设置的工作目录是
/usr/src/app,编译好的二进制也存放在这个路径下,但docker-compose里给api服务配置的卷挂载是./:/go/src/app,路径完全不匹配,虽然不会直接触发网络错误,但后续本地改代码后容器内不会同步更新,调试时很容易踩坑。 - 容器内的
localhost指向容器自身,不是宿主机也不是其他同网络容器,你代码里用mongodb:27017、redis:6379作为连接地址的写法是对的,不用改,本地跑go run能用localhost是因为本地服务直接跑在宿主机环回网卡上,和Docker内部网络是两套完全隔离的环境。
修复方案
第一步:给依赖服务加健康检查,让API等服务真正就绪再启动
修改docker-compose.yml,给MongoDB、Redis加健康检查规则,同时调整api服务的依赖条件,不要只等容器启动,要等服务健康检查通过再启动:
version: "3.7" services: api: container_name: 'api' build: './' ports: - '1323:1323' # 和Go服务实际监听的端口对齐 volumes: - './:/usr/src/app' # 修正挂载路径,和Dockerfile的WORKDIR保持一致 depends_on: mongodb: condition: service_healthy redis: condition: service_healthy networks: - api-network mongodb: image: mongo:latest ports: - "27017:27017" volumes: - ./data/mongo:/data/db networks: - api-network # MongoDB健康检查配置 healthcheck: test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet interval: 5s timeout: 5s retries: 5 redis: image: redis:alpine ports: - "6379:6379" volumes: - ./data/redis:/data networks: - api-network # Redis健康检查配置 healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 5 networks: api-network:
第二步:给连接逻辑加重试(可选但强烈建议)
健康检查只能保证启动阶段的可用性,服务运行中如果出现MongoDB/Redis重启,重连逻辑能避免API直接崩溃。修改Go代码里的连接部分,加简单的循环重试即可,不需要引入额外依赖:
// MongoDB连接重试 var db *mgo.Session var err error maxRetries := 10 for i := 0; i < maxRetries; i++ { db, err = mgo.Dial("mongodb://mongodb:27017") if err == nil { break } log.Printf("连接MongoDB失败,2秒后重试,错误信息: %v", err) time.Sleep(2 * time.Second) } if err != nil { e.Logger.Fatal("多次重试后仍无法连接MongoDB: ", err) } // Redis连接重试 var rdb *redis.Client for i := 0; i < maxRetries; i++ { rdb = redis.NewClient(&redis.Options{ Addr: "redis:6379", }) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) err = rdb.Ping(ctx).Err() cancel() if err == nil { break } log.Printf("连接Redis失败,2秒后重试,错误信息: %v", err) time.Sleep(2 * time.Second) } if err != nil { e.Logger.Fatal("多次重试后仍无法连接Redis: ", err) } limiter = redis_rate.NewLimiter(rdb)
第三步:清理旧容器重新启动
执行以下命令清除旧的容器和无效缓存,重新构建启动:
docker compose down -v docker compose up --build -d
验证
启动后执行命令查看服务日志:
docker compose logs -f
可以看到MongoDB、Redis先启动,等健康检查通过后API服务才会启动,不会再出现no reachable servers的报错。
内容的提问来源于stack exchange,提问作者Halil Yıldırım
相关产品推荐
相关产品推荐

