无法从另一个docker容器连接到dockerized Express.js应用



我有两个容器应用程序和web服务器。Webserver是普通的nginx:alpine image, App是expressjs App,运行在ubuntu:focal下的3030端口上。我听说为应用程序和服务器使用单独的容器是一种常见的做法。所以我将proxy_pass http://app:3030/;添加到nginx配置中。有些地方出了问题,我深入研究了一下。为了排除不正确的nginx设置,我检查了从web服务器到应用程序容器的原始curl请求,没有运气。这是我的docker-compose:

version: '3.5'
services:
webserver:
image: nginx:alpine
env_file: .env
restart: always
tty: true
ports:
- ${NGINX_HOST_HTTP_PORT}:80
- ${NGINX_HOST_HTTPS_PORT}:443
volumes:
- ${APP_CODE_PATH_HOST}:${APP_DIR}
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/sites/:/etc/nginx/conf.d/
- ./nginx/ssl/:/etc/ssl/
depends_on:
- app
container_name: ${CONTAINER_NAME_PREFIX}-webserver
networks:
- app-network
app:
env_file: .env
restart: on-failure
tty: true
build:
dockerfile: ./docker/app/Dockerfile
args:
APP_ENV: ${APP_ENV}
APP_DIR: ${APP_DIR}
PRODUCT_ID: ${PRODUCT_ID}
context: ../
environment:
- DEBIAN_FRONTEND=noninteractive
container_name: ${CONTAINER_NAME_PREFIX}-app
networks:
- app-network
networks:
app-network:
driver: bridge

我可以从App容器命令行请求express:

/ # curl -X GET http://127.0.0.1:3030/multichannel/4034?uid=a6O5iTje8sR2PESbWpAM
{"status": "OK"}

现在从Webserver:

/ # ping app
PING app (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.090 ms
/ # curl -X GET http://172.19.0.2:3030/multichannel/4034?uid=a6O5iTje8sR2PESbWpAM
curl: (7) Failed to connect to 172.19.0.2 port 3030 after 0 ms: Connection refused
/ # curl -X GET http://app:3030/multichannel/4034?uid=a6O5iTje8sR2PESbWpAM
curl: (7) Failed to connect to app port 3030 after 0 ms: Connection refused

Nginx日志(不要关注IP,它是正确的,在容器重建后它改变了):

2021/10/29 15:40:46 [error] 24#24: *12 connect() failed (111: Connection refused) while connecting to upstream, client: 172.20.0.1, server: api.test, request: "GET /multichannel/4034?uid=a6O5iTje8sR2PESbWpAM HTTP/1.1", upstream: "http://172.20.0.3:3030/multichannel/4034?uid=a6O5iTje8sR2PESbWpAM", host: "api.test"

当然Nginx conf有:

location / {
proxy_pass http://app:3030;
}

为什么连接被拒绝?两个容器都在同一个网络

你至少需要公开应用容器上的3030端口,以使其可用于其他容器:

app:
expose:
- "3030"

参见expose文档。

或者,您可以发布端口:要么指定两个端口(HOST:CONTAINER),要么只指定容器端口(将选择一个随机的主机端口):

app:
ports:
- "3030"

我终于找到了连接不上的原因。当@devatherock提到ARTIFACTORY_URL=http://localhost:8081时,我检查了使用的主机上的应用程序代码,发现了这个:

server.listen(3030, "127.0.0.1", () => {
logger.debug(`Server is running on ${serverConfig.port} port`);
});

然后我尝试了"localhost"作为东道主,还是得到了502。所以是时候读一下文档了。Expressjsserver.listen像nodejsserver.listen一样工作https://nodejs.org/api/net.html#serverlistenport-host-backlog-callback

如果省略host,服务器将接受未指定的IPv6地址(::)的连接,否则接受未指定的IPv4地址(0.0.0.0)的连接。在大多数操作系统中,监听未指定的IPv6地址(::)可能会导致网络阻塞。服务器也监听未指定的IPv4地址(0.0.0.0)。

答案是使用'0.0.0.0'或省略主机参数。127.0.0.1或localhost只能在本地机器上访问,因为它被绑定到loopback接口。

由于我没有express应用程序的代码,我试图用简化版本的docker-compose文件和nginx配置来模拟另一个应用程序的docker映像问题。我也在用一台ubuntu机器。但在我的情况下,从nginx到应用程序的调用可以正常进行。

docker-compose.yml:

version: '3.5'
services:
webserver:
image: nginx:alpine
restart: always
tty: true
ports:
- 8081:80
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
container_name: test-webserver
networks:
- app-network
app:
image: devatherock/artifactory-badge:latest
environment:
- ARTIFACTORY_URL=http://localhost:8081
- ARTIFACTORY_API_KEY=dummy
restart: on-failure
tty: true
container_name: test-app
networks:
- app-network

networks:
app-network:
driver: bridge

nginx.conf:

events {
}
http {
server {
listen 80;
location / {
proxy_pass http://app:8080;
}
}
}

甚至我也和Robert有同样的假设,我们需要为app服务指定ports部分,但是从nginx到app的调用即使没有它也能工作。当我在nginx上到达端点http://localhost:8081/health时,它将请求转发到http://app:8080/health的应用程序,并返回应用程序的健康检查返回的响应{"status":"UP"}

最新更新