Search code examples
pythondockergoogle-app-enginegoogle-cloud-platformgunicorn

'exec: gunicorn: not found' but is in requirements.txt, how to add to Google App Engine Dockerfile-configured app path?


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:

  1. I have gunicorn in my requirements.txt

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
  1. I don't have both a Pipfile and Pipfile.lock

  2. I don't use entrypoint in my yaml file so I'm not sure how to change the entrypoint configuration

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.


Solution

  • 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" ]