Search code examples
dockerdocker-composefastapiopensearch

Connection Refused Error in OpenSearch with FastAPI and Docker Compose Setup


I’m working on a FastAPI project that integrates with OpenSearch using Docker Compose. I keep encountering a ConnectionRefusedError when trying to connect to the OpenSearch service.

Here is my setup:

docker-compose.yml:

services:
  app:
    container_name: app-search
    build: .
    command: "uvicorn main:app --host 0.0.0.0 --port 8000 --reload"
    ports:
      - 8001:8000
    env_file:
      - .env
    networks:
      - appsearchnet
    depends_on:
      - db
      - opensearch
    restart: always
    volumes:
      - .:/app  # Mount the project directory to /app in the container

  opensearch:
    image: opensearchproject/opensearch:latest
    container_name: opensearch
    environment:
      - plugins.security.disabled=true
      - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD}
      - cluster.name=opensearch-cluster
      - node.name=opensearch
      - discovery.seed_hosts=opensearch
      - cluster.initial_cluster_manager_nodes=opensearch
      - bootstrap.memory_lock=true
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - opensearch-data:/usr/share/opensearch/data
    ports:
      - "9200:9200"
    networks:
      - appsearchnet
    healthcheck:
      test: ["CMD-SHELL", "curl -s http://localhost:9200 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
volumes:
  opensearch-data:
networks:
  appsearchnet:
    name: appsearchnet

main.py

from db import opensearch_client

@app.on_event("startup")
def on_startup():
        opensearch_client.create_index()

OpenSearch Client Configuration (db/opensearch_client.py):

from dotenv import load_dotenv
from opensearchpy import OpenSearch

load_dotenv()

opensearch_client = OpenSearch(
    hosts=[{'host': "opensearch", 'port': 9200}],
    http_auth=('admin', 'XXXX'),
    http_compress=True,
    use_ssl=False,
    verify_certs=False,
    ssl_assert_hostname=False,
    ssl_show_warn=False
)

INDEX_NAME = "books"

def create_index():
    if not opensearch_client.indices.exists(INDEX_NAME):
        opensearch_client.indices.create(
            INDEX_NAME,
            body={
                "settings": {"number_of_shards": 1},
                "mappings": {
                    "properties": {
                        "id": {"type": "integer"},
                        "title": {"type": "text"},
                        "language": {"type": "keyword"},
                        "chapters": {
                            "type": "nested",
                            "properties": {
                                "chapter_no": {"type": "integer"},
                                "title": {"type": "text"},
                                "content": {"type": "text"},
                            }
                        }
                    }
                }
            }
        )

When I run the setup, I keep getting the following error in my logs:

2024-10-21 12:51:19,796 - WARNING - HEAD http://opensearch:9200/books [status:N/A request:0.002s]
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/urllib3/connection.py", line 196, in _new_conn
    sock = connection.create_connection(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    raise ConnectionRefusedError: [Errno 111] Connection refused

I have confirmed that the OpenSearch service is running, but my FastAPI application can’t seem to connect. It looks like it is unable to reach http://opensearch:9200.

But I can call http://localhost:9200/ in my browser and print this:

{
  "name" : "opensearch",
  "cluster_name" : "opensearch-cluster",
  "cluster_uuid" : "kGBFpJNlReiU2qdifiZsFQ",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.17.1",
    "build_type" : "tar",
    "build_hash" : "1893d20797e30110e5877170e44d42275ce5951e",
    "build_date" : "2024-09-26T21:59:32.078798875Z",
    "build_snapshot" : false,
    "lucene_version" : "9.11.1",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

What could be causing this error, and how can I fix it?


Solution

  • Adding as an answer for posterity. depends_on alone doesn't guarantee that the service will wait for the database to be healthy. To wait until the healthcheck succeeds, you need a condition in the depends_on block:

    services:
      app:
        container_name: app-search
        build: .
        command: "uvicorn main:app --host 0.0.0.0 --port 8000 --reload"
        ports:
          - 8001:8000
        env_file:
          - .env
        networks:
          - appsearchnet
        depends_on:
          opensearch:
            condition: service_healthy
        restart: always
        volumes:
          - .:/app  # Mount the project directory to /app in the container
    
      opensearch:
        image: opensearchproject/opensearch:latest
        container_name: opensearch
        environment:
          - plugins.security.disabled=true
          - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD}
          - cluster.name=opensearch-cluster
          - node.name=opensearch
          - discovery.seed_hosts=opensearch
          - cluster.initial_cluster_manager_nodes=opensearch
          - bootstrap.memory_lock=true
          - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
          memlock:
            soft: -1
            hard: -1
        volumes:
          - opensearch-data:/usr/share/opensearch/data
        ports:
          - "9200:9200"
        networks:
          - appsearchnet
        healthcheck:
          test: ["CMD-SHELL", "curl -s http://localhost:9200 || exit 1"]
          interval: 30s
          timeout: 10s
          retries: 3
    volumes:
      opensearch-data:
    networks:
      appsearchnet:
        name: appsearchnet
    
    

    See the Docker docs here for more information.