Pulumiデプロイを爆速化する並列処理とキャッシング戦略:数千リソースを従える大規模インフラの最適解
こんにちは。大規模クラウド基盤のSREを率いるテックリードの皆さん、あるいは日々の `pulumi up` の待ち時間にコーヒーを何杯もおかわりしている皆さん。
リソース数が数千規模に達した巨大な Pulumi プロジェクトにおいて、単一のモノリシックなスタックで `pulumi preview` や `pulumi up` を実行したとき、プログレスバーが永遠に進まない絶望を味わったことはないだろうか?
「Terraformよりは速いはずのPulumiなのに、なぜこんなに遅いのか?」
その原因はツールにあるのではなく、「並列度のデフォルト値への依存」「モノリシックすぎるスタック設計」「言語ランタイムのIOボトルネック」という、我々のアーキテクチャの設計ミスにある。
本記事では、Pulumiのエンジン内部の挙動とクラウドプロバイダーのAPI制限の限界ギリギリまで攻め込み、デプロイ速度を最大10倍に引き上げるための実践的な極限チューニングノウハウを余すところなく伝授する。
—
1. パフォーマンス低下の根本原因:なぜ巨大スタックは遅くなるのか?
Pulumiのアーキテクチャは、宣言されたコードとクラウドプロバイダーのAPIの間に「Language Host」「Resource Engine」「Resource Provider」という3層のプロセス挟んでいる。数千のリソースを扱う場合、以下の3点がボトルネックになる。
1. デフォルトの並列度(Parallelism)の罠: Pulumiのデフォルト並列度は `8` である。現代のクラウドAPIやマルチコアCPU、非同期I/Oのポテンシャルから見れば、これは圧倒的に低すぎる。
2. 依存関係グラフ(DAG)の過剰な肥大化: 全てのリソースを1つのスタックに入れているため、関係のないリソースの変更検知やステート(State)のロック・同期に無駄な時間がかかる。
3. 言語ランタイムのシリアライゼーション: TypeScript/Node.jsやPythonにおいて、オブジェクトのシリアライズ・デシリアライズやガベージコレクションがCPUを圧迫する。
これを解決するための具体的なアプローチを見ていこう。
—
2. デプロイを爆速化する3つの柱
柱①:並列度(Parallelism)の限界突破
`pulumi up` または `pulumi preview` を実行する際、`–parallel` フラグで同時に処理するリソース数を指定できる。これをインフラの規模とAPIレートリミットに合わせて引き上げる。
並列度を「64」に引き上げてデプロイを爆速化する
pulumi up –parallel 64
ただし、無制限に大きくすれば良いわけではない。AWSの各API(IAM, EC2, RDSなど)には秒間リクエスト数(TPS)の制限がある。AWS側で `ThrottlingException` が頻発する場合は逆効果になるため、「プロバイダー側のRate Limitの許容値 ÷ リソースあたりのAPIコール数」の黄金比を見極める必要がある。
柱②:ドメイン駆動型スタック分割(Micro-Stacks)の設計思想
数千リソースを1つのスタックで管理するのを今すぐやめよう。Terraformにおける「モジュール分割」の概念を、Pulumiでは「スタック(Stack)の疎結合な分割」として実装する。
- Network Stack: VPC, Subnets, Route Tables (変更頻度:極小)
- Data Stack: RDS, ElastiCache, S3 (変更頻度:低、データ消失リスク高)
- Compute Stack: EKS, ECS, Lambda (変更頻度:高)
これらを `pulumi.yaml` とスタック参照(`pulumi.StackReference`)を使って有機的に結合する。これにより、頻繁に変更されるCompute層のデプロイ時に、変更のないNetwork層やData層のグラフ評価を完全にバイパスできる。
柱③:言語ランタイム別のチューニングノウハウ
多くの場合、Pulumiの言語には TypeScript (Node.js) や Python が選ばれる。それぞれのランタイムで高速化のためのチューニングポイントが存在する。
- TypeScript / Node.js の場合:
- Node.jsのイベントループをブロックしないよう、重い同期処理を排除する。
- `tsconfig.json` で不要な型チェックを省き、ビルドを高速化する。
- 可能であれば、V8エンジンのヒープサイズを拡大する (`NODE_OPTIONS=”–max-old-space-size=4096″`)。
- Python の場合:
- グローバルインタプリタロック(GIL)の影響を避けるため、可能な限り非同期プログラミングパターン(`asyncio`)を活用したプロバイダー実装を行う。
—
3. 開発スピードを劇的に高めるプロの環境設定
日々の開発イテレーションを高速化するため、チーム全体で導入すべき設定とツールを紹介する。
隠れたキーボードショートカット & CLIテクニック
- `–refresh` の悪夢を断ち切る:
デプロイのたびに `–refresh` をつけるのは厳禁だ。すべてのクラウドAPIにステートの同期確認を取るため、数千リソースの環境では数十分のロスを生む。明示的にリソースのドリフトを直したい時だけに限定せよ。
- プレビューの高速化(Targeting):
特定のコンポーネントだけをテストしたいときは、`-t`(`–target`)フラグを使って評価範囲を絞り込む。
pulumi preview –target “urn:pulumi:prod::my-app::aws:ec2/subnet:Subnet::app-subnet-1”
絶対入れるべき神プラグイン・ツール
1. Pulumi Graph Visualizer (`pulumi graph`):
依存関係のループや、予期せぬクリティカルパスを視覚化する。
pulumi graph –dot > graph.dot
dot -Tpng graph.dot -o graph.png
2. direnv:
環境変数(`PULUMI_ACCESS_TOKEN`, AWSプロファイルなど)をディレクトリごとに自動切り替えし、コンテキストスイッチのロスをゼロにする。
—
4. チーム開発で役立つ設定の共有化ルール
CI/CDパイプラインや複数人での開発において、パフォーマンスと安全性を両立させるための `Pulumi.yaml` および設定ファイルのベストプラクティスを公開する。
最適化された `Pulumi.yaml` のベストプラクティス構成例
name: enterprise-cloud-infra
runtime:
name: nodejs
options:
# TypeScriptのビルド設定を最適化
packagemanager: pnpm
description: Highly scalable, parallelized enterprise infrastructure stack
config:
pulumi:tags:
value:
environment: production
managed-by: pulumi
cost-center: “1042”
タイムアウトや並列度のデフォルトをプロジェクトレベルで定義
options:
pluginDownloadURL: “https://get.pulumi.com”
チーム共有のための `Pulumi.settings.yaml` (グローバル設定)
開発者のローカル環境における挙動を統一し、無駄なビルドや不要なログ出力を抑制するための設定。各エンジニアの `~/.pulumi/config.yaml` またはプロジェクトルートに配置する。
プロジェクト非依存のグローバル設定
clear-screen-on-refresh: true
skip-update-check: false
ログ出力レベルを最適化し、I/Oボトルネックを防ぐ
log-flow: false
tracing: “”
—
5. CI/CDパイプライン(GitHub Actions)での極限キャッシュ戦略
数千リソースを扱うプロジェクトでは、CI/CD上でのプラグインダウンロードと依存関係のインストール時間が無視できない。以下の GitHub Actions ワークフロー例は、キャッシュを極限まで活用し、ジョブの立ち上がりを爆速化する決定版だ。
name: Pulumi Production Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘pnpm’ # pnpmのキャッシュを有効化
- name: Setup Pulumi
uses: pulumi/action-up@v3
with:
stack-name: production
# デプロイ時に自動でスタックを選択・作成
work-dir: ./infra
env:
- name: Restore Pulumi Plugin Cache
uses: actions/cache@v3
with:
path: ~/.pulumi/plugins
key: ${{ runner.os }}-pulumi-plugins-${{ hashFiles(‘/Pulumi.yaml’) }}
restore-keys: |
${{ runner.os }}-pulumi-plugins-
- name: Run High-Performance Pulumi Up
uses: pulumi/actions@v5
with:
command: up
stack: production
# 並列度を「48」に指定し、APIの限界まで高速化
args: –yes –skip-preview –parallel 48
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
—
結びにかえて:スピードはインフラエンジニアの正義である
インフラのデプロイ速度は、そのまま開発チームのデリバリー速度(Time to Market)に直結する。
「数千リソースあるから遅いのは仕方ない」という諦めは今日で終わりにしよう。
スタックを適切にドメイン分割し、`–parallel` で並列度を極限まで高め、キャッシュを制圧すれば、巨大なインフラストラクチャであっても秒速のフィードバックループを手に入れることができる。
あなたの `pulumi up` が、音を立てるように軽快に完了することを願っている。