Kaniko: ルート権限不要で「クリーン」かつセキュアなDockerイメージをビルドする方法

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

なぜDocker-in-Dockerはもはや最良の選択肢ではないのか?

Kubernetes上でCI/CDを導入し始めた初期の頃, Docker-in-Docker (DinD) は多くの人にとってデフォルトの選択肢でした。しかし、DinDを動作させるには、コンテナに privileged: true 権限を付与する必要があります。これは重大なセキュリティ上の脆弱性となります。もし攻撃者がビルドコンテナを乗っ取った場合、コンテナを脱出してクラスターの物理ノード全体を制御される恐れがあるからです。

大企業のセキュリティチームであれば、ルート権限や /var/run/docker.sock へのアクセスを要求した時点で、即座に却下されるでしょう。Kanikoは、この課題を根本的に解決するために誕生しました。これはGoogleが開発したオープンソースツールで、Dockerデーモンを必要とせず、ユーザ空間(userspace)でDockerfileからイメージをビルドすることを可能にします。

50以上のマイクロサービスを運用している私の実環境での経験では、Kanikoに移行したことでシステムがより安全になっただけでなく、RAM/CPUリソースを35〜40%削減できました。その理由は、各ビルドジョブごとにバックグラウンドで無駄なDockerデーモンを稼働させ続ける必要がなくなったためです。

Kanikoの導入:煩わしいインストールは不要

Kanikoを使用するために apt-get install を実行する必要はありません。このツールは、実行可能なDockerイメージ(executor)としてパッケージ化されています。パイプライン内でこのイメージを呼び出すだけで、すぐにビルドを開始できます。

私は通常、gcr.io/kaniko-project/executor:debug イメージを使用することをお勧めしています。この debug バージョンにはシェル(/busybox/sh など)が組み込まれています。これは、パイプラインの実行中に補助スクリプトを挿入したり、ファイルシステムを調査したりする必要がある場合に非常に便利です。

ローカル環境でKanikoをクイックテストする

サーバーにデプロイする前に、以下のコマンドを使用してローカルでKanikoの仕組みを試してみましょう。

docker run \
    -v $(pwd):/workspace \
    -v ~/.docker/config.json:/kaniko/.docker/config.json \
    gcr.io/kaniko-project/executor:latest \
    --dockerfile=Dockerfile \
    --context=dir:///workspace \
    --destination=your-registry.com/your-image:v1

GitLab CIへの統合

GitLab CIの設定は非常にシンプルです。複雑なDockerサービスを追加でセットアップする必要はありません。

build-image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - /kaniko/executor \
      --context "${CI_PROJECT_DIR}" \
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile" \
      --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"

ビルドパフォーマンスを最適化して時間を短縮する

キャッシュを設定しない場合、パイプラインが実行されるたびにすべてのレイヤーを再ダウンロードする必要があり、非常に時間がかかります。node_modules フォルダが重くなりがちなNode.jsプロジェクトなどでは、最適化が不可欠です。

1. コンテナレジストリでの認証

Kanikoは、Docker Hub、GCR、またはECRにイメージをプッシュする権限が必要です。Kanikoは /kaniko/.docker/ ディレクトリ内の config.json ファイルを参照します。ログイン情報を安全に保つために、パイプラインのスクリプト内でこのファイルを動的に作成できます。

{
  "auths": {
    "https://index.docker.io/v1/": {
      "auth": "base64_encoded_token"
    }
  }
}

2. レイヤーキャッシュの有効化

これは、私がビルド時間を7分から2分未満に短縮したテクニックです。Kanikoは、正常にビルドされたレイヤーを専用のレジストリにキャッシュとして保存します。

/kaniko/executor \
  --context "${CI_PROJECT_DIR}" \
  --cache=true \
  --cache-repo=your-registry.com/kaniko-cache \
  --destination=your-registry.com/your-app:latest

--cache=true を有効にすると、Kanikoは現在のレイヤーがすでに cache-repo に存在するかどうかを確認します。存在する場合、Dockerfileのコマンドを再実行する代わりに、キャッシュをダウンロードします。

トラブルシューティングと実践的なTips

強力なツールである一方で、Kanikoは特有のエラーで初心者を戸惑わせることがあります。

  • メモリ不足 (OOM) エラー: Kanikoはスナップショットを作成するためにファイルシステム全体をメモリ上に展開します。大きなデータ(2GB以上)を含むイメージをビルドする場合は、ビルドを実行するPodのRAM制限を最低でも4GB以上に増やしてください。
  • 権限拒否 (Permission Denied) エラー: 通常、config.json ファイルの配置場所が間違っていることが原因です。絶対パスが /kaniko/.docker/config.json になっているか再確認してください。
  • 詳細なデバッグ: -v=debug フラグを追加して、Kanikoがファイルシステムをスキャンする方法を詳細に確認します。これにより、どのステップでイメージが異常に重くなっているのかを特定できます。

Kubernetesユーザ向けのちょっとしたコツ:--digest-file=/dev/termination-log パラメータを使用してください。これにより、パイプラインはイメージの正確なSHA値をキャプチャできます。これは、一般的でありながら危険な latest タグの使用によるデプロイミスを防ぐのに役立ちます。

Kanikoに慣れるには、最初は少し設定に時間がかかるかもしれません。しかし、セキュリティの安心感とリソース効率の向上は、あらゆる本番システムにおいて十分に価値のある投資となります。

Share: