V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
peefy
V2EX  ›  程序员

KCL 与其他 Kubernetes 配置管理工具的异同 - Kustomize 篇 [一个自研编程语言能做什么?(系列 2)]

  •  
  •   peefy · 2023-02-01 13:22:40 +08:00 · 971 次点击
    这是一个创建于 653 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在系列上一篇文章 https://juejin.cn/post/7176250368827850810 中,我们介绍了如何使用 KCL 编写并管理 Kubernetes 配置并将配置下发到集群,这一篇我们通过 KCL 与其他 Kubernetes 配置管理工具的对比如 Kustomize 进一步介绍 KCL 在 Kubernetes 配置管理场景中的用法。

    简介

    KCL 是一个开源的基于约束的记录及函数语言。KCL 通过成熟的编程语言技术和实践来改进对大量繁杂配置的编写,致力于构建围绕配置的更好的模块化、扩展性和稳定性,更简单的逻辑编写,以及更快的自动化集成和良好的生态延展性。

    KCL 期望在 Kubernetes YAML 资源管理层面解决如下问题:

    1. 生产级高性能编程语言以编写代码的方式提升配置的灵活度,比如条件语句、循环、函数、包管理等特性提升配置重用的能力
    2. 在代码层面提升配置语义验证的能力,比如字段可选 / 必选、类型、范围等配置检查能力
    3. 提供配置分块编写、组合和抽象的能力,比如结构定义、结构继承、约束定义等能力

    本篇文章是 KCL 可以做什么系列文章第二篇,重点讲述 KCL 语言一些进阶用法以及与 Kustomize 工具的区别,后续会持续更新和分享 KCL 的一系列特点、使用场景和其他 Kubernetes 配置管理工具的异同,大家敬请期待!

    KCL 和 Kustomize 的区别

    Kustomize 提供了一种无需模板和即可自定义 Kubernetes 资源基础配置和差异化配置的解决方案,通过文件级的 YAML 配置方式完成配置合并或覆盖。在 Kustomize 中用户需要更详细地了解将要发生更改的内容和位置,对于复杂递归过深的基础 YAML 可能不太容易通过选择器来匹配 Kustomize 文件。

    而在 KCL 中,用户可以直接把对应代码需要修改的配置书写在对应的地方,免去了阅读基础 YAML 的成本,同时能够通过代码的方式复用配置片段,避免 YAML 配置的大量复制粘贴,信息密度更高,更不容易出错。

    下面以一个经典的 Kustomize 多环境配置管理例子详细说明 Kustomize 和 KCL 在 Kubernetes 资源配置管理上的区别。

    Kustomize

    Kustomize 有 base 和 overlay 的概念,bases 和 overlays 一般是一个包含 kustomization.yaml 文件的目录,一个 base 可以被多个 overlay 使用

    我们可以执行如下命令行获得一个典型的 Kustomize 工程

    • 创建 base 目录并新建一个 deployment 资源
     # Create a directory to hold the base
    mkdir base
    # Create a base/deployment.yaml
    cat <<EOF > base/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ldap
      labels:
        app: ldap
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: ldap
      template:
        metadata:
          labels:
            app: ldap
        spec:
          containers:
            - name: ldap
              image: osixia/openldap:1.1.11
              args: ["--copy-service"]
              volumeMounts:
                - name: ldap-data
                  mountPath: /var/lib/ldap
              ports:
                - containerPort: 389
                  name: openldap
          volumes:
            - name: ldap-data
              emptyDir: {}
    EOF
    # Create a base/kustomization.yaml
    cat <<EOF > base/kustomization.yaml
    resources:
    - deployment.yaml
    EOF
    
    • 创建一个 prod 目录并放置生产环境的配置
     # Create a directory to hold the prod overlay
    mkdir prod
    # Create a prod/deployment.yaml
    cat <<EOF > prod/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ldap
    spec:
      replicas: 6
      template:
        spec:
          volumes:
            - name: ldap-data
              emptyDir: null
              gcePersistentDisk:
                readOnly: true
                pdName: ldap-persistent-storage
    EOF
    cat <<EOF > prod/kustomization.yaml
    resources:
      - ../base
    patchesStrategicMerge:
      - deployment.yaml
    EOF
    

    此时我们可以得到一个基本的 Kustomize 目录

    .
    ├── base
    │   ├── deployment.yaml
    │   └── kustomization.yaml
    └── prod
        ├── deployment.yaml
        └── kustomization.yaml
    

    其中,base 目录存放的是基本的 deployment 配置,prod 环境存放的是需要覆盖的 deployment 配置,在其中指定了 metadata.name 等字段用于表示对哪个资源进行覆盖

    我们可以通过如下命令行显示 prod 环境的真实 deployment 配置

    $ kubectl kustomize ./prod
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: ldap
      name: ldap
    spec:
      replicas: 6
      selector:
        matchLabels:
          app: ldap
      template:
        metadata:
          labels:
            app: ldap
        spec:
          containers:
          - args:
            - --copy-service
            image: osixia/openldap:1.1.11
            name: ldap
            ports:
            - containerPort: 389
              name: openldap
            volumeMounts:
            - mountPath: /var/lib/ldap
              name: ldap-data
          volumes:
          - gcePersistentDisk:
              pdName: ldap-persistent-storage
              readOnly: true
            name: ldap-data
    

    也可以通过如下命令行直接将配置下发到集群当中

    $ kubectl apply -k ./prod
    
    deployment.apps/ldap created
    

    KCL

    我们可以编写如下 KCL 代码并命名为 main.k ,KCL 受 Python 启发,基础语法十分接近 Python, 比较容易学习和上手

    apiVersion = "apps/v1"
    kind = "Deployment"
    metadata = {
        name = "ldap"
        labels.app = "ldap"
    }
    spec = {
        replicas = 1
        # When env is prod, override the `replicas` attribute with `6`
        if option("env") == "prod": replicas = 6
        # Assign `metadata.labels` to `selector.matchLabels`
        selector.matchLabels = metadata.labels
        template.metadata.labels = metadata.labels
        template.spec.containers = [
            {
                name = metadata.name
                image = "osixia/openldap:1.1.11"
                args = ["--copy-service"]
                volumeMounts = [{ name = "ldap-data", mountPath = "/var/lib/ldap" }]
                ports = [{ containerPort = 80, name = "openldap" }]
            }
        ]
        template.spec.volumes = [
            {
                name = "ldap-data"
                emptyDir = {}
                # When env is prod
                # override the `emptyDir` attribute with `None`
                # patch a `gcePersistentDisk` attribute with the value `{readOnly = True, pdName = "ldap-persistent-storage"}`
                if option("env") == "prod":
                    emptyDir = None
                    gcePersistentDisk = {
                        readOnly = True
                        pdName = "ldap-persistent-storage"
                    }
            }
        ]
    }
    

    上述 KCL 代码中我们分别声明了一个 Kubernetes Deployment 资源的 apiVersionkindmetadata 和 spec 等变量,并分别赋值了相应的内容,特别地,我们将 metadata.labels 字段分别重用在 spec.selector.matchLabels 和 spec.template.metadata.labels 字段。可以看出,相比于 Kustomize 或者 YAML ,KCL 定义的数据结构更加紧凑,而且可以通过定义局部变量实现配置重用。

    在 KCL 中,我们可以通过条件语句和 option 函数动态地接收外部参数,为不同的环境需要设置不同的配置值生成不同环境的资源。比如对于如上代码,我们编写了一个条件语句并输入一个名为 env 的动态参数,当 env 为 prod 时,我们将 replicas 字段由 1 覆盖为 6,并且对名为 ldap-data 的 volume 配置进行一些调整,如将 emptyDir 字段修改为 None, 增加 gcePersistentDisk 的配置值等。

    可以使用如下命令查看不同环境配置的 diff

    diff \
      <(kcl main.k) \
      <(kcl main.k -D env=prod) |\
      more
    

    输出如下:

    8c8
    <   replicas: 1
    ---
    >   replicas: 6
    30c30,33
    <         emptyDir: {}
    ---
    >         emptyDir: null
    >         gcePersistentDisk:
    >           readOnly: true
    >           pdName: ldap-persistent-storage
    

    可以看到生产环境的配置和基本配置的 diff 主要在于 replicas 和 emptyDir 等字段,与预期相符。

    此外,我们可以使用 KCL 命令行工具的 -o 参数将编译产生的 YAML 输出到文件中,并查看文件之间的 diff 。

     # Generate base deployment
    kcl main.k -o deployment.yaml
    # Generate prod deployment
    kcl main.k -o prod-deployment.yaml -D env=prod
    # Diff prod deployment and base deployment
    diff prod-deployment.yaml deployment.yaml
    

    当然我们也可以将 KCL 工具与 kubectl 等工具结合使用,将生产环境的配置下发到集群当中。

    $ kcl main.k -D env=prod | kubectl apply -f -
    
    deployment.apps/ldap created
    

    可以从命令行的结果看出看出与我们使用直接使用 Kustomize 配置和 kubectl apply 的一个 Deployment 体验完全一致,并且无更多的副作用。

    最后,通过 kubectl 检查部署状态。

    $ kubectl get deploy
    
    NAME   READY   UP-TO-DATE   AVAILABLE   AGE
    ldap   0/6     6            0           15s
    

    小结

    本期内容大概简单介绍了用 KCL 编写复杂多环境 Kubernetes 配置的快速入门和使用 Kustomize 工具进行 Kubernetes 多环境配置管理的对比,可以看出相比于 Kustomize, KCL 在实现配置复用和覆盖的基础上,通过代码化的方式减少了配置文件的个数和代码行数,提升了信息密度比,并且同 Kustomize 一样是一个纯客户端方案,可以将配置和策略的验证尽可能左移,并不会对集群有额外依赖或造成负担,甚至无需一个真实的 Kubernetes 集群。

    除了 Kustomize, 目前阶段 Helm 也在 Kubernetes 配置定义和管理领域也十分流行,熟悉 Kubernetes 的小伙伴可能更喜欢显式配置编写方式。那么相较于 Helm ,用 KCL 来写配置文件渲染,又有什么异同呢?考虑到有很多小伙伴已经在使用 Helm 这样的工具,下一期我将介绍用 KCL 的方式来写 Helm 对应的配置代码,敬请期待!!

    如果您喜欢这篇文章,一定记得收藏 + 关注!!更多精彩内容请访问:

    如果您喜欢这些项目,欢迎 Github Star 鼓励一下 🌟🌟🌟,同时欢迎访问下面的链接加入我们的社区进行交流 👏👏👏

    https://github.com/KusionStack/community

    5 条回复    2023-09-11 15:03:49 +08:00
    xabcstack
        1
    xabcstack  
       2023-02-01 18:28:04 +08:00
    简单问题复杂化,阿里的传统糟粕
    defunct9
        2
    defunct9  
       2023-02-01 22:18:49 +08:00 via iPhone
    累🥱
    xyzxiaoking
        3
    xyzxiaoking  
       2023-02-02 09:25:13 +08:00
    有没有一些内置模版,或者提供一个现有 yaml 转到 kcl 工具,现在这样感觉对已有的配置使用成本还变高了
    peefy
        4
    peefy  
    OP
       2023-02-02 14:22:17 +08:00
    @xyzxiaoking 目前是有一系列内置模版 schema 和抽象 ,具体可以参考后续的文章或者这里的文档内容 https://kcl-lang.io/docs/user_docs/guides/working-with-konfig/overview 。另外我们正在逐步完善 kube2kcl 工具,支持现有 yaml 转到 kcl 和为已有的 yaml/kustomize/helm 编写 transformer, validator 等,后续会逐步完善并开源。
    lidashuang
        5
    lidashuang  
       2023-09-11 15:03:49 +08:00
    为啥发明一种新语言直接用 python 不好吗?
    比如 https://www.pulumi.com/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1143 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 18:51 · PVG 02:51 · LAX 10:51 · JFK 13:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.