複数サービスを同時管理する際に頭を悩ませた問題
本番環境で5〜10個のサービスが並行稼働している場合 — MySQL、Redis、Nginx、Flaskアプリ、Celeryワーカーなど — 手動管理はすぐに限界を迎えます。以前、deploy.shのようなbashスクリプトを大量に抱えていた時期がありました。新しいサービスを追加するたびにスクリプトを書き、ドキュメントを更新し(誰も読まないのですが)、起動順序を頭に入れておく必要がありました。「MySQLを先に、次にRedis、アプリは最後…」といった具合に。
スケールが必要になった時はさらに大変でした。トラフィックのピーク時に対応するためWebノードを2台追加しなければならず、1台ずつ手動でインストールし、設定をコピーして接続テストをする作業に半日かかりました。しかも更新のたびにどのサービスが壊れるか心配でした。新メンバーはシステムを理解するためにWikiを何十ページも読む必要があり、そのWikiは常に実態より数ヶ月遅れていました。
原因分析:従来ツールに何が足りないのか?
Ansible、Chef、Puppet — どれも優れた構成管理ツールです。しかし、共通の盲点があります。「WHAT」(マシンをどう設定するか)は表現できるが、「HOW」(サービス同士がどのように連携するか)は理解できないという点です。
Ansibleでは、ノードAにMySQLをインストールし、ノードBにWordPressをインストールするplaybookを書けます。しかし、その関係性 — WordPressがどのユーザーで、どのデータベースに、どんなパスワードで接続するか — はハードコードするか、variableで個別管理しなければなりません。トポロジーが変わると(MySQLレプリカの追加、IP変更など)、3〜4箇所を手動で更新してからplaybookを再実行する必要があります。
Kubernetes + Helmはコンテナオーケストレーションの問題をうまく解決します。しかし、ベアメタルや古いVM上で動作している場合や、コンテナ化が難しいサービス(レガシーデータベース、ハードウェア固有のワークロードなど)がある場合は、Helmは適切な選択肢ではありません。
3つすべてが見落としていること:どのツールもサービス間の関係のセマンティクスを理解できていません。「WordPressはデータベースが必要」「KafkaにはZooKeeperが必要」「GrafanaはPrometheusをデータソースとして必要」といった意味的な関係です。トポロジーが変わると、結局人間が手で介入しなければなりません。
複雑なオーケストレーション問題の解決アプローチ
アプローチ1:バージョン管理された手動スクリプト
bash/Pythonスクリプトを丁寧に書き、Gitで管理し、ドキュメントを整備する方法です。最も一般的なアプローチですが、メンテナンスに手間がかかり、スケールが難しい — トポロジーが変わるたびに手動での介入が必要です。
アプローチ2:Dynamic InventoryによるAnsible
Ansibleのdynamic inventoryとgroup_varsを組み合わせてサービス間の関係を管理する方法です。純粋なスクリプトよりは優れていますが、サービスの追加・削除時のロジックは自分で書く必要があります。
アプローチ3:Kubernetes Operators
Kubernetesをすでに使用している場合、Operatorsパターン(Prometheus OperatorやMongoDB Operatorが行っているようなもの)はコンテナ環境でのライフサイクル管理問題を解決します。ただし、K8sクラスタと、operatorを自作するためのGo/Pythonの深い知識が必要で、production-readyな状態にするまで通常数週間かかります。
アプローチ4:JujuとCharmed Operators
JujuはoperatorパターンをKubernetesだけでなく、ベアメタルやVMにも適用します。Charmed Operators(Charms)は、deploy、configure、scale、upgradeのロジック全体をパッケージ化したPythonパッケージです — 他サービスとの関係も含めて。
最善策:Juju Model-drivenオーケストレーション
把握すべき核心概念
- Controller:Jujuの「頭脳」。システム全体を管理し、専用マシンまたはコンテナ上で動作する
- Model:アプリケーションとそれらの関係をまとめた論理グループ(namespaceに相当)
- Charm:特定アプリのdeploy/configure/scale/upgradeロジックを格納したPythonパッケージ
- Relation:2つのサービスが情報(credentials、endpoints、config)をやり取りする方法を定義
- Unit:アプリケーションの1インスタンス(K8sのPodに相当)
UbuntuにJujuをインストールする
sudo snap install juju --classic
LXDでControllerをBootstrapする(ローカル環境)
# LXDがインストールされていない場合はインストール
sudo snap install lxd
lxd init --auto
# Controllerをブートストラップ — JujuがControllerとして専用LXDコンテナを作成
juju bootstrap localhost lxd-controller
Ubuntu 22.04で動作する会社のステージング環境でこの設定をテストしてから本番に適用しました。Bootstrapはネットワーク速度にもよりますが、約3〜5分かかります。
アプリケーションのデプロイとサービスの接続
# 新しいモデルを作成
juju add-model mywebapp
# CharmhubからMySQLとWordPressをデプロイ
juju deploy mysql
juju deploy wordpress
# ステータスを監視 — "active"になるまで待機
juju status
juju statusの典型的な出力:
App Version Status Scale Charm
mysql 8.0.36 active 1 mysql
wordpress 6.4.2 waiting 1 wordpress waiting: ready
Unit Workload Agent Public address
mysql/0* active idle 10.0.0.10
wordpress/0* waiting idle 10.0.0.11
最も重要なステップ — 2つのサービスを接続する:
juju integrate wordpress:db mysql:db
このコマンド実行後、Jujuが自動で:
- MySQL charmが新しいデータベースとユーザーをランダムcredentialsで作成
- host、port、dbname、user、passwordをrelation data経由でWordPress charmに送信
- WordPress charmがその情報で
wp-config.phpを書き込む - 必要に応じてPHP-FPMを再起動 — SSHで入る必要なし
ライフサイクル管理:スケール・設定・アップグレード
# WordPressを3ユニットにスケール — Jujuが自動でコンテナ作成、デプロイ、MySQL接続
juju scale-application wordpress 3
# 設定の確認と変更 — charmが自動適用、SSH不要
juju config wordpress
juju config wordpress blog-title="IT From Zero"
# charmを新バージョンにアップグレード
juju refresh wordpress --channel latest/stable
# charmに定義されたアクションを実行
juju actions mysql # アクション一覧を確認
juju run mysql/0 backup # バックアップを実行
複雑なスタックのデプロイ:数分でKafkaとモニタリング環境を構築する
juju add-model kafka-stack
# KafkaとZooKeeperをデプロイ
juju deploy kafka --channel 3/stable
juju deploy zookeeper --channel 3/stable
juju integrate kafka:zookeeper zookeeper:zookeeper
# モニタリングを追加 — PrometheusとGrafanaが自動で相互設定
juju deploy prometheus2
juju deploy grafana
juju integrate kafka:metrics prometheus2:scrape
juju integrate prometheus2:grafana-source grafana:grafana-source
# システム全体を確認
juju status --color
結果:手動で設定ファイルを1行も書かずに、PrometheusメトリクスとGrafanaダッシュボードを完備したKafkaクラスタが構築できます。
実際に導入する際の注意点
- JujuはDocker/Kubernetesの代替ではない — 補完関係にあります。JujuはK8sクラスタにcharmをデプロイできます(Juju + K8sモード)。その場合、charmはVMではなくKubernetesリソースを管理します。
- 高品質なcharmを選ぶ:Charmhub上のすべてのcharmがproduction-readyとは限りません。Canonicalバッジやダウンロード数の多いcharmを優先し、実際に使う前にREADMEをよく読んでください。
- 概念の学習に投資する:本番デプロイの前に、model、controller、charm、relation、unitを理解するために1〜2日かけましょう。
- 複雑なスタックに最適:Charmed Kubernetes、Kubeflow charmを使ったMLOps、Spark + HDFSのデータプラットフォームなど。1〜2つの単純なサービスであれば、Ansibleの方が手軽です。
まとめると:Jujuは「マシンにソフトウェアをインストールする」問題を解決するのではなく、「複雑なアプリケーションのライフサイクル全体を、再現可能でチーム全体が標準化された方法で管理する」問題を解決します。それはAnsible、Chef、Helmがこれまで目指してきたものとは根本的に異なります。

