Alistair
Alistair

Reputation: 491

FastAPI app running locally but not in Docker container

I have a FastAPI app that is working as expected when running locally, however, I get an 'Internal Server Error' when I try to run in a Docker container. Here's the code for my app:

from fastapi import FastAPI
from pydantic import BaseModel
import pandas as pd
from fbprophet import Prophet

class Data(BaseModel):
    length: int
    ds: list
    y: list
    model: str
    changepoint: float = 0.5
    daily: bool = False
    weekly: bool = False
    annual: bool = False
    upper: float = None
    lower: float = 0.0
    national_holidays: str = None

app = FastAPI()

@app.post("/predict/")
async def create_item(data: Data):

    # Create df from base model
    df = pd.DataFrame(list(zip(data.ds, data.y)), columns =['ds', 'y'])

    # Add the cap and floor to df for logistic model
    if data.model == "logistic":
        df['y'] = 10 - df['y']
        df['cap'] = data.upper
        df['floor'] = data.lower

    # make basic prediction
    m = Prophet(growth=data.model,
                changepoint_prior_scale=data.changepoint,
                weekly_seasonality=data.weekly,
                daily_seasonality=data.daily,
                yearly_seasonality=data.annual
                )

    # Add national holidays
    if data.national_holidays is not None:
        m.add_country_holidays(country_name=data.national_holidays)

    # Fit data frame
    m.fit(df)

    # Create data frame for future
    future = m.make_future_dataframe(periods=data.length)

    # Add the cap and floor to future for logistic model
    if data.model == "logistic":
        future['cap'] = 6
        future['floor'] = 1.5

    # forecast
    forecast = m.predict(future)

    # Print values
    print(list(forecast[['ds']].values))

    # Return results
    # {'ds': forecast[['ds']], 'yhat': forecast[['yhat']], 'yhat_lower': forecast[['yhat_lower']], 'yhat_upper': forecast[['yhat_upper']] }
    return [forecast[['ds']], forecast[['yhat']], forecast[['yhat_lower']], forecast[['yhat_upper']]]

Which is working locally with uvicorn main:app, but not when I build using this Dockerfile:

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
COPY ./app /app
RUN pip install -r requirements.txt

and start with

docker run -d --name mycontainer -p 8000:80 myimage

I'm seeing Internal Server Error in Postman. Is there something wrong with my dockerfile or docker commands? Or else how do I debug this?

Upvotes: 12

Views: 14367

Answers (4)

Dinuda Yaggahavita
Dinuda Yaggahavita

Reputation: 990

When you run a FastAPI app inside a Docker container and try to access it from outside the container (like from your host machine or another container), you might run into an 'Internal Server Error' or 'Error: read ECONNRESET' even if the app works perfectly fine on your local machine. This issue often comes due to how the app is configured to handle network connections.

In the provided code snippet, the settings.host value tells us the address the app will bind to. If settings.host is set to 127.0.0.1, the app will only accept connections from within the container itself. This is because 127.0.0.1 is the loopback address, which means it's only accessible from the same container.

Docker containers have their own isolated network environments, and each container has its own localhost (127.0.0.1) that isn't shared with the host machine or other containers. So, if you bind your app to 127.0.0.1, it won't be accessible from outside the container.

To make your Fastapi app accessible from outside the container, you need to set settings.host to 0.0.0.0. This tells the app to listen on all available network interfaces, including the one Docker uses to route external traffic to the container.

Fix: Update your code to ensure settings.host is set to 0.0.0.0:

uvicorn.run(
    "exg_platform.web.application:get_app",
    workers=settings.workers_count,
    host="0.0.0.0",  # Bind to all interfaces
    port=settings.port,
    reload=settings.reload,
    log_level=settings.log_level.lower(),
    factory=True,
)

Upvotes: 1

Evgene
Evgene

Reputation: 596

I had the same problem because --port 8000 was missing in

command: uvicorn main:app --host 0.0.0.0 --port 8000

Upvotes: 11

hi2meuk
hi2meuk

Reputation: 2014

There are a number of possible issues here.

  1. When running in a container, you need to tell Uvicorn to not care about the incoming host IP with option --host 0.0.0.0

  2. You have not specified the command executed when the image runs. Best to make this clear in the dockerfile e.g. CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

  3. Make sure when you run the image you map the ports correctly. From what you have given you are expecting the service in the container to be listening on port 80, that is OK but the default for uvicorn is 8000 (see point 2 for explicitly setting port).

Upvotes: 20

Marcelo Trylesinski
Marcelo Trylesinski

Reputation: 824

Run the docker without the -d parameter and you'll get more clues about it. If I were to guess, I might say that you're missing some python requirement.

Upvotes: 7

Related Questions