G-gen の三浦です。当記事では、distroless という Google が提供するコンテナイメージを使って、イメージの脆弱性に対処する方法を紹介します。
distroless とは
distroless は、Google が提供する Docker コンテナのベースイメージです。不要なコンポーネントを削除した最小限のイメージであり、セキュリティ上のメリットがあります。主な特徴は以下の通りです。
特徴 | 内容 |
---|---|
攻撃対象領域を縮小 | OS の不要なファイルやバイナリを削除することで、攻撃対象領域を小さくします。 |
イメージサイズを削減 | イメージが軽量になり、デプロイや転送、コンテナ起動が高速になります。 |
セキュリティ向上 | 脆弱性の原因となる不要なコンポーネントを削除します。 |
distroless は、Ubuntu や Alpine などの汎用的なイメージと比較して、セキュリティに特化しています。
- 参考:distroless
検証の流れ
検証の流れは以下の通りです。
項番 | 項目 | 内容 |
---|---|---|
1 | 検証準備 | アプリケーションコードを準備し、必要な API を有効化します。 |
2 | Cloud Run のデプロイと脆弱性件数の確認 | まずは Python 3.12 slim を使用して Cloud Run にデプロイし、脆弱性があることを確認します。 |
3 | Dockerfile の変更(distroless 対応) | Dockerfile を distroless に対応するように変更します。 |
4 | Cloud Run の再デプロイと脆弱性件数の確認 | 再度 Cloud Run にデプロイし、一部の脆弱性が解消されたことを確認します。 |
検証準備
ディレクトリ構成
ディレクトリ構成は以下の通りです。
.├── Dockerfile├── main.py└── requirements.txt
Dockerfile
最初は、ベースイメージとして Python 3.12 slim を使用します。
# ベースイメージとして Python 3.12 slim を使用FROM python:3.12-slim# 環境変数を設定ENV PYTHONDONTWRITEBYTECODE=1ENV PYTHONUNBUFFERED=1# 作業ディレクトリを設定WORKDIR /app# 必要な依存関係をインストールRUN apt-get update && apt-get install -y \gcc \&& rm -rf /var/lib/apt/lists/*# 必要な Python パッケージをインストールCOPY requirements.txt /app/RUN pip install --no-cache-dir -r requirements.txt# アプリケーションコードをコンテナにコピーCOPY . /app# アプリケーションを実行CMD ["python", "main.py"]
main.py
以下の Python コードを使用します。
from flask import Flaskimport osapp = Flask(__name__)@app.route("/")def hello():return "Hello, Cloud Run!"if __name__ == "__main__":port = int(os.environ.get("PORT", 8080))app.run(host="0.0.0.0", port=port)
requirements.txt
flask
API の有効化
イメージの脆弱性スキャンを行う Artifact Analysis の API と Cloud Run 等の各種 API を有効化します。
gcloud services enable \artifactregistry.googleapis.com \containerscanning.googleapis.com \cloudbuild.googleapis.com \run.googleapis.com
検証
Cloud Run のデプロイと脆弱性件数の確認
環境変数を設定し、Cloud Run をデプロイします。
# 環境変数の設定export SERVICE_NAME=test-app # イメージと Cloud Run サービス名# Cloud Run のデプロイgcloud run deploy $SERVICE_NAME --source . \--region=asia-northeast1 \--allow-unauthenticated
Y を入力して Enter を実行すると、Artifact Registry のリポジトリ (cloud-run-source-deploy) が自動的に作成されます。
# 出力例Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in region [asia-northeast1] will be created.Do you want to continue (Y/n)? Y
デプロイが完了したら、Cloud Run の URL にアクセスし、Web ページが表示されることを確認します。
# 出力例Service [test-app] revision [test-app-XXXXXX-XXX] has been deployed and is serving 100 percent of traffic.Service URL: https://xxx-xxx-xxxxxx.asia-northeast1.run.app # Cloud Run の URL

Google Cloud コンソールにログインし、検索バーにArtifact Registry
と入力し、[Artifact Registry] を選択します。

[cloud-run-source-deploy] > [イメージ名] へ移動し、表示されている脆弱性の件数を確認します。例では、591 件の脆弱性を確認しています。

名前の部分を選択し、仮想サイズ
からイメージの容量を確認します。例では、138MB です。

[脆弱性]タブで、重大度に応じた件数などの詳細を確認できます。

Dockerfile の変更(distroless 対応)
Dockerfile を以下の通りに変更します。この変更では、ビルド用のイメージと実行用のイメージを分離し、distroless イメージを使用します。
# ビルド用イメージFROM python:3.12-slim AS builder# 必要なパッケージをインストールWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir --target=/app/libs -r requirements.txt# アプリケーションコードをコピーCOPY main.py /app/# 実行用の Distroless イメージFROM gcr.io/distroless/python3-debian12# アプリケーションファイルをコピーWORKDIR /appCOPY --from=builder /app/libs /app/libsCOPY --from=builder /app/main.py /app/main.py# Pythonのライブラリパスを設定ENV PYTHONPATH=/app/libs# Python実行環境をENTRYPOINTで指定し、アプリケーションファイルをCMDで指定ENTRYPOINT ["/usr/bin/python3"]CMD ["/app/main.py"]
これは Multi-stage builds という方式であり、ビルド用途と実行環境用途で異なるベースイメージを使用しています。
- 参考 : Multi-stage builds
- 参考 : distroless / README.md
Cloud Run の再デプロイと脆弱性件数の確認
初回と同様に Cloud Run をデプロイします。
gcloud run deploy $SERVICE_NAME --source . \--region=asia-northeast1 \--allow-unauthenticated
Cloud Run の URL にアクセスし、Web ページが表示されることを確認します。

イメージの脆弱性件数を確認します。例では 21 件となり、先ほどの 591 件と比較して大幅に減っています。

同様に容量を確認します。例では 20.7MB となり、先ほどの 138MB と比較して大幅に減っています。

脆弱性の詳細は以下の通りです。
