Operator学习笔记
一、什么是OperatorOperator是CoreOS(一种操作系统,用于大量基于云计算的虚拟服务器)推出的,旨在简化复杂有状态应用管理。Operator是一个感知应用状态的控制器,通过扩展k8s API来自动创建、管理和配置应用实例。Operator基于CRD(Custom Resource Definition)扩展资源对象,并通过控制器来保证应用处于预期状态。Operator可用于:通过k
一、什么是Operator
Operator是CoreOS(一种操作系统,用于大量基于云计算的虚拟服务器)推出的,旨在简化复杂有状态应用管理。
Operator是一个感知应用状态的控制器,通过扩展k8s API来自动创建、管理和配置应用实例。Operator基于CRD(Custom Resource Definition)扩展资源对象,并通过控制器来保证应用处于预期状态。
Operator可用于:
- 通过k8s API观察集群的当前状态
- 分析当前状态与期望状态的差别
- 调用k8s API消除这些差别
二、为什么使用CRD
k8s内置的controller可满足大多数的使用场景,但对于很多定制化需求,其表达能力是不够的。因此,k8s支持了CRD(Custom Resource Definition),让用户可以自己定义资源类型,k8s会把用户通过CRD定义的资源视为资源的一种,对其提供像内置资源对象一样的支持。
CRD主要用于提高k8s的扩展能力。
三、Operator设计初衷
k8s管理应用时,应用分无状态和有状态:无状态管理比较简单,有状态管理比较复杂。Operator的的目的就是用于简化复杂的有状态的应用管理,Operator通过CRD扩展k8s API来自动创建、管理、和配置应用实例。其本质是针对特定的场景去做有状态服务。
Operator以deployment的形式部署到k8s中。部署完这个operator后,想要部署一个集群,就会很方便。因为不需要再去管理这个集群的配置信息了,只需要创建一个CRD,指定创建多少个节点,需要什么版本,Operator会监听该资源对象,创建出符合配置要求的集群,从而大大简化运维的难度和成本。
白话理解:Operator就是调用k8s的API来管理集群的,如对集群创建、删除;新增和减少节点。
四、开发Redis Operator
开发不同中间件operator流程基本相同,以redis operator为例说明:
4.1、首先准备
首先要一个资源定义(CRD)yaml,operator代码中会根据该yaml去组装并创建CRD:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: redisclusters.redis.middleware.hc.cn
spec:
group: redis.middleware.hc.cn
version: v1alpha1
scope: Namespaced
names:
kind: RedisCluster
singular: rediscluster
listKind: RedisClusterList
plural: redisclusters
shortNames:
- rec
后面创建的该CRD类型的资源对象(CR),其kind为该yaml描述中spec.names.kind的值。CR相当于CRD的具体实现(不同operator, CRD和CR定义不同)。
- 准备一个CR yaml文件,后面Operator代码要根据该yaml结构在types.go中定义结构体。redis的CR yaml如下。operator最终会监听该CR,解析里面定义的节点数、版本号等参数,驱动做一些事情。
apiVersion: redis.middleware.hc.cn/v1alpha1
kind: RedisCluster
metadata:
name: example000-redis-cluster
namespace: kube-system
spec:
# 代表redis集群的个数
replicas: 7
# 代表是否进入维修状态
pause: true
# 是否删除crd以及redis集群
finalizers: foreground
# 镜像地址
repository: library/redis
# 镜像版本,便于后续多版本特化支持
version: 3.2.8
#redis集群升级策略
updateStrategy:
# 升级类型为AutoReceive(自动分配,不用AssignStrategies), AssignReceive(指定值分配,需要用AssignStrategies)
type: AssignReceive
pipeline: "100"
assignStrategies:
-
slots: 2000
fromReplicas: nodeId1
-
# 从nodeId3,nodeId4一共分配1000个卡槽
slots: 1000
# 多个nodeId用逗号分隔
fromReplicas: nodeId3,nodeId4
# redis 实例配置详情
pod:
# 标签管理:map[string][string]
- labels:
key: value
# 备注管理:map[string][string]
annotations:
key: value
# 环境变量管理
env:
- name: tony
value: aa
- name: MAXMEMORY
value: 2gb
# 亲和性管理
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: HC_Status
operator: In
values:
- C
podAntiAffinity: {}
# 资源管理
resources:
limits:
#cpu, memory, storage,ephemeral-storage
cpu: "2"
memory: 4Gi
requests:
cpu: "1"
memory: 2Gi
#statefulset更新模式
updateStrategy:
type: RollingUpdate
# 支持挂载形式: hostPath(不需要persistentVolumeClaimName),nfs(需要persistentVolumeClaimName)
volumes:
type: nfs
persistentVolumeClaimName: pvcName
# 配置文件模板名
configmap: name
# 监控镜像
monitorImage: string
# 初始化镜像
initImage: string
# 中间件容器镜像
middlewareImage: string
status:
#当前statefulset replicas情况
replicas: 6
# 集群阶段,None,Creating,Running,Failed,Scaling
# None 或 “”, 就是代表该CRD刚创建
# Creating 代表等待redis资源对象创建完毕(operator 发现CRD创建,创建资源对象,更新状态)
# Running 代表已进行初始化操作(在Creating之后,发现实例起来完毕,初始化操作)
# Failed 代表着某异常故障
# ---------------------
# Scaling 代表着实例不一致(用户修改实例,operator发现实例不一致,更新statefulset,更新状态)
# Upgrading 代表着升级中
# ---------------------
phase: Creating
# 异常问题解释
reason: "异常问题"
conditions:
- name: redis-cluster-0
instance: 10.168.78.90:6379
type: master
masterNodeId: allkk111snknkcs
nodeId: allkk111snknkcs
domainName: redis-cluster-0.redis-cluster.kube-system.svc.cluster.local
slots: 1024-2048
hostname: docker-vm-3
hostIP: 192.168.26.122
# true or flase
status: "True"
reason: xxxx
message: xxxx
lastTransitionTime: 2019-03-25T03:10:29Z
4.2、代码生成
生成符合k8s风格的代码
4.3、operator主流程代码开发
首先operator的入口为operator-manager.go里的main函数:
package main
import (
"fmt"
"github.com/spf13/pflag"
"harmonycloud.cn/middleware-operator-manager/cmd/operator-manager/app"
"harmonycloud.cn/middleware-operator-manager/cmd/operator-manager/app/options"
"k8s.io/apiserver/pkg/util/flag"
"k8s.io/apiserver/pkg/util/logs"
"k8s.io/kubernetes/pkg/version/verflag"
"os"
)
func main() {
//参数初始化配置
s := options.NewOMServer()
s.AddFlags(pflag.CommandLine, app.KnownOperators())
flag.InitFlags()
//日志初始化
logs.InitLogs()
defer logs.FlushLogs()
verflag.PrintAndExitIfRequested()
//进行operator初始化
if err := app.Run(s); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
main函数中首先进行对参数的初始化,其中主要包括:operator多实例时的选主配置;事件同步时间;集群创建、升级超时时间;是否启用leader功能;是否开启pprof分析功能等,代码在options.go中。
app.Run(s)根据参数配置进行operator初始化:
- 首先根据参数配置,构建默认客户端(操作k8s已有资源对象)、leader选举客户端、操作扩展资源客户端等。
- 之后创建CRD资源对象定义,后续创建的CR对象都是该CRD的实例;
- 注册健康检查接口,根据启动参数配置决定是否开启pprof分析接口功能;
- 创建recorder,主要用于记录events(k8s资源),用于操作审计;
- 定义Run函数,进行启动operator,选举结果的leader执行该函数;
- 判断是否开启leader选举功能;
- 创建leader选举资源锁,目前资源锁实现了configmaps和endpoints方式,具体代码在client-go下,默认使用endpoints方式。
- 启动Leader选举机制,争抢到锁,选举为leader的实例执行OnStartedLeading,即上面定义的Run函数;失去锁的实例执行OnStoppedLeading函数。
更多推荐
所有评论(0)