Kubernetesのスケーリング:HelmがYAML管理地獄から私たちを救った方法

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

手動YAMLの限界

Kubernetesで単一のデプロイメントを管理するのは簡単です。Deploymentマニフェスト、Service、Ingressを作成し、kubectl apply -fを実行するだけです。しかし、規模が大きくなるとこの手法は限界に突き当たります。半年前、私たちのチームはその壁にぶつかりました。開発、ステージング、本番の3つの環境にわたる18のマイクロサービスをやりくりしていましたが、CPU制限やレプリカ数がわずかに異なるだけの、2,000行を超えるほぼ同一のYAMLに埋もれていました。

設定のコピー&ペーストは、避けられない「設定の乖離(Configuration Drift)」を引き起こしました。開発者がステージング環境のマニフェストでLiveness Probeを調整しても、本番環境への同期を忘れてしまうのです。この手作業によるオーバーヘッドは単に面倒なだけでなく、いつ爆発してもおかしくない時限爆弾でした。私たちは、Kubernetesアプリをバラバラなスクリプトの集合体としてではなく、バージョン管理されたパッケージとして扱う必要がありました。それが、すべてをHelmに移行した理由です。

Helmのアーキテクチャ:チャート、バリュー、リリース

HelmはKubernetesのパッケージマネージャーとして機能します。Helmを使用すると、最も複雑なアプリケーションであっても、単一のコマンドで定義、インストール、アップグレードが可能になります。Helmを使いこなすには、3つの主要な柱を理解する必要があります。

  • チャート(Chart): これは設計図です。Deployment、Service、ConfigMapのYAMLテンプレートが含まれています。
  • バリュー(Values): これらは設定の切り替えスイッチです。イメージタグをハードコードする代わりに、values.yamlファイルからデータを取得するプレースホルダーを使用します。
  • リリース(Release): これはチャートの実行インスタンスです。同じチャートを使用しながら異なるバリューを適用することで、同一クラスター内で「api-gateway-dev」と「api-gateway-prod」を実行できます。

ロジックとデータの分離がすべてを変えました。インフラストラクチャのテンプレートを環境固有の数値から切り離すことで、設定ミスを約85%削減することができました。

実践的なワークフロー:最初のチャートの作成

開始するのは簡単です。kubectlがすでにクラスターを指しているなら、60秒以内にHelmのバイナリを入手できます。

# macOSの場合
brew install helm

# Linuxの場合
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

アプリケーションの雛形作成

手動でのファイル作成はスキップしましょう。組み込みの雛形作成(スキャフォールディング)ツールを使用して、業界のベストプラティスに従った標準的なディレクトリ構造を生成します。

helm create my-python-app

このコマンドにより、my-python-app/ディレクトリが作成されます。その中のtemplates/フォルダーが実際の作業場所です。deployment.yamlservice.yamlといった、Goテンプレート構文が組み込まれたファイルが用意されています。

柔軟なテンプレート化

Helmの真の力は、その変数システムにあります。静的なマニフェストがテンプレートによってどのように動的になるかを確認してください。

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deploy
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: app-container
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

その後、values.yamlが設定の唯一の真実のソース(Single Source of Truth)となります。

# values.yaml
replicaCount: 3
image:
  repository: my-registry/my-python-app
  tag: "1.2.5"

リリースライフサイクルの制御

デプロイメントは、単一のアトミックな操作になりました。5つの異なるkubectlコマンドを特定の順序で実行し、途中で失敗しないことを祈る必要はもうありません。

# チャートをインストールする
helm install my-app-prod ./my-python-app --values prod-values.yaml

安全なアップグレードと即時のロールバック

新しいバージョンをリリースする際、実行中のオブジェクトを直接操作することはありません。values.yamlを更新してアップグレードを実行するだけです。Helmはすべての変更をバージョン管理されたリビジョンとして追跡します。新しいイメージが原因でメモリ使用量が急増した場合でも、スタック全体を5秒以内に元に戻せます。これは、前四半期の金曜午後のデプロイ失敗時に、私たちの窮地を救ってくれました。

# バージョン履歴を表示する
helm history my-app-prod

# リビジョン2に即座にロールバックする
helm rollback my-app-prod 2

本番環境から得た教訓

Helmへの移行は、「Infrastructure as Code(コードとしてのインフラ)」へのマインドセットの転換でした。最大の成果は、社内標準の確立です。各チームがRedisのデプロイ方法を個別に考えるのではなく、全員が利用できる1つの社内用Redisチャートを作成しました。これにより一貫性が向上し、トラブルシューティングの時間が短縮されました。

注意点として、テンプレートを過剰に作り込まないでください。あらゆる例外ケースに対して複雑なif-elseロジックを追加したくなりますが、それではチャートが読めなくなってしまいます。テンプレートのロジックが結果のYAMLよりも長くなっているなら、やりすぎです。シンプルさを保ちましょう。

2つか3つ以上のサービスを運用しているチームにとって、Helmは必須のツールです。正気を失うことなくスケーリングするために必要なガードレールを提供してくれます。過去半年間の私たちのクラスターの安定性は、デプロイを手動のパッチ当てではなく、管理されたリリースとして扱った直接的な結果です。

まとめ

Helmへの切り替えには、マニフェストのリファクタリングという先行投資が必要です。しかし、その見返りは絶大です。まずはシンプルなアプリから始め、helm installをマスターし、設定をバリューファイルに移行しましょう。次の緊急ロールバックが30分ではなく3秒で終わったとき、SREチームはあなたに感謝することでしょう。

Share: