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=1 ENV 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 Flask import os app = 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 /app COPY 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 /app COPY --from=builder /app/libs /app/libs COPY --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 と比較して大幅に減っています。

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