Skip to content
Published on

Operator vs Helm vs GitOps — 何をいつ使うか

Authors

はじめに — 3つを同じ土俵に並べるよくある誤解

Kubernetes をある程度運用したことがある方なら、「うちは Helm を使っているが Operator も必要か」「GitOps を導入したら Helm は捨てるのか」といった質問を一度は受けたり投げかけたりしたことがあるでしょう。これらの質問が混乱を招くのは、Operator、Helm、GitOps が同じ席を争う3人の候補のように見えるからです。

しかしこの3つは、そもそも異なる問題を解きます。一行で要約すると次の通りです。

  • Helm は Kubernetes マニフェストをパッケージ化してテンプレート化するツールです。「何をデプロイするか」を束として定義します。
  • GitOps は Git リポジトリの宣言をクラスタ状態へ継続的に同期する運用方式です。「どうデプロイし維持するか」を定義します。
  • Operator は特定アプリケーションの運用知識をコードに収めたコントローラです。「デプロイ以降の運用を誰が代わりに行うか」を定義します。

つまりこれらは競合関係ではなく、おおむね補完関係です。実際のプロダクションでは3つを同時に使うのが最も一般的です。本記事の目的は、各ツールの本質をはっきり切り分け、どの状況で何を選ぶべきかの判断基準を立てることです。

3つの本質 — パッケージング、同期、コントローラ

Helm — パッケージングとテンプレート

Helm の核心は「チャート(chart)」というパッケージです。チャートは複数の Kubernetes マニフェスト(Deployment、Service、ConfigMap など)を1単位にまとめ、値(values)を注入して環境ごとに異なる結果を生成します。

# templates/deployment.yaml (Helm テンプレートの一部)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: app
          image: my-app:1.2.3

同じチャートに値だけ変えて dev/stage/prod を作るのが Helm の典型的な使い方です。重要なのは、Helm 自体は「一度レンダリングしてクラスタへ送る」ツールだという点です。helm install や helm upgrade を実行したその瞬間にのみ動作し、その後に誰かがクラスタを手で変えても Helm は気づきません。Helm は静的デプロイツールです。

GitOps — 宣言的な継続同期

GitOps はツールというより運用モデルです。核心となる原則は4つです。

  1. システムのあるべき状態(desired state)をすべて宣言で表現する。
  2. その宣言を Git でバージョン管理する。Git が単一の真実の供給源(single source of truth)になる。
  3. 承認された変更は自動的にクラスタへ適用される。
  4. エージェントが実際の状態と宣言を継続的に比較し、ドリフトが生じれば通知または巻き戻す。

Argo CD や Flux といったツールがこのモデルを実装します。ここで決定的な違いは4番、継続的 reconcile です。誰かがクラスタで replicas を手動で変えても、Argo CD はすぐにそれをドリフトとして検知し Git の状態へ戻します。Helm が「撃ちっぱなし」なら、GitOps は「絶え間ない照準補正」です。

Operator — 運用知識を収めたコントローラ

Operator はもう一段深く入ります。Helm と GitOps が Kubernetes のビルトインリソースを扱うことに集中するのに対し、Operator は新しいリソース種別(CRD、Custom Resource Definition)を追加し、それを世話する専用コントローラを一緒にデプロイします。

例えば PostgreSQL Operator をインストールすると、クラスタに PostgresCluster のような新しいリソースタイプができます。ユーザーはこう宣言します。

apiVersion: postgres.example.com/v1
kind: PostgresCluster
metadata:
  name: orders-db
spec:
  instances: 3
  version: "16"
  backup:
    schedule: "0 2 * * *"

この宣言1つに対して、Operator は StatefulSet 生成、レプリケーション構成、フェイルオーバー、バックアップのスケジューリング、マイナーバージョンアップグレードまで自動で処理します。人が直接行うはずだった運用手順(Day-2 operations)をコードで自動化したのです。

核心的な違いを図にすると次の通りです。

Helm:    values -> [一度レンダリング] -> マニフェスト -> クラスタ
                  (実行時点のみ動作)

GitOps:  Git の宣言 -> [エージェントが比較し続ける] -> クラスタ
                      (ドリフト自動補正、継続 reconcile)

Operator: CR 宣言 -> [専用コントローラが reconcile し続ける] -> ビルトインリソース + 運用行為
                    (バックアップ/フェイルオーバー/アップグレードまで自動化)

静的デプロイ vs 継続的 reconcile

3つのツールを分ける最も重要な軸は「一度だけ動くか、動き続けるか」です。

区分動作時点ドリフト補正たとえ
Helm 単独コマンド実行の瞬間なし写真を1枚撮る
GitOps常時(周期 + イベント)自動オートフォーカスカメラ
Operator常時(イベント駆動)自動 + 運用行為専任運用者を雇う

Helm を単独で使うと、クラスタ状態は次第に宣言から外れます。誰かがホットフィックスで kubectl edit をし、別の誰かが replicas を一時的に上げ、時間が経つと「今クラスタに何が乗っているか」を誰も正確に知らなくなります。GitOps はまさにこのドリフト問題を構造的に防ぎます。Operator はここからさらに一歩進み、単にマニフェストを合わせるだけでなく、アプリケーション固有の運用行為(バックアップ、フェイルオーバーなど)まで reconcile ループの中で処理します。

Day-1 vs Day-2 — 境界はどこか

運用の世界では作業を Day-0/Day-1/Day-2 に分けて呼びます。

  • Day-0: 設計と計画。アーキテクチャ決定、容量見積もり。
  • Day-1: インストールと初期デプロイ。「最初の起動」。
  • Day-2: 運用。アップグレード、バックアップ/復旧、スケーリング、障害対応、パッチ。システム寿命の大半を占める区間。

この区分で3つのツールの強みを見ると明確になります。

  • Helm は Day-1 に強いです。複雑なアプリケーションを一度できれいにインストールするのに最適です。しかし Day-2 の運用行為は Helm 自体には含まれません。
  • GitOps は Day-1 と Day-2 の両方にまたがります。初期デプロイから以降のすべての変更を同じ Git ワークフローで管理します。ただし GitOps も「宣言を合わせる」ところまでで、アプリケーション内部の複雑な運用(例: DB のフェイルオーバー手順)を自ら知るわけではありません。
  • Operator は Day-2 に強いです。アプリケーション特有の運用知識がコントローラのコードに収められており、人が毎回介入する必要をなくします。

結論として Operator は、Day-2 運用が複雑で反復的なステートフルアプリケーション(データベース、メッセージキュー、検索エンジン)で真価を発揮します。一方、運用が単純なステートレス Web アプリケーションなら、Operator は過剰な選択になり得ます。

組み合わせパターン — 実践では3つを一緒に使う

最も一般的で推奨される構成は、3つの役割が重ならないように配置することです。

パターン1 — GitOps で Helm チャートをデプロイ

Argo CD や Flux は Helm チャートを直接ソースとして扱えます。つまりチャートはパッケージング単位として使い、それをクラスタへ適用し維持する仕事は GitOps エージェントが担います。

# Argo CD Application — Helm チャートを GitOps で管理
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/example/my-app-chart
    targetRevision: main
    path: charts/my-app
    helm:
      valueFiles:
        - values-prod.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

こうすると Helm のテンプレート能力と GitOps の継続同期/ドリフト補正を同時に得られます。helm install を人が直接実行することはなくなり、すべての変更は Git PR を通じて流れます。

パターン2 — Helm で Operator をインストールし、GitOps で CR を管理

Operator 自体は通常 Helm チャートや OLM(Operator Lifecycle Manager)でインストールします。つまり Operator コントローラのデプロイは Helm が、その Operator が扱うカスタムリソース(CR)は GitOps が管理します。

[プラットフォームチーム]
  Helm チャートで PostgreSQL Operator をインストール(コントローラ + CRD)
        |
        v
[アプリケーションチーム]
  Git に PostgresCluster CR を宣言 -> Argo CD が同期
        |
        v
  Operator が CR を reconcile -> StatefulSet/バックアップ/フェイルオーバーを自動化

この構成では各自の責任がきれいに分かれます。Helm は「Operator ソフトウェアを入れる」、GitOps は「宣言をクラスタと一致させる」、Operator は「宣言された DB を実際に運用する」。3つのツールが互いの領域を侵しません。

パターン3 — App-of-Apps とブートストラップ

大規模プラットフォームでは Argo CD の App-of-Apps パターンで「Argo CD が他のすべての Application を管理」するようにし、それらの Application が再び Helm チャート(共通インフラ)と Operator のインストール、そしてワークロードをデプロイします。クラスタブートストラップ全体が1つの Git ツリーから流れ出る構造です。

意思決定テーブル — 何をいつ

次の表は状況ごとにどのツールが適するかを整理したものです。

状況推奨ツール理由
ステートレス Web アプリのデプロイHelm + GitOps運用が単純、Operator 不要
環境別の値だけ異なる多数のデプロイHelm + GitOpsテンプレート + 継続同期
本番データベースの運用Operatorバックアップ/フェイルオーバー/アップグレードの自動化が必要
メッセージキュー、検索エンジンの運用Operator複雑な Day-2 運用
複数クラスタの一貫管理GitOpsGit 単一の真実の供給源
ドリフトが頻繁な環境GitOpsselfHeal で自動補正
社内標準ワークフローの自動化Operator(または単純コントローラ)運用知識のコード化
単発の実験/PoCHelm 単独最も速い、ガバナンス不要

表を暗記するより1つの質問に圧縮できます。「このシステムの運用(Day-2)は反復的で、アプリケーション特有の知識を要するか」。そうなら Operator を、そうでなければ Helm + GitOps の組み合わせをまず検討してください。

ケース別の推奨

  • 新興 SaaS のバックエンド API: Helm チャート1つ + Argo CD。デプロイが単純で変更が頻繁なので、GitOps の PR ベースの流れが最大の利点を与えます。Operator はまだ不要です。
  • 金融系のエアギャップ環境の PostgreSQL: 検証済みの Operator を Helm でインストールし、CR は GitOps で管理。バックアップとフェイルオーバーを人手に委ねられない環境なので、Operator の自動化が要です。
  • 数十のエッジクラスタへの共通エージェントのデプロイ: GitOps 中心(Flux のマルチテナンシーまたは Argo CD の ApplicationSet)。クラスタごとに同一宣言を一貫適用することが最優先です。
  • 機械学習プラットフォームのモデルサービング: サービングフレームワークが提供する Operator(CRD ベース)を使い、その上のワークロードは GitOps で管理。

Operator が過剰な場合 — コストを直視する

Operator は強力ですがタダではありません。次の場合、Operator は過剰エンジニアリングである可能性が高いです。

  • 運用行為が単純で、Deployment 1行の修正で十分な場合。CRD とコントローラを作る/運用する理由がありません。
  • チームにコントローラを保守する Go の力量や人員がない場合。自作の Operator はもう1つの保守対象になります。
  • 変更頻度が低く、自動化への投資が回収できない場合。

Operator の本当のコストはコードを書くことより長期の保守にあります。Kubernetes のバージョンが上がるたびに controller-runtime の依存を追わねばならず、RBAC 権限を精緻に管理せねばならず、reconcile ロジックのバグはそのまま運用事故につながります。「外部のよく管理された Operator を使う」ことと「自分で Operator を作る」ことは、まったく異なるコスト構造であることを明確に区別すべきです。

移行 — Helm チャートから Operator へ

成熟したアプリケーションはしばしば Helm チャートで始まり、運用の複雑度が増すにつれて Operator へ進化します。典型的な道筋は次の通りです。

  1. 現在のチャートの運用負担をリスト化する。 人が手動で行っていた手順(バックアップ、スケーリング、障害復旧)をすべて書き出します。これが Operator が自動化する候補です。
  2. CRD のスキーマを設計する。 ユーザーが宣言する最小限の意図(例: インスタンス数、バージョン、バックアップスケジュール)だけ残し、残りはコントローラに決めさせます。
  3. 段階的に移行する。 最初は Operator が既存チャートが作っていたのと同じリソースを生成するようにし、その後バックアップ/フェイルオーバーのような運用行為を段階的に追加します。
  4. 共存期間を設ける。 既存の Helm デプロイと Operator ベースのデプロイをしばらく並行運用し、等価性を検証してから切り替えます。

逆に「あえて Operator へ行く必要はない」という結論なら、そのまま Helm + GitOps にとどまるのも十分に正しい選択です。移行そのものが目的になってはいけません。

運用コスト比較

項目Helm + GitOpsOperator(外部導入)Operator(自社開発)
初期導入の難易度
Day-2 自動化レベル限定的
保守負担中(アップストリーム追従)高(自前でコード管理)
必要な力量YAML、GitOps導入/設定Go、controller-runtime
障害時のデバッグマニフェスト追跡CR + コントローラログコントローラのコードまで

この表の核心メッセージは「自動化レベルが上がるほど保守負担も一緒に上がる」ことです。自動化はタダではなく、コストを未来へ移すことに近いのです。

観測とトラブルシューティング — ツールごとにどこを見るか

障害が起きたときどこを見るべきかはツールごとに異なります。この地図を前もって持っておくと対応速度が大きく上がります。

症状Helm の観点GitOps の観点Operator の観点
デプロイが反映されないhelm history を確認Application の同期状態を確認CR status とコントローラログ
リソースが何度も戻る(該当なし)selfHeal のドリフト補正を疑うreconcile ループを疑う
一部の環境だけ異なるvalues ファイルを比較環境別ソースパスを確認CR spec の差を確認
リソースが消えないhelm uninstall の残りprune 無効を疑うfinalizer 未完了を疑う

核心は「このリソースを今誰が所有しているか」をまず把握することです。Helm が作ったのか、GitOps が同期したのか、Operator が reconcile したのかによって、手を入れる場所が完全に変わります。同じリソースを2つの主体が管理すると、まさにそこが衝突の震源地です。

所有権をラベルで追跡する

3つのツールはすべて、自分が作ったリソースに識別メタデータを残します。Helm はアノテーションとラベルで release を示し、Argo CD は tracking ラベルでどの Application の所有かを残し、Operator は通常 ownerReference で親 CR を指します。障害分析の第一歩は、問題リソースのメタデータを読んで「誰のものか」を確認することです。

このリソースの出所を追跡する順序
  1. kubectl get <resource> -o yaml でメタデータを確認
  2. ownerReference があれば -> Operator(またはビルトインコントローラ)の所有
  3. Argo CD tracking ラベルがあれば -> GitOps の所有
  4. Helm release アノテーションがあれば -> Helm の所有

落とし穴集

  • GitOps と Helm の release 衝突: helm install で直接インストールした release を後で Argo CD が管理しようとすると所有権の衝突が起きます。最初から一方に統一してください。
  • Operator を GitOps でインストールする際の CRD 順序: CRD が作られる前に CR を適用しようとすると同期が失敗します。Argo CD の sync wave や Flux の依存で順序を保証してください。
  • selfHeal が Operator の正常動作を巻き戻す場合: Operator が status や一部フィールドを更新するのに、GitOps がそれをドリフトと誤認して戻すと無限の衝突が起きます。ignoreDifferences でコントローラ管理フィールドを除外してください。
  • values の機密情報: Helm values に平文の機密を入れて Git にコミットする事故がよくあります。Sealed Secrets、External Secrets Operator、SOPS などを併用してください。

意思決定フローチャート

表をフローチャートに変えると、実際の選択の場面でより役立ちます。

開始: 何をデプロイ/運用しようとしているか?
   |
   +- 単発の実験か? --はい--> Helm 単独で十分
   |
   +- いいえ
        |
        +- 複数の環境/クラスタへの一貫適用が必要か? --はい--> GitOps 導入
        |                                              (Helm/Kustomize をソースに)
        |
        +- このシステムの Day-2 運用は複雑か?
             (バックアップ/フェイルオーバー/バージョンアップグレードが反復的か?)
                 |
                 +- はい --> よく管理された外部 Operator があるか?
                 |            +- ある --> その Operator を Helm でインストール + CR を GitOps で
                 |            +- ない --> 自社 Operator 開発を検討 (コストを直視)
                 |
                 +- いいえ --> Helm + GitOps で十分

このフローチャートの分岐点はわずか2つです。1つ目「一貫性が必要か」(-> GitOps)、2つ目「Day-2 が複雑か」(-> Operator)。ほとんどの意思決定はこの2つの質問で終わります。

よくあるアンチパターン

3つのツールを扱う中でよく陥る落とし穴を集めました。

  • すべてを Operator で作ろうとする衝動: 自動化できるという事実は、自動化すべきという意味ではありません。単純なワークロードに CRD を導入すると、認知負荷と保守負担だけが増えます。
  • GitOps なしで Operator だけ導入: Operator は CR を reconcile しますが、その CR 自体が人の手で恣意的に管理されると、再びドリフトと追跡不能の問題が生じます。CR も Git に置いてください。
  • Helm を命令型で使う: helm install/upgrade を人が直接、しかも環境ごとに異なる形で実行すると再現性が崩れます。チャートはパッケージングだけに使い、適用は GitOps に任せてください。
  • 機密情報を values の平文で: 最もよくあるセキュリティ事故です。機密は常に専用ツールで暗号化してください。
  • ツールを目的と取り違える: 「うちも Operator を作ろう」「GitOps がかっこいいから導入しよう」という動機は危険です。出発点は常に解決すべき運用問題であるべきです。

実践シナリオ — あるサービスの進化を追って

抽象的な比較だけでは掴みにくいものです。仮想の注文サービスが時間とともに3つのツールをどう順に受け入れるかを追ってみます。

段階1 — Helm 単独(立ち上げ直後)

サービス初期は Deployment 1つ、Service 1つ、ConfigMap 1つが全てです。開発者がノートパソコンから helm upgrade を直接実行してデプロイします。この段階ではこれで十分です。ガバナンスもドリフトの心配もまだ大きな問題ではありません。ただし、そろそろ次の症状が見え始めます。

  • 「昨日誰が prod に何をデプロイしたか」に答える記録がありません。
  • ステージングとプロダクションの values が微妙に食い違っているのに、なぜか分かりません。
  • デプロイ権限が特定の開発者のノートパソコンに紐づいています。

段階2 — GitOps 導入(チームが大きくなり)

ここで Argo CD を導入します。チャートはそのままに、デプロイトリガーだけを Git PR へ移します。これで全ての変更が PR レビューを経て、誰がいつ何を変えたかが Git 履歴に残り、ドリフトは selfHeal で自動補正されます。開発者ノートパソコンの helm コマンドは消えます。

変化: "人がクラスタに直接 apply"
   -> "人が Git に PR、エージェントがクラスタに apply"

この転換の核心的な利得はデプロイそのものではなく、監査可能性と一貫性です。

段階3 — Operator 導入(データ層が重くなり)

注文量が増えて PostgreSQL を自前で運用し始めます。バックアップ、フェイルオーバー、マイナーバージョンアップグレードが人手を離れるべき水準になります。このとき検証済みの PostgreSQL Operator を導入します。Operator コントローラは Helm でインストールし、PostgresCluster CR は GitOps で管理します。先に見た組み合わせパターン2がまさにこの段階です。

この進化の教訓は明確です。最初から3つを全て導入する必要はありません。 運用の複雑度が実際にそのツールを正当化する時点で1つずつ加えます。過剰な先行投資はそれ自体が負債です。

よくある質問

Q. Helm はもう時代遅れですか。Kustomize や GitOps が置き換えますか。 いいえ。Helm は依然として最も広く使われるパッケージングツールで、GitOps エージェントは Helm チャートを一級でサポートします。Kustomize はテンプレートなしでオーバーレイで変形する別のアプローチで、Helm と競うというより好みと状況で選ばれます。3つとも GitOps の下で共存できます。

Q. Operator を使えば GitOps は要りませんか。 むしろ一緒に使うのが定石です。Operator は「CR をどう運用するか」を知り、GitOps は「その CR 宣言をどうクラスタと一致させるか」を知ります。役割が異なります。

Q. 小さなチームですが3つ全て学ぶ必要がありますか。 いいえ。小さなチームなら Helm + GitOps だけでほとんどのステートレスワークロードをうまく運用できます。Operator は本当に複雑なステートフル運用が生じたとき、それもなるべくよく管理された外部 Operator を導入する形で始めてください。

Q. 自作の Operator と Helm チャート、どちらが保守しやすいですか。 一般に Helm チャートの方がはるかに簡単です。Operator は Go コードと controller-runtime 依存、RBAC、reconcile ロジックという継続的な保守面を加えます。自動化の価値がその負担を超えるときにのみ正当化されます。

Q. GitOps エージェントとして Argo CD と Flux のどちらを選ぶべきですか。 どちらも OpenGitOps 原則を忠実に実装し、核心機能はほぼ同等です。Argo CD は豊かな Web UI と直感的な同期の可視化が強みで、Flux は GitOps Toolkit というモジュール式の構成と軽量さが強みです。UI を重視するチームは Argo CD、コード/CRD 中心の組み立てを好むチームは Flux をよく選びます。この記事の結論(3つの役割分担)はどちらを選んでも同じく適用されます。

Q. Operator を導入するとクラスタ権限が大きくなりすぎませんか。 もっともな懸念です。Operator は自分が管理するリソースへの権限が必要で、データベース Operator なら Secret アクセスが避けられない場合が多いです。そのため信頼できる出所の Operator を使い、インストール時に付与される RBAC を必ずレビューし、可能ならネームスペース範囲に限定するのが良いです。権限レビューは Operator 選択の核心的な基準の1つです。

3つを一緒に収めるリポジトリ構造の例

理論を実際のディレクトリへ移すとより明確になります。次は Helm + GitOps + Operator を1つのリポジトリで運用する典型的な構造です。

platform-gitops/
├── bootstrap/
│   └── argocd-apps.yaml          # App-of-Apps: 以下すべてを管理
├── infrastructure/
│   ├── postgres-operator/        # Operator インストール (Helm チャート参照)
│   │   └── application.yaml
│   └── cert-manager/             # 共通インフラ (Helm)
│       └── application.yaml
├── databases/
│   └── orders-db/
│       └── postgrescluster.yaml  # Operator が reconcile する CR (GitOps 管理)
└── apps/
    └── orders-api/
        ├── application.yaml      # Helm チャートを指す Argo CD Application
        └── values-prod.yaml      # 環境別の値

この構造では、各層の役割がディレクトリに現れます。

  • infrastructure/ は Operator と共通コンポーネントを Helm でインストールします。
  • databases/ は Operator が扱う CR を GitOps で宣言します。
  • apps/ は一般のワークロードを Helm チャート + GitOps でデプロイします。
  • bootstrap/ の App-of-Apps がこの全体を1つにまとめ、空のクラスタに一度適用すれば残りが自動で流れ出ます。

核心は sync wave で順序を保証することです。Operator(とその CRD)が先にインストールされた後に databases/ の CR が適用されるべきです。Argo CD ならアノテーションで wave 番号を付けてこの依存を表現します。この1枚のディレクトリ構造が、この記事全体の結論を視覚的に要約します。3つのツールがそれぞれの場所で、衝突なく、1つの Git ツリーの下で共存する姿です。

おわりに

3つのツールを再び一文でまとめると次の通りです。Helm は束ね、GitOps は合わせ、Operator は世話します。これらは競合相手ではなく、運用スタックの異なる層です。ほとんどのプロダクションは「Helm でパッケージング、GitOps で同期、必要な所だけ Operator で運用自動化」という組み合わせに収束します。

選択の出発点は常に運用の複雑度です。単純なワークロードに Operator を引き込まず、複雑なステートフル運用を人手に委ねないでください。ツールの本質を知れば「何をいつ使うか」は自然と従います。

参考資料