Use Golang at Heroku and free SSL from CloudFlare.com

August 16th, 2016 1381 Words

It only takes a couple of minutes to deploy a go application to a free Heroku dyno, use a custom domain name and enable free SSL using CloudFlare. If you still have some seconds left - and you will - it’s dead simple to add Prometheus metrics as well.

Simple web server using go and gin

For easy HTTP handling the go application will make use of the gin web server framework. The following lines are everything needed to have an application respond to HTTP requests:

package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
)

func main() {
  r := gin.Default()

  r.GET("/", func(c *gin.Context) {
    c.String(http.StatusOK, "Welcome!")
  })

  r.Run("localhost:5000")
}

Just create a new folder, in this case mtrs.io and store the lines above into a file called main.go and you are done. After saving the file you are able to start the gin server with go run main.go and see a lovely welcome message when you open a web browser and point it to localhost:5000. Well, that was fast …

Configure prometheus client library

As said in the introduction the new application must of course collect metrics, for example the number of requests to the / ressource. Using the prometheus client library for go it’s pretty easy to create a new counter and configure the label path in order to handle future requests other than / :

var (
  requestCounter = prometheus.NewCounterVec(
    prometheus.CounterOpts{
      Namespace: "mtrs",
      Name:      "request",
      Help:      "Total number of requests",
    },
    []string{"path"},
  )
)

func init() {
  prometheus.MustRegister(requestCounter)
}

After the configuration and registration of the counter you need to configure the route handler to increase requestCounter every time the application responds to a requests:

r.GET("/", func(c *gin.Context) {
  c.String(http.StatusOK, "Done.")
  requestCounter.WithLabelValues("/").Inc()
})

Prometheus uses a pull-workflow, so the application needs an HTTP endpoint which can be crawled by a prometheus backend. This endpoint should be kind of secured and luckily gin already supports BasicAuth . With the simple syntax it’s easy create the credentials and secure the prometheus endpoint at /metrics .

r.Group("/metrics", gin.BasicAuth(gin.Accounts{
  "prometheus": "secret",
})).GET("/", func(c *gin.Context) {
  prometheus.Handler().ServeHTTP(c.Writer, c.Request)
})

If you now start the application again with go run main.go and point your browser to localhost:5000/metrics you will be asked for a username and password. The code above uses the user prometheus and the password secret . After sending a couple of requests to the configured / route you will see the counter value in the metrics:

# HELP mtrs_request Total number of requests
# TYPE mtrs_request counter
mtrs_request{path="/"} 4

To get started with running a prometheus backend, just follow the official installation guide using Docker. The required configuration is stored inside the prometheus.yml file.

scrape_configs:
  - job_name: "mtrs-development"
    scrape_interval: 5s
    basic_auth:
      username: prometheus
      password: secret
    static_configs:
      - targets: ["127.0.0.1:5000"]

As you can see the YAML configuration is again not that complex and it can easily be extended to support the production application beside the development environment as soon as the application is running on the final custom domain.

Deploy application to Heroku

The first steps are completed now and the go project with a gin web server is collecting metrics using the offical prometheus golang client library.

It’s time to commit the created files to a git repository and call the Godep command afterwars to install all needed librarys into the ventor folder.

$ > git add .
$ > git commit -m "Initial commit"
$ > godep save ./...
$ > git add .
$ > git commit -m "Add godeps"

With this default file pattern Heroku can detect the go application without extra configuration. Just create a Procfile with web: mtrs.io as the content and create a new Heroku application using the official command line tool:

$ > echo "web: mtrs.io" > Procfile
$ > heroku create
Creating app... done, warm-crag-52063
https://warm-crag-52063.herokuapp.com/ | https://git.heroku.com/warm-crag-52063.git
$ > heroku rename mtrs-io
$ > git push heroku master
$ > heroku open

After the deployment is done, you can open your browser and try to access mtrs-io.herokuapp.com but all you will see is the default Heroku application error. Where did the lovely greeting go?

When the main.go file was created the gin router had been configured to listen on localhost on port 5000 , this has to be changed in order to use the application on Heroku:

r.Run("localhost:5000")

Heroku will pass the required port number as an environment variable called PORT:

func main() {
  port := os.Getenv("PORT")

  if port == "" {
    log.Fatal("$PORT must be set")
  } else {
    log.Print("Using port ", port)
  }

  []

  r.Run(":" + port)
}

Add the changes to git, deploy them to Heroku and you should finally see the welcome message. Of course the prometheus metrics are available as well, secured with the same BasicAuth settings as before.

Use custom domain with Heroku

Heroku has a built in support for customs domains. Just use the official Heroku command line tool and add the custom domain to the project:

$ > heroku domains:add mtrs.io
Adding mtrs.io to ⬢ mtrs-io... done

To test access from a custom domain register for a free DNSimple.com trial account and configure your existing domain to use the DNSimple servers. Obtain a user access tokens from the DNSimple profile settings and you can use the API to create the domain records:

curl  -H 'Authorization: Bearer YOUR_SECRET_BEARER_TOKEN' \
      -H 'Accept: application/json' \
      -H 'Content-Type: application/json' \
      -X POST \
      -d '{"name": "mtrs.io"}' \
      https://api.dnsimple.com/v2/YOUR_ACCOUNT_ID/domains

After the domain has been created there needs to be an ALIAS record which points to the Heroku domain, in this case it’s mtrs-io.herokuapp.com:

curl  -H 'Authorization: Bearer YOUR_SECRET_BEARER_TOKEN' \
      -H 'Accept: application/json' \
      -H 'Content-Type: application/json' \
      -X POST \
      -d '{"name": "","type": "ALIAS","content": "mtrs-io.herokuapp.com","ttl":600}' \
      https://api.dnsimple.com/v2/YOUR_ACCOUNT_ID/zones/mtrs.io/records

Now the gin web server runs on the free Heroku dyno and can be access using the custom domain, use dig to verify the changes:

$ > dig mtrs.io

;; ANSWER SECTION:
mtrs.io.    600  IN  A  23.21.231.39

After everything is set up it’s time to add the production configuration to prometheus. Just configure a second job_name like the development environment and adjust the targets inside static_configs :

- job_name: "mtrs-production"
  scrape_interval: 5s
  basic_auth:
    username: prometheus
    password: secret
  static_configs:
    - targets: ["mtrs.io"]

Sadly SSL is quite expensive at Heroku, but you can create an account at CloudFlare.com and use the service with a free SSL certificate.

Use CloudFlare.com for free SSL

Add your domain and wait for CloudFlare to scan the domain settings. Delete all records CloudFlare has detected and create a new CNAME record for @ with the heroku URL mtrs-io.herokuapp.com as the value. Of course you want to make use of all the CloudFlare features, so disable DNS passthru and let CloudFlare handle all requests.

Now it’s time to configure the nameserver for the domain again. Sadly you have to say good bye to DNSimple.com again and change the nameserver to the configuration CloudFlare provides:

Current Nameservers     Change Nameservers to:
ns1.dnsimple.com       gene.ns.cloudflare.com
ns2.dnsimple.com       piotr.ns.cloudflare.com
ns3.dnsimple.com       Remove this nameserver
ns4.dnsimple.com       Remove this nameserver

As soon as the configuration has been deployed by the domain registrar CloudFlare should respond to requests at mtrs.io with some A records hosted somewhere in 104.*:

$ > dig mtrs.io

;; ANSWER SECTION:
mtrs.io.    300  IN  A  104.24.103.167
mtrs.io.    300  IN  A  104.24.102.167

When the configured TTL from DNSimple.com has passed a curl request to the domain responds with header information from CloudFlare and shows the greeting of the go application:

$ > curl -i mtrs.io
domain=.mtrs.io; HttpOnly
Via: 1.1 vegur
Server: cloudflare-nginx
CF-RAY: 2da2352cf6204106-HAM

Done.

That’s kind of the same setting you already achieved with DNSimple.com so let’s configure free SSL for the custom domain. First create a new rule in CloudFlare for all HTTP requests matching http://*mtrs.io* to always use HTTPS:

CloudFlare.com Rules - Redirect HTTP to HTTPS

Now every request to http://mtrs.io includes a 301 Moved Permanently header and a Location: https://mtrs.io information:

$ > curl http://mtrs.io -i
HTTP/1.1 301 Moved Permanently
Location: https://mtrs.io/

A request to https://mtrs.io may still fail, as CloudFlare needs some time to configure the SSL certificate for the domain:

curl https://mtrs.io
curl: (35) Unknown SSL protocol error in connection to mtrs.io:-9838

This usually takes about ten minutes to complete and is enabled per default. While waiting for the SSL certificate you could have a look at all the additional configuration CloudFlare.com offers and activate HTTP Strict Transport Security (HSTS) for example.