【テクニカル・上級編】Pulumiで実現するCI/CDパイプライン構築:GitHub Actions連携の実装手順 – インフラ構成管理(IaC)活用バイブル

Pulumi × GitHub Actions:極限まで洗練されたIaCパイプラインの構築と内部最適化

インフラストラクチャ・アズ・コード(IaC)の領域において、TerraformのHCLが持つ表現力の限界や、CDKの複雑なトランスパイル機構にフラストレーションを感じたシニアエンジニアたちが最後にたどり着く答え、それがPulumiだ。TypeScript、Python、Goといった真のプログラミング言語を用いてインフラを定義できるこのパラダイムは、コードの再利用性、テスト容易性、そしてモジュラリティにおいて圧倒的な優位性をもたらす。

しかし、ローカルマシンで `pulumi up` を実行して満足しているうちは、まだPulumiの真価の半分も引き出せていない。真のSRE/DevOpsエンジニアが目指すべきは、「人間の介在を完全に排除し、Gitの操作だけでセキュアかつ高速にインフラが収束する(Reconciliation)完全自動化パイプライン」である。

本稿では、GitHub ActionsとPulumi(Pulumi Cloudバックエンド)を極限まで最適化し、エンタープライズグレードの安全性と極小のレイテンシを両立させるCI/CDパイプラインの実装手順を、現場の泥臭い知見と低レイヤの挙動を踏まえて徹底解説する。

—

1. バックエンドアーキテクチャの選定と状態管理の極意

Pulumiのパイプライン設計において最初に直面する重大な意思決定は、State(状態)の保管場所の選定である。選択肢は大きく分けて2つある。
1. Pulumi Cloud (Managed Backend)
2. Self-Managed Backend (S3 + DynamoDB / GCSなど)

結論から言えば、CI/CDパイプラインのパフォーマンス、監査ログ、そしてチームスケーラビリティを考慮するならば、Pulumi Cloud一択である。

なぜ Pulumi Cloud なのか?

  • 分散ロックの堅牢性: S3バックエンドにおけるDynamoDBを用いた悲観的ロックは、稀にデッドロックやリークを引き起こす。Pulumi CloudはAPIレイヤでアトミックな状態更新を保証する。
  • Previewの可視化: `pulumi preview` の結果(JSON差分)がPulumi CloudのWeb UI上でリッチにレンダリングされ、PRのコメントと双方向リンクされる。
  • 細粒度なシークレット暗号化: デフォルトでKMS(AWS KMS、HashiCorp Vault、またはPulumi独自のSecrets Provider)と統合され、変数ごとに暗号化レベルを制御できる。

—

2. 権限委譲のパラダイムシフト:OIDCによるセキュア認証

従来のCI/CDにおける最大のセキュリティアンチパターンは、AWSの長期的なクレデンシャル(`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`)をGitHub ActionsのSecretsにハードコードすることだ。鍵のローテーション漏れや、万が一のリポジトリ侵害時のBlast Radius(影響範囲)が計り知れない。

ここでは、AWS IAM Role と GitHub Actions 間での OIDC(OpenID Connect)信頼関係を構築し、短命な一時トークンのみを利用する設計を強制する。

Terraform (またはPulumi自体) でプロビジョニングするGitHub OIDCプロバイダーの概念設定
resource “aws_iam_openid_connect_provider” “github” {
url = “https://token.actions.githubusercontent.com”
client_id_list = [“sts.amazonaws.com”]
thumbprint_list = [“6938fd4d98bab03faadb97b34396831e3780aea1”] # GitHub Actions OIDC Root CA Thumbprint
}

このOIDC設定により、GitHub Actionsのワークフロー実行時に、AWS側で「どの組織の、どのリポジトリの、どのブランチ/環境からのリクエストか」を検証し、ミリ秒単位で有効期限が切れるSTS Temporary Security Credentialsを発行させることが可能になる。

—

3. プルリクエスト時のプレビュー自動化(`pulumi preview`)

プルリクエスト作成時、コードの変更が実際のインフラにどのような差分(Diff)を生むのかを機械的に検証し、レビュアーに視覚化する。これがCIの必須条件である。

以下のワークフローは、メモリ消費の最適化と並列実行制御を考慮したプロダクション品質のGitHub Actions定義である。

name: “Pulumi CI – Preview”

on:
pull_request:
branches:

  • main

paths:

  • ‘infra/’ # インフラストラクチャのコード変更時のみ発火

permissions:
id-token: write # OIDC認証に必須
contents: read
pull-requests: write # PRへのコメント自動投稿に必須

jobs:
preview:
name: “Pulumi Preview”
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./infra

steps:

  • name: Checkout Repository

uses: actions/checkout@v4

# Node.js/Python等のランタイム依存関係キャッシュによりビルド時間を極限まで削減

  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘npm’
cache-dependency-path: ‘./infra/package-lock.json’

  • name: Install Dependencies

run: npm ci

# AWS OIDC Assume Role

  • name: Configure AWS Credentials

uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsPulumiDeployRole
aws-region: ap-northeast-1

  • name: Login to Pulumi Cloud

uses: pulumi/action-pulumi-secret-decrypt@v1 # 必要に応じて
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}

# Pulumi Preview の実行と結果のキャプチャ

  • name: Run Pulumi Preview

uses: pulumi/actions@v5
with:
command: preview
stack: production
comment-on-pr: true # PRにリッチなDiffを自動コメント
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
AWS_REGION: ap-northeast-1

エキスパートハック:大規模スタックにおけるメモリプレッシャーの回避

リソース数が数千を超える巨大なPulumiスタックでは、TypeScriptのコンパイラ(tsc)やPulumiエンジン自体のメモリ消費量が急増し、GitHub Actionsのデフォルトランナー(2vCPU / 7GB RAM)で OOM Killer(Out Of Memory) が発動することがある。
これを防ぐため、Node.jsのヒープサイズを明示的に拡張する環境変数をCI側に注入せよ。

env:
NODE_OPTIONS: “–max-old-space-size=4096”

—

4. マージ時の本番デプロイ自動化ワークフロー(`pulumi up`)

PRがレビューされ、`main` ブランチへマージされた瞬間に、インフラストラクチャは宣言された状態へ自動的に収束(Reconciliation)されなければならない。

ここで重要なのは、「Race Condition(競合状態)」の排除である。複数のPRが連続してマージされた場合、デプロイが直列化されなければステートの破損を招く。GitHub Actionsの `concurrency` グループを活用し、スタックごとの排他制御を実装する。

name: “Pulumi CD – Deploy”

on:
push:
branches:

  • main

paths:

  • ‘infra/’

concurrency:
group: pulumi-production-stack
cancel-in-progress: false # デプロイ途中のジョブをキャンセルせず、キューイングして直列化する

permissions:
id-token: write
contents: read

jobs:
deploy:
name: “Pulumi Up”
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./infra

steps:

  • name: Checkout Repository

uses: actions/checkout@v4

  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘npm’
cache-dependency-path: ‘./infra/package-lock.json’

  • name: Install Dependencies

run: npm ci

  • name: Configure AWS Credentials

uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsPulumiDeployRole
aws-region: ap-northeast-1

# 本番デプロイ実行:–yesフラグでインタラクティブプロンプトをバイパス

  • name: Run Pulumi Up

uses: pulumi/actions@v5
with:
command: up
stack: production
options: –refresh # デプロイ前にクラウド側の実態とステートを強制同期し、ドリフトを検知
stack-version: v1.0.0
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
AWS_REGION: ap-northeast-1

`–refresh` フラグの重要性

本番環境では、コンソールからの手動変更(ドリフト)が発生しうる。`pulumi up` 実行時に `–refresh` を付与することで、デプロイの直前にクラウドプロバイダーから最新のリソース状態をフェッチし、ステートファイルと強制的に突き合わせる。これにより、「コード上は変更がないのに、実態と乖離していてエラーになる」という最悪の事態を未然に防ぐ。

—

5. セキュリティとシークレット管理の極意

IaCにおけるシークレット(DBパスワード、APIトークン、SSL証明書など)の扱いは、セキュリティインシデントの温床になりやすい。Pulumiは、シークレットを暗号化されたプレースホルダーとしてステートに保存する強力な仕組みを持っている。

1. コード内でのシークレットの型強制

TypeScriptなどの言語を使用する場合、単なる `string` ではなく `pulumi.Output` の中で `pulumi.secret()` を使用してラップする。これにより、コンソール出力やCIのログ上で自動的に `[secret]` とマスクされる。

import as pulumi from “@pulumi/pulumi”;
import as aws from “@pulumi/aws”;

// 機密情報の定義:明示的に secret() で包む
const dbPassword = pulumi.secret(“SuperSecretPassword123!”);

const dbInstance = new aws.rds.Instance(“my-db”, {
engine: “postgres”,
password: dbPassword, // AWS側には平文で渡るが、Pulumi State上では暗号化される
// … other configurations
});

// 出力時も暗号化を維持
export const dbEndpoint = dbInstance.endpoint;
export const encryptedPassword = dbPassword; // ステート内では暗号化されたまま

2. シークレットプロバイダーの外部化

デフォルトのPulumi Secrets EncryptionはPulumi CloudのKMSによって管理されるが、エンタープライズ要件によっては、自社管理の AWS KMS (CMK – Customer Managed Key) を指定する必要がある。
これを行うには、スタックの初期化時、あるいは `Pulumi.yaml` にプロバイダーを明記する。

name: my-infrastructure
runtime: nodejs
description: Enterprise Pulumi Stack with AWS KMS
backend:
url: pulumi://
options:
encryptor: awskms://arn:aws:iam::123456789012:key/12345678-1234-1234-1234-123456789012

この設定により、シークレットの暗号化・復号の鍵のライフサイクルを完全に自社のAWSアカウント下でコントロールでき、コンプライアンス要件(SOC2, ISO27001等)を容易にクリアできる。

—

結び:インフラストラクチャを「ソフトウェア」として扱うために

PulumiとGitHub Actionsを組み合わせたCI/CDパイプラインは、単なる「手動コマンドの代替」ではない。それは、インフラストラクチャという複雑怪奇な物理・仮想リソースの集合体を、完全に予測可能で、テストされ、バージョン管理された純粋なソフトウェアへと昇華させるための唯一無二の手段である。

OIDCによる認証の近代化、確実なステート同期と排他制御、そして厳格なシークレット管理。これらを極限までチューニングしたパイプラインを構築した瞬間から、あなたのチームはインフラの運用保守という不毛な呪縛から解放され、真にビジネス価値を生むプロダクト開発へと全力を注ぐことができるようになるだろう。

コードを書け。インフラをデプロイしろ。そして、システムを完璧な調和へと導け。

タイトルとURLをコピーしました