kubernetes优化
ETCD优化
减少磁盘 I/O 延迟
非常重要,建议使用ssd、nvme来做数据存储,立竿见影
减少网络时延
etcd基于raft算法,所以网络是一个十分重要的因素,这可不单单和优化有关,而是影响集群的整体可用性
自动压缩历史版本
这个调整 Etcd 的参数 --auto--compaction 即可,一般默认是开启的,这个有两种压缩模式,一种是周期性的,一种是根据历史记录。
定期消除碎片
Etcd 是不会主动归还空闲的磁盘空间的,这个时候需要调用etcdctl defrag命令来释放空间,另外在 Etcd 中,我们调用 delete 接口删除数据时,并不会从磁盘删数据,只是对对应的版本数据添加一个tombstone 标志,因此我们删除数据并不会释放空间,除非调用 defrag 命令释放磁盘空间。调用 defrag 会影响读写性能,尽量不要调用,或者在业务低峰期调用。
优化运行参数
这个主要是心跳周期(heartbeat,默认100ms)和选举超时时间(election timeout,默认1000ms),尤其是在高迟延场景下,需要增加这两个参数,避免造成频繁选主,造成集群不稳定。
心跳周期一般设置为 Etcd 成员直接平均往返周期的最大值,一般是平均RTT 的 0.55~1.5 倍。选举超时一般设置为心跳周期的10倍。测量 TTL 最简单的方法是使用 ping 工具。
集群etcd拆分
etcd 作为数据存储数据库,是整个集群的基石,能够直接决定性能天花板。默认 ETCD 空间配额大小为 2G,超过 2G 将不再写入数据。通过给 ETCD 配置 --quota-backend-bytes 参数增大空间配额,最大支持 8G。
针对 etcd 集群进行数据水平拆分是一个有效的优化手段,典型的拆分是把 Pod 等重要数据单独 etcd 集群来存储,从而降低单 etcd 存储和请求处理的压力,降低请求处理延迟。
--etcd-servers-overrides strings
etcd 服务器针对每个资源的重载设置,以逗号分隔。 单个替代格式:组/资源#服务器(group/resource#servers),其中服务器是 URL,以分号分隔。
注意,此选项仅适用于编译进此服务器二进制文件的资源。
event 事件(最主要的)
K8s event 资源数据并不是 watch 中的 event,一般是表示关联对象发生的事件,比如 Pod 拉取镜像,容器启动等。在业务上一般是 CI/CD 需要流水式展示状态时间轴,需要频繁拉取 event 资源数据。
event 资源数据本身就是有效期的(默认是 2 小时),除了通过 event 观测资源对象生命周期变化外,一般没有重要的业务依赖,所以说 event 数据一般认为是可以丢弃,不需要保障数据前后一致性的。
因为上述的数据特点,event 的拆分是最为简单的,只需要修改 APIServer 的启动配置,重启 APIServer 即可,不需要做数据迁移,也不需要做老旧数据的清理。整个拆分过程除了 Kube-apiserver 外,不需要任何组件的重启或者修改配置。
Lease 租约
Lease 资源一般用于 Kubelet 心跳上报,另外也是社区推荐的 controller 类组件选主的资源类型。
每个 Kubelet 都使用一个 Lease 对象进行心跳上报,默认是每 10s 上报一次。节点越多,etcd 承担的 update 请求越多,节点 Lease 的每分钟更新次数是节点总量的 6 倍,1 万个节点就是每分钟 6 万次,还是非常可观的。Lease 资源的更新对于判断 Node 是否 Ready 非常重要,所以单独拆分出来。
controller 类组件的选主逻辑基本上都是使用的开源的选主代码包,即使用 Lease 选主的组件都是统一的选主逻辑。Kubelet 的上报心跳的代码逻辑更是在我们掌控之中。从代码中分析可知 Lease 资源并不需要严格的数据一致性,只需要在一定时间内保障 Lease 数据被更新过,就不影响使用 Lease 的组件正常功能。
Kubelet 判断 Ready 的逻辑是否在 controller-manager 中的时间默认设置是 40s,即只要对应 Lease 资源在 40s 内被更新过,就不会被判断为 NotReady。而且 40s 这个时间可以调长,只要在这个时间更新就不影响正常功能。使用选主的 controller 类组件的选主 Lease duration 一般为 5s~65s 可以自行设置。
因此 Lease 资源拆分虽和 event 相比要复杂一些,但也是比较简单的。多出来的步骤就是在拆分的过程中,需要把老 etcd 中的 Lease 资源数据同步到新的 etcd 集群中,一般我们使用 etcdctl make-mirror 工具同步数据。此时若有组件更新 Lease 对象,请求可能会落在老 etcd,也可能落在新的 etcd 中。落在老 etcd 中的更新会通过 make-mirror 工具同步到新的 etcd 中,因为 Lease 对象较少,整个过程持续时间很短,也不会存在问题。另外还需要迁移拆分完成后,删除老 etcd 中的 Lease 资源数据,以便释放锁占用的空间,虽然空间很小,但也不要浪费。类似 event 资源拆分,整个拆分过程除了 kube-apiserver 外,同样不需要任何组件的重启或者修改配置。
Pod 资源
Pod 资源可能是我们最熟悉的资源数据了,所有的 workload 最终都是由 Pod 来真实承载。K8s 集群的管理核心就在于 Pod 资源的调度和管理。Pod 资源数据要求严格的数据一致性,Pod 的任何更新产生的 watch event 事件,都不能错过,否则就有可能影响 Pod 资源交付。Pod 资源的特点也正是导致传统 Pod 资源数据拆分过程中需要大规模重启相关组件的原因,后文会解析其中的原因。
新etcd如何部署
使用静态pod拉起etcd集群,我们只要将原来的yaml文件拷贝一份,修改后拉起即可
# 修改etcd配置
- name: ETCD_NAME
value: event-node20.gy.ntes # 添加event-
- name: ETCD_LISTEN_PEER_URLS # 所有端口+10000
value: https://0.0.0.0:12380
- name: ETCD_LISTEN_CLIENT_URLS
value: https://0.0.0.0:12379
- name: ETCD_LISTEN_METRICS_URLS
value: http://127.0.0.1:2381
- name: ETCD_INITIAL_ADVERTISE_PEER_URLS
value: https://10.XX.0.2:12380
- name: ETCD_ADVERTISE_CLIENT_URLS
value: https://10.XX.0.2:12379
- name: ETCD_INITIAL_CLUSTER
value: event-node20.gy.ntes=https://10.XX.0.2:12380,event-node21.gy.ntes=https://10.XX.0.3:12380,event-node22.gy.ntes=https://10.XX.0.4:12380
- name: ENDPOINTS
value: https://10.XX.0.2:12379,https://10.XX.0.3:12379,https://10.XX.0.4:12379
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /health
port: 12381
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: etcd
ports:
- containerPort: 12380
name: peer
- containerPort: 12379
name: client
volumes:
- hostPath:
path: /data-ssd/etcd/ # 新目录
type: DirectoryOrCreate
name: etcd-data
- hostPath:
path: /etc/kubernetes/pki/etcd/peer
type: Directory
name: cert-peer
- hostPath:
path: /etc/kubernetes/pki/etcd/client
type: Directory
name: cert-client
- hostPath:
path: /etc/kubernetes/pki/etcd/server
type: Directory
name: cert-server
- hostPath:
path: /data/log/etcd/event
type: DirectoryOrCreate
name: etcd-log
检查集群状态
$ ETCDCTL_API=3 etcdctl --endpoints https://127.0.0.1:12379 --cacert /etc/kubernetes/pki/etcd/client/ca.crt --cert /etc/kubernetes/pki/etcd/client/tls.crt --key /etc/kubernetes/pki/etcd/client/tls.key \
endpoint status --cluster -w table
如何配置
events 拆分配置
--etcd-servers-overrides=/events#https://etcd1.events.org:2379;https://etcd2.events.org:2379;https://etcd3.events.org:2379
$ ETCDCTL_API=3 etcdctl get "" --prefix --keys-only |grep /registry/events/
/registry/events/default/admin-cluster-admin.17f906f706c0a356
/registry/events/default/calico-kube-controllers.17f906f702065e9c
……
leases 拆分配置
--etcd-servers-overrides=/leases#https://etcd1.leases.org:2379;https://etcd2.leases.org:2379;https://etcd3.leases.org:2379
$ ETCDCTL_API=3 etcdctl get "" --prefix --keys-only |grep leases
/registry/apiextensions.k8s.io/customresourcedefinitions/helmreleases.application.kubesphere.io
/registry/leases/ingress-nginx/ingress-nginx-leader
/registry/leases/kube-node-lease/betagz-kvm-kubesphere-master1.gy.ntes
……
/registry/masterleases/10.57.1.95
pods 拆分配置
--etcd-servers-overrides=/pods#https://etcd1.pods.org:2379;https://etcd2.pods.org:2379;https://etcd3.pods.org:2379
$ ETCDCTL_API=3 etcdctl get "" --prefix --keys-only |grep /registry/pods/
/registry/pods/argocd/devops-argocd-application-controller-0
/registry/pods/argocd/devops-argocd-applicationset-controller-7d9db7b9bc-8dlgb
/registry/pods/argocd/devops-argocd-dex-server-9c8cdc69b-cnh5c
……
内核参数优化
# max-file 表示系统级别的能够打开的文件句柄的数量, 一般如果遇到文件句柄达到上限时,会碰到
# "Too many open files" 或者 Socket/File: Can’t open so many files 等错误
fs.file-max=1000000
# 配置 arp cache 大小
# 存在于 ARP 高速缓存中的最少层数,如果少于这个数,垃圾收集器将不会运行。缺省值是 128
net.ipv4.neigh.default.gc_thresh1=1024
# 保存在 ARP 高速缓存中的最多的记录软限制。垃圾收集器在开始收集前,允许记录数超过这个数字 5 秒。缺省值是 512
net.ipv4.neigh.default.gc_thresh2=4096
# 保存在 ARP 高速缓存中的最多记录的硬限制,一旦高速缓存中的数目高于此,垃圾收集器将马上运行。缺省值是 1024
net.ipv4.neigh.default.gc_thresh3=8192
# 以上三个参数,当内核维护的 arp 表过于庞大时候,可以考虑优化
# 允许的最大跟踪连接条目,是在内核内存中 netfilter 可以同时处理的“任务”(连接跟踪条目)
net.netfilter.nf_conntrack_max=10485760
net.netfilter.nf_conntrack_tcp_timeout_established=300
# 哈希表大小(只读)(64位系统、8G内存默认 65536,16G翻倍,如此类推)
net.netfilter.nf_conntrack_buckets=655360
# 每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog=10000
# 默认值: 128 指定了每一个 real user ID 可创建的 inotify instatnces 的数量上限
fs.inotify.max_user_instances=524288
# 默认值: 8192 指定了每个inotify instance相关联的watches的上限
fs.inotify.max_user_watches=524288
Kube-apiserver 配置
- 设置
--max-requests-inflight=3000 - 设置
--max-mutating-requests-inflight=1000
Kube-scheduler 配置
- 设置
--kube-api-qps=100
Kube-controller-manager 配置
- 设置
--kube-api-qps=100 - 设置
--kube-api-burst=100
Kubelet 配置
- 设置
--image-pull-progress-deadline=30m - 设置
--serialize-image-pulls=false(需要 Docker 使用 overlay2 ) - Kubelet 单节点允许运行的最大 Pod 数:
--max-pods=110(默认是 110,可以根据实际需要设置)
Docker 配置
- 设置
max-concurrent-downloads=10 - 使用 SSD 存储
graph=/ssd-storage-path - 预加载 pause 镜像,比如
docker image save -o /opt/preloaded_docker_images.tar和docker image load -i /opt/preloaded_docker_images.tar - 值得一题的参数,优雅重启(docker服务重启不影响容器运行):
"live-restore": true,