从 python 到 gcs 模拟器的 IPv4 请求
Request with IPv4 from python to gcs emulator
我正在尝试从 python 应用程序向 docker 中的 gcs 模拟器发出请求-在 docker 中为 mac 编写桥接网络。
当我尝试时,我发现 gcs 客户端库以某种方式尝试使用 IPv6 向 gcs 模拟器发出请求但失败了,因为 docker 对于 mac.
不支持 IPv6
我已经实施了以下答案来纠正 IPv4,但它似乎仍在尝试通过 IPv6 发出请求。
如何在 docker-compose 网络中从 python 向 gcs 模拟器发出成功的请求?
我已经确认从本地 Python 脚本到 gcs 模拟器的请求没有 docker-compose 是成功的。
docker-for-mac 问题:https://github.com/docker/for-mac/issues/1432
参考答案:Force requests to use IPv4 / IPv6
gcs 模拟器:https://github.com/fsouza/fake-gcs-server
样本docker-compose.yaml
version: '3'
services:
run:
build: .
container_name: run
ports:
- 9090:8080
env_file:
- ./.env
environment:
- PORT=8080
gcs:
image: fsouza/fake-gcs-server:latest
container_name: fake-gcs-server
ports:
- 4443:4443
env_file:
- ./.env
实施示例:
from google.cloud import storage
from google.api_core.client_options import ClientOptions
from google.auth.credentials import AnonymousCredentials
from unittest.mock import patch
from multijob_sample import variables as vs
import requests
import urllib3
import urllib3.util.connection
import traceback
import socket
orig_getaddrinfo = socket.getaddrinfo
def getaddrinfoIPv4(host, port, family=0, type=0, proto=0, flags=0):
print(f'running patched getaddrinfo')
return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET, type=type, proto=proto, flags=flags)
patcher = patch('socket.getaddrinfo', side_effect=getaddrinfoIPv4)
patcher.start()
# for fake-gcs-emulator
http_ssl_disabled = requests.Session()
http_ssl_disabled.verify = False
urllib3.disable_warnings(
urllib3.exceptions.InsecureRequestWarning
) # disable https warnings for https insecure certs
client = storage.Client(
credentials=AnonymousCredentials(),
project=vs.project_id,
client_options=ClientOptions(api_endpoint='https://gcs:4443'),
_http=http_ssl_disabled,
)
def put_file(bucket_id: str, file, blobname: str):
file.seek(0)
try:
client.get_bucket(bucket_id).blob(blob_name=blobname).upload_from_file(file)
print(f'file {blobname} uploaded')
except Exception as e:
print(f'failed to put file: {blobname}')
print(f'error: {e}')
print(f'trace: {traceback.format_exc()}')
put_file("bucketid", file, "blobname") # do put_file
错误信息:
run | running patched getaddrinfo
run | failed to put file: test.csv
run | error: HTTPSConnectionPool(host='::', port=4443): Max retries exceeded with url: /upload/resumable/efbbcde9c49cda2ff78e8da24371ea03 (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f8fb0765be0>: Failed to establish a new connection: [Errno -9] Address family for hostname not supported'))
run | trace: Traceback (most recent call last):
run | File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 169, in _new_conn
run | conn = connection.create_connection(
run | File "/usr/local/lib/python3.9/site-packages/urllib3/util/connection.py", line 73, in create_connection
run | for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
run | File "/usr/local/lib/python3.9/unittest/mock.py", line 1093, in __call__
run | return self._mock_call(*args, **kwargs)
run | File "/usr/local/lib/python3.9/unittest/mock.py", line 1097, in _mock_call
run | return self._execute_mock_call(*args, **kwargs)
run | File "/usr/local/lib/python3.9/unittest/mock.py", line 1158, in _execute_mock_call
run | result = effect(*args, **kwargs)
run | File "/app/multijob_sample/storage.py", line 26, in getaddrinfoIPv4
run | return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET, type=type, proto=proto, flags=flags)
run | File "/usr/local/lib/python3.9/socket.py", line 954, in getaddrinfo
run | for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
run | socket.gaierror: [Errno -9] Address family for hostname not supported
到目前为止,这是我一段时间以来不得不解决的最烦人的问题之一。解决方案是 运行 带有 -external-url http://<your docker compose service name>:<port>
选项的模拟器。
此问题仅发生在文件上传时,因为它只发生在可恢复的上传中。
对于可恢复上传,GCS 客户端首先“启动”与服务器的可恢复上传,并且在响应中服务器包含一个 URL 以供将来请求转到(不知道为什么,但这似乎是一个复杂的API)。问题是模拟器不知道它自己的url!事实上,如果您查看模拟器的日志,您会看到它打印出类似 server started at http://[::]:4443
的内容。 ::
与您在错误中看到的 ::
相同。
所以模拟器用它的 ::
URL 响应,然后过了一会儿客户端崩溃试图解析 URL.
我仍然不确定为什么 运行 在 docker-compose 之外工作,我想在 ""
、"localhost"
或周围某处有一些特殊的外壳"::"`.
我正在尝试从 python 应用程序向 docker 中的 gcs 模拟器发出请求-在 docker 中为 mac 编写桥接网络。 当我尝试时,我发现 gcs 客户端库以某种方式尝试使用 IPv6 向 gcs 模拟器发出请求但失败了,因为 docker 对于 mac.
不支持 IPv6我已经实施了以下答案来纠正 IPv4,但它似乎仍在尝试通过 IPv6 发出请求。
如何在 docker-compose 网络中从 python 向 gcs 模拟器发出成功的请求?
我已经确认从本地 Python 脚本到 gcs 模拟器的请求没有 docker-compose 是成功的。
docker-for-mac 问题:https://github.com/docker/for-mac/issues/1432
参考答案:Force requests to use IPv4 / IPv6
gcs 模拟器:https://github.com/fsouza/fake-gcs-server
样本docker-compose.yaml
version: '3'
services:
run:
build: .
container_name: run
ports:
- 9090:8080
env_file:
- ./.env
environment:
- PORT=8080
gcs:
image: fsouza/fake-gcs-server:latest
container_name: fake-gcs-server
ports:
- 4443:4443
env_file:
- ./.env
实施示例:
from google.cloud import storage
from google.api_core.client_options import ClientOptions
from google.auth.credentials import AnonymousCredentials
from unittest.mock import patch
from multijob_sample import variables as vs
import requests
import urllib3
import urllib3.util.connection
import traceback
import socket
orig_getaddrinfo = socket.getaddrinfo
def getaddrinfoIPv4(host, port, family=0, type=0, proto=0, flags=0):
print(f'running patched getaddrinfo')
return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET, type=type, proto=proto, flags=flags)
patcher = patch('socket.getaddrinfo', side_effect=getaddrinfoIPv4)
patcher.start()
# for fake-gcs-emulator
http_ssl_disabled = requests.Session()
http_ssl_disabled.verify = False
urllib3.disable_warnings(
urllib3.exceptions.InsecureRequestWarning
) # disable https warnings for https insecure certs
client = storage.Client(
credentials=AnonymousCredentials(),
project=vs.project_id,
client_options=ClientOptions(api_endpoint='https://gcs:4443'),
_http=http_ssl_disabled,
)
def put_file(bucket_id: str, file, blobname: str):
file.seek(0)
try:
client.get_bucket(bucket_id).blob(blob_name=blobname).upload_from_file(file)
print(f'file {blobname} uploaded')
except Exception as e:
print(f'failed to put file: {blobname}')
print(f'error: {e}')
print(f'trace: {traceback.format_exc()}')
put_file("bucketid", file, "blobname") # do put_file
错误信息:
run | running patched getaddrinfo
run | failed to put file: test.csv
run | error: HTTPSConnectionPool(host='::', port=4443): Max retries exceeded with url: /upload/resumable/efbbcde9c49cda2ff78e8da24371ea03 (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f8fb0765be0>: Failed to establish a new connection: [Errno -9] Address family for hostname not supported'))
run | trace: Traceback (most recent call last):
run | File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 169, in _new_conn
run | conn = connection.create_connection(
run | File "/usr/local/lib/python3.9/site-packages/urllib3/util/connection.py", line 73, in create_connection
run | for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
run | File "/usr/local/lib/python3.9/unittest/mock.py", line 1093, in __call__
run | return self._mock_call(*args, **kwargs)
run | File "/usr/local/lib/python3.9/unittest/mock.py", line 1097, in _mock_call
run | return self._execute_mock_call(*args, **kwargs)
run | File "/usr/local/lib/python3.9/unittest/mock.py", line 1158, in _execute_mock_call
run | result = effect(*args, **kwargs)
run | File "/app/multijob_sample/storage.py", line 26, in getaddrinfoIPv4
run | return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET, type=type, proto=proto, flags=flags)
run | File "/usr/local/lib/python3.9/socket.py", line 954, in getaddrinfo
run | for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
run | socket.gaierror: [Errno -9] Address family for hostname not supported
到目前为止,这是我一段时间以来不得不解决的最烦人的问题之一。解决方案是 运行 带有 -external-url http://<your docker compose service name>:<port>
选项的模拟器。
此问题仅发生在文件上传时,因为它只发生在可恢复的上传中。
对于可恢复上传,GCS 客户端首先“启动”与服务器的可恢复上传,并且在响应中服务器包含一个 URL 以供将来请求转到(不知道为什么,但这似乎是一个复杂的API)。问题是模拟器不知道它自己的url!事实上,如果您查看模拟器的日志,您会看到它打印出类似 server started at http://[::]:4443
的内容。 ::
与您在错误中看到的 ::
相同。
所以模拟器用它的 ::
URL 响应,然后过了一会儿客户端崩溃试图解析 URL.
我仍然不确定为什么 运行 在 docker-compose 之外工作,我想在 ""
、"localhost"
或周围某处有一些特殊的外壳"::"`.