使用 client-go 模仿 kube-controller-manager 实现一个简单的控制器。
需求背景
之前开发了一些关于 kubernetes 集群运维相关的工具有以下几个问题:
因此,针对以上问题,我们需要将集群相关工具的所有功能集成到一起,并且优化相关功能代码。
原理与实现
离不开 informer 哪一套东西。
1.原理示意图
关于功能集成问题:
-
控制器实现模块化管理,如果以后有其他需求,可以直接在原有的基础上新增控制器而不会干扰到其他控制器代码。
-
CRUD 相关操作是从本地缓存获取的 api-server 数据,减轻资源同步对 api-server 造成的额外负担。
-
多线程操作,运行速度快。
-
新增告警功能和控制器日志按等级显示(平时不显示详细日志),方便第一时间知道控制器处理了什么,和问题排查。
2.关键启动流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// 存放所有控制器启动函数
func NewControllerInitializers() map[string]InitFunc {
controllers := map[string]InitFunc{}
controllers["SendReport"] = startSendReportController
controllers["AutoCordon"] = startAutoCordonController
controllers["DelException"] = startDelExceptionController
return controllers
}
// 所有控制器启动入口函数
func StartControllers(ctx ControllerContext, controllers map[string]InitFunc) error {
for controllerName, initFn := range controllers {
if !ctx.IsControllerEnabled(controllerName) {
klog.Warningf("%q is disabled", controllerName)
continue
}
// time.Sleep(wait.Jitter(ctx.ComponentConfig.Generic.ControllerStartInterval.Duration, ControllerStartJitter))
klog.V(1).Infof("Starting %q", controllerName)
_, started, err := initFn(ctx)
if err != nil {
klog.Errorf("Error starting %q", controllerName)
return err
}
if !started {
klog.Warningf("Skipping %q", controllerName)
continue
}
klog.Infof("Started %q", controllerName)
}
return nil
}
|
- 所有控制器对象都抽象为 InitFunc 接口对象,以后新增控制器只要实现 InitFunc 接口即可,无需改到其他代码,就可以作为控制器加入到原有的架构里面。
3.主要功能
-
生成重启次数大于指定阈值的报告并发送到飞书。
-
清理异常状态 pods ,目前只清理一直卡在 terminating 和被驱逐后残留的 pod ,后续觉得好用的话,可以把状态字段提取出来,放到 comfigmap ,以实现清理我们指定状态的 pod 。
-
自动给节点打污点。
-
待有需求再添加其他功能。
方案实施步骤
部署到集群。
1.配置授权
1
2
3
4
5
6
7
8
9
|
// 使用 KubeConfig 文件创建集群配置 Config 对象
if config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig); err != nil {
klog.Errorln(err.Error())
if config, err = rest.InClusterConfig(); err != nil {
// 首先使用 inCluster 模式(需要去配置对应的 RBAC 权限,默认的sa是default->是没有获取deployments的List权限)
panic(err.Error())
}
}
|
ServiceAccount
创建 ServiceAccount
1
2
3
4
5
6
7
|
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: ops-resource-controller
name: ops-resource-controller
namespace: kube-system
|
将这个 ServiceAccount 跟 ClusterRole 进行绑定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ops-resource-controller
labels:
k8s-app: ops-resource-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: ops-resource-controller
namespace: kube-system
|
在 Pod 中使用 ServiceAccount 。
serviceAccountName: ops-resource-controller
配置文件
在 deployment 配置文件中,配置挂载 master 宿主机目录(此方式适合 master 节点在自己手上,否则按这个思路需要将 config 配置文件放在 configmap 或 secret 挂载进控制器容器。)
1
2
3
4
5
6
7
|
...
volumes:
- hostPath:
path: /root/.kube
type: DirectoryOrCreate
name: kubeconfig
...
|
容器挂载该路径,并使其能正确读取到 config 配置文件。
1
2
3
4
5
6
7
8
9
10
11
12
|
...
volumeMounts:
- mountPath: /root/.kube
name: kubeconfig
readOnly: true
nodeSelector:
node-role.kubernetes.io/master: ""
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
...
|
2.configmap
完成前置工作以后,我们还得配置供控制器运行的配置文件,具体含义看注释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
apiVersion: v1
data:
config.yml: |
# 周期性发送重启次数过多的服务
sendReport:
total: 100
# 秒分时日月周
schedule: "00 00 10 * * 1"
# 告警信息title
msgtitle: "pod重启次数过多"
apiurl: "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
enable: true
# 自动给节点打污点
autoCordon:
enable: true
interval: 1m
maxPods: 100
minPods: 90
# 删除一直卡在 terminating 状态和 Evicted 状态的 pod
delException:
enable: true
# 执行周期
interval: 2m
msgtitle: "删除异常状态 Pod"
apiurl: "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
# 控制器的全局配置
controller:
# 单位是秒,不清楚什么意思不要改
controllerResyncPeriod: 10
# 单位是秒,不清楚什么意思不要改
InformersResyncPeriod: 30
# leader 选举,高可用的同时防止多个 pod 同时启动,不然定时类任务会被多次执行
LeaderElect: true
leaseLockName: ops-resource-controller
leaseLockNamespace: kube-system
kind: ConfigMap
metadata:
name: ops-resource-controller-cm
namespace: kube-system
|
运行时间间隔,控制器是否启用,按需设置。
3.deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: ops-resource-controller
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: ops-resource-controller
template:
metadata:
labels:
app: ops-resource-controller
spec:
containers:
- name: ops-resource-controller
image: yourrepositryurl/ops-resource-controller:v1.1
env:
- name: TZ
value: Asia/Shanghai
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /root/.kube
name: kubeconfig
readOnly: true
- mountPath: /opt/config.yml
name: config
subPath: config.yml
nodeSelector:
node-role.kubernetes.io/master: ""
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
volumes:
- hostPath:
path: /root/.kube
type: DirectoryOrCreate
name: kubeconfig
- name: config
configMap:
name: ops-resource-controller-cm
items:
- key: config.yml
path: config.yml
|
应用到k8s集群
kubectl apply -f ops-resource-controller.yaml
观察效果