Deploy a Go Web App to the Swisscom Application Cloud
Dec 17, 2015golang
Inspired by the this blog post about the deployment of a node.js example app to the Swisscom Application Cloud, I decided to test the Swisscom Application Cloud my self with a similar app, written in Google Go language.
This blog should take you through the essential steps on a Linux based system to push your own Go based app to a Cloud Foundry based app cloud, for example the one from Swisscom.
Preparations
Go development environment
I assume you already have a prepared Go development environment. Otherwise I suggest you to start with the Getting Started manual.
To check, if you have the go tool chain ready, just enter
go env
this should print something like the below
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/user/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GO15VENDOREXPERIMENT="0"
CC="gcc"
GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread"
CXX="g++"
CGO_ENABLED="1"
If you are using Go in version 1.5.x, you may have set the environment variable GO15VENDOREXPERIMENT=1
. For this tutorial I suggest to switch the Go 1.5 vendor experiment off (GO15VENDOREXPERIMENT=0
), as this makes our deployment to the app cloud a little bit easier. (Additional information about the hitches with godep and the Go 1.5 vendor experiment could be found in this article on heroku.)
Godep
Cloud Foundry uses so called buildpacks to provide framework and runtime support for the application. We will later use the Go buildpack to deploy our web app to the app cloud.
In order to specify the external dependencies (external Go packages) for the Go web app, the above mentioned buildpack uses the tool Godep.
The installation of Godep is quite straight forward. Just let Go do the work for us:
go get github.com/tools/godep
This will place the godep
command in the directory $GOPATH/bin
. If $GOPATH/bin
is part of your $PATH
, godep is now directly available as command.
Cloud Foundry CLI
The Cloud Foundry Command Line Interface (CF CLI) is a command line tool for deploying and managing your applications within the app cloud. You can download the CF CLI from github.com/cloudfoundry/cli/releases.\
The CF CLI tool is written in Go as well and therefore consists of a single static binary. The installation is as simple as download and decompress the executable. For a 64 bit Linux systems, you could use the following commands to download the latest version (6.14.0 as of writing) and to place the executable to the current directory.
curl -L 'https://cli.run.pivotal.io/stable?release=linux64-binary&version=6.14.0&source=github-rel' | tar -xz > cf; chmod +x cf
To check if everything is working, execute ./cf help
. For best comfort, you should move the cf
binary to a directory within you $PATH
.
Alternatively there are installable packages for several OS available as well.
Create the Go “Hello world” Web App
Now we have everything in place to get started with writing the Go version of the “Hello world” web app.
First you should create a new directory named appcloud within $GOPATH/src
and enter it. If you are a github.com user, a good choice would be $GOPATH/src/github.com/<your github username>/appcloud
. Secondly create a new file named main.go with the following content:
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello world!")
}
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
log.Printf("Use port: %s\n", port)
http.HandleFunc("/", helloHandler)
err := http.ListenAndServe(":"+port, nil)
if err != nil {
log.Panicf("ListenAndServe error: %v\n", err)
}
}
This program is quite straight forward:
- We name the package “main”, because in Go the main package is the entry point for the application.
- We import some additional packages from the fantastic Go standard library, which will allow us to listen and answer to web requests.
- The function
helloHandler
will be executed for every request to the web app and will write the famous “Hello world” to thehttp.ResponseWriter
, which will be returned to the browser of the client. - The
main
function is where our program starts and where we do some setup:- First we collect the TCP Port to bind from the environment variable
PORT
, because this variable will be set in the app cloud the tell our web app on which TCP port we should bind. If no such environment variable is present, we default to port 3000. (Side note: Go expects the port to be a string, because values likehttp
as alias for port 80, are understood by Go as well) - For convenience we log (defaults to stderr) the TCP port, where the web app will bind.
- With
http.HandleFunc("/", helloHandler)
we add the functionhelloHandler
as handler function to our web server. Because this is the only handler function and we set the pattern to/
, every request to our web app will execute the helloHandler. - The whole “start a web server” magic happens in the line
err := http.ListenAndServe(":"+port, nil)
. With this line our program starts an endless loop to listen on the respective TCP port, on all available IP addresses, and to execute the defined handler functions (in our case there is just one).
- First we collect the TCP Port to bind from the environment variable
That’s it for the web app. Now let’s have a look, if every thing is working properly.
We can start our web app with the following command:
go run main.go
Just after starting you should see an output similar to the one below:
2015/12/17 10:58:38 Use port: 3000
If no other output is printed out, the program is ready to serve HTTP requests. So point your web browser to http://localhost:3000 and enjoy the “Hello world!” output.
Move into the Cloud
Before we can move our newly build Go based web app to the Swisscom App Cloud, we have to do some preparations:
- Setup a Swisscom Developer Account
- Specify our dependencies with Godep
- Create a Cloud Foundry manifest.yml
- Upload everything to the cloud
Setup a Swisscom Developer Account
I don’t go into all the details, because they are already outlined in this blog post. For the brave, just head to Swisscom Developer Account and follow the instructions (you will need a credit card in order to precede).
Specify our Dependencies with Godep
Even if we don’t use any external dependencies, because all used packages belong to the Go standard library, the file, describing the dependencies for our program has still to be present.
This file is auto generated with the command godep save -r
(assuming the godep command resides within the $PATH
). This will create a new directory named Godeps. Within this directory we find a file called Godeps.json with a content like to following:
{
"ImportPath": "github.com/breml/appcloud",
"GoVersion": "go1.5.2",
"Deps": []
}
If you have a properly setup $GOPATH
and your web app for the app cloud is placed within the $PATH/src/
, everything should be fine.
If this is not the case, for whatever reason you have to manually modify the just generated Godeps/Godeps.json
file in order to work with the Go buildpack. Most likely the line with ImportPath
reads as follows:
"ImportPath": ".",
In this case the “.” has to be replaced with the directory name, where your go web app resides. If you have followed the instructions above, your directory is named appcloud and you have to replace the above line with:
"ImportPath": "appcloud",
Create a Cloud Foundry manifest.yml
From the Cloud Foundry docs we learn about manifest.yml:
The application manifest tell the
cf push
command what to do with applications. This includes everything from how many instances to create and how much memory to allocate to what services applications should use.
For our small Go web app, we need to provide the following information within the manifest.yml
:
---
applications:
- name: DemoApp
memory: 64M
host: demoapp<YOURNUMBER>
buildpack: https://github.com/cloudfoundry/go-buildpack.git
command: appcloud
In detail:
- name: name of the app within the app cloud.
- memory: how much memory should be provided for the app. For our small Go example the minimum setting of 64M is by far enough.
- host: hostname of the app. This name combined with the assigned domain will be the URL for the app (E. g. demoapp.scapp.io). You should replace
<YOURNUMBER>
with a random number of yours. If you catch a hostname, which is already taken, you will be notified while pushing the app. - buildpack: for Go apps, the recommended buildpack is the mentioned above. This provides framework and runtime support for Go apps.
- command: Our Go source code, including the respective dependencies managed by Godep, will be compiled while pushing to the app cloud with the provided support by the buildpack. The result will be an executable. The command setting tells the app cloud which program to execute.
Upload Everything to the Cloud
Now with everything set, we may login at the Swisscom App Cloud with:
cf login -a https://api.lyra-836.appcloud.swisscom.com
and finally deploy our application:
cf push
If everything is successful, you may now access your “Hello world” app at http://demoapp<YOURNUMBER>.scapp.io/.
Annex
An extended version of the example, described in this blog post, may be found on github.com/breml/appcloud. Commit dcb5ce9d does correspond to this post.
In the original post you may find information about scaling your app and combining your app with services.
Updates
17.12.2015
Just found out, Go version 1.5 is not supported by the buildpack. Therefore we have to provide the Go version in Godeps/Godeps.json
including the minor version (1.5.1 or 1.5.2) even though godep in the latest version suggests to only specify the major version of Go.
Warning by godep:
godep: WARNING: Recorded go version (go1.5.2) with minor version string found.
godep: To record current major go version run `godep update -goversion`.