shigebeyond / K8sBoot

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

GitHub | Gitee

K8sBoot - 简化k8s资源定义文件

1 概述

k8s太复杂了,特别是资源定义文件,学习与使用成本很高,大部分伙伴很难学会,因此创作了K8sBoot工具,支持通过简化版的yaml配置来生成k8s最终的资源定义文件,yaml的代码量大大缩小;

框架通过编写简单的yaml, 就可以执行一系列复杂的操作步骤, 如打印变量/生成rc/rs/deploy等资源文件,极大的简化了伙伴编写k8s资源定义文件的工作量与工作难度,大幅提高人效;

框架通过提供类似pythonfor/if/break语义与变量赋值等步骤动作,赋予伙伴极大的开发能力,让资源定义文件的编写工作更具灵活性,能适用于广泛的应用场景。

框架提供include机制,用来加载并执行其他的步骤yaml,一方面是功能解耦,方便分工,一方面是功能复用,提高效率与质量,从而推进脚本整体的工程化。

2 特性

  1. 支持通过yaml来配置执行的步骤,简化了生成代码的开发: 每个步骤可以有多个动作,但单个步骤中动作名不能相同(yaml语法要求); 动作代表k8s的某个资源定义,如config/rc/rs/deploy等等;
  2. 支持类似pythonfor/if/break语义的步骤动作,灵活适应各种场景
  3. 支持include引用其他的yaml文件,以便解耦与复用
  4. 增加了应用(即app)的概念,为k8s资源定义增加了面向应用编程的方式,更加切合应用维度的部署与管理需求
  5. yaml的代码量大大缩小,K8sBoot的yaml代码量相当于k8s原生资源文件的1/4~1/10

3 搭配k8s命令简化框架,使用更简单

k8scmd:对k8s的复杂命令做了大量简化

4 同类yaml驱动框架

5 todo

  1. 支持更多的动作

6 安装

pip3 install K8sBoot

安装后会生成命令K8sBoot;

注: 对于深度deepin-linux系统,生成的命令放在目录~/.local/bin,建议将该目录添加到环境变量PATH中,如

export PATH="$PATH:/home/shi/.local/bin"

7 使用

# 1 执行单个文件
K8sBoot 步骤配置文件.yml

# 2 执行多个文件
K8sBoot 步骤配置文件1.yml 步骤配置文件2.yml ...

# 3 执行单个目录, 即执行该目录下所有的yml文件
K8sBoot 步骤配置目录

# 4 执行单个目录下的指定模式的文件
K8sBoot 步骤配置目录/step-*.yml

如执行 K8sBoot example/ingress/1hello.yml -o data/,输出如下

shi@shi-PC:[~/code/python/K8sBoot]: K8sBoot example/ingress/1hello.yml -o data/
2023-07-10 18:29:02,857 - ThreadPoolExecutor-0_0        - boot - DEBUG - Load and run step file: /home/shi/code/python/K8sBoot/example/ingress/1hello.yml
2023-07-10 18:29:02,860 - ThreadPoolExecutor-0_0        - boot - DEBUG - handle action: app(hello)=[{'containers': {'hello': {'image': 'registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server', 'ports': ['8000:9000']}}}, {'deploy': {'replicas': 2}}]
2023-07-10 18:29:02,860 - ThreadPoolExecutor-0_0        - boot - DEBUG - handle action: containers={'hello': {'image': 'registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server', 'ports': ['8000:9000']}}
2023-07-10 18:29:02,860 - ThreadPoolExecutor-0_0        - boot - DEBUG - handle action: deploy={'replicas': 2}
2023-07-10 18:29:02,862 - ThreadPoolExecutor-0_0        - boot - INFO - App[hello]的资源定义文件已生成完毕, 如要更新到集群中的资源请手动执行: kubectl apply --record=true -f /home/shi/code/python/K8sBoot/data

命令会自动操作并生成k8s资源文件

shi@shi-PC:[~/code/python/K8sBoot]: tree data
data
├── hello-deploy.yml
└── hello-svc.yml

8 步骤yaml详解

支持通过yaml文件来配置执行的步骤;

每个步骤可以有多个动作,但单个步骤中动作名不能相同(yaml语法要求);

动作代表k8s上的一种操作,如config/rc/rs/deploy等等;

下面详细介绍每个动作

8.1 基本动作

  1. ns:设置与生成 namespace 资源
ns: 命名空间名
  1. app:生成应用,并执行子步骤
# 如果应用名以@开头, 表示应用名也作为pod的主机名
# 如果应用名以-结尾,表示应用名作为资源名前缀,在创建资源时自动生成资源名
app(应用名):
    # 子步骤
    - config:
        auther: shigebeyond
  1. cname:为外部域名设置别名,会生成 ExternalName 类型的 Service 资源
cname:
  # 别名: 外部域名
  baidu: www.baidu.com # 外部域名
  sk: www.shikee.com
  example-test: jkmvc-example.test.svc.cluster.local:8080 # 其他命名空间中的服务, 带端口

cname可结合ingress

# 自己配host, 测试: curl example-test.k8s.com
- app(example-test):
  - cname:
      example-test: jkmvc-example.test.svc.cluster.local # 其他命名空间中的服务, 不带端口
  - ingress:
      # url对转发的(服务)端口映射,支持字典树形式
      example-test.k8s.com:
          /: example-test:8080 # 指定cname服务端口
  1. print: 打印, 支持输出变量/函数;
# 调试打印
print: "总申请数=${dyn_data.total_apply}, 剩余份数=${dyn_data.quantity_remain}"
  1. set_vars: 设置变量;
set_vars:
  name: shi
  password: 123456
  birthday: 5-27
  1. print_vars: 打印所有变量;
print_vars:
  1. for: 循环; for动作下包含一系列子步骤,表示循环执行这系列子步骤;变量for_i记录是第几次迭代(从1开始),变量for_v记录是每次迭代的元素值(仅当是list类型的变量迭代时有效)
# 循环3次
for(3) :
  # 每次迭代要执行的子步骤
  - print: $for_v

# 循环list类型的变量urls
for(urls) :
  # 每次迭代要执行的子步骤
  - print: $for_v

# 无限循环,直到遇到跳出动作
# 有变量for_i记录是第几次迭代(从1开始)
for:
  # 每次迭代要执行的子步骤
  - break_if: for_i>2 # 满足条件则跳出循环
    print: $for_v
  1. once: 只执行一次,等价于 for(1); once 结合 moveon_if,可以模拟 python 的 if 语法效果
once:
  # 每次迭代要执行的子步骤
  - moveon_if: for_i<=2 # 满足条件则往下走,否则跳出循环
    print: $for_v
  1. break_if: 满足条件则跳出循环; 只能定义在for/once循环的子步骤中
break_if: for_i>2 # 条件表达式,python语法
  1. moveon_if: 满足条件则往下走,否则跳出循环; 只能定义在for/once循环的子步骤中
moveon_if: for_i<=2 # 条件表达式,python语法
  1. if/else: 满足条件则执行if分支,否则执行else分支
- set_vars:
    txt: '进入首页'
- if(txt=='进入首页'): # 括号中包含的是布尔表达式,如果表达式结果为true,则执行if动作下的子步骤,否则执行else动作下的子步骤
    - print: '----- 执行if -----'
  else:
    - print: '----- 执行else -----'
  1. include: 包含其他步骤文件,如记录公共的步骤,或记录配置数据(如用户名密码);
include: part-common.yml

8.2 app作用域下的子动作

以下的动作,必须声明在app动作的子步骤中,动作的参数支持传递变量;

这种设计隐藏了k8s资源定义规范:

1 一般资源都需要放到app作用域下, 即资源的label=app名, 代表一组资源组成了一个app。
一般资源是指: config/secret/pod/rc/rs/ds/sts/deploy/service/job/cronjob/hpa/ingress/svc,以应用作为管理粒度。
2 一般资源的资源名=app名, 除了以下情况的service资源
2.1 ClusterIP类型的service资源名=app名
2.2 如果不存在ClusterIP类型的service资源,则其他类型的service资源名=app名
2.3 如果存在ClusterIP类型的service资源,则NodePort类型的service资源名=app名-np,LoadBalancer类型的service资源名=app名-lb
  1. labels:设置应用标签
labels: 
    env: prod
    env2: $env # 支持传递变量
  1. config:以键值对的方式来设置 Config 资源
config:
    auther: shigebeyond
  1. config_from_files:以文件内容的方式来设置 Config 资源,在挂载configmap时items默认填充用config_from_files()写入的key
# 读配置文件内容作为配置项
- config_from_files: ./default.conf # 单个文件, 文件名作为配置名, 文件内容作为配置值
- config_from_files: ./conf/ # 目录, 遍历目录下的所有文件作为配置项
- config_from_files: # 文件list, 遍历所有文件作为配置项
    - ./default.conf
    - ./index.html
- config_from_files: # 文件dict,key是配置名,value是文件路径
    default.conf: ./default.conf
  1. secret:以键值对的方式来设置 Secret 资源
secret:
    auther: c2hpZ2ViZXlvbmQK
  1. secret_from_files:以文件内容的方式来设置 Secret 资源,在挂载secret时items默认填充用secret_from_files()写入的key
secret_from_files: # secret文件
    - ./admin.conf
  1. containers:设置容器,用于生成资源 pod / ReplicationController / ReplicaSet / DaemonSet / StatefulSet / Deployment / Job / Cronjob / HorizontalPodAutoscaler 文件中的 spec.containers 元素
containers:
    nginx: # 定义多个容器, dict形式, 键是容器名, 值是容器配置
      image: nginx # 镜像
      env: # 以dict方式设置环境变量
        TZ: Asia/Shanghai
        # 引用pod信息
        POD_NAME: ${ref_pod_field(metadata.name)}
        POD_NAMESPACE: ${ref_pod_field(metadata.namespace)}
        POD_IP: ${ref_pod_field(status.podIP)}
        # 引用容器资源信息
        CPU_MIN: ${ref_resource_field(requests.cpu)}
        CPU_MAX: ${ref_resource_field(limits.cpu)}
        MEM_MIN: ${ref_resource_field(requests.memory)}
        MEM_MAX: ${ref_resource_field(limits.memory)}
        # 引用配置
        AUTHOR: ${ref_config(auther)}
      env_from: # 从当前应用的 config 或 secret 资源中导入环境变量
        - config
        #- config:xxx # 引用其他应用xxx的配置
        #- secret
      ports: # 端口映射
        - 80 # 容器端口
        #- 30000:80 # 服务端口:容器端口
        #- 30000:30000:80 # 宿主机端口:服务端口:容器端口
        #- udp://30000:80 # 前面加协议,默认tcp
      volumes: # 卷映射
        - /var/log/nginx
        #- /lnmp/www:/www
        - config://:/www # 挂载configmap所有key到目录
        - config://default.conf:/etc/nginx/conf.d/default.conf # 挂载configmap单个key到文件
        #- config://xxx/default.conf:/etc/nginx/conf.d/default.conf -- 挂载其他应用xxx的configmap
        - downwardAPI://:/etc/podinfo # 将元数据labels和annotations以文件的形式挂载到目录
        - downwardAPI://labels:/etc/podinfo2/labels.properties # 将元数据labels挂载为文件
        #- pvc://pvc1:/usr/share/nginx/html # 将pvc挂载为目录
      # 启动命令:命令改写后导致nginx自身服务没起来,应该是覆盖了nginx镜像自身的启动命令
      #command: sed -i 's/POD_IP/\$POD_IP/g' /www/index.html; tail -f /etc/profile
      #command: while true;do echo hello;sleep 1;done # 死循环维持pod运行
      ready?: # 就绪态
        # 各种秒数
        #seconds: initialDelaySeconds=5 periodSeconds=5 timeoutSeconds=5 successThreshold=1 failureThreshold=5 # 全写
        seconds: i=5 p=5 t=5 s=1 f=5 # 简写
        # 动作
        action: ls /etc/nginx/
      live?: # 存活性探针
        # 各种秒数
        #seconds: initialDelaySeconds=5 periodSeconds=5 timeoutSeconds=5 successThreshold=1 failureThreshold=5 # 全写
        seconds: i=5 p=5 t=5 s=1 f=5 # 简写
        # 动作
        action: http://localhost:80 # 在pod中执行,请使用容器端口
      resources: # 资源
        cpu: 0.01 # 最小值
        #cpu: 0.01~0.02 # 最小值~最大值
        memory: 50Mi
  1. initContainers:设置初始化容器,用于生成资源 pod / ReplicationController / ReplicaSet / DaemonSet / StatefulSet / Deployment / Job / Cronjob / HorizontalPodAutoscaler 文件中的 spec.initContainers 元素
initContainers:
  # 参数跟 containers 动作一样
  init:
    #image: busybox # 镜像可省,默认为busybox
    command: chmod 0777 /usr/share/filebeat/data
    volumes:
      - /data/filebeat:/usr/share/filebeat/data
  1. pod:生成 pod 资源
pod:
  1. deploy:生成 Deployment 资源
deploy:
    replicas: 1 # 副本数
# 简写
deploy: 1

# 更详细的参数
deploy:
    #hostname: nginx # 设置pod的主机名
    #hostname:  # 设置pod的主机名,如果不赋值,则默认取app名
    replicas: 2 # 副本数
    nodeSelector: # 节点选择: dict形式
      kubernetes.io/os: linux
    nodeAffinity:
      require: # requiredDuringSchedulingIgnoredDuringExecution简写
        - kubernetes.io/os in linux # 标签选择的表达式
      prefer: # preferredDuringSchedulingIgnoredDuringExecution简写
        - kubernetes.io/os in linux
      weight: 1
    podAffinity:
      require: # requiredDuringSchedulingIgnoredDuringExecution简写
        - app = nginx
      prefer: # preferredDuringSchedulingIgnoredDuringExecution简写
        - app = nginx
      weight: 1
      tkey: kubernetes.io/hostname # topologyKey简写
    podAntiAffinity:
      require: # requiredDuringSchedulingIgnoredDuringExecution简写
        - app = xxx
      prefer: # preferredDuringSchedulingIgnoredDuringExecution简写
        - app = xxx
      weight: 1
      tkey: kubernetes.io/hostname # topologyKey简写
    tolerations: # 容忍
      - node-role.kubernetes.io/master:NoSchedule
      - node-role.kubernetes.io/control-plane:NoSchedule
  1. rc:生成 ReplicationController 资源
rc:
    replicas: 1 # 副本数
# 简写
rc: 1
# 更详细的参数:参考 deploy 动作
  1. rs:生成 ReplicaSet 资源
rs:
    replicas: 1 # 副本数
# 简写
rs: 1
# 更详细的参数:参考 deploy 动作
  1. ds:生成 DaemonSet 资源
ds:
# 更详细的参数:参考 deploy 动作
  1. sts:生成 StatefulSet 资源
sts:
    replicas: 1 # 副本数
# 简写
sts: 1
# 更详细的参数:参考 deploy 动作
  1. job:生成 Job 资源: 完整写法
- app(counter):
    - containers:
        counter:
          image: busybox
          command: 'for i in 9 8 7 6 5 4 3 2 1; do echo \$i;sleep 2;done'
    # 任务
    - job:
        completions: 2 # job需要成功运行 Pods 的次数。默认为1
        parallelism: 2 # job在任一时刻应该并发运行 Pods 的数量。默认为1,如果上面的 completions 为 6 ,这个参数为 3 ,表示有 6 个 pod,允许有 3 个 pod 并发运行
        activeDeadlineSeconds: 30 # job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
        backoffLimit: 3 # job失败后进行重试的次数。默认为6

简写

- app(counter):
    # 任务
    - job:
        completions: 2 # job需要成功运行 Pods 的次数。默认为1
        parallelism: 2 # job在任一时刻应该并发运行 Pods 的数量。默认为1,如果上面的 completions 为 6 ,这个参数为 3 ,表示有 6 个 pod,允许有 3 个 pod 并发运行
        activeDeadlineSeconds: 30 # job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
        backoffLimit: 3 # job失败后进行重试的次数。默认为6
        # 任务命令:当没有声明容器时,它会自动构建一个busybox的container来运行命令
        command: 'for i in 9 8 7 6 5 4 3 2 1; do echo \$i;sleep 2;done'
  1. cronjob:生成 Cronjob 资源: 完整写法
- app(clock):
    - containers:
        clock:
          image: busybox
          command: 'date'
    # 定时任务
    - cronjob:
        schedule: "*/1 * * * *"

简写

- app(clock):
    # 定时任务
    - cronjob:
        schedule: "*/1 * * * *"
        # 任务命令:当没有声明容器时,它会自动构建一个busybox的container来运行命令
        command: 'date'
  1. hpa:生成 HorizontalPodAutoscaler 资源
hpa:
    by: # 扩容的度量指标
      memory: 50% # 尾部带%表示用使用率(百分比),否则用使用量(绝对值)
      cpu: 50%
    deploy: # 部署相关的子动作,可以是 rc/rs/deploy 等
      replicas: 1~3 # 副本数的最小值+最大值,应用在hpa
  1. ingress:生成 Ingress 资源
ingress:
    # url对转发的(服务)端口映射,支持字典树形式
    http://k8s.com/a: 80 # 当前应用的服务端口
    http://k8s.com/b: nginx:80 # 指定应用的服务端口
    http://k8s.com/c: nginx # 指定应用的第一个服务端口
    # 等价于
    http://k8s.com:
      /a: 80 # 当前应用的服务端口
      /d: nginx:80 # 指定应用的服务端口
      /c: nginx # 指定应用的第一个服务端口
      
    # 路径重写,如果是/api/hello,则去掉前缀api,访问服务的/hello,网关一般这么搞
    http://k8s.com/api(/|$)(.*): 80  

默认后端服务

ingress: nginx:80 # 直接int或str
  1. ingress_by_cookie: 基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试
# K8sBoot/example/ingress-by-cookie/gateway-by-cookie.yml
# 测试: curl --cookie "test=always" http://canary.com
- include: ../ingress/1hello.yml
- include: ../ingress/2demo.yml
- app(gateway-prod):
    - ingress:
        canary.com: hello
- app(gateway-grey):
    - ingress_by_cookie(test):
        canary.com: demo
  1. ingress_by_header: 基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试
# K8sBoot/example/ingress-by-header/gateway-by-header.yml
# 测试:curl -H "Region: cd" http://canary.com
- include: ../ingress/1hello.yml
- include: ../ingress/2demo.yml
- app(gateway-prod):
    - ingress:
        canary.com: hello
- app(gateway-grey):
    - ingress_by_header(Region=cd):
        canary.com: demo
  1. ingress_by_weight: 基于服务权重的流量切分,适用于蓝绿部署
# K8sBoot/example/ingress-by-weight/gateway-by-weight.yml
# 测试: for i in {1..10}; do  curl http://canary.com/; done;
- include: ../ingress/1hello.yml
- include: ../ingress/2demo.yml
- app(gateway-green):
    - ingress:
        canary.com: hello
- app(gateway-blue):
    - ingress_by_weight(50):
        canary.com: demo
  1. pvc: 生成pvc资源
- pvc: # 创建pvc
    size: 100Mi # 存储大小
    #storageClassName: xxx # 存储类型,可省(用集群的默认存储类)
    #accessModes: ['ReadWriteOnce'] # 访问模式,可省默认为['ReadWriteOnce']

9 demo

示例见源码 example 目录,接下来以 example/ingress 为案例讲解下 K8sBoot 与 k8scmd 的使用:

  1. 目录结构
shi@shi-PC:[~/code/python/K8sBoot]: tree example/ingress/
example/ingress/
├── 1hello.yml
├── 2demo.yml
└── 3gateway.yml
  1. 步骤yaml
# 1hello.yml
- app(hello):
    - containers:
        hello:
          image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server
          ports: # 端口映射
            - 8000:9000 # 服务端口:容器端口
    # 部署
    - deploy:
        replicas: 2 # 副本数
# 2demo.yml
- app(demo):
    - containers:
        demo:
          image: nginxdemos/hello:plain-text
          ports: # 端口映射
            - 8001:80 # 服务端口:容器端口
    # 部署
    - deploy:
        replicas: 2 # 副本数
# 3gateway.yml
- app(gateway):
    - ingress:
        # url对转发的(服务)端口映射,支持字典树形式
        k8s.com:
            /hello: hello:8000 # 指定应用的服务端口
            /demo: demo:8001
  1. 生成k8s资源文件
K8sBoot example/ingress/ -o data

生成的文件如下:

shi@shi-PC:[~/code/python/K8sBoot]: tree data/
data/
├── demo-deploy.yml
├── demo-svc.yml
├── gateway-ingress.yml
├── hello-deploy.yml
└── hello-svc.yml

生成的文件内容,其代码量是K8sBoot的yaml代码量的4.4倍

# demo-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels: &id001
    app: demo
  name: demo
spec:
  replicas: 2
  selector:
    matchLabels: *id001
  template:
    metadata:
      labels: *id001
    spec:
      containers:
      - image: nginxdemos/hello:plain-text
        imagePullPolicy: IfNotPresent
        name: demo
        ports:
        - containerPort: 80
      restartPolicy: Always
      volumes: []
---
# demo-svc.yml
apiVersion: v1
kind: Service
metadata:
  labels: &id001
    app: demo
  name: demo-svc-vip
spec:
  ports:
  - name: p80
    port: 8001
    protocol: TCP
    targetPort: 80
  selector: *id001
  type: ClusterIP
status:
  loadBalancer: {}
---
# hello-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels: &id001
    app: hello
  name: hello
spec:
  replicas: 2
  selector:
    matchLabels: *id001
  template:
    metadata:
      labels: *id001
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server
        imagePullPolicy: IfNotPresent
        name: hello
        ports:
        - containerPort: 9000
      restartPolicy: Always
      volumes: []
---
# hello-svc.yml
apiVersion: v1
kind: Service
metadata:
  labels: &id001
    app: hello
  name: hello-svc-vip
spec:
  ports:
  - name: p9000
    port: 8000
    protocol: TCP
    targetPort: 9000
  selector: *id001
  type: ClusterIP
status:
  loadBalancer: {}
---
# gateway-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  labels:
    app: gateway
  name: gateway
spec:
  ingressClassName: nginx
  rules:
  - host: k8s.com
    http:
      paths:
      - backend:
          service:
            name: hello-svc-vip
            port:
              number: 8000
        path: /hello
        pathType: Prefix
      - backend:
          service:
            name: demo-svc-vip
            port:
              number: 8001
        path: /demo
        pathType: Prefix
  tls: []
  1. 应用k8s资源文件
kubectl apply --record=true -f data/
#
k8sapply data/
  1. 查看pod

  1. 查看service

  1. 访问service url

  1. 查看ingress

  1. 访问ingress

About


Languages

Language:Python 98.6%Language:Shell 0.7%Language:HTML 0.6%Language:PHP 0.0%