🤖

🤖

:gijutsu_burogu:

Dockerfileを正しく書けるように指摘してくれる静的解析ツール「hadolint」

サイボウズ社の Docker 研修資料を見ていると Dockerfile の静的解析ツールが紹介されており、どれくらい解析するのか興味が湧いたので試してみました。

hadolint

エラーを事前に検知してくれたり、よりよい Dockerfile の書き方を指摘してくれます。 Haskell で描かれており、スターは 4000 程度です。 vscode とも連携することができます。

github.com

インストール

# Mac
$ brew install hadolint

# Windows
$ scoop install hadolint

# Docker
$ docker pull hadolint/hadolint

# Build locally
$ git clone https://github.com/hadolint/hadolint
$ cd hadolint
$ stack install

使い方

$ hadolint Dockerfile

# Dockerコンテナの場合
$ docker run --rm -i hadolint/hadolint < Dockerfile

VS Code との連携はこちら

marketplace.visualstudio.com

使用例

自分が書いた Dockerfile を静的解析してみます。

FROM golang as qiitaexporter-builder
RUN CGO_ENABLED=0 go get github.com/tenntenn/qiitaexporter

FROM golang as blogsync-builder
RUN CGO_ENABLED=0 go get github.com/x-motemen/blogsync

FROM alpine:latest

WORKDIR /Documents
COPY setup.sh blogsync.template /Documents
RUN chmod +x setup.sh

RUN mkdir -p ~/.config/blogsync
COPY config.yaml.tmp /root/.config/blogsync

RUN apk --no-cache add libintl && \
    apk --no-cache add --virtual .gettext gettext && \
    cp /usr/bin/envsubst /usr/local/bin/envsubst && \
    apk del .gettext

COPY --from=qiitaexporter-builder /go/bin/qiitaexporter /bin/qiitaexporter
COPY --from=blogsync-builder /go/bin/blogsync /bin/blogsync

ENTRYPOINT ./setup.sh
$ hadolint Dockerfile
Dockerfile:1 DL3006 Always tag the version of an image explicitly
Dockerfile:4 DL3006 Always tag the version of an image explicitly
Dockerfile:7 DL3007 Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag
Dockerfile:10 DL3021 COPY with more than 2 arguments requires the last argument to end with /
Dockerfile:16 DL3018 Pin versions in apk add. Instead of `apk add <package>` use `apk add <package>=<version>`
Dockerfile:24 DL3025 Use arguments JSON notation for CMD and ENTRYPOINT arguments

重複を除き 5 点も指摘されました。

  • イメージのバージョンを明示的にタグ付する。
  • イメージが更新される場合、latest を使用するとエラーが発生しやすくなる。バージョンをリリースタグに明示的に固定する。
  • 2 つ以上の引数を持つ COPY では、最後の引数が/で終わる。
  • apk addの時、バージョンを指定する。
  • CMD,ENTRYPOINT に JSON 表記を使う。

このような粒度で指摘されます。 apk など AlpineLinux 独自のものにも対応されています。 問題点の説明と修正方法が GitHub の wiki で説明されており親切です。

DL3006 · hadolint/hadolint Wiki · GitHub

設定ファイル

3 パターンの方法で設定ファイルを適応できます。

  • Dockerfileと同じディレクトリに.hadolint.yamlを設置
  • XDG_CONFIG_HOMEで指定するフォルダにhadolint.yamlを設置
  • 実行時にhadolint --config /path/to/config.yaml Dockerfileと指定
# .hadolint.yaml

# 無視するルール
ignored:
  - DL3000
  - SC1010

# 信頼するレジストリ(hadolintは信頼できないリポジトリからのイメージを使う場合、警告をだす)
trustedRegistries:
  - docker.io
  - my-company.com:5000

行単位で設定する

行単位で無視するルールを設定することもできます。

# hadolint ignore=DL3006
FROM ubuntu

# hadolint ignore=DL3003,SC1035
RUN cd /tmp && echo "hello!"

出力

なにも指摘がない場合には終了ステータス 0、指摘がある場合には終了ステータス 1 で終了します。

# &&はコマンドがステータス0の時のみ次へ
$ hadolint Dockerfile && echo NO_WARNING
NO_WARNING

# ||はコマンドがステータス1の時のみ次へ
$ hadolint Dockerfile || echo WARNING_EXIST
Dockerfile:9 DL3006 Always tag the version of an image explicitly
WARNING_EXIST

また、--formatで tty, json, checkstyle, codeclimate, codacy と出力形式を選択することができます。

これら出力に対して柔軟であるため、CI 等のワークフローにも組み込みやすくなっています。

おわりに

さまざまな種類の警告やエラーを出してくれるようで、これを導入すると Dockerfile が書きやすくなるなと感じました。 正しい書き方は教えてくれるものの、軽量化までは導いてくれません。 当たり前ですが、軽量化など Dockerfile の構成は別途学ぶ必要があります。 ひとまず、明日職場の Dockerfile 全部 hadolint 掛けてこようと思います。