yispはほとんどyamlの構文を維持したまま記述できる、yaml用のテンプレートエンジンです。
lispをベースにしており扱う構文はシンプルながら強力な表現力を持っているという特徴があります。
yispはKubernetesのマニフェストの生成に特に便利です。
本記事では、yispを使ってKubernetesのマニフェストをテンプレート化し、管理する方法を紹介します。
yispの基本
yispは以下のコマンドでインストールできます
go install github.com/totegamma/yisp@latest
yispはyaml中にコードを埋め込みます。たとえば、文字列を結合するconcat
関数を使って"hello"と"world"を結合する例は以下の通りです。
mymessage: !yisp
- concat
- "hello "
- "world"
これをyispで実行します。
yisp build helloworld.yaml
すると、以下のような出力が得られます。
mymessage: hello world
ここでのポイントは以下の2つです
- !yispタグを使って、arrayをyispコードであると示すことができる
- !yispタグの外(mymessage)はそのまま出力されている
deploymentをテンプレートで作成する
では早速Kubernetesマニフェストのテンプレートを作成してみましょう。
テンプレートとは、つまり関数のことです。特定の引数を受け取って、その引数に基づいてマニフェストを生成します。
yispでは関数はラムダ式として記述し、アンカーでそれに名前をつけます。
# deployment.yaml
!yisp &main # アンカー記法&mainでこの値に名前を付ける
- lambda # lambda関数を使ってlambda式を生成する
- [props] # 引数のリストを宣言する
- !quote # !quoteタグ以下の値は変数展開以外そのまま出力される
apiVersion: apps/v1
kind: Deployment
metadata:
name: *props.name # アンカー記法*props.nameでpropsのnameを参照
spec:
replicas: 1
selector:
matchLabels:
app: *props.name
template:
metadata:
labels:
name: *props.name
spec:
containers:
- name: *props.name
image: *props.image
ports:
- containerPort: *props.port
---
!yisp
- *main # mainテンプレートを呼び出す
- !quote
name: myapp
image: myimage:latest
port: 8080
これを評価すると、以下のマニフェストが出力されます。
yisp build deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
name: myapp
spec:
containers:
- name: myapp
image: myimage:latest
ports:
- containerPort: 8080
これをこのままkubectlコマンドに渡すことで、Kubernetesにデプロイできます。
yisp build deployment.yaml | kubectl apply -f -
別ファイルからテンプレートを読み込む
テンプレートをより効率的に運用するために、別ファイルから呼びだすようにしましょう。index.yamlを新しく作成します。
!yisp
- import
- [deployment, deployment.yaml] #名前付きimport
---
!yisp
- *deployment.main # importで名前つけたdeploymentモジュールのmainを呼び出す
- !quote
name: myapp
image: myimage:latest
port: 8080
index.yamlはディレクトリ名を代表して読み込まれるので、次のコマンドでビルドできるようになります。
yisp build .
serviceを追加する
次にDeploymentに対応するServiceを追加してみましょう。
このとき、Serviceのtypeにデフォルト値を設定して、指定がなかった場合はLoadBalancer
を使うようにします。
default関数を使うことで、第二引数がnull
の場合に第三引数が返ります。
また、yispでは.
を使った変数参照において、参照した変数が存在しなかった場合例外が発生します。変数名に?
を付けることで、変数が存在しなかった場合にnull
を返すようになります。
!yisp &main
- lambda
- [props]
- !quote
apiVersion: v1
kind: Service
metadata:
name: *props.name
spec:
selector:
name: *props.name
ports:
- port: *props.port
targetPort: *props.port
type: !yisp [default, *props.lbtype?, LoadBalancer]
複数のテンプレートをまとめる
deploymentとserviceをまとめて、一つのテンプレート関数としてまとめましょう。
引数は同じpropsを使い、deploymentとserviceの両方に渡します。
また、ドキュメントをまとめるためにas-document-root
関数を使います。これにより、評価したドキュメントの配列が、出力するyaml上ではドキュメントとして扱われます。
!yisp
- import
- [deployment, deployment.yaml]
- [service, service.yaml]
---
!yisp &app
- lambda
- [props]
- - as-document-root
- - *deployment.main
- *props
- - *service.main
- *props
また、ディレクトリ構造を整理しましょう。
templates
index.yaml
deployment.yaml
service.yaml
environments
develop
index.yaml
これからはenvironments/develop/index.yaml
で作業を進めます。
environments/develop/index.yamlは以下のようになります。
!yisp
- import
- [template, ../../templates]
---
!yisp
- *template.app
- !quote
name: myapp
image: myapp:latest
port: 8080
statefulsetとdeploymentを選べるようにする
アプリケーションをdeploymentとしてデプロイするか、statefulsetとしてデプロイするかを選べるようにしましょう。
まずは、statefulset.yamlを作成します。
!yisp &main
- lambda
- [props]
- !quote
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: *props.name
spec:
replicas: !yisp [default, *props.replicas?, 1]
selector:
matchLabels:
app: *props.name
template:
metadata:
labels:
name: *props.name
spec:
containers:
- name: *props.name
image: *props.image
ports:
- containerPort: *props.port
つぎに、template/index.yamlを修正して、パラメーターのtypeによってどちらのテンプレートを使うか分岐するようにします。
これは、if
関数を使って実現できます。
!yisp
- import
- [deployment, deployment.yaml]
- [service, service.yaml]
- [statefulset, statefulset.yaml]
---
!yisp &app
- lambda
- [props]
- - as-document-root
- - if
- [eq, *props.type?, "statefulset"]
- - *statefulset.main
- *props
- - *deployment.main
- *props
- - *service.main
- *props
configmapなど個別リソースを追加する
使っている環境ごとに、テンプレートとは別に独自のリソースを追加したい場合があると思います。
その場合は、include関数を使って外部のyamlファイルをそのまま読み込むことができます。
例えば、developディレクトリに新しくconfigmap.yamlを作成します。
environments
develop
index.yaml
configmap.yaml
これをそのまま読み込むように、environments/develop/index.yamlに追記しましょう。
!yisp
- import
- [template, ../../templates]
---
!yisp
- *template.app
- !quote
name: myapp
image: myapp:latest
port: 8080
---
# ここを追記
!yisp
- include
- configmap.yaml
configmap-generator相当の読み込みを行う
直前では、configmap.yamlを自前で作成し、直接includeしていました。
次はkustomizeのconfigmap-generatorのように、ディレクトリ内のファイルを読み込んで自動的にConfigMapを生成してみましょう。
まずはconfigmapとして生成するためのファイルを配置するディレクトリを作成し、適当にテキストデータを配置します
environments
develop
index.yaml
configmap.yaml
cm-files
file1.txt
file2.txt
...
template/index.yamlに以下のようなコードを追加します。
!yisp &configmap-generator
- lambda
- [props]
- !quote
apiVersion: v1
kind: ConfigMap
metadata:
name: *props.name
data: !yisp
- from-entries
- - map
- - lambda
- [file]
- !quote
- *file.name
- *file.body
- - read-files
- *props.path
environments/develop/index.yamlに以下のようにconfigmap-generatorを呼び出すコードを追加します。
!yisp
- *template.configmap-generator
- name: myapp-config
path: ./cm-files/*
これで以下のようなconfigmapをマニフェストに含めることができるようになりました。
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
hoge.conf: |
piyopiyio
最後に
本記事では、yispを使ってKubernetesのマニフェストをテンプレート化し、管理する方法を紹介しました。
yispは非常に柔軟で強力なツールであり、Kubernetesのマニフェストを効率的に管理するための強力な手段となります。テンプレート化することで、環境ごとの差異を簡単に管理できるようになり、再利用性も向上します。
また、本記事ではkubernetesマニフェストのみに焦点を当てましたが、yispはprometheusの設定やansibleのplaybookなどの他のyamlファイルにも、そしてgrafanaのダッシュボードなどjsonファイルにも利用できます。
yispはオープンソースで開発されています。ぜひGitHubのレポジトリをチェックしてみてください。