
Guide to Writing Dockerfiles for Go Web-Apps - alberteinstein
https://blog.hasura.io/the-ultimate-guide-to-writing-dockerfiles-for-go-web-apps-336efad7012c
======
c2h5oh
1\. Running strip on go binaries is a BAD IDEA:
[https://github.com/moby/moby/blob/2a95488f7843a773de2b541a47...](https://github.com/moby/moby/blob/2a95488f7843a773de2b541a47d9b971a635bfff/project/PACKAGERS.md#stripping-
binaries)

Use -ldflags="-s -w" instead

2\. Production build should NOT be running glide install - you want ALL your
dependencies vendored, locked and commited to your repo before you build it.
Bonus: you can have your Docker image build by CI pipeline and know it's going
to be exactly like the one you've got locally.

3\. If you're including external resources in your container (upx in this
case) via url it's common sense to verify GPG signature or, when one isn't
available, at least file hash

4\. If your app doesn't need things like ca-certs you don't need Alpine - you
can just use "FROM SCRATCH" to only have the statically linked binary in your
container slashing another 50% off the final size of container.

~~~
andrewstuart2
I really don't understand the obsession with small docker final images, unless
you're doing something silly like export/import and throwing away the benefits
of layers. Removing temporary files from layers is great, but you get very
little benefit from starting smaller. _Especially_ so if that means your final
layer is bigger. That's actually a lot worse storage-wise than having a fat
base image and a bunch of tiny final layers.

I do understand the desire to omit unnecessary attack surface, but if you have
a good common base image, then you're not incurring any additional storage
beyond once per docker host for all the layers your images have in common.

~~~
alberteinstein
When a container is scheduled to a node in Kubernetes, if the image is already
available, it takes only ~2s for it to be up and running. But, if it is not
present on a node and the image is of 2GB, the download speed is the rate
limiting factor, taking start up times well beyond 2 seconds. And in a multi
node environment where you do scaling up and down or maybe autoscale, new
nodes could come up a lot. So, Leaner images are always better.

~~~
andrewstuart2
> So, Leaner images are always better.

That's a very vague statement, though, given the layer concept. The same final
filesystem image can use any number of layers to get there. The way you
organize the layers in your images, and share data between multiple apps, is
important on top of generally keeping things reasonably small.

What I think you're trying to say is that fast startup is preferable. What you
have not addressed is average startup time, which is where you still benefit
dramatically by understanding layers.

The smallest average amortized image size is best. That means if you can get
all the services for a new node started with 2GB total download, it's better
than 3GB total download.

Using layers appropriately, and perhaps adding dependencies used by 80%
services to your base image, may be best for overall efficiency. Know the tool
and know your usage patterns.

~~~
alberteinstein
This adds an un-necessary dependency between services. Team working on service
A would need to coordinate with team working on service B to keep same layers.
The contract should be to keep the image size as small as possible, and teams
can work independently.

Also, python:3.6 doesn't always mean the same layers, since it could be
rebuilt with entirely different layers with same tag, unless FROM is locked to
a particular layer.

------
q3k
I don't understand what is the advantage of using Docker in production to run
Go. It can already emit static binaries, can contain embedded assets and you
can cross-compile for all supported systems using xgo... Ship your binaries,
drop them anywhere in the filesystem, run them however you wish and you're
done.

Even for building, a proper build system (like Bazel) is a better tool than
building in Docker containers...

~~~
tango12
Running in container only environments (Kubernetes) would require it to be
packaged in a container.

~~~
pcnix
This is definitely seems to be the use case the blog was written for, seeing
the author's background.

------
tptacek
Why not use GOOS/GOARCH to cross-compile, so you don't have to do the
compilation step in a virtualized environment? You can build on the host and
just copy the resulting binary in like all the rest of your artifacts.

------
srameshc
I've been following this pattern by Kelsey Hightower on his blog
[https://medium.com/@kelseyhightower/optimizing-docker-
images...](https://medium.com/@kelseyhightower/optimizing-docker-images-for-
static-binaries-b5696e26eb07). I love because the end result is a small base
image.

~~~
zokier
I would strongly advice on using multi-stage dockerfiles instead of building
binaries on host. Conveniently Docker manual has an example for Go already:
[https://docs.docker.com/develop/develop-images/multistage-
bu...](https://docs.docker.com/develop/develop-images/multistage-build/#use-
multi-stage-builds)

------
jpsim
Someone should make a collection of these for all languages. I’d love to see
an equivalent for Swift.

------
collyw
Question, is Docker actually a reliable way of distributing software? I have
toyed with it a couple of times and all I ever got was errors about
incompatible client and server versions.

~~~
merb
I found it akward when running docker "standalone". However after I learned
about k8s, all the things started to make sense. So my advice is, start using
it. If it doesn't click at least try to use it in k8s, if you still can't
grasp it you probably do not need it.

------
czbond
Related: Anyone have experience with Go and Unikernels? Saw a dead discussion
a week ago on unik ([https://github.com/solo-io/unik](https://github.com/solo-
io/unik))

For security purposes, the idea of a unikernel rather than a container has
been very interesting. Wanted to use them in a test soon.

~~~
jwhitlark
Docker Inc. bought a unikernel startup a while back. Presumably they see that
as a possible endpoint.

------
sigjuice
Why is it a problem to have different versions of Go on the same machine?

~~~
alberteinstein
Having two different versions is not an issue, tools like GVM helps in
managing them. Docker helps in handling this easily without any extra tools.
But, if something is built with a version, it cannot be easily changed without
thorough testing.

For e.g. when Kubernetes was built with Go 1.8 instead of 1.7, emptyDir
teardown path was broken due to the change in behavior of os.Rename in Go 1.8
[1]. This bug caused other issues like the one with minikube [2] and they had
to rebuild with Go 1.7 and update new binaries for already released v0.20.0

[1]
[https://github.com/kubernetes/kubernetes/issues/43534#issuec...](https://github.com/kubernetes/kubernetes/issues/43534#issuecomment-288824459)
[2]
[https://github.com/kubernetes/minikube/issues/1630#issuecomm...](https://github.com/kubernetes/minikube/issues/1630#issuecomment-310275970)

------
throwaway993472
I've been using Habitat[1] for shipping all my Go applications. That way I can
run the go binaries on whatever kind of infrastructure I need (containers,
vms, bare metal). I can mix and match for different environments, or change my
mind later without needing to repackage my application or my infrastructure
automation.

Ultimately at the end of the day I need to work on a team with other
operations-minded developers, and shipping them a dockerfile / docker
container is kind of a spit in the face, so this has been a good solution for
me.

[1] [https://habitat.sh](https://habitat.sh)

