Today I Learned

A Zero One initiative

3 posts about #docker

A simple way to keep the latest n docker images

We recently had a CI server run out of space because it had too many images on it that were no longer relevant.

This handy command can be used to delete all but the newest number of images. Slap it into cron and never worry about your disk filling up from too many images

docker images --format "{{.Repository}}:{{.Tag}}" | grep my-fancy-image | tail --lines +21 | xargs --no-run-if-empty docker image rm

The example above will remove all but the latest 20 versions of my-fancy-image.

Take note to add 1 to the amount you want to keep when passing that value to tail, e.g. if you want to keep the latest 5, pass +6 to tail --lines.

Save 100s of MBs with Docker multi-stage builds

If you’re building a Docker image to serve a frontend that is compiled down to static files, you can save hundreds of MBs - and avoid some serious security issues - by excluding node_modules and everything else required to compile your Nodejs app by using multi-stage builds.

FROM node:15-something AS builder # use AS to define a name for this build stage

COPY    package*.json /app/
WORKDIR /app
RUN     npm install

COPY . /app/
RUN  npm run build # or whatever your build command is

# Now we start a new build stage
FROM nginx:1-something AS runtime

COPY --from=builder /app/dist/index.html /usr/share/nginx/html/ # Pay attention to the --from command which
COPY --from=builder /app/dist/static     /usr/share/nginx/html/ # which references the previous stage's name

WORKDIR /usr/share/nginx/html

Here’s a before and after:

REPOSITORY   TAG     IMAGE ID     CREATED          SIZE
my_node_app  latest  2a0e6ea0c2fb 51 minutes ago   168MB
my_node_app  fatty   f262d57f5c62 4 hours ago      1.1 GB

The new image only has NGINX, and the static files it needs, and none of the Nodejs bloat required for compiling the app at all, and coming in at only 15% of its original size!

Quickly find your largest docker images

Here’s a handy command to stuff into a shell alias somewhere:

docker images --format '{{.Size}} {{.Repository}} {{.Tag}} {{.ID}}' | sort --human-numeric-sort | column -t

The --format param on the docker command takes a Go template string that will print the size, name, tag and id of your images.

It then gets piped to the sort command with a switch for that understands the difference between MB and GB, and is finally then piped to a handy utility that attempts to format input as a table. Example output:

104MB redis                     latest            ef47f3b6dc11
109MB nginx                     1.15              881bd08c0b08
110MB hashicorp/terraform       0.14.4            18e46faca2ff
113MB certbot/certbot           latest            4bdc1514009b
118MB k8s.gcr.io/kube-proxy     v1.19.3           cdef7632a242
119MB k8s.gcr.io/kube-apiserver v1.19.3           a301be0cd44b
132MB nginx                     1.19.1            0901fa9da894
133MB nginx                     1.19.5            bc9a0695f571
133MB nginx                     latest            ae2feff98a0c
147MB ruby                      2.6.6-slim-buster ab543f69a598
183MB phusion/baseimage         0.11              14e6c478b00a
205MB ruby                      2.3-slim-stretch  6c82e230dca6
222MB kubernetesui/dashboard    v2.0.0            8b32422733b3
222MB ruby                      2.3-slim-jessie   4804bf5dd1e3
240MB osixia/phpldapadmin       latest            afce031a5c02
253MB k8s.gcr.io/etcd           3.4.13-0          0369cf4303ff
265MB postgres                  9.6.1             4023a747a01a
313MB postgres                  12                b97bae343e06
314MB postgres                  latest            f51c55ac75ed
372MB mysql                     5.7               ee7cbd482336
544MB mysql                     latest            0d64f46acfd1
692MB jboss/keycloak            12.0.3            eb71e4d34b56
858MB jboss/keycloak            4.5.0.Final       7874d3a082ee