SEANK.H.LIAO

micro boilerplate

leveraging the kubernetes environment

k8s is a platform, with features that can be used to make things run more smoothly

lifecycle signals

SIGINT and SIGKILL are the 2 main ones to care about, pod termination:

  1. preStop hook is called
  2. SIGINT is called
  3. after terminationGracePeriodSeconds
  4. SIGKILL is called
 1func main() {
 2        // register handlers
 3
 4        // spin off server
 5        svr := &http.Server{}
 6        go svr.ListenAndServe()
 7
 8        // block on waiting for signal
 9        sigs := make(chan os.Signal)
10        signal.Notify(sigs, syscall.SIGINT, syscall.SIGKILL)
11        <-sigs
12
13        // handle graceful shutdown
14        svr.Shutdown(context.Background())
15}

healthcheck

liveness: you can only die once (unlike cats), pod is restarted if it fails livenessProbe

readiness: transient service unavailability should be signalled here, failure will mean Service won't route to the pod, sometimes may be easier to just fail a liveliness and restart

deployment.yaml:

 1kind: Deployment
 2...
 3spec:
 4  ...
 5  template:
 6    ...
 7    spec:
 8      ...
 9      containers:
10        - name: liveness
11          ...
12          livenessProbe:
13            httpGet:
14              path: /health     # /healthz
15              port: http
16              initialDelaySeconds: 3
17              periodSeconds: 3
18          readinessProbe:
19            httpGet:
20              path: /ready      # /readyz
21              port: http
22              initialDelaySeconds: 3
23              periodSeconds: 3

logging

k8s reads structured logs, fluentd is common

there is a tradeoff between structure logging for machines and text logs for humans

with logrus

 1import (
 2        log "github.com/sirupsen/logrus"
 3)
 4
 5func init() {
 6        switch os.Getenv("LOG_LEVEL") {
 7        case "DEBUG":
 8                log.SetLevel(log.DebugLevel)
 9        case "INFO":
10                log.SetLevel(log.InfoLevel)
11        case "ERROR":
12                fallthrough
13        default:
14                log.SetLevel(log.ErrorLevel)
15        }
16
17        switch os.Getenv("LOG_FORMAT") {
18        case "JSON":
19                log.SetFormatter(&log.JSONFormatter{})
20        default:
21                log.SetFormatter(&log.TextFormatter{})
22        }
23}
24
25func main() {
26        log.Debugf("...")
27        log.Printf("...") // equiv to log.Infof
28        log.Errorf("...")
29}

metrics

metrics endpoint works with prometheus

prometheus has a go client library

1import (
2        "github.com/prometheus/client_golang/prometheus/promhttp"
3)
4func main() {
5        // default metrics
6        http.Handle("/metrics", promhttp.Handler())
7        http.ListenAndServe(":2112", nil)
8}

tracing

see opentracing, also jaeger

basically tracer := opentracing.Tracer

and span := tracer.StartSpan() and span.Finish()

additional

go-grpc-middleware