第4回:堅牢なCI/CDパイプライン構築へ:クリーンビルドと二層のセキュリティチェック
前回は、.gitlab-ci.ymlの基本とローカルビルドから単体テストまでを解説しました。もし前回の記事をまだご覧になっていない方は、以下のリンクからぜひご確認ください。
GitLab で実現する実践CI/CD パイプライン構築ガイド 第3回:.gitlab-ci.yml の基本とビルド・テスト
この回では、クリーンな環境でのビルドとビルドされたイメージの保存をCI/CDパイプラインに組み込む手順を解説します。
📄Python プロジェクトのサンプルの更新
CI/CD パイプラインでDocker イメージをビルドするため、プロジェクトのルートディレクトリにDockerfile を追加します。
├── main.py
├── tests/
│ └── test_main.py
├── .pre-commit-config.yaml
├── .gitlab-ci.yml
├── requirements.txt
└── Dockerfile
ここではDockerfile の詳しい説明は省略しますが、ファイルの中身は以下です。
# コンテナを小さく保つためにslim Python イメージ利用
FROM python:3.9-slim
# コンテナ内のワーキングディレクトリをセット
WORKDIR /app
# requirements ファイルをコピーして依存関係をインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションコードをコピー
COPY . .
# アプリケーションを実行するコマンド
CMD ["python", "main.py"]
🚀クリーンビルドとContainer Registry への保存
プロジェクト構成が整ったので、いよいよCI/CD パイプラインにDocker イメージのビルドとプッシュを組み込みます。このプロセスを「クリーンビルド」と呼び、ローカルキャッシュに依存しない、信頼性の高いビルドを保証します。
.gitlab-ci.yml のステージにdocker_build を、ジョブにbuild_and_push_image を追加します。このジョブは単体テストが成功した後に実行されます。
stages:
# 既存の単体テスト
- build_and_test
# 追加するDokcerイメージのビルドとプッシュ
- docker_build
# 既存の単体テストジョブ(省略)
run_unit_tests:
# ...省略...
# ---------------- Build Stage ----------------
build_and_push_image:
stage: docker_build
# Dockerクライアントを持つイメージを使用
image: docker:latest
# Dockerコマンド実行のため、Dockerデーモンを提供するサービスを起動 (Docker-in-Docker)
services:
- docker:dind
script:
# Container Registryへのログイン処理
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
# Dockerイメージのビルド
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG .
# Dockerイメージのプッシュ
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
・Docker-in-Dockerによるクリーンビルド
通常のCIジョブは、ホスト環境から完全に隔離されたコンテナ内で実行されますが、このままでは、Dockerイメージのビルドに必要なDockerデーモン(Dockerエンジン本体)が利用できません。そのため、Dockerクライアント(dockerコマンド)を実行するための環境image: docker:latestを利用します。そしてジョブの実行中に、独立したDocker デーモンコンテナをサービスとして起動します(DinD)。つまり、真新しいまっさらなDockerコンテナの中でDockerビルドを行いDockerイメージを生成することになります。ローカル(開発者のPC)で残ったキャッシュや設定の影響を一切受けず「クリーンビルド」の実現が可能となるのです。
・GitLab の組み込み環境変数による認証
GitLab のContainer Registry にイメージをプッシュ(書き込み)する操作には認証が必要です。しかし、認証情報を直接コードに書くのはセキュリティ上のリスクとなります。そこで、GitLab が予め用意している組み込み環境変数を利用します。
$CI_REGISTRY: プロジェクトのContainer Registry のURL。
$CI_REGISTRY_USER / $CI_REGISTRY_PASSWORD: GitLab がこのジョブのために一時的に発行する、プッシュ権限を持つユーザー名とパスワード。
script の最初の行では、これらの変数を使ってDocker クライアントに認証情報を渡しています。これにより、次のdocker push コマンドが認証エラーにならず実行できます。
・トレーサビリティのためのタグ付け
$CI_REGISTRY_IMAGE: プッシュされたコンテナイメージのレジストリパス。
$CI_COMMIT_REF_SLUG: ビルドしているブランチ名やタグ名。
これら環境変数を利用することでブランチでもタグでもコードの状態とイメージの内容が一対一で対応し、人間の目で識別しやすいイメージ名でContainer Registry に保存されます。
例えば、$CI_REGISTRY_IMAGE が"my-registry/my-app"、ブランチ名が"fix-bug-memory-leak" だとするとContainer Registry上のイメージ名は"my-registry/my-app:fix-bug-memory-leak" となります。
▶️ GitLab パイプラインの実行結果の確認
前回と同じ方法で新しい.gitlab-ci.ymlをGitリポジトリにプッシュし、パイプラインの結果を確認します。
$ git add .
$ git commit -m "feat: Add Dockerfile and enable CI/CD build step"
$ git push -u origin main
プッシュ後、GitLab のWeb ページで該当のプロジェクトの左メニューから「Build -> Pipelines」 を選択します。最新のパイプラインは、定義したbuild_and_test とdocker_build の2つのステージが順に実行され、Stages 欄で両方ともpassed(成功)していることが確認できます。
最後に、左のメニューから「Deploy -> Container registry」 を選択します。クリーンビルドで作成されたイメージが保存されていることが確認できます。ここでブランチ名(今回はブランチを作成していないためデフォルトのmainブランチ)のタグがついたイメージが保存されていることが確認できます。
✅ まとめと次回予告
今回は、CI/CDパイプラインをより堅牢にするための最初のステップとして、クリーンビルドとコンテナイメージの保存を実装しました。これで、次のセキュリティステージに進むための、テスト済みで確かな出所のDockerイメージが用意できました。
次回は、今回保存したコンテナイメージを利用して、デプロイ準備としてセキュリティを最終的に保証するステップに進みます。

