1 Deploying Go Web Apps to Heroku

There are plenty of definitions for “cloud computing” online, and for the most part, they generally point to the same thing: taking applications and running them on infrastructure other than your own. Companies or individuals who offload or effectively “outsource” their hardware and/or applications are running those apps “in the cloud.”

1.1 Cloud Computing Service Levels

In the figure below, you can see how the analyst firm Gartner segregates cloud computing into three distinct classes of service.

Cloud Computing Service Levels

Cloud Computing Service Levels

1.1.1 SaaS

Let’s start at the highest level: software applications that are only available online fall into the “Software-as-a-Service” category, also known as “SaaS”. The simplest example to understand is e-mail. For personal e-mail, people typically select from a variety of free web-based e-mail servers such as Google’s Gmail, Yahoo!Mail, or Microsoft’s Hotmail, rather than setting up all of the above through their provider. Not only is it “free” (supported through advertising), but users are freed from any additional server maintenance. Because these applications run (and store their data online), users no longer need to worry about managing, saving, and backing up their files. Of course, now it becomes Google’s responsibility to ensure that your data is safe and secure. Other examples of SaaS include Salesforce, IBM’s NetSuite, and online games.

1.1.2 IaaS

On the opposite end of the spectrum, we have “Infrastructure-as-a-Service,” or “IaaS,” where you outsource the hardware. In such cases, it’s not just the computing power that you rent; it also includes power, cooling, and networking. Furthermore, it’s more than likely that you’ll need storage as well. Generally IaaS is this combination of compute and cloud storage.

When you choose to run your applications at this cloud service level, you’re responsible for everything on the stack that is required to operate above it. By this, we mean necessities such as the operating system followed by additional (yet optional services) like database servers, web servers, load-balancing, monitoring, reporting, logging, middleware, etc. Furthermore, you’re responsible for all hardware and software upgrades, patches, security fixes, and licensing, any of which can affect your application’s software stack in a major way.

1.1.3 PaaS

In the middle, we have “Platform-as-a-Service,” or “PaaS.” At this service level, the vendor takes care of the underlying infrastructure for you, giving you only a platform with which to (build and) host your application(s). Gone are the hardware concerns of IaaS, yet with PaaS, you control the application it’s your code unlike as the SaaS level where you’re dependent on the cloud software vendor. The only thing you have to worry about is your application itself.

Systems like Google App Engine, Salesforce’s Heroku and force.com, Microsoft Azure, and VMwares Cloud Foundry, all fall under the PaaS umbrella.

A number of Platform-as-a-Service (PaaS) providers allow you to use Go applications on their clouds.

Heroku is a new approach to deploying web applications. Forget about servers; the fundamental unit is the app. Develop locally on your machine just like you always do. When you’re ready to deploy, use the Heroku client gem to create your application in their cloud, then deploy with a single git push. Heroku has full support for Go applications.

We shall soon see how we can deploy an app to Heroku.

1.2 Create an account on Heroku

Please ensure that you are connected to the internet and then create an account on Heroku (obviously do this only once). If you don’t have one, then signup. It’s free and instant. A free account can have upto 5 apps without registering your credit card.

1.3 Install the Heroku Toolbelt

The Heroku Toolbelt ensures that you have access to the Heroku command-line client, Foreman which is used to run applications locally, and the Git revision control system that is used to deploy sites to Heroku.

Once installed, you’ll have access to the heroku command from your command window. Log in using the email address and password you used when creating your Heroku account:

1 $ cd $GOPATH/src/github.com/SatishTalim/webapp
2 $ heroku login
3 Enter your Heroku credentials.
4 Email: satish@rubylearning.org
5 Password:
6 Could not find an existing public key.
7 Would you like to generate one? [Yn]
8 Generating new SSH public key.
9 Uploading ssh public key /Users/satish/.ssh/id_rsa.pub

1.4 Create a Procfile

Create a Procfile to tell Heroku what command to run for the web process in our app. The Procfile should be created in the folder $GOPATH/src/github.com/SatishTalim/webapp and contains:

1 web: webapp

1.5 Use Git

In order to deploy to Heroku we’ll need the app stored in Git. In the same folder i.e. $GOPATH/src/github.com/SatishTalim/webapp type:

1 $ git init
2 $ git add -A .
3 $ git commit -m "code"

1.6 Install Godep

The recommended way to manage Go package dependencies on Heroku is with Godep, which helps build applications reproducibly by fixing their dependencies.

Before installing Godep ensure that you have Mercurial installed (check that you have an hg command) and have set the path environment variable for mercurial for eg. c:\Mercurial.

Now let us install Godep:

1 $ go get github.com/kr/godep

Now save your dependencies:

1 $ godep save

This will save a list of dependencies to the file Godeps/Godeps.json, and copy their source code into Godeps/_workspace.

Add these new files to git:

1 $ git add -A .
2 $ git commit -m "dependencies"

Now we’re ready to ship this to Heroku.

1.7 Heroku deploy

Create a new Heroku app, telling it to use the Go Heroku Buildpack to build your Go code:

1 $ heroku create -b https://github.com/kr/heroku-buildpack-go.git
2 Creating tranquil-bastion-1774... done, stack is cedar
3 BUILDPACK_URL=https://github.com/kr/heroku-buildpack-go.git
4 http://tranquil-bastion-1774.herokuapp.com/ | git@heroku.com:tranquil-bastion-1774.git
5 Git remote heroku added

Push the code to Heroku:

 1 $ git push heroku master
 2 Initializing repository, done.
 3 Counting objects: 11, done.
 4 Delta compression using up to 4 threads.
 5 Compressing objects: 100% (8/8), done.
 6 Writing objects: 100% (11/11), 1.29 KiB | 0 bytes/s, done.
 7 Total 11 (delta 0), reused 0 (delta 0)
 8 
 9 -----> Fetching custom git buildpack... done
10 -----> Go app detected
11 -----> Installing go1.2... done
12 -----> Running: godep go install -tags heroku ./...
13 -----> Discovering process types
14        Procfile declares types -> web
15 
16 -----> Compressing... done, 1.7MB
17 -----> Launching... done, v4
18        http://tranquil-bastion-1774.herokuapp.com deployed to Heroku
19 
20 To git@heroku.com:tranquil-bastion-1774.git
21  * [new branch]      master -> master

Your app should be up and running. Visit it with:

1 $ heroku open
2 Opening tranquil-bastion-1774... done

That’s it - you now have a running Go app on Heroku!

Exercises

Deploy the apps webtime.go, dosasite.go, ipweb.go, stringupper.go, geoweb.go and trails.go that you had written previously to Heroku.

1.8 Program gomongohq.go

The program gomongohq.go located on Heroku, connects to our database godata hosted on MongoHQ and fetches information (the email id of say user Stefan Klaste) from the collection user.

Program gomongohq.go


  1 package main
  2 
  3 import (
  4 	"fmt"
  5 	"html/template"
  6         "labix.org/v2/mgo"
  7         "labix.org/v2/mgo/bson"
  8         "log"
  9 	"net/http"
 10 	"os"
 11 )
 12 
 13 type Person struct {
 14         Name string
 15         Email string
 16 }
 17 
 18 func main() {
 19 	http.HandleFunc("/", root)
 20         http.HandleFunc("/display", display)
 21         fmt.Println("listening...")
 22         err := http.ListenAndServe(GetPort(), nil)
 23         if err != nil {
 24                 log.Fatal("ListenAndServe: ", err)
 25         }
 26 }
 27 
 28 func root(w http.ResponseWriter, r *http.Request) {
 29 	fmt.Fprint(w, rootForm)
 30 }
 31 
 32 const rootForm = `
 33   <!DOCTYPE html>
 34     <html>
 35       <head>
 36         <meta charset="utf-8">
 37         <title>Your details</title>
 38         <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css">
 39       </head>
 40       <body style="margin: 20px;">
 41         <h2>A Fun Go App on Heroku to access MongoDB on MongoHQ</h2>
 42         <p>This simple app will fetch the email id of a person, if it's already there in t\
 43 he MongoDB database.</p>
 44         <p>Please enter a name (example: Stefan Klaste)</p>
 45         <form action="/display" method="post" accept-charset="utf-8" class="pure-form">
 46           <input type="text" name="name" placeholder="name" />
 47           <input type="submit" value=".. and query database!" class="pure-button pure-butt\
 48 on-primary"/>
 49 	</form>
 50         <div>
 51           <p><b>&copy; 2014 RubyLearning. All rights reserved.</b></p>
 52         </div>	
 53       </body>
 54     </html>
 55 `
 56 
 57 var displayTemplate = template.Must(template.New("display").Parse(displayTemplateHTML))
 58 
 59 func display(w http.ResponseWriter, r *http.Request) {
 60         // In the open command window set the following for Heroku:
 61         // heroku config:set MONGOHQ_URL=mongodb://IndianGuru:password@troup.mongohq.com:1\
 62 0080/godata
 63         uri := os.Getenv("MONGOHQ_URL")
 64         if uri == "" {
 65                 fmt.Println("no connection string provided")
 66                 os.Exit(1)
 67         }
 68  
 69         sess, err := mgo.Dial(uri)
 70         if err != nil {
 71                 fmt.Printf("Can't connect to mongo, go error %v\n", err)
 72                 os.Exit(1)
 73         }
 74         defer sess.Close()
 75         
 76         sess.SetSafe(&mgo.Safe{})
 77         
 78         collection := sess.DB("godata").C("user")
 79 
 80         result := Person{}
 81 
 82         collection.Find(bson.M{"name": r.FormValue("name")}).One(&result)
 83 
 84         if result.Email != "" {
 85                 errn := displayTemplate.Execute(w, "The email id you wanted is: " + result\
 86 .Email)
 87                 if errn != nil {
 88                         http.Error(w, errn.Error(), http.StatusInternalServerError)
 89                 } 
 90         } else {
 91                 displayTemplate.Execute(w, "Sorry... The email id you wanted does not exis\
 92 t.")
 93         }
 94 }
 95 
 96 const displayTemplateHTML = ` 
 97 <!DOCTYPE html>
 98   <html>
 99     <head>
100       <meta charset="utf-8">
101       <title>Results</title>
102       <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css">
103     </head>
104     <body>
105       <h2>A Fun Go App on Heroku to access MongoDB on MongoHQ</h2>
106       <p><b>{{html .}}</b></p>
107       <p><a href="/">Start again!</a></p>
108       <div>
109         <p><b>&copy; 2014 RubyLearning. All rights reserved.</b></p>
110       </div>
111     </body>
112   </html>
113 `
114 
115 // Get the Port from the environment so we can run on Heroku
116 func GetPort() string {
117         var port = os.Getenv("PORT")
118 	// Set a default port if there is nothing in the environment
119 	if port == "" {
120 		port = "4747"
121 		fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
122 	}
123 	return ":" + port
124 }

The program by now should be self explanatory.

  • The rootForm uses Pure a set of small, responsive CSS modules that you can use in every web project.
  • The function display uses the html/template package and the mgo driver to access the database on MongoHQ. If the name is found in the database the function throws a page to the user with the email id for that name.

You can visit this app here.

Exercise

Write a simple Go program (getcapital.go) and host it on Heroku. This program when accessed shows a simple form to the user, where he/she enters a country name. The Go app uses this information to access a MongoDB database on MongoHQ and fetches the capital of that country. It then either displays the name of the capital or an error message to the user.

Exercise

The Gopher Academy Blog has an excellent article titled “Build a Christmas List with Martini”. As an exercise build this app on Heroku and which accesses the MongoDB database on MongoHQ.