helmfileに入門するときがやってきたのです!

はじめまして、SREのid:renjikari です。この記事はfreee Developers Advent Calendar 2019の3日目です!

adventar.org

はじめに

freeeではKubernetesを本番環境にも利用しており、そのマニフェスト管理にはhelm&helmfileを使っています。 helmについてはたくさんの情報がインターネットにあるものの、helmfileに関しては今のところ記事や事例が少なく、 とても便利なmoduleなんですが、ちょっと導入障壁が高いのかなと思ったりします。

先日こんなつぶやきをしたところ、脳内に「お前がやるんだよ!」という言葉が響いてきたので書いていきます。

(なお、このAdvent Calendarは会社の公式ブログでやることを前日に知ったのでなんかちょっと他と毛並みが違かったらすいません)

helmについて

本記事はhelmfileの入門を目指すものですが、一応helmについても少しだけ触れておきます。 helmはKubernetesのためのpackage managerと呼ばれています。KuberntesのManifestをCharts(設計図)としてまとめて管理することのできるものになっています。 CNCFで管理されており、今日時点でIncubating Projectに位置しています。

helmの大きな3つの特徴だけ説明します。

  1. Chartがpackageになっていること。Chartの中にはK8sで動くリソース定義が入っています。Apt dpkgやYum RPM fileに似ているものと考えられます。
  2. ChartのためのRepositoryが存在します。Chartを集め、共有するためです。これもAptやYumのrepositoryを想像するとわかりやすいかもしれません
  3. 最後はK8s上で動くchartのinstanceに相当するReleaseです。heleはChartをReleaseという単位でデプロイしていくものになります。

さらに詳しくは別のhelmの解説をお読みください

How to install helmfile

公式のinstallationを参考に簡単にinstallできます。

helmfileについて

helmfileはhelm chartをより宣言的にデプロイするためのものです。 helmfileを使うことで以下のようなことができます

  • ChartやReleaseのための変数を宣言するファイルをきれいなディレクトリ構造に収めバージョン管理すること。またそれによる再現性の確保
  • Chartの再利用性をより高める
  • 変更に対してCI/CDを使いやすくなる

ここからは実際にデプロイする際のhelmとhelmfileの差について解説していきます。

実行環境について

つい半月ほど前にhelm v3.0.0が出たんですが今回はv2.14.3で以下を実行しています。 また、Kubernetes ClusterはDocker for MacのKubernetesを利用しており、clientもserverもversionは1.14です。

実際にhelmとhelmfileを使うところをみて実感を得る

さて、ここからはhelmの利点でもあるstableにあるChart(今回はenvoy)をKubernetes Clusterにデプロイするところで違いをみていきます。 なお、すべての工程において、実際にはcreate namespaceが必要なので実際に試される際には注意して下さい

その1 シンプルにstable/envoyをinstall

helmの場合

  • 以下のように簡単にinstallできます
    • helm tiller run -- helm install stable/envoy --name helm-envoy
  • こんな感じで確認できます(いらない出力はばっさりカットしてます)
❯ helm tiller run -- helm ls
Running: helm ls

NAME            REVISION        UPDATED                         STATUS          CHART           APP VERSION     NAMESPACE
helm-envoy      1               Tue Dec  3 01:08:13 2019        DEPLOYED        envoy-1.9.0     1.11.2          helm-envoy
❯ kubectl run -it --rm --image alpine:3.9 tester sh
/ # apk add --update curl
/ # curl -I http://envoy:10000
HTTP/1.1 200 OK

helmfileの場合

  • ディレクトリ構成とyamlファイルはこんな感じです
❯ tree
.
├── envoy
│   └── helmfile.yaml
└── helmfile.yaml
❯ cat ./helmfile.yaml
helmfiles:
  - ./envoy/helmfile.yaml
  #- ./other-app/helmfile.yaml
❯ cat ./envoy/helmfile.yaml
releases:
  - name: helmfile-envoy
    namespace: helmfile-envoy
    chart: stable/envoy
  • デプロイするためのコマンド
    • helm tiller run -- helmfile sync
    • helmのときと同様に適当なコンテナを立ててcurlすると200が返ってきます。
  • このhelmとhelmfileを比べるとまだあまりhelmfileの良さが分かりづらいですが、より宣言的に記述できそうに見えますね
    • 次はChartに渡したい変数がある場合についてみていきます

その2 envoyをinstallするときに変数を渡す

helmの場合

  • 今回はこんな変数を渡します
    • type: LoadBalancer にすることで直接envoyを呼べます
service:
    type: LoadBalancer
    ports:
      n0:
        port: 11111
        targetPort: 10000
        protocol: TCP
  • valuesを読んでデプロイ
    • helm tiller run -- helm install stable/envoy --name helm-envoy -f envoy/values.yaml
  • 結果
❯ kubectl get svc
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                           AGE
envoy   LoadBalancer   10.103.54.185   <pending>     11111:32001/TCP,10000:30175/TCP   3s

❯ curl -I localhost:11111 #localから実行
HTTP/1.1 200 OK

helmの場合

  • valuesは同じものを利用します
  • 一つ前の例から変更したhelmfileとディレクトリ構造はこんな感じ
❯ tree
.
├── envoy
│   ├── helmfile.yaml
│   └── values.yaml
└── helmfile.yaml
❯ cat ./envoy/helmfile.yaml
releases:
  - name: helmfile-envoy
    namespace: helmfile-envoy
    chart: stable/envoy
    values:
      - values.yaml
  • deployコマンドは全く同じで
    • helm tiller run -- helmfile sync
  • この例をみると、まずhelmではvalues fileを呼ぶことがコマンドベースで表現されていますが、helmfileでは宣言的に表現されています。
    • 実際の本番運用にはたくさんの変数を利用することを考えると、これは大きな利点になります

さらなるhelmfileの利点

  • ここまで読んでいただいて、helmfileの使い方はわかったかと思います。
  • 実際にK8sを本番サービスの基盤として利用する場合には、production/staging/developなどの環境が必要になるでしょう
  • そんな場合にもうまく再利用しつつ対応できます
    • まずはディレクトリ構造とyamlの差分をすべて書きます
❯ tree
.
├── envoy
│   ├── environment
│   │   ├── develop
│   │   │   └── values.yaml
│   │   ├── production
│   │   │   └── values.yaml
│   │   └── staging
│   │       └── values.yaml
│   ├── helmfile.yaml
│   └── values.yaml
└── helmfile.yaml
❯ cat ./helmfile.yaml
environments:
  develop:
  staging:
  production:
helmfiles:
  - ./envoy/helmfile.yaml
  #- ./other-app/helmfile.yaml
❯ cat ./envoy/helmfile.yaml
environments:
  develop:
    values:
    - environment/develop/values.yaml
  staging:
    values:
    - environment/staging/values.yaml
  production:
    values:
    - environment/production/values.yaml

releases:
  - name: helmfile-envoy-{{ .Values.stage }}
    namespace: helmfile-envoy-{{ .Environment.Name }}
    chart: stable/envoy
    values:
      - values.yaml
❯ cat ./envoy/environment/staging/values.yaml
stage: staging
  • この状態で helm tiller run -- helmfile -e staging syncというコマンドを打てば
    • release nameが helmfile-envoy-staging
    • namespaceが helmfile-envoy-staging
  • となってデプロイされます。
  • このようにhelmfile自体にもchartにも変数を渡すことができ便利です。
  • ここまで紹介してきたような利点を使いさらにCI/CDと組み合わすことで、複数環境/複数clusterに対応可能な柔軟なマニフェスト管理を行えます。

参考と引用

明日は新卒のid:MitubaEX です!楽しみ!!