📝
Cloud Build で CD パイプラインを構築してみた
はじめに
Hogetic Lab でエンジニアをしている佐々木と申します。
現在のソフトウェア開発の環境では、新機能やアップデート、バグ修正を迅速かつ確実にエンドユーザーに届ける能力が重要です。Continuous Deployment(CD)は、このプロセスを自動化し、コードがリポジトリにプッシュされるたびに自動でデプロイを行う手法です。アプリケーションの信頼性を高めるためには必須の機能ではないでしょうか。
前回 「API テストを Scenarigo と GitHub Actions で自動化する」 の記事の続きになります。
それでは早速構築していきましょう!
今回は terraform を使用して作成しています。
概要
-
ネットワーク環境及び DB の作成
- VPC, Subnet, Private Service Access
-
アプリケーション環境の構築
- Artifact Registry, Serverless VPC Access, Cloud Run
-
CD パイプラインの構築
- Secret Manager, Cloud Build
ネットワーク環境及び DB の作成
-
Private Service Access とは
- コンソールからの抜粋
- Cloud SQL インスタンスに外部 IP を割り当てることなく Cloud Run から安全に接続できるようになるということですね!
resource "google_compute_network" "network" {
name = "network"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "subnet" {
name = "subnet"
ip_cidr_range = "192.168.0.0/24"
network = google_compute_network.network.id
depends_on = [google_compute_network.network]
}
// * 内部 IPv4 アドレス範囲を割り振る
// https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_global_address
resource "google_compute_global_address" "private_ip" {
name = "private-ip"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = "16"
network = google_compute_network.network.id
depends_on = [google_compute_network.network]
}
// * プライベート接続を作成する
// https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_networking_connection
resource "google_service_networking_connection" "connection" {
network = google_compute_network.network.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.private_ip.name]
deletion_policy = "ABANDON"
depends_on = [google_compute_global_address.private_ip]
}
resource "google_sql_database_instance" "db_instance" {
name = "db-instance"
database_version = "POSTGRES_13"
region = var.region
settings {
tier = "db-f1-micro"
ip_configuration {
ipv4_enabled = false
private_network = google_compute_network.network.id
}
}
deletion_protection = false
depends_on = [google_service_networking_connection.connection]
}
アプリケーション環境の構築
-
Serverless VPC Access とは
- コンソールからの抜粋
サーバーレス VPC アクセスを使用すると、Cloud Functions、Cloud Run(フルマネージド)サービス、App Engine スタンダード環境アプリで、これらのリソースの内部 IP アドレスを使って VPC ネットワークのリソースにアクセスできます。 - Cloud Run は VPC リソースではないので、上述の Private Service Access を使用するためには VPC に接続する必要があるということですね!
- コンソールからの抜粋
resource "google_artifact_registry_repository" "repository" {
repository_id = "repository"
format = "DOCKER"
location = var.region
}
// Serverless VPC Access Connector を作成する
// https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/vpc_access_connector
resource "google_vpc_access_connector" "connector" {
name = "serverless-vpc-connector"
ip_cidr_range = "192.168.1.0/28"
network = google_compute_network.network.name
depends_on = [google_compute_network.network]
}
resource "google_cloud_run_service" "service" {
name = "service"
location = var.region
template {
spec {
containers {
image = "${var.region}-docker.pkg.dev/${var.project}/${google_artifact_registry_repository.repository.repository_id}/application:latest"
env {
name = "DB_HOST"
// Cloud SQL のプライベート IP アドレスを指定する
value = google_sql_database_instance.db_instance.private_ip_address
}
...
}
}
metadata {
annotations = {
"run.googleapis.com/vpc-access-egress" = "all-traffic"
// 上で作成した VPC Access Connector を指定する
"run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.connector.name
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
CD パイプラインの構築
- 今回は Github Actions で CI を実行し、 main ブランチにマージされたタイミングでビルド, デプロイを Cloud Build Trigger が行う構成になっています。
- 前提条件
- Github と Cloud Build が統合されている。参考URL
- Github Token を取得している
// Github Token を保存する Secret Manager を用意する
resource "google_secret_manager_secret" "sm" {
secret_id = "github-token"
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "smv" {
secret = google_secret_manager_secret.sm.id
// 前提条件で取得している Github のトークン
secret_data = var.github_token
depends_on = [google_secret_manager_secret.sm]
}
data "google_iam_policy" "secretAccessor" {
binding {
role = "roles/secretmanager.secretAccessor"
members = ["serviceAccount:service-${var.project_id}@gcp-sa-cloudbuild.iam.gserviceaccount.com"]
}
}
resource "google_secret_manager_secret_iam_policy" "policy" {
secret_id = google_secret_manager_secret.sm.secret_id
policy_data = data.google_iam_policy.secretAccessor.policy_data
depends_on = [google_secret_manager_secret_version.smv]
}
resource "google_cloudbuildv2_connection" "connection" {
location = var.region
name = "connection"
github_config {
// Github のコンソールから Cloud Build の installation id を取得する。
app_installation_id = var.installation_id
authorizer_credential {
// 上で作成した Github Token を保存した際の Secret Manager の バージョン ID を指定します。
oauth_token_secret_version = google_secret_manager_secret_version.smv.id
}
}
depends_on = [google_secret_manager_secret_iam_policy.policy]
}
resource "google_project_iam_member" "attach_permissions" {
// https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudbuild_trigger#example-usage---cloudbuild-trigger-service-account
// 上記リンクを参考に今回は Cloud Build Trigger から Cloud Run をデプロイするので権限を付与しました。
for_each = toset([
"roles/run.admin",
"roles/logging.logWriter",
"roles/iam.serviceAccountUser",
])
role = each.key
project = var.project
member = "serviceAccount:${var.project_id}@cloudbuild.gserviceaccount.com"
}
resource "google_cloudbuild_trigger" "trigger" {
name = "trigger"
// github の関連要素を指定
github {
owner = var.github_owner
name = var.github_repository
push {
branch = "^main$"
}
}
filename = "cloudbuild.yml"
depends_on = [google_cloudbuildv2_connection.connection]
}
おまけ
-
cloudbuild.yml
- 特筆すべきことはあまりないですが、 docker のイメージタグには commit ハッシュを利用しましょう!
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-f', 'docker/Dockerfile', '-t', 'asia-northeast1-docker.pkg.dev/${PROJECT_ID}/repository/application:${COMMIT_SHA}', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'asia-northeast1-docker.pkg.dev/${PROJECT_ID}/repository/application:${COMMIT_SHA}']
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args:
- 'run'
- 'deploy'
- 'service'
- '--image'
- 'asia-northeast1-docker.pkg.dev/${PROJECT_ID}/repository/application:${COMMIT_SHA}'
- '--region'
- 'asia-northeast1'
まとめ
- 今回初めて Google Cloud で CI/CD を組んでみましたが、Cloud Build Trigger の構築は非常にわかりやすく、シンプルだと感じました。
- また Cloud Run は最大同時実行数を 0 に設定できるので 概念検証の段階 や ステージング環境 や テスト環境 などで使用するとコスト面で非常に優秀だと感じています。
- サンプルコード
Discussion