1. 什么是文档工具化
文档工具化,工具产品化,是我之前博文中反复提过的一个口号。
好的文档,不如好用的工具。一个脚本、一条命令,比阅读文档更加直接,更能快速解决问题。同时,有很多文档会让读者对知识产生眩晕,在急于解决问题的窗口期无法补全知识体系的情况下,很容易出现错误的理解。
这种有领域壁垒的知识,需要融入一点设计对其进行转换,才能让用户更好的使用。这就是文档工具化的意义。
从文档到工具,从工具到产品,雕琢地痕迹会越来越明显,这是一个很有趣的过程,不断地去掉冗余,去掉不必要的东西,让用户更加专注于解决问题。
2. Ops 工具
有了上述想法之后,我一直希望能够将平时工作中的一些操作,通过工具的方式进行封装,让这些操作能够得到更好的复用。
半年多前,我新建了一个项目,叫做 ops,主要用来辅助我完成工作中的一些操作。
如上图,是这个项目的一个构想。设计理念在于,运维工具的核心在于文本分发和脚本执行,实现了这两种能力就能够满足运维的功能需求。
目前,我面向的运维对象是 Host 主机、Kubernetes 集群,很少直接面向容器。因此在 OpsObject 层实现了 Host 和 Cluster 对象,分别对应主机和 Kubernetes 集群。
而在此之上,分别实现了面向主机的文件分发、脚本执行,以及面向 Kubernetes 集群的文件分发、脚本执行,也就是 Core 核心能力层。
得益于 Core 层的能力,我已经可以实现一些简单的运维功能,比如:批量添加 hosts,批量安装、变更 Prometheus 等。
但这还不够,很多运维操作不是单步能够完成,因此需要一些流程控制,比如: 备份集群之前,需要先安装 Velero,安装 Velero 之前,需要先安装 Helm 等。因此引入了一个新的对象 Task,用于完成编排,这样其实就和 Ansible 很像了。
在 Tools 层,我提供了三个入口,:opscli、opsserver、opscontroller。目前主要完成了 opscli 和 opscontroller 两个部分。
3. opscli
opscli 是一个静态的二进制文件,支持 Linux 和 macOS 系统,可以通过 curl 命令直接下载使用。
3.1 安装
如果网络连接 GitHub 很好,可以使用下面的命令安装:
1
| curl -sfL https://raw.githubusercontent.com/shaowenchen/ops/main/getcli.sh | VERSION=latest sh -
|
如果网络连接 GitHub 不好,可以使用下面的命令安装:
1
| curl -sfL https://cf.ghproxy.cc/https://raw.githubusercontent.com/shaowenchen/ops/main/getcli.sh |VERSION=latest sh -
|
3.2 配置自动补全
1
| echo 'source <(opscli completion bash)' >>~/.bashrc
|
1
| echo 'source <(opscli completion zsh)' >>~/.zshrc
|
3.3 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| opscli --help
Usage:
opscli [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
create command about Ops Resource
file transfer between local and remote file
help Help about any command
shell run shell on hosts
task command about task
upgrade upgrade to latest version
version get current version
|
1
2
3
| opscli shell -i 1.1.1.1 --port 2222 --username root --content "uname -a"
Linux node1 5.4.219-1.el7.elrepo.x86_64 #1 SMP Sun Oct 16 10:03:45 EDT 2022 x86_64 x86_64 x86_64 GNU/Linux
|
这里的 -i
可以指向一个 ip,也可以指向一个文件,文件中包含多个 ip,每行一个 ip。--content
指向要执行的命令,也可以是脚本文件。
1
| opscli shell -i ~/.kube/config --content "docker pull shaowenchen/ops-cli:latest" --sudo --all
|
这里的 -i
指向一个 kubeconfig 文件,--all
表示对集群中的所有节点都执行命令,如果不加 --all
,则只会在 master 节点执行命令,使用 --nodename
可以指定指向命令的节点。
另外有一个特殊的参数 --incluster
表示这条命令将会在集群的某一个 Pod 运行,这在集群内部检测问题时非常有用。
文件的分发,是一个映射的过程,本地、远程两个目标 + 数据流方向。
1
| opscli file --remotefile /etc/hosts --localfile ./file1 --direction download -i 1.1.1.1 --port 2222 --username root
|
这里的 --direction
表示数据流方向,download
表示从远程下载到本地,upload
表示从本地上传到远程。
1
| opscli file --remotefile /etc/hosts --localfile ./file1 --direction download -i ~/.kube/config --nodename node1
|
这里的 --nodename
表示目标节点。
实际上文件分发,还支持源文件来自镜像、S3,这里就不一一列举了。
1
2
3
4
5
6
7
8
9
| ls ~/.ops/tasks/
add-file2image.yaml alert-http-status.yaml app-openebs.yaml get-kubeconfig.yaml pull-file.yaml upgrade-1.16m.yaml upgrade-kernel3to5.yaml
add-imagepullsecret.yaml alert-promql.yaml app-prometheus.yaml get-osstaus.yaml push-file.yaml upgrade-1.16n.yaml velero-backup.yaml
add-localbinpath.yaml app-descheduler.yaml clear-docker.yaml k8s-drain-node.yaml renew-kube-cert.yaml upgrade-1.17m.yaml velero-install.yaml
add-opscli.yaml app-grafana.yaml clear-kube.yaml k8s-hpa.yaml set-docker-liverestore.yaml upgrade-1.17n.yaml velero-restore.yaml
add-reqlimit.yaml app-istio.yaml clear-opstask.yaml list-podimage.yaml set-hosts.yaml upgrade-1.19m.yaml velero-status.yaml
add-sshkey.yaml app-longhorn.yaml get-diskio-byfio.yaml list-reqlimit.yaml set-hubimage.yaml upgrade-1.19n.yaml velero-uninstall.yaml
add-superuser.yaml app-metricsserver.yaml get-imagefile.yaml list-svc.yaml set-kernel.yaml upgrade-base.yaml
|
在 ~/.ops/tasks/
目录下,有很多的 task yaml 文件,可以直接使用。
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
| opscli task -f ~/.ops/tasks/get-osstaus.yaml -i 1.1.1.1 --port 2222 --username root
> Run Task / on 1.1.1.1
(1/11) Kernel Version
5.4.219-1.el7.elrepo.x86_64
(2/11) CPU Usage Percent/Load/Total
10.11%/0.00/4
(3/11) Mem Usage Percent/Total
33.02%/7.8G
(4/11) Disk Usage Percent/Total
69%/52G /dev/mapper/centos-root
35%/1.1G /dev/sda1
4%/26G /dev/mapper/centos-home
(5/11) NF_Conntrack Usage/Total
1344/131072
(6/11) PID Usage
1.57%/65535
(7/11) ARP Router
0.02%/80000
(8/11) Open Files Number
1.29%/1048576
(9/11) User Instances
0.23%/8192
(10/11) User Watches
0.00%/524288
(11/11) User Processes
360/31711
|
如果是面向集群,只需要将 -i
指向一个 kubeconfig 文件,然后加上 --all
或者指定 --nodename
即可。
4 opsserver
这部分还在开发中,主要是为了提供一个 HTTP API,以供其他系统、脚本调用。
5 opscontroller
5.1 安装
1
| curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
|
1
| helm repo add ops https://www.chenshaowen.com/ops/charts
|
1
| helm install myops ops/ops --version 1.0.0 --namespace ops-system --create-namespace
|
1
| kubectl get pods -n ops-system
|
opscontroller 默认只会处理 ops-system 命名空间下的 CRD 资源对象。
如果需要变更,可以修改 Env 中 ACTIVE_NAMESPACE
的值,指定某一个命令空间,如果为空,则表示处理所有命名空间。
5.2 使用
1
| opscli create host --name dev1 -i 1.1.1.1 --port 2222 --namespace ops-system
|
1
2
3
4
5
6
7
8
| kubectl -n ops-system get hosts
NAME HOSTNAME ADDRESS DISTRIBUTION ARCH CPU MEM DISK HEARTTIME HEARTSTATUS
dev1 node1 1.1.1.1 centos x86_64 4 7.8G 52G 59s successed
dev2 node2 1.1.1.1 centos x86_64 4 7.8G 52G 60s successed
dev3 node3 1.1.1.1 centos x86_64 4 7.8G 52G 0s successed
dev4 node4 1.1.1.1 centos x86_64 4 7.8G 52G 2s successed
dev5 node5 1.1.1.1 centos x86_64 1 1.8G 95G 0s successed
|
controller 会定时检测主机的状态,并将主机的状态更新到 host 对象中。
1
| opscli create cluster --name dev1 -i ~/.kube/config --namespace ops-system
|
1
2
3
4
5
6
7
8
| kubectl -n ops-system get cluster
NAME SERVER VERSION NODE RUNNING TOTALPOD CERTDAYS STATUS
dev1 https://1.1.1.1:6443 v1.21.0 1 14 14 193 successed
dev2 https://1.1.1.1:6443 v1.23.0 3 22 22 350 successed
intl https://1.1.1.1:6443 v1.21.4 1 18 95 268 successed
prod https://1.1.1.1:6443 v1.21.4 10 134 1098 183 successed
test https://1.1.1.1:6443 v1.21.4 1 23 23 227 successed
|
controller 会定时检测集群的状态,并将集群的状态更新到 cluster 对象中。
- task 任务也可以通过
opscli
创建,但我更希望的是用 yaml 直接创建
controller 主要是定时执行 task 对象中的任务。比如清理集群,提供告警等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| kubectl -n ops-system get task
NAME CRONTAB TYPEREF NAMEREF NODENAME ALL STARTTIME RUNSTATUS
alert-http-status-dockermirror */1 * * * *
alert-http-status-harbor */1 * * * *
alert-http-status-git */1 * * * *
alert-http-status-mirror-go */1 * * * *
alert-http-status-mirror-maven */1 * * * *
alert-http-status-mirror-npm */1 * * * *
alert-http-status-mirror-pypi */1 * * * *
alert-promql-node-cpu */1 * * * *
alert-promql-node-disk */1 * * * *
alert-promql-node-io */1 * * * *
alert-promql-node-mem */1 * * * *
alert-promql-pending */1 * * * *
alert-promql-pending-pod */1 * * * *
alert-promql-pvc */1 * * * *
alert-trigger-autotest */5 * * * *
clear-docker 15 * * * * cluster prod true
clear-opstask-dev1 10 * * * * cluster dev1
clear-opstask-dev2 10 * * * * cluster dev2
clear-opstask-prod 10 * * * * cluster prod
clear-opstask-test 10 * * * * cluster test
|
通过 TYPEREF
指向对象类型,NAMEREF
指向对象名称。上面是我生产环境的配置,下面是发出的告警通知。
alert 告警如上图,主要支持两种方式,一种是通过 promql 查询,另一种是通过 http 请求。
定时任务 clear-docker 会清理集群每个节点上的构建残留物,clear-opstask 会清理因执行集群 Ops 错误时产生的 Pod。
6. 总结
这个项目主要是为了方便我自己运维主机和管理集群,同时提供了周期任务能力,以及告警能力。目前还在开发中,后续会继续完善。
在工作中去抽取项目,不是一件容易的事,需要找到项目和工作之间的契合点。很多工作中的项目和业务紧密耦合,无法抽取;而如果脱离工作,没有实际的业务场景打磨,项目也很难有价值。
起初 opscli 有很多的子命令用于满足各种场景,但是后来我又通过 task 对象来重新实现。这样做的好处是,task 对象可以作为公司敏感数据单独存放和配置,而功能实现部分可以放开限制,不用担心敏感信息泄露。
当然,Ansible 也实现了类似的功能,程序员的快乐之一也是不断地重复造轮子。在造轮子的过程中,我也重新学习了一遍 CRD 开发,也学习了 Helm Chart 的开发,并将其发布到了 https://artifacthub.io/。
7. 参考