HadolintでDockerfileをクリーンに:イメージの「肥大化」と脆弱性を防ぐ

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

なぜDockerfileの「健康診断」が必要なのか?

Dockerを使い始めたばかりの頃、私は単純に考えていました。docker buildでイメージができ、docker runでアプリが動けばそれでOKだと。正直なところ、当時はStackOverflowや大昔のブログ記事からコードをコピペして、Dockerfileを「適当に」作っていました。その結果、単純なNode.jsアプリなのにイメージサイズが1.2GBにもなり、ビルドに5分以上かかり、自分でも気づかないうちにセキュリティ脆弱性が山積みになっていました。

転機が訪れたのは、システム全体をDocker Compose v1からv2にアップグレードした時でした。この過程で、古びたDockerfileたちの実態が浮き彫りになったのです。多くのイメージがいまだに古いベースイメージ(Bullseye/BookwormではなくDebian Busterなど)を使用していたり、パッケージインストール後のキャッシュ削除を忘れていたり、最悪なことにコンテナをroot権限で無造作に実行していたりすることに気づきました。そこで頼ったのがHadolintです。

Hadolintは単なる構文チェックツールではありません。Dockerの「ベストプラクティス」に従っているかどうか、コードを一行ずつ精査してくれます。最大の魅力は、RUN命令内のシェルコマンドのミスをキャッチするためにShellcheckが組み込まれている点です。HadolintなしでDockerfileを書くのは、ESLintなしでJavaScriptを書くようなものです。本番環境(Production)で自らトラブルを招いているようなものです。

Hadolintを導入する4つの大きなメリット

  • イメージの軽量化:/var/lib/apt/lists/*のキャッシュ削除や、マルチステージビルド(Multi-stage build)の使用を促してくれます。私はHadolintの指摘に従って修正しただけで、あるPythonイメージを850MBから120MBまで削減できました。
  • セキュリティ脆弱性の修正:root権限の使用や、ハッカーの標的になりやすい不要なパッケージのインストールに対して即座に警告を出してくれます。
  • スタイルの統一:チーム全体で共通の標準に従ってDockerfileを書くようになり、人によって書き方がバラバラになるのを防げます。
  • 初歩的なミスの防止:latestタグの使用(ステージングでは動くのに本番で突然動かなくなる致命的なミス)などを検出してくれます。

Hadolintのインストール方法

Hadolintのインストールは、コーヒーを淹れ終わるよりも早く完了します。

1. Docker経由ですぐに実行する

ローカルマシンにインストールするのが面倒ですか?Docker自体を使ってDockerfileをチェックしましょう:

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

2. MacOS/Linuxに直接インストールする

Macユーザーならbrewで簡単に入手できます:

brew install hadolint

Linuxの場合は、GitHubのリリースページからバイナリを直接ダウンロードするだけです(わずか2行のコマンド):

sudo wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
sudo chmod +x /usr/local/bin/hadolint

3. VS Code用の「最強兵器」

ジュニアエンジニアの方には、VS Codeの拡張機能(Extension)版Hadolintを今すぐインストールすることをお勧めします。入力中にリアルタイムで不適切な箇所を赤線で示してくれます。まるで経験豊富なシニアエンジニアが隣でアドバイスしてくれているような感覚で、分厚いドキュメントを読み込まなくても、みるみるスキルアップできます。

設定と実践的な使い方

Hadolintは、DL(Docker Lint)またはSC(ShellCheck)で始まるエラーコードを返します。リストが長くても慌てずに、一つずつ解決していきましょう。

最初のDockerfileチェック

作成したファイルに対して以下のコマンドを実行してみてください:

hadolint Dockerfile

もしDL3008 warning: Pin versions in apt-get installというメッセージが表示されたら、パッケージのバージョンを明示(例:python3=3.10.6-1)する必要があるという意味です。

好みに合わせたカスタマイズ

小規模なプロジェクトでは、一部のルールが厳しすぎると感じることがあります。その場合は、ルートディレクトリに.hadolint.yamlファイルを作成して、特定のルールを無効化できます:

# .hadolint.yaml
ignored:
  - DL3008 # 作業効率化のためパッケージバージョンの固定要件を無視
  - DL3015 # --no-install-recommendsの推奨を非表示に

override:
  error:
    - DL3001 # この警告をエラーとして扱い、修正を必須にする

忘れてはならない3つの「黄金律」

私の経験上、Hadolintが防いでくれる最も重要なポイントは以下の通りです:

  1. バージョンを固定する:FROM node:latestは絶対に避け、FROM node:18.16.0-alpineのように指定しましょう。
  2. 現場をクリーンに保つ:レイヤーの肥大化を防ぐため、apt-get installとキャッシュの削除は常に一つのRUN命令にまとめましょう。
  3. WORKDIRを優先する:RUN cd /appではなく、WORKDIR /appを使いましょう。これにより、以降の命令が常に正しいディレクトリで実行されることが保証されます。

自動化:HadolintをCI/CDに組み込む

ビルドが失敗してから直すのではなく、Hadolintを自動チェックのゲートキーパーにしましょう。

GitHub Actionsとの連携

ワークフローに以下の数行を追加するだけで、チーム内の誰かが「質の低い」Dockerfileをプッシュしても、システムが即座に拒否してくれます。

name: Lint Dockerfile
on: [push, pull_request]
jobs:
  hadolint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Hadolint
        uses: hadolint/[email protected]
        with:
          dockerfile: Dockerfile

Hadolintを導入して以来、イメージサイズが異常に大きくなったり、シェルスクリプト起因の些細なエラーに悩まされたりすることがなくなりました。クリーンなイメージはビルドが速く、デプロイもスムーズで、上司からの評価も「プロフェッショナルな仕事」として向上するでしょう。ぜひ、あなたの古いDockerfileをチェックしてみてください。真っ赤な警告の多さに驚くはずですよ!

Share: