hadolintでDockerfileを静的解析する方法|ベストプラクティス違反の検出とCI組み込みの実践手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Docker > hadolintでDockerfileを静的解析する方法|ベストプラクティス違反の検出とCI組み込みの実践手順
「Dockerfileはひとまず動いている。でもこれで本当に良い書き方なのか、誰も指摘してくれない」
そんな不安を抱えながらDockerfileを書き続けていないでしょうか。apt-getのバージョン固定忘れ、キャッシュ層の壊し方、rootのままの実行など、動作はするのに後々トラブルの種になる書き方は、レビューだけでは見落とされがちです。

この記事では、OSSのDockerfile静的解析ツール hadolint(ハドリント)を使って、ベストプラクティス違反を機械的に検出する方法を解説します。インストールから基本実行、ルールの読み方、.hadolint.yamlでの除外設定、GitHub ActionsへのCI組み込みまで一通り説明します。動作確認環境:Rocky Linux 9.4 / Ubuntu 24.04 LTS(hadolint v2.12.0)。

この記事のポイント

・docker run --rm -i hadolint/hadolint < Dockerfile でインストール不要のまま即座に解析できる
・DLxxxxのルール番号ごとにバージョン固定漏れやapt-getキャッシュ残存を検出できる
・.hadolint.yamlでプロジェクトの事情に合わせたルール除外・重要度変更ができる
・GitHub Actionsに組み込めばDockerfile変更のたびに自動でレビューされる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜhadolintが必要なのか|Dockerfileレビューの限界

hadolintはHaskell製のOSSで、Dockerfileの記述内容を静的に解析し、Docker公式のベストプラクティスに反する書き方を検出するリンター(構文チェッカー)です。RUN命令のシェルスクリプト部分は内部でShellCheckを呼び出して解析するため、Dockerfileの命令とシェルコマンドの両方をまとめてチェックできます。

なぜ人力レビューだけでは不十分なのか

Dockerfileは「動けば正解」に見えてしまう性質があります。次のような書き方は、ビルドもコンテナ起動も成功するため、レビューで指摘されない限り気づかれません。

apt-get install curl(バージョン固定なし)→ ビルドタイミングによって挙動が変わる
apt-get install後にrm -rf /var/lib/apt/lists/*を忘れる → イメージが無駄に肥大化する
FROM ubuntu(タグ省略)→ latestタグが指す中身がいつの間にか変わる
・USER命令なしでrootのまま実行 → コンテナ脱出時の被害が広がる
・RUN内のパイプでset -o pipefailを付けない → 途中のコマンドが失敗してもビルドが成功してしまう

これらはtrivyのようなイメージスキャナーでは検出できません。trivyは「ビルド後のイメージに含まれるCVE(既知の脆弱性)」を見つけるツールであるのに対し、hadolintは「Dockerfileのソースコードそのものの書き方」を見るツールだからです。両者は対象がまったく異なり、Dockerfile作成の初期段階で使うのがhadolint、ビルド後の継続的な脆弱性監視で使うのがtrivyという役割分担になります。

hadolintのインストールと初回実行

1. Dockerイメージで実行する(インストール不要・推奨)

hadolintはDocker Hub公式イメージが提供されているため、ローカルにバイナリをインストールしなくても実行できます。CI環境でもこの方式が最もシンプルです。

# カレントディレクトリのDockerfileを標準入力で渡す $ docker run --rm -i hadolint/hadolint < Dockerfile Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly Dockerfile:3 DL3008 warning: Pin versions in apt-get install. Instead of `apt-get install ` use `apt-get install =` 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 DL3002 warning: Last USER should not be root

各行は「ファイル名:行番号 ルールID 重要度: メッセージ」の形式で出力されます。重要度はerror/warning/info/styleの4段階です。

2. バイナリを直接インストールする(Linux)

CIでdocker-in-dockerを避けたい場合や、エディタ連携をしたい場合はバイナリ版を使います。

# 最新リリースのバイナリを取得する(x86_64 Linux向け) $ sudo curl -sL -o /usr/local/bin/hadolint \ https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 $ sudo chmod 755 /usr/local/bin/hadolint # バージョン確認 $ hadolint --version Haskell Dockerfile Linter 2.12.0

3. Dockerfileを直接指定して実行する

# ファイルパスを直接渡す(標準入力より読みやすい出力) $ hadolint Dockerfile Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly Dockerfile:5 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it

exit codeは検出した問題がerrorレベルを含む場合に1、それ以外(warning/info/styleのみ、または問題なし)は0を返します。この挙動はCIでのビルド失敗判定に直結するため、後述の--failure-thresholdで調整します。

主要ルールの読み方と修正パターン

hadolintは200近いルールを持ちますが、現場でまず対応すべきものは限られています。頻出ルールを実例で見ていきます。

1. DL3006・DL3007|イメージタグの明示

# 修正前(DL3006違反) FROM ubuntu # 修正後 FROM ubuntu:22.04

タグを省略するとlatestが暗黙的に使われますが、latestが指す実体は時間とともに変わります。「先週まで動いていたビルドが急に失敗する」事故の典型的な原因です。

2. DL3008・DL3009・DL3015|apt-getの3点セット

Debian/Ubuntu系ベースイメージで最も頻出するのがこの3つです。

# 修正前(DL3008・DL3009・DL3015すべて違反) RUN apt-get update && apt-get install -y curl vim # 修正後(バージョン固定・キャッシュ削除・推奨パッケージ除外) RUN apt-get update \ && apt-get install -y --no-install-recommends \ curl=8.5.0-2ubuntu10.4 \ vim=2:9.1.0016-1ubuntu7 \ && rm -rf /var/lib/apt/lists/*

・DL3008:パッケージのバージョンを固定しないと、再ビルド時に意図しないバージョンが入り再現性が失われる
・DL3009:apt-get install後に/var/lib/apt/lists/*を消さないと、パッケージ索引がレイヤーに残りイメージが肥大化する
・DL3015:--no-install-recommendsを付けないと、不要な推奨パッケージまで一緒に入り攻撃対象領域が広がる

3. DL3002|rootユーザーでの実行

# 修正前(DL3002違反:最終ステージがrootのまま) FROM node:20-slim WORKDIR /app COPY . . CMD ["node", "server.js"] # 修正後 FROM node:20-slim WORKDIR /app COPY . . RUN chown -R node:node /app USER node CMD ["node", "server.js"]

USER命令がないと、コンテナ内のプロセスはrootで動作します。コンテナ脱出の脆弱性が発見された場合、rootのまま動いているコンテナほど被害が大きくなります。

4. DL4006|パイプのpipefail未設定

# 修正前(DL4006違反:curlが失敗してもビルドが成功してしまう) RUN curl -sSL https://example.com/install.sh | bash # 修正後 SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN curl -sSL https://example.com/install.sh | bash

デフォルトのシェルはパイプの最後のコマンドの終了コードしか見ません。curlがネットワークエラーで失敗しても、後続のbashが正常終了すればRUN全体は成功扱いになり、壊れたイメージがそのままビルドされてしまいます。

.hadolint.yamlでプロジェクトに合わせてルールを調整する

すべてのルールをそのまま適用すると、社内の事情(信頼済みの内部レジストリを使う、特定のディストロで--no-install-recommendsが使えない等)に合わずノイズになる場合があります。プロジェクトルートに.hadolint.yamlを置くと、実行時に自動で読み込まれます。

# .hadolint.yaml # 完全に無視するルール(理由をコメントで残す) ignored: - DL3008 # 社内ベースイメージは自動更新運用のためバージョン固定を採用しない - DL3059 # 複数RUNの統合は可読性を優先し許容する # 重要度を個別に上書きする override: error: - DL3002 # rootユーザー実行は必ずビルド失敗にする warning: - DL3015 # 社内の信頼済みレジストリを登録する(FROM命令のドメインチェックに使用) trustedRegistries: - registry.internal.example.com - docker.io

ignoredは完全無視、overrideは重要度の変更(infoをerrorに引き上げてCI必須化する、逆に緩めるなど)に使い分けます。安易にignoredへ入れるのではなく、DL3002のようなセキュリティ直結のルールはむしろoverrideでerror昇格させ、CIで確実に止める運用が現実的です。

CI/CDパイプライン(GitHub Actions)へのhadolint組み込み

公式のGitHub Actions hadolint/hadolint-actionを使うと、Dockerfile変更を含むPRのたびに自動でチェックできます。

# .github/workflows/hadolint.yml name: Dockerfile Lint on: push: paths: - '**/Dockerfile' pull_request: paths: - '**/Dockerfile' jobs: hadolint: name: hadolint runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run hadolint uses: hadolint/hadolint-action@v3.1.0 with: dockerfile: Dockerfile config: .hadolint.yaml failure-threshold: error

設定のポイントをまとめます。

paths: ['**/Dockerfile']でDockerfile変更時のみジョブを起動し、無関係な変更でのCI消費を抑える
failure-threshold: errorを指定すると、errorレベルの違反のみでCIを失敗させ、warning/info/styleは表示のみに留める段階的導入ができる
config: .hadolint.yamlでローカル実行時と同じルール設定をCIでも適用し、手元とCIの結果を一致させる
・SARIF出力オプションと組み合わせれば、trivyと同様にGitHub Securityタブへ結果を集約することもできる

よくあるエラーとトラブルシュート

hadolintを導入すると、以下のような場面でつまずくことがあります。

1. 「Couldn't parse Dockerfile」が出た場合

# エラー例 :1:1 error: Couldn't parse Dockerfile unexpected '#' expecting instruction # 原因: BuildKitのsyntax指定行の書式ミス # 修正前(コメントとして認識されずパースエラー) #syntax=docker/dockerfile:1.7 # 修正後(先頭に空白なし・#の直後にsyntaxが必要) # syntax=docker/dockerfile:1.7

hadolintのパーサーはDockerfileのBNF構文に厳密なため、コメント記法のわずかな崩れでも解析に失敗します。

2. 大量の指摘で最初から心が折れる場合

既存の古いDockerfileにいきなり導入すると、数十件の指摘が一度に出ることがあります。全件を即座に直そうとせず、段階的に導入するのが現実的です。

ステップ1: failure-threshold: errorでerrorのみをCIブロック対象にする
ステップ2: 既存の指摘は.hadolint.yamlのignoredに一旦登録し、新規追加分だけを厳格にチェックする
ステップ3: ignoredに入れたルールを月次でレビューし、優先度の高いものから解消する
ステップ4: 全ルールを解消できたらignoredを空にし、標準ルールセットへ移行する

3. 特定行だけルールを無視したい場合

プロジェクト全体ではなく、特定の1行だけ意図的にルールを外したいケースもあります。

# その行の直前にDL番号を指定してインライン無視する # hadolint ignore=DL3008 RUN apt-get update && apt-get install -y curl

【注意】インライン無視は理由が残らず属人化しやすいため、無視した意図をコメントで併記し、なぜバージョン固定できないのか(内部ミラーの都合、頻繁な更新が必要等)を明記してください。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、hadolintによる静的解析を含むDockerfile設計のベストプラクティスを基礎から習得できる講座を用意しています。
Docker実践講座(linuxmaster.jp)の詳細はこちら >>

本記事のまとめ

hadolintを使ったDockerfileの静的解析と、.hadolint.yamlでのルール調整、CI組み込みまでを解説しました。

やりたいこと コマンド/設定
インストールせずに解析する docker run --rm -i hadolint/hadolint < Dockerfile
ファイルを直接指定して解析する hadolint Dockerfile
特定ルールを無視する .hadolint.yamlignoredに追加
重要度を変更する .hadolint.yamloverrideに追加
特定行だけ無視する # hadolint ignore=DLxxxxを行の直前に記述
CIに組み込む hadolint/hadolint-actionを使用する
・hadolintはDockerfileの「書き方」を、trivyはビルド後イメージの「中身の脆弱性」をチェックする、対象の異なるツールとして両方を運用に組み込むのが理想的
・DL3002(rootユーザー実行)のようなセキュリティ直結のルールは、.hadolint.yamlのoverrideでerror昇格させCIで確実に止める
・既存プロジェクトへの導入は全件対応を目指さず、failure-threshold設定と段階的なignored解消で無理なく浸透させるのが現実的

Dockerfileのベストプラクティスをさらに深く学びたい方は、Docker実践講座(linuxmaster.jp)をご覧ください。hadolintによる静的解析からマルチステージビルド、セキュリティ設計まで、現役エンジニアが現場目線で教えるハンズオン形式で体系的に習得できます。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。