Kubernetes [kubə’netis]简称K8S或Kube,平时使用K8S主力是Minikube,在几个项目生产实践中,则使用GCP/AWS/阿里云的对应的K8S实现,GKE/EKS/ACK。以下是作者在项目运维中获得和经反思提炼的知识总结,部分理解带有个人观点。
1、Kubernetes 基础概念¶
理解 Pods¶
Kubernetes 中最小计算单元,内含pause和一个或一组进程。深入pods技术细节见Kubernetes Pod 网络精髓:pause 容器详解。
理解网络¶
Kubernetes有2个网络地址池CIDR分别供Pod和Service。Service和Pod都是有IP的网络实体,会消耗ip池地址,且各IP互通(由CNI实现)。Pod还是个计算实体承载服务,Service则按一定规则转发流量到0个或多个pod。实现模型理解见浅聊 Kubernetes 网络模型
理解Service¶
Service本质就是dns解析+proxy转发,通过Service名:port方式就能访问真正的实体pod,类似非K8S生态的consul,但K8S内部服务名字的dns解析都是高度自动化的,无感知的。
有状态下pod漂移与vm漂移¶
ip在pod生命周期内不变,但pod会因销毁重建导致ip漂移。内存与未绑定文件在pod销毁期间就清空了。相比而言,vm漂移技术成熟,ip内存文件都是一起在线飘到新地方,不存在重启销毁问题。
2、理解Kubernetes技术¶
1. 社区角度:¶
从2015年7月Kubernetes v1.0正式发布至今v1.22版本,基于Kubernetes的生态已经非常庞大,直接导致go的流行,运维云原生化趋势。
2. 工程师角度:¶
绝大部分编排k8s的中间产物都是yaml文件,所以k8s工程师被称为yaml工程师。但其实ansible/salt工程师才是最先全用yaml的。
3. 技术角度:¶
内核技术升级是上层技术革新的基础,当年nginx使用epoll网络IO模型率先实现10K并发,如今docker/Kube实际上是内核cgroup/namespace进程环境限制/隔离加内核netfilter网络模块的上层产物。——技术早已出现,只是换个地方而更被人熟知,这现象可称为【军用科技民用化】。
4. 运维效用角度:¶
自动化套件ansible/salt也是非常成熟的,另一方面非容器或非linux机的自动化集群管理并非k8s强项,k8s仅在支持容器化的项目上高效,而大部分项目可以容器化(0.9*0.9依然接近1,但不是全部)。
5. 基于作者理解:¶
- k8s是“定义即状态”的运维实现,对应软件代码“定义即实现”具体指发展到IDL层面(接口描述语言)。
- k8s内置电池,一个k8s集群相当于实现了一个跑在容器的逻辑机房(计算+网络+存储都是可扩展的),外加一个ansible/salt控制中心。
- k8s本身学习难度大,非初学者友好,需要较深的linux内核/网络基本做底,但k8s生态的文章,精品率更高,反过来能加速学习容器化与linux相关知识的过程。
3、Kube入门学习建议¶
💡 理解上把容器对标物,从虚拟机转变到进程来。¶
- 容器是一个新概念,从字面上并不好理解其真本质:隔离
- 容器更像一个进程,而不是一个轻量的虚拟机。
💡 个人学习k8s路径选型¶
- 从单机上的容器(docker/containerd)出发,跳一步到单机的K8S(minikube),再到多节点K8S(minikube),再适应云商的改版K8S。
- Ingress也有很多种,建议如果熟悉nginx,先从ingress-nginx入手。
- 服务治理Istio囊括范围非常大,k8s只是组件之一,容易找不着北。个人觉得不适合用来入门k8s。
- 官方dashboard已经很好,配合K9S工具已经够用了。一个管UI,一个管console。k9s真是vi党的福音。
- lens也是好工具,管理国内集群无明显延迟感。国外可能会稍卡顿。
💡 容器 ≈ 进程+网络。¶
- 除了理解linux内核,也需要iptables/ipvs理解能力。
- 网络理解更难啃些,建议不要跳过docker网络直接理解k8s网络cni。
💡 使用docker-compose作为对照参考¶
- 对比中理解docker编排/k8s编排各自的长短处。会对k8s理解得更加全面和立体。
- docker-compose也是一股容器编排方面的重要力量。有余力者学1得2。
💡 个人经验最佳学习搭档官方文档书籍 + minikube实践 。¶
- 官方文档要重复看,坚持看,一般看第三遍就懂了。
- minikube是官方首推的学习和测试k8s环境,只需单机docker环境即可启动一个完备的k8s集群,很轻量,最快支持最新k8s版本。
- 推荐第三方书的话 《kubernetes网络权威指南》
4、Kube最佳实践,经验建议¶
💡 打包images兼容非K8S容器环境运行,更利运维部署,以及开发调试。【Projzz, Projxx】¶
- Projzz的镜像兼容docker,并且cp内部某套集群就是用docker部署的,线上则全用K8S部署的。
- Projxx的Pod启动是准备3-4个部分容器,最终合并到最终容器的emptyDir来running,如果选这种模式确实只能k8s了。
做一个通用的镜像,能独立运行。用好动态部分来让同一镜像变成不同的runtime容器,如volume挂载,配置,环境env变量都是动态的。
💡 相似微服务可以统一镜像,使用APP环境变量启动镜像的不同部分【Projyy】¶
相对于各微服务独立打包镜像的分包模式,还有一种简单的全包模式。
Projyy镜像都是java进程,启动脚本命令基本一样,只是目录和jar包不同,使用了轻量的全包模式。通过指定APP环境变量,启动对应微服务。兼容docker/k8s。
全包镜像模式好处:
- 统一的Dockerfile,entrypoint.sh不用维护多个文件。
- 减少镜像总占用空间,版本管理起来更轻量简洁。(harbor界面也简洁了)
- 更接近vm时代supervisor的管理整代码包的方式。更好理解。
💡 用ExternalName型和EndPoints型的服务进行Kube内部服务名固定。【Projxx】¶
用Service映射内部/外部服务简化架构:
组件依赖以服务SVC出现供微服务调用。Projxx依赖为zoo-svr, mysql-svr, redis-svr 与 zoo-svr-out, mysql-svr-out。
SVC底层的EndPoints/ExternalName映射各集群不同。如正式va集群是中心,微服务调用mysql-svr-out/mysql-svr-out底层相同,sg集群则不同。
如下图,屏蔽底层差异后,研发不用关心底层,mysql等服务的映射关系由运维定义和修改,容灾切换。
Projxx图解:
💡 在namespace级别隔离业务环境,而非集群级别隔离。¶
减少k8s集群,理想状态是一个测试集群(dev/qa/pre/time/audit服)+一个正式集群。【Projyy】
为了让单集群支持多环境,需要以下条件:
- yaml/chart中不写死namespace,使用kubectl -n
和 helm -n 在运行时指定namespace。 - 节点映射到pod中的目录或文件名,需要保证不同namespace间不不一样。注意hostpaht和local映射。
- ingress域名不能在多namespace里共用,不同namespace用不同域名。(一般已经天然满足的)
- daemonset组件处理好不同namespace里不同环境的情况。比如filebeat以daemonset部署时可以收集多namespace的日志。
💡 善用hostpath在pod层面实现节点的初始化,日志清理等,不建议ssh到node进行初始化。【Projzz】¶
将node的东西暴露给pod,就可以从pod层面处理好node的事项。以下推荐度逐渐递减
- 以crontab/job加hostpath挂载处理初始化,还能定时清理日志等。
- 以daemonset加hostpath挂载
- 使用initpod模式加hostpath挂载
💡 多用Deployment而不是多用StatefulSet【Projxx】¶
如无必要,Deployment好于StatefulSet,尤其在大副本数滚动更新时StatefulSet慢
- 默认情况,Deployments更新2批次完成,而StatefulSet逐个重启,虽然可优化,但不如Deployment快和便捷。
- 需要hostname/dns固定时间,启动顺序(如mysql主从集群)有要求时才特别的需要StatefulSet。
- Ro的Account登陆校验,Bgame跨服,Global模块,用不到特殊情况,account更新比较慢,理论上改成Deployments会更优。
💡 其他建议:¶
- kubectx/kubens实际上在交互shell时才比较好用图省敲键盘。在脚本或自动化流程中,多使用–kubeconfig和–context参数指定集群更好。
- Fairy使用kustomize跑着也很稳,其求同存异的patch思路,也是很好的,免去helm那样写go模版。
- pod/service/namespace的名字尽量精简,Linux内核的主机名字段限定了FQDN最多 64 个字符。
- ingress尽量合并以减少总量,像gcp的gke就存在多于15个ingress是重载非常慢的问题。
- 珍惜两个cidr地址的使用,太大了网络ip使用量,即pods/svc太多,会导致集群内耗过多。 这类比开发微服务太小散,会导致cpu内耗在(反)序列化中。某字节内部资料说有10%
- 尽量减少总容器量,比如微服务较多时,daemonset的filebeat部署,比inject/sidecar类型的部署模式省容器。
- 提升副本数不如提升配置,副本数20升级60, 在业务代码支持情况下,不如保持20个,配置升3倍。
- 总的来说,本质上就是微服务不是越多越好,目前业界有种不良趋势,服务拆得太小。把服务越拆越细,这事本身也像一个技术界内卷行为。
5、Kube带来的问题反思¶
1. 云厂商各自实现部分,并不统一,造成割裂,在复杂(云)之上制造复杂(Kube)。¶
正例:¶
基于LB的证书解密实现,阿里云/AWS均是annotation注释指向某个cert证书对象的id。运维开发的效用在于屏蔽这些差异。参考【某_ingress_chart】代码。
反例:¶
基于LB的whiteiplist实现,阿里云 / AWS不同。annotation注释指向一块acl对象id / 使用spec中loadBalancerSourceRanges。兼容起来麻烦,也只能运维配置,最终可能还不如在游戏层实现【Projxx】
2. 产生大量不好识别的16进制名称的LB¶
LoadBalance型服务,产生的名字不规律。难识别。
3. ingress 与动静文件分离问题¶
使用nginx ingress也无法做到动静分离,只能将静态文件另外部署,如cdn,ingress后配nginx服等等。
How to setup ingress to serve static content on kubernetes? #323
原文出自Pythonwood发表的https://blog.pythonwood.com/2021/12/K8S运维总结之关于Kubernetes项目最佳实践的思考/