I am trying to make a multi-container docker app using docker-compose
.
Here’s what I am trying to accomplish: I have a python3 app, that takes a list of list of numbers as input from API call(fastAPI
with gunicorn server) and pass the numbers to a function(an ML model actually) that returns a number, which will then be sent back(in json of course) as result to that API call. That part is working absolutely fine. Problem started when I introduced a postgres container to store the inputs I receive into a postgres table and I am yet to add the part where I should also be access data of this postgres database from my local pgadmin4 app.
Here’s what I have done till now: I am using "docker-compose.yml" file to set up both of these containers and here it is:
version: '3.8'
services:
postgres:
image: postgres:12.4
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres_password
- POSTGRES_DATABASE=postgres
docker_fastapi:
# use the Dockerfile in the current directory.
build: .
ports:
# 3000 is what I send API calls to
- "3000:3000"
# this is postgres's port
- "5432:5432"
environment:
# these are the environment variables that I am using inside psycop2 to make connection.
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres_password
- POSTGRES_DATABASE=postgres
he
Here’s how I am using those environment variables in psycopg2
:
import os
from psycopg2 import connect
# making database connection using environement variables.
connection = connect(host=os.environ['POSTGRES_HOST'], port=os.environ['POSTGRES_PORT'],
user=os.environ['POSTGRES_USER'], password=os.environ['POSTGRES_PASSWORD'],
database=os.environ['POSTGRES_DATABASE']
)
here’s the Dockerfile:
FROM tiangolo/uvicorn-gunicorn:python3.8-slim
# slim = debian-based. Not using alpine because it has poor python3 support.
LABEL maintainer="Sebastian Ramirez <[email protected]>"
RUN apt-get update
RUN apt-get install -y libpq-dev gcc
# copy and install from requirements.txt file
COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt
# remove all the dependency files to reduce the final image size
RUN apt-get autoremove -y gcc
# copying all the code files to the container's file system
COPY ./api /app/api
WORKDIR /app/api
EXPOSE 3000
ENTRYPOINT ["uvicorn"]
CMD ["api.main:app", "--host", "0.0.0.0", "--port", "3000"]
And here’s the error it generates for an API call I send:
root@naveen-hp:/home/naveen/Videos/ML-Model-serving-with-fastapi-and-Docker# # docker-compose up
Starting ml-model-serving-with-fastapi-and-docker_docker_fastapi_1 ... done
Starting ml-model-serving-with-fastapi-and-docker_postgres_1 ... done
Attaching to ml-model-serving-with-fastapi-and-docker_postgres_1, ml-model-serving-with-fastapi-and-docker_docker_fastapi_1
postgres_1 |
postgres_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres_1 |
postgres_1 | 2020-10-22 13:17:14.080 UTC [1] LOG: starting PostgreSQL 12.4 (Debian 12.4-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
postgres_1 | 2020-10-22 13:17:14.080 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2020-10-22 13:17:14.080 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2020-10-22 13:17:14.092 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2020-10-22 13:17:14.120 UTC [24] LOG: database system was shut down at 2020-10-22 12:48:50 UTC
postgres_1 | 2020-10-22 13:17:14.130 UTC [1] LOG: database system is ready to accept connections
docker_fastapi_1 | INFO: Started server process [1]
docker_fastapi_1 | INFO: Waiting for application startup.
docker_fastapi_1 | INFO: Application startup complete.
docker_fastapi_1 | INFO: Uvicorn running on http://0.0.0.0:3000 (Press CTRL+C to quit)
docker_fastapi_1 | INFO: 172.18.0.1:56094 - "POST /predict HTTP/1.1" 500 Internal Server Error
docker_fastapi_1 | ERROR: Exception in ASGI application
docker_fastapi_1 | Traceback (most recent call last):
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 391, in run_asgi
docker_fastapi_1 | result = await app(self.scope, self.receive, self.send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
docker_fastapi_1 | return await self.app(scope, receive, send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/fastapi/applications.py", line 179, in __call__
docker_fastapi_1 | await super().__call__(scope, receive, send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
docker_fastapi_1 | await self.middleware_stack(scope, receive, send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
docker_fastapi_1 | raise exc from None
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
docker_fastapi_1 | await self.app(scope, receive, _send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
docker_fastapi_1 | raise exc from None
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
docker_fastapi_1 | await self.app(scope, receive, sender)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 566, in __call__
docker_fastapi_1 | await route.handle(scope, receive, send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle
docker_fastapi_1 | await self.app(scope, receive, send)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 41, in app
docker_fastapi_1 | response = await func(request)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/fastapi/routing.py", line 182, in app
docker_fastapi_1 | raw_response = await run_endpoint_function(
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/fastapi/routing.py", line 135, in run_endpoint_function
docker_fastapi_1 | return await run_in_threadpool(dependant.call, **values)
docker_fastapi_1 | File "/usr/local/lib/python3.8/site-packages/starlette/concurrency.py", line 34, in run_in_threadpool
docker_fastapi_1 | return await loop.run_in_executor(None, func, *args)
docker_fastapi_1 | File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run
docker_fastapi_1 | result = self.fn(*self.args, **self.kwargs)
docker_fastapi_1 | File "/app/api/main.py", line 83, in predict
docker_fastapi_1 | insert_into_db(X)
docker_fastapi_1 | File "/app/api/main.py", line 38, in insert_into_db
docker_fastapi_1 | cursor.execute(f"INSERT INTO public."API_Test""
docker_fastapi_1 | IndexError: index 1 is out of bounds for axis 0 with size 1
Here’s how I am sending API calls:
curl -X POST "http://0.0.0.0:3000/predict" -H "accept: application/json" -H "Content-Type: application/json" -d "{"input_data":[[
1.354e+01, 1.436e+01, 8.746e+01, 5.663e+02, 9.779e-02, 8.129e-02,
6.664e-02, 4.781e-02, 1.885e-01, 5.766e-02, 2.699e-01, 7.886e-01,
2.058e+00, 2.356e+01, 8.462e-03, 1.460e-02, 2.387e-02, 1.315e-02,
1.980e-02, 2.300e-03, 1.511e+01, 1.926e+01, 9.970e+01, 7.112e+02,
1.440e-01, 1.773e-01, 2.390e-01, 1.288e-01, 2.977e-01, 7.259e-02]]}"
This works just as expected when I build it with credentials of postgres instance of AWS RDS without this second postgres container and specify credentials directly inside psycopg2.connect()
without using environment variables and docker-compose and built directly using Dockerfile shown above; So, my code to insert the received data into postgres is presumably fine. And problems started when I introduced second container. What causes errors like these and How do I fix this?
2
Answers
The problem originated because of lot of
postgres
inside "docker-compose.yml" file.with the help of alim91's answer, and my realisation; here's what's working, if anyone might need it.
as can be seen, changing the service name from
postgres
topostgres_instance
fixed everything. presumably because,postgres
actually referred to where the database was hosted, but the tag is same as the user name and database name.you have to add network network and depend_on flags. try this: