これは、なにをしたくて書いたもの?
hadolintというDockerfileのlintがあるので、試してみることにしました。
hadolint
hadolintは、Dockerイメージをベストプラクティスに従って構築するためのDockerfileのlinterです。
GitHub - hadolint/hadolint: Dockerfile linter, validate inline bash, written in Haskell
ベストプラクティスというのは、こちらのことです。
Best practices for writing Dockerfiles | Docker Documentation
機能としては、以下のようですね。
- DockerfileのASTを解析して、ルール実行
- Shellcheckによる
RUN
命令内のBashコードチェック
hadolintというのは、Haskell Dockerfile Linterの略で良いのでしょうか?
現時点でのバージョンは2.12.0のようです。
https://github.com/hadolint/hadolint/tree/v2.12.0
使い方としては、オンラインで実行するか
ダウンロードやパッケージインストールして
Haskell Dockerfile Linter / Install
実行することになるようです。
Haskell Dockerfile Linter / How to use
Dockerイメージもあるようです。
設定ファイル。
Haskell Dockerfile Linter / Configure
定義されているルール。
Haskell Dockerfile Linter / Rules
インテグレーションについては、こちら。
コードレビューに組み込んだり、GitHub Actions、GitLab CIなどに組み込む方法が書かれています。
EmacsやVisual Studio Code、vimなどのエディター向けのエクステンションやプラグインもあるようです。
とりあえず、使っていってみましょう。
環境
今回の環境は、こちら。Ubuntu Linux 20.04 LTSです。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.5 LTS Release: 20.04 Codename: focal $ uname -srvmpoi Linux 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
使用するDocker。
$ docker version Client: Docker Engine - Community Version: 20.10.22 API version: 1.41 Go version: go1.18.9 Git commit: 3a2c30b Built: Thu Dec 15 22:28:08 2022 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.22 API version: 1.41 (minimum version 1.12) Go version: go1.18.9 Git commit: 42c8b31 Built: Thu Dec 15 22:25:58 2022 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.13 GitCommit: 78f51771157abb6c9ed224c22013cdf09962315d runc: Version: 1.1.4 GitCommit: v1.1.4-0-g5fd4c4d docker-init: Version: 0.19.0 GitCommit: de40ad0
hadolintをインストールする
今回は、GitHubのReleasesからバイナリをダウンロードして使うことにします。
Releases · hadolint/hadolint · GitHub
ダウンロード。
$ curl -o hadolint -L https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
実行権限を与えて
$ chmod a+x hadolint
バージョン確認。
$ ./hadolint --version Haskell Dockerfile Linter 2.12.0
ヘルプ。
$ ./hadolint --help hadolint - Dockerfile Linter written in Haskell Usage: hadolint [-v|--version] [-c|--config FILENAME] [DOCKERFILE...] [--file-path-in-report FILEPATHINREPORT] [--no-fail] [--no-color] [-V|--verbose] [-f|--format ARG] [--error RULECODE] [--warning RULECODE] [--info RULECODE] [--style RULECODE] [--ignore RULECODE] [--trusted-registry REGISTRY (e.g. docker.io)] [--require-label LABELSCHEMA (e.g. maintainer:text)] [--strict-labels] [--disable-ignore-pragma] [-t|--failure-threshold THRESHOLD] Lint Dockerfile for errors and best practices Available options: -h,--help Show this help text -v,--version Show version -c,--config FILENAME Path to the configuration file --file-path-in-report FILEPATHINREPORT The file path referenced in the generated report. This only applies for the 'checkstyle' format and is useful when running Hadolint with Docker to set the correct file path. --no-fail Don't exit with a failure status code when any rule is violated --no-color Don't colorize output -V,--verbose Enables verbose logging of hadolint's output to stderr -f,--format ARG The output format for the results [tty | json | checkstyle | codeclimate | gitlab_codeclimate | gnu | codacy | sonarqube | sarif] (default: tty) --error RULECODE Make the rule `RULECODE` have the level `error` --warning RULECODE Make the rule `RULECODE` have the level `warning` --info RULECODE Make the rule `RULECODE` have the level `info` --style RULECODE Make the rule `RULECODE` have the level `style` --ignore RULECODE A rule to ignore. If present, the ignore list in the config file is ignored --trusted-registry REGISTRY (e.g. docker.io) A docker registry to allow to appear in FROM instructions --require-label LABELSCHEMA (e.g. maintainer:text) The option --require-label=label:format makes Hadolint check that the label `label` conforms to format requirement `format` --strict-labels Do not permit labels other than specified in `label-schema` --disable-ignore-pragma Disable inline ignore pragmas `# hadolint ignore=DLxxxx` -t,--failure-threshold THRESHOLD Exit with failure code only when rules with a severity equal to or above THRESHOLD are violated. Accepted values: [error | warning | info | style | ignore | none] (default: info)
では、使っていってみましょう。
hadolintを使ってみる
まずはDockerfile
が必要ですね。hadolintのサンプルのDockerfile
をそのまま使ってもよいのですが、ここは簡単に作成してみましょう。
ローカルのNode.jsはこちら。
$ node --version v18.12.1 $ npm --version 8.19.2
Expressを使った、簡単なWebアプリケーションを作成することにします。
$ npm init -y $ npm i express
バージョン。
"dependencies": { "express": "^4.18.2" }
app.js
const express = require('express'); const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello World!'); }) const server = app.listen(port, () => { console.log(`Example app listening on port ${port}`); }); process.on('SIGINT', () => server.close(() => {})); process.on('SIGTERM', () => server.close(() => {}));
ここで、hadolintのサンプルをマネたDockerfile
を作成してみます。
Dockerfile
FROM debian RUN export node_version="12.*" \ && export npm_version="7.*" \ && apt-get update \ && apt-get -y install nodejs="${node_ver}" npm="${npm_version}" ADD package*.json /usr/src/app/ ADD app.js /usr/src/app RUN cd /usr/src/app \ && npm ci WORKDIR /usr/src/app EXPOSE 3000 CMD ["node", "app.js"]
これに対して、hadolintを実行してみます。
$ ./hadolint Dockerfile Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly Dockerfile:3 SC2154 warning: node_ver is referenced but not assigned. Dockerfile:3 DL3009 info: Delete the apt-get lists after installing something Dockerfile:3 DL3015 info: Avoid additional packages by specifying `--no-install-recommends` Dockerfile:8 DL3020 error: Use COPY instead of ADD for files and folders Dockerfile:9 DL3020 error: Use COPY instead of ADD for files and folders Dockerfile:11 DL3003 warning: Use WORKDIR to switch to a directory
各種エラー、警告などが表示されました。
シェルスクリプトの未割り当ての変数についての警告も出ています。
Dockerfile:3 SC2154 warning: node_ver is referenced but not assigned.
出力された情報をDockerfile
にマッピングすると、こんな感じですね。
Dockerfile
# DL3006 FROM debian # SC2154, DL3009, DL3015 RUN export node_version="12.*" \ && export npm_version="7.*" \ && apt-get update \ && apt-get -y install nodejs="${node_ver}" npm="${npm_version}" # DL3020 ADD package*.json /usr/src/app/ # DL3020 ADD app.js /usr/src/app # DL3003 RUN cd /usr/src/app \ && npm ci WORKDIR /usr/src/app EXPOSE 3000 CMD ["node", "app.js"]
では、hadolintから出力された内容に沿って書き直してみます。
Dockerfile
FROM debian:bullseye-slim RUN export node_version="12.*" \ && export npm_version="7.*" \ && apt-get update \ && apt-get -y install --no-install-recommends nodejs="${node_version}" npm="${npm_version}" \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* WORKDIR /usr/src/app COPY package*.json /usr/src/app/ COPY app.js /usr/src/app/ RUN npm ci EXPOSE 3000 CMD ["node", "app.js"]
今度は、hadolintを実行してもなにも言われなくなります。
$ ./hadolint Dockerfile
OKですね。
設定してみる
ここで元のDockerfile
に戻してみます。
Dockerfile
FROM debian RUN export node_version="12.*" \ && export npm_version="7.*" \ && apt-get update \ && apt-get -y install nodejs="${node_ver}" npm="${npm_version}" ADD package*.json /usr/src/app/ ADD app.js /usr/src/app RUN cd /usr/src/app \ && npm ci WORKDIR /usr/src/app EXPOSE 3000 CMD ["node", "app.js"]
この状態だと、こんな出力になるのでした。
$ ./hadolint Dockerfile Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly Dockerfile:3 SC2154 warning: node_ver is referenced but not assigned. Dockerfile:3 DL3009 info: Delete the apt-get lists after installing something Dockerfile:3 DL3015 info: Avoid additional packages by specifying `--no-install-recommends` Dockerfile:8 DL3020 error: Use COPY instead of ADD for files and folders Dockerfile:9 DL3020 error: Use COPY instead of ADD for files and folders Dockerfile:11 DL3003 warning: Use WORKDIR to switch to a directory
ここでいくつか、エラーや警告を無視するようにしてみましょう。--ignore
オプションを使用します。
$ ./hadolint Dockerfile --ignore DL3006 --ignore DL3020 Dockerfile:3 DL3009 info: Delete the apt-get lists after installing something Dockerfile:3 SC2154 warning: node_ver is referenced but not assigned. Dockerfile:3 DL3015 info: Avoid additional packages by specifying `--no-install-recommends` Dockerfile:11 DL3003 warning: Use WORKDIR to switch to a directory
こんな感じですね。複数指定する場合は、--ignore
オプションを繰り返します。
次は、設定ファイルを使ってみます。
Haskell Dockerfile Linter / Configure
設定ファイルは、以下の順番で探していくようです。上にある方が優先ですね。
$PWD/.hadolint.yaml
$XDG_CONFIG_HOME/hadolint.yaml
$HOME/.config/hadolint.yaml
$HOME/.hadolint/hadolint.yaml
or$HOME/hadolint/config.yaml
$HOME/.hadolint.yaml
今回は、カレントディレクトリで.hadolint.yaml
ファイルを作成してみます。
.hadolint.yaml
ignored: - DL3006 - DL3020
これで、先ほど--ignore
オプションを指定したのと同じ効果になりました。
$ ./hadolint Dockerfile Dockerfile:3 DL3009 info: Delete the apt-get lists after installing something Dockerfile:3 SC2154 warning: node_ver is referenced but not assigned. Dockerfile:3 DL3015 info: Avoid additional packages by specifying `--no-install-recommends` Dockerfile:11 DL3003 warning: Use WORKDIR to switch to a directory
無視の設定は、インラインにも書けるようです。
Emacsに組み込む
最後に、Emacsに組み込んでみましょう。
こちらを見ると、Flycheckというものを使うようです。
Flycheckは、Emacsのオンザフライ構文チェック機能です。
Flycheck — Syntax checking for GNU Emacs — Flycheck 33-cvs documentation
hadolintとの組み合わせもサポートしています。
Supported Languages / Dockerfile
こちらを使う場合、hadolint
に対するパスを通しておきましょう。
あとはパッケージからflycheck
をインストールして、
Installation — Flycheck 33-cvs documentation
Emacsの設定ファイルに以下を追加します。
(use-package flycheck :ensure t :init (global-flycheck-mode))
推奨されているエクステンションということで、flycheck-color-mode-line
とflycheck-pos-tip
も入れておきました。
Recommended extensions — Flycheck 33-cvs documentation
flycheckとdocker-modeが有効になっていると、以下のようにhadolintと組み合わせて動かすことができます。
まとめ
hadolintを試してみました。けっこう簡単にDockerfile
の確認ができるので、導入しておくと良さそうですね。