I have a Python FastAPI app I'm trying to deploy to Google App Engine (GAE). I believe the gunicorn
command is the preferred command to have GAE run. It seems that the GAE deploy environment can be configured through an app.yaml
alone or in combination with a Dockerfile
. I need google chrome installed in the environment for a scraper, so I'm trying to configure with a Dockerfile
.
I deploy using the google cloud sdk via the command gcloud app deploy api.yaml
. It appears to deploy correctly, however, I get a 502
error on the actual deploy link, and my GAE logs show the error:
"/bin/sh: 1: exec: gunicorn: not found"
This is oft-asked across StackOverflow:
requirements.txt
asgiref==3.4.1
beautifulsoup4==4.9.3
certifi==2021.5.30
cffi==1.14.6
charset-normalizer==2.0.3
click==8.0.1
cryptography==3.4.7
fastapi==0.66.1
greenlet==1.1.0
h11==0.12.0
idna==3.2
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
mysql-connector-python==8.0.25
protobuf==3.17.3
pycparser==2.20
pydantic==1.8.2
PyMySQL==1.0.2
selenium==3.141.0
six==1.16.0
soupsieve==2.2.1
SQLAlchemy==1.4.21
SQLAlchemy-Utils==0.37.8
starlette==0.14.2
typing-extensions==3.10.0.0
urllib3==1.26.6
uvicorn==0.14.0
Werkzeug==2.0.1
gunicorn==19.9.0
api.yaml
env: flex
runtime: custom
service: api
My "entrypoint" is a CMD
line in my Dockerfile
:
Dockerfile
# syntax=docker/dockerfile:1
FROM python:3.8-slim-buster
WORKDIR /app
SHELL ["/bin/bash", "-c"]
RUN apt-get update && \
apt-get install -y gnupg wget curl unzip --no-install-recommends && \
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list && \
apt-get update -y && \
apt-get install -y google-chrome-stable && \
CHROMEVER=$(google-chrome --product-version | grep -o "[^\.]*\.[^\.]*\.[^\.]*") && \
DRIVERVER=$(curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROMEVER") && \
wget -q --continue -P /chromedriver "http://chromedriver.storage.googleapis.com/$DRIVERVER/chromedriver_linux64.zip" && \
unzip /chromedriver/chromedriver* -d /chromedriver
ENV CHROMEDRIVER_DIR /chromedriver
ENV PATH $CHROMEDRIVER_DIR:$PATH
COPY requirements.txt requirements.txt
COPY run.sh run.sh
RUN pip3 install -r requirements.txt
COPY . .
RUN chmod u+x run.sh
CMD ./run.sh
I did this because I wasn't sure how else to differentiate my run commands for local dev and production:
run.sh
set -a
source .env
set +a
if [ ${ENV} = "DEV" ]; then
uvicorn app.main:app --reload --host=0.0.0.0 --port=8000
else
gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker --bind :${PORT}
fi
I suspect that I'll need to set gunicorn in my PATH, however I'm not exactly sure how to determine my PYTHONPATH
from within the GAE deployment, so I don't know what to add to my Dockerfile
. I tried spinning up the container locally to test, but when I do that, gunicorn
is available on PATH:
with the docker image running on my machine:
docker exec -it game-remix-guesser_api_1 bash
root@41143c2ef307:/app# gunicorn
usage: gunicorn [OPTIONS] [APP_MODULE]
If my likely solution is to make gunicorn
available on PATH, how can I access the deployed GAE instance so as to determine what the path is to gunicorn so as to add it to PATH?
I believe I included relevant files above, but the whole repo is on github, and the relevant directory is backend
.
I would suggest the following : You can set the entry point in your code in app.yaml file :
entrypoint: gunicorn -b :$PORT main:app
Since your app.yaml is already configured, you don't need to customize your Dockerfile as Google App Engine requires. Keep it as your own custom one. So, in the Docker file, replace the command CMD ./run.sh with the following command to set the entry point:
CMD [ "python", "./run.sh" ]