
当前,Go是人们选择为Kubernetes编写语句的编程语言中的垄断者。 出于以下客观原因:
- 有一个强大的框架可用于在Go- Operator SDK上开发运算符 。
- Go编写了颠倒的应用程序,例如Docker和Kubernetes。 要在Go中编写自己的运算符,请使用与生态系统相同的语言。
- Go上的高性能应用程序和用于并发使用的简单工具。
注意 :顺便说一句,我们已经描述了外国作者在我们的翻译版本中如何在Go上编写自己的运算符。但是,如果由于缺乏时间或琐碎的动机而阻止学习Go,该怎么办? 本文提供了一个示例,说明了如何使用几乎每位DevOps工程师都知道的最流行的语言之一编写可靠的运算符-Python。
见面:文案-复制操作员!
例如,考虑开发一个简单的运算符,该运算符旨在在出现新的名称空间或两个实体之一(ConfigMap和Secret)发生更改时复制ConfigMap。 从实际应用程序的角度来看,操作员对于大规模更新应用程序配置(通过更新ConfigMap)或更新秘密数据(例如,用于Docker注册表的密钥(在将Secret添加到名称空间时))非常有用。
因此
,好的操作员应该具备以下条件 :
- 使用自定义资源定义 (以下称为CRD)与操作员进行交互。
- 可以定制操作员。 为此,我们将使用命令行标志和环境变量。
- 正在设计Docker容器和Helm图表的组合,以便用户可以轻松地(使用一个命令)将操作员安装在其Kubernetes集群中。
CRD
为了使操作员知道要寻找什么资源以及在哪里寻找他,我们需要为他制定规则。 每个规则将表示为单个CRD对象。 该CRD应该拥有哪些领域?
- 我们正在寻找的资源类型 (ConfigMap或Secret)。
- 资源应位于的名称空间列表 。
- Selector ,通过它我们可以在命名空间中查找资源。
我们描述CRD:
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: copyrator.flant.com spec: group: flant.com versions: - name: v1 served: true storage: true scope: Namespaced names: plural: copyrators singular: copyrator kind: CopyratorRule shortNames: - copyr validation: openAPIV3Schema: type: object properties: ruleType: type: string namespaces: type: array items: type: string selector: type: string
并立即创建一个
简单规则 -在名称空间中搜索所有ConfigMap的
default
名称,并使用诸如
copyrator: "true"
类的标签:
apiVersion: flant.com/v1 kind: CopyratorRule metadata: name: main-rule labels: module: copyrator ruleType: configmap selector: copyrator: "true" namespace: default
做完了! 现在,您需要以某种方式获取有关我们规则的信息。 我必须立即预约,我们将不会向集群服务器API写入请求。 为此,我们将使用现成的Python库
kubernetes-client :
import kubernetes from contextlib import suppress CRD_GROUP = 'flant.com' CRD_VERSION = 'v1' CRD_PLURAL = 'copyrators' def load_crd(namespace, name): client = kubernetes.client.ApiClient() custom_api = kubernetes.client.CustomObjectsApi(client) with suppress(kubernetes.client.api_client.ApiException): crd = custom_api.get_namespaced_custom_object( CRD_GROUP, CRD_VERSION, namespace, CRD_PLURAL, name, ) return {x: crd[x] for x in ('ruleType', 'selector', 'namespace')}
作为此代码的结果,我们得到以下信息:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}
优秀:我们设法为运营商制定了规则。 最重要的是,我们采用了所谓的Kubernetes方式。
环境变量或标志? 我们承担一切!
我们转到操作员的主要配置。 有两种配置应用程序的基本方法:
- 使用命令行选项
- 使用环境变量。
命令行选项使您可以更灵活地读取设置,并支持和验证数据类型。 Python标准库有一个
argparser
模块,我们将使用它。
官方文档中提供了其功能的详细信息和示例。
这是在我们的案例中设置命令行标志读取的示例:
parser = ArgumentParser( description='Copyrator - copy operator.', prog='copyrator' ) parser.add_argument( '--namespace', type=str, default=getenv('NAMESPACE', 'default'), help='Operator Namespace' ) parser.add_argument( '--rule-name', type=str, default=getenv('RULE_NAME', 'main-rule'), help='CRD Name' ) args = parser.parse_args()
另一方面,使用Kubernetes中的环境变量,您可以轻松地将有关Pod的服务信息传输到容器。 例如,我们可以通过以下结构获取有关运行pod的名称空间的信息:
env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace
运算符逻辑
为了了解如何区分使用ConfigMap和Secret的方法,我们将使用特殊的卡。 然后,我们可以了解跟踪和创建对象所需的方法:
LIST_TYPES_MAP = { 'configmap': 'list_namespaced_config_map', 'secret': 'list_namespaced_secret', } CREATE_TYPES_MAP = { 'configmap': 'create_namespaced_config_map', 'secret': 'create_namespaced_secret', }
接下来,您需要从API服务器接收事件。 我们将其实现如下:
def handle(specs): kubernetes.config.load_incluster_config() v1 = kubernetes.client.CoreV1Api()
收到事件后,我们继续其处理的主要逻辑:
基本逻辑已准备就绪! 现在,您需要将所有这些打包到一个Python包中。 我们执行
setup.py
,在其中写入有关项目的元信息:
from sys import version_info from setuptools import find_packages, setup if version_info[:2] < (3, 5): raise RuntimeError( 'Unsupported python version %s.' % '.'.join(version_info) ) _NAME = 'copyrator' setup( name=_NAME, version='0.0.1', packages=find_packages(), classifiers=[ 'Development Status :: 3 - Alpha', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], author='Flant', author_email='maksim.nabokikh@flant.com', include_package_data=True, install_requires=[ 'kubernetes==9.0.0', ], entry_points={ 'console_scripts': [ '{0} = {0}.cli:main'.format(_NAME), ] } )
注意 :适用于Python的kubernetes客户端具有自己的版本控制。 您可以从兼容性列表中了解有关客户端版本和Kubernetes版本之间的兼容性的更多信息。现在,我们的项目如下所示:
copyrator ├── copyrator │ ├── cli.py # │ ├── constant.py # , │ ├── load_crd.py # CRD │ └── operator.py # └── setup.py #
Docker和Helm
Dockerfile将非常简单:获取基本的python-alpine映像并安装我们的软件包。 我们会将其优化推迟到更好的时候:
FROM python:3.7.3-alpine3.9 ADD . /app RUN pip3 install /app ENTRYPOINT ["copyrator"]
操作员的部署也非常简单:
apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Chart.Name }} spec: selector: matchLabels: name: {{ .Chart.Name }} template: metadata: labels: name: {{ .Chart.Name }} spec: containers: - name: {{ .Chart.Name }} image: privaterepo.yourcompany.com/copyrator:latest imagePullPolicy: Always args: ["--rule-type", "main-rule"] env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace serviceAccountName: {{ .Chart.Name }}-acc
最后,您必须为具有必要权限的操作员创建适当的角色:
apiVersion: v1 kind: ServiceAccount metadata: name: {{ .Chart.Name }}-acc --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: {{ .Chart.Name }} rules: - apiGroups: [""] resources: ["namespaces"] verbs: ["get", "watch", "list"] - apiGroups: [""] resources: ["secrets", "configmaps"] verbs: ["*"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: {{ .Chart.Name }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: {{ .Chart.Name }} subjects: - kind: ServiceAccount name: {{ .Chart.Name }}-acc
总结
因此,无需担心,责备和学习Go,我们就可以使用Python编写自己的Kubernetes运算符。 当然,他仍然有成长的空间:将来,他将能够处理多个规则,在多个线程中工作,独立监视其CRD中的更改...
为了更仔细地查看代码,我们将其放在
公共存储库中 。 如果要使用Python实现更严格的运算符的示例,可以将注意力转向两个用于部署mongodb的运算符(第
一个和
第二个 )。
PS并且,如果您懒于处理Kubernetes事件,或者您只是更习惯使用Bash,我们的同事将准备一个现成的解决方案,以
shell运算符的形式(我们在4月
宣布 )。
PPS
另请参阅我们的博客: