通过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进行部署.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章