In terms of modern programming languages, I would pick golang as a great language to use. To me, it feels a little bit like a modernized C and has a nice approach to building single file binaries. It has a module system, type safety and garbage collection.
I really dislike the way that you install golang onto your system and the way you end up managing that install with environment variables and the like. It’s not a terrible install story, but I don’t like polluting my development laptop with cruft. Multiple versions make this more annoying.
Docker on the other hand, despite it’s flaws, is worth having installed. It allows you to run lots of different stuff without needing to make a big commitment about the install. So instead of re-imaging my machine to start fresh, I just purge the containers and start again. Another benefit is that it’s relatively easy for me to point someone else at my configuration and for them to re-use it nearly directly.
Getting a docker image that will persist state and make it trivial to compile golang programs turns out to be very easy
1 2 3 4 5 |
# First we create the image docker create -it --name gobuild -v $PWD:/data golang:1.17-bullseye bash # Now we will run/attach to it by name docker start -ai gobuild |
A couple of things to note. I’m using a host mounted volume – specifically the current directory that I issue the docker create in. From inside the container it is mapped to /data
. I’ve also named the container, making it easy for me to re-run/attach to it for future compiles.
Edit – running with -u is a good idea to make docker run as the right user (you). This will mean that files created by that container on the mounted host volume are owned by you.
1 2 3 |
docker create -it --name gobuild \ -v $PWD:/data -u $UID:`id -g` \ golang:1.17-bullseye bash |
As an example, here is how I’d go about compiling a github project that is in golang.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ git clone https://github.com/die-net/http-tarpit Cloning into 'http-tarpit'... remote: Enumerating objects: 122, done. remote: Counting objects: 100% (13/13), done. remote: Compressing objects: 100% (12/12), done. remote: Total 122 (delta 0), reused 10 (delta 0), pack-reused 109 Receiving objects: 100% (122/122), 35.08 KiB | 394.00 KiB/s, done. Resolving deltas: 100% (52/52), done. $ cd http-tarpit $ docker create -it --name gobuild -v $PWD:/data golang:1.17-bullseye bash 7ba9d1a8e367e4cadbc77bfbe69244c1ceca30a96134cc817e54ac3b4a8f39c2 $ docker start -ai gobuild root@7ba9d1a8e367:/go# cd /data root@7ba9d1a8e367:/data# go build root@7ba9d1a8e367:/data# exit exit $ ls -l http-tarpit -rwxr-xr-x 1 root root 6224123 Feb 11 13:14 http-tarpit |
How slick is that? My development machine needs docker and git installed. The rest of this environment is entirely inside the docker container.
Let me now demonstrate persistence of the container from run to run.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ docker start -ai gobuild root@7ba9d1a8e367:/go# nmap bash: nmap: command not found root@7ba9d1a8e367:/go# apt update; apt install nmap [...install noise...] root@7ba9d1a8e367:/go# which nmap /usr/bin/nmap root@7ba9d1a8e367:/go# exit exit $ docker start -ai gobuild root@7ba9d1a8e367:/go# which nmap /usr/bin/nmap root@7ba9d1a8e367:/go# exit |
Thus if I happen to need a utility which isn’t installed in the base golang bullseye image, it’s easy for me to install. Also, from run to run – I have persistence of the changes I’ve made to my named image.