更新日:2025年6月11日
21分で読めます
GitLabを活用して堅牢な継続的デプロイパイプラインを構築する方法をご紹介します。具体的な手順や例、ベストプラクティスを通して段階的にプロセスを理解できます。
継続的デプロイは、チームがより迅速かつ高い信頼性で価値を生み出せる画期的な手法です。しかし、GitOpsやKubernetesを用いたコンテナオーケストレーション、動的環境などの高度なデプロイワークフローに取り組むのは、継続的デプロイの導入を始めたばかりのチームにとってはハードルが高いと感じられるかもしれません。
GitLabでは、シームレスかつスケーラブルなデリバリーを実現することに注力しています。チームが基本に集中できるようにすることで、時間をかけてより複雑な戦略へと発展できる、強固な基盤を構築できます。このガイドでは、GitLabを活用した継続的デプロイの導入に必要な基本的なステップを紹介し、長期的な成功のための土台作りをサポートします。
技術的な実装に入る前に、デプロイワークフローをしっかりと設計する時間を取りましょう。成功の鍵は、慎重な計画と体系的なアプローチにあります。
継続的デプロイにおいて、アーティファクトとは、ビルドプロセスによって生成されるパッケージ化された成果物を指し、保存、バージョン管理、デプロイが必要です。アーティファクトには、次のようなものが含まれます。
アプリケーション用のコンテナイメージ
パッケージ
コンパイル済みのバイナリや実行ファイル
ライブラリ
設定ファイル
ドキュメントパッケージ
その他のアーティファクト
各アーティファクトは、デプロイプロセスにおいて特定の役割を果たします。たとえば、一般的なWebアプリケーションでは、次のようなアーティファクトが生成されることがあります。
バックエンドサービス用のコンテナイメージ
コンパイル済みフロントエンドアセットのZIPアーカイブ
データベース変更用のSQLファイル
環境ごとの設定ファイル
アーティファクトの適切な管理はデプロイを成功させる鍵となります。ここからは、アーティファクト管理のアプローチについて詳しく見ていきましょう。
クリーンで整理された構造を保つためのベストプラクティスとして、アーティファクトの明確なバージョニング戦略を確立することが重要です。リリース作成時のポイントは以下のとおりです。
リリースタグにセマンティックバージョニング(major.minor.patch)を使用する
myapp:1.2.3
の場合最新の安定版を示す「latest」タグを維持する
myapp:latest
(自動デプロイ用)コミットSHAを含めることで、正確なバージョン追跡を行う
myapp:1.2.3-abc123f
(デバッグ用)開発環境向けにブランチベースのタグを使用する
myapp:feature-user-auth
(新機能テスト用)明確な保持ルールを実装しましょう。
一時的なアーティファクトの明確な有効期限を設定する
永続的に保持する必要があるアーティファクトを定義する
ストレージ管理のためのクリーンアップポリシーを設定する
適切なアクセス制御でアーティファクトのセキュリティを確保しましょう。
デベロッパーのアクセス用にパーソナルアクセストークンを実装する
パイプラインの認証にCI/CD変数を設定する
適切なアクセススコープを設定する
環境設計はデプロイパイプライン全体の構成に影響を与えるため、早い段階で検討しましょう。
開発、ステージング環境、本番環境の設定
環境ごとの変数とシークレットの管理
アクセス制御と保護ルールの設定
デプロイの追跡とモニタリングのアプローチ
どこに、どのようにデプロイするのかを慎重に検討しましょう。これらの決定は重要であり、それぞれのメリットとデメリットを考慮する必要があります。
インフラ要件(仮想マシン、コンテナ、クラウドサービス)
ネットワークアクセスとセキュリティ設定
認証メカニズム(SSH鍵、アクセストークン)
リソースの割り当てとスケーリングの考慮
これで戦略が定まり、基盤となる決定が完了したので、これらの計画を実際に動作するパイプラインに落とし込んでいきます。それでは、実際に機能する例を作ることで概念を深掘りしていきましょう。まずはシンプルなアプリケーションから始め、徐々にデプロイの機能を追加していきます。
Webアプリケーション向けの基本的な継続的デプロイ(CD)パイプラインの実装手順を順を追って説明します。例としてシンプルなHTMLアプリケーションを使用しますが、ここで紹介する原則はどのタイプのアプリケーションにも応用できます。今回は、アプリケーションをDockerイメージとしてパッケージ化し、シンプルな仮想マシン上にデプロイします。これにより、最小限の依存関係を持つ厳選されたイメージを活用し、環境依存の要件が意図せず混入するのを防ぐことができます。また、仮想マシン上で動作させることで、GitLabのネイティブなインテグレーション機能を活用しない設定となります。複雑で本格的な環境ではなく、理解しやすい簡易的な環境から始めてみましょう。
この例では、クラウドプロバイダーの仮想マシン上で実行するアプリケーションをコンテナ化することを目指します。また、ローカル環境でもこのアプリケーションをテストします。以下の前提条件は、このシナリオにおいてのみ必要なものです。
お好みのクラウドプロバイダーでVMをプロビジョニングします(例:GCP、AWS、Azure)
ネットワークルールを設定し、ポート22、80、443へのアクセスを許可します
デプロイ用にマシンのパブリックIPアドレスを記録します
マシン用の公開鍵と秘密鍵のペアを生成します
GitLabで設定 > CI/CD > 変数を開きます
GITLAB_KEY
という変数を作成します
タイプを「ファイル」に設定します(SSH認証に必須)
秘密鍵を「値」フィールドに貼り付けます
「ユーザー」変数を定義します(VMにログインしてスクリプトを実行するユーザー)
デプロイターゲット用の変数を作成します
STAGING_TARGET
:ステージング環境のサーバーIPまたはドメインPRODUCTION_TARGET
:本番環境のサーバーIPまたはドメインレジストリパスを確認します:
認証の設定:
ローカルレジストリのアクセス設定:
docker login registry.gitlab.com
# パーソナルアクセストークンを使用する場合のユーザー名はgitlab-ci-tokenです
# Password:(ここにアクセストークンを入力)
基本的なWebアプリケーションから始めましょう。今回の例では、シンプルなHTMLページを使用します。
<!|||UNTRANSLATED_CONTENT_START|||-- index.html -->
<html>
<head>
<style>
body {
background-color: #171321; /* GitLab dark */
}
</style>
</head>
<body>
<!|||UNTRANSLATED_CONTENT_END|||-- ここにコンテンツを追加 -->
</body>
</html>
アプリケーションをパッケージ化するために、Dockerfileを作成します:
FROM nginx:1.26.2
COPY index.html /usr/share/nginx/html/index.html
このDockerfileは
nginxをベースイメージとして使用し、Webコンテンツを配信できるようにします
作成したindex.htmlをnginxのディレクトリ構造内の適切な場所にコピーします
GitLabのパイプラインステージを定義するために、.gitlab-ci.yml
ファイル を作成します:
variables:
TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest
TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHA
stages:
- publish
- deploy
解説:
TAG_LATEST
は、次の3つの要素で構成されています:
$CI_REGISTRY_IMAGE
:GitLabのプロジェクトのコンテナレジストリのパス例:registry.gitlab.com/your-group/your-project
$CI_COMMIT_REF_NAME
:ブランチ名またはタグ名例:ブランチの場合は/main
、フィーチャーブランチの場合は/feature-login
:latest
:固定のサフィックスこれにより、mainブランチのTAG_LATEST
は次のようになります:registry.gitlab.com/your-group/your-project/main:latest
TAG_COMMIT
はTAG_LATEST
とほぼ同じですが、:latest
の代わりにコミット識別子の$CI_COMMIT_SHA
を使います。例::abc123def456
したがって、同じmainブランチでのTAG_COMMIT
は次のようになります:registry.gitlab.com/your-group/your-project/main:abc123def456
両方のタグを使用する理由は、TAG_LATEST
が常に最新バージョンを取得しやすくするのに対し、TAG_COMMIT
は必要に応じて特定のバージョンに戻れるようにするためです。
パイプラインに公開ジョブを追加します:
publish:
stage: publish
image: docker:latest
services:
- docker:dind
script:
- docker build -t $TAG_LATEST -t $TAG_COMMIT .
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $TAG_LATEST
- docker push $TAG_COMMIT
このジョブは
Docker-in-Dockerを使用してイメージをビルドします
2つのタグを付けたイメージを作成します
GitLabレジストリへの認証を行います
両方のイメージをレジストリにプッシュします
これで、コンテナイメージが安全にレジストリに保存されました。次は、ターゲット環境へのデプロイを進めていきます。本番環境へ移行する前に、まずローカル環境でテストを行い、設定が正しく機能していることを確認しましょう。
本番環境にデプロイする前に、ローカル環境でテストできます。先ほどGitLabレジストリにイメージを公開したので、それをローカル環境でプルしてテストします。コンテナイメージのパスが分からない場合は、GitLabの**デプロイ
コンテナレジストリ**に移動し、該当のコンテナイメージの行末にあるアイコンをクリックすると、パスをコピーできます。
docker login registry.gitlab.com
docker run -p 80:80 registry.gitlab.com/your-project-path/main:latest
これにより、Webブラウザからlocalhostにアクセスし、ローカル環境でアプリケーションを確認できるようになります。
それでは、パイプラインにデプロイジョブを追加しましょう:
deploy:
stage: deploy
image: alpine:latest
script:
- chmod 400 $GITLAB_KEY
- apk add openssh-client
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- ssh -i $GITLAB_KEY -o StrictHostKeyChecking=no $USER@$TARGET_SERVER
docker pull $TAG_COMMIT &&
docker rm -f myapp || true &&
docker run -d -p 80:80 --name myapp $TAG_COMMIT
このジョブは
デプロイ先のサーバーへのSSHアクセスを確立します
最新のコンテナイメージをプルします
既存のコンテナを削除します
新しいバージョンのコンテナをデプロイします
環境設定を追加してデプロイの追跡を有効にします:
deploy:
environment:
name: production
url: https://your-application-url.com
これにより、GitLabのオペレーション > 環境セクションに環境オブジェクトが作成され、以下の情報が提供されます:
デプロイ履歴
現在のデプロイ状況
アプリケーションへのクイックアクセス
単一の環境向けのパイプラインは、最初のステップとしては有効ですが、ほとんどのチームでは適切なテストやステージングを行うために複数の環境を管理する必要があります。このより実践的なシナリオに対応するために、パイプラインを拡張していきましょう。
より堅牢なパイプラインを構築するために、ステージング環境と本番環境のデプロイを設定します:
stages:
- publish
- staging
- release
- version
- production
staging:
stage: staging
rules:
- if: $CI_COMMIT_BRANCH == "main" && $CI_COMMIT_TAG == null
environment:
name: staging
url: https://staging.your-app.com
# デプロイスクリプトを記述
production:
stage: production
rules:
- if: $CI_COMMIT_TAG
environment:
name: production
url: https://your-app.com
# デプロイスクリプトを記述
この設定は
mainブランチからステージ環境へデプロイします
GitLabのタグを利用して本番環境へのデプロイをトリガーします
環境ごとに分けてデプロイの状況を管理できるようにします
このステップと次のステップでは、GitLabの非常に便利な機能であるタグを活用しています。GitLabの**コード >
タグ**セクションで手動でタグを作成すると、'$ CI_COMMMIT_TAG
`変数が設定され、本番環境へのデプロイジョブが適切にトリガーされるようになります。
CI/CDパイプラインを通じてGitLabのリリース機能を使用します。まず、.gitlab-ci.yml
のstagesを更新 します:
stages:
- publish
- staging
- release # New stage for releases
- version
- production
次に、リリースジョブを追加します:
release_job:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG # タグが作成された場合のみ実行する
script:
- echo "Creating release for $CI_COMMIT_TAG"
release: # リリース設定
name: 'Release $CI_COMMIT_TAG'
description: 'Release created from $CI_COMMIT_TAG'
tag_name: '$CI_COMMIT_TAG' # タグを作成する
ref: '$CI_COMMIT_TAG' # リリースのベースとなるタグ
コンテナイメージのリンクを追加することで、さらに内容を充実させることができます:
release:
name: 'Release $CI_COMMIT_TAG'
description: 'Release created from $CI_COMMIT_TAG'
tag_name: '$CI_COMMIT_TAG'
ref: '$CI_COMMIT_TAG'
assets:
links:
- name: 'Container Image'
url: '$CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG'
link_type: 'image'
コミットメッセージに基づいて自動リリースノートを生成する場合は次のようにします:
release:
name: 'Release $CI_COMMIT_TAG'
description: 'Release notes for version $CI_COMMIT_TAG'
tag_name: '$CI_COMMIT_TAG'
ref: '$CI_COMMIT_TAG'
auto_generate_release_notes: true # Enables automatic notes
自動リリースノートを充実させるには
規則的なコミットメッセージを使用する(例:feat:, fix:)
イシュー番号を含める(例:#123)
件名と本文の間に空行を入れる
デプロイ時の情報をリリースノートに記載したい場合は、以下のように設定できます:
release_job:
script:
- |
DEPLOY_TIME=$(date '+%Y-%m-%d %H:%M:%S')
CHANGES=$(git log $(git describe --tags --abbrev=0 @^)..@ --pretty=format:"- %s")
cat > release_notes.md << EOF
## デプロイ情報
- デプロイ日時: $DEPLOY_TIME
- 環境: 本番環境
- バージョン: $CI_COMMIT_TAG
## 変更内容
$CHANGES
## アーティファクト
- コンテナイメージ: \`$CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG\`
EOF
release:
description: './release_notes.md'
この設定を行うと、Gitタグの作成時に自動でリリースが作成されるようになります。作成されたリリースは、GitLabの**デプロイ >
リリース**で確認できます。
最終的なYAMLファイルは以下のようになります:
variables:
TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest
TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHA
STAGING_TARGET: $STAGING_TARGET # CI/CD変数で設定
PRODUCTION_TARGET: $PRODUCTION_TARGET # CI/CD変数で設定
stages:
- publish
- staging
- release
- version
- production
# ビルドとレジストリへの公開
publish:
stage: publish
image: docker:latest
services:
- docker:dind
rules:
- if: $CI_COMMIT_BRANCH == "main" && $CI_COMMIT_TAG == null
script:
- docker build -t $TAG_LATEST -t $TAG_COMMIT .
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $TAG_LATEST
- docker push $TAG_COMMIT
# stagingへデプロイ
staging:
stage: staging
image: alpine:latest
rules:
- if: $CI_COMMIT_BRANCH == "main" && $CI_COMMIT_TAG == null
script:
- chmod 400 $GITLAB_KEY
- apk add openssh-client
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- ssh -i $GITLAB_KEY -o StrictHostKeyChecking=no $USER@$STAGING_TARGET "
docker pull $TAG_COMMIT &&
docker rm -f myapp || true &&
docker run -d -p 80:80 --name myapp $TAG_COMMIT"
environment:
name: staging
url: http://$STAGING_TARGET
# リリースの作成
release_job:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG
script:
- |
DEPLOY_TIME=$(date '+%Y-%m-%d %H:%M:%S')
CHANGES=$(git log $(git describe --tags --abbrev=0 @^)..@ --pretty=format:"- %s")
cat > release_notes.md << EOF
## デプロイ情報
- デプロイ日時:$DEPLOY_TIME
- 環境:本番環境
- バージョン:$CI_COMMIT_TAG
## 変更内容
$CHANGES
## アーティファクト
- コンテナイメージ:\`$CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG\`
EOF
release:
name: 'Release $CI_COMMIT_TAG'
description: './release_notes.md'
tag_name: '$CI_COMMIT_TAG'
ref: '$CI_COMMIT_TAG'
assets:
links:
- name: 'コンテナイメージ'
url: '$CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG'
link_type: 'image'
# リリースタグ付きのバージョンを作成
version_job:
stage: version
image: docker:latest
services:
- docker:dind
rules:
- if: $CI_COMMIT_TAG
script:
- docker pull $TAG_COMMIT
- docker tag $TAG_COMMIT $CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG
# 本番環境へデプロイ
production:
stage: production
image: alpine:latest
rules:
- if: $CI_COMMIT_TAG
script:
- chmod 400 $GITLAB_KEY
- apk add openssh-client
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- ssh -i $GITLAB_KEY -o StrictHostKeyChecking=no $USER@$PRODUCTION_TARGET "
docker pull $CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG &&
docker rm -f myapp || true &&
docker run -d -p 80:80 --name myapp $CI_REGISTRY_IMAGE/main:$CI_COMMIT_TAG"
environment:
name: production
url: http://$PRODUCTION_TARGET
完成したパイプラインは
mainブランチでの変更をレジストリに公開します
mainブランチでの変更をstagingへデプロイします
タグが作成された際にリリースを作成します
リリースタグを付けたイメージを作成します
タグを基に本番環境へデプロイします
主なメリット:
クリーンで再現性のあるローカル開発およびテスト環境が完成します
構造化された本番環境へのデプロイフローにより、信頼性の高いデプロイを実現できます
予期しない障害からの復旧パターンを確立します
より複雑なデプロイ戦略への拡張/導入に対応できます
実装の際は、以下の原則を守りましょう:
変数の使用方法からデプロイ手順まで、すべてを文書化する
GitLabのビルトイン機能(環境管理、リリース、レジストリ)を活用する
適切なアクセス制御とセキュリティ対策を実施する
強固なロールバック手順を計画し、障害に備える
パイプラインの設定はDRY(Don't Repeat Yourself:重複を避ける)を意識する
次は何をすべきでしょうか?ここでは、継続的デプロイ戦略をさらに成熟させるために、検討すべきポイントを紹介します。
以下の方法でセキュリティを強化しましょう:
アクセスを制限した保護環境を設定する
本番環境へのデプロイに承認を必須とする
セキュリティスキャンをパイプラインに統合する
自動脆弱性評価を導入する
デプロイ関連の変更に対するブランチ保護ルールを適用する
以下のような高度なデプロイ戦略を実装しましょう。
機能フラグを活用して制御されたロールアウトを行う
リスクを軽減するためにカナリアデプロイを実施する
ブルーグリーンデプロイ戦略を導入する
A/Bテストを実施する
動的環境管理を行う
強固なモニタリング体制を確立しましょう:
デプロイ指標を追跡する
パフォーマンスモニタリングをセットアップする
デプロイアラートを設定する
デプロイのサービスレベル目標(SLO)を確立する
定期的にパイプラインを最適化する
GitLabの継続的デプロイ機能は、現代のデプロイワークフローに最適です。GitLabは、コンテナレジストリ、環境管理、デプロイ追跡などの機能が1つのインターフェースに集約されており、コードから本番環境へのプロセスを効率化します。また、環境ごとの変数、デプロイ承認ゲート、ロールバック機能を備えており、本番環境へのデプロイ時に求められるセキュリティと管理を実現します。さらに、Review
Apps(レビューアプリ)や機能フラグを活用することで、段階的なデリバリー(プログレッシブデリバリー)戦略も可能です。GitLabのDevSecOpsプラットフォームの一部として、これらのCD機能はソフトウェアライフサイクル全体とシームレスに連携します。
継続的デプロイへの道のりは、革命ではなく進化のプロセスです。まずは基本を押さえ、しっかりとした基盤を築き、チームの成長に応じて徐々に高度な機能を取り入れていきましょう。GitLabは、最初の自動デプロイから、複数環境にまたがる複雑なデリバリーパイプラインまで、あらゆる段階でサポートするためのツールと柔軟性を提供します。
[GitLab
Ultimateの無料トライアル](https://about.gitlab.com/ja-jp/free-trial/devsecops/)で、今日から継続的デプロイメントを始めましょう。
*監修:川瀬 洋平 @ykawase
(GitLab合同会社 カスタマーサクセス本部 シニアカスタマーサクセスマネージャー)*