有了这篇核心概念讲解,30分钟也能“玩转” K8s?新手必看!
懂 Pod 就懂了 Kubernetes 的一半
# nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
然后用 kubectl 将 nginx 的 Pod 进行创建,命令后面加
-v8
是详细日志模式,可以看出来 kubectl 到底做了什么事情。kubectl create pod -f nginx.yaml -v8
kubectl get pod -v8
在 OpenLens 或 K9S 等可视化工具中,我们可以看到一个叫 Nginx 的 Pod 就被”生“出来了,从 kubectl 的详细日志中也可以看到 POST/GET 等请求的信息。
I1127 14:55:06.886901 83798 round_trippers.go:463] GET http://127.0.0.1:60649/77046cfbc5f80b52d9a1501954ee0672/api/v1/namespaces/default/pods?limit=500
I1127 14:55:06.886916 83798 round_trippers.go:469] Request Headers:
I1127 14:55:06.886921 83798 round_trippers.go:473] User-Agent: kubectl...
I1127 14:55:07.166333 83798 round_trippers.go:580] Cache-Control: no-cache, private
可以看到,在创建完成 Pod 之后,实际的 Pod 比我们在 Yaml 中声明的字段更多,这些多出来的字段就是 Pod 从出生后的经历证明:由调度器调度到集群可用节点、交给 kubelet 管理 Pod 生命周期、分配网络IP、挂载临时存储、容器运行时拉取镜像启动容器、控制器协调校正运行状态等等。 apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
status:
phase: Running
hostIP: 10.....
podIP: 10.....
conditions:
- type: Initialized
status: 'True'
lastProbeTime: null
lastTransitionTime: '2023-11-27T06:59:13Z'
- type: Ready
status: 'True'
lastProbeTime: null
.....
spec:
volumes:
- name: kube-api-access-72rkq
......
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
resources: {}
volumeMounts:
- name: kube-api-access-72rkq
readOnly: true
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
serviceAccountName: default
serviceAccount: default
securityContext: {}
schedulerName: default-scheduler
tolerations:
- key: node.kubernetes.io/not-ready
operator: Exists
effect: NoExecute
tolerationSeconds: 300
- key: node.kubernetes.io/unreachable
operator: Exists
effect: NoExecute
tolerationSeconds: 300
priority: 0
enableServiceLinks: true
preemptionPolicy: PreemptLowerPriority
展开来看,运行一个容器,必要的就是3大件:计算、网络、存储。
计算资源,就是 CPU/Mem/GPU,是在 spec 的 container 部分声明,这个案例中没有设置到底需要多少 resources,requests 和 resources.limits 为空,也就是说可能占满整个宿主机,这种情况一般叫 Best-Effort QoS 级别,调度优先级是比较低的。 实际情况下一般会设置合理的 requests/limits,达到 Burstable QoS 级别或者设置 requests、limits 一模一样达到 Guarantee 级别。这个 Pod 经过调度器调度到某个节点之后,就会交给一个叫 CRI(Container Runtime Interface)的接口,让 CRI 的实现来把容器真正建出来,通常是 containerd,cri-o,podman, docker 等等。 网络方面,可以看到在 status 里面,多出了 PodIP 字段,这个是调用底层一个叫 CNI(Container Network Interface)的接口,让 CNI 的实现层给出的IP,这个过程比较复杂,涉及到一个叫 pause 容器的东西,入门的时候可以忽略这些细节。 存储方面,可以看到自动挂载了一个 volume/volumeMounts,这是对 Pod 挂载的额外存储,可能是配置文件或密钥,也可能是挂载一些云厂商提供的持久化存储,比如 EBS、EFS 盘,则会涉及到 K8S 第三类底层接口,CSI(Container Storage Interface,我们用的云厂商的 Kubernetes 发行版本一般都已经内置了CSI的实现。 有了计算网络存储,Pod 就运行起来了,如果我们要更新 Pod,可以用 Update, Patch 接口,但是,Pod是一个 Kubernetes 的原子资源,只能更新极少数字段,比如 image 和 readinessGate。 如果想结束掉这个 Pod,可以用 Delete 接口,来让 Pod 进入 Terminating 状态,最终被控制器删除,回收掉计算资源,容器镜像文件最终也会被 GC 掉。 这里只是讲了最浅显的流程,详细的 Pod 生命周期可以参考这里:https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/,尤其是一些和业务强相关的生命周期活动,比如 postStart 并行的启动 hook,preStop 串行的停止 hook,发送 SIGTERM 尝试结束进程,过了 Graceful Period 后发送 SIGKILL 信号等等,对业务很有用。 Kubernetes 集群视角的计算、网络、存储
至此,我们明白了计算网络存储资源,如何赋予到 Pod 这个载体上。那么,计算、网络、存储的资源池本身,在 Kubernetes 里叫什么? Kubernetes 集群的计算节点叫 Node,和传统云平台对硬件机器的定义不同,Node 也是抽象的资源,可以长出来,也可以死掉,不和运行哪个容器直接绑定,而是通过 label/selector,affinity 等调度相关的机制关联到 Pod。 Kubernetes 集群的块存储资源叫 PersistentVolume,实际使用场景下,一般是用分布式文件系统来实现,根据业务的磁盘请求,自动创建 PersistentVolume 的东西叫 StorageClass。 Kubernetes 的网络是分好几层的:让整个集群变成一个大内网的 Pod 网络;让集群内服务互相访问自带 L4 负载均衡的 Service 网络,以及做精细流量治理的 L7 Ingress/GatewayAPI/ServiceMesh 网络。还有控制网络访问策略的 NetworkPolicy 资源。 首先我们看 Pod 网络,虽然不同 CNI 实现的网络原理差别巨大,但目的都是一样的,给每个 Pod 分配IP,并打通和其他 Pod 之间的路由。比如 AWS 就用了一个很讨巧的方式实现,直接给 Pod 分配当前 VPC-Subnet 的二级IP,DHCP和路由表都是复用的,Pod 之间和现有 EC2 节点之间的通信方式完全一样。 再来看作为内部L4负载均衡器的 Service 网络,给每个 K8S services 资源分配一个虚拟 IP(ClusterIP)。ClusterIP 分配后,kube-proxy 组件负责来实现这个虚拟IP的路由的创建和 Pod Endpoint 变化的实时校正。
还是以 AWS EKS 为例,EKS 默认使用的 iptables 模式,kube-proxy 会在每个节点上把每个 ClusterIP Service 的 IP 写入 iptables,用 iptables 命令可以看到实现细节。
-
由于每次变更导致的 iptables 修改,大规模集群用 K8S 内置的 Service 负载均衡是存在性能问题的,切换到 ipvs 模式可以解决;
-
还有一种没有 ClusterIP 的 Headless Service,借助 DNS 实现了端点自动发现,不是常规的 L4 负载均衡;
-
如果需要直接把某个 Service 暴露到公网去,还有 NodePort/LoadBalancer 类型的 Service,kube-proxy 会在集群每个节点 listen NodePort 端口,iptables写入 NodePort 对应的 DNAT 规则; -
LoadBalancer / NodePort类型的Service还有一个关键字段“externalTrafficPolicy”,简单理解是跨节点负载均衡模式还是本地节点直连模式,跨节点负载均衡还会引起外部 LB 的健康检查失效问题以及内部服务无法获取 Client IP 问题,这些都是平台方需要处理好的,不需要业务方关心,业务团队记住一个原则,永远不要用 NodePort Service 就行。
https://zhuanlan.zhihu.com/p/196393839
iptables -L -n
Chain OUTPUT (policy ACCEPT)
source destination target prot opt
KUBE-PROXY-FIREWALL all -- anywhere anywhere ctstate NEW /* kubernetes load balancer firewall */
KUBE-SERVICES all -- anywhere anywhere ctstate NEW /* kubernetes service portals */
KUBE-FIREWALL all -- anywhere anywhere
iptables -L -t nat
Chain KUBE-SVC-TCOU7JCQXEZGVUNU (1 references)
source destination target prot opt
KUBE-SEP-HI2KQBDGYW5OVKWN all -- anywhere anywhere /* kube-system/kube-dns:dns -> 10.52.xx.xx:53 */ statistic mode random probability 0.50000000000
KUBE-SEP-XQT5TF2PMBOMEGDC all -- anywhere anywhere /* kube-system/kube-dns:dns -> 10.52.xx.xx:53 */
最后了解一下L7服务网络,一般由类似 Nginx Ingress/Envoy 之类的应用流量负载均衡器提供,对于业务来说,就当把 nginx conf 拆成一个一个 yaml 片段就好。Ingress 直管南北向流量,而 Service Mesh 把东西南边向流量全托管了,也能实现一些比 Nginx conf 里面更复杂的行为,比如流量加密、鉴权、故障注入、熔断降级、重试等等。 其他原生资源要么是对 Pod 套娃、要么是打辅助的
Kubernetes 的 API 设计非常符合单一职责原则(SRP),Pod 就是一个包容器的单纯的豆荚,delete 了就没了。 但是,你要跑服务怎么办?没关系,单一职责原则下,实现新功能就是一个套娃,Kubernetes 抽象了一个叫 Deployment 套住一个叫 ReplicaSet的东西,ReplicaSet 再来套住 Pod。 这样,Deployment 只管变更处理轮换 RS,RS 只管保证有 n 个 pod 在跑,Pod 没了再生,死了重启,这里也体现了 Erlang 典型的 let it crash 思维。 哪天你说要训练一个AI模型干掉 OpenAI,Kubernetes 给你提供了一个叫 CronJob和 Job 的抽象,CronJob 套住了 Job,Job 又套住了 Pod。Job 只管一次性 run 的东西,要重试几次,跑完多久删 Pod 这些事,Cronjob则是一个天然的分布式cron,只管定时把 job 这个东西生出来。CronJob 和 Job 广泛用在大数据处理管线、CICD管线,AI训练这些领域。OpenAI 也是一个包含 8000 多个节点的巨大Kubernetes 集群训练出来的。 哪天你又想用 Kubernetes 运行一个数据库,恭喜头铁的你,学到了最复杂的一种原生资源,StatefulSet,deployment是把豆荚当牲畜,想杀就杀,StatefulSet 是把Pod 当宠物,宠物不好养的,每个 Pod 都不能随便动,更新的时候只能按序一个一个更新。 除了这些对 Pod 套娃的资源,剩下的可以理解成打辅助的,比如把流量引入集群的Ingress,内部流量负载均衡的 Service,给每个Pod提供的分布式配置ConfigMap、分布式密钥存储 Secret。还有一些策略控制和资源限额的辅助类,这里不一一展开了。 重新思考 Kubernetes 是什么
到这里,我们大概搞清楚了 Kubernetes 对于使用者来说意味着什么。从Kubernetes 自身的组件视图来看包括这些东西:
-
每个机器装一个叫 kubelet 的 Agent,控制这台机器运行什么
-
每个机器装一个 kube proxy 的东西用来托管网络防火墙规则,并装一个 CNI 的实现,控制集群内部的 Pod IP 分配和网络路由 -
可选的,装一个CSI的实现,接管持久化存储盘的创建和挂载
-
这些东西都听 API Server + Controller Manager + Scheduler 组成的控制中心,这套控制中心暴露一套标准的可扩展的 REST API,数据全部存到了 ETCD 元数据集群里。
Kubernetes 包含了分布式集群的一切,Kubernetes 又一无所有。
Kubernetes 的 A/B 面
Kubernetes 带来的最大的几个好处,分别是标准化、弹性、可扩展。
REST API 带来的管理界面完全标准化,
存个 ETCD 记录就创建或校正资源状态带来了极致弹性,扩缩容就在弹指之间,
复杂,是成长的代价。
来源:https://code2life.top/2023/11/24/0074-k8s-in-30-min/
多项首批评估结果揭晓!2023年12月15日,中国信通院 DevOps、AIOps 系列标准最新评估结果重磅发布!
相关评估详情如下:3个项目同时过级!中国银河证券通过 DevOps 持续交付标准 3 级评估,相关能力达到国内领先水平
近期好文:
运维必须 7*24 在线?还不能请假?周六日都要加班,尊嘟假嘟?
点个“在看”,一年不宕机
发表评论