PAYATHON 2026

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,模型也已下载到正确的持久化目录。

备注:内容仅供参考。