【実務・中級編】Pulumiのデプロイを爆速化する並列処理とキャッシング戦略:大規模インフラ管理の最適解 – インフラ構成管理(IaC)活用バイブル

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` が、音を立てるように軽快に完了することを願っている。

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