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

こんにちは!クラウドインフラの世界へようこそ。
私はこれまで数多くの巨大なクラウド環境を設計・運用してきましたが、インフラ構築の自動化(IaC)において、最近多くのエンジニアから熱い視線を集めているツールが「Pulumi(プルミ)」です。

TerraformやAnsibleなどに慣れ親しんだ方でも、「TypeScriptやPython、Goといった普段使いのプログラミング言語でインフラが書ける」という体験は、一度味わうと抜け出せなくなるほどの快適さがあります。

今回は、そのPulumiを使いこなし、大規模なインフラ構築を「圧倒的に高速化する」ための極意を解説します。
「インフラのデプロイに毎回何十分もかかってイライラする…」そんな悩みを抱えているなら、今回の記事はあなたのためのものです。これをマスターすれば、日々の開発作業が劇的に軽くなりますよ!

—

1. Pulumiって何?(基礎と役割)

一言で言えば、Pulumiは「プログラミング言語を使ってクラウド・インフラを定義・管理する次世代のIaCツール」です。

従来のIaCツール(例えばTerraformのHCLなど)は、独自の設定言語を覚える必要があり、複雑な条件分岐やループを書こうとすると頭が痛くなりがちでした。しかしPulumiなら、普段私たちがアプリケーション開発で使っている言語(TypeScript, Python, Go, C#など)をそのまま使えます。そのため、関数化やテスト駆動開発(TDD)、既存のライブラリの流用が驚くほどシームレスに行えます。

ざっくり分かる!環境構築の3ステップ

まずは手元でPulumiを動かせる状態を作りましょう。ここでは最もポピュラーな TypeScript をベースに進めます。

Step 1: Pulumi CLIのインストール

お使いのOSに合わせてCLIをインストールします(MacならHomebrewが一番楽です)。

macOSの場合
brew install pulumi

インストールできたら、バージョンを確認してみましょう。

pulumi version

Step 2: プロジェクトの初期化

適当なディレクトリを作り、そこで初期化コマンドを実行します。対話形式で質問されるので、そのままEnterを押していけばOKです。

mkdir pulumi-fast-demo
cd pulumi-fast-demo
pulumi new aws-typescript

(※途中でAWSの認証情報や、Pulumiのバックエンドログインを求められます。個人学習であればPulumi Serviceの無料アカウントを作成してログインするのが一番簡単です)

Step 3: 動作確認(Hello World的な何か)

プロジェクトが生成されると、`index.ts` というファイルができています。中を覗いてみましょう。これがあなたのインフラの設計図です。

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

// S3バケットを1つ作るだけの超シンプルなコード
const bucket = new aws.s3.Bucket(“my-bucket”, {
bucket: “my-ultra-fast-pulumi-bucket-2026”,
});

// バケットの名前をアウトプット(出力)として定義
export const bucketName = bucket.id;

これをデプロイしてみます。

pulumi up

画面にプレビューが表示され、「yes」と答えると、AWS上に実際にS3バケットが作成されます。おめでとうございます!これでPulumiの基礎マスターの仲間入りです。

—

2. 本題:なぜPulumiのデプロイは遅くなるのか?(グラフ評価と非同期処理)

さて、ここからが本題です。
インフラストラクチャが数十、数百と巨大になってくると、`pulumi up` の実行に時間がかかるようになります。なぜ遅くなるのでしょうか?その秘密は、Pulumiの「グラフ評価エンジン」と「非同期処理(Async/Await)」のメカニズムにあります。

Pulumiのプログラムを実行すると、裏側で「リソースの依存関係グラフ(DAG)」が構築されます。
「データベースがないとアプリケーションサーバが起動できない」といった依存関係をエンジンが解析し、正しい順番でクラウドAPIを叩いていく仕組みです。

しかし、ここで多くの人がやりがちな間違いがあります。それが「不要な明示的 `dependsOn` の多用」です。

「とりあえず dependsOn を書く」がパフォーマンスを殺す理由

例えば、次のようなコードを見たことはありませんか?

// 悪い例:全部に無理やり依存関係を持たせている
const vpc = new aws.ec2.Vpc(“vpc”, { … });
const subnet1 = new aws.ec2.Subnet(“subnet1”, { vpcId: vpc.id }, { dependsOn: [vpc] });
const subnet2 = new aws.ec2.Subnet(“subnet2”, { vpcId: vpc.id }, { dependsOn: [subnet1] }); // ?!
const securityGroup = new aws.ec2.SecurityGroup(“sg”, { vpcId: vpc.id }, { dependsOn: [subnet2] });

`subnet2` を作るのに、なぜ `subnet1` の完了を待たなければならないのでしょうか?
VPCさえあれば、2つのサブネットは同時に(並行して)作れるはずです。

`dependsOn` を無駄にベタ書きすると、Pulumiエンジンは「あ、これは順番に1つずつ作らないといけないんだな」と勘違いし、本来並行処理できるはずのリソースをわざわざ直列(シーケンシャル)に処理してしまいます。これが、デプロイが遅くなる最大の原因です。

—

3. Outputの連鎖を最適化し、時間を劇的に短縮するリファクタリング術

Pulumiを高速化する極意、それは「明示的な `dependsOn` を捨て、暗黙的な `Output` の連鎖に頼る」ことです。

Pulumiの最大の特徴は、リソースのプロパティが `Output` という特殊な非同期オブジェクトとして扱われる点です。リソースAの作成結果(IDやエンドポイントなど)を、リソースBの入力にそのまま渡すだけで、Pulumiエンジンは「自動的にAのあとにBを作るべきだ」と判断します。

リファクタリング前(直列処理で遅いコード)

// リソースを1つずつ順番に作らせようとしている
const role = new aws.iam.Role(“my-role”, { … });
const policy = new aws.iam.Policy(“my-policy”, { … });

// 依存関係を明示的に指定してしまっている
const attachment = new aws.iam.RolePolicyAttachment(“r-p-attach”, {
role: role.name,
policyArn: policy.arn,
}, { dependsOn: [role, policy] }); // ← これ、実は不要です!

リファクタリング後(並行処理を活かした爆速コード)

const role = new aws.iam.Role(“my-role”, { … });
const policy = new aws.iam.Policy(“my-policy”, { … });

// role.name と policy.arn を渡すだけで、Pulumiが勝手に依存関係を解決する
const attachment = new aws.iam.RolePolicyAttachment(“r-p-attach”, {
role: role.name,
policyArn: policy.arn,
}); // dependsOn は書かない!

【プロの知見】
`role.name` や `policy.arn` を入力として渡している時点で、Pulumiは「RoleとPolicyが完成してからAttachmentを作る」という依存関係を完璧に理解しています。そこにわざわざ `dependsOn` を書くのは、エンジンの賢い並行処理能力に「いや、一列に並んで歩いてくれ」と命じるようなものです。`dependsOn` は、APIの仕様上どうしても結びつきが見えない隠れ依存関係(例: IAM Policyが伝播するのを少し待ちたい等)を除き、極力書かないのが高速化の鉄則です。

—

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

では、実際のプロダクション環境(例えば、マイクロサービス向けのVPC、ECSクラスター、RDS、ALBが入り組んだ環境)を想定した、高速化のための設計パターンを見てみましょう。

鉄則:リソース定義はモジュール化し、Promise.allや配列展開を活用する

大量の似たようなリソース(複数のLambda関数や、複数のサブネットなど)を作る場合、ループ処理をうまく使うことで、Pulumiの並行処理の恩恵を最大限に受けることができます。

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

// 例:複数のS3バケットを同時に爆速で生成する
const bucketNames = [“logs”, “assets”, “backups”, “cache”];

// 配列をmapで展開し、Pulumiエンジンに「これら全部並行で頼む!」と伝える
const buckets = bucketNames.map(name => {
return new aws.s3.Bucket(`app-${name}`, {
bucket: `mycompany-prod-${name}-2026`,
// 余計なdependsOnは一切なし
});
});

// アウトプットとしてまとめてエクスポート
export const bucketIds = buckets.map(b => b.id);

このアプローチをとることで、Pulumiは4つのバケット作成リクエストを同時にAWSへ投げます。結果として、デプロイ時間が従来の直列スクリプトに比べて数分単位で短縮されることも珍しくありません。

—

まとめ

今回は、Pulumiのグラフ評価メカニズムと、並行処理を最大化するための `dependsOn` の正しい設計について解説しました。

  • Pulumiはデフォルトで並行処理が得意なスマートなエンジンである
  • 不要な `dependsOn` は、並行処理を阻害しデプロイを遅くする悪者である
  • リソース間のプロパティ(`Output`)の受け渡しだけで、暗黙的かつ最適な依存関係グラフが構築される

この原則を理解してコードをリファクタリングするだけで、あなたのインフラデプロイは見違えるほど高速になります。
「毎回の `pulumi up` が待ち遠しくなる」、そんな快適なIaCライフをぜひ今日から始めてみてください!

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