とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

HadolintでDockerfileを静的解析

Dockerfileを書くとき、ベストプラクティスに則っているか、RUNの実行に問題はないかなど、意識していますか?コンテナイメージのサイズは目に見えるので意識しやすのですが、Dockerfileがベストプラクティスに則っていなくても警告なく動いてしまうので、ついつい意識が逸れてしまいます。ベストプラクティスを覚えていくのもなかなかに大変です。そこで今回はDockerfileの静的解析ツール、Hadolintを使える環境を用意していきたいと思います。

github.com

Hadolintってなに?

Haskell製のDockerfile静的解析ツールです。Docker公式で公開しているベストプラクティスに則っているかをチェックすることができます。RUNの中身はShellCheckで静的解析してくれるので、ビルドしてみたらRUNでコケた、なんてことも減らせると思います。

docs.docker.com

インストール

macOSならHomebrewでインストールできます。

brew install hadolint

GitHubのReleasesからバイナリをダウンロードすることも可能です。

コマンドをインストールしたくない方は、Dockerイメージが公開されているので、そちらを使うといいと思います。
標準入力からDockerfileを渡すことで静的解析することができます。

docker run --rm -i hadolint/hadolint < /path/to/Dockerfile

or

cat /path/to/Dockerfile | docker run --rm -i hadolint/hadolint

HadolintのDockerfileを確認してみると、CMD ["/bin/hadolint", "-"]でコマンドを実行していることがわかります。 このままコマンドラインオプションを渡すとCMDが上書きされhadolintが実行されません。
オプションを指定して静的解析を実行する場合はhadolint/hadolint hadolint [options]のようにコマンド部分も指定する必要がありました。

Webアプリも公開されているので、こちらを試してみるのもいいかもしれません。

使ってみる

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] [-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 |
                           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`
  -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)

サンプルプロジェクトを用意しました。警告の出るDockerfileや、CIの設定が入っているので、forkして手元にcloneしておいてください。

GitHub - VirtualTech-DevOps/hadolint-sample

それではさっそく、実行してみましょう。

hadolint Dockerfile

以下のようなメッセージが出たと思います。

Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly
Dockerfile:3 DL3027 warning: Do not use apt as it is meant to be a end-user tool, use apt-get or apt-cache instead
Dockerfile:4 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.
Dockerfile:4 DL3027 warning: Do not use apt as it is meant to be a end-user tool, use apt-get or apt-cache instead
Dockerfile:6 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.
Dockerfile:7 DL3000 error: Use absolute WORKDIR

左から、ファイル名、問題のあった行番号、エラーコード、エラーレベル、メッセージの順で出力されています。

DLやSCから始まるエラーコードがリポジトリのWikiにまとまっています。エラーの詳細と、どう記述したらよかったのかが書いてありますので、こちらを確認しながら、修正していくのが楽かと思います。

エラーや警告が出ていても、アプリの作りや手順の都合で、どうしても直せない状況はよくあると思います。そういう場合は--ignore=エラーコードで無視することができます。

hadolint Dockerfile --ignore=DL3059

先ほど出ていたDL3059 infoが報告されなくなったと思います。ただし、コマンドラインオプションからエラーコードで抑制してしまうと、そのエラーについて、すべての警告が無視されてしまいます。 Dockerfile内の無視してほしい行にのみignoreを追加することで、特定の行のみ無視することができます。
先ほどのDockerfileを以下のように修正し、hadolintを実行してみてください。

# hadolint ignore=DL3059
RUN mkdir tmp

Dockerfile:6 DL3059 info: Multiple consecutiveRUNinstructions. Consider consolidation.の報告のみが、出力されなくなったと思います。

このように、Dockerfile内にルールを記述できるので、設定による環境差が生まれづらくなります。また、コミットメッセージになぜ無視するのかを記述できるため、他の人への共有も楽になるのではないでしょうか。

インテグレーション

Hadolintは様々なアプリや、サービスから使用することが可能です。インテグレーションについて、公式で以下のようなドキュメントが公開されています。

hadolint/docs/INTEGRATION.md at master · hadolint/hadolint · GitHub

Visual Studio Codeで静的解析

弊社ではVisual Studio Code(以下VS Code)の使用を推奨していますので、VS CodeのExtensionをインストールしてみたいと思います。

以下のページからインストール画面に飛ぶか、VS CodeのMarketplace検索窓からhadolintを検索し、インストールしてみてください。

marketplace.visualstudio.com

インストールするとすぐに使えるようになると思います。 もしうまく動かない場合は、Hadolintを設置したディレクトリが$PATHに追加されているか確認してみてください。VS CodeからHadolintのバイナリを実行しているので、パスが通っていないと実行できません。

それではVS CodeでDockerfileを開いてみましょう。
問題のある行にマウスオーバーすると・・・

問題点と改善方法がポップアップで報告されます。エラーコードはWikiへのリンクになっているので、そこからWikiに飛ぶのもいいでしょう。

CircleCIで静的解析

CircleCIの設定はこちらの記事を参考にしてみてください。

devops-blog.virtualtech.jp

先ほどforkしたサンプルプロジェクトには既にCircleCIの設定ファイルが含まれています。CircleCIにプロジェクトを追加した時点でJobが実行されたと思います。もし実行されなければ、適当なコミットをPushしてみてください。

実際にJobが実行されると以下のようになります。

ベストプラクティスに則っていないDockerfileがPushされると、自動でHadolintが実行され、警告を出してJobがFailedしました。

まとめ

Dockerfile内にignoreを記述できるので、仕方なく、といった部分も吸収できるのはいいなと思いました。ただ、これを多用すると見通しが悪くなり、セキュリティもどんどんボロボロになっていくので、ignoreを増やすより、根本解決したほうがよさそうです。

エディタから静的解析を行うことで、ベストプラクティスに則ったコードが書きやすくなります。また、ベストプラクティスに則っていないコードをPushしないことで、手戻りも発生しにくくなるとのではないでしょうか。

CIで静的解析できればPull requestsで弾くことができるので、リポジトリを綺麗な状態に保つことができます。Dockerfileの構成についてレビューの手間も省けるでしょう。

エディタもCIも、どちらも導入に大きなデメリットはないと思います、是非活用してみてください。