-
Notifications
You must be signed in to change notification settings - Fork 1
Tutorial part 1
This tutorial describes how to run a simple Google App Engine app with static files on Docker. In part 1, the app will be run locally for development purposes. Files can be edited and the server will automatically reload the new files. In part 2 we'll cover how to run the app in a production environment.
These instructions are tailored to running Docker on a Mac, since it involved a couple of additional stumbling blocks. If you're on Linux, it should be easier to install and run Docker. In particular, you should be able to skip the parts about using boot2docker and shared folders. Consult the official documentation for the latest install instructions or for other platforms.
These are the steps I used to install Docker on my Mac circa July 2014:
- Install boot2docker using the latest Mac package.
- There is a boot2docker GUI app, but the command line is pretty simple. Initialize and start the VM with:
$ boot2docker init # You only need this step once!!
$ boot2docker start
Set up the environment variable, if prompted. 3. Set up a shared directory by following the steps in the first two code boxes of the boot2docker folder sharing instructions. Note that connecting to the shared folder can be found in the Finder menus at Go -> Connect to Server, and you should connect as a guest. 4. I personally ran into two connection issues with the VM which were both solved by using SSH to connect to the VM with:
$ boot2docker ssh -L 8080:localhost:8080
On my machine the Docker commands were not always forwarded to the VM, and the VM ports were not opened by default per the boot2docker documentation. Thus, this SSH session is the best place to run docker commands.
Run these two commands on your Mac (i.e. not in the boot2docker SSH session) to get the sample app and put it in the shared directory:
- cd /Volumes/data
- git clone https://github.com/Davepar/docker-webapp2.git
If you happen to have the Google App Engine tools installed, you can optionally verify that the app actually runs in that environment with:
$ dev_appserver.py /Volumes/data/docker-webapp2/app
And then visiting http://localhost:8080. Be sure to kill that server with ctrl-c to avoid port conflicts before moving on.
The last step is to run an HTTP server in a Docker container. Here's the command and I'll explain it afterward (run this in the boot2docker SSH session):
$ docker run -it --rm -p 8080:8080 --volumes-from my-data davepar/webapp2 \
python /data/docker-webapp2/app/main.py
On Linux (i.e. not using boot2docker), you should use -v instead of --volumes-from to share a directory from the host inside the container.
Once you see the line that starts with "Running on", visit http://localhost:8080 and you should see a fancy "Hello world". Try editing the HTML in main.py from you Mac, save, and then reload the page. You should see that it's updated.
To stop the server, just hit ctrl-c in the SSH session.
To explain the "run" command a bit, a new Docker container was created from an image on Docker Hub and then a Python script was run inside it. Here's a piece-by-piece breakdown of each of the args:
-
-it This runs the container in interactive mode. This makes it easy to see the messages from the server and easier to kill with ctrl-c. You could run the command with -d instead and use the
docker logs
command to see the output, anddocker stop
to kill it. -
--rm This will delete the container after we're done with it. You can leave this option off, but you'll end up with quite a few lingering stopped containers after a while. You can see the stopped containers with
docker ps -a
. - -p 8080:8080 Our HTTP server is listening on port 8080 in the container. This parameter will tie port 8080 from the Docker container to port 8080 in the boot2docker VM. Without this the port would not be visible outside the container.
- --volumes-from my-data This tells the container to mount the same volumes as the "my-data" container that we created in the "folder sharing" instructions. This is an easy way to persist storage inside the stopped container. We can run containers that access that volume over and over without it disappearing... until you delete the my-data container. I'd suggest making sure you have a backup of anything on that volume via source control or at least a file copy.
- davepar/webapp2 The container will be created from this image. You can view information about the image on Docker Hub. There you'll see that the image is automatically built from my Github repository Davepar/docker-webapp2. Looking at the Dockerfile there, you'll see the image is based on Ubuntu 14.04 with the addition of webapp2 and it's dependencies. One additional piece is Werkzeug, explained below.
- python /data/docker-webapp2/app/main.py This runs the main script of the website inside the Docker container.
If you looked at the Dockerfile for the image, you may be wondering about Werkzeug. It's a small utility library that among other things can turn a WSGI application into an HTTP server. All webapp2 applications are WSGI, so this is perfect for our needs. This small chunk of code in main.py invokes Werkzeug:
def main():
import os
from werkzeug.serving import run_simple
from werkzeug.wsgi import SharedDataMiddleware
# Add handler for static files
app_with_static = SharedDataMiddleware(app, {
'/static': os.path.join(os.path.dirname(__file__), 'static')
})
# Bind to all addresses (i.e. 0.0.0.0), otherwise the world outside this
# Docker container won't be able to see the server
run_simple('0.0.0.0', 8080, app_with_static, use_reloader=True)
The SharedDataMiddleware call is an easy way to serve static files from the same HTTP server.
When you're done playing with Docker, you can exit the SSH session and then stop the VM with:
$ boot2docker stop
The shared directory will be disconnected, but it will survive inside the Docker container within the VM image. When you want to play again, do the following:
$ boot2docker start
$ boot2docker ssh -L 8080:localhost:8080
$ docker ps -a # Look for the ID of the Samba container
$ docker start <samba_container_id>
And then reconnect to the shared directory in Finder. Use the "docker run" command above to re-run the HTTP server.
The Python experts might notice that virtualenv was not used anywhere. Virtualenv is a way to isolate Python environments when running multiple projects on the same machine. In the example above, Docker was used as the isolating container instead. There's no need to use virtualenv.
Now that you've followed this tutorial, you've seen how to run a simple App Engine app inside a Docker container for development purposes. You can edit the code and the changes will be reflected in the HTTP server immediately. Any real application will use one or more of App Engine's other services, so you might need to find alternatives for those. If you will be running your server on Google Cloud Platform, some services (e.g. Datastore) are also accessible from Compute Engine. To completely eliminate the dependency on App Engine, you could convert you code to use an open source alternative running in a separate Docker container. For example, the Google Datastore could be replaced with Cassandra. Task Queues could be replaced with RabbitMQ. Memcache by memcached.
Now that we have an easy way to run our app on Docker, part 2 will explore how to run it in a production environment. Check back soon!