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

为 Kubernetes 领域模型抽象应用层模型 - 使用 KCL 配置语言 & Konfig 配置库

  •  1
     
  •   peefy · 2023-02-20 11:59:30 +08:00 · 553 次点击
    这是一个创建于 402 天前的主题,其中的信息可能已经有所发展或是发生改变。

    KCL & Konfig 概述

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

    在 KCL 中, 推荐通过配置库的方式统一管理所有的配置清单和模型库,即不仅存放抽象模型本身的 KCL 定义,还存放各种类型的配置清单,比如应用的运维配置、策略配置等。配置大库推荐托管在各类 VCS 系统中,以方便做配置的回滚和漂移检查。配置大库的最佳实践代号为 Konfig ,仓库托管在 Github

    ⚡️ 配置大库主要包括:

    • KCL 模块声明文件( kcl.mod )
    • KCL 领域模型库 (Kubernetes, Prometheus 等)
    • 各类配置清单目录 (应用运维配置等)
    • 配置构建和测试脚本 (Makefile ,Github CI 文件等)

    之所以用一个统一的仓库管理全部的 KCL 配置代码,是由于不同代码包的研发主体不同,会引发出包管理和版本管理的问题。将业务配置代码、基础配置代码在一个统一仓库中,代码间的版本依赖管理会比较简单,通过定位唯一代码库的目录及文件即可,可以将配置代码统一管理,便于查找、修改、维护。

    Konfig 提供给用户开箱即用、高度抽象的配置界面,模型库最初朴素的出发点就是改善 YAML 用户的效率和体验,我们希望通过将代码更繁杂的模型抽象封装到统一的模型中,从而简化用户侧配置代码的编写。Konfig 由以下部分组成:

    • 核心模型
      • 前端模型:前端模型即「用户界面」,包含平台侧暴露给用户的所有可配置属性,其中省略了一些重复的、可推导的配置,抽象出必要属性暴露给用户,具有用户友好的特性,比如 server.k 。
      • 后端模型:后端模型是「模型实现」,是让前端模型属性生效的模型,主要包含前端模型实例的渲染逻辑,后端模型中可借助 KCL 编写校验、逻辑判断、代码片段复用等代码,提高配置代码复用性和健壮性,对用户不感知,比如 server_backend.k
    • 领域模型:是不包含任何实现逻辑和抽象的模型,往往由工具转换生成,无需修改,和真正生效的 YAML 属性一一对应,底层模型需要经过进一步抽象,一般不直接被用户使用。比如,kusion_kubernetes 是 Kubernetes 场景的底层模型库。

    此外,核心模型内部通过前端模型和后端模型两层抽象简化前端用户的配置代码,底层模型则是通过 KCL OpenAPI 工具 自动生成。

    整体结构

    .
    ├── .github             # CI 脚本
    ├── Makefile            # 通过 Makefile 封装常用命令
    ├── README.md           # 配置大库说明
    ├── appops              # 应用运维目录,用来放置所有应用的 KCL 运维配置
    │   ├── clickhouse-operator
    │   ├── code-city
    │   ├── guestbook
    │   ├── http-echo
    │   └── nginx-example
    ├── base                # 模型库
    │   ├── examples        # 样例代码
    │   │   ├── monitoring  # 监控配置样例
    │   │   ├── native      # Kubernetes 资源配置样例
    │   │   ├── provider    # 基础资源配置样例
    │   │   └── server      # 云原生应用运维配置模型样例
    │   └── pkg
    │       ├── kusion_kubernetes   # Kubernetes 底层模型库
    │       ├── kusion_models       # 核心模型库
    │       ├── kusion_prometheus   # Prometheus 底层模型库
    │       └── kusion_provider     # 基础资源 底层模型库
    └── kcl.mod             # 大库配置文件,通常用来标识大库根目录位置以及大库所需依赖
    

    核心模型库结构

    核心模型库一般命名为 kusion_models ,主要包含前端模型、后端模型、Mixin 、渲染器等,目录结构为:

    ├── commons         # 基础资源核心模型库
    ├── kube            # 云原生资源核心模型库
    │   ├── backend         # 后端模型
    │   ├── frontend        # 前端模型
    │   │   ├── common          # 通用前端模型
    │   │   ├── configmap       # ConfigMap 前端模型
    │   │   ├── container       # 容器前端模型
    │   │   ├── ingress         # Ingress 前端模型
    │   │   ├── resource        # 资源规格前端模型
    │   │   ├── secret          # Secret 前端模型
    │   │   ├── service         # Service 前端模型
    │   │   ├── sidecar         # Sidecar 容器前端模型
    │   │   ├── strategy        # 策略前端模型
    │   │   ├── volume          # Volume 前端模型
    │   │   └── server.k        # 云原生应用运维前端模型
    │   ├── metadata        # 应用运维的元数据模型
    │   ├── mixins          # 统一放置可复用的 Mixin
    │   ├── render          # 渲染器,把前后端模型联系在一起的桥梁
    │   ├── templates       # 静态配置
    │   └── utils           # 工具方法
    └── metadata        # 通用元数据模型
    

    Project 和 Stack 结构

    Project 和 Stack 是用于组织 Konfig 的逻辑隔离概念。

    Project

    任何包含文件 "project.yaml" 的文件夹都将被视为一个 Project ,"project.yaml" 用于描述此 Project 的元数据,如 "name" 和 "tenant" 等。项目必须具有明确的业务语义,用户可以将应用程序或运维场景映射到项目。

    Stack

    与 Project 一样,包含文件 "stack.yaml" 的任何文件夹都将被视为一个 Stack ,"stack.yaml" 用于描述此 Stack 的元数据。Stack 是一组 KCL 文件,表示可以单独配置和部署的最小操作单元,它通常代表 CI/CD 过程中的不同阶段。

    Project 与 Stack 之间的关系

    一个 Project 包含一个或多个 Stack ,Stack 必须属于且只能属于一个 Project 。用户可以根据自己的需要解释 Project 和 Stack 的含义,并灵活组织 Konfig 结构。根据我们的经验,我们提供以下示例作为最佳实践:

    ├── README.md       # Project 介绍文件
    ├── base            # 各环境通用配置
    │   └── base.k      # 通用 KCL 配置
    ├── dev             # 环境特有配置
    │   ├── ci-test     # 测试目录
    │   │   ├── settings.yaml       # 测试数据
    │   │   └── stdout.golden.yaml  # 测试期望结果
    │   ├── kcl.yaml    # 多文件编译配置,是 KCL 编译的入口
    │   ├── main.k      # 当前环境 KCL 配置
    │   └── stack.yaml  # Stack 配置文件
    └── project.yaml    # Project 配置文件
    

    Project 通常表示一个应用程序,Stack 表示该应用程序的不同环境的配置,例如 dev 、pre 和 prod 等。通用配置可以存储在该 Project 下的 "base" 目录中。

    快速开始

    本节向你展示,如何使用 KCL 语言与其相对应的 CLI 工具,完成一个运行在 Kubernetes 中的 Long-Running 应用的部署,我们将组织配置的单位叫做应用( Application ),描述应用部署和运维细节的配置集合叫做应用服务( Server ),它本质上是通过 KCL 定义的运维模型。

    要将一个运行在 Kubernetes 中的应用完全部署起来,一般需要下发多个 Kubernetes 资源,本次演示的样例涉及以下 Kubernetes 资源:

    • 命名空间( Namespace )
    • 无状态工作负载( Deployment )
    • 服务( Service )

    不清楚相关概念的,可以前往 Kubernetes 官方网站,查看相关说明:

    准备工作

    在开始之前,我们需要做以下准备工作:

    1. 安装 KCL - 详情信息请参阅下载和安装

    2. 下载开源 Konfig 大库,仓库地址: https://github.com/KusionStack/konfig.git

    git clone https://github.com/KusionStack/konfig.git && cd konfig
    

    配置编译

    Konfig 的编程语言是 KCL ,不是 Kubernetes 认识的 JSON/YAML ,因此还需要编译得到最终输出。

    进入到项目的 Stack 目录(appops/nginx-example/dev)并执行编译:

    cd appops/nginx-example/dev && kcl -Y kcl.yaml -D __konfig_output_format__=raw
    

    可以获得如下 YAML 输出:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-exampledev
      namespace: nginx-example
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: nginx-example
          app.kubernetes.io/env: dev
          app.kubernetes.io/instance: nginx-example-dev
          app.kubernetes.io/component: nginx-exampledev
      template:
        metadata:
          labels:
            app.kubernetes.io/name: nginx-example
            app.kubernetes.io/env: dev
            app.kubernetes.io/instance: nginx-example-dev
            app.kubernetes.io/component: nginx-exampledev
        spec:
          containers:
          - image: nginx:1.7.8
            name: main
            ports:
            - containerPort: 80
              protocol: TCP
            resources:
              limits:
                cpu: 100m
                memory: 100Mi
                ephemeral-storage: 1Gi
              requests:
                cpu: 100m
                memory: 100Mi
                ephemeral-storage: 1Gi
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
      name: nginx-example
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-example
      namespace: nginx-example
    spec:
      ports:
      - nodePort: 30201
        port: 80
        targetPort: 80
      selector:
        app.kubernetes.io/name: nginx-example
        app.kubernetes.io/env: dev
        app.kubernetes.io/instance: nginx-example-dev
        app.kubernetes.io/component: nginx-exampledev
      type: NodePort
    

    完成编译,可以看到 3 个资源:

    • 一个 name 为 nginx-exampledev 的 Deployment
    • 一个 name 为 nginx-example 的 Namespace
    • 一个 name 为 nginx-example 的 Service

    以上就完成了配置生效,后续可以使用 kubectl apply 等命令下发并检查资源的实际状态,本文不在赘述。

    配置修改

    Server 模型中的 image 属性用于声明应用的业务容器镜像,我们可以修改 base/main.k 中的 image 的值进行镜像修改或升级:

    14c14
    <     image = "nginx:1.7.8"
    ---
    >     image = "nginx:latest"
    

    重新编译配置代码可以获得修改后的 YAML 输出:

    kcl -Y kcl.yaml -D __konfig_output_format__=raw
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-exampledev
      namespace: nginx-example
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: nginx-example
          app.kubernetes.io/env: dev
          app.kubernetes.io/instance: nginx-example-dev
          app.kubernetes.io/component: nginx-exampledev
      template:
        metadata:
          labels:
            app.kubernetes.io/name: nginx-example
            app.kubernetes.io/env: dev
            app.kubernetes.io/instance: nginx-example-dev
            app.kubernetes.io/component: nginx-exampledev
        spec:
          containers:
          - image: nginx:latest
            name: main
            ports:
            - containerPort: 80
              protocol: TCP
            resources:
              limits:
                cpu: 100m
                memory: 100Mi
                ephemeral-storage: 1Gi
              requests:
                cpu: 100m
                memory: 100Mi
                ephemeral-storage: 1Gi
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
      name: nginx-example
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-example
      namespace: nginx-example
    spec:
      ports:
      - nodePort: 30201
        port: 80
        targetPort: 80
      selector:
        app.kubernetes.io/name: nginx-example
        app.kubernetes.io/env: dev
        app.kubernetes.io/instance: nginx-example-dev
        app.kubernetes.io/component: nginx-exampledev
      type: NodePort
    

    结语

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

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1396 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 17:33 · PVG 01:33 · LAX 10:33 · JFK 13:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.