こんにちは、クラウドエースの岸本です。
この記事では、GitLab CI/CD と Google Cloud の Workload Identity 連携を利用して、Infrastructure as Code (以下、IaC) のデプロイハンズオンを行います。
ゴールは、Workload Identity 連携を利用し、GitLab CI/CD から Google Cloud のリソースを作成することです。
前提条件
本ハンズオンでは、SaaS 版の GitLab を使用します。
事前に以下の環境が準備されていることを前提とします。
- Google Cloud プロジェクトが作成されていること
- GitLab プロジェクトが作成されていること
ハンズオン中に特に指定がない値に関してはデフォルト値を利用します。
また、Google Cloud のコンソールから以下の API を有効にしておきます。
手順はこちらを参考にしてください。
- Cloud Storage API
- Compute Engine API
- IAM Service Account Credentials API
- Cloud Resource Manager API
今回のハンズオンでは、主に以下のプロダクトを利用します。
GitLab
GitLab CI/CD
GitLab CI/CD は、「あるブランチにコミットされたら」といったトリガーを設定し、自動的にビルド、テスト、デプロイなどを実行することができます。
今回は、ソースコードが main ブランチにコミットされるたびに、Terraform コマンドが実行されるように設定します。
GitLab Runner
GitLab Runner は、GitLab CI/CD のパイプラインを実行するためのアプリケーションです。
今回は、GitLab Runner を利用して、Terraform を実行し、Google Cloud のリソースを作成します。
Infrastructure as Code
Terraform
Terraform は、HashiCorp が提供する IaC ツールです。
Terraform を使用すると、コードで定義されたリソースをプロビジョニングできます。
今回は、Terraform を利用して、Google Cloud のリソースを作成します。
Google Cloud
Workload Identity Federation
Workload Identity Federation は、Google Cloud と外部の ID プロバイダ(GitLab、AWS、Azure など)との関係を定義します。
これにより、外部システムは OAuth 2.0 トークン交換を通じて Google Cloud へ安全にアクセスできます。
以下の 2 つのエンティティを作成することで、Workload Identity 連携を行うことができます。
-
- Workload Identity Pool
Workload Identity Pool は、GitLab のような Google Cloud 外部の ID プロバイダーを信頼し、連携させるためのエンティティです。
このプールを利用することで、外部ワークロードが発行した OIDC トークンを認証し、サービスアカウントキーを使わずに Google Cloud リソースへの安全なアクセスを実現します。
- Workload Identity Pool
-
- Workload Identity Provider
Workload Identity Provider は、GitLab や AWS、Azure といった外部の ID プロバイダ(IdP)を、Google Cloud が信頼するための設定エンティティです。
このプロバイダを構成すると、外部 IdP が発行した OIDC トークンなどを Google Cloud の認証情報として受け入れ、OAuth 2.0 のトークン交換を通じて安全なアクセスを実現します。
今回は、この仕組みを利用し、GitLab が発行する OIDC トークンを使って Google Cloud リソースへアクセスするために、Workload Identity プロバイダを作成します。
- Workload Identity Provider
サービスアカウント
サービスアカウント は、アプリケーションやシステムが Google Cloud のリソースにアクセスするために使用するためのアカウントです。
今回は、GitLab CI/CD から Google Cloud リソースへのアクセス権限を付与するために、このサービスアカウントを作成します。
Workload Identity 連携の設定を通じて、GitLab CI/CD がこのサービスアカウントの権限を借用できるように構成します。
ハンズオンの流れと概要は以下の通りです。
詳細は各章で解説します。
-
Cloud Storage バケットの作成
Terraform には、管理しているリソースの現在の状態を記録する tfstate ファイルがあり、これを適切に管理する必要があります。
今回は、その保存先として Cloud Storage を使用します。 -
サービスアカウントの作成
GitLab CI/CD から Google Cloud のリソースを操作するためのサービスアカウントを作成します。 -
Workload Identity Pool と Workload Identity Provider の作成と設定
Workload Identity Pool と Workload Identity Provider を作成し、サービスアカウントを登録します。
これにより、GitLab CI/CD が上記で作成したサービスアカウントを利用して、Google Cloud のリソースを操作することができるようになります。 -
ソースコードの準備
Terraform のソースコードと GitLab CI/CD の設定ファイルを準備します。
今回は、GitLab CI/CD が Workload Identity を経由して Google Cloud にリソースを作成できることを目的としているため、ソースコードも最小限で作成します。 -
パイプラインの実行と確認
パイプラインを実行し、Terraform に記載された通りに Google Cloud のリソースが作成されることを確認します。
それでは、ハンズオンを始めます。
1. Cloud Storage バケットの作成
はじめに、Terraform の状態管理に使用する Cloud Storage バケットを作成します。
Google Cloud のコンソールから、「Cloud Storage」と検索し、「バケット」の「作成」ボタンをクリックします。
【バケットの作成ボタン】
次の画面では以下の項目を入力します。
項目 | 入力内容 | 補足 |
---|---|---|
バケット名 | 任意の名前を入力します。 | グローバルに一意になるようにします。 |
ロケーションタイプ | リージョンを選択します。 | 今回は例として東京リージョンを選択しています。 |
【Cloud Storage バケットの作成】
バケットが作成されたことを確認します。
【バケットの確認】
2. サービスアカウントの作成
GitLab CI/CD から Google Cloud のリソースを操作するためのサービスアカウントを作成します。
コンソール画面の「IAM と管理」から「サービスアカウント」をクリックし、「サービスアカウントを作成」ボタンをクリックします。
【サービスアカウントの作成ボタン】
次の画面では以下の項目を入力します。
項目 | 入力内容 | 補足 |
---|---|---|
サービスアカウント名 | 任意の名前を入力します。 | 使用用途がわかるような命名にします。 |
サービスアカウントID | 任意の ID を入力します。 | サービスアカウント名と同じにすると、わかりやすいです。 |
【サービスアカウントの作成】
次に、サービスアカウントに対して以下の権限を付与します。
入力後、「完了」ボタンをクリックします。
権限 | ロール | 補足 |
---|---|---|
権限 | Storage オブジェクト管理者(roles/storage.objectAdmin) | Terraform から Cloud Storage バケットの tfstate ファイルを参照/編集するために必要です。 |
権限 | Compute ネットワーク管理者(roles/compute.networkAdmin) | Terraform から Compute Engine のインスタンスを作成するために必要です。 |
権限 | Compute 管理者(roles/compute.admin) | Terraform から Compute Engine のインスタンスを作成するために必要です。 |
サービスアカウントが作成されたことを確認します。
【サービスアカウントの作成後の画面】
3. Workload Identity Pool と Workload Identity Provider の作成と設定
Workload Identity Pool と Workload Identity Provider を作成します。
次に、作成したサービスアカウントを Workload Identity Pool に登録します。
これにより、GitLab CI/CD から Google Cloud のリソースにアクセスすることができるようになります。
コンソール画面の「IAM と管理」から「Workload Identity 連携」をクリックし、「プールを作成」ボタンをクリックします。
【Workload Identity Pool の作成】
次の画面では以下の項目を入力します。
入力後は「続行」ボタンをクリックします。
項目 | 入力内容 | 補足 |
---|---|---|
名前 | 任意の名前を入力します。 | 使用用途がわかるような命名にします。 |
プールID | 任意の ID を入力します。 | プールIDは、Workload Identity Pool の ID です。 |
【Workload Identity Pool の作成】
プロバイダの項目は以下の通りです。
入力後、「続行」ボタンをクリックします。
項目 | 入力内容 | 補足 |
---|---|---|
プロバイダの選択 | OpenID Connect(OIDC) | 今回は OIDC を選択します。 |
プロバイダ名 | 任意の名前を入力します。 | 使用用途がわかるような命名にします。 |
プロバイダ ID | 任意の ID を入力します。 | プロバイダ ID は、Workload Identity Provider の ID です。 |
発行元 URL | https://gitlab.com/ | IdP の発行元の URL を入力します。今回は GitLab の URL (https://gitlab.com)を入力します。https は必須です。 |
オーディエンス | (選択)許可するオーディエンス | OIDC トークンの aud (audience) フィールドに期待される値を指定する際に使用します。 |
対象 | https://gitlab.com/ | 許可するオーディエンスの URL を指定します。 |
【Workload Identity Provider の作成】
プロバイダの属性は以下の通りです。
入力後、「保存」ボタンをクリックします。
項目 | 入力内容 | 補足 |
---|---|---|
属性マッピング | google.subject = assertion.sub | 必須の項目です。 |
属性マッピング | attribute.project_path = assertion.project_path | 今回はプロジェクトのパスを指定します。そのほかの属性マッピングについては、こちらから確認できます。 |
属性条件 | assertion.project_path == ‘組織名/プロジェクト名’ | プロジェクトのパスを指定すると、そのプロジェクトのみ利用できるようになります。例えば、プロジェクトの URL がhttps://gitlab.com/hoge/fuga であれば、hoge/fuga と入力します。 |
【属性マッピングの設定】
最後に、サービスアカウントを Workload Identity Pool に登録します。
先ほど作成した「Workload Identity Pool」を選択し、「アクセスを許可」ボタンをクリックします。
以下の項目を入力します。
入力後、「保存」ボタンをクリックします。
項目 | 入力内容 | 補足 |
---|---|---|
選択 | サービス アカウントの権限借用を使用してアクセス権を付与する | 今回はサービスアカウントの権限借用を使用してアクセス権を付与するため、この選択肢を選択します。 |
サービスアカウントを選択 | [email protected] | 先ほど作成したサービスアカウントを選択します。 |
プリンシパル | project_path = PROJECT_PATH | PROJECT_PATH は、プロジェクトのパスを入力します。プロジェクトの URL が https://gitlab.com/hoge/fuga であれば、hoge/fuga と入力します。 |
【サービスアカウントの登録】
4. ソースコードの準備
次にソースコードを準備します。
今回は環境の差異が生まれないように、Web IDE を利用して、ソースコードを作成します。
GitLab のプロジェクトから、「Web IDE」をクリックします。
【Web IDE の起動】
ディレクトリ構成は以下の通りです。
はじめにファイルを作成します。
.
├── .gitlab-ci.yml
├── backend.tf
├── main.tf
└── variables.tf
以下の画像の赤枠をクリックし、ファイル名を入力します。
上記で記載した 4 つのファイルを作成します。
【ファイル作成】
次に作成したファイルにソースコードを記述します。
.gitlab-ci.yml
stages:
- plan
- apply
- destroy
variables:
TERRAFORM_VERSION: "1.12.0"
PROJECT_NAME: "PROJECT_NAME"
WORKLOAD_IDENTITY_PROVIDER: "WORKLOAD_IDENTITY_PROVIDER"
SERVICE_ACCOUNT: "SERVICE_ACCOUNT"
.id_tokens:
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com/
.gcp_auth_script: &gcp_auth_script
- echo "$GITLAB_OIDC_TOKEN" > /tmp/oidc_token.json
- gcloud iam workload-identity-pools create-cred-config "$WORKLOAD_IDENTITY_PROVIDER" --service-account="$SERVICE_ACCOUNT" --output-file=/tmp/gcp_cred.json --credential-source-file=/tmp/oidc_token.json
- gcloud auth login --cred-file=/tmp/gcp_cred.json --update-adc
- gcloud config set project ${PROJECT_NAME}
.install_terraform: &install_terraform
- apt-get update && apt-get install -y wget unzip
- wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
- unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip
- mv terraform /usr/local/bin/
- terraform --version
.terraform_base:
image: google/cloud-sdk:slim
extends: .id_tokens
cache:
key: ${CI_COMMIT_REF_SLUG}-terraform
paths:
- .terraform/
before_script:
- *install_terraform
- *gcp_auth_script
terraform_plan:
extends: .terraform_base
stage: plan
script:
- terraform init
- terraform plan -out="tfplan"
artifacts:
paths:
- tfplan
terraform_apply:
extends: .terraform_base
stage: apply
script:
- terraform init
- terraform apply -auto-approve "tfplan"
dependencies:
- terraform_plan
when: manual
terraform_destroy:
extends: .terraform_base
stage: destroy
script:
- terraform init
- terraform destroy -auto-approve
when: manual
allow_failure: true
environment:
name: production
action: stop
backend.tf
「1. Cloud Storage バケットの作成」で作成したバケットを BUCKET_NAME
に入力します。
terraform {
backend "gcs" {
bucket = "BUCKET_NAME" # バケット名を入力します。
}
}
main.tf
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = "ca-gitlab-iac-dem"
region = "asia-northeast1"
}
resource "google_compute_network" "custom_vpc" {
name = "sample-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "custom_subnet" {
name = "sample-subnet"
ip_cidr_range = "10.10.0.0/24"
region = "asia-northeast1"
network = google_compute_network.custom_vpc.id
}
resource "google_compute_firewall" "allow_ssh_via_iap" {
name = "allow-ssh-via-iap"
network = google_compute_network.custom_vpc.name
target_tags = ["allow-ssh-via-iap"]
direction = "INGRESS"
allow {
protocol = "tcp"
ports = ["22"]
}
source_ranges = ["35.235.240.0/20"]
}
resource "google_compute_instance" "default" {
name = "sample-instance"
machine_type = "e2-micro"
zone = "asia-northeast1-c"
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
size = 10
}
}
network_interface {
network = google_compute_network.custom_vpc.id
subnetwork = google_compute_subnetwork.custom_subnet.id
}
tags = ["allow-ssh-via-iap"]
labels = {
"managed-by" = "terraform"
}
}
.gitignore
terraform-example-foundationレポジトリの.gitignore
をコピーしたものです。
# OSX leaves these everywhere on SMB shares
._*
# OSX trash
.DS_Store
# Python
*.pyc
# Emacs save files
*~
\#*\#
.\#*
# Vim-related files
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
### https://raw.github.com/github/gitignore/90f149de451a5433aebd94d02d11b0e28843a1af/Terraform.gitignore
# Local .terraform directories
**/.terraform*
# .tfstate files
*.tfstate
*.tfstate.*
# Local tfvars terraform.tfvars
**/*.tfvars
# tf lock file
**/.terraform.lock.hcl
# Crash log files
crash.log
# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
# .tfvars files are managed as part of configuration and so should be included in
# version control.
#
# example.tfvars
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.idea/
.vscode/
# Kitchen files
**/inspec.lock
**.gem
**/.kitchen
**/.kitchen.local.yml
**/Gemfile.lock
# Plan files
**/tmp_plan
**/.tmp
**/tmp
test/fixtures/shared/terraform.tfvars
test/integration/gcloud/config.sh
test/integration/tmp
credentials.json
helpers/foundation-deployer/foundation-deployer
helpers/foundation-deployer/.steps.json
# File to populate env vars used by Docker test runs
.envrc
# Handle files generated on sed command by old (2013-) MacOS versions
*.tf-e
# Go multi-module workspace sum
go.work.sum
余談:効率的なパイプラインの実行
以下の Dockerfile でイメージを作成し、Container Registry にプッシュしておくことで、
コマンドのインストール時間を短縮することができます。
またイメージも容量が少ない slim イメージを使用することで、ダウンロードの時間を短縮することができます。(イメージの詳細については、こちらを参考にしました。)
以下の Dockerfile の内容としては、gcloud コマンドのインストール、terraform コマンドのインストールしています。
FROM google/cloud-sdk:slim
RUN apt-get update && apt-get install -y \
curl \
wget \
unzip \
git \
jq \
vim \
&& rm -rf /var/lib/apt/lists/*
ENV TERRAFORM_VERSION=1.9.0
RUN wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
&& unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
&& mv terraform /usr/local/bin/ \
&& rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip
以上でソースコードの準備は完了です。
5. パイプラインの実行と確認
パイプラインを実行し、Google Cloud のリソースを作成します。
Web IDE から main ブランチへのプッシュするとパイプラインが実行されます。
それでは、早速パイプラインを実行してみます。
画面左の赤枠をクリックし、コミットメッセージを入力、「Commit and push to main」ボタンをクリックします。
画面右下に「デフォルトブランチにプッシュしますか?」と表示されますが、今回は「Continue」ボタンをクリックします。
【パイプラインの実行】
GitLab プロジェクトに移動し、以下の画面赤枠をクリックする、または「Build」、「Pipeline」からパイプラインのログを確認することができます。
【パイプラインの確認】
パイプラインが実行されていると、以下のように青色のマークが表示されます。
【パイプラインのログ】
パイプラインが正常に実行されると、緑色のチェックマークが表示されます。
【パイプライン完了】
次に、terraform apply
を実行し実際にリソースを作成します。
その前に、terraform plan
ジョブのログを確認し、内容に問題がないことを確認します。
【terraform plan のログ】
terraform plan
のログに問題がなさそうなので、terraform apply
ジョブを実行します。
画面の赤枠をクリックし、terraform apply
ジョブを実行します。
【terraform apply の実行】
青色のマークが表示されれば、正常に実行されています。
【terraform apply の実行】
ログを確認すると、最後に「Job succeeded」と表示されていることが確認できます。
【terraform apply の実行】
今回実行したパイプラインのterraform plan
ジョブと terraform apply
ジョブの実行は完了です。
【パイプラインの実行完了】
それでは、Google Cloud のリソースが作成されていることを確認します。
【Compute Engine のインスタンスの確認】
【VPC ネットワークとサブネットの確認】
【ファイアウォールの確認】
無事全てのリソースが作成されていることを確認できました。
6. Terraform で作成したリソースの削除
ハンズオンで作成したリソースを削除する手順を説明します。
Terraform で作成したリソースは、GitLab CI/CD パイプラインの terraform_destroy
ジョブで削除できます。
GitLab プロジェクトの「CI/CD」→「Pipelines」から先ほどのパイプラインを選択します。
以下画面赤枠の terraform_destroy
ジョブの三角ボタンをクリックして実行します。
【terraform_destroy の実行】
ジョブが正常に完了すると、Terraform で作成したリソースが削除されます。
【terraform_destroy の実行完了】
以上で、ハンズオンは完了です。
今回は、GitLab CI/CD と Google Cloud の Workload Identity 連携を利用して、IaC のデプロイハンズオンを行いました。
この設定により、GitLab CI/CD パイプラインはサービスアカウントキーを直接扱うことなく、GitLab OIDC トークンと Google Cloud Workload Identity 連携を通じて、安全に Google Cloud リソースを操作できます。
この記事が、あなたのプロジェクトの一助となれば幸いです。
弊社は、Google Cloud だけでなく、GitLab の導入、運用サポートを行っております。
ご不明な点、質問等ございましたら、お気軽にお尋ねください。
Views: 0