大規模Puppet環境におけるコンパイル負荷軽減:R10KとPuppet Serverのスケールアウト戦略
こんにちは。大規模インフラの自動化とSREを統括しているテックリードです。
数千台規模のノードをPuppetで管理し始めた組織が、必ず直面する「壁」があります。それが CatalogコンパイルのCPU・メモリ枯渇問題 です。
朝の出社時や大規模な一斉メンテンスの際、Puppet ServerのCPU使用率が100%に張り付き、ノードからの `puppet agent -t` がタイムアウト、あるいは「Failed to open connection」の嵐……。この悪夢のような状況を、あなたも経験したことがあるのではないでしょうか。
今回は、この限界を突破し、数千〜数万ノードの環境でも秒速で構成管理を完遂させるための「R10Kによるコードデプロイの極限最適化」と「Load Balancerを活用したPuppet Serverの水平スケールアウト戦略」を、実戦で培ったコードとアーキテクチャの全貌とともに解説します。
—
1. 大規模環境におけるボトルネックの正体
Puppetのアーキテクチャにおいて、最もリソースを消費するのは JRubyインスタンス上で行われるCatalogのコンパイル です。
Puppet Enterprise (PE) やオープンソースのPuppet Serverは、内部でClojureとJRubyを稼働させており、各ノードの事実上の「状態(Facts)」を受け取ってから、Hiera階層を走査し、ERBテンプレートを評価してカタログを生成します。
ノード数が3,000台を超えたあたりから、以下の要因でスループットが劇的に低下します。
1. JRubyのGlobal Interpreter Lock (GIL) 類似の競合: スレッド数が増えてもCPUコアを使い切れない。
2. Hieraの巨大化と肥大化したモジュール群: コードベースが大きくなるほど、コンパイル時のI/Oとメモリ消費が爆発する。
3. モノリシックなPuppet Serverへの集中: すべてのノードが1台(あるいは数台)のマスターに向かって我先にとリクエストを投げる。
これを解決するには、「コード配信(R10K)の無駄を削ぎ落とすこと」と、「コンパイル処理の負荷分散(スケールアウト)」の両輪が不可欠です。
—
2. R10Kによるコードデプロイの高速化と並列制御
複数のPuppet Serverをスケールアウトさせる場合、すべてのサーバー間で「全く同一のコードベース(環境・モジュール)」がミリ秒単位で同期されていなければなりません。ここでデプロイツールとしてR10Kを使用しますが、デフォルト設定のままでは大規模環境で破綻します。
隠れたキーボードショートカット & 開発スピードを高めるCLIハック
日々のモジュール検証で、ローカルからマスターへのブランチ同期やキャッシュクリアを高速に行うための実戦的コマンドです。
【超重要】キャッシュをクリアしながら特定環境(例: feature-auth)のみを強制的かつ高速にデプロイ
r10k deploy environment feature-auth –pv –force
全環境のデプロイ時に、裏で走るgit fetchを並列化して爆速化する(後述の設定と連動)
r10k deploy environment -p –trace
チーム開発で絶対入れるべき神設定:`r10k.yaml` のベストプラクティス
無駄なGitクローンやI/O待ちを排除し、並列処理を最大化するプロダクション環境向けの設定ファイルです。
/etc/puppetlabs/r10k/r10k.yaml
—
キャッシュディレクトリの指定(SSD上の高速な領域を必ず指定すること)
cachedir: ‘/var/cache/r10k’
複数ブランチを安全に管理するためのソース定義
sources:
# メインのコントロールレポジトリ
- prefix: false
remote: ‘git@github.com:your-org/puppet-control-repo.git’
basedir: ‘/etc/puppetlabs/code/environments’
デプロイ時のパフォーマンスと安全性を担保する高度なチューニング
チーム開発におけるモジュールの競合を防ぎつつ、同期速度を限界まで高める
git:
# Git操作のタイムアウト(秒)。大規模モジュール対策
timeout: 300
# 可能な限りshallow cloneを使用し、ネットワーク転送量を最小化
private_key: ‘/root/.ssh/id_rsa_r10k’
デプロイ後のフック設定(デプロイ成功時に外部へ通知、あるいはPuppet Serverの環境キャッシュをクリア)
postrun: [‘/usr/local/bin/puppet-server-reload-env.sh’]
—
3. 複数Puppet Serverの前段にLoad Balancerを配置するアーキテクチャ
数千台のノードをさばくには、Puppet Serverを複数台並べ、その前段にハードウェアまたはソフトウェアのLoad Balancer(HAProxy / AWS ALB / F5など)を配置し、トラフィックをラウンドロビン(あるいはleast connections)で分散させる必要があります。
全体アーキテクチャ概要
[ Puppet Agent (Node 1..N) ] ──(HTTPS: 8140)──┐
[ Puppet Agent (Node 1..N) ] ──(HTTPS: 8140)──┼──> [ Load Balancer (HAProxy / ALB) ]
[ Puppet Agent (Node 1..N) ] ──(HTTPS: 8140)──┘ │
├──> [ Puppet Server 01 (Compile Pool) ]
├──> [ Puppet Server 02 (Compile Pool) ]
└──> [ Puppet Server 03 (Compile Pool) ]
│ (Shared NFS / GlusterFS / r10k sync)
▼
[ Codebase / Hiera Data ]
1. Load Balancer(HAProxy)の極限設定例
Puppet AgentとServer間の通信は双方向SSL( mTLS )で行われます。そのため、LBでSSL終端(SSL Termination)をせず、TCPパススルー(4層ロードバランス)として構成するのが鉄則です。
/etc/haproxy/haproxy.cfg
global
log /dev/log local0
maxconn 10000
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
option tcplog
option dontlognull
retries 3
timeout connect 5s
timeout client 30m # 長時間のカタログコンパイルやファイル転送を考慮し長めに設定
timeout server 30m
frontend puppet_server_front
bind :8140
mode tcp
# 接続負荷分散アルゴリズム(コネクション数が少ないサーバーを優先)
balance leastconn
default_backend puppet_server_back
backend puppet_server_back
mode tcp
balance leastconn
# ヘルスチェックの設定:Puppet Serverのステータスエンドポイントを監視
option httpchk GET /status/v1/services
http-check expect status 200
# 各Puppet Serverの定義(インターナルIP)
server puppet-master-01 10.0.1.10:8140 check port 8140 inter 10s rise 2 fall 3
server puppet-master-02 10.0.1.11:8140 check port 8140 inter 10s rise 2 fall 3
server puppet-master-03 10.0.1.12:8140 check port 8140 inter 10s rise 2 fall 3
2. Puppet Server側のチューニング設定(`puppetserver.conf`)
複数台構成をとる場合、各Puppet ServerのJRubyインスタンス数をマシンのコア数に合わせて厳密にチューニングする必要があります。
// /etc/puppetlabs/puppetserver/services.d/puppetserver.conf の抜粋
{
// JRuby-9k ランタイム設定
jruby-puppet: {
// このサーバーの物理・仮想CPUコア数に応じて最適化する
// 公式推奨: (CPUコア数 – 1) または メモリ量から逆算(1インスタンスあたり約500MB〜1GB必要)
max-requests-per-instance: 500
// 同時にCatalogをコンパイルできるスレッド数(JRubyインスタンス数)
// 8コア16GBのインスタンスの場合、安全のため「4」〜「6」あたりに設定
master-max-active-instances: 6
// 最大ヒープメモリ(JVM)
// 例: 16GBメモリのサーバーであれば、Xmxを6g〜8gに設定し残りをOSキャッシュやJRubyに割り当てる
},
// プロファイルやメトリクスを有効化し、ボトルネックを可視化
profiler: {
enabled: true
}
}
—
4. チーム開発で役立つ設定の共有化ルールと自動化
スケールアウト環境において最も怖いのは、「あるマスターでは動くが、別のマスターではコンパイルエラーになる(コードの同期ズレ)」というインシデントです。これを防ぐためには、CI/CDパイプラインとR10Kを完全に統合し、人間が手動でマスターにログインして作業することを一切禁止(Immutable Infrastructureの徹底)にするルールが必要です。
チーム開発における黄金律
1. Control Repoファースト: すべての変更は `puppet-control-repo` へのPull Request経由で行う。
2. CIによる構文チェックの強制: PR作成時に GitHub Actions や GitLab CI で `puppet-lint` と `rubocop`、さらに `puppet parser validate` を必ず走らせる。
3. Webhook駆動の自動デプロイ: マージをトリガーに、全Puppet Serverに対してR10Kが数秒以内にコードを同期する仕組みを構築する。
現場で即採用できる GitHub Actions ワークフロー例
.github/workflows/puppet-ci-cd.yml
name: Puppet Production CI/CD
on:
push:
branches:
- production
jobs:
validate-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Control Repo
uses: actions/checkout@v3
- name: Setup Ruby & Puppet Development Kit (PDK)
uses: ruby/setup-ruby@v1
with:
ruby-version: ‘3.2’
- name: Install Dependencies
run: |
gem install puppet-lint r10k
- name: Run Puppet Syntax Validation
run: |
# コントロールレポ内の全ERBやPPファイルの構文を爆速チェック
find . -name “.pp” -exec puppet parser validate {} +
- name: Trigger R10K Deployment via Webhook / SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.PUPPET_LOAD_BALANCER_IP }} # または管理用踏み台
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
echo “=== Starting R10K Deployment across cluster ===”
sudo r10k deploy environment -p
echo “=== Deployment Completed Successfully ===”
—
5. まとめ:スケールアウトがもたらす真の自由
数千台規模のPuppet環境における負荷軽減の要諦をまとめます。
- ボトルネックの特定: カタログコンパイルを担うJRubyの限界を理解し、モノリスな運用を捨てる。
- R10Kの最適化: キャッシュと並列処理をチューニングし、コードの不整合を完全に排除する。
- ロードバランサーの導入: 4層(TCP)パススルーによるHAProxy構成で、リクエストを美しく分散させる。
- CI/CDによる自動化: 人の手によるデプロイを廃し、コードの同期ミスによるコンパイルエラーを根絶する。
このアーキテクチャを導入した現場では、コンパイルエラーの発生率がゼロになり、数千台のノードが並列で数分以内に安全に収束する「圧倒的な安定性」を手に入れることができました。
あなたのインフラも、今日からこの設計思想を取り入れて、真のスケーラビリティを手に入れませんか? 自動化の限界を突破するのは、いつだって緻密に設計されたアーキテクチャです。