Docker 容器中的 Ollama 报 Connection Refused
结论
问题很可能出在 ChatOllama 的连接参数上:
server_url=self.ollama_url
在 langchain-ollama 中,ChatOllama 通常通过 base_url 接收 Ollama 服务地址。如果 server_url 没有覆盖默认地址,客户端仍会连接 http://localhost:11434。app 容器里的 localhost 指向该容器自身,其中并没有运行 Ollama,所以会返回 [Errno 111] Connection refused。
当前 Python 代码也没有读取 Compose 注入的 OLLAMA_URL。此外,模型目录被挂载到 /models,而 Ollama 默认使用 /root/.ollama。这些配置都要调整。
修改 ChatOllama 初始化代码
显式读取 Compose 注入的环境变量,并通过 base_url 传入 Ollama 地址:
import os
class ChatbotManager:
def __init__(
self,
model_name: str = "BAAI/bge-small-en",
device: str = "cpu",
encode_kwargs: dict | None = None,
llm_model: str = "llama3.2:3b",
llm_temperature: float = 0.7,
qdrant_url: str = "http://qdrant:6333",
ollama_url: str = "http://ollama:11434",
collection_name: str = "vector_db",
):
self.model_name = model_name
self.device = device
self.encode_kwargs = encode_kwargs or {
"normalize_embeddings": True
}
self.llm_model = os.getenv("OLLAMA_MODEL", llm_model)
self.ollama_url = os.getenv("OLLAMA_URL", ollama_url)
self.qdrant_url = os.getenv("QDRANT_URL", qdrant_url)
self.llm_temperature = llm_temperature
self.collection_name = collection_name
self.embeddings = HuggingFaceBgeEmbeddings(
model_name=self.model_name,
model_kwargs={"device": self.device},
encode_kwargs=self.encode_kwargs,
)
self.llm = ChatOllama(
model=self.llm_model,
temperature=self.llm_temperature,
base_url=self.ollama_url,
)
self.client = QdrantClient(
url=self.qdrant_url,
prefer_grpc=False,
)
需要注意这两处修改:
self.ollama_url = os.getenv("OLLAMA_URL", ollama_url)
base_url=self.ollama_url
如果安装的 langchain-ollama 版本较旧,或者不确定参数名,可以在 app 容器中查看构造函数签名:
docker compose exec app python -c "from langchain_ollama import ChatOllama; import inspect; print(inspect.signature(ChatOllama))"
目前常见的 langchain_ollama.ChatOllama 用法中,这个参数名是 base_url。
建议使用的 Docker Compose 配置
让三个服务由同一个 Compose 项目管理,无需再执行 docker network connect:
services:
qdrant:
image: qdrant/qdrant:v1.12.1
container_name: qdrant
ports:
- "6333:6333"
volumes:
- qdrant_data:/qdrant/storage
networks:
- my_network
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- "11434:11434"
environment:
OLLAMA_HOST: "0.0.0.0:11434"
volumes:
- ollama_data:/root/.ollama
networks:
- my_network
healthcheck:
test: ["CMD", "ollama", "list"]
interval: 10s
timeout: 5s
retries: 12
app:
build: .
container_name: app_new
ports:
- "8501:8501"
environment:
QDRANT_URL: "http://qdrant:6333"
OLLAMA_URL: "http://ollama:11434"
OLLAMA_MODEL: "llama3.2:3b"
depends_on:
qdrant:
condition: service_started
ollama:
condition: service_healthy
volumes:
- ./models:/models
networks:
- my_network
volumes:
qdrant_data:
ollama_data:
networks:
my_network:
driver: bridge
Compose 中的容器通过 service name 通信,因此 app 应访问:
http://ollama:11434
这里使用的是容器端口 11434。ports: "11434:11434" 主要供宿主机访问 Ollama,容器之间通信并不依赖这项端口映射。
OLLAMA_HOST=0.0.0.0:11434 会让 Ollama 监听容器的所有网络接口。如果只监听 127.0.0.1,其他容器将无法连接。
正确持久化并下载模型
Ollama 默认将模型和配置保存在:
/root/.ollama
原配置把宿主机目录挂载到了 /models:
volumes:
- /d/myollamamodels:/models
只有同时设置 OLLAMA_MODELS=/models,Ollama 才会把这个目录作为模型目录。更简单的方式是使用命名卷,并将其挂载到 /root/.ollama:
volumes:
- ollama_data:/root/.ollama
启动 Ollama 后,再下载模型:
docker compose up -d ollama qdrant
docker compose exec ollama ollama pull llama3.2:3b
docker compose up -d --build app
只设置下面的环境变量通常不会触发模型下载:
OLLAMA_MODEL: "llama3.2:3b"
这个变量适合由 app 读取,用于指定请求的模型。模型仍需提前通过 ollama pull 下载,或交给专门的初始化流程处理。
如果必须挂载 Windows 宿主机目录,可以这样写:
volumes:
- "D:/myollamamodels:/root/.ollama"
部署到 Linux 服务器时,需要改成服务器上的实际绝对路径,也可以继续使用 Docker named volume。
从 app 容器内检查连接
宿主机可以访问 localhost:11434,只能证明端口映射有效。还需要确认 app 容器能够访问 ollama:11434。
可以直接调用 Ollama API:
docker compose exec app python -c "import urllib.request; print(urllib.request.urlopen('http://ollama:11434/api/tags').read().decode())"
如果命令返回模型列表,说明 ollama service name 可以正常解析,两个容器位于同一个 Docker network,Ollama 正在监听 11434,而且 app 容器能够访问 Ollama API。
还可以检查模型和服务日志:
docker compose exec ollama ollama list
docker compose logs ollama
docker compose logs app
如果 API 检查仍返回 Connection refused,需要查看 Ollama 是否已经启动完成,并检查日志中是否有端口监听或模型目录权限错误。
不要混用两套容器启动方式
如果已经执行过:
docker run ... --name ollama ollama/ollama
之后又通过 Compose 创建同名的 ollama 容器,可能遇到容器名称冲突、网络不一致,或者实际操作了另一个容器。
建议统一交给 Compose 管理:
docker compose down
docker compose up -d
如果旧容器仍然存在,先确认是否还需要保留。删除前可以检查:
docker ps -a --filter name=ollama
docker inspect ollama
应避免使用 docker run 管理 Ollama,同时又用 Compose 管理 app 和 Qdrant。
depends_on 不能完全代表服务已就绪
普通写法如下:
depends_on:
- ollama
它只保证 Docker 先启动 Ollama 容器,无法保证 Ollama API 已经开始接受请求。模型目录较大或服务器启动较慢时,app 可能提前发出请求,并暂时收到连接错误。
上面的 healthcheck 配合以下配置可以减少这种情况:
depends_on:
ollama:
condition: service_healthy
生产环境中,应用侧仍应为 Ollama 请求设置有限次数的重试,因为服务重启期间也可能暂时无法访问。
更新 LangChain 的调用方式
日志中的警告:
LangChainDeprecationWarning: The method Chain.run was deprecated
它与本次连接被拒绝的问题无关,不过可以将调用方式改为 invoke():
def get_response(self, query: str) -> str:
try:
response = self.qa.invoke({"query": query})
return response["result"]
except Exception as e:
st.error(f"An error occurred while processing your request: {e}")
return "Sorry, I couldn't process your request at the moment."
torch.classes 相关日志通常也不会直接导致这次 Ollama 网络错误。Qdrant 能够正常写入,只能证明 app 与 Qdrant 之间的连接正常,无法说明 app 使用的 Ollama 地址和参数也正确。
最后应逐项确认:app 使用 base_url="http://ollama:11434",该值确实从环境变量读取,Ollama 监听 0.0.0.0:11434,模型也已下载到正确的持久化目录。
备注:内容仅供参考。
