I am trying to deploy a FastAPI application on a VPS running Linux CentOS 7 by using Docker and Docker compose, but I am running into issues connecting to a database I have stored on the server. Either the database instance won't connect or the FastAPI workers can't attach themselves to a port.
I am using FastAPI to run my server and databases to interact with the MySQL database. Below are my files:
Docker
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
docker-compose.yml
services:
backend:
container_name: backend
build: ./
restart: always
ports:
- 8080:80
extra_hosts:
- "host.docker.internal:host-gateway"
main.py
from fastapi import FastAPI
from database import db
from endpoints import ingredient, recipe, recipe_ingredient
app = FastAPI()
app.include_router(ingredient.router)
app.include_router(recipe_ingredient.router)
app.include_router(recipe.router)
@app.on_event("startup")
async def startup():
await db.connect()
@app.on_event("shutdown")
async def shutdown():
await db.disconnect()
database.py
import databases
from starlette.config import Config
config = Config(".env")
# Database configurations
_user = config("USER", cast=str)
_password = config("PASSWORD", cast=str)
_host = config("HOST", cast=str)
_database = config("DATABASE", cast=str)
_port = config("PORT", cast=int, default=3306)
db = databases.Database(
f'mysql://{_user}:{_password}@{_host}/{_database}?port={_port}'
)
In this configuration I set the environment variables to:
USER=root
PASSWORD=***
HOST=host.docker.internal
DATABSE=test_database
However, I think this configuration causes connection issues as what happens is that the uvicorn workers continually wait for the application to start (since the database connection happens before the instantiation of app = FastAPI()).
Error Logs
Attaching to backend
backend | Checking for script in /app/prestart.sh
backend | Running script /app/prestart.sh
backend | Running inside /app/prestart.sh, you could add migrations to this file, e.g.:
backend |
backend | #! /usr/bin/env bash
backend |
backend | # Let the DB start
backend | sleep 10;
backend | # Run migrations
backend | alembic upgrade head
backend |
backend | [2022-10-18 05:41:31 +0000] [1] [INFO] Starting gunicorn 20.1.0
backend | [2022-10-18 05:41:31 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
backend | [2022-10-18 05:41:31 +0000] [1] [INFO] Using worker: uvicorn.workers.UvicornWorker
backend | [2022-10-18 05:41:31 +0000] [7] [INFO] Booting worker with pid: 7
backend | [2022-10-18 05:41:31 +0000] [8] [INFO] Booting worker with pid: 8
backend | [2022-10-18 05:41:31 +0000] [9] [INFO] Booting worker with pid: 9
backend | [2022-10-18 05:41:32 +0000] [10] [INFO] Booting worker with pid: 10
backend | [2022-10-18 05:41:32 +0000] [8] [INFO] Started server process [8]
backend | [2022-10-18 05:41:32 +0000] [8] [INFO] Waiting for application startup.
backend | [2022-10-18 05:41:32 +0000] [7] [INFO] Started server process [7]
backend | [2022-10-18 05:41:32 +0000] [7] [INFO] Waiting for application startup.
backend | [2022-10-18 05:41:32 +0000] [9] [INFO] Started server process [9]
backend | [2022-10-18 05:41:32 +0000] [9] [INFO] Waiting for application startup.
backend | [2022-10-18 05:41:32 +0000] [10] [INFO] Started server process [10]
backend | [2022-10-18 05:41:32 +0000] [10] [INFO] Waiting for application startup.
Here the workers are kept waiting for application startup indefinitely.
I have also tried a different configuration, changing the docker-compose file to:
services:
backend:
container_name: backend
build: ./
restart: always
ports:
- 8080:80
network_mode: "host"
And then then HOST=127.0.0.0 as the environment variable, but this produces a completely different error. Now, instead of hanging FastAPI says that port 80 is in use!
Attaching to backend
backend | Checking for script in /app/prestart.sh
backend | Running script /app/prestart.sh
backend | Running inside /app/prestart.sh, you could add migrations to this file, e.g.:
backend |
backend | #! /usr/bin/env bash
backend |
backend | # Let the DB start
backend | sleep 10;
backend | # Run migrations
backend | alembic upgrade head
backend |
backend | [2022-10-18 15:44:43 +0000] [1] [INFO] Starting gunicorn 20.1.0
backend | [2022-10-18 15:44:43 +0000] [1] [ERROR] Connection in use: ('0.0.0.0', 80)
backend | [2022-10-18 15:44:43 +0000] [1] [ERROR] Retrying in 1 second.
backend | [2022-10-18 15:44:44 +0000] [1] [ERROR] Connection in use: ('0.0.0.0', 80)
backend | [2022-10-18 15:44:44 +0000] [1] [ERROR] Retrying in 1 second.
backend | [2022-10-18 15:44:45 +0000] [1] [ERROR] Connection in use: ('0.0.0.0', 80)
backend | [2022-10-18 15:44:45 +0000] [1] [ERROR] Retrying in 1 second.
backend | [2022-10-18 15:44:46 +0000] [1] [ERROR] Connection in use: ('0.0.0.0', 80)
backend | [2022-10-18 15:44:46 +0000] [1] [ERROR] Retrying in 1 second.
backend | [2022-10-18 15:44:47 +0000] [1] [ERROR] Connection in use: ('0.0.0.0', 80)
backend | [2022-10-18 15:44:47 +0000] [1] [ERROR] Retrying in 1 second.
etc.
This is especially weird because port 80 is not in use! It's open. Running sudo ss -tulpn | grep :80 reveals
tcp LISTEN 0 128 *:80 *:* users:(("httpd",pid=29213,fd=3),("httpd",pid=29202,fd=3),("httpd",pid=29184,fd=3),("httpd",pid=29157,fd=3),("httpd",pid=29155,fd=3),("httpd",pid=29152,fd=3),("httpd",pid=4823,fd=3))
tcp LISTEN 0 128 [::]:80 [::]:* users:(("httpd",pid=29213,fd=4),("httpd",pid=29202,fd=4),("httpd",pid=29184,fd=4),("httpd",pid=29157,fd=4),("httpd",pid=29155,fd=4),("httpd",pid=29152,fd=4),("httpd",pid=4823,fd=4))
I am all out of ideas. Any suggestions as to what I could try next?
Edit
Where is the database running, and where is the fastapi service running?
My database is running on the VPS. A service called phpMyAdmin came installed with the server, and I uploaded my existing database using this service. The hosting provider is Namecheap.
FastAPI is running in a Docker container, also on the VPS, located in directory specific to my subdomain.
Is the database running on the host?
Yes. phpMyAdmin tells me it is running on localhost:3306
If the fastapi service is running in the container, where are you setting the environment variables?
I have placed a .env in with the container. It is in the same directory as my main.py files. I have not noticed any issues with the reading in of these environment variables - all processes seem to access them fine.
network: hostbecause your FastAPI app is trying to open port 80, and something on your host already has port 80 open. Port publishing (8080:80) is a no-op when using host network mode. I would just remove that entire section of the question, because it's distracting from other issues.