Go Gopher wearing a Platform.sh space helmet
Blog

Go[lang] forth and deploy

Larry Garfield
Larry Garfield

Platform.sh aims to support your applications, whatever they may be. We've supported PHP, Python, Ruby, and Node.js for quite some time now. Golang support has also been available as a beta for over a year.

Why just beta all this time? It worked and ran fine, but there was a catch: The bane of every Go developer's existence, the GOPATH. Making the GOPATH design play nicely with a build pipeline that focused on your code, rather than on code in some fixed parent directory, wasn't easy. It was possible with some intricate Makefile dancing but it was never something we really felt comfortable calling fully supported.

Fortunately, the new release of Go 1.11 has solved that problem for us. Amongst its other improvements are support for Go modules. The link there has far more detail for Go developers, but in short Go dependencies can now be managed in almost the same way as in other dependecy-managed languages like PHP (Composer), Ruby (Bundler), or Node.js (npm). No GOPATH required!

Specifically:

  • Dependencies can be specified in a separate file (go.mod), and don't have to be checked into Git.
  • Dependencies can be downloaded on the fly during build, reliably and repeatably.
  • Everything is local-directory relative. No more GOPATH!
  • Compiling Go is still embarrassingly fast.

I'm sold, how do I use it?

It's surprisingly easy now.

First, include a go.mod file in your project. You can create one with go mod init and then modify it yourself or have Go populate it for you based on your source code.

When you run go build locally, it will generate a go.sum file that is a hash of the code you've downloaded for later validation checks. Add both the go.mod file and go.sum file to Git.

Now you can setup your .platform.app.yaml file like so:

name: app
type: golang:1.11

hooks:
    build: |
        go build -o bin/app

disk: 1024

web:
    upstream:
        socket_family: tcp
        protocol: http
    commands:
        start: ./bin/app

    locations:
        /:
            allow: false
            passthru: true

That configuration says:

  • Use the Go 1.11 container image.
  • Use go build as normal in the build step; because the Go modules files are in Git it will detect them and use those to locate and download dependencies, then stick the resulting binary at bin/app. (Feel free to change that output location if you want.)
  • At runtime, start the Go application we build and forward all requests of any kind to it.

If the Go app stops for any reason, Platform.sh will automatically restart it.

That's it that's all, you now have a working Go app on Platform.sh.

But wait, how do I wire Go to Platform.sh?

You do need to tell your HTTP server in Go what port to listen on. Fortunately, that's easy! We have a Go helper library available (which is now super easy to install with Go modules) that gives you a clean struct to access all of the Platform.sh-specific environment information you need, including the port. If you're using the net/http web server from the standard library, it's as simple as:

package main

import (
	"net/http"

	psh "github.com/platformsh/gohelper"
)

func main() {
	p, err := psh.NewPlatformInfo()
	if err != nil {
		panic("Not in a Platform.sh Environment.")
	}

	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		// ...
	})

	// Note the Port value used here.
	http.ListenAndServe(":"+p.Port, nil)
}

And now your Go server will accept requests forwarded from Platform.sh. Beyond that, the sky's the limit.

Welcome, Gophers, to the joys of safely deploying on Friday!