記事検索

検索ワードを入力してください。
Sky Tech Blog
Helmfileを​使った​複数Kubernetesクラスターの​ミドルウェア管理

Helmfileを​使った​複数Kubernetesクラスターの​ミドルウェア管理

社内システム部門のSREチームが行った、Kubernetesクラスターのミドルウェア管理の改善について紹介します。HelmとHelmfileを活用し、複数クラスターの管理を効率化する取り組みを説明します。

はじめに

現在、社内システム部門のSREチームではここ数年で導入したテクノロジーについて、暗黙知の排除、安定化、平準化といった開発効率改善の取り組みを進めています。

今回は、各Kubernetesクラスターのミドルウェア管理の改善について紹介します。

ミドルウェアって​どれの​こと?

現在、作ったアプリケーションをコンテナ基盤で動かす際、Podをはじめとしたアプリケーション周りのリソースをデプロイするとブラウザで見られるようになっていますが、それを支えるものとして裏で様々なミドルウェアが動作しています。
例えば、以下のようなものがあります。

ミドルウェア 説明
Traefik ブラウザからのHTTPリクエストを受け取るL7ロードバランサー。ユーザー認証やSSL終端なども担う
OAuth2-Proxy Traefikがユーザー認証のために利用。Microsoft EntraIDを使ったOIDC認証を提供
MetalLB(オンプレのみ) type="LoadBalancer"のServiceリソースを使用するために利用。社内のL3スイッチにBGPでルーティング情報を広告
Vault Secret Operator Vaultに格納しているシークレット情報をPodに受け渡す
Logging Operator Podの標準出力やイベント情報をログ基盤に転送

他にも様々なミドルウェアが動作しており、ざっとこの倍はあると思います。

Helmに​ついて

ほぼ全てのミドルウェアは、Helmによって管理しています。

HelmはKubernetes向けのパッケージマネージャーで、Valuesという変数ファイルをYAML形式で書いて、実行するとそれに応じたKubernetesマニフェストを生成して、クラスターに適用できるツールです。

例えば、TraefikのHelmチャートでは以下のようなValuesが定義されています。

# Default values for Traefik
# This is a YAML-formatted file.
# Declare variables to be passed into templates

image:  # @schema additionalProperties: false
  # -- Traefik image host registry
  registry: docker.io
  # -- Traefik image repository
  repository: traefik
  # -- defaults to appVersion
  tag:  # @schema type:[string, null]
  # -- Traefik image pull policy
  pullPolicy: IfNotPresent

# -- Add additional label to all resources
commonLabels: {}

deployment:
  # -- Enable deployment
  enabled: true
  # -- Deployment or DaemonSet
  kind: Deployment
  # -- Number of pods of the deployment (only applies when kind == Deployment)
  replicas: 1
  # -- Number of old history to retain to allow rollback (If not set, default Kubernetes value is set to 10)
  revisionHistoryLimit:  # @schema type:[integer, null];minimum:0
  # -- Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down
  terminationGracePeriodSeconds: 60
  # -- The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available
  minReadySeconds: 0
(以下略)

各ベンダーがチャートを作成して公開しているため、当然チャートによってValuesの定義方法はバラバラです。
Traefikでは、deployment.kindでPodをDeploymentとするかDaemonSetとするかを選べることや、deployment.replicasでレプリカ数を設定できることが分かります。

このYAMLを適宜修正して、helm installを実行すると各Kubernetesリソースの定義が生成されてクラスターに適用されます。(実際にはValuesに書いた内容がどう展開されるのか分かりづらかったりするので、使っている箇所のテンプレートと行ったり来たりしながらValuesを用意する苦行が待ってます)

これまでの​ミドルウェア管理

上述の通り、ミドルウェアごとにHelm Valuesを用意してクラスターに適用していくわけですが、現在管理しているクラスターは用途別にオンプレ、AWS合わせて7つあります。

Valuesの内容は、各クラスターで全く同じということはほぼなく、設定する値が変わることが大半です。
例えば、TraefikであればダッシュボードページのIngressのURLが異なるなどの違いがあります。

これまでは、各SREメンバーが自分でValuesを用意してhelm installするか、helm get valuesして現在適用されているValuesを持ってきて、変更した後helm upgradeするなどの運用を行っていました。

設定変更やチャートのバージョンアップでは、1クラスターずつ上記作業を行うため、非常に煩雑でかつ重要なコンポーネントが壊れるリスクもありました。

これからの​ミドルウェア管理

ほぼ手動のミドルウェア管理から脱却するために、今回 Helmfileというツールの利用を検証中です。

Helmfileは、Helmのラッパーとして動作し、複数のチャートや複数のクラスターの管理を容易にしてくれるツールです。(Terraformを使っている方は、Terragruntをイメージしてもらうと分かりやすいです)

基本的な使い方は、まず以下のようにhelmfile.yamlを用意します。

repositories:
 - name: prometheus-community
   url: https://prometheus-community.github.io/helm-charts

releases:
- name: prom-norbac-ubuntu
  namespace: prometheus
  chart: prometheus-community/prometheus
  values:
  - values.yaml

その後、以下のコマンドを実行すると、指定したHelmチャートがダウンロードされ、Kubernetesクラスターに適用されます。

helmfile apply

helmfile.yamlで設定できる項目は多岐に渡りますが、特筆すべきはenvironmentによる環境ごとの記述を行える点です。

例えば、以下のように記述すると、values.yamlの内容を環境ごとに変えることができます。(YAML内でGoテンプレート構文を使う場合は、拡張子を .gotmplにする必要があります。)

helmfile.yaml.gotmpl
environments:
  dev:
  - serverName: bar
  - myChartVer: 1.5.0
  prod:
  - serverName: baz
  - myChartVer: 1.1.0
repositories:
 - name: prometheus-community
   url: https://prometheus-community.github.io/helm-charts

releases:
- name: prom-norbac-ubuntu
  namespace: prometheus
  chart: prometheus-community/prometheus
  version: {{ .Values.myChartVer }}
  values:
  - values.yaml.gotmpl
values.yaml.gotmpl
server:
  name: {{ .Values.serverName }}

Helmfileはその多様な設定項目により、様々な管理方法ができる作りになっていますが、我々の環境では以下のような構造にしました。

.
├── bases
│   ├── environments
│   │   ├── _defaults.yaml.gotmpl
│   │   ├── dev.yaml.gotmpl
│   │   └── prod.yaml.gotmpl
│   └── environments.yaml.gotmpl
├── releases
│   ├── oauth2-proxy
│   │   ├── helmfile.yaml.gotmpl
│   │   └── values-7.7.28.yaml.gotmpl
│   └── traefik
│       ├── helmfile.yaml.gotmpl
│       └── values-32.1.1.yaml.gotmpl
└── helmfile.yaml

releasesディレクトリは各ミドルウェアごとにディレクトリを切って、 helmfile.yaml.gotmplを置き、ルートのhelmfile.yamlで子helmfileとして読み込むようにしています。

basesディレクトリは環境ごとの変数を定義できるようにしています。
bases/environments/_default.yaml.gotmplは、環境によらないデフォルト値を記述し、必要に応じて (環境).yaml.gotmplで上書く構成にしています。

以降では、順に内容を追っていきたいと思います。

./helmfile.yaml

ルートのhelmfile.yamlでは、まずbasesでhelmコマンドのオプション値を記述した helmDefaults.yamlと環境ごとの設定値を集約した environments.yaml.gotmplを読み込みます。

次の helmfilesでは、各ミドルウェアのhelmfileを子として列記しています。

./helmfile.yaml
missingFileHandler: Error

bases:
  - bases/environments.yaml.gotmpl

helmfiles:
  - releases/oauth2-proxy/helmfile.yaml.gotmpl
  - releases/traefik/helmfile.yaml.gotmpl

./releases/traefik/helmfile.yaml.gotmpl

サンプルとしてTraefikのhelmfileです。

repositoriesでチャートのリポジトリを指定します。releasesでリポジトリ内のどのチャートをインストールするか指定します。

releases[].installedを変数にしているのは、環境によってインストールの有無をコントロールするためです。
releases[].versionを変数にしているのは、例えばチャートのアップデート時にどこかのクラスターだけ先行してアップデートするといったことをできるようにするためです。 releases[].valuesはvaluesファイルを列記しますが、上記により環境ごとにチャートバージョンが異なる可能性があるため、values-(バージョン).yaml.gotmplを読み込むようにしています。

---
bases:
  - ../../bases/environments.yaml.gotmpl

---
repositories:
  - name: traefik
    url: ghcr.io/traefik/helm/traefik
    oci: true

---
releases:
  - name: traefik1
    namespace: traefik
    createNamespace: true
    chart: traefik/traefik
    installed: {{ .Values | get "traefik.installed" false }}
    version: {{ .Values | get "traefik.chartVersion" | quote }}
    values:
      - values-{{ .Values | get "traefik.chartVersion" }}.yaml.gotmpl

./releases/traefik/values-32.1.1.yaml.gotmpl

Traefikチャートのvaluesを記述します。
サンプルとして環境ごとにPodのレプリカ数を変更できるようにしています。

deployment:
  replicas: {{ .Values.traefik.deploymentReplicas }}

./bases/environments.yaml.gotmpl

各環境ごとの定義と、その環境で使用するクラスターのコンテキスト、読み込むvaluesを定義しています。 (コンテキストは我々の環境の都合で、環境変数から読み込むようにしていますが、.kube/configのコンテキストを記述してもOKです。)

{{- $envDir := print (regexReplaceAll "/releases/[^/]+$" (env "PWD") "") "/bases/environments" }}

environments:
  prod:
    kubeContext: {{ env "KUBE_PROD" }}
    values:
      - {{ $envDir }}/_defaults.yaml.gotmpl
      - {{ $envDir }}/prod.yaml.gotmpl
  dev:
    kubeContext: {{ env "KUBE_DEV" }}
    values:
      - {{ $envDir }}/_defaults.yaml.gotmpl
      - {{ $envDir }}/dev.yaml.gotmpl

./bases/environments/_defaults.yaml.gotmpl

各ミドルウェアにおけるデフォルト値を記述します。

oauth2-proxy:
  installed: true
  chartVersion: 7.7.28

traefik:
  installed: true
  chartVersion: 32.1.1
  deploymentReplicas: 1

./bases/environments/prod.yaml.gotmpl

_defaults.yaml.gotmplの上書き内容を記述します。
ここでは本番環境のみTraefikのPodが2レプリカとなるように変更しています。

traefik:
  deploymentReplicas: 2

長くなりましたが、以上が各ファイルの説明でした。

これらを実際に適用する際は、以下のコマンドを実行します。

helmfile -e dev apply

また、ミドルウェア単体でApplyすることも可能です。

helmfile -e dev -f releases/traefil/helmfile.yaml.gotmpl apply

なお、Helm Diffプラグインのラッパーとしても動作するため、以下のように実行すると、適用されるリソースの差分を確認することもできます。

helmfile -e dev diff

今後に​向けて

まだ検証中の内容となりますが、今後に向けてCIによる自動化も検討中です。

マージリクエストを起点として、helmfile diffの適用差分を確認し、手動によるApplyができるようにしたいと考えています。

また、Helmfileのドキュメントには、Argo CDとの連携も記載されているため、合わせてこちらも検証していきます。


XFacebookLINE
Sky株式会社のソフトウェア開発や製品、採用に関するお問い合わせについては、下記のリンクをご確認ください。
お問い合わせ
ホーム