A code base of high quality that makes an engineer proud cannot do without a proper CI/CD pipeline. Linting, analyzing your code for potential errors and bad coding practices, should be a standard part of your pipeline. Especially interpreted languages such as Python benefit greatly from linting because of the lack of compilation. In this blog post, I check out a Dockerfile linter called Hadolint.
Linting your Dockerfile
Just like programming languages, static files can also be linted. Why would you lint your Dockerfile? See and validate your own Dockerfile online at fromlatest.io for potential flaws. It checks for optimizations and likely failures, resulting in cleaner code, no errors and possibly a smaller Docker image. There are several Dockerfile linters available, the most popular being Hadolint, short for Haskell Dockerfile linter.
Hadolint is available as a Docker image and you can lint with:
docker run --rm -i hadolint/hadolint
If your Dockerfile is immaculate, you won’t see any output and receive an exit code 0. If your Dockerfile contains flaws such as this one:
1 # ===== Flawed Dockerfile, do not copy ===== 2 3 FROM continuumio/miniconda:latest 4 RUN apt-get update 5 && apt-get install -y 6 gcc 7 fortunes 8 cowsay 9 && pip install apache-airflow[crypto,postgres] 10 11 CMD /usr/games/fortune | /usr/games/cowsay
This Dockerfile might build and run fine:
$ docker build -t moo . $ docker run moo ________________________________ / No problem is insoluble in all conceivable circumstances. / -------------------------------- ^__^ (oo)_______ (__) )/ ||----w | || ||
However Hadolint points out several flaws and potential issues which might break in the future, and returns an exit code 1:
/dev/stdin:3 DL3007 Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag /dev/stdin:4 SC2102 Ranges can only match single chars (mentioned due to duplicates). /dev/stdin:4 DL3008 Pin versions in apt get install. Instead ofapt-get install <package>
useapt-get install <package>=<version>
/dev/stdin:4 DL3009 Delete the apt-get lists after installing something /dev/stdin:4 DL3013 Pin versions in pip. Instead ofpip install <package>
usepip install <package>==<version>
/dev/stdin:4 DL3015 Avoid additional packages by specifying--no-install-recommends
/dev/stdin:11 DL3025 Use arguments JSON notation for CMD and ENTRYPOINT arguments
Each error is represented by a key DL (Hadolint error) or SC (ShellCheck error). Documentation for the most common errors is given on the Hadolint wiki: github.com/hadolint/hadolint#rules.
Configuration
A nice feature of Hadolint is the ability pass along configuration. For example, the error SC2102 above is ShellCheck thinking the pip install
command of Airflow with additional packages is a range, which in this case does not apply. So, we would like to ignore this error. We can do this is 3 ways:
- Ignore in entire Dockerfile by passing –ignore flag with docker run:
docker run --rm -i hadolint/hadolint hadolint --ignore SC2102 -
- Ignore in entire Dockerfile by mounting configuration yaml file:
.hadolint.yml:
ignored: - SC2102
docker run --rm -i -v ${PWD}/.hadolint.yml:/.hadolint.yaml hadolint/hadolint
- Ignore specific instructions inline. The exception works for a Dockerfile full instruction, individual lines cannot be excluded.
# ===== Flawed Dockerfile, do not copy ===== FROM continuumio/miniconda:latest # hadolint ignore=SC2102 RUN apt-get update && apt-get install -y gcc fortunes cowsay && pip install apache-airflow[crypto,postgres] CMD /usr/games/fortune | /usr/games/cowsay
Integrate in your CI pipeline
To continuously verify changes on your Dockerfiles while you’re writing them, integrate the linter in your CI. Linter errors return exit code 1 and will fail your pipeline. E.g. for Bitbucket Pipelines you could define a custom "build-docker" pipeline:
custom: build-docker: - step: name: Dockerlint script: - docker run --rm -i -v ${PWD}/.hadolint.yml:/.hadolint.yaml hadolint/hadolint:v1.10.3 Dockerfile - step: name: Build image script: - docker build -t myimage .
Other CI pipeline examples are given on the Hadolint integration docs.
That’s it for Dockerfile linting, enjoy!