In my last 'Go Tutorial with MongoDB on Heorku' post, we built a simple 'cribs' application using Martini where topcoder members can showcase where they work. If you are just jumping in, you can run the application on Heroku and find the code on our github repo.
Today we are going to take that same application, Dockerize it and deploy it to AWS Elastic Beanstalk. "What about Google App Engine", you ask? Unfortunately, I believe that App Engine only supports Docker in closed "preview" mode at this time so maybe somewhere down the road we can take look at it.
Getting Started with Docker
Docker's documentation is top notch and they even have a 10 minute "try docker" online tutorial that's completely browser based. Once you are ready to get started you can install docker on your machine and fire it up. I'm not going to go over the basics of Docker, their User Guide does a great job of getting you up and running.
The first thing we need to do is create our image. We can either:
- Update a container created from an image and commit the results to an image to customize it.
- Use a Dockerfile to specify instructions to create an image.
We are going to do the later as it seems much simpler to build an image and easier to share them.
Here is our main Go file that we will be Dockerizing. The only difference from our Heroku version is that we've specified port 8080 for Amazon. We'll need to add two files to our application:
- Dockerfile - to create a Docker image that contains your source bundle
- Dockerrun.aws.json - to deploy your application to AWS. (Note, I think if you specify your port in the Dockerfile that you don't need the Dockerrun.aws.json file.)
Since the Dockerfile is the heart of the Docker process, let take an in-depth look at it:
FROM google/golang WORKDIR /gopath/src/github.com/topcoderinc/cribs ADD . /gopath/src/github.com/topcoderinc/cribs/ # go get all of the dependencies RUN go get github.com/codegangsta/martini RUN go get github.com/codegangsta/martini-contrib/render RUN go get github.com/codegangsta/martini-contrib/binding RUN go get labix.org/v2/mgo RUN go get labix.org/v2/mgo/bson RUN go get github.com/topcoderinc/cribs # set env variables to mongo ENV MONGO_DB YOUR-MONGO-DB ENV MONGO_URL YOUR-MONGO-URL EXPOSE 8080 CMD  ENTRYPOINT ["/gopath/bin/cribs"]
The first line tells Docker what to use for our source image. In this case Google was kind enough to bundle the latest version of golang installed from golang.org into a base image for us so we'll gladly use it.
Next we'll set the WORKDIR, which sets the working directory for any RUN, CMD and ENTRYPOINT instructions. We are going to set it to the root of our source files.
We'll next use the ADD instruction to copy our source code to the container's filesystem for our source directory.
ADD . /gopath/src/github.com/topcoderinc/cribs/
There are a number of dependencies that we need for Martini so we'll have
go get them and add them to our image:
RUN go get github.com/codegangsta/martini RUN go get github.com/codegangsta/martini-contrib/render RUN go get github.com/codegangsta/martini-contrib/binding RUN go get labix.org/v2/mgo RUN go get labix.org/v2/mgo/bson
Finally, we'll get our source code and install it.
RUN go get github.com/topcoderinc/cribs
Since our application still uses the MongoDB sitting on Heroku, we'll use the ENV instruction to set our environment variables we'll need to connect to MongoDB. We would have been better off if we would have created a MongoDB container and linked to it but we'll fight that battle another day.
Make sure you change these values before building your image.
ENV MONGO_DB YOUR-MONGO-DB ENV MONGO_URL YOUR-MONGO-URL
Another major difference with Elastic Beanstalk, is that we declare the port we are using for our application:
Again, according to the docs you only need to specify the port in the Dockerfile or dockerrun.aws.json.
We don't need to provide defaults for executing the container so we leave our CMD empty and just set the executable for the container to run, which is our cribs application.
CMD  ENTRYPOINT ["/gopath/bin/cribs"]
Building & Running our Container
Now that our Dockerfile is all setup, let's build our container and run it locally before deploying it to Elastic Beanstalk. FYI, here's a great Docker cheatsheet that I found.
Open Terminal, and start boot2docker, the Linux distribution made specifically to run Docker containers:
$ boot2docker init # if you haven't downloaded latest image $ boot2docker start
Next, change to the directory with your Dockerfile and build the image:
$ cd ~/go/github.com/topcoderinc/cribs # my directory $ docker build -t cribs . # You'll see a bunch of images being downloaded and built, then finally... Successfully built 2fd0b5a7bb4d
Now you can list your Docker images and see that your cribs image exists:
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE cribs latest 2fd0b5a7bb4d 39 seconds ago 570.6 MB google/golang latest fa77fdfe2188 2 weeks ago 556.9 MB
Start the container in the background for the cribs image with port 49160 mapped to 8080:
$ docker run -p 49160:8080 -d cribs 7b12355a9ae83700da09dd26060df751739a2497d7a75f1beca8e085d7768c58
You can view the details of the container with the following. Take note of the container id and name of the running container as you'll need them later.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7b12355a9ae8 cribs:latest /gopath/bin/cribs 18 hours ago Up 4 seconds 0.0.0.0:49160->8080/tcp stupefied_archimedes
If you run a container with an exposed port, then you should be able to access that server using the IP address reported to you using the following. Typically, it is 192.168.59.103, but it can change as it's dynamically allocated by the VirtualBox DHCP server.
$ boot2docker ip The VM's Host only interface IP address is: 192.168.59.103
Now you can open up the browser with the following URL and our app should be running:
If you are curious about what the container is actually doing, you can view the logs with either the container id or name:
$ docker logs stupefied_archimedes [martini] Started GET / for 192.168.59.3:61354 [martini] Completed 200 OK in 259.43934ms
Once we are done running our container we need to of course shut it down using either the container id or name:
$ docker stop stupefied_archimedes stupefied_archimedes
Deploying to Elastic Beanstalk
Deploying our app to Elastic Beanstalk isn't as fast nor as easy as Heroku but it's relatively painless. Once logged into Elastic Beanstalk, click
Create New Application in the upper right to get started.
Application Name and hit
Next. For the
Environment tier select 'Web Server' and for
Predefined configuration select 'Docker'. Hit
Now we need upload our source code. Zip up the Dockerfile, Dockerrun.aws.json, server.go and the /templates directory into
app.zip. Now choose the middle radio button, upload the zip file and hit
Just accept the defaults and hit
Next for the next four pages. When you are finally done, scroll down to the bottom of the Review page and click
Launch. Now wait 10 minutes or so for the little wheel to stop spinning and your environment and application should be configured and deployed successfully!
You can run the cribs application on Elastic Beanstalk at http://cribs-env.elasticbeanstalk.com