GoogleCloudPlatform / functions-framework-python

FaaS (Function as a service) framework for writing portable Python functions
https://pypi.org/p/functions-framework/
Apache License 2.0
863 stars 118 forks source link

Document ability to use Functions Framework as a WSGI app #43

Open di opened 4 years ago

di commented 4 years ago

While #36 removed the example of fine-tuning gunicorn, it's still possible to use the Functions Framework as a WSGI app in this way.

We should document how to do this if you need finer control over the HTTP server used, and also document some other WSGI servers that users could use instead of gunicorn if they need to.

someone1 commented 4 years ago

Sorry to lurk onto this - but the example Dockerfile's use of CMD exec functions-framework --target=hello seems to conflict with the README.md's guidance of using CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 -e FUNCTION_TARGET=hello functions_framework:app.

Additionally, using functions-framework outputs the following log suggesting we shouldn't use it as found in the example Dockerfile:

   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.

Is using function-framework as the entrypoint/cmd in Docker production-ready? If so, should the README and log outputs be updated to reflect that? If not, should the Dockerfile examples be updated to something that is production ready?

di commented 4 years ago

Is it possible you're using an older version of the repo, or an older version of the framework, or have some of the Docker layers cached? Since version 1.4.0, invoking functions-framework will use gunicorn as a production HTTP server by default.

Building the example Dockerfile gives me the following:

$ docker build -t foo . && docker run -it foo
Sending build context to Docker daemon   2.26MB
Step 1/7 : FROM python:3.7-slim
 ---> 7e61acc68112
Step 2/7 : ENV APP_HOME /app
 ---> Running in fc8547ea7c82
Removing intermediate container fc8547ea7c82
 ---> 4e7b96ad14c7
Step 3/7 : WORKDIR $APP_HOME
 ---> Running in 7c1692337e18
Removing intermediate container 7c1692337e18
 ---> 8b5d0a1917fe
Step 4/7 : COPY . .
 ---> 6060bfbd412f
Step 5/7 : RUN pip install gunicorn functions-framework
 ---> Running in 6bfc44611d78
Collecting gunicorn
  Downloading gunicorn-20.0.4-py2.py3-none-any.whl (77 kB)
Collecting functions-framework
  Downloading functions_framework-2.0.0-py3-none-any.whl (21 kB)
Requirement already satisfied: setuptools>=3.0 in /usr/local/lib/python3.7/site-packages (from gunicorn) (45.1.0)
Collecting click<8.0,>=7.0
  Downloading click-7.1.2-py2.py3-none-any.whl (82 kB)
Collecting watchdog>=0.10.0
  Downloading watchdog-0.10.3.tar.gz (94 kB)
Collecting cloudevents<1.0
  Downloading cloudevents-0.3.0-py3-none-any.whl (26 kB)
Collecting flask<2.0,>=1.0
  Downloading Flask-1.1.2-py2.py3-none-any.whl (94 kB)
Collecting pathtools>=0.1.1
  Downloading pathtools-0.1.2.tar.gz (11 kB)
Collecting Werkzeug>=0.15
  Downloading Werkzeug-1.0.1-py2.py3-none-any.whl (298 kB)
Collecting Jinja2>=2.10.1
  Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
Collecting itsdangerous>=0.24
  Downloading itsdangerous-1.1.0-py2.py3-none-any.whl (16 kB)
Collecting MarkupSafe>=0.23
  Downloading MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl (27 kB)
Building wheels for collected packages: watchdog, pathtools
  Building wheel for watchdog (setup.py): started
  Building wheel for watchdog (setup.py): finished with status 'done'
  Created wheel for watchdog: filename=watchdog-0.10.3-py3-none-any.whl size=73871 sha256=b83e6a9b18502f9e804c26cccbed2455eb498b8a3b8549bca45c6bf843b9374b
  Stored in directory: /root/.cache/pip/wheels/27/21/35/9d1e531f9de5335147dbef07e9cc99d312525ba128a93d1225
  Building wheel for pathtools (setup.py): started
  Building wheel for pathtools (setup.py): finished with status 'done'
  Created wheel for pathtools: filename=pathtools-0.1.2-py3-none-any.whl size=8784 sha256=452f069740a235eefed3845fab97a27f6a49770f75258795324e94bd7730f944
  Stored in directory: /root/.cache/pip/wheels/3e/31/09/fa59cef12cdcfecc627b3d24273699f390e71828921b2cbba2
Successfully built watchdog pathtools
Installing collected packages: gunicorn, click, pathtools, watchdog, cloudevents, Werkzeug, MarkupSafe, Jinja2, itsdangerous, flask, functions-framework
Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1 Werkzeug-1.0.1 click-7.1.2 cloudevents-0.3.0 flask-1.1.2 functions-framework-2.0.0 gunicorn-20.0.4 itsdangerous-1.1.0 pathtools-0.1.2 watchdog-0.10.3
WARNING: You are using pip version 20.0.2; however, version 20.1.1 is available.
You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
Removing intermediate container 6bfc44611d78
 ---> 347c1de2a315
Step 6/7 : RUN pip install -r requirements.txt
 ---> Running in 9af3481eb650
WARNING: You are using pip version 20.0.2; however, version 20.1.1 is available.
You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
Removing intermediate container 9af3481eb650
 ---> af2de3fe8bae
Step 7/7 : CMD exec functions-framework --target=hello
 ---> Running in 489432a1b73a
Removing intermediate container 489432a1b73a
 ---> 7d495285d82c
Successfully built 7d495285d82c
Successfully tagged foo:latest
[2020-07-02 20:13:52 +0000] [1] [INFO] Starting gunicorn 20.0.4
[2020-07-02 20:13:52 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[2020-07-02 20:13:52 +0000] [1] [INFO] Using worker: threads
[2020-07-02 20:13:52 +0000] [8] [INFO] Booting worker with pid: 8

Note that there is no warning and that it invokes gunicorn.

someone1 commented 4 years ago

Ahhh - I think the confusion was that the root README recommends we run functions-framework --target hello --debug which I guess starts a different server with the --debug flag.

After I saw the warning message and how the cloud run http example README says to use gunicorn but the Dockerfile uses functions-framework had me thinking the Dockerfile was using a development server as the --debug option logs out:

* Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.

So I guess the answer to my question is, functions-framework without the --debug flag will start the service suitable for production (using gunicorn) and the mismatch between README and Dockerfile is unimportant.

Apologies for the confusion!