编写另一个Kubernetes模板工具


如果您正在使用Kubernetes环境,那么您可能会使用几种现有的模板工具,其中一些是软件包管理器的一部分,例如HelmKsonnet ,或者只是模板语言(Jinja2,Go模板等)。 它们都有自己的缺点和优点,我们将仔细研究它们并编写自己的工具,以尝试结合最佳功能。


那么,为什么不舵?


有许多文章批评Helm(例如,其中只有一篇: 在使用Helm之前要三思而后行 )。 Helm的主要问题在于它可以使用字符串表示形式,而Kubernetes清单是(json)对象 。 当Helm图表开发人员需要为yaml清单计算缩进时,真正的地狱开始了,有时看起来像这样(这是我图表中的真实示例):


 spec: jobTemplate: spec: template: spec: containers: - name: my-awesome-container resources: {{ toYaml .Values.resources | indent 14 }} 


但是如今,Helm实际上已成为Kubernetes应用程序包装的标准。 Helm的主要优势是社区很大,并且有大量带有图表的公共存储库。 最近,Helm开发人员宣布了Helm中心 。 因此,今天的Helm就像Docker-它不是唯一的Docker,但它具有社区和支持。


Helm 3发行版带来了可喜的变化,但没人知道它何时会发布。


总之,Helm的优势:


  • 大型社区和许多公共图表
  • (相对)人类友好的语法。 至少是yaml + go模板;)

缺点:


  • 使用字符串而不是对象
  • 您可以使用的操作员和功能数量有限

好吧,也许是Ksonnet?


如果将Helm与Ksonnet进行比较,后者具有巨大的优势,即它可以与对象一起使用。 Ksonnet是基于JSON模板语言Jsonnet的工具。 关于Ksonnet的另一个很酷的功能是,它具有与Kubernetes-API兼容的Jsonnet库,您可以将其导入模板并像使用任何OOP语言一样使用Kubernetes对象:


 local k = import "k.libsonnet"; local deployment = k.apps.v1beta1.deployment; local appDeployment = deployment .new( params.name, params.replicas, container .new(params.name, params.image) .withPorts(containerPort.new(targetPort)), labels); 

看起来不错,不是吗?
当您不使用API​​对象而是仅使用从yaml / json文件导入的json对象时,它会稍微整洁一些:


 { global: {}, components: { "deployment-nginx-deployment-dkecx"+: { spec+: { replicas: 10, template+: { spec+: { containers+: [ { name: "nginx", image: "nginx:latest", ports: [ { containerPort: 80, }, ], }, ], }, }, }, }, }, } 

但这仍然是一件事情,它比在Helm中使用琴弦更好。 Ksonnet的缺点是与Helm相比,它的社区更小,程序包也更少(尽管您可以将Helm图表导入到Ksonnet项目中,但是您将它们作为json对象而不是jsonnet-library对象使用)。 而且由于社区和贡献的减少,试图编写自己的图表时缺少某些功能。 我经历了其中之一:您知道在Helm中,您可以通过以下方式从包含大量配置文件的目录中构建ConfigMap


 apiVersion: v1 kind: ConfigMap metadata: name: conf data: {{- (.Files.Glob "foo/*").AsConfig | nindent 2 }} 

当我发现Ksonnet中没有这样的功能时,您可以想象我的沮丧。 虽然有解决方法。 关键是,这只是当您快乐地编写图表时突然缺少某些功能而使您半途而废的情况的一个示例。
总体而言,Ksonnet的优势:


  • 处理对象
  • 兼容Kubernetes-API的Jsonnet库
  • 舵图导入支持

缺点:


  • 社区较小,Ksonnet本机软件包数量较少
  • 缺少可以在Helm中使用的某些功能
  • 新语法=>增加学习时间=>增加总线系数
  • 语法有时会变得丑陋且难以理解(尤其是在针对缺少功能的解决方法时)

让我们考虑一个理想的模板工具


以下是“理想”模板工具的一些标准:


  • 它应该适用于对象,而不是字符串
  • 它应该具有与Kubernetes-API兼容的对象的功能
  • 它应该具有一组处理字符串的功能
  • 它应该与json和yaml格式一起很好地工作
  • 应该是人类友好的
  • 应该很简单
  • 它应该具有导入现有Helm图表的能力(因为这是现实,我们想利用Helm社区)

现在就足够了。 我脑海中浏览了这份清单,对自己想:好吧,为什么不试试Python? 让我们看看它是否符合我们的标准:


  • 使用对象,而不是字符串 。 是的,我们可以使用dictlist类型。
  • 能够使用与Kubernetes-API兼容的对象 。 是的, from kubernetes import client
  • 有一套不错的函数可以处理字符串 。 好多!
  • 与json和yaml格式配合很好 。 很好
  • 人性化 没事
  • 简单的 是的
  • 能够导入现有的Helm图表 。 那,我们将添加自己。

好的,看起来很有希望。 我决定在官方Python客户端库的顶部为kubernetes编写一个简单的模板工具,现在让我向您展示它的结果。


认识卡拉维


这个工具没有什么特别或复杂的地方。 我只是使用了Kubernetes库(这使我能够使用Kubernetes对象),并为现有的Helm图表编写了一些基本功能(以便人们可以获取它们并将其添加到自己的图表中)。 因此,让我们一起游览。
首先,该工具可在Github存储库中访问,您可以在其中找到包含示例的目录。


Docker映像快速入门


如果您想尝试一下,最简单的方法是使用以下docker映像


 $ docker run greegorey/karavel -h usage: karavelcli.py [-h] subcommand ... optional arguments: -h, --help show this help message and exit list of subcommands: subcommand template generates manifests from template ensure ensure helm dependencies 

当然,如果要模板化图表,则需要挂载图表目录:


 $ cd example $ docker run -v $PWD:/chart greegorey/karavel template . 

因此,让我们看一下图表结构。 它与Helm之一非常相似:


 $ cd example $ tree . . ├── dependencies ├── prod.yaml ├── requirements.yaml ├── templates │  ├── custom-resource.py │  ├── deployment.py │  └── service-helm.py └── values.yaml 2 directories, 6 files 

与Helm一样,它具有带有相同布局的requirements.yaml文件:


 dependencies: - name: mysql version: 0.13.1 repository: https://kubernetes-charts.storage.googleapis.com/ 

在这里,您只列出了要导入到图表中的Helm依赖项。 依赖关系转到dependencies目录。 要获取或更新它们,请使用ensure命令:


 $ karavel ensure . 

之后,您的dependencies录将如下所示:


 $ tree dependencies dependencies └── mysql-0.13.1 └── mysql ├── Chart.yaml ├── README.md ├── templates │  ├── NOTES.txt │  ├── _helpers.tpl │  ├── configurationFiles-configmap.yaml │  ├── deployment.yaml │  ├── initializationFiles-configmap.yaml │  ├── pvc.yaml │  ├── secrets.yaml │  ├── svc.yaml │  └── tests │  ├── test-configmap.yaml │  └── test.yaml └── values.yaml 4 directories, 13 files 

现在,在确保依赖关系之后,让我们看一下模板。 首先,我们创建一个简单的nginx部署:


 from kubernetes import client from karavel.helpers import Values def template(): values = Values().values # Configure Pod template container container = client.V1Container( name='nginx', image='{}:{}'.format(values.nginx.image.repository, values.nginx.image.tag), ports=[client.V1ContainerPort(container_port=80)]) # Create and configurate a spec section template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(labels={'app': 'nginx'}), spec=client.V1PodSpec(containers=[container])) # Create the specification of deployment spec = client.ExtensionsV1beta1DeploymentSpec( replicas=3, template=template) # Instantiate the deployment object deployment = client.ExtensionsV1beta1Deployment( api_version='extensions/v1beta1', kind='Deployment', metadata=client.V1ObjectMeta(name='nginx-deployment'), spec=spec) return deployment # [deployment], (deployment, deployment) are valid 

因此,要使template()有效,您需要具有template()函数,该函数返回单个Kubernetes对象或它们的list / tuple 。 您可以在此处找到Python客户端的API对象列表。
如您所见,代码干净,简单,易读。 您可能想知道values.nginx.image.repository来自何处? 它从您在绘制图表时传递的值文件中获取值,就像在Helm中一样: karavel template -f one.yaml --values two.yaml 。 我们稍后再看。


好的,Helm图表怎么样?


现在,我们创建了自己的部署。 但是,如果我们要导入Helm图表或图表的一部分怎么办? 让我们看一下templates/service-helm.py


 from kubernetes import client from karavel.helm import HelmChart from karavel.helpers import Values def template(): values = Values().values # Initialize the chart (== helm template --values) chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm) # Get the desired object from chart service = chart.get(name='svc', obj_class=client.V1Service) # Create custom objects to add custom_ports = [ client.V1ServicePort( name='my-custom-port', protocol=values.mysql.protocol, port=values.mysql.port, target_port=39000, ) ] # Add custom objects to the service service.spec['ports'] = custom_ports # Change Helm-generated label service.metadata['labels']['release'] += '-suffix' # Delete Helm-generated label `heritage: Tiller` del service.metadata['labels']['heritage'] return service # [service], (service, service) are valid 

简单吧? 注意这一行: service = chart.get(name='svc', obj_class=client.V1Service) -我们从Helm yaml文件创建了V1Service类的对象。 如果您不想/不需要做-您可以只用dict来工作。


如果要创建自定义资源怎么办?


好吧,这有一个小问题。 Kubernetes API不会在/openapi/v2 swagger json定义中添加CRD对象,Python-client对象是基于此定义构建的。 但是您仍然可以轻松地使用dict对象。 像这样:


 from kubernetes import client def template(): resource = { 'apiVersion': 'stable.example.com/v1', 'kind': 'Whale', 'metadata': client.V1ObjectMeta( name='my-object', ), 'spec': { 'image': 'my-whale-image:0.0.1', 'tail': 1, 'fins': 4, } } return resource # [resource], (resource, resource) are valid 

看起来还不错,不是吗?


我可以在不同的环境中使用值吗,例如dev / prod?


是的,可以!
首先让我们看一下values.yaml


 nginx: image: repository: nginx tag: 1.15-alpine mysql: port: 3307 protocol: TCP helm: releaseName: my-release namespace: prod imageTag: '5.7.14' service: type: NodePort 

请注意mysql dict中的helm键:在为helm chart chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm)指定值时,我们使用了它。 一些Helm图表需要releaseName来进行应用程序命名,并需要使用RBAC策略的namespace 。 这两个值在helm template作为--namespaceNAME参数传递给Helm。


现在,您可以为prod env指定其他文件,并为所有示例创建模板:


 $ karavel template -f values.yaml -f prod.yaml . --- # Source: templates/custom-resource.py apiVersion: stable.example.com/v1 kind: Whale metadata: name: my-object spec: fins: 4 image: my-whale-image:0.0.1 tail: 1 --- # Source: templates/deployment.py apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - image: nginx:1.14-alpine name: nginx ports: - containerPort: 80 --- # Source: templates/service-helm.py apiVersion: v1 kind: Service metadata: annotations: null labels: app: prod-release-mysql chart: mysql-0.13.1 release: prod-release-suffix name: prod-release-mysql spec: ports: - name: my-custom-port port: 3308 protocol: TCP targetPort: 39000 selector: app: prod-release-mysql type: NodePort 

之后,您可以执行kubeclt apply并将这些对象部署到集群中。


好酷! 编码和base64呢?


import base64


将保险柜用于机密该怎么办?


import hvac


正在获取网址?


import importlib


安全的哈希函数?


import Crypto


知道了 使用Python,您可以使用Kubernetes清单执行很多操作。


是NIH综合征吗?


不:)
我是 愉快地 在我目前的项目中使用Helm。 虽然有些事情我很想念。 我在某些项目中也使用了Ksonnet。
我想将此工具视为概念证明,我们可以拥有比Helm更好的模板工具,并且使用Python开发它们并不是很困难。 如果社区对此工具有兴趣/需要,我们可以一起继续开发它。 或者我们可以等待Helm 3发布;)


结论


我向您展示了Kubernetes的基于Python的模板工具,该工具具有与Kubernetes-API兼容的对象支持以及对导入Helm图表的支持。 欢迎社区提供任何意见和讨论,并再次欢迎访问此回购协议


感谢您阅读本文,并祝您愉快!


参考文献


Source: https://habr.com/ru/post/zh-CN437682/


All Articles