Sunday, 7 December 2014

Apps Dev

Welcome to Docker Hub!

Docker Hub is a centralized place to build and share Docker container images, collaborate with friends and colleagues, and automate pipelines.
With Docker Hub you can...

>>>

Deploying Go Apps with Docker to Elastic Beanstalk

In my last 'Building Go Web Apps' post, we built a simple 'cribs' application using Martini where 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:
  1. Update a container created from an image and commit the results to an image to customize it.
  2. 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:
  1. Dockerfile – to create a Docker image that contains your source bundle
  2. 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:
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.
FROM google/golang

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.
WORKDIR /gopath/src/github.com/topcoderinc/cribs

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:
EXPOSE 8080

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"]

We didn’t touch on all of the Dockerfile commands, so I encourage you to check out the Dockerfile Reference for more info.

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             PORTS                     NAMES
7b12355a9ae8        cribs:latest        /gopath/bin/cribs   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

That does it for Dockerizing our cribs application. Now we just need to push it to Amazon.

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.
Enter the Application Name and hit Next. For the Environment tier select 'Web Server' and for Predefined configuration select 'Docker'. Hit Next.
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 Next.
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
 
 
 
>>>>>
 

Tutorial – Building Go Web Apps

topcoder-cribsFollowing up on my last post, Ready… Set… Golang!, we're going to go over some resources for learning Go and build a small web app and host it on Heroku.
I've spent the last couple of days digging into Go and getting the feel for it and so far I really like it. It's different than most other languages in a number of ways. It's not about object-oriented programming or functional programming. It about getting stuff done. Build times are almost negligible and the code runs super fast. It's concurrency model is very powerful and it’s standard library provides almost everything you need out of the box. For a good overview of what Go is, how it was devised and why it is so cool, check out this blog post.

Learning Golang

Here are some links that I found useful. For installation, the Getting Start docs should work for you but you may want to take a peek at this short screencast.
I started off with the interactive Tour of Go. The tour is divided into three sections: basic concepts, methods and interfaces, and concurrency. Throughout the tour you will find a series of exercises for you to complete. Click the Run button for each section to compile and run the program on a remote server. No software installation needed to get start playing. If you are feeling adventurous, you can head over to the Go Playground and run your own code. Great for testing snippet you find or code you want to experiment with.
You definitely want to check out the Go docs and language specs next.
I found How to Write Go Code a god start as it demonstrates the development of a simple Go package and introduces the go tool, the standard way to fetch, build, test and install Go packages and commands. However, if you prefer, there is a video version instead.
A must read for any new Go programmer is Effective Go. The document gives tips for writing clear, idiomatic Go code.

Books

If you prefer books (or PDFs of books), I found the following to be really helpful:
Learning Go – a free PDF for learning the Go language. You can build the code yourself or download the PDF
Go Bootcamp (by Matt Aimonetti) – The PDF is available here.

Resource Sites

I think my favorite site is Learn X in Y minutes, Where X=Go. The site has one long Go file with a ton of effective commenting that teaches concepts along the way. I really love this site.
Go by Example is a hands-on introduction to Go using annotated example programs.
If you are fan of Railscasts, there a Gophercasts with a couple of good videos, especially for Postgres and Martini.
And finally Go (Golang) Pointers in 5 Minutes covers of course pointers in Go. Not surprising.

Go Help

If you need a little help now and then, there is of course Stackoverflow and the go-nuts IRC channel, freenode.net#go-nuts. If you are keen on Slack, Gohper Academy just announced a new Slack community you can join.

Building Topcoder 'Cribs'

I learn best by doing so I looked around for something to build. I wanted something a little more than a hello world but definitely not production quality. One thing we want to do with topcoder is allow members to post pictures and video of where they work, i.e., their cribs.
So the first thing I did was look around for a web framework for Go. There's a great reddit thread with a ton of info. The Square Engineering blog also has an in-depth analysis as well with their winner. I finally decided to use Martini for a couple of reasons: 1) it smells a lot like Express and Sinatra so it was easy for me to grok, has a huge community and seems to be growing by leaps and bounds.
So here's my 'Cribs' application along with the complete code. Let's walk through some of the code.
Server.go is where all of the action happens. It’s pretty small but straight forward and well documented so you can see what’s going on.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
package main
 
import (
"os"
"github.com/codegangsta/martini"
"github.com/codegangsta/martini-contrib/render"
"github.com/codegangsta/martini-contrib/binding"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
)
 
// the Crib struct that we can serialize and deserialize into Mongodb
type Crib struct {
Handle string `form:"handle"`
URL string `form:"url"`
Type string `form:"type"`
Description string `form:"description"`
}
 
/*
the function returns a martini.Handler which is called on each request. We simply clone
the session for each request and close it when the request is complete. The call to c.Map
maps an instance of *mgo.Database to the request context. Then *mgo.Database
is injected into each handler function.
*/
func DB() martini.Handler {
session, err := mgo.Dial(os.Getenv("MONGO_URL")) // mongodb://localhost
if err != nil {
panic(err)
}
 
return func(c martini.Context) {
s := session.Clone()
c.Map(s.DB(os.Getenv("MONGO_DB"))) // local
defer s.Close()
c.Next()
}
}
 
// function to return an array of all Cribs from mondodb
func All(db *mgo.Database) []Crib {
var cribs []Crib
db.C("cribs").Find(nil).All(&cribs)
return cribs
}
 
// function to return a specific Crib by handle
func Fetch(db *mgo.Database, handle string) Crib {
var crib Crib
db.C("cribs").Find(bson.M{"handle": handle}).One(&crib)
return crib
}
 
func main() {
 
m := martini.Classic()
// specify the layout to use when rendering HTML
m.Use(render.Renderer(render.Options {
Layout: "layout",
}))
// use the Mongo middleware
m.Use(DB())
 
// list of all cribs
m.Get("/", func(r render.Render, db *mgo.Database) {
r.HTML(200, "list", All(db))
})
 
/*
create a new crib the form submission. Contains some martini magic. The call
to binding.Form(Crib{}) parses out form data when the request comes in.
It binds the data to the struct, maps it to the request context and
injects into our next handler function to insert into Mongodb.
*/
m.Post("/", binding.Form(Crib{}), func(crib Crib, r render.Render, db *mgo.Database) {
db.C("cribs").Insert(crib)
r.HTML(200, "list", All(db))
})
 
// display the crib for a specific user
m.Get("/:handle", func(params martini.Params, r render.Render, db *mgo.Database) {
r.HTML(200, "display", Fetch(db, params["handle"]))
})
 
m.Run()
 
}
view raw server.go hosted with ❤ by GitHub
Here is the layout template the all of the views use. The HTML for each view is injected into the layout.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Topcoder Cribs</title>
<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.5.0/pure-min.css">
</head>
 
<body style="margin: 20px;">
<h1>Topcoder Cribs!</h1>
{{ yield }}
</body>
</html>
view raw layout.tmpl hosted with ❤ by GitHub
The home page iterates over the array of all returned Cribs from mongodb and links them to the display page. It also contains a form to post new Cribs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
<h2>Members' Cribs</h2>
<ul>
{{range .}}
<li><a href="/{{.Handle}}">{{.Handle}}</a></li>
{{ end }}
</ul>
 
<form class="pure-form pure-form-stacked" style="padding-top:25px" action="/" method="POST">
<fieldset>
<legend>Add Your Crib!</legend>
 
<label for="handle">Handle</label>
<input id="handle" name="handle" type="text" placeholder="Your handle">
 
<label for="url">URL</label>
<input id="url" name="url" type="text" placeholder="Complete URL for images or just the ID for videos" style="width: 400px">
 
 
<label for="type">Type</label>
<select id="type" name="type">
<option>Image</option>
<option>Youtube</option>
<option>Vimeo</option>
</select>
 
<label for="description">Description</label>
<textarea id="description" name="description" rows="5" cols="50"></textarea>
 
<button type="submit" class="pure-button pure-button-primary">Submit</button>
</fieldset>
</form>
view raw list.tmpl hosted with ❤ by GitHub
Any finally the display page shows an image, youtube video or vimeo video based upon the type of Crib.
1 2 3 4 5 6 7 8 9 10 11
<h2>{{.Handle}}'s Crib</h2>
 
{{ if eq .Type "Youtube" }}
<iframe width="560" height="315" src="//www.youtube.com/embed/{{ .URL }}" frameborder="0" allowfullscreen></iframe>
{{ else if eq .Type "Vimeo" }}
<iframe src="//player.vimeo.com/video/{{ .URL }}" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
{{ else }}
<img src="{{ .URL }}">
{{end}}
 
<p>{{ .Description }}</p>
view raw display.tmpl hosted with ❤ by GitHub

Deploying to Heorku

If you are deploying to Heroku you’ll need to add the following files. The Heroku buildpack needs to know where to put your code in the image. Add the .godir file in your project root with your directory structure:
1
github.com/topcoderinc/cribs
view raw .godir hosted with ❤ by GitHub
You’ll also need a Procfile in your project root so heroku knows what type of dyno to spin up:
1
web: cribs -port=$PORT
view raw Procfile hosted with ❤ by GitHub
Finally, when creating your application add the buildpack flag and don’t forget to add the Mongodb and environment variables:
1 2 3 4 5 6 7
// create
heroku create -b https://github.com/kr/heroku-buildpack-go.git my-app
 
// add the mongolabs addon
heroku addons:add mongolab
 
// add the envrironment variables for mongolab. see environment.sh
view raw heroku-create hosted with ❤ by GitHub

Wrapup

So now I have my first Go application running and am fairly happy with it. However, after building the app, reading this rebuttal of Martini and talking with a couple of Appirians that use Go, I'm thinking of scrapping Martini and using simply the standard Go library. Here's an interesting blog post that I have been looking at regarding this.
In the next blog post, I plan on deploying my app to AWS with Docker.
 

No comments:

Post a Comment