Why

Versioning is hard, and we’ve found the freedom Docker tagging allows isn’t always up for the task in practice. It’s a policy problem.

We depend on specific version of a Go package, and pin to it in our go.mod, and that means we need matching specific versions of Docker images to build. Sadly the tagging for this Docker image is just environments, so author/package:android, author/package:windows. All at the latest. If one of our devs sets up a new machine, they have no way to pull the docker image that corresponds to the version of the code we use.

So we decided to set up our own docker registry so that we could:

  • distribute the versions of the docker image we use
  • tag them with additional version information so we could support multiple versions in our packages and build tools

Notes on Phase 1

Resources:

Took the gist and tailored it to our resources. I had already provisioned SSL certs with nginx earlier for the domain so dropped the provisioning part and just fed them in.

It gives us a docker registry running on port 5000 that has SSL (from Lets Encrypt) and uses HTTP basic auth to gate access (because otherwise anyone could push images and fill our server). We can add our own versioning info to tag strings so that we can have both images differentiated by environment but also version, so that also in the future we could store multiple versions of the images, and migrate our software package at a time. Our build system could also move to these so as to support multiple versions at the same time, instead of essentially either the one version we have, or environment-latest.

Example use

Server

./start_docker_registry.bash 

docker login registry.openprivacy.ca:5000
docker tag 572cd474b221 registry.openprivacy.ca:5000/therecipe/qt:linux_static-2019.11
git push registry.openprivacy.ca:5000/therecipe/qt:linux_static-2019.11

As you can see, we have added versioning info to the free form tags, along with the existing environment distinction information.

Client

docker login registry.openprivacy.ca:5000
docker pull registry.openprivacy.ca:5000/therecipe/qt:linux_static-2019.11
docker tag 572cd474b221 therecipe/qt:linux_static
docker rmi registry.openprivacy.ca:5000/therecipe/qt:linux_static-2019.11

which pull the version of the image we want, adds the tag we need for our tools to recognize it and use it, and deletes the imported but unnecessary tag.

Unresolved issues / nice to haves

This gives us only private access. If anyone outside of our team want so use these images to build our software they need to ask for access and we’d need to a an htpasswd entry for them. Ideally we would like anonymous open pull access but restricted push access. Sadly docker registry doesn’t have great support for this. The docs do mention using token authentication as a way to include permissions with auth access, but it looks pretty non trivial or simple to set this up and there don’t seem to be any full simple examples.

The other approach, that looks more used in practice is fronting the docker image with nginx, moving the SSL and HTTP basic auth there, and restricting access to push features via hard coded user name and location/path. The rough idea is outlined in this gist:Run docker private registry with read/write access control.

How ever the open question my limited one day of research hasn’t turned up is how to connect and existing on machine Nginx instance to the docker container safely with out making it’s port open to the internet (with no SSL or auth). The recommendation is using docker compose and spinning up a nginx docker with that. I would however prefer not to have a second nginx running on our machine to front this.