Docker化NestJS调用Strapi出现ECONNREFUSED错误的解决求助
问题描述
我正在Docker化基于Nest框架的NodeJS(16.20.0版本)服务,环境包含运行在http://localhost:1337/的Strapi容器和http://localhost:4000/的nodejs_api容器。当Docker化的NodeJS服务向Strapi发送GET请求时,出现以下错误:
[2] FetchError: request to http://127.0.0.1:1337/api/login-page failed, reason: connect ECONNREFUSED 127.0.0.1:1337
非Docker化的相同服务可以正常请求,但Docker化服务始终报错。已尝试使用http://localhost:3000/、http://127.0.0.1:1337/发起请求,也重建过Docker镜像,问题仍未解决。
相关代码与配置
panel-content.controller.ts
import { Controller, Get } from '@nestjs/common'; import { GetLabelsService } from '../get-labels/get-labels.service'; import fetch from 'node-fetch'; //Get all the labels related to the admin-panel @Controller('panel-content') export class PanelContentController { constructor(private readonly getLabels: GetLabelsService){} @Get('ad-board-page') async getAdBoardLabels(){ const labels = this.getLabels.getRouteLabels('/api/ad-board-page'); return labels } @Get('chats-page') async getChatsLabels(){ const labels = this.getLabels.getRouteLabels('/api/chats-page'); return labels } @Get('collections-page') async getCollectionsLabels(){ const labels = await this.getLabels.getRouteLabels('/api/collections-page'); return labels } @Get('events-page') async getEventsLabels(){ const labels = await this.getLabels.getRouteLabels('/api/events-page'); return labels } @Get('home-page') async getHomepageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/home-page'); return labels } @Get('login-page') async getLoginPageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/login-page'); return labels } @Get('news-page') async getNewsPageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/news-page'); return labels } @Get('notifications-page') async getNotificationsPageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/notifications-page'); return labels } @Get('orders-page') async getOrdersPageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/orders-page'); return labels } @Get('shop-page') async getShopPageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/shop-page'); return labels } @Get('users-page') async getUsersPageLabels(){ const labels = await this.getLabels.getRouteLabels('/api/users-page'); return labels } }
getLabels.service.ts
import { Injectable } from "@nestjs/common"; import fetch from 'node-fetch'; @Injectable() export class GetLabelsService { async getRouteLabels(labelPath: string) { const request = `http://127.0.0.1:1337${labelPath}`; console.log(request) const res = await fetch(request); const labels_data = await res.json(); delete labels_data.data.attributes.createdAt; delete labels_data.data.attributes.publishedAt; delete labels_data.data.attributes.updatedAt; delete labels_data.meta; return labels_data } }
docker-compose.yml
version: "3.8" services: api: build: context: . dockerfile: Dockerfile-api environment: DB_TYPE: ${DB_TYPE} DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_USERNAME: ${DB_USERNAME} DB_PASSWORD: ${DB_PASSWORD} DB_DATABASE: ${DB_DATABASE} APP_HOST: ${APP_HOST} JWT_SECRET: ${JWT_SECRET} JWT_EXIPRY_TIME: ${JWT_EXIPRY_TIME} STRIPE_PUBLISH_KEY: ${STRIPE_PUBLISH_KEY} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY} currency: ${currency} FCM_DATABASE_URL: ${FCM_DATABASE_URL} SMTP_HOST: ${SMTP_HOST} SMTP_USERNAME: ${SMTP_USERNAME} SMTP_PASSWORD: ${SMTP_PASSWORD} ports: - 4000:4000 networks: - app-network phpmyadmin: image: phpmyadmin/phpmyadmin container_name: phpmyadmin_container restart: always environment: PMA_HOST: ${DB_HOST} PMA_USER: ${DB_USERNAME} PMA_PASSWORD: ${DB_PASSWORD} UPLOAD_LIMIT: 64M ports: - "8080:80" networks: - app-network strapi: build: context: ./cms dockerfile: Dockerfile-cms environment: DATABASE_CLIENT: ${DATABASE_CLIENT} DATABASE_HOST: ${DATABASE_HOST} DATABASE_PORT: ${DATABASE_PORT} DATABASE_NAME: ${DATABASE_NAME} DATABASE_USERNAME: ${DATABASE_USERNAME} DATABASE_PASSWORD: ${DATABASE_PASSWORD} APP_KEYS: ${APP_KEYS} API_TOKEN_SALT: ${API_TOKEN_SALT} ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET} JWT_SECRET: ${JWT_SECRET} NODE_ENV: ${NODE_ENV} MASTER_KEY: ${MASTER_KEY} MEILI_HOST: http://meilisearch:7700 CLOUDINARY_KEY: ${CLOUDINARY_KEY} CLOUDINARY_NAME: ${CLOUDINARY_NAME} CLOUDINARY_SECRET: ${CLOUDINARY_SECRET} volumes: - ./cms/config:/srv/app/config - ./cms/src:/srv/app/src - ./cms/package.json:/srv/package.json - ./cms/yarn.lock:/srv/yarn.lock - ./cms/datadump:/srv/app/datadump - ./cms/public:/srv/app/public ports: - 1337:1337 depends_on: - postgres networks: - app-network command: yarn develop meilisearch: image: getmeili/meilisearch:latest environment: MEILI_MASTER_KEY: ${MASTER_KEY} ports: - 7700:7700 networks: - app-network volumes: - ./data:/srv/app/data pgadmin: image: dpage/pgadmin4 environment: PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL} PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD} ports: - 8088:80 depends_on: - postgres networks: - app-network # Back-end services postgres: image: postgres:12 environment: POSTGRES_USER: ${DATABASE_USERNAME} POSTGRES_PASSWORD: ${DATABASE_PASSWORD} POSTGRES_DB: ${DATABASE_NAME} volumes: - pgdata:/var/lib/postgresql/data ports: - 5432:5432 networks: - app-network volumes: db_data: pgdata: networks: app-network: driver: bridge
问题原因与解决方法
核心原因
Docker容器内的127.0.0.1或localhost指向容器自身,而非宿主机。你的NodeJS容器和Strapi容器虽在宿主机上有端口映射,但容器间无法通过宿主机的localhost地址互相访问。好在两个容器已加入同一Docker网络app-network,可直接通过容器服务名称通信。
具体修复步骤
修改请求地址
在getLabels.service.ts中,将硬编码的http://127.0.0.1:1337替换为http://strapi:1337(strapi是docker-compose中Strapi服务的名称):@Injectable() export class GetLabelsService { async getRouteLabels(labelPath: string) { // 替换为容器服务名称作为主机 const request = `http://strapi:1337${labelPath}`; console.log(request) const res = await fetch(request); const labels_data = await res.json(); delete labels_data.data.attributes.createdAt; delete labels_data.data.attributes.publishedAt; delete labels_data.data.attributes.updatedAt; delete labels_data.meta; return labels_data } }推荐:用环境变量管理地址(可选)
为避免硬编码,建议通过环境变量配置Strapi地址:- 在docker-compose的
api服务中添加环境变量:api: build: context: . dockerfile: Dockerfile-api environment: # ... 其他环境变量 STRAPI_URL: http://strapi:1337 # ... 其他配置 - 在
getLabels.service.ts中读取环境变量:@Injectable() export class GetLabelsService { async getRouteLabels(labelPath: string) { const strapiUrl = process.env.STRAPI_URL || 'http://127.0.0.1:1337'; const request = `${strapiUrl}${labelPath}`; console.log(request) const res = await fetch(request); const labels_data = await res.json(); delete labels_data.data.attributes.createdAt; delete labels_data.data.attributes.publishedAt; delete labels_data.data.attributes.updatedAt; delete labels_data.meta; return labels_data } }
- 在docker-compose的
验证网络连通性(可选排查)
进入NodeJS容器内部,测试是否能ping通Strapi容器:# 进入api容器 docker exec -it <api_container_name> sh # ping strapi服务 ping strapi若能ping通,说明网络正常,修改请求地址后即可通信。
额外注意事项
depends_on仅保证容器启动顺序,不保证Strapi服务完全就绪。若Strapi启动较慢,需添加健康检查或等待逻辑,避免NodeJS服务在Strapi未就绪时发起请求。- 确保两个容器均加入同一Docker网络(你的配置已满足,无需额外修改)。
内容的提问来源于stack exchange,提问作者George Marwanqana
相关产品推荐
相关产品推荐

