bezel zelek
bezel zelek

Reputation: 31

FastApi on uvicorn works good but fails to work with Docker and docker-compose

I have a FastAPI app that works well when I launch it as a script. Unfortunately, I can't get access to my app when I'm using Docker and docker-compose.

I can't reach it by 127.0.0.1:80 or 127.0.0.1:8000. At the same time, I'm using configs that worked well for me with another project but with Flask. Can someone say what I did wrong?

My project structure

├── projectname
│   ├── src
│   └── app.py
├── Dockerfile
├── Makefile
├── docker-compose.yaml
└── requirements.txt

This is my app.py

import uvicorn

from typing import Any
from fastapi import FastAPI, HTTPException
from motor.motor_asyncio import AsyncIOMotorClient

from src.fastapi_pagination.ext.motor import paginate
from src.fastapi_pagination import Page, add_pagination

from src.settings import DB_CONNECTION
from src.models import PropertyModel, LogModel, LogDetailsModel


app = FastAPI()
client: AsyncIOMotorClient


@app.on_event("startup")
async def on_startup() -> None:
    global client
    client = AsyncIOMotorClient(DB_CONNECTION)


@app.get("/api/property/{property_id}", response_description="Get a single property", response_model=PropertyModel)
async def show_property(property_id: str):
    if (result := await client.property_listing.properties.find_one({"property_id": property_id})) is not None:
        return result
    raise HTTPException(status_code=404, detail=f"Property {property_id} not found")


@app.get("/api/properties", response_description="Get property list", response_model=Page[PropertyModel])
async def show_properties() -> Any:
    sort = [('property_new', -1), ('property_date_updated', -1)]
    if (result := await paginate(client.property_listing.properties, sort_query=sort)) is not None:
        return result
    else:
        raise HTTPException(status_code=404, detail=f"No properties on this page")


add_pagination(app)

if __name__ == "__main__":
    uvicorn.run("app:app", port=8000, host='0.0.0.0')

My Dcokerfile

FROM python:3.8.3-alpine3.12

COPY ["requirements.txt", "/app/"]

RUN apk add --update --no-cache make bash libpq

RUN apk add --no-cache --virtual build \
    build-base \
    musl-dev \
    libffi-dev \
    openssl-dev \
    && pip install --no-cache-dir -r /app/requirements.txt \
    && apk del build

WORKDIR /app
EXPOSE 8000

My docker-compose.yaml file

version: '3.8'

services:
  app_launch:
    build: .
    image: fastapi-service:dev
    ports:
    - "8000:8000"
    volumes:
    - .:/app

My Makefile

### Compose shortcuts
up:
    docker-compose up -d

down:
    docker-compose down

build:
    docker-compose build

sh:
    docker-compose run -p 8000:8000 --rm app_launch bash

logs:
    docker-compose logs -f

### Project shortcuts
fast_api:
    docker-compose run --rm app_launch python src/app.py

fast_api_app:
    docker-compose run --rm app_launch uvicorn src.app:app --proxy-headers --host 0.0.0.0 --port 8000

When I use make build image builds well. make fast_api and make fast_api_app both work well. I have an output like that:

docker-compose run --rm app_launch python src/app.py
Creating fast_test_app_launch_run ... done
INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

But I still can't reach my app from the browser. Could someone explain how to fix it?

Upvotes: 3

Views: 5900

Answers (1)

David Maze
David Maze

Reputation: 158647

docker-compose run by default does not publish ports: from the container. It's not intended to be the way you launch your main application; instead, it's there to run one-off commands like database migrations that require most of the container setup but can run alongside the application proper.

Probably the best way to address this is to set a standard CMD in your Dockerfile, so the container knows what to do:

CMD uvicorn src.app:app --proxy-headers --host 0.0.0.0 --port 8000

Then you don't need docker-compose run or any of the other variants you show here; just running docker-compose up will start your application.

docker-compose run also has a --service-ports option that will make it publish the container's ports: anyways, if that's the problem you're running into, but this wouldn't be my preferred way to launch a service container.

Upvotes: 4

Related Questions