Best Practices
for Cloud-Native Go Services
Elena Grahovac
Video: https://youtu.be/rRSkbOMTHLw
Best Practices
for Cloud-Native Go Services
Elena Grahovac
Video: https://youtu.be/rRSkbOMTHLw
Lead TechOps Automation Engineer @N26
Podcast co-host @GolangShow
hello@grahovac.pro
webdeva
rumyantseva
“Hello, World”
“Production ready”
How to be Cloud-Native?
DevOps
Cloud-Native
People and culture
Design and development practices
DevOps is not a one-way road!
“
Cloud-Native Requirements
Structuring
in Practice
“
Structuring
in Practice
Kat Zień - How do you structure your Go apps: https://youtu.be/VQym87o91f8
Structuring
in Practice
If I have cmd/myctl and cmd/myserver
I can simply do go get cmd/… to have myctl and myserver binaries inside $GOPATH/bin
Structuring
in Practice
If I don’t want a package being available for import by other projects, I’ll store it under internal
See also: ‘Go 1.4 Internal Packages’ - https://docs.google.com/document/d/1e8kOo3r51b2BWtTs_1uADIA5djfXhPT36s6eHVRIvaU/edit
Structuring
in Practice
Other possible ideas (for inspiration): https://github.com/golang-standards/project-layout
Code Quality
This and other wonderful gophers: https://github.com/ashleymcnamara/gophers
Code Quality
Practices:
Code Quality:
Testing
If you love the standard library and hate external dependencies:
func TestDoSomething(t *testing.T) {
// "want" is your expected result
have, err := mypkg.DoSomething()
if err != nil {
t.Errorf("%v", err)
} else if !reflect.DeepEqual(have, want) {
t.Errorf("have %+v, want %+v", have, want)
}
}
Code Quality:
Testing
If you love the standard library and hate external dependencies:
func TestDoSomething(t *testing.T) {
// "want" is your expected result
have, err := mypkg.DoSomething()
if err != nil {
t.Errorf("%v", err)
} else if !reflect.DeepEqual(have, want) {
t.Errorf("have %+v, want %+v", have, want)
}
}
Code Quality:
Testing
If you love to “assert”: https://github.com/stretchr/testify
func TestDoSomething(t *testing.T) {
// "want" is your expected result
have, err := mypkg.DoSomething()
require.NoError(t, err)
assert.Equal(t, want, have)
}
Code Quality:
Testing
Additional topics:
Code Quality:
Analysis
Static code analyzers:
Code Quality:
Analysis
Tools:
Code Quality:
Analysis
When Develop/CI/CD-ing:
Code Quality:
Runtime
Profiler:
Code Quality:
Runtime
package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
log.Println(http.ListenAndServe(":8080", nil))
}
Hello, pprof!
Code Quality:
Runtime
Want to use custom URLs instead of debug/pprof?
r := mux.NewRouter()
r.HandleFunc("/my-prefix/pprof", pprof.Index)
r.HandleFunc("/my-prefix/cmdline", pprof.Cmdline)
r.HandleFunc("/my-prefix/profile", pprof.Profile)
r.HandleFunc("/my-prefix/symbol", pprof.Symbol)
r.HandleFunc("/my-prefix/trace", pprof.Trace)
r.Handle(
"/my-prefix/goroutine", pprof.Handler("goroutine"),
)
r.Handle(
"/my-prefix/heap", pprof.Handler("heap"),
)
r.Handle(
"/my-prefix/threadcreate",
pprof.Handler("threadcreate"),
)
r.Handle("/my-prefix/block",pprof.Handler("block"))
Observability
&
Operability
This and other wonderful gophers: https://github.com/ashleymcnamara/gophers
Observability
Logs:
Observability
Hello, logs!
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
Observability
Metrics:
Traces:
How to start? Check out OpenCensus: https://opencensus.io
Operability
Health checks:
Graceful Shutdown
Versioning
Use the linker to provide a version, commit hash or any other data to identify the version:
go build -ldflags "-X main.version=put_version_here"
package main
import (
"fmt"
)
var (
version = "unset"
)
func main() {
fmt.Printf("The version is: %s\n", version)
}
Configuration
Dependency Management
Dependency Management
Dockerization
Dockerization
Simplest “monostage” image
FROM scratch
ENV PORT 8080
COPY bin/myapp /myapp
EXPOSE $PORT
CMD ["/myapp"]
Dockerization
Multi-stage “all-in-one” builds
# Stage 1. Build the binary
FROM golang:1.11
# add a non-privileged user
RUN useradd -u 10001 myapp
RUN mkdir -p /go/src/myteam/myapp
ADD . /go/src/myteam/myapp
WORKDIR /go/src/myteam/myapp
# build the binary with go build
...
Dockerization
Multi-stage “all-in-one” builds
# Stage 2. Run the binary
FROM scratch
ENV PORT 8080
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=0 /etc/passwd /etc/passwd
USER myapp
COPY --from=0 /go/src/myteam/myapp/bin/myapp /myapp
EXPOSE $PORT
CMD ["/myapp"]
Automation
Repeatable Actions
Automate it!
For inspiration: https://github.com/takama/caldera
Summary
These and other wonderful gophers: https://github.com/ashleymcnamara/gophers
Summary
Summary
Summary
What’s Next?
hello@grahovac.pro
webdeva
rumyantseva
These slides: https://gowayfest.grahovac.pro