Apollo Gateway在Docker内无法连接GraphQL服务问题排查
我的项目运行两个GraphQL API,通过Apollo Gateway实现聚合。执行Docker Compose后,API服务能正常构建并通过浏览器访问,但Gateway始终无法连接这些子服务。
我知道这类问题通常是因为Gateway指向http://localhost:port/graphql而非http://service:port/graphql,但修改后仍报错:
gateway | Error: Couldn't load service definitions for "images" at http://images:3000: request to http://images:3000/ failed, reason: connect ECONNREFUSED 172.25.0.4:3000
已尝试的操作:
- 在Gateway的app.module.ts中添加
/graphql路径 - 为Docker Compose配置自定义网络
- 进入Gateway容器ping其他服务,能正常解析服务名称
本地启动服务命令:
pnpm start:dev images启动images服务pnpm start:dev products启动products服务pnpm start:dev启动Gateway
各服务Dockerfile
FROM node:18-alpine WORKDIR /usr/src/app RUN npm install -g pnpm COPY package.json pnpm-lock.yaml ./ RUN pnpm install COPY . . EXPOSE 3000 CMD ["pnpm", "start:dev", "images"]
Docker Compose配置文件
version: '3' services: database: container_name: database image: 'postgres:latest' ports: - 5432:5432 restart: always env_file: - .env volumes: - ${PWD}/db-data/:/var/lib/postgresql/data/ - ${PWD}/init.sql:/docker-entrypoint-initdb.d/init.sql images: container_name: images build: context: . dockerfile: ./apps/images/Dockerfile args: - SERVICE_NAME=images ports: - 3000:3000 env_file: - .env depends_on: - database products: container_name: products build: context: . dockerfile: ./apps/products/Dockerfile args: - SERVICE_NAME=products ports: - 3001:3001 env_file: - .env depends_on: - database gateway: container_name: gateway build: context: . dockerfile: ./apps/gateway/Dockerfile ports: - 3002:3002 environment: - PRODUCTS_HOST=products:3001 - IMAGES_HOST=images:3000 depends_on: - database - products - images volumes: postgres_data: driver: local
Gateway的app.module.ts代码
import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo'; import { IntrospectAndCompose } from '@apollo/gateway'; const productsHost = process.env['PRODUCTS_HOST'] || 'localhost:3001'; const imagesHost = process.env['IMAGES_HOST'] || 'localhost:3000'; @Module({ imports: [ GraphQLModule.forRoot<ApolloGatewayDriverConfig>({ driver: ApolloGatewayDriver, server: { csrfPrevention: false, }, gateway: { supergraphSdl: new IntrospectAndCompose({ subgraphs: [ { name: 'images', url: `http://${imagesHost}`, }, { name: 'products', url: `http://${productsHost}`, }, ], }), }, }), ], }) export class AppModule {}
docker ps运行结果
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6f81e45e1d49 gateway-gateway "docker-entrypoint.s…" 38 seconds ago Up 37 seconds 0.0.0.0:3002->3002/tcp, :::3002->3002/tcp gateway cf77b2cd277b gateway-products "docker-entrypoint.s…" 38 seconds ago Up 37 seconds 0.0.0.0:3001->3001/tcp, :::3001->3001/tcp products f02de42daed3 gateway-images "docker-entrypoint.s…" 38 seconds ago Up 37 seconds 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp images 77e194ce3169 postgres:latest "docker-entrypoint.s…" 38 seconds ago Up 37 seconds 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp database
问题分析与解决建议
服务启动顺序问题:
depends_on仅保证容器启动顺序,不确保服务完全就绪。子服务可能还在等待数据库连接或初始化,此时Gateway尝试连接会失败。- 解决:给Gateway添加启动延迟,或在Docker Compose中为子服务配置
healthcheck,让Gateway仅在子服务健康后启动。
- 解决:给Gateway添加启动延迟,或在Docker Compose中为子服务配置
子服务绑定地址问题:本地开发时服务默认绑定
localhost,但Docker容器中需要绑定0.0.0.0才能被其他容器访问。检查子服务的NestJS配置,确保listen方法绑定'0.0.0.0'而非'localhost':await app.listen(3000, '0.0.0.0');子服务GraphQL路径问题:Apollo Gateway默认访问子服务根路径,但NestJS的GraphQL端点默认是
/graphql。需要在Gateway的子服务URL中明确添加该路径:{ name: 'images', url: `http://${imagesHost}/graphql`, }, { name: 'products', url: `http://${productsHost}/graphql`, },网络连通性验证:进入Gateway容器执行
curl http://images:3000/graphql,测试是否能正常访问子服务端点,确认端口是否真的开放。
内容的提问来源于stack exchange,提问作者Ile Ristovski

