通過jsonnet編寫K8s部署配置文件

0x00 前言

上一篇 已經介紹了jsonnet這個語法的一般用法, 學習之後就來體驗一下它的使用場景:
我們先寫好一些常用的部署配置模板, 比如Deployment, Service 等.
然後在需要臨時部署的時候 , 只需要寫最小化代碼, 完成配置文件的生成,驗證,部署.

0x01 相關工具

  • jsonnet : 用於解析jsonnet源碼至json文檔, 安裝方式: brew install jsonnet
  • gojsontoyaml: json格式向yaml格式轉換 go install github.com/brancz/gojsontoyaml@latest
  • kubeval: 驗證生成的yaml 是否是合法的k8s部署文件, 安裝: brew tap instrumenta/instrumenta && brew install kubeval
  • kubecfg: 支持jsonnet文件的yaml展示, 以及與k8s集羣的交互, 詳見這裏 安裝: brew install kubecfg
  • 開發環境: 使用VSCode, 加上插件 Jsonnet NG就可以高亮, 格式化, 錯誤提示等.

0x02 模板文件編寫

先編寫兩個模板文件

  • deploy-base.jsonnet
{
  name:: error 'name is required',
  container:: error 'container is required',

  apiVersion: 'apps/v1',
  kind: 'Deployment',
  metadata: {
    name: $.name,
    labels: { app: $.name },
  },
  spec: {
    selector: { matchLabels: $.metadata.labels },
    template: {
      metadata: { labels: $.metadata.labels },
      spec: {
        containers: [
          $.container { name: $.name },
        ],
      },
    },
  },
}
  • service-base.jsonnet
{
  name:: error 'name is required',
  namespace:: error 'namespace is required',
  # 定義默認值,外部可以覆蓋
  port:: {
    port: 8080,
    targetPort: 8080,
  },

  apiVersion: 'v1',
  kind: 'Service',
  metadata: {
    name: $.name,
    namespace: $.namespace,
  },
  spec: {
    selector: { app: $.name },
    ports: [
      $.port,
    ],
    type: 'ClusterIP',
  },
}

先來寫一個部署文件:

  • my-nginx.jsonnet
local deploy = import 'deploy-base.jsonnet';

deploy {
  name: 'my-nginx',
  container: {
    image: 'nginx:1.12',
  },
}

我們看到部署文件就比較簡單, 只需提供最基礎參數完成配置.
接下來看看, 如何使用上面的工具, 進行格式轉換/部署.

jsonnet 作用是翻譯成json格式, 此時, 這個json文件也是可以被kubectl 提交部署的.
但是, 一般我們還是習慣了使用 yaml 格式. 先看下圖效果:


當然, 這時候, 如果這個yaml 沒有問題的話, 我們可以直接在後面 , 再跟一個部署指令就好:
jsonnet my-nginx.jsonnet | gojsontoyaml | kubectl apply -f -

但是, 我們還是希望提交前先驗證一下:


接下來, 再寫一個Service 服務試一下:

local service = import 'service-base.jsonnet';
service {
    name: 'my-nginx',
    namespace: 'dev',
}

執行轉換: jsonnet demo-svc.jsonnet | gojsontoyaml

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  namespace: dev
spec:
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: my-nginx
  type: ClusterIP

問題不大.

到這裏, 似乎可以結束了.
但是, 我們發現, 每一個部署, 都要單獨寫一個文件, 是不是有點不夠優雅?
要是能把 Deployment, Service 這些寫在一個文件裏, 應該真的很香.


解決方法, 也是參考了 這裏, 關於 YAML Stream Output的描述.

我們把兩個文件結合後, 效果如下:

local deploy = import 'deploy-base.jsonnet';
local service = import 'service-base.jsonnet';


local
  a = deploy {
    name: 'my-nginx',
    container: {
      image: 'nginx:1.12',
    },
  },
  b = service {
    name: 'my-nginx',
    namespace: 'dev',
  };

[a, b]

此時, 解析的方式稍有不同, 需要以 yaml 流的方法進行解析:
jsonnet -y my-service.jsonnet
得到結果:

---
{
   "apiVersion": "apps/v1",
   "kind": "Deployment",
   "metadata": {
      "labels": {
         "app": "my-nginx"
      },
      "name": "my-nginx"
   },
   "spec": {
      "selector": {
         "matchLabels": {
            "app": "my-nginx"
         }
      },
      "template": {
         "metadata": {
            "labels": {
               "app": "my-nginx"
            }
         },
         "spec": {
            "containers": [
               {
                  "image": "nginx:1.12",
                  "name": "my-nginx"
               }
            ]
         }
      }
   }
}
---
{
   "apiVersion": "v1",
   "kind": "Service",
   "metadata": {
      "name": "my-nginx",
      "namespace": "dev"
   },
   "spec": {
      "ports": [
         {
            "port": 8080,
            "targetPort": 8080
         }
      ],
      "selector": {
         "app": "my-nginx"
      },
      "type": "ClusterIP"
   }
}
...

這裏拿 gojsontoyaml去轉換, 只能拿到一個yaml對象:jsonnet -y my-service.jsonnet | gojsontoyaml, 這個就有點拉垮了 - -!

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-nginx
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.12
        name: my-nginx

但是不影響我們進行驗證可用性, 和部署.


是不是到這裏就可以結束了?
不不不, 是時候請出 kubecfg 了.

來, 我們直接用kubecfg 查看一下jsonnet的yaml格式:kubecfg show my-service.jsonnet

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-nginx
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.12
        name: my-nginx
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  namespace: dev
spec:
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: my-nginx
  type: ClusterIP

還可以進行和線上的對比和部署更新:

kubecfg diff my-service.jsonnet
kubecfg update my-service.jsonnet

0x03 總結

我們已經有了Kustomize 這樣的模板化工具, 還需要 jsonnet 麼?
我的答案是需要的, 特別是在臨時部署, 比如只需要增加一個Service 之類的場景.
只需要打開VSCode, 新加一個svc.jsonnet , 再加一個命令行, 就可以搞定.

另一個總結: kubectl 支持 json, yaml格式的部署文件, 以 -f 參數指定; 同時以 -k 的參數 支持kustomize 文件.
但是 kubectl 目前不支持jsonnet格式,有兩種方案:

  • 直接使用 kubecfg 進行維護
  • 通過格式轉換工具, 拿到 yaml 文件後, 再通過kubectl進行部署.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章