为什么我的自定义 Dockerfile 无法通过 docker-compose 网络连接,而其他服务会连接?



问题

我正在尝试创建一个docker-compose文件,它将承载三个服务。InfluxDB, Grafana,以及客户Dockerfile中填充数据库的自定义脚本。我有网络问题,自定义脚本无法连接到InfluxDB由于连接拒绝错误(如下所示)。

目前为止是什么工作

有趣的是,当我从我的docker-compose文件中删除自定义脚本服务(称为ads_agent),要么从本地主机运行该脚本,甚至构建和在自己的容器中运行Dockerfile,它连接得很好.

两者的区别是什么

我的脚本读取一个名为KTS_TELEMETRY_INFLUXDB_URL的环境变量,用于连接InfluxDB客户端的URL。我可以使用"http://localhost:8086"对于URL,当我从命令行运行时,这是有效的。当我将脚本包装在Docker容器中时,我使用本地机器的LAN IP地址,因为对它来说,localhost只是容器。但是,不管怎样,这工作得很好。

在我的docker-compose中,因为所有三个服务都在同一个网络上,所以我使用"http://influxdb:8086"因为该主机名应该绑定到该服务的网络接口。确实如此,因为Grafana使用URL连接得很好。遗憾的是,当我尝试使用脚本时,我得到连接拒绝。

的错误
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7f18c1fec970>: Failed to establish a new connection: [Errno 111] Connection refused

我的代码这是我的docker-compose.yaml

version: "3"
services:
influxdb:
container_name: influxdb
image: influxdb:2.0.9-alpine # influxdb:latest
networks:
- telemetry_network
ports:
- 8086:8086
volumes:
- influxdb-storage:/var/lib/influxdb2
restart: always
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=$KTS_TELEMETRY_INFLUXDB_USERNAME
- DOCKER_INFLUXDB_INIT_PASSWORD=$KTS_TELEMETRY_INFLUXDB_PASSWORD
- DOCKER_INFLUXDB_INIT_ORG=$KTS_TELEMETRY_INFLUXDB_ORG
- DOCKER_INFLUXDB_INIT_BUCKET=$KTS_TELEMETRY_INFLUXDB_BUCKET
- DOCKER_INFLUXDB_INIT_RETENTION=$KTS_TELEMETRY_INFLUXDB_RETENTION
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=$KTS_TELEMETRY_INFLUXDB_TOKEN
grafana:
container_name: grafana
image: grafana/grafana:8.1.7 # grafana/grafana:latest
networks:
- telemetry_network
ports:
- 3000:3000
volumes:
- grafana-storage:/var/lib/grafana
restart: always
depends_on:
- influxdb
ads_agent:
container_name: ads_agent
build: ./ads_agent
networks:
- telemetry_network
restart: always
depends_on:
- influxdb
environment:
- KTS_TELEMETRY_INFLUXDB_URL=http://influxdb:8086
- KTS_TELEMETRY_INFLUXDB_TOKEN=$KTS_TELEMETRY_INFLUXDB_TOKEN
- KTS_TELEMETRY_INFLUXDB_ORG=$KTS_TELEMETRY_INFLUXDB_ORG
- KTS_TELEMETRY_INFLUXDB_BUCKET=$KTS_TELEMETRY_INFLUXDB_BUCKET
networks:
telemetry_network:
volumes:
influxdb-storage:
grafana-storage:

我的ads_agent/Dockerfile

FROM python:3.9
COPY requirements.txt .
RUN pip install --upgrade pip
RUN pip install -r /requirements.txt
COPY main.py .
ENTRYPOINT /usr/local/bin/python3 /main.py

ads_agent/requirements.txt只是有influxdb-client,这是我的ads/main.py

import os
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
from datetime import datetime
import random
import time
token = os.environ["KTS_TELEMETRY_INFLUXDB_TOKEN"]
org = os.environ["KTS_TELEMETRY_INFLUXDB_ORG"]
bucket = os.environ["KTS_TELEMETRY_INFLUXDB_BUCKET"]
url = os.environ["KTS_TELEMETRY_INFLUXDB_URL"]
client = InfluxDBClient(url=url, token=token)
dbh = client.write_api(write_options=SYNCHRONOUS)
while True:
symbol_name = 'rand_num'
value = random.random()
timestamp = datetime.utcnow()
print(timestamp, symbol_name, value)
point = Point("mem") 
.field(symbol_name, value) 
.time(timestamp, WritePrecision.NS)
dbh.write(bucket, org, point)
time.sleep(1)

你的问题与network connectivity无关,只与startup order有关。虽然您将depends_on - influxdb定义为ads_agent,但仍有可能当你的脚本尝试连接influxdb,涌入db仍然没有结束。

这就是为什么手动操作可以成功的原因,因为手动操作有时间延迟,那时数据库已经准备好了。

原因如下:

depends_on在启动web之前不等待db和redis"准备好"-只在它们启动之前。如果您需要等待服务准备好。)

为了确保你的db在你的脚本开始之前真的启动,你需要参考Compose:

中的Control startup and shutdown order。

要处理此问题,请将应用程序设计为在失败后尝试重新建立与数据库的连接。如果应用程序重试连接,它最终可以连接到数据库。

最好的解决方案是在应用程序代码中执行此检查,无论是在启动时还是在任何原因导致连接丢失时。然而,如果您不需要这种级别的弹性,您可以使用包装器脚本来解决这个问题:

  • 使用wait-for-it、dockerize、sh-compatible wait-for或RelayAndContainers模板等工具。这些是小的包装器脚本,您可以将其包含在应用程序的映像中,以轮询给定的主机和端口,直到它接受TCP连接。例如,使用wait-for-it.sh或wait-for包装服务的命令:

    version: "2"
    services:
    web:
    build: .
    ports:
    - "80:8000"
    depends_on:
    - "db"
    command: ["./wait-for-it.sh", "db:5432", "--", "python", "app.py"]
    db:
    image: postgres
    
  • 或者,编写您自己的包装器脚本来执行更特定于应用程序的健康检查。