【実務・中級編】Pulumiプログラムの高速化手法:並行処理制御(Concurrency)とリソース依存関係(DependsOn)の正しい設計 – インフラ構成管理(IaC)活用バイブル

Pulumiプログラムの高速化極意:並行処理制御と`dependsOn`地獄からの脱出

こんにちは。大規模クラウドインフラの設計・運用において、デプロイパイロットの待ち時間(`pulumi up`のプログレスバーを眺める時間)ほどエンジニアの生産性を削ぐものはありません。

TerraformのHCLが持つ静的な依存関係グラフ評価にフラストレーションを感じ、プログラミング言語の表現力と非同期処理(Async/Await)のパワーを求めてPulumiに移行したチームも多いでしょう。しかし、「TypeScriptやPythonで書けるから」という理由で雑にコードを書くと、かえってデプロイが遅くなり、挙動が予測不能になるという罠に陥ります。

今回は、Pulumiのエンジン内部におけるグラフ評価と非同期処理のメカニズムを解き明かし、`dependsOn`のアンチパターンを排除してプレビュー・更新時間を劇的に短縮する、プロの実践的チューニング手法を伝授します。

—

1. Pulumiのグラフ評価と非同期処理のメカニズム

Pulumiのエンジンは、プログラムを実行してリソースの望ましい状態(Desired State)のグラフ(Resource Graph)を構築し、それをクラウドプロバイダのAPIへ適用します。

ここで重要なのは、Pulumiプログラムはシーケンシャルにリソースを作成しているわけではないという点です。TypeScriptやPythonのランタイム上でプロミス(Promise)やコルーチンが解決されるにつれ、エンジンは「このリソースとこのリソースの間にはデータ依存関係(Data Dependency)がある」と動的に検出し、可能な限りすべてのリソースを並行(Concurrent)に作成・更新しようとします。

[Resource A] –(Outputの参照)–\
–> [Resource C] (自動的にA, Bの作成を待つ)
[Resource B] –(Outputの参照)–/

この「データ依存関係」の連鎖が正しく設計されていれば、Pulumiは極めて高い並行性を発揮し、数百あるリソースであっても一瞬でデプロイを完了させます。

—

2. なぜ「不要な明示的 `dependsOn`」がパフォーマンスを殺すのか

初心者にありがちな失敗が、「順序を保証したいから」という理由で `dependsOn` を乱用することです。

// ❌ アンチパターン:不要な明示的 dependsOn の連鎖
const vpc = new aws.ec2.Vpc(“vpc”, { … });
const subnetA = new aws.ec2.Subnet(“subnet-a”, { vpcId: vpc.id }, { dependsOn: [vpc] });
const subnetB = new aws.ec2.Subnet(“subnet-b”, { vpcId: vpc.id }, { dependsOn: [subnetA] }); // 致命的!

パフォーマンスが落ちる理由

1. 並行性の破壊: `dependsOn` は、エンジンに対して「データとしての依存関係はないが、実行順序を強制的に直列化せよ」と命令するものです。これにより、本来なら同時に作れるはずのリソースが1つずつ順番に作られるようになり、デプロイ時間が線形(リソース数 × API遅延)に増大します。
2. グラフ評価の肥大化: 不要な依存関係エッジがグラフに追加されることで、Pulumiのプランニングフェーズ(`pulumi preview`)におけるメモリ消費と計算量が爆発します。

原則:明示的な `dependsOn` は、APIの仕様上の理由(例:IAMロールがアタッチされる前にポリシーの伝播を待ちたい等)で、どうしてもデータ参照が作れない最終手段にのみ使え。

—

3. Outputの連鎖を最適化し、プレビュー・更新時間を劇的に短縮するリファクタリング術

データ依存関係は、リソースの `Output` プロパティを別のリソースの入力として渡すことで、自動的かつ安全に構築されます。

リファクタリング前:手動の順序制御と遅延

❌ Pythonでのアンチパターン
import pulumi_aws as aws

仮想的な処理待ちや順序制御を意識しすぎてコードが縦長に
sg = aws.ec2.SecurityGroup(“sg”, …)
ここで意味もなくダミーの処理を入れたりする

リファクタリング後:`apply` と `Output` の活用

リソースの作成順序を制御したいときは、`dependsOn` ではなく `Output.apply()` を使ってデータの流れ(Data Flow)として表現します。

// ✅ ベストプラクティス:Outputの連鎖による暗黙の並行制御
const vpc = new aws.ec2.Vpc(“vpc”, { cidrBlock: “10.0.0.0/16” });

// Subnet A と Subnet B は、vpc.id を参照しているため、
// vpc の作成完了後に「自動的に並行して」作成される。
const subnetA = new aws.ec2.Subnet(“subnet-a”, {
vpcId: vpc.id,
cidrBlock: “10.0.1.0/24”,
});

const subnetB = new aws.ec2.Subnet(“subnet-b”, {
vpcId: vpc.id,
cidrBlock: “10.0.2.0/24”,
});

この設計により、エンジンは `subnetA` と `subnetB` が独立していることを即座に理解し、両者を同時にAWS APIへリクエストします。

—

4. 大規模プロジェクトでの具体的なチューニング事例

数十〜数百のマイクロサービスやAWSインフラが混在するモノリシックなPulumiプロジェクトにおいて、パフォーマンスを限界まで引き出すための実践テクニックです。

A. プロジェクトの分割(Stack Referencesの活用)

すべてを1つのPulumiプログラムに入れるのはアンチパターンです。VPC、Kubernetesクラスター、各アプリケーション層は別々のプロジェクト(=別々のStateファイル)に分割し、`pulumi.StackReference` で結合します。これにより、変更のあったレイヤーだけが高速にプレビュー・更新されます。

B. 開発スピードを爆発させるキーボードショートカット

日常のコーディングやデプロイにおいて、以下のショートカットを体に覚え込ませてください。

  • `pulumi up –refresh` の代わりにプレビューを活用する:

`pulumi preview` を即座に実行するエイリアスをターミナルに設定。

  • 対話型プロンプトのスキップ:

CI/CDや日常の高速デプロイでは `-y`(`–yes`)と `–skip-preview`(状況に応じて)を使い分けますが、開発時はプレビュー結果の差分(Diff)を視覚的に捉える癖をつけましょう。

—

5. 現場で即効性を発揮する設定・ツールの極意

絶対入れるべき神プラグイン・拡張機能

1. Pulumi Language Server (IDE Integration):
VS CodeやNeovimのLSPと連携させ、`Output` の型安全性をリアルタイムで視覚化します。これがないコードレビューは自殺行為です。
2. jq + Pulumi CLI:
`pulumi stack export` の出力を `jq` でパースし、リソースグラフのボトルネックを特定するスクリプトをチームで共有しましょう。

チーム開発で絶対共有すべき `Pulumi.yaml` の設定ルール

大規模開発では、リソースの並行処理数(Parallels)を明示的に制御することが、APIレートリミット(スロットリング)回避の鍵になります。

Pulumi.yaml のベストプラクティス構成例
name: production-infrastructure
runtime: nodejs
description: Highly optimized enterprise cloud infrastructure using Pulumi

config:
pulumi:tags:
value:
environment: production
owner: platform-sre-team

options:
# AWS APIのレートリミット(Throttling)を回避しつつ、最大限の並行性を引き出す
# デフォルトは無制限に近いか環境依存だが、大規模環境では明示的チューニングが必須
parallel: 30

> SREの現場からの警告: `parallel` の値を無限に上げればいいというものではありません。AWSやGCPのAPIスロットリング(`RequestLimitExceeded`)に当たり、かえってリトライ処理でデプロイが遅延します。「お使いのクラウドプロバイダの耐えうる限界値の8割」に設定するのがプロの技です。

—

まとめ

Pulumiの真価は、「コードでインフラを書けること」ではありません。「プログラミング言語の抽象化能力と非同期ランタイムをフルに活かし、インフラ構築の速度を限界まで高められること」にあります。

  • 無駄な `dependsOn` を捨て、データ依存関係(`Output`)に委ねる。
  • グラフ評価の並行性を阻害しないコード構造を徹底する。
  • 適切な `parallel` 設定でAPIレートリミットをハックする。

今日からあなたのコードをリファクタリングし、あの待ち遠しいプログレスバーの完了スピードを体感してください。インフラエンジニアとしての腕の見せ所です。

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