Operator Framework是由CoreOS开发,后被RedHat收买的一个开源工具包,它能够无效的、自动化的和可扩大的形式治理 Kubernetes原生应用程序。该框架下蕴含Operator SDK,可帮助开发人员利用本人的专业知识来疏导和构建Operator,而无需理解 Kubernetes API复杂性。明天咱们就学习它,用于创立一个基于Ansible的Operator利用(之前小白在《Loki Operator扼要教程》中也简略聊到过),它能够利用现有 Ansible playbook和模块来部署和治理Kubernetes资源作为对立应用程序的生命周期,而无需编写任何Go代码。

装置

  • Homebrew装置 (MacOS)

如果你应用的是Homebrew,你能够用以下命令装置SDK CLI工具。

$ brew install operator-sdk
  • 二进制装置
$ export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n  arm64 ;; *) echo -n $(uname -m) ;; esac)$ export OS=$(uname | awk '{print tolower($0)}')$ export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.10.0#下载operator-sdk$ curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}$ chmod +x operator-sdk_${OS}_${ARCH} && sudo mv operator-sdk_${OS}_${ARCH} /usr/local/bin/operator-sdk
  • 编译装置
$ git clone https://github.com/operator-framework/operator-sdk$ cd operator-sdk$ git checkout master$ make install

初始化

1. 初始化我的项目

应用operator-sdk命令来疾速初始化这个我的项目的空间。

$ mkdir loki-operator$ cd loki-operator$ operator-sdk init --plugins=ansible --domain cloudxiaobai.com

这条命令生成的文件中有一个Kubebuilder的PROJECT形容文件。从外面咱们能够得悉,该工程是一个基于Ansible实现的我的项目

$ cat PROJECTdomain: loki.cloudxiaobai.comlayout:- ansible.sdk.operatorframework.io/v1plugins:  manifests.sdk.operatorframework.io/v2: {}  scorecard.sdk.operatorframework.io/v2: {}projectName: cloudxiaobaiversion: "3"

2. 创立API

$ operator-sdk create api \      --group=plugins --version=v1 --kind=Loki \      --generate-playbook \      --generate-role

下面这条命令能够疾速帮忙咱们生成这个我的项目的脚手架。其中蕴含如下内容:

  • 生成了Loki这个CRD的定义,和一个Loki CR的样例;
  • 生成了Controller,用于治理CR的的服务在Kubernetes集群上的状态
  • 生成了Ansible Playbook的目录构造
  • 生成了一个 watches.yaml文件,它用来将CR资源与Ansible Playbook关联起来。

Ansible Playbook

在实现我的项目的初始化后,咱们在我的项目下就失去了以下次要的目录。

├── Dockerfile├── config│   ├── samples│   │   └── plugins_v1_loki.yaml //Loki的CR样例├── playbooks    //playbook的入口│   └── loki.yml├── roles       //playbook文件│   └── loki│       ├── README.md│       ├── defaults│       │   └── main.yml│       ├── files│       ├── handlers│       │   └── main.yml│       ├── meta│       │   └── main.yml│       ├── tasks│       │   └── main.yml│       ├── templates│       └── vars│           └── main.yml

相熟Ansible的敌人看到这个就比拟眼生了,接下来咱们就开始写入一个Loki服务简略playbook文件来演示它的根本逻辑。

1. 批改 roles/loki/tasks/main.yaml

- name: Loki Operator | Loki | StatefulSet  community.kubernetes.k8s:    definition: "{{ lookup('template', 'statefulset.yaml') | from_yaml }}"

能够看到ansible是通过community.kubernetes的k8s模块来跟kubernetes交互的。下面这条task的意思是让ansible去templates目录上来寻找statefulset.yaml文件,而后将它渲染成yaml文件后提交给kubernetes。

2. 创立 roles/loki/template/statefulset.yaml

statefulset.yaml是真正须要部署的ansible模版文件,咱们须要创立的内容如下:

#jinja2:lstrip_blocks: TrueapiVersion: apps/v1kind: StatefulSetmetadata:  name: '{{ansible_operator_meta.name}}'  namespace: '{{ansible_operator_meta.namespace}}'  labels:     app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system'spec:  replicas: {{ replicas }}  serviceName:  '{{ansible_operator_meta.name}}-headless'  selector:    matchLabels:       app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system'  template:    metadata:      labels:         app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system'    spec:      containers:      - name: loki-system        image: grafana/loki:2.2.1        imagePullPolicy: IfNotPresent        ports:        - containerPort: 3100          name: http-3100          protocol: TCP

下面的Ansible利用模版中有几点信息值得强调:

  • #jinja2:lstrip_blocks: True保留yaml文件中的空格、缩进等语句
  • {{ansible_operator_meta.name}}定义了CR外面管制的Loki资源名称
  • {{ansible_operator_meta.namespace}}定义了CR作用的命名空间
  • {{ replicas }}自定义的CR变量

能够看到咱们申明了一个名replicas的自定义变量,咱们须要通过它来管制服务的正本数。Ansible中的变量取值由用户创立的CR来管制。

Ansible的roles文件中task实际上定义了CR的状态,在Kubernetes在创立资源时,因为容许输出任意字段,所以咱们不须要在CRD中理论定义CR字段类型的申明。尽管在Operator SDK中它不能被主动生成,不过还是倡议在理论应用时最好增加上CRD的字段阐明,以便Kubernetes用户在应用CR时能够看到它对应的形容信息。

3. 创立CR

默认状况下Operator SDK会创立一个默认的CR样例文件,它位于config/samples/目录下。在本文中,咱们的CR文件名称为plugins_v1_loki.yaml

apiVersion: plugins.cloudxiaobai.com/v1kind: Lokimetadata:  name: lokispec:  replicas: 1

构建与部署

当实现利用的playbook编写后,咱们就能够应用make命令进行镜像的构建和上传。咱们能够提前编辑Makefile来自定义镜像的名称,

IMAGE_TAG_BASE ?= cloudxiaobai.com/cloudxiaobaiIMG ?= $(IMAGE_TAG_BASE):$(VERSION)

这样,当咱们每次build镜像时,产生固定的镜像名称。

1. 构建Operator镜像

$ make docker-build docker-push VERSION="0.1"

构建和上传operator镜像。

2. 部署Operator服务

$ make deploy# kustomize build config/default | kubectl apply -f -

能够看到在部署时调用了kustomize将config/default目录下的文件进行构建后提交到kubernetes。这外面须要留神的是,默认状况下,kustomize会创立一个名为<project-name>-system的新命名空间用于部署Operator。

当部署实现后,咱们就能够用kubectl进行验证服务状态。

$ kubectl get deployment -n cloudxiaobai--systemNAME                      READY   UP-TO-DATE   AVAILABLE   AGEloki-controller-manager   1/1     1            1           8m

3. 提交CR

将上文的CR申明文件提交到kubernetes

$ cat config/samples/plugins_v1_loki.yamlapiVersion: plugins.cloudxiaobai.com/v1kind: Lokimetadata:  name: lokispec:  replicas: 1$ kubectl apply -f config/samples/plugins_v1_loki.yaml

之后咱们就能够通过以下命令进行验证服务是否达到预期

$ kubectl get deploymentNAME                                     READY   UP-TO-DATE   AVAILABLE   AGEcloudxiaobai-31e1d3526-a1klm             1/1     1            1           1m

如果咱们要调整Loki的正本数,咱们只需有批改CR中的replicas变量即可,也能够通过patch的形式批改。

$ kubectl patch loki cloudxiaobai  -p '{"spec":{"size": 2    }}' --type=merge

4. 删除资源

间接执行下列命令就能别离删除CR和Operator关联的所有资源

$ kubectl delete -f config/samples/plugins_v1_loki.yaml$ make undeploy

小技巧

1. Ansible变量转化

自定义CR中的spec字段中所有变量的名称均被操作符转换为snake_case(下横线)命名格局。例如,咱们在spec中写入了一个变量serviceAccount,那么在ansible中会被转成service_account。小白倡议能够在watches.yaml中将snakeCaseParameters设置为false来禁用这种大小写转换。

2. 应用默认值

为了能将ansible template适配大部分场景,小白倡议在模版中应用默认值,防止在CR中没有定义变量而造成的playbook执行报错。例如,咱们在CR中定义了Loki的镜像信息如下。

spec:  image:    repository: grafana/loki    tag: 2.1.1

那咱们在template就能够如下编写:

containers:      - name: loki-system        image: '{{service.cluster.loki.image | default('grafana/loki')}}:{{version | default('latest')}}'

这里,如果咱们没在CR中定义image的变量,当playbook执行到这里时,就会采纳grafana/loki:latest镜像。

3. 巧用items

因为community.kubernetes.k8s模块不容许提交一个allinone的yaml文件,所以当在理论编写playbook时,如果想在一个task时外面实现所有yaml提交时,咱们能够应用如下语句:

- name: Loki Operator | Loki | Deploy  community.kubernetes.k8s:    definition: "{{ lookup('template', item) | from_yaml }}"  with_items:    - statefulset.yaml    - service.yaml

4. 管制模块启用或敞开

如果咱们的CR中心愿可能管制某些服务的启用或敞开时,通常状况下会间接应用enabled: true来做明确的定义,例如上面这个CR

spec:  redis:    enabled: true

那么,咱们在写playbook时,能够通过在state中判断CR中的该变量来决定是否执行,如下:

- name: Loki Operator | Reids Cache | PVC  community.kubernetes.k8s:    state: '{{"present" if redis is defined and redis.enabled else "absent"}}'    definition: "{{ lookup('template', 'redis/pvc.yaml') | from_yaml }}

5. Owner References

Owner References是Kubernetes中的垃圾删除机制,它可能在删除CR后进行后续清理。默认状况下,ownerReferences由代理在ansible执行时注入。在当CR和被治理的资源在同一个命名空间下时,ownerReferences会在资源中如下显示:

metadata:  ownerReferences:    - apiVersion: loki.cloudxiaobai.com/v1      kind: loki      name: loki      uid:fda3b711-1f25-4d14-bf84-cf70f48e7ff4

CR和被治理的资源不在一个命名空间下时,ownerReferences便是通过如下的annotation字段来定义的:

operator-sdk/primary-resource: {metadata.namespace}/{metadata.name}operator-sdk/primary-resource-type: {kind}.{group}

当然,如果你要禁用ownerReferences的话,如下批改dockerfile即可:

ENTRYPOINT ["/usr/local/bin/entrypoint", "--inject-owner-ref=false"]

6. 工作并发

默认状况下,ansible调用的runtime.NumCPU()来设置最大并发协调值,能够看到它取决于你主机的CPU外围数。减少并发协调的数量和容许并发处理事件,能够进步ansible执行工作时的协调性能。咱们能够在operator的启动参数来手动设置

- name: manager  args:    - "--max-concurrent-reconciles"    - "3"

总结

本文通过一个小例子介绍了如何通过Operator SDK疾速构建、治理Loki利用。并通过6个小技巧让读者在应用过程中尽量避坑。目前小白在理论工作中,将Ansible Operator次要利用在kubernetes平台初始化时各种插件、三方服务的装置治理,如prometheus、loki、grafna等等,心愿它也能在其它方面帮忙到大家。


更多内容,请关注「云原生小白」