🐳

[kubernetes] Aqua + Tilt + Kind

2022/10/24に公開

概要

普段から Kubernetes 上で動作するアプリケーションを開発する際には、 Kind を利用して、ローカル環境に K8s の環境を用意しています。
Docker Desktop でも K8s の環境を用意することはできますが、 Kind を利用するとアプリケーションごとにノードを用意することができ、ローカル環境がしっちゃかめっちゃかになることが無いなどの利点があります。

一方で、これは Kind に起因する訳ではないですが、開発手順が冗長でスピーディに開発ができていませんでした。
そこで AquaTilt を利用して、ローカルの K8s の開発環境を改善してみました。

これまでの手順

Kind に自身の作成したアプリケーションの変更を適用する時に、これまでは下記のような手順を踏んでいました。

  1. docker build でイメージを更新
  2. kind load でイメージを Kind にアップロード
  3. kustomize edit でマニフェストのイメージタグを更新
  4. kubectl apply で最新のイメージをデプロイ

これらの手順は Makefile で管理することで make xxx で実行可能とはなりますが、できればホットリロードみたく、ファイルの変更を保存したら勝手に反映されて欲しいものです。

KAIZEN後

冒頭にある通り、 Aqua と Tilt を利用しました。
今回は簡単に job (go/cmd/job/main.go)sample (go/cmd/sample/main.go) という文字列を出力するプログラムを Go で作成して、それを継続的に Kind 上にデプロイ可能としてみました。
最終的には下記のようなディレクトリ構成となっています。

├── Tiltfile
├── aqua.yaml
├── cluster.yaml
├── go
│   ├── build
│   │   ├── job
│   │   │   └── Dockerfile
│   │   └── sample
│   │       └── Dockerfile
│   ├── cmd
│   │   ├── job
│   │   │   └── main.go
│   │   └── sample
│   │       └── main.go
│   └── go.mod
└── manifest
    ├── crd.yaml
    ├── job.yaml
    ├── kustomization.yaml
    └── sample.yaml

aqua.yaml

Aqua は必須ではないですが、 CLI の管理が楽になるため利用しています。
実際に導入する際には下記にコマンドを利用します。aqua g で対話形式で導入したい CLI を選択することができるため必要なものを選択するようにします。

aqua init
aqua g >> aqua.yaml
aqua i -l

今回は下記の内容となりました。

registries:
- type: standard
  ref: v3.79.0 # renovate: depName=aquaproj/aqua-registry
packages:
- name: kubernetes-sigs/kind@v0.14.0
- name: kubernetes/kubectl
  version: v1.22.15
- name: tilt-dev/ctlptl@v0.8.9
- name: tilt-dev/tilt@v0.30.9

cluster.yaml

Aqua でインストールした ctlptl を利用して、 Kind のクラスターの起動をするための設定ファイルです。
Kind で利用するイメージのリポジトリの作成の定義も行なっています。これにより kind load の手間が省けます。
実行するコマンドは ctlptl apply -f cluster.yaml となります。

apiVersion: ctlptl.dev/v1alpha1
kind: Registry
name: hello-registry
port: 5000
---
apiVersion: ctlptl.dev/v1alpha1
kind: Cluster
name: kind-hello
product: kind
kubernetesVersion: v1.22.0
registry: hello-registry

docker ps で作成されたことが確認できます。

$ docker ps
CONTAINER ID   IMAGE                             COMMAND                  CREATED       STATUS      PORTS                                              NAMES
317f22b5d8c3   kindest/node:v1.22.9              "/usr/local/bin/entr…"   2 days ago    Up 2 days   127.0.0.1:58121->6443/tcp                          hello-control-plane
92a56ac5e767   registry:2                        "/entrypoint.sh /etc…"   2 days ago    Up 2 days   127.0.0.1:5000->5000/tcp                           hello-registry

Tiltfile

Tiltfile を作成し、tilt up で利用可能です。
起動後は指定されたアドレスにアクセスすることでブラウザ上で K8s の様子を見ることができます。

$ tilt up
Tilt started on http://localhost:10350/
v0.30.9, built 2022-10-08

(space) to open the browser
(s) to stream logs (--stream=true)
(t) to open legacy terminal mode (--legacy=true)
(ctrl-c) to exit

Job のデプロイ

下記の Job リソースを Tilt を利用してデプロイしていきます。

apiVersion: batch/v1
kind: Job
metadata:
  name: hello-job
spec:
  completions: 1
  backoffLimit: 1
  template:
    spec:
      containers:
        - name: hello-job
          image: hello-job:latest
      restartPolicy: Never

Tiltfile は下記のようになります。
ディレクトリ構造で紹介したとおり、Kustomize の利用を想定しています。本来は overlays などの作成が一般的ですが、ローカルでの開発が主眼なため用意せず雰囲気を味わうためだけに、各マニフェストを読み込むだけの kustomization.yaml を作成しました。
Tiltfileに k8s_yaml(kustomize('xxx')) とするだけで、kustomize build -> kubectl apply -f をよしなに行ってくれます。
そして、docker_build で指定したとおりに、go/cmd/job/main.go に変更が加わったり、マニフェストに変更が加わった際には docker build をして hello-job:xxxxx の形式で hello-registry にイメージをプッシュしてくれます。
またマニフェストにある hello-job:latest のタグは自動で最新のタグに置換されます。

docker_build(
    'hello-job:latest',
    context='go/',
    dockerfile='go/build/job/Dockerfile'
)

k8s_yaml(kustomize('./manifest'))

Custom Resource の場合

下記のような CustomResourceDefinition を利用した sample は k8s_yaml(kustomize('./manifest')) ではイメージの更新を行うことができませんが、k8s_kind() を利用することでイメージの更新を自動で適用することができます。

## crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: mines.myfirstcontroller.mshr.doc
spec:
  group: myfirstcontroller.mshr.doc
  names:
    kind: Mine
    plural: mines
    singular: mine
    shortNames:
      - mine
  scope: Namespaced
  versions:
    - name: v1alpha1
(後略)

## sample.yaml
apiVersion: "myfirstcontroller.mshr.doc/v1alpha1"
kind: Mine
metadata:
  name: sample
spec:
  image: hello-sample:latest

k8s_kind() を利用して Custom Resource のイメージ更新にも適用した Tiltfile。

docker_build(
    'hello-job:latest',
    context='go/',
    dockerfile='go/build/job/Dockerfile'
)

docker_build(
    'hello-sample:latest',
    context='go/',
    dockerfile='go/build/sample/Dockerfile'
)

k8s_yaml(kustomize('./manifest'))
k8s_kind('Mine', image_json_path='{.spec.image}')

ホットリロードの確認

試しに go/cmd/job/main.go で出力する文字列を Hello, Job! から Hello, Aqua and Tilt! に変更してみると Tilt の UI 上でイメージが更新されて、ログに出力される文字列が変更されていることが分かります。

出力が多く、一連の流れをうまいことスクショできませんが、メッセージが意図した通りに変更されていることが確認できるはずです。

34s 程度で完了したようでした。

まとめ

これまでも Makefile やシェルスクリプトを利用して、なるべくローカルでの K8s 環境を快適にしようと努めてはきましたが、それでもわざわざエディターから Terminal に移動してコマンドを実行したり、アプリケーションが増えた際のスクリプトのメンテナンスなどはやはり手間でした。
今回の Aqua + Tilt + Kind の組み合わせを導入することで、それらの手間が省ける上開発もスピーディに行うことが可能となるため、是非とも興味がある方は導入してみてください。

Discussion