In this post we’re going to create a simple microservice in Go. We’ll go on to deploy our simple service in Docker and prepare for some exercises in Kubernetes.
The Dockerfile below is a multistage build file, which will compile our microservice as a Linux app which later will run in a Linux container. I’m building the container on a Windows 10 laptop, providing the directive GOOS=linux during the build stage, to cross-compile as a Linux executable application.
For a better understanding about Go environment settings, see my article environment-variables-in-go.
My source directory is located in my GOPATH here:
# GOPATH
C:\Home\dev\Go\src\github.com\mjd\basic-svc
# dir
.gitignore Dockerfile main.go README.md
Dockerfile settings:
# Dockerfile
FROM golang
ADD . /go/src/github.com/mjd/basic-svc
# Build our app for Linux
RUN go install github.com/mjd/basic-svc
# Run the basic-svc command by default when the container starts.
ENTRYPOINT /go/bin/basic-svc
# Document that the service listens on port 8083
EXPOSE 8083
Our microservice application will use the Go http package to provide services for:
- health
- version
- greet
main.go
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
)
const (
version = "v1.0.0"
addr = "0.0.0.0:8083"
)
func main() {
msg := fmt.Sprintf("Basic service running: http://%s", addr)
log.Println(msg)
// Configure API handlers
http.HandleFunc("/greet",
func(w http.ResponseWriter, r *http.Request {
fmt.Fprintf(w, "Howdy\n")
})
http.HandleFunc("/health",
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "okie-dokie\n")
})
http.HandleFunc("/version",
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, version)
})
// Listen for inbound API connections
s := http.Server{Addr: addr}
go func() {
log.Fatal(s.ListenAndServe())
}()
// Signal handlers to terminate service
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
<-signalChan
log.Println("Basic service received exit signal")
s.Shutdown(context.Background())
}
The health API is typically utilized by load balancers to check the liveness of our application. If our application doesn’t answer after a predetermined number of requests a replicator will start a new instance. Version and greet are just other endpoints for our simple service. Our application will listen for incoming HTTP requests on port 8083.
When ListenAndServe is invoked, our application will respond to inbound requests and continue running until terminated by a Control-C (^C) from the console, or is sent a Linux kill signal.
Building the Dockerfile
# build Dockerfile
docker build -t mitchd/basic-svc .
# run the container
docker run -itd -p 8083:8083 --name mysvc mitchd/basic-svc
With the docker container now running you can exercise the services using the browser, curl, httpie or your favorite testing tool. I’ll show examples using httpie.
# curl example: curl -i http://localhost:8083/greet
# httpie example
$ http :8083/greet
HTTP/1.1 200 OK
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Date: Sun, 16 Feb 2020 22:57:33 GMT
Howdy
# version
$ http :8083/version
HTTP/1.1 200 OK
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Date: Sun, 16 Feb 2020 23:00:23 GMT
v1.0.0
# health
$ http :8083/health
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Date: Sun, 16 Feb 2020 23:00:55 GMT
okie-dokie
With our simple microservice running in a Docker container, we’re now ready to push it out into our Kubernetes cluster and perform some basic operations. We’ll do this in our next post.
Clean up
# stop the running service instance
$ docker stop mysvc
# remove the container
$ docker rm mysvc
# remove the image
$ docker rmi mitchd/basic-svc