Docker Compose 容器中的 Python 请求

Mik*_*son 5 python python-requests docker docker-compose

问题

  • 我有一个 2 容器docker-compose.yml文件。
  • 其中一个容器是一个小型FastAPI应用程序。
  • 另一个只是尝试使用 Python 的requests包来访问 API 。

我可以使用与尝试命中它的 Python 包中的代码完全相同的代码从外部访问应用程序容器,并且它可以工作,但它在包中不起作用。

docker-compose.yml

version: "3.8"
services:
  read-api:
    build:
      context: ./read-api
    depends_on:
      - "toy-api"
    networks:
      - ds-net
  toy-api:
    build:
      context: ./api
    networks:
      - ds-net
    ports:
      - "80:80"
networks:
  ds-net:
Run Code Online (Sandbox Code Playgroud)

相关requests代码

from requests import Session

def post_to_api(session, raw_input, path):
    print(f"The script is sending: {raw_input}")
    print(f"The script is sending it to: {path}")
    response = session.post(path, json={"payload": raw_input})
    print(f"The script received: {response.text}")

def get_from_api(session, path):
    print(f"The datalake script is trying to GET from: {path}")
    response = session.get(path)
    print(f"The datalake script received: {response.text}")

session = Session()
session.trust_env = False ### I got that from here: https://stackoverflow.com/a/50326101/534238
get_from_api(session, path="http://localhost/test")
post_to_api(session, "this is a test", path="http://localhost/raw")
Run Code Online (Sandbox Code Playgroud)

运行它 REPL 风格

如果我创建一个交互式会话并在requests代码部分运行上面那些确切的命令,它会起作用:

>>> get_from_api(session, path="http://localhost/test")
The script is trying to GET from: http://localhost/test
The script received: {"payload":"Yes, you reached here..."}
>>> post_to_api(session, "this is a test", path="http://localhost/raw")
The script is sending: this is a test
The script is sending it to: http://localhost/raw
The script received: {"payload":"received `raw_input`: this is a test"}
Run Code Online (Sandbox Code Playgroud)

需要明确的是:API 代码仍在作为容器运行,并且该容器仍然是使用该docker-compose.yml文件创建的。(换句话说,当从主机访问时,API 容器工作正常。)

在容器内运行

在容器内做同样的事情,我得到以下(相当长的)错误:

read-api_1    | The script is trying to GET from: http://localhost/test
read-api_1    | Traceback (most recent call last):
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 159, in _new_conn
read-api_1    |     conn = connection.create_connection(
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py", line 84, in create_connection
read-api_1    |     raise err
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py", line 74, in create_connection
read-api_1    |     sock.connect(sa)
read-api_1    | ConnectionRefusedError: [Errno 111] Connection refused
read-api_1    | 
read-api_1    | During handling of the above exception, another exception occurred:
.
.
.
read-api_1    | Traceback (most recent call last):
read-api_1    |   File "access_api.py", line 99, in <module>
read-api_1    |     get_from_api(session, path="http://localhost/test")
read-api_1    |   File "access_datalake.py", line 86, in get_from_api
read-api_1    |     response = session.get(path)
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 543, in get
read-api_1    |     return self.request('GET', url, **kwargs)
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 530, in request
read-api_1    |     resp = self.send(prep, **send_kwargs)
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 643, in send
read-api_1    |     r = adapter.send(request, **kwargs)
read-api_1    |   File "/usr/local/lib/python3.8/site-packages/requests/adapters.py", line 516, in send
read-api_1    |     raise ConnectionError(e, request=request)
read-api_1    | requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=80): Max retries exceeded with url: /test (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7ffa9c69b3a0>: Failed to establish a new connection: [Errno 111] Connection refused'))
ai_poc_work_read-api_1 exited with code 1
Run Code Online (Sandbox Code Playgroud)

尝试解决

我认为这是主机如何在容器组中标识自己,或者是否可以访问该源,所以我已经尝试更改以下内容,但没有成功:

  • 而不是使用localhost作为东道主,我用read-api
    • 实际上,我从 开始read-api,并没有运气,但是一旦使用localhost,我至少可以在主机上使用 REPL,如上所示。
    • 我也试过0.0.0.0,没有运气。(我没想到会解决它。)
  • 我已经更改ORIGINS了 API 中允许使用的CORS ,包括尝试读取的容器的所有可能路径,并仅"*"用于标记所有 CORS 来源。没运气。

我究竟做错了什么?似乎问题必须出在容器上,或者可能requests与容器的交互方式有关,但我无法弄清楚是什么。

以下是我发现的一些相关 GitHub 问题或 SO 答案,但都没有解决:

小智 2

在 Docker 网络中,必须使用docker-compose.yml.

如果您尝试访问该toy-api服务,请使用

get_from_api(session, path="http://toy-api/test")
Run Code Online (Sandbox Code Playgroud)

http://localhost/test您可以通过主机访问应用程序,因为 Docker 将应用程序公开给主机。然而,宽松地说,在 Docker 网络中,localhost不指主机localhostlocalhost. 对于服务来说read-api,没有应用程序在监听http://localhost/test