Statically Compiled Go on Alibaba Cloud Container Service
We received a lot of great entries in our recent competition to find the best tip for making the most out of Alibaba Cloud services. It was a fun but challenging task for our judges to pick the winners amongst so many helpful and interesting entries. But alas after fiery deliberations and heated debates they’ve decided that the third prize of the competition goes to David Banham. His winning entry is a succinct tutorial on statically compiling a Go program, and using Docker to containerize and distribute it.
Alibaba Cloud’s Container Service is a great example of how Docker and Kubernetes are revolutionising the cloud landscape. The curmudgeons will rail that it’s all still just some software running on someone else’s computer, but the transformative difference is that k8s and Docker provide what is effectively a platform-agnostic management API. If you build your DevOps pipelines against k8s you have the lowest possible switching friction between AWS, Google Cloud, Azure and Alibaba. The closer we can get to the dream of write once, run anywhere, the better!
Another tool I love for enhancing software portability is the Go language. Cross compilation in Go is as easy as falling off a log. I develop software on my Linux laptop and in the blink of an eye I can have binaries built for Windows, OSX, WASM, etc! Here’s the Makefile snippet I use to do it:
name = demo PLATFORMS := linux/amd64 windows/amd64 linux/arm darwin/amd64 temp = $(subst /, ,$@) os = $(word 1, $(temp)) arch = $(word 2, $(temp)) release: make -l inner_release inner_release: $(PLATFORMS) $(PLATFORMS): @-mkdir -p ../web/api/clients/$(os)-$(arch) @-rm ../web/api/clients/$(os)-$(arch)/* GOOS=$(os) GOARCH=$(arch) go build -o '../web/api/clients/$(os)-$(arch)/$(name) . @chmod +x ../web/api/clients/$(os)-$(arch)/$(name) @if [ $(os) = windows ]; then mv ../web/api/clients/$(os)-$(arch)/$(name) ../web/api/clients/$(os)-$(arch)/$(name).exe; fi zip --junk-paths ../web/api/clients/$(os)-$(arch)/$(name)$(os)-$(arch).zip ../web/api/clients/$(os)-$(arch)/* @if [ $(os) = windows ]; then cp ../web/api/clients/$(os)-$(arch)/$(name).exe ../web/api/clients/$(os)-$(arch)/$(name); fi
Neat! That will get you a tidy little binary that will run on your target operating systems. Even that is overkill if you’re targeting a Docker host like Cloud Container Service. All you need to do there is just
GOOS=linux GOARCH=amd64 go build and you’re done! Then, you just need to choose your favorite Linux distribution and build that into the Dockerfile.
What if we wanted to simplify our lives even further, though? What if we could do away with the Linux distribution entirely?
Go supports the compilation of statically linked binaries. That means we can write code that doesn’t rely on any external DLLs at all. Observe this magic Dockerfile:
FROM scratch ADD ./bin/web /app ENTRYPOINT ["/app"]
What is scratch? It’s nothing. That tells Docker to bundle in nothing except the Linux kernel and the bare bones required to make Docker work. Smaller than Ubuntu? There’s Alpine. Smaller than Alpine? There’s scratch.
What’s cooler than being Alpine? Ice cold! Uh, scratch. Alright alright alright alright.
The benefit of this is that we remove a whole host of moving parts from our deployment. There’s no chance that a distro maintainers decision to bundle or not-bundle some service in their distro will take down your production environment. There are no security holes introduced by distro code. There are no updates to manage. There’s just… nothing.
As a bonus, you get a teeny tiny Docker image that’s easier to ship around. Easier to ship around to any cloud you like!
Give it a shot. Write some Go, statically compile it and put it in a scratch Docker container. Deploy it across Alibaba, AWS and Google Cloud just for funsies! See which service suits you the best.