.. _appengine_flexible_tutorial: Google App Engine Flexible with Datastore and :app:`Pyramid` ============================================================ It is possible to run a :app:`Pyramid` application on `Google App Engine `_. This tutorial is written "environment agnostic", meaning the commands here should work on Linux, macOS or Windows. This tutorial also assumes you've already installed and created a :app:`Pyramid` application, and that you have a Google App Engine account. Setup ----- First we'll need to set up a few things in App Engine. If you don't need Datastore access for your project or any other GCP service, you can skip the Credentials section. Credentials ########### Navigate to App Engine's IAM And Admin section and click on Service Accounts in the left sidebar, then create a `Service Account `_. Once a service account is created, you will be given a ``.json`` key file. This will be used to allow your Pyramid application to communicate with GCP services. Move this file to your Pyramid project. A best practice here would be to make sure this file is listed in ``.gitignore`` so that it's not checked in with the rest of your code. Now that we have a service account, we'll need to give it a couple of roles. Click :guilabel:`IAM` in the left sidebar of :guilabel:`IAM And Admin`. Find the service account you've just created and click the :guilabel:`Edit` button. Give this account the *Cloud Datastore User* role for read/write access. For read-only access, give it *Cloud Datastore Viewer*. Project Files ############# Create the files with content as follows. #. ``requirements.txt`` .. code-block:: text Pyramid waitress pyramid_debugtoolbar pyramid_chameleon google-cloud-ndb If you are not using Datastore, you can exclude ``google-cloud-ndb``. #. ``dockerfile`` .. code-block:: docker FROM gcr.io/google-appengine/python # Create a virtualenv for dependencies. This isolates these packages from # system-level packages. # Use -p python3 or -p python3.7 to select python version. Default is version 2. RUN virtualenv /env -p python3 # Setting these environment variables are the same as running # source /env/bin/activate. ENV VIRTUAL_ENV /env ENV PATH /env/bin:$PATH ENV PYTHONUNBUFFERED 0 # Copy the application's requirements.txt and run pip to install all # dependencies into the virtualenv. ADD requirements.txt /app/requirements.txt ADD my-gcp-key.json /app/my-gcp-key.json ENV GOOGLE_APPLICATION_CREDENTIALS /app/my-gcp-key.json RUN pip install -r /app/requirements.txt # Add the application source code. ADD . /app # Run a WSGI server to serve the application. waitress must be declared as # a dependency in requirements.txt. RUN pip install -e . CMD pserve production.ini Replace ``my-gcp-key.json`` filename with the JSON file you were provided when you created the Service Account. #. ``datastore_tween.py`` .. code-block:: python from my_project import datastore_client class datastore_tween_factory(object): def __init__(self, handler, registry): self.handler = handler self.registry = registry def __call__(self, request): with datastore_client.context(): response = self.handler(request) return response #. ``app.yaml`` .. code-block:: yaml runtime: custom env: flex service: default runtime_config: python_version: 3.7 manual_scaling: instances: 1 resources: cpu: 1 memory_gb: 0.5 disk_size_gb: 10 For more details about ``app.yaml``, see `app.yaml Reference `_. #. ``__init__.py`` This file should already exist in your project at the root level as it would've been generated by Pyramid's `cookiecutters `_. Add the following line within the ``main`` method's ``config`` context: .. code-block:: python config.add_tween('my_project.datastore_tween.datastore_tween_factory') This allows you to communicate with Datastore within every request. #. ``production.ini`` Your :app:`Pyramid` application should already contain both a ``development.ini`` and a ``production.ini``. For App Engine to communicate with your application, it will need to be listening on port 8080. Assuming you are using the Waitress WSGI server, modify the ``listen`` variable within the ``server:main`` block. .. code-block:: ini listen = *:8080 Now let's assume you have the following model defined somewhere in your code that relates to a Datastore "kind": .. code-block:: python from google.cloud import ndb class Accounts(ndb.Model): email = ndb.StringProperty() password = ndb.StringProperty() def __init__(self, **kwds): super(Accounts, self).__init__(**kwds) You could then query this model within any handler/endpoint like so: .. code-block:: python Accounts.query().filter(Accounts.email == user_email).get() Running locally --------------- Unlike App Engine's Standard environment, we're running Pyramid in a pretty typical fashion. You can run this locally on your machine using the same line in the ``dockerfile`` we created earlier as ``pserve development.ini``, or you can run in a Docker container using the same ``dockerfile`` that Flexible will be using. No changes need to be made there. This is useful for debugging any issues you may run in to under Flexible, without needing to deploy to it. Deploying --------- Using the Google Cloud SDK, deploying is pretty straightforward. .. code-block:: bash $ gcloud app deploy app.yaml --version my-version --project my-gcp-project Replace ``my-version`` with some kind of identifier so you know what code is deployed. This can pretty much be anything. Replace ``my-gcp-project`` with your App Engine application's ID. Your Pyramid application is now live to the world! You can access it by navigating to your domain name, by ".appspot.com", or if you've specified a version outside of your default then it would be ".appspot.com".