Deployment
Heroku makes deploying applications easy. It is a perfect platform for small to medium size web applications that are willing to sacrifice a little bit of flexibility in infrastructure to gain a fairly pain-free environment for deploying and maintaining web applications.
I am choosing to deploy our web application to Heroku for the sake of this tutorial because in my experience it has been the fastest way to get a web application up and running in no time. Remember that the focus of this tutorial is how to build web applications in Go and not getting caught up in all of the distraction of provisioning, configuring, deploying, and maintaining the machines that our Go code will be run on.
Getting setup
If you don't already have a Heroku account, sign up at id.heroku.com/signup. It's quick, easy and free.
Application management and configuration is done through the Heroku toolbelt, which is a free command line tool maintained by Heroku. We will be using it to create our application on Heroku. You can get it from toolbelt.heroku.com.
Changing the Code
To make sure the application from our last chapter will work on Heroku, we will
need to make a few changes. Heroku gives us a PORT
environment variable
and expects our web application to bind to it. Let's start by importing the
"os" package so we can grab that PORT
environment variable:
import (
"net/http"
"os"
"github.com/russross/blackfriday"
)
Next, we need to grab the PORT
environment variable, check if it is set, and
if it is we should bind to that instead of our hardcoded port (8080).
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
Lastly, we want to bind to that port in our http.ListenAndServe
call:
http.ListenAndServe(":"+port, nil)
The final code should look like this:
package main
import (
"net/http"
"os"
"github.com/russross/blackfriday"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/markdown", GenerateMarkdown)
http.Handle("/", http.FileServer(http.Dir("public")))
http.ListenAndServe(":"+port, nil)
}
func GenerateMarkdown(rw http.ResponseWriter, r *http.Request) {
markdown := blackfriday.MarkdownCommon([]byte(r.FormValue("body")))
rw.Write(markdown)
}
Configuration
We need a couple small configuration files to tell Heroku how it should run our
application. The first one is the Procfile
, which allows us to define which
processes should be run for our application. By default, Go will name the
executable after the containing directory of your main package. For instance,
if my web application lived in GOPATH/github.com/codegangsta/bwag/deployment
, my
Procfile
will look like this:
web: deployment
Dependencies
Our example application uses github.com/russross/blackfriday
a package that isn't
present in Go's standard library. To ensure that this package is available and the
correct version at compile time the standard Go practice is to "vendor" (aka
copy) the version of github.com/russross/blackfriday
that we are using locally
into our code base.
We'll use the tool godep
to record this dependency and write it into our application's
vendor
folder.
$ go get -u github.com/tools/godep # Fetch the tool
$ godep save ./...
Starting with go1.6 the vendor/
directory is the standard location used by Go for
vendored code. This is where godep made a copy of github.com/russross/blackfriday
and any of it's dependencies.
godep also created a Godeps/Godeps.json
file to record what it found.
Both the vendor/
and Godeps/
directories should be committed to git. Heroku uses
the information recorded in those locations to properly compile Go applications.
Deployment
Once all these things in place, Heroku makes it easy to deploy.
Initialize the project as a Git repository:
git init
git add -A
git commit -m "Initial Commit"
Create your Heroku application:
heroku create
Push it to Heroku and watch your application be deployed!
git push heroku master
View your application in your browser:
heroku open
NB: If godep
is not in your $PATH
, you may see the following error
bash: command not found: godep
In order to fix this, you must add godep
to your $PATH
by using the
following command to add godep
to your $PATH on shell initialization:
echo "export PATH=$PATH:$GOPATH/bin" >> ~/.bash_profile
(if you are using zsh
, replace ~/.bash_profile
with ~/.zshrc
in the
preceding command):