Deploying Dash App to AWS Server using Gunicorn

I am running my application in a docker container using Gunicorn as a production server on 0.0.0.0:8000. Within the VM, I used “sudo docker run -it -p 8000:8000 --name <container_name> <container_id>” to start up my container image and application. I can exec into the running container and check out the application log, which as a troubleshooting tip I printed “working” during application initialization (in app.py file), which I was able to read when querying the logs. When I go to access the application externally at “https://ip address:8000” or “https://url” or “https://url:8000” the site says “cannot be reached”. I have already enabled a security group to work on tcp protocol on port 8000 for this VM. What can I do to troubleshoot this setup? The app works fine locally and on a VM hosted on my local machine.

Hello @lukekasper25,

Did you follow any guides to host? You need to make sure you expose the underlying server and not the dash app for this to run.

I followed several, can you elaborate on your solution? My command in my dockerfile to start the server was CMD [“gunicorn”, “–workers=8”, “–threads=4”, “-b 0.0.0.0:8000”, “sitrep:server”] if thats what you were referring to.

Yes, this was what I was referring to.

Are your requirements loading properly to the server?

Yes requirements all look fine. My command to start the dash app initially was:
app = Dash(
name, server=server, use_pages=True, suppress_callback_exceptions=True
)
and
if name == “main”:
# Used for development
app.run(debug=False)

Do I need to add host and port number options here at the bottom in app.run? Would host ip be ‘0.0.0.0’ or the ip address of the host VM?

Can you go into the console and run it on port 8000?

Yes everything looks fine in that case, see above modifications to my answer.

No, you dont need to pass those arguments to your configuration, this is all handled by gunicorn and how it spins up.

Thats what I thought, then I truly am at a loss here. Do I need to provide additional configuration files for Gunicorn?

So I have a container running in AWS ECS, this is what my dockerfile has in it:

Dockerfile

FROM ubuntu:latest 

WORKDIR /app
COPY . /app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHON UNBUFFERED 1

RUN apt-get update \
    && apt-get upgrade -y python3-pip \
    && apt-get install -y python3.8 wget curl \
    && pip3 install -r requirements.txt \
    && apt autoremove -y

EXPOSE 8050

ENTRYPOINT [ "gunicorn", "--config", "gunicorn_config.py", "app:server" ]

gunicorn_config.py

bind = "0.0.0.0:8050"
workers = 3
timeout = 120

app.py

app = Dash(__name__)
server = app.server

if __name__ == '__main__':
    app.run_server()

I’m no container (or Docker) expert but maybe try exposing the port in the Dockerfile? You can (within reason) give it any IP or port, i think 8050 is a pretty common one. Is your app behind a load balancer? Is the routing set up if you have a proxy? I usually find it’s the simplest of things thats wrong for me.

2 Likes

@jinnyzor

Since it is an ongoing discussing, my I ask what is the difference between adding

CMD [ "gunicorn", "--config", "gunicorn_config.py", "app:server" ]

vs

CMD ["python", "app.py"]

I am especially curious when should I use the latter over the former etc.

I guess the latter one is used to run a local container?

Correct.

The second is to run a local version without gunicorn.

Gunicorn runs the wsgi server (flask), so it needs to be getting the flask server from the app. Now, if you wanted it to be easier, you could have it configured like this:

from dash import *

dash_app = Dash(__name__)

app = dash_app.server

Then you wouldn’t need to actually do anything different when configuring for gunicorn, and in fact the default setting for gunicorn is to search for the “app” in the “app.py”.

Just depends on how you want to configure your variables.

1 Like

To answer your top question @tphil10, I’ve tried exposing the port within the Dockerfile with no effect. When I start up my docker container with docker run command, I specify the port binding with -p 8000:8000, so that should take care of that.

Update: when starting the container in attached mode and curling the localhost within the virtual machine, the application spits out the following error:


Exception occurred during processing of request from (‘127.0.0.1’, 35168)
Traceback (most recent call last):
File “/usr/local/lib/python3.9/socketserver.py”, line 316, in _handle_request_noblock
self.process_request(request, client_address)
File “/usr/local/lib/python3.9/socketserver.py”, line 697, in process_request
t.start()
File “/usr/local/lib/python3.9/threading.py”, line 899, in start
_start_new_thread(self._bootstrap, ())
RuntimeError: can’t start new thread

Has anyone seen this before?

So are you saying, as long as I have

app = dash_app.server

in my app.py; I can use

CMD ["python", "app.py"]

to run a remote container?

No, that would be the configuration for using gunicorn to run 1 worker at port 8000.

CMD ['gunicorn']

My googling skills are not the greatest but that usually has something to do with a version mismatch. I did find an article here that may get you started?