Deploying docker containers to appengine

For my Ephemeral Exchange project I wanted to containerize as much as possible, as I wan't sure where I was eventually going to host it, and containerizing meant it would be easy to put pretty much anywhere. I'm not much of Linux guy, so hopefully this post will help other in the same position get started.

The server component consists of two main parts, both of which are containerized.
  • Redis - the back end cache store
  • effex-server - A Node/Express js app
This article will give a brief overview of how this stuff is deployed and is aimed at those that are thinking about Docker and App Engine for the first time.

Development environment

I use Cloud 9 for developing both front and back end apps. You get a full VM so you can run pretty much everything over there before deployment.

Hosting environment

Ephemeral Exchange is in developer preview right now, so I'm using a fairly minimal set up. The Redis container runs on Google Compute engine, and the the Server component runs on Google Appengine. This article is going to focus on the Appengine component

Setting up the container

There are plenty of articles out there for how to set up Docker, so I'll leave that to others, and just discuss the mechanics of docker/appengine here. I use a small compute engine instance to prepare the Docker images for deployment. It's basic and it's only job is to manage and deploy updates. When I want to deploy an update, I first push the updated code from Cloud 9 to github, then head over to my deployment instance and run this script. 
sudo usermod -a -G docker ${USER}
cd ~/docker/effex-api/server/src/effex-server
git pull
cd ..
cp effex-server/* .
cd ~/docker/effex-api/server docker build -t effex-api/server .
docker tag effex-api/server
gcloud docker -- push
gcloud app deploy

Here's a quick walk-through of  what's going on.
  • set the current user to be capable of doing stuff in su mode
  • move to the right place
  • pull the latest code from github
  • build the container image with the updated source code
  • tag the image with a google specific tag
  • push the tagged image to the google container repo
  • deploy the app to app engine

The Dockerfile

In the directory in which  the container build takes place, I have a very simple Dockerfile describing how its image should be built. 
# image for effex-api
# Pull base image.

# create place for app
RUN mkdir -p /usr/src/effex-api-server
WORKDIR /usr/src/effex-api-server

# copy the source files over and get the dependencies
COPY src/* /usr/src/effex-api-server/
RUN npm install

#sh for starting the server app
RUN mkdir -p /etc/service/start-effex-api-server
ADD /etc/service/start-effex-api-server/run

# Clean up APT when done.
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Expose ports. EXPOSE 8080
ENTRYPOINT ["/etc/service/start-effex-api-server/run"]

.. and a walk-through
  • base this container on the standard Node environment used by App engine
  • populate the container with the source of my app (that was copied from github earlier)
  • install the Node dependencies
  • copy over the starting script for the service (below) 
cd /usr/src/effex-api-server
node index.js
  • clean up and set the entry point to the starting script I just copied over

Tagging and pushing the docker file

I'll often accidentally use image and container interchangeably, but a container is a definition of how to build an image, which is an instance of a container. Google maintain a private registry of containers (a little like the Docker container registry), in the cloud storage for you project. Tagging the image with a tag tells it where to store it in that registry when its uploaded. It creates an entry in the container registry - you can see a snip below.

which reference to a bunch of dependent images in cloud storage like this when pushed.

Deploying to appengine

All that's left now is to deploy the app. For the sake of this post I've simplified things a bit as I have a couple of versions of this depending on whether I'm working on prod or dev, but the prod version simply uses the default appengine service and my app.yaml is just this - nothing more.

runtime: custom
env: flex

Using the flex environment instructs app engine that it has to load and run a container. The Dockerfile along with the google tags tells it which one, and where to find it in cloud storage. I could have used the nodejs runtime rather than the custom runtime (since I'm using node), but I wanted to have a little more control over what's in the container for some future developments so I stuck with the custom runtime and built node into my own image.


And that's all there is. I had pretty much kept away from Appengine for a long time, since I didn't really want to use any of the supported languages such as python or Java, but now that it supports containers it really is a great hosting environment to which you can deploy whatever you can containerize.

For more like this, see React, redux, redis, material-UI and firebase Why not join our forum, follow the blog or follow me on twitter to ensure you get updates when they are available.