Kustomize 简介

Kubernetes 原生配置管理工具, 它自定义引入了一种无需模板的形式来定制应用程序配置,从而简化了对现成应用程序的应用。目前,在kubectl中内置了,通过 apply -k 即可应用。

Kustomize 遍历 Kubernetes 清单以增加、删除或更新配置选项,而无需分叉。它既能够作为独立的二进制文件应用,也能够作为kubectl的原生个性应用。

Kustomize 劣势

  • 齐全申明式的配置定制办法
  • 原生构建进 kubectl
  • 治理任意数量的独特定制的 Kubernetes 配置
  • ☸ 作为独立的二进制文件提供,用于扩大和集成到其余服务
  • 定制应用的每个工件都是纯 YAML,并且能够被验证和解决
  • Kustomize 反对 fork/modify/rebase 工作流
  • GitOps 工具(如 ArgoCD) 对其的完满反对

Kustomize 能够做什么

Reference:

URL: https://mp.weixin.qq.com/s/gmwkoqZpKbq1hM0B8XxQNw
在 Kubernetes 中咱们应用 YAML 文件来申明咱们的利用应该如何部署到底层的集群中,这些 YAML 文件中蕴含利用定义、治理须要的标签、日志、平安上下文定义、资源依赖关系等,当咱们利用扩大到成千盈百个 Pod 当前,治理这些 YAML 文件就会成为一场噩梦了。

最典型的就是,有很多我的项目要治理,同时又有多套不同的部署环境:开发环境、测试环境、UAT 环境、生产环境。甚至可能有不同的私有云 Kubernetes 发行版。那么每一套环境都须要一套各种各样的 YAML 文件, 然而它们间接只有局部细节有差别。比方:镜像 Tag,服务 Name,Label,有没有存储等。

如果全是手动,保护工作量十分微小,同时也容易出错。

Kustomize 相比 Helm, 更适宜解决这种场景的痛点:有一个根底(base)的模板治理一个我的项目的所有根底 YAML,更多高级的需要通过 overlay 来实现叠加笼罩。

另外还有一类典型场景,就是交融来自不同用户的配置数据(例如来自 devops/SRE 和 developers).

同时也能够联合 helm, 进行一些更高级的配置。

明天就以一个典型场景为例:生产环境 Deployment 主动注入商业(如:AppDynamics) 或开源 (SkyWalking/pinpoint) 的开箱即用的 Java Agent.

实战 - 通过 Kustomize 注入监控 APM Agent

背景概述

以 Java 为例,这里的 APM Agent 包是商业(如:AppDynamics) 或开源 (SkyWalking/pinpoint) 产品提供的开箱即用的 Agent jar 包。

在 Kubernetes 场景中,出于以下几点思考:

  1. 和利用镜像拆散;
  2. 复用

Agent jar 包做成了一个通用镜像,通过 init container 形式拷贝到运行中的利用容器中,并通过配置环境变量进行参数的主动设置。

✍️ 笔者注:

其实商业 APM 都有 Helm 或 Operator 实现自动化装置配置的性能,然而理论应用中体验不佳,不太适宜咱们的理论场景。

通过 Kustomize 注入监控 APM Agent 步骤

步骤简述如下:(以 AppDynamics Java Agent 为例)

  1. 获取原始的 Deployment yaml 文件(通过 kubectlkubectl neat 插件)
  2. 通过 Kustomize 对每个 Deployment 通过 patches 实现以下步骤:

    1. 注入 initContainers: appd-agent-attach-java, 该 initContainers 有:

      1. volumeMounts: 把 java agent jar 包挂载到 volume 实现共享;
    2. 在 Deployment -> 利用本人的 container 中,退出以下参数:

      1. volumeMounts: 把 java agent jar 包挂载到 volume 实现共享;
      2. 退出各种 AppD Java agent 须要的 env 信息;
    3. volumes 通过 tmpdir 实现共享。

Kustomize 目录构造

目录构造如下:

inject-appd-agent/├── base│   └── kustomization.yaml      ├── overlays    └── prod        ├── appd_agent.yaml           └── kustomization.yaml

其中,后续所有须要注入 APM Agent 的利用的 Deployment, 都放在 base 目录中。

base/kustomization.yaml

resources:  - ./foo-deployment.yml

留神:
这里提一句,目前的 resources 是不反对文件通配符 (file glob) 匹配的,具体 issue 能够见这里:

  • Enable globbing in "resources" field of kustomization.yaml · Issue #3205 · kubernetes-sigs/kustomize (github.com)

然而有个长期解决方案,就是通过执行命令:kustomize edit add resource base/*.yml 运行后会遍历 file blob, 将后果一个个加到 kustomization.yaml 中。

运行后的文件可能是这样:

resources:  - ./foo-deployment.yml  - ./bar-deployment.yml  - ./a-deployment.yml  - ./b-deployment.yml  - ./c-deployment.ymlapiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomization

overlays/prod/kustomization.yaml

bases:  - ../../base# patchesStrategicMerge:#   - appd_agent.yamlpatches:  - path: appd_agent.yaml    target:      kind: Deployment

留神:

这里没用 patchesStrategicMerge, 而是用了patches. 目标就是为了批量 patch. 下面 YAML 的意思是,将appd_agent.yaml 利用于所有的 Deployment manifests 中。

overlays/prod/appd_agent.yaml

具体内容如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: not-importantspec:  template:    spec:      containers:        - name: app          volumeMounts:            - mountPath: /opt/appdynamics-java              name: appd-agent-repo-java          env:            - name: APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY              value: xxxxxxxxxxxxxxxxxxxxxxxxxxxx            - name: APPDYNAMICS_CONTROLLER_HOST_NAME              value: 192.168.1.10            - name: APPDYNAMICS_CONTROLLER_PORT              value: '8090'            - name: APPDYNAMICS_CONTROLLER_SSL_ENABLED              value: 'false'            - name: APPDYNAMICS_AGENT_ACCOUNT_NAME              value: my_account            - name: APPDYNAMICS_AGENT_APPLICATION_NAME              valueFrom:                fieldRef:                  fieldPath: metadata.name            - name: APPDYNAMICS_AGENT_TIER_NAME              valueFrom:                fieldRef:                  fieldPath: metadata.namespace            - name: APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX              valueFrom:                fieldRef:                  fieldPath: metadata.name            - name: JAVA_TOOL_OPTIONS              value:                ' -Dappdynamics.agent.accountAccessKey=$(APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY)                -Dappdynamics.agent.reuse.nodeName=true -Dappdynamics.socket.collection.bci.enable=true                -javaagent:/opt/appdynamics-java/javaagent.jar'            - name: APPDYNAMICS_NETVIZ_AGENT_HOST              valueFrom:                fieldRef:                  apiVersion: v1                  fieldPath: status.hostIP            - name: APPDYNAMICS_NETVIZ_AGENT_PORT              value: '3892'      initContainers:        - command:            - cp            - -r            - /opt/appdynamics/.            - /opt/appdynamics-java          image: appdynamics/appdynamics-java-agent:1.8-21.11.3.33314          imagePullPolicy: IfNotPresent          name: appd-agent-attach-java          resources:            limits:              cpu: 200m              memory: 75M            requests:              cpu: 100m              memory: 50M          terminationMessagePath: /dev/termination-log          terminationMessagePolicy: File          volumeMounts:            - mountPath: /opt/appdynamics-java              name: appd-agent-repo-java      volumes:        - name: appd-agent-repo-java

下面内容就不具体阐明了,具体能够参考这篇文章,理解 APM Agent 注入的几种思路:

  • Best Practices for Instrumenting Containers with AppDynamics Agents - Application Performance Monitoring Blog | AppDynamics

留神:
实际中,下面内容有几点须要留神的:

  1. spec/template/spec/containers/name 这里往往是利用的名字,如foo, 如果 appd_agent.yaml 的内容要正确地 patch, 也须要写上同样的 container name; 这对于一个利用来说不是什么问题,然而对于批量自动化/GitOps 来说,就十分不不便。

    1. 我之前想用 Kustomize 中的 nameReference 来实现,然而没搞进去,有晓得的能够教教我
    2. ✔️20220706 11:00 update: Kustomize 出于平安思考, 是严格依照 metadata.name 进行 merge 的, 所以通过 Kustomize 无奈解决; 然而能够通过 yq(相似jq, 然而针对 yaml)来解决, 示例命令为: i=foo yq -i '.spec.template.spec.containers[0].name="strenv(i)"' appd_agent.yaml
  2. 之前的环境变量,手动部署的时候如这个:

    - name: APPDYNAMICS_AGENT_APPLICATION_NAME  value: foo

    间接写死,这样不便于批量自动化/GitOps. 解决办法就是利用 Kubernetes env 的 valueFrom 能力。改为如下就能够了。

    valueFrom:  fieldRef:    fieldPath: metadata.name
  3. appd_agent.yaml 的 Deployment name 是无所谓的,因为仍会是被 patch 清单的名字;另外 appd_agent.yaml 也不要带 namespace, 不便批量自动化/GitOps, 能够适应所有 NameSpace.

自动化脚本

最初,自动化脚本 demo 如下:(假如脚本位于 /inject-appd-agent/scripts/ 目录下)

#!/bin/bash# USAGE:# bash inject-appd-agent.sh default# NAMESPACE=defaultNAMESPACE=$1# only get deploy namedeployments=$(kubectl get deploy --no-headers=true -o custom-columns=:.metadata.name -n "${NAMESPACE}")for i in ${deployments}; do    # get deploy yaml    cd ../base && kubectl get deploy -n "${NAMESPACE}" "${i}" -o yaml | kubectl neat >./"${i}.yml"    # add the deploy yaml to base    kustomize edit add resource ./"${i}.yml"    # change appd_agent.yaml .spec.template.spec.containers[0].name    #   = <the deploy name>    cd ../overlays/prod && DEPLOY="${i}" yq -i '.spec.template.spec.containers[0].name = strenv(DEPLOY)' ./appd_agent.yaml    # dry run    # kustomize build . >../../examples/output/"${i}.yml"    # deploy rollout    kubectl apply -k .    # remove the deploy yaml resource from base    cd ../../base && kustomize edit remove resource ./"${i}.yml"done# clean upkustomize edit remove resource ./*.ymlcd ../overlays/prod && yq -i '.spec.template.spec.containers[0].name = "app"' ./appd_agent.yaml

运行下面脚本,即可实现对所有 Deployment 主动注入 AppD Java Agent, 并 Rollout 失效。

️ Reference

  • Kustomize - Kubernetes native configuration management
  • K8S 实用工具之四 - kubectl 实用插件 - 东风微鸣技术博客 (ewhisper.cn)
三人行, 必有我师; 常识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.