FastAPI调用Unix Socket获取本地Docker容器列表失败求助
错误原因及解决方法
核心错误原因
- httpx不支持直接使用
http+unix://协议:你代码里用的http+unix://%2Fvar%2Frun%2Fdocker.sock格式,httpx默认无法识别,直接触发UnsupportedProtocol错误。 - FastAPI容器未挂载宿主机Docker Socket:即便解决协议问题,容器内部默认没有访问宿主机
/var/run/docker.sock的权限,无法调用Docker API。 - 代码与配置不统一:Dockerfile里定义了
DOCKER_API_URL环境变量,但代码里硬编码了Socket路径,没有利用该变量。
解决方法(推荐方案:Unix Socket挂载,安全可靠)
1. 修改main.py代码,适配httpx访问Unix Socket
httpx需要通过UnixSocketTransport来支持Unix Socket访问,调整代码如下:
from fastapi import FastAPI, HTTPException from httpx import ConnectError, AsyncClient, UnixSocketTransport import logging import os app = FastAPI() # 优先从环境变量获取,默认值为宿主机Socket路径 DOCKER_SOCKET_PATH = os.getenv("DOCKER_SOCKET_PATH", "/var/run/docker.sock") DOCKER_API_VERSION = "v1.41" async def get_containers(): # 指定UnixSocketTransport访问Docker API async with AsyncClient(transport=UnixSocketTransport(DOCKER_SOCKET_PATH)) as client: response = await client.get(f"http://localhost/{DOCKER_API_VERSION}/containers/json") response.raise_for_status() return response.json() @app.get("/containers") async def list_containers(): try: containers = await get_containers() return containers except ConnectError as e: logging.error(f"连接Docker API失败: {str(e)}") raise HTTPException(status_code=500, detail="内部服务器错误") if __name__ == '__main__': import uvicorn uvicorn.run(app, host="127.0.0.1", port=8000, reload=True)
2. 调整Docker运行命令,挂载宿主机Socket
运行FastAPI容器时,需要将宿主机的/var/run/docker.sock挂载到容器内相同路径,同时确保容器有访问权限:
docker run -d -p 8000:8000 -v /var/run/docker.sock:/var/run/docker.sock your-image-name
注意:如果容器内用户非root,可能需要添加
--user $(id -u):$(id -g)或者调整Socket权限,但测试阶段可以直接用root运行。
3. 可选:更新Dockerfile(配置统一优化)
可以在Dockerfile里添加环境变量默认值,保持配置统一:
# 原内容不变,新增环境变量 ENV DOCKER_SOCKET_PATH /var/run/docker.sock
备选方案:TCP连接(仅测试用,不安全)
如果不想挂载Socket,可通过TCP访问宿主机Docker API(需宿主机开启TCP端口,不推荐生产环境):
1. 宿主机开启Docker TCP端口
修改Docker配置文件(如/etc/docker/daemon.json):
{ "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"] }
重启Docker服务:systemctl restart docker
2. 修改main.py使用环境变量
# 替换原DOCKER_API_UNIX_SOCKET定义 DOCKER_API_URL = os.getenv("DOCKER_API_URL", "http://host.docker.internal:2375") async def get_containers(): async with AsyncClient() as client: response = await client.get(f"{DOCKER_API_URL}/v1.41/containers/json") response.raise_for_status() return response.json()
3. 运行容器
直接运行即可,无需挂载Socket:
docker run -d -p 8000:8000 your-image-name
内容的提问来源于stack exchange,提问作者UnboxTiny
相关产品推荐
相关产品推荐

