The Most Production Ready Hello World Go Server Ever*

*Probably not production ready, but still pretty good.

Dara Hayes

dara.hayes@redhat.com

@darahayess

@darahayes

I think Node is not the best system to build a massive web server. I would use Go for that. And honestly, that’s the reason why I left Node. It was the realization that: oh, actually, this is not the best server-side system ever.

Ryan Dahl - Creator of Node.js

package main

import (
	"io"
	"net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Hello world!")
}

func main() {
	http.HandleFunc("/", hello)
	http.ListenAndServe(":8000", nil)
}

Hello World!

So Many Questions...

What about?

  • Dependency Management
  • Popular Frameworks/Libraries?
  • Project Structure
  • Architecture/Design Patterns
  • App Configuration
  • Build Steps
  • Testing and Code Coverage
  • Docker
  • Continuous Integration
  • Release Process

Let's build it!

HTTP Frameworks/Libraries

So many frameworks

HTTP Frameworks/Libraries

The built in net/http library is really good

But routing is hard

Gorilla Mux is our friend

net/http + gorilla/mux = $$$

Dependency Management

also known as 'vendoring'

Dependency Management (vendoring)

dep is the official experiment, but not yet the official tool

Deps are defined in Gopkg.toml and Gopkg.lock

$ dep init

$ dep ensure -add github.com/user/library

Project Structure

.
├── Gopkg.lock
├── Gopkg.toml
├── README.md
├── cmd               # Entry Points or executables in here
│   └── my-app
│       └── my-app.go
├── pkg               # App modules/business logic stuff in here
│   ├── business
│   ├── config
│   ├── db
│   └── web
└── vendor            # Dependencies are in here

Architecture/Design Patterns

Dependency Injection & Interfaces

HTTP Handlers

Business Logic

Database

Dependency Injection & Interfaces

type HelloWorldable interface {
	Hello(string) (string, error)
}

HTTP handler(s) define interfaces

type HelloWorldService struct{}

func (hs *HelloWorldService) Hello(name string) (string, error) {
	return "Hello world!", nil
}

Actual Implementations are separate

App Configuration

Use environment variables and fallback to sensible defaults

Personal Rule of Thumb:

The app should start (in local dev) with zero config

Testing

Testing Framework is built in!

Excellent HTTP Testing capabilities

Dependency Injection allows us to easily test components in isolation

Test files are normally kept in the same directory

Good Practice to separate integration tests and unit tests

Code Coverage

Code Coverage reporting built in!

Not easy to aggregate coverage across multiple packages

(example shown later)

go test -cover -coverprofile=coverage.out

Upload to coveralls.io as part of CI using goveralls

Build & Test Commands

Use Make for common build and test commands

$ make test
$ make test_integration
$ make build
$ make build_linux
$ make docker_build
$ make docker_release
  ...

Docker

FROM centos
ARG BINARY=./go-hello-server
EXPOSE 4000

COPY ${BINARY} /opt/go-hello-server
CMD "/opt/go-hello-server"

Build the binary first, then pass into the Docker image

Wait a minute...
That means our build environment is not guaranteed to be clean
This is a terrible idea.
The CI environment is guaranteed to be clean.

Why?

ONE super simple, low maintenance Dockerfile

Simplest developer workflow

Works in dev and in prod

Easiest way to build any version of the project, anytime.

Aim for simplicity, only complicate things if you really need to

Keeps custom build tooling to a minimum

Continuous Integration & Release
poorly documented black magic rituals
unnecessary amounts of setup & coordination
Let's make it ridiculously easy
(with CircleCI and goreleaser)

Goreleaser - CLI tool for managing Github releases

CircleCI Worflows

Define small single purpose, self contained CI jobs

- build
- test
- push
- deploy

Create different workflows that use those jobs (based on conditions)

use git tags to kick off release process
(push button release/deploy)
okay... maybe it's not the *most* production ready server
(it's pretty good though)

We didn't talk about

- Handling JSON input
- Input Validation
- Logging
- Middleware Functions
- More
But these are really well documented online
end 🍕

The Most Production Ready Hello World Server Ever

By Dara Hayes

The Most Production Ready Hello World Server Ever

This presentation is the Golang Hello World Server on Steroids. In addition to 'Hello World' This presentation demonstrates approaches for unit tests, parsing and sending JSON, handling environment variables, using interfaces and dependency injection, Building Docker Images and Setting up automated releases in a CI/CD environment.

  • 450

More from Dara Hayes