cuelang kubernetes チュートリアル#2
cuelang kubernetes チュートリアル#2
前回はディレクトリに散らばった yaml を cue にした。今回は cue をスリムにしていく。クイックダーティーコンバージョン
https://github.com/cuelang/cue/tree/master/doc/tutorial/kubernetes#quick-n-dirty-conversion
cue eval -c ./... > snapshot
ディレクトリ下の cue ファイルを評価し snapshot を得る。改変しても差異がないことを検証するため取っておく。
Usage: cue eval [flags] Flags: -c, --concrete require the evaluation to be concrete 評価を具体的にする必要がある
snapshot
service: { bartender: { apiVersion: "v1" kind: "Service" metadata: { name: "bartender" labels: { component: "frontend" app: "bartender" domain: "prod" } :
cp frontend/breaddispatcher/kube.cue .
テンプレートを作成するため deployment と service を含むファイルをコピーする。kube.cue をシンプル化していくのだが変更前の状態を確認しておく。
frontend/breaddispatcher/kube.cue
package kube service: breaddispatcher: { apiVersion: "v1" kind: "Service" metadata: { name: "breaddispatcher" labels: { app: "breaddispatcher" component: "frontend" domain: "prod" } } spec: { ports: [{ port: 7080 targetPort: 7080 protocol: "TCP" name: "client" }] selector: { app: "breaddispatcher" component: "frontend" domain: "prod" } } } deployment: breaddispatcher: { apiVersion: "apps/v1" kind: "Deployment" metadata: name: "breaddispatcher" spec: { replicas: 1 template: { metadata: { labels: { app: "breaddispatcher" component: "frontend" domain: "prod" } annotations: { "prometheus.io.scrape": "true" "prometheus.io.port": "7080" } } spec: containers: [{ name: "breaddispatcher" image: "gcr.io/myproj/breaddispatcher:v0.3.24" ports: [{ containerPort: 7080 }] args: [ "-etcd=etcd:2379", "-event-server=events:7788", ] }] } } }
次のように書き換える。kind:Service と kind:deployment のテンプレートができた。
package kube service: [ID=_]: { apiVersion: "v1" kind: "Service" metadata: { name: ID labels: { app: ID // by convention domain: "prod" // always the same in the given files component: string // varies per directory } } spec: { // Any port has the following properties. ports: [...{ port: int protocol: *"TCP" | "UDP" // from the Kubernetes definition name: string | *"client" }] selector: metadata.labels // we want those to be the same } } deployment: [ID=_]: { apiVersion: "apps/v1" kind: "Deployment" metadata: name: ID spec: { // 1 is the default, but we allow any number replicas: *1 | int template: { metadata: labels: { app: ID domain: "prod" component: string } // we always have one namesake container spec: containers: [{ name: ID }] } } }
この辺りを見ながら復習
int
https://cuelang.org/docs/tutorials/tour/types/numbers/[ID=_]
https://cuelang.org/docs/tutorials/tour/types/templates/*"TCP"
https://cuelang.org/docs/tutorials/tour/types/defaults/*"TCP" | "UDP"
https://cuelang.org/docs/tutorials/tour/types/disjunctions/ports: [...{
https://cuelang.org/docs/tutorials/tour/types/lists/metadata: labels:
https://cuelang.org/docs/tutorials/tour/intro/fold/package kube
https://cuelang.org/docs/tutorials/tour/packages/packages/
cue eval ./... -c > snapshot2
再びディレクトリ下の cue ファイルを評価し snapshot2 を生成。が alert が出る。
$ cue eval ./... -c > snapshot2 /// .../kubernetes/tmp/services/mon/alertmanager service.alertmanager.metadata.labels.component: incomplete value (string): ./kube.cue:11:24 service.alertmanager.spec.selector.component: incomplete value (string): ./kube.cue:11:24 deployment.alertmanager.spec.template.metadata.labels.component: incomplete value (string): ./kube.cue:36:28 :
kubernetes/tmp/services/mon/alertmanager/kube.cue を確認する。component がない。
package kube service: alertmanager: { apiVersion: "v1" kind: "Service" metadata: { annotations: { "prometheus.io/scrape": "true" "prometheus.io/path": "/metrics" } labels: name: "alertmanager" name: "alertmanager" } spec: { selector: app: "alertmanager" // type: ClusterIP ports: [{ name: "main" protocol: "TCP" port: 9093 targetPort: 9093 }] } }
- kube.cue を置換
component: string
->component: #Component
- kube.cue の末尾に次を追加
#Component: string
- frontend,infra ... の各ディレクトリにコンポ―メント名を指定したがファイルを生成
#Component: "frontend"
- cue ファイルをtrim
diff をとると spec.selector の domain, component が揃っていることが分かる。
snapshot2 の mon alertmanager service
service: { alertmanager: { apiVersion: "v1" kind: "Service" metadata: { name: "alertmanager" labels: { name: "alertmanager" app: "alertmanager" domain: "prod" component: "mon" } annotations: { "prometheus.io/scrape": "true" "prometheus.io/path": "/metrics" } } spec: { ports: [{ name: "main" port: 9093 protocol: "TCP" targetPort: 9093 }] selector: { name: "alertmanager" app: "alertmanager" domain: "prod" component: "mon" } } } }
部分 eval を試す。
$ cue eval ./mon/alertmanager -e service alertmanager: { apiVersion: "v1" kind: "Service" metadata: { name: "alertmanager" labels: { name: "alertmanager" app: "alertmanager" domain: "prod" component: "mon" } annotations: { "prometheus.io/scrape": "true" "prometheus.io/path": "/metrics" } } spec: { ports: [{ name: "main" port: 9093 protocol: "TCP" targetPort: 9093 }] selector: { name: "alertmanager" app: "alertmanager" domain: "prod" component: "mon" } } }
service.alertmanager を yaml で得る。
$ cue eval ./mon/alertmanager -e service.alertmanager --out yaml apiVersion: v1 kind: Service metadata: name: alertmanager labels: name: alertmanager app: alertmanager domain: prod component: mon annotations: prometheus.io/scrape: "true" prometheus.io/path: /metrics spec: ports: - name: main port: 9093 protocol: TCP targetPort: 9093 selector: name: alertmanager app: alertmanager domain: prod component: mon
cue trim ./...
trim 作用はうまく説明できない。cue はこんなふうに矛盾しない限り合わせていくのだが、
- Duplicate Fields https://cuelang.org/docs/tutorials/tour/intro/duplicates/
- Order is irrelevant https://cuelang.org/docs/tutorials/tour/intro/order/
trim したときどっちに寄るんだろうとか。いい感じにしてくれるということで。
- Boilerplate removal https://cuelang.org/docs/concepts/logic/#boilerplate-removal
trim の help を見る
$ cue help trim trim removes fields from structs that can be inferred from constraints A field, struct, or list is removed if it is implied by a constraint, such as from an optional field maching a required field, a list type value, a comprehension or any other implied content. It will modify the files in place. Limitations Removal is on a best effort basis. Some caveats: - Fields in implied content may refer to fields within the struct in which they are included, but are only resolved on a best-effort basis. - Disjunctions that contain structs in implied content cannot be used to remove fields. - There is currently no verification step: manual verification is required. Examples: $ cat <<EOF > foo.cue light: [string]: { room: string brightnessOff: *0.0 | >=0 & <=100.0 brightnessOn: *100.0 | >=0 & <=100.0 } light: ceiling50: { room: "MasterBedroom" brightnessOff: 0.0 // this line brightnessOn: 100.0 // and this line will be removed } EOF $ cue trim foo.cue $ cat foo.cue light: [string]: { room: string brightnessOff: *0.0 | >=0 & <=100.0 brightnessOn: *100.0 | >=0 & <=100.0 } light: ceiling50: { room: "MasterBedroom" } It is guaranteed that the resulting files give the same output as before the removal.
トップレベルテンプレートの編集
ここからはチュートリアル見るべき。。。
- kind 毎の共通構造と spec 構造をトップレベルテンプレートに抽出
- port 定義簡略化の工夫
- サブレベルテンプレート作成
- cue trim --simplify Folding of Single-Field Structs https://cuelang.org/docs/tutorials/tour/intro/fold/
- サブレベル毎のカスタマイズ
そうこうしているうちに
そうこうしているうちに v0.3.0-alpha3 がでている。