そんな不安を抱えながら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変更のたびに自動でレビューされる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ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
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
主要ルールの読み方と修正パターン
hadolintは200近いルールを持ちますが、現場でまず対応すべきものは限られています。頻出ルールを実例で見ていきます。1. DL3006・DL3007|イメージタグの明示
# 修正前(DL3006違反) FROM ubuntu # 修正後 FROM ubuntu:22.04
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/*
・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"]
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 Actionshadolint/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
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.yamlのignoredに追加 |
| 重要度を変更する | .hadolint.yamlのoverrideに追加 |
| 特定行だけ無視する | # hadolint ignore=DLxxxxを行の直前に記述 |
| CIに組み込む | hadolint/hadolint-actionを使用する |
・DL3002(rootユーザー実行)のようなセキュリティ直結のルールは、.hadolint.yamlのoverrideでerror昇格させCIで確実に止める
・既存プロジェクトへの導入は全件対応を目指さず、failure-threshold設定と段階的なignored解消で無理なく浸透させるのが現実的
Dockerfileのベストプラクティスをさらに深く学びたい方は、Docker実践講座(linuxmaster.jp)をご覧ください。hadolintによる静的解析からマルチステージビルド、セキュリティ設計まで、現役エンジニアが現場目線で教えるハンズオン形式で体系的に習得できます。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Dockerのuserns-remap設計|user namespaceリマップでコンテナのroot権限をホストから分離する方法
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます