另辟蹊径的kustomize

缘起

众所周知,Helm管理k8s manifest做法是分别定义value.yaml 和 template 文件,而我们的部署环境往往有多套,开发、测试、预发和线上,不同环境存在一些策略或者配置差异,例如:

  • 线上环境需要启动initContainer执行skywalking agent的部署工作,而非线上环境却往往不需要这一步
  • 线上k8s的yaml文件一般配置反亲和性策略实现服务高可用,而非线上环境就无所谓了

显而易见,单个value.yaml文件是无法满足这种参差不齐的多环境状况;解决方法也很简单,就是衍生出多个valve文件,value-dev.yaml,value-test.yaml分别对应不同环境。

牵一发而动全身,为了适配这种情况,我们的template也需要采用条件语句兼容多环境的配置,如下所示:

...

{{- if eq .Values.env "prod" -}} 
  initcontainer:
     ...
{{- end }}

{{- if eq .Values.env "prod" -}} 
  podAntiAffinity:
     ...
{{- end }}

{{- if eq .Values.env "prod" -}} 
  Volumes:
     ...
{{- end }}

 ...
复制代码

少量的if/else语句还能接受,但如果过多的if/else充斥在template中,就会造成template可读性变差

那么有没有相对优雅的实现?还真有,那就是Kustomize

Kustomize的哲学

与helm不同的是,Kustomize的做法是首先定义一个基础模板,不同环境基于基础模板再定义个性化的配置和参数。

这种做法其实就是类似于OOP编程中父类和子类的关系,子类集成父类的属性和方法,并可按需重写相应的属性和方法

image.png kustomize的约定:

  • 基础模板放在base目录中,其他的个性化配置放在overlay目录中
  • 每个目录中都必须有一个kustomization.yaml文件,用来申明文件之间的引用关系
  • overlay中的配置引用base目录中的基础yaml文件并可做相应的覆盖

base

看起来kustomize 似乎就是为多环境编排量身定做的.按照kustomize的做法,我们可以将各环境通用的yaml配置放在base目录中,其他环境的个性化、定制化参数放在overlay目录中 最终形成的目录结构如下:

   .
├── base  # 放置每个环境都会用到的通用配置
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── service.yaml
└── overlay
    ├── prod
    │   ├── kustomization.yaml
    │   ├── podAntiAffinity.yaml
    │   └── volumes.yaml
    └── test
        ├── env.yaml
        ├── kustomization.yaml
        └── volumes.yaml
 
复制代码

注意:每个目录中的kustomization.yaml文件是必须的

  • kustomization.yaml: 定义一些公共配置或者是引用其他yaml文件的内容
  • commonLabels: 定义公共标签,所有资源都会用到的标签
  • resources: 引用同目录下的哪些文件
cat base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:   #定义公共标签,所有yaml都会引用的标签
  app: application-name

resources: #定义引用同目录下的哪些yaml文件
- deployment.yaml
- service.yaml
复制代码

base目录中放置deployment.yaml 和serivce.yaml,并被kustomization.yaml文件所引用,当然让你也可以放置ingressl.yaml configmap.yaml等更多的对象

cat base/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-name
  namespace: namespace-A
spec:
  replicas: 1
  selector:
    matchLabels:
      app: application-name
  template:
    metadata:
      labels:
        app: application-name
    spec:
      imagePullSecrets:
      - name: registry-pull-secret
      containers:
      - name: application-name
        image: application-image
        imagePullPolicy: Always
        ports:
        - containerPort: application-port
          name: port
复制代码

overlay

overlay中建立两个目录

  • prod: 存放线上环境的yaml编排文件
  • test: 存放测试环境的yaml编排文件

再来看下prod目录下的kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:       # 定义基础yaml模板文件 
- ../../base # 表示引用base目录中的基础yaml文件

patches:     # 引用同目录下的个性化文件,并覆盖基础Yaml文件
- podAntiAffinity.yaml # 表示引用同目录中podAntiAffinity.yaml的文件

复制代码

再来看下podAntiAffinity.yaml的内容,定义了节点反亲和性的配置

cat overlay/prod/podAntiAffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-name # 引用base中已经定义的deployment
spec:
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - application-name
              topologyKey: kubernetes.io/hostname
复制代码

yaml文件的生成需要借助kusomize命令,下载安装kustomize

先了解一些基础命令:

# 生成编排文件,默认输出到终端
kustomize build -path

# 使用 -o 选项输出到指定路径
kustomize build -path -o target_file_path

# Kubernetes v1.14 版本之后,kustomize已经成为了kubectl内置的子命令
kubectl kustomize  -path
复制代码

万事俱备,我们执行命令生成最终文件

# 生成用于线上环境的yaml
kubectl kustomize overlay/prod

apiVersion: v1
kind: Service
metadata:
  labels:
    app: application-name
  name: application-name
  namespace: namespace-A
spec:
  ports:
  - name: http
    port: port
    protocol: TCP
    targetPort: application-port
  selector:
    app: application-name
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: application-name
  name: application-name
  namespace: namespace-A
spec:
  replicas: 1
  selector:
    matchLabels:
      app: application-name
  template:
    metadata:
      labels:
        app: application-name
    spec:
      `affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - application-name
            topologyKey: kubernetes.io/hostname`
      containers:
      - image: image-name
        imagePullPolicy: Always
        name: application-name
        ports:
        - containerPort: application-port
          name: application-port
      imagePullSecrets:
      - name: registry-pull-secret
复制代码

与原先base目录的基础deployment.yaml的仔细对比下,已经成功的将podAntiAffinity配置应用上了

实际的工作场景中,我们往往会将yaml文件保存在gitlab中做版本管理,而不是本地目录;没关系,kustomize也支持remote target, 简直完美!

target="https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6"
kubectl kustomize $target

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: myapp
  name: dev-myapp-pod
spec:
  containers:
  - image: nginx:1.7.9
    name: nginx
复制代码

与Jenkins的集成

kustomize结合Jenkins的 pipeline,实现k8s的发布

// 伪代码
   pipeline {
        agent {}
        parameters {
          string(name: 'application' description: '应用名',trim: true)
          string(name: 'deployEnv', description: '部署环境', trim: true)
        
        environment {
           manifestRepo="gitlab.com/walrus/manifest/${prarams.application}/overlay/${params.deployEnv}"
        }
        ......
        stage('部署'){
            steps {
                script {
                    sh "
                        kubectl kustomize ${env.manifestRepo} | kubectl apply -f -"
                }
            }
        }
    }
复制代码

猜你喜欢

转载自juejin.im/post/7110475167427461151