Kubernetes使用指南
Kubernetes
Kubernetes是一个开源的容器编排引擎,用来对容器化应用进行自动化部署、扩缩容和管理。对应用开发者而言,可以把Kubernetes看成一个集群操作系统。Kubernetes提供服务发现、伸缩、负载均衡、自愈甚至选举等功能,让开发者从基础设施相关配置等解脱出来。
Kubernetes 在希腊语意为舵手、驾驶员,由Google 开源,在2014年首次对外宣布 。该系统的开发和设计都深受谷歌的 Borg系统的影响,其许多顶级贡献者之前也是Borg系统的开发者。在谷歌内部,Kubernetes的原始代号曾经是Seven,即星际迷航中的Borg(博格人)。Kubernetes标识中舵轮有七个轮辐就是对该项目代号的致意。
Kubernetes v1.0于2015年7月21日发布。随着v1.0版本发布,谷歌与Linux 基金会合作组建了Cloud Native Computing Foundation(CNCF)并将Kubernetes作为种子技术来提供。Kubernetes 又简称k8s,k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。
实际应用
当下使用Kubernetes加CI/CD(持续集成/持续交付)已经是非常普遍的组合。
字节跳动技术团队使用 Kubernetes 构建云原生架构,管理十万个微服务。(2021年12月)
阿里技术团队使用 Kubernetes 管理几万个微服务容器。(2021年12月):《阿里巴巴超大规模Kubernetes基础设施运维体系揭秘》
云服务
各大运营厂商均推出自家的Kubernates托管服务:
- 阿里云 ACK
- 腾讯云 (TKE,Tencent Kubernetes Engine)
- 华为云 Cloud Container Engine,云容器引擎(简称CCE)
发版历史
版本 | 时间 |
---|---|
v1.0 | 2015-07 |
v1.5 | 2016-12 |
v1.9 | 2017-12 |
v1.13 | 2018-12 |
v1.17 | 2019-12 |
v1.20 | 2020-12 |
v1.23 | 2021-12 |
v1.27 | 2023-04 |
v1.32 | 2025-04 |
与传统SpringCloud方案对比
Kubernates 中提供的基础设施层面的解决方案与传统Spring Cloud 中提供的应用层面的解决方案对比:
场景 | Kubernates | Spring Cloud |
---|---|---|
弹性伸缩 | 支持自动调整 | 不支持 |
服务发现 | KubeDNS / CoreDNS | Spring Cloud Eureka |
配置中心 | ConfigMap / Secret | Spring Cloud Config |
服务网关 | Ingress Controller | Spring Cloud Zuul |
负载均衡 | Load Balancer | Spring Cloud Ribbon |
服务安全 | RBAC API | Spring Cloud Security |
跟踪监控 | Metrics API / Dashboard | Spring Cloud Turbine |
降级熔断 | 不支持 | Spring Cloud Hystrix |
Kubernetes 是后微服务时代开始的标识,但 Kubernetes 仍不能完美解决全部的分布式问题——不完美的意思是,仅从功能上看,单纯的 Kubernetes 反而不如之前的 Spring Cloud 方案。这是因为有一些问题处于应用系统与基础设施的边缘,使得完全在基础设施层面中确实很难精细化地处理。举个例子,微服务 A 调用了微服务 B 的两个服务,称为 B1和 B2,假设 B1表现正常但 B2出现了持续的 500 错,那在达到一定阈值之后就应该对 B2进行熔断,以避免产生雪崩效应。如果仅在基础设施层面来处理,这会遇到一个两难问题,切断 A 到 B 的网络通路则会影响到 B1的正常调用,不切断的话则持续受 B2的错误影响。
架构设计
K8s 的架构设计遵循分层和模块化原则,采用主从架构设计。
- 控制平面 control plane
- 工作节点 Node
- 插件 Add-ons

控制平面 Control Plane
控制平面是集群的大脑 ,负责全局决策和状态管理。
四个核心组件
控制平面包含四个核心组件:
- API Server
- Controller Manager 控制器管理器
- Scheduler 调度器
- Etcd 存储
API Server
集群的 API 入口,接收用户指令。
controller-manager 控制器管理器
- 运行一系列控制器 Controller,确保集群实际状态与期望状态一致。
- 常见控制器:节点控制器(Node Controller)、副本控制器(Replication Controller)、端点控制器(Endpoint Controller)等。
Scheduler 调度器
将新创建的 Pod 分配到最合适的节点(基于资源需求、亲和性策略等)。
Etcd
- 分布式键值存储 ,保存集群的所有状态数据 (如节点、Pod、配置、密钥等)。
- 采用 Raft 协议保证数据一致性,通常以集群形式部署实现高可用。Etcd集群可以和Master节点部署在同一个宿主机,也可以单独部署,生产环境建议部署大于3的奇数台Etcd节点实现Etcd集群的高可用。
控制平面和主节点的关系
Kubernetes 社区逐渐弱化“主节点”这一术语,更强调“控制平面”作为逻辑管理单元,避免用户误以为集群依赖单点(主节点)。例如,云服务商(如 GKE、EKS)的托管 Kubernetes 服务通常隐藏主节点的细节,仅暴露控制平面的逻辑概念。
两者的关系
- 单主节点场景 :
主节点直接等同于控制平面,因为所有控制平面组件运行在同一个节点上。此时“主节点就是控制平面”完全正确。 - 高可用集群场景 :
控制平面由多个主节点共同组成 ,每个主节点运行部分控制平面组件,并通过 etcd 集群共享状态。此时控制平面是一个逻辑概念 ,而主节点是物理节点。
常见误区 :
- 主节点 ≠ 单节点 :在生产环境中,控制平面通常跨多个主节点部署,实现高可用。
- 控制平面 ≠ 单个组件 :控制平面是多个组件的集合,而非单一实体。
工作节点 Node
作用 :运行在每个节点上,负责维护容器生命周期和网络代理。
三个核心组件
- kubelet (节点的代理程序):
- 管理 Pod 生命周期(创建、销毁容器)。
- 监控容器健康状态。
- 挂载存储卷。
- 与 API Server通信,上报节点状态。
- kube-proxy
- 维护节点上的网络规则 ,实现 Service 的负载均衡和流量转发。
- 支持 iptables/IPVS 模式,将请求路由到后端 Pod。
- 容器运行时(Container Runtime)
- 负责运行容器,如 Docker 、containerd 、CRI-O 等。
- 遵循 Kubernetes 的 CRI(Container Runtime Interface) 标准。
插件
作用 :扩展集群功能,提供 DNS、网络、监控等核心能力。
常见组件 :
组件 | 描述 |
---|---|
CoreDNS | 集群内部的 DNS 服务器,为 Service 和 Pod 提供域名解析。 |
Ingress Controller | 实现 HTTP/HTTPS 路由规则(如 Nginx Ingress Controller) |
CNI 网络插件 | 管理 Pod 网络(如 Calico、Flannel、Cilium)。 |
Dashboard | Kubernetes 的 Web 管理界面 |
监控组件 | 如 Prometheus、Metrics Server(提供资源使用指标)。 |
…… |
基础概念
Kubernetes对象
在 Kubernetes 系统中,Kubernetes 对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。 具体而言,它们描述了如下信息:
哪些容器化应用正在运行(以及在哪些节点上运行)
可以被应用使用的资源
关于应用运行时行为的策略,比如重启策略、升级策略以及容错策略
Kubernetes 对象是一种 “意向表达”。一旦创建该对象, Kubernetes 系统将不断工作以确保该对象存在。通过创建对象,你本质上是在告知 Kubernetes 系统你想要的集群工作负载状态看起来应是什么样子的, 这就是 Kubernetes 集群所谓的期望状态(Desired State)。
对象规约(Spec)与对象状态(Status)
几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置: 对象规约(Spec)与对象状态(Status)。
- 对于具有
spec
的对象,你必须在创建对象时设置其内容,描述你希望对象所具有的特征: 期望状态(Desired State)。 status
描述了对象的当前状态(Current State),它是由 Kubernetes 系统和组件设置并更新的。
在任何时刻,Kubernetes 控制平面都一直在积极地管理着对象的实际状态,以使之达成期望状态。
必需字段
在想要创建的 Kubernetes 对象所对应的 .yaml
文件中,需要配置的字段如下:
apiVersion
- 创建该对象所使用的 Kubernetes API 的版本kind
- 想要创建的对象的类别metadata
- 帮助唯一标识对象的一些数据,包括一个name
字符串、UID
和可选的namespace
spec
- 你所期望的该对象的状态
常用基本对象
下面将介绍Kubernetes中基本对象及它们之间的一些关系

Pod
Pod是Kubernetes创建或部署的最小单位。一个Pod封装一个或多个容器(container)、存储资源(volume)、一个独立的网络IP以及管理控制容器运行方式的策略选项。
Deployment
Deployment是对Pod的服务化封装。一个Deployment可以包含一个或多个Pod,每个Pod的角色相同,所以系统会自动为Deployment的多个Pod分发请求。
StatefulSet
StatefulSet是用来管理有状态应用的对象。和Deployment相同的是,StatefulSet管理了基于相同容器定义的一组Pod。但和Deployment不同的是,StatefulSet为它们的每个Pod维护了一个固定的ID。这些Pod是基于相同的声明来创建的,但是不能相互替换,无论怎么调度,每个Pod都有一个永久不变的ID。
Job
Job是用来控制批处理型任务的对象。批处理业务与长期伺服业务(Deployment)的主要区别是批处理业务的运行有头有尾,而长期伺服业务在用户不停止的情况下永远运行。Job管理的Pod根据用户的设置把任务成功完成就自动退出(Pod自动删除)。
CronJob
CronJob是基于时间控制的Job,类似于Linux系统的crontab,在指定的时间周期运行指定的任务。
DaemonSet
DaemonSet是这样一种对象(守护进程),它在集群的每个节点上运行一个Pod,且保证只有一个Pod,这非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是Kubernetes的kube-proxy。
Service
Service是用来解决Pod访问问题的。Service有一个固定IP地址,Service将访问流量转发给Pod,而且Service可以给这些Pod做负载均衡。
Ingress
Service是基于四层TCP和UDP协议转发的,Ingress可以基于七层的HTTP和HTTPS协议转发,可以通过域名和路径做到更细粒度的划分。
ConfigMap
ConfigMap是一种用于存储应用所需配置信息的资源类型,用于保存配置数据的键值对。通过ConfigMap可以方便的做到配置解耦,使得不同环境有不同的配置。
Secret
Secret是一种加密存储的资源对象,您可以将认证信息、证书、私钥等保存在Secret中,而不需要把这些敏感数据暴露到镜像或者Pod定义中,从而更加安全和灵活。
PersistentVolume(PV)
PV指持久化数据存储卷,主要定义的是一个持久化存储在宿主机上的目录,比如一个NFS的挂载目录。
PersistentVolumeClaim(PVC)
Kubernetes提供PVC专门用于持久化存储的申请,PVC可以让您无需关心底层存储资源如何创建、释放等动作,而只需要申明您需要何种类型的存储资源、多大的存储空间。
对象的声明式描述
kubernetes中资源可以使用 YAML 或 JSON描述。其内容可以分为如下四个部分:
- typeMeta:对象类型的元信息,声明对象使用哪个API版本,哪个类型的对象。
- objectMeta:对象的元信息,包括对象名称、使用的标签等。
- spec:对象的期望状态,例如对象使用什么镜像、有多少副本等。
- status:对象的实际状态,只能在对象创建后看到,创建对象时无需指定。

下面是 nginx-deployment.yaml 对象描述文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
使用 kubectl 创建对象:(后面会更详细的讲解kubectl)
# 使用 kubectl 创建对象
> kubectl create -f nginx-deployment.yaml
#命令执行后,Kubernetes集群中会创建3个Pod,使用如下命令可以查询到Deployment和Pod:
> kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 9s
> kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-685898579b-qrt4d 1/1 Running 0 15s
nginx-685898579b-t9zd2 1/1 Running 0 15s
nginx-685898579b-w59jn 1/1 Running 0 15s
Kubernetes API
操作 Kubernetes 对象 —— 无论是创建、修改或者删除 —— 需要使用 Kubernetes API。 比如,当使用 kubectl 命令行接口时,kubectl 会调用 Kubernetes API; 你也可以在程序中使用客户端库来直接调用 Kubernetes API。
API Server 提供 HTTP API,以供用户、集群中的不同部分和集群外部组件相互通信。
kubelet
Kubernetes 提供 kubectl 是使用 Kubernetes API 与 Kubernetes 集群的控制面进行通信的命令行工具。这个工具叫做 kubectl。
kubectl
的配置信息在 $HOME/.kube
目录中一个名为 config
的配置文件中。
常用命令汇总
- kubectl get - 列出资源
- kubectl describe - 显示有关资源的详细信息
- kubectl logs - 打印 Pod 中容器的日志
- kubectl exec - 在 Pod 中的容器上执行命令
# 查看帮助命令
kubectl -h
# 查看集群信息
kubectl cluster-info
# 查看某节点详情信息
$ kubectl describe node 192.168.0.212
# 给节点打标签
$ kubectl label node 192.168.0.212 gpu=true
# 查看指定标签的节点列表
$ kubectl get node -L gpu
# 查询节点的标签
$ kubectl get node --show-labels
账号相关:
# 查看ServiceAccount 列表
$ kubectl get sa #或 kubectl get serviceaccounts
# 查看某个 ServiceAccount详情
$ kubectl describe sa default
# 创建ServiceAccount
$ kubectl create serviceaccount sa-example
标签相关:
kubectl label pods foo app=xxx # 增加app=xxx 标签,已有不会覆盖
kubectl label pods foo app=xxx --overwrite # 增加app=xxx 标签,已有则覆盖
kubectl get pods -l app=xxx # 查询资源列表时,带标签过滤
节点相关:
# 查看集群所有节点
kubectl get nodes
# 查看某节点的详情信息
kubectl describe nodes k8s-node01
# 标记 my-node 为 unschedulable,禁止pod被调度过来。注意这时现有的pod还会继续运行,不会被驱逐。
kubectl cordon my-node
# 与cordon相反,标记 my-node 为 允许调度。
kubectl uncordon my-node
# drain字面意思为排水,实际就是把my-node的pod平滑切换到其他node,同时标记pod为unschedulable,也就是包含了cordon命令。
kubectl drain my-node
Deployment 相关:
# 查看 deployment列表
$ kubectl get deploy
# 查看某个 deployment详情
$ kubectl describe deploy nginx
# 升级
$ kubectl edit deploy nginx
# 回滚
$ kubectl rollout undo deployment nginx
# 查看ReplicaSet列表
$ kubectl get rs #或 kubectl get replicasets
Pod相关:
# 查看 Pod列表(默认命名空间下)
$ kubectl get pods
# 查看 Pod列表(所有命名空间下)
$ kubectl get pods --all-namespaces
# 查看 Pod列表(指定命名空间下)
$ kubectl get pods -n kube-public
# 查看 Pod列表,可以显示更多资源信息,比如pod的IP、Node等
kubectl get pods -o wide
# 查看 Pod列表,根据指定标签进行过滤
kubectl get pods -l app=xxx
# 查看某个 Pod详情
$ kubectl describe pods nginx-7fd9798f6c-k7bt6
查看资源对象定义
kubectl explain 命令可以输出资源对应的属性字段及定义,在定义资源配置文件时候非常有用。
$ kubectl explain deployment.spec.selector
声明式资源对象管理
对集群资源的声明式管理,是Kubernetes最主要的特性之一,而kubectl apply命令是最能体现这个特性的命令。
kubectl apply -f FILENAME
-f 参数后跟 yaml或 json 格式的资源配置文件 。
为什么说apply是声明式管理呢,因为所有对集群的增改操作,都能用apply命令完成,一切取决于后面的配置文件:
- 如果配置文件中的资源找集群中不存在,则创建这个资源。
- 如果配置文件中的资源在集群中已存在,则根据配置对资源字段进行更新
同一个 kubectl apply -f deployment-goweb.yaml
命令,可以用来创建资源也可以更新资源。简单来说,apply命令的作用就是一个:使集群的实际状态朝用户声明的期望状态变化,而用户不用关心具体要进行怎样的增删改操作才能呢达到这个期望状态,也即Kubernetes的声明式资源管理。
命令式资源对象管理
命令式管理类就是直接通过命令执行增删改的操作,除了删除资源外,下面的命令能用apply代替,kubernetes也建议尽量使用apply命令。
创建资源
kubectl create deployment my-dep --image=busybox # 创建一个deplpyme
kubectl expose rc nginx --port=80 --target-port=8000 # 创建一个svc,暴露 nginx 这个rc
更新资源
kubectl label pods foo app=xxx # 增加status=unhealthy 标签,已有不会覆盖
kubectl label pods foo app=xxx --overwrite # 增加status=unhealthy 标签,已有则覆盖
kubectl scale --replicas=3 -f foo.yaml # 将foo.yaml中描述的对象扩展为3个
kubectl annotate pods foo description='my frontend' # 增加description='my frontend'备注,已有保留不覆盖
删除资源
kubectl delete -f xxx.yaml # 删除一个配置文件对应的资源对象
kubectl delete pod,service baz foo # 删除名字为baz或foo的pod和service
kubectl delete pods,services -l name=myLabel # -l 参数可以删除包含指定label的资源对象
kubectl delete pod foo --grace-period=0 --force # 强制删除一个pod,在各种原因pod一直terminate不掉的时候很有用
查看资源状态
kubectl get services # 列出当前NS中所有service资源
kubectl get pods --all-namespaces # 列出集群所有NS中所有的Pod
kubectl get pods -o wide # -o wide 比较常用,可以显示更多资源信息,比如pod的IP等
kubectl get deployment my-dep # 可以直接指定资源名查看
kubectl get deployment my-dep --watch # --watch 参数可以监控资源的状态,在状态变换时输出。在跟踪服务部署情况时很有用
kubectl get pod my-pod -o yaml # 查看yaml格式的资源配置,这里包括资实际的status,可以用--export排除
kubectl get pod my-pod -l app=nginx # 查看所有带有标签app: nginx的pod
日志查看
kubectl logs my-pod # 输出一个单容器pod my-pod的日志到标准输出
kubectl logs nginx-78f5d695bd-czm8z -c nginx # 输出多容器pod中的某个nginx容器的日志
kubectl logs -l app=nginx # 输出所有包含app=nginx标签的pod日志
kubectl logs -f my-pod # 加上-f参数跟踪日志,类似tail -f
kubectl logs my-pod -p # 输出该pod的上一个退出的容器实例日志。在pod容器异常退出时很有用
kubectl logs my-pod --since-time=2018-11-01T15:00:00Z # 指定时间戳输出日志
kubectl logs my-pod --since=1h # 指定时间段输出日志,单位s/m/h
查看命名空间
[root@k8s-master01 ~]# kubectl get namespaces
NAME STATUS AGE
default Active 45h
kube-node-lease Active 45h
kube-public Active 45h
kube-system Active 45h
集群信息查看
kubectl version # 查看Kubernetes集群和客户端版本
kubectl cluster-info # 查看master和集群服务的地址
kubectl cluster-info dump # 查看集群详细日志
重启Pod 方法
如果有最新的 yaml 文件
kubectl replace --force -f xxxx.yaml
没有 yaml 文件,但是使用的是 Deployment 对象。
kubectl scale deployment esb-admin --replicas=0 -n {namespace}
kubectl scale deployment esb-admin --replicas=1 -n {namespace}
由于 Deployment 对象并不是直接操控的 Pod 对象,而是操控的 ReplicaSet 对象,而 ReplicaSet 对象就是由副本的数目的定义和Pod 模板组成的。所以这条命令分别是将ReplicaSet 的数量 scale 到 0,然后又 scale 到 1,那么 Pod 也就重启了。
节点管理
# 标记 my-node 为 unschedulable,禁止pod被调度过来。注意这时现有的pod还会继续运行,不会被驱逐。
kubectl cordon my-node
# 与cordon相反,标记 my-node 为 允许调度。
kubectl uncordon my-node
# drain字面意思为排水,实际就是把my-node的pod平滑切换到其他node,同时标记pod为unschedulable,也就是包含了cordon命令。
kubectl drain my-node
文件传输
在排错和测试服务的时候,时不时需要和容器互相交互文件,比如传输容器内存的dump到宿主机,或从宿主机临时拷贝个新配置文件做调试,这时就可以用*kubectl cp命令。要注意的是,cp命令需要容器里已安装有tar程序
kubectl cp /tmp/foo_dir <some-pod>:/tmp/bar_dir # 拷贝宿主机本地文件夹到pod
kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar # 指定namespace的拷贝pod文件到宿主机本地目录
kubectl cp /tmp/foo <some-pod>:/tmp/bar -c <specific-container> # 对于多容器pod,用-c指定容器名
执行命令
命令作用和参数基本与docker exec一致
kubectl exec my-pod ls # 对my-pod执行ls命令
kubectl exec -it nginx-78f5d695bd-czm8z bash # 进入pod的shell,并打开伪终端和标准输入
Rancher UI
部署后,你可以通过 Rancher 的 UI 完成大部分 Kubernetes 操作,而无需频繁使用 kubectl
命令行。
调度基础
Deployment 无状态服务
Pod是Kubernetes创建或部署的最小单位,但是Pod是被设计为相对短暂的一次性实体,Pod可以被驱逐(当节点资源不足时)、随着集群的节点崩溃而消失。Kubernetes提供了Controller(控制器)来管理Pod,Controller可以创建和管理多个Pod,提供副本管理、滚动升级和自愈能力,其中最为常用的就是Deployment。
Deployment一般用于部署公司的无状态服务,这也是最常用的控制器,因为企业内部现在都是以微服务为主,而微服务实现无状态化也是最佳实践。
创建Deployment
以下示例为创建一个名为nginx的Deployment负载,使用nginx:latest镜像创建两个Pod,每个Pod占用100m core CPU、200Mi内存。
apiVersion: apps/v1 # 注意这里与Pod的区别,Deployment是apps/v1而不是v1
kind: Deployment # 资源类型为Deployment
metadata:
name: nginx # Deployment的名称
spec:
replicas: 2 # Pod的数量,Deployment会确保一直有2个Pod运行
selector: # Label Selector
matchLabels:
app: nginx
template: # Pod的定义,用于创建Pod,也称为Pod template
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
imagePullSecrets:
- name: default-secret
从这个定义中可以看到Deployment的名称为nginx,spec.replicas
定义了Pod的数量,即这个Deployment控制2个Pod;spec.selector
是Label Selector(标签选择器),表示这个Deployment会选择标签为app: nginx
的Pod;spec.template
是Pod的定义,内容与Pod中的定义完全一致。
将上面Deployment的定义保存到 deployment.yaml 文件中,使用kubectl 创建这个Deployment。
使用 kubectl get查看Deployment和Pod,可以看到READY值为2/2,前一个2表示当前有2个Pod运行,后一个2表示期望有2个Pod,AVAILABLE 为2表示有2个Pod是可用的。
$ kubectl create -f deployment.yaml
deployment.apps/nginx created
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 4m5s
Deployment如何控制Pod
继续查询Pod,如下所示
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-7f98958cdf-tdmqk 1/1 Running 0 13s
nginx-7f98958cdf-txckx 1/1 Running 0 13s
如果删掉一个Pod,您会发现立马会有一个新的Pod被创建出来,如下所示,这就是前面所说的Deployment会确保有2个Pod在运行,如果删掉一个,Deployment会重新创建一个,如果某个Pod故障或有其他问题,Deployment会自动拉起这个Pod。
看到有如下两个名为 nginx-7f98958cdf-tdmqk和 nginx-7f98958cdf-tesqr的Pod, 其中nginx是直接使用Deployment的名称,-7f98958cdf-tdmqk和-7f98958cdf-tesqr是kubernetes随机生成的后缀。
您也许会发现这两个后缀中前面一部分是相同的,都是7f98958cdf,这是因为Deployment不是直接控制Pod的,Deployment是通过一种名为 ReplicaSet的控制器控制Pod,通过如下命令可以查询ReplicaSet,其中rs是ReplicaSet的缩写。
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-7f98958cdf 2 2 2 1m
这个ReplicaSet的名称为nginx-7f98958cdf,后缀-7f98958cdf也是随机生成的。Deployment控制Pod的方式如下图所示,Deployment控制ReplicaSet,ReplicaSet控制Pod。

如果使用kubectl describe
命令查看Deployment的详情,您就可以看到ReplicaSet,如下所示,可以看到有一行NewReplicaSet: nginx-7f98958cdf (2/2 replicas created),而且Events里面事件确是把ReplicaSet的实例扩容到2个。在实际使用中您也许不会直接操作ReplicaSet,但了解Deployment通过控制ReplicaSet来控制Pod会有助于您定位问题。
$ kubectl describe deploy nginx
Name: nginx
Namespace: default
CreationTimestamp: Sun, 16 Dec 2018 19:21:58 +0800
Labels: app=nginx
...
NewReplicaSet: nginx-7f98958cdf (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m deployment-controller Scaled up replica set nginx-7f98958cdf to 2
升级
在实际应用中,升级是一个常见的场景,Deployment能够很方便的支撑应用升级。
Deployment可以设置不同的升级策略,有如下两种。
- RollingUpdate:滚动升级,即逐步创建新Pod再删除旧Pod,为默认策略。
- Recreate:替换升级,即先把当前Pod删掉再重新创建Pod。
Deployment的升级可以是声明式的,也就是说只需要修改Deployment的YAML定义即可,比如使用kubectl edit命令将上面Deployment中的镜像修改为nginx:alpine。修改完成后再查询ReplicaSet和Pod,发现创建了一个新的ReplicaSet,Pod也重新创建了。
$ kubectl edit deploy nginx
Deployment可以通过maxSurge和maxUnavailable两个参数控制升级过程中同时重新创建Pod的比例,这在很多时候是非常有用,配置如下所示。
spec:
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
- maxSurge:与Deployment中spec.replicas相比,可以有多少个Pod存在,默认值是25%,比如spec.replicas为 4,那升级过程中就不能超过5个Pod存在,即按1个的步伐升级,实际升级过程中会换算成数字,且换算会向上取整。这个值也可以直接设置成数字。
- maxUnavailable:与Deployment中spec.replicas相比,可以有多少个Pod失效,也就是删除的比例,默认值是25%,比如spec.replicas为4,那升级过程中就至少有3个Pod存在,即删除Pod的步伐是1。同样这个值也可以设置成数字。
在前面的例子中,由于spec.replicas是2,如果maxSurge 和 maxUnavailable都为默认值25%,那实际升级过程中,maxSurge允许最多3个Pod存在(向上取整,2*1.25=2.5
,取整为3),而maxUnavailable则不允许有Pod Unavailable(向上取整,2*0.75=1.5
,取整为2),也就是说在升级过程中,一直会有2个Pod处于运行状态,每次新建一个Pod,等这个Pod创建成功后再删掉一个旧Pod,直至Pod全部为新Pod。
回滚
回滚也称为回退,即当发现升级出现问题时,让应用回到老的版本。Deployment可以非常方便的回滚到老版本。
例如上面升级的新版镜像有问题,可以执行kubectl rollout undo命令进行回滚。
$ kubectl rollout undo deployment nginx
deployment.apps/nginx rolled back
Deployment之所以能如此容易的做到回滚,是因为Deployment是通过ReplicaSet控制Pod的,升级后之前ReplicaSet都一直存在,Deployment回滚做的就是使用之前的ReplicaSet再次把Pod创建出来。Deployment中保存ReplicaSet的数量可以使用revisionHistoryLimit
参数限制,默认值为10。
StatefulSet 有状态服务
Deployment控制器下的Pod都有个共同特点,那就是每个Pod除了名称和IP地址不同,其余完全相同。需要的时候,Deployment可以通过Pod模板创建新的Pod;不需要的时候,Deployment就可以删除任意一个Pod。
但是在某些场景下,这并不满足需求,比如有些分布式的场景,要求每个Pod都有自己单独的状态时,比如分布式数据库,每个Pod要求有单独的存储,这时Deployment就不能满足需求了。
在 Kubernetes 生产环境中,StatefulSet 主要用于管理 有状态应用 ,这类应用需要以下特性:
- 稳定的唯一网络标识 (每个 Pod 有固定名称和 DNS 域名)。
- 持久化存储 (数据需长期保存,即使 Pod 重建也不丢失)。
- 有序部署/扩展/更新 (如主从节点需按顺序启动)。
实际应用场景:
- 关系型数据库(如 MySQL、PostgreSQL)
- 缓存服务(如 Redis、Memcached)
- 消息队列(如 Kafka、RabbitMQ)
- 全文搜索数据库 如 Elasticsearch
- 对象存储数据库 如 MinIO
示例
示例1:部署 MySQL 主从集群
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpass"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
示例2:Redis 主从集群
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2
ports:
- containerPort: 6379
volumeMounts:
- name: redis-data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
示例3:部署Elasticsearch 集群
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:8.9.0
env:
- name: discovery.seed_hosts
value: "elasticsearch-0.elasticsearch.default.svc.cluster.local,elasticsearch-1.elasticsearch.default.svc.cluster.local"
ports:
- containerPort: 9200
- containerPort: 9300
volumeMounts:
- name: es-data
mountPath: /usr/share/elasticsearch/data
volumeClaimTemplates:
- metadata:
name: es-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 500Gi
注意事项
- 存储类(StorageClass) :需提前配置支持动态 PV 的 StorageClass。
- 网络策略 :有状态服务可能需要限制网络访问(如仅允许特定 Pod 连接数据库)。
- 备份与恢复 :定期备份 PV 数据(如通过 Velero)。
在 Web 应用的生产环境中,StatefulSet 主要用于部署数据库、缓存、消息队列等有状态组件 ,确保数据持久性、服务高可用性和有序管理。对于无状态的 Web 服务器(如 Nginx、Spring Boot 应用),通常使用 Deployment 而非 StatefulSet。
Job 一次性任务
CronJob 周期任务
DeamonSet 守护进程集
DaemonSet是这样一种对象(守护进程),它在集群的每个节点上运行一个Pod,且保证只有一个Pod,这非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是Kubernetes的kube-proxy。
DaemonSet跟节点相关,如果节点异常,也不会在其他节点重新创建。
下面是一个DaemonSet的示例。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-daemonset
labels:
app: nginx-daemonset
spec:
selector:
matchLabels:
app: nginx-daemonset
template:
metadata:
labels:
app: nginx-daemonset
spec:
nodeSelector: # 节点选择,当节点拥有daemon=need时才在节点上创建Pod
daemon: need
containers:
- name: nginx-daemonset
image: nginx:alpine
resources:
limits:
cpu: 250m
memory: 512Mi
requests:
cpu: 250m
memory: 512Mi
imagePullSecrets:
- name: default-secret
这里可以看出没有Deployment或StatefulSet中的replicas参数,因为是每个节点固定一个。
Pod模板中有个nodeSelector,指定了只在有“daemon=need”的节点上才创建Pod,如下图所示,DaemonSet只在指定标签的节点上创建Pod。如果需要在每一个节点上创建Pod可以删除该标签。
亲和和反亲和调度
Kubernetes支持节点和Pod两个层级的亲和与反亲和。通过配置亲和与反亲和规则,可以允许您指定硬性限制或者偏好,例如将前台Pod和后台Pod部署在一起、某类应用部署到某些特定的节点、不同应用部署到不同的节点等等。
Node Affinity(节点亲和)
节点的亲和性规则是基于标签实现的。
通常情况下,对于一个大型Kubernetes集群,肯定会根据业务需要定义很多标签。
在DaemonSet中介绍了nodeSelector,通过nodeSelector可以让Pod只部署在具有特定标签的节点上。如下所示,Pod只会部署在拥有gpu=true这个标签的节点上。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeSelector: # 节点选择,当节点拥有gpu=true时才在节点上创建Pod
gpu: true
...
通过节点亲和性规则配置,也可以做到同样的事情,如下所示
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu
labels:
app: gpu
spec:
selector:
matchLabels:
app: gpu
replicas: 3
template:
metadata:
labels:
app: gpu
spec:
containers:
- image: nginx:alpine
name: gpu
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
看起来这要复杂很多,但这种方式可以得到更强的表达能力,后面会进一步介绍。
这里affinity表示亲和,nodeAffinity表示节点亲和,requiredDuringSchedulingIgnoredDuringExecution非常长,不过可以将这个分作两段来看:
- 前半段requiredDuringScheduling表示下面定义的规则必须强制满足(require)才会调度Pod到节点上。
- 后半段IgnoredDuringExecution表示已经在节点上运行的Pod不需要满足下面定义的规则,即去除节点上的某个标签,那些需要节点包含该标签的Pod不会被重新调度。
另外操作符operator的值为In,表示标签值需要在values的列表中,其他operator取值如下。
- NotIn:标签的值不在某个列表中
- Exists:某个标签存在
- DoesNotExist:某个标签不存在
- Gt:标签的值大于某个值(字符串比较)
- Lt:标签的值小于某个值(字符串比较)
需要说明的是并没有nodeAntiAffinity(节点反亲和),因为NotIn和DoesNotExist可以提供相同的功能。
节点优先选择规则
上面讲的requiredDuringSchedulingIgnoredDuringExecution是一种强制选择的规则,节点亲和还有一种优先选择规则,即preferredDuringSchedulingIgnoredDuringExecution,表示会根据规则优先选择哪些节点。
在优先选择规则下,如果节点A的优先级大于节点B,但节点A部署了很多Pod,资源使用比节点B多,最终的结果可能并不会往A节点上调度,这样侧面说明了preferredDuringSchedulingIgnoredDuringExecution是优先规则,而不是强制规则。
Pod Affinity(Pod亲和)
节点亲和的规则只能影响Pod和节点之间的亲和,Kubernetes还支持Pod和Pod之间的亲和,例如将应用的前端和后端部署在一起,从而减少访问延迟。Pod亲和同样有 requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution两种规则。
Pod AntiAffinity(Pod反亲和)
持久化存储
配置管理
传统架构中,配置文件往往被保存在宿主机上,程序启动时可以指定某个配置文件,但是使用容器部署时,容器所在的节点并不固定,所以不能使用这种方式,此处在构建镜像时,如果把配置文件也放在容器里面,那么配置文件一旦有更改的话,也是一件非常麻烦的事情。所以Kubernetes抽象了一个ConfigMap的概念,将配置与Pod和组件分开,这有助于保持工作负载的可移植性,使配置更易于更改和管理。比如在生产环境中,可以将Nginx、Redis等应用的配置文件存储在ConfigMap上,然后将其挂载即可使用。相对于Secret,ConfigMap更倾向于存储和共享非敏感、未加密的配置信息,假如是在集群中使用敏感信息,最好使用Secret。
ConfigMap
限制:ConfigMap资源文件大小不得超过2MB。
注意:
在工作负载里使用ConfigMap时,需要工作负载和ConfigMap处于同一集群和命名空间中。
以数据卷挂载使用ConfigMap时,当ConfigMap被更新,Kubernetes会同时更新数据卷中的数据。
对于以subPath形式挂载的ConfigMap数据卷,当ConfigMap被更新时,Kubernetes无法自动更新数据卷中的数据。
以环境变量方式使用ConfigMap时,当ConfigMap被更新,数据不会被自动更新。 更新这些数据需要重新启动Pod。
创建配置项
创建并编辑cce-configmap.yaml文件:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
data:
SPECIAL_LEVEL: Hello
SPECIAL_TYPE: CCE
使用 kubectl 创建配置项
kubectl create -f my-configmap.yaml
查看已创建的配置项:
$ kubectl get cm
NAME DATA AGE
my-configmap 3 7m
工作负载中使用配置项
如果要将一个配置项中所有数据都添加到环境变量中,可以使用 envFrom参数,配置项中的Key会成为工作负载中的环境变量名称。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-configmap
spec:
replicas: 1
selector:
matchLabels:
app: nginx-configmap
template:
metadata:
labels:
app: nginx-configmap
spec:
containers:
- name: container-1
image: nginx:latest
envFrom: # 使用envFrom来指定环境变量引用的配置项
- configMapRef:
name: my-configmap # 引用的配置项名称
创建负载完成后,查看Pod中的环境变量
# 查看pod名称
kubectl get pod | grep nginx-configmap
# 查看Pod中的环境变量
kubectl exec nginx-configmap-*** -- printenv SPECIAL_LEVEL SPECIAL_TYPE
配置项挂载到工作负载数据卷
配置项挂载完成后,最终会在容器中的 /etc/config目录下生成以配置项中的key为文件名, value为文件内容的配置文件。
等待工作负载正常运行后,在/etc/config目录下会生成 SPECIAL_LEVEL和 SPECIAL_TYPE两个文件,且文件的内容分别为Hello和CCE。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-configmap
spec:
replicas: 1
selector:
matchLabels:
app: nginx-configmap
template:
metadata:
labels:
app: nginx-configmap
spec:
containers:
- name: container-1
image: nginx:latest
volumeMounts:
- name: config-volume
mountPath: /etc/config # 挂载到/etc/config目录下
readOnly: true
volumes:
- name: config-volume
configMap:
name: cce-configmap # 引用的配置项名称
Secret
上一节讲解的ConfigMap主要用于非安全的数据,与其对应的是Secret对象类型,用来保存敏感信息,例如密码、令牌和SSH Key,将这些信息放在Secret中比较安全和灵活。用户可以创建Secret并且引用到Pod中,比如使用Secret初始化Redis、MySQL密码等。
密钥(Secret)是一种用于存储工作负载所需要认证信息、密钥的敏感信息等的资源类型,内容由用户决定。资源创建完成后,可在容器工作负载中作为文件或者环境变量使用。
Secret资源有多种类型:
- Opaque
- kubernetes.io/dockerconfigjson
- kubernetes.io/tls
- IngressTLS
创建密钥
Opaque类型定义的Secret文件secret.yaml内容如下。其中data字段以键值对的形式填写,value需要用Base64编码。
apiVersion: v1
kind: Secret
metadata:
name: mysecret # secret的名称
namespace: default #命名空间,默认为default
data:
<your_key>: <your_value> #填写键值对,其中value需要用Base64编码
type: Opaque
kubernetes.io/dockerconfigjson类型定义的Secret文件secret.yaml内容如下。其中.dockerconfigjson需要用Base64 编码。
apiVersion: v1
kind: Secret
metadata:
name: mysecret # secret的名称
namespace: default #命名空间,默认为default
data:
.dockerconfigjson: eyJh***** #Base64编码后的内容
type: kubernetes.io/dockerconfigjson
kubernetes.io/tls类型其中tls.crt和tls.key需要用Base64编码。
kind: Secret
apiVersion: v1
metadata:
name: mysecret # secret的名称
namespace: default #命名空间,默认为default
data:
tls.crt: LS0tLS1CRU*****FURS0tLS0t #证书内容,需要Base64编码
tls.key: LS0tLS1CRU*****VZLS0tLS0= #私钥内容,需要Base64编码
type: kubernetes.io/tls
IngressTLS类型其中tls.crt和tls.key需要用Base64编码。
kind: Secret
apiVersion: v1
metadata:
name: mysecret # secret的名称
namespace: default #命名空间,默认为default
data:
tls.crt: LS0tLS1CRU*****FURS0tLS0t #证书内容,需要Base64编码
tls.key: LS0tLS1CRU*****VZLS0tLS0= #私钥内容,需要Base64编码
type: IngressTLS
使用kubectl创建密钥
# 创建密钥
kubectl create -f my-secret.yaml
# 查询密钥
kubectl get secret -n default
使用密钥
Volume
容器中的文件在磁盘上是临时存放的,当容器重建时,容器中的文件将会丢失,另外当在一个Pod中同时运行多个容器时,常常需要在这些容器之间共享文件,这也是容器不好解决的问题。 Kubernetes抽象出了Volume来解决这两个问题,也就是存储卷,Kubernetes的Volume是Pod的一部分,Volume不是单独的对象,不能独立创建,只能在Pod中定义。
Pod中的所有容器都可以访问Volume,但必须要挂载,且可以挂载到容器中任何目录。实际中使用容器存储如下图所示,将容器的内容挂载到Volume中,通过Volume两个容器间实现了存储共享。

Volume的生命周期与挂载它的Pod相同,但是Volume里面的文件可能在Volume消失后仍然存在,这取决于Volume的类型。
Volume的类型
Kubernetes的Volume有非常多的类型,在实际使用中使用最多的类型如下:
- emptyDir:一种简单的空目录,主要用于临时存储。
- hostPath:将主机某个目录挂载到容器中。
- ConfigMap、Secret:特殊类型,将Kubernetes特定的对象类型挂载到Pod,在ConfigMap和Secret章节介绍过如何将ConfigMap和Secret挂载到Volume中。
- persistentVolumeClaim:Kubernetes的持久化存储类型
PV 和 PVC
PV(Persistent Volume) 是集群中的一块物理存储资源 ,由管理员预先创建或通过 StorageClass 动态生成。例如:AWS 的 EBS 磁盘、本地硬盘、NFS 共享目录等。作用 :直接提供存储空间,供 Pod 通过 PVC 使用。
关键字段 :
存储容量(`capacity`)。
访问模式(`accessModes`,如 `ReadWriteOnce`)。
回收策略(`reclaimPolicy`,如 `Retain` 或 `Delete`)
示例 :手动创建一个 NFS 类型的 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany # 支持多节点读写
nfs:
server: nfs-server.example.com
path: "/exports"
persistentVolumeReclaimPolicy: Retain # 保留数据
PVC(Persistent Volume Claim) 持久化存储卷声明
- PVC 是用户对存储资源的请求和声明 ,类似于“申请存储的申请单”。
- 用户通过 PVC 申请存储资源,Kubernetes 会自动匹配符合要求的 PV 或通过 StorageClass 动态创建 PV。

示例:声明的PVC中未指定 StorageClass,直接绑定到手动创建的 PV
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Gi
StorageClass
上节说的PV和PVC方法虽然能实现屏蔽底层存储,但是PV创建比较复杂(可以看到PV中csi字段的配置很麻烦),通常都是由集群管理员管理,这非常不方便。
Kubernetes解决这个问题的方法是提供动态配置PV的方法,可以自动创PV。管理员可以部署PV配置器(provisioner),然后定义对应的StorageClass,这样开发者在创建PVC的时候就可以选择需要创建存储的类型,PVC会把StorageClass传递给PV provisioner,由provisioner自动创建PV。
StorageClass 是存储类型的模板 ,定义了如何动态创建 PV。
示例:定义一个 AWS EBS 的 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-storage
provisioner: kubernetes.io/aws-ebs # AWS EBS 驱动
parameters:
type: gp3 # SSD 类型
fsType: ext4 # 文件系统
reclaimPolicy: Delete # 删除 PV 时自动清理存储
在 PVC中指定 StorageClass :
# 动态创建 PVC(使用 AWS EBS)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: aws-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: aws-storage # 指定 StorageClass
resources:
requests:
storage: 100Gi
完整协作流程:
静态模式
PV(手动创建) -> PVC (申请存储) -> Pod (使用PVC)
动态模式
StorageClass(定义规则) -> PVC (申请存储) -> PV(自动创建) -> Pod (使用PVC)
总结
- PV 是存储资源 ,PVC 是存储请求,StorageClass 是动态分配规则。
- 静态模式 :管理员手动创建 PV → 用户通过 PVC 申请 → 绑定到 Pod。
- 动态模式 :用户通过 PVC + StorageClass → 系统自动创建 PV → 绑定到 Pod。
通过 StorageClass,Kubernetes 实现了存储资源的自动化管理 ,用户无需关注底层存储细节。
网络管理
Kubernetes本身并不负责网络通信,Kubernetes提供了容器网络接口CNI(Container Network Interface),具体的网络通信交给CNI插件来负责,开源的CNI插件非常多,像Flannel、Calico等。
Kubernetes虽然不负责网络,但要求集群中的Pod能够互相通信,且Pod必须通过非NAT网络连接,即收到的数据包的源IP就是发送数据包Pod的IP。同时Pod与节点之间的通信也是通过非NAT网络。但是Pod访问集群外部时源IP会被修改成节点的IP。
Pod内部是通过虚拟Ethernet接口对(Veth pair)与Pod外部连接,Veth pair就像一根网线,一端留在Pod内部,一端在Pod之外。而同一个节点上的Pod通过网桥(Linux Bridge)通信,如下图所示
图1:同一个节点中的Pod通信

不同节点间的网桥连接有很多种方式,这跟具体实现相关。但集群要求Pod的地址唯一,所以跨节点的网桥通常使用不同的地址段,以防止Pod的IP地址重复。
图2:不同节点上的Pod通信

Kubernetes 中为了实现服务实例间的负载均衡和不同服务间的服务发现,创造了 Serivce 对象,同时又为从集群外部访问集群创建了 Ingress 对象。
Service
Service是基于四层TCP和UDP协议转发的,而Ingress可以基于七层的HTTP和HTTPS协议转发。
为什么需要Service
直接访问Pod的问题
Pod创建完成后,如何访问Pod呢?直接访问Pod会有如下几个问题:
- Pod会随时被Deployment这样的控制器删除重建,那访问Pod的结果就会变得不可预知。
- Pod的IP地址是在Pod启动后才被分配,在启动前并不知道Pod的IP地址。
- 应用往往都是由多个运行相同镜像的一组Pod组成,逐个访问Pod也变得不现实。
使用Service解决Pod的访问问题
Kubernetes中的Service对象就是用来解决上述Pod访问问题的。Service有一个固定IP地址,Service将访问它的流量转发给Pod,具体转发给哪些Pod通过Label来选择,而且Service可以给这些Pod做负载均衡。
举个例子,假设有这样一个应用程序,使用Deployment创建了前台和后台,前台会调用后台做一些计算处理,如下图所示。后台运行了3个Pod,这些Pod是相互独立且可被替换的,当Pod出现状况被重建时,新建的Pod的IP地址是新IP,前台的Pod无法直接感知。
这种情况下,为后台添加一个Service,通过Service来访问Pod,这样前台Pod就无需感知后台Pod的变化。

如何使用Service
创建后台Pod
首先创建一个3副本的Deployment,即3个Pod,且Pod上带有标签“app: nginx”,具体如下所示
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
imagePullSecrets:
- name: default-secret
创建Service
下面示例创建一个名为“nginx”的Service,通过selector选择到标签“app:nginx”的Pod,目标Pod的端口为80,Service对外暴露的端口为8080。
访问服务只需要通过“服务名称:对外暴露的端口”接口,对应本例即“nginx:8080”。这样,在其他Pod中,只需要通过“nginx:8080”就可以访问到“nginx”关联的Pod。
apiVersion: v1
kind: Service
metadata:
name: nginx # Service的名称
spec:
selector: # Label Selector,选择包含app=nginx标签的Pod
app: nginx
ports:
- name: service0
targetPort: 80 # Pod的端口
port: 8080 # Service对外暴露的端口
protocol: TCP # 转发协议类型,支持TCP和UDP
type: ClusterIP # Service的类型
将上面Service的定义保存到 nginx-svc.yaml文件中,使用kubectl创建这个Service。
$ kubectl create -f nginx-svc.yaml
service/nginx created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.247.0.1 <none> 443/TCP 7h19m
nginx ClusterIP 10.247.124.252 <none> 8080/TCP 5h48m
您可以看到Service有个Cluster IP,这个IP是固定不变的,除非Service被删除,所以您也可以使用ClusterIP在集群内部访问Service。
下面创建一个Pod并进入容器,使用ClusterIP访问Pod,可以看到能直接返回内容。
$ kubectl run -i --tty --image nginx:alpine test --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # curl 10.247.124.252:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
使用ServiceName访问Service
在创建Kubernates 集群时,会默认安装CoreDNS插件,在 kube-system命名空间下可以查看到CoreDNS的Pod。
$ kubectl get po --namespace=kube-system
NAME READY STATUS RESTARTS AGE
coredns-7689f8bdf-295rk 1/1 Running 0 9m11s
coredns-7689f8bdf-h7n68 1/1 Running 0 11m
CoreDNS安装成功后会成为DNS服务器,当创建Service后,CoreDNS会将Service的名称与IP记录起来,这样Pod就可以通过向CoreDNS查询Service的名称获得Service的IP地址。
访问时通过 nginx.<namespace>.svc.cluster.local
访问,其中nginx为Service的名称,
使用ServiceName的方式有个主要的优点就是可以在开发应用程序时可以将ServiceName写在程序中,这样无需感知具体Service的IP地址。
Service的类型与使用场景
Service 类型:
- ClusterIP (默认)
- NodePort
- LoadBalancer
- Headless
- ExternalName
1. ClusterIP(默认类型)
作用
- 在集群内部创建一个虚拟 IP(ClusterIP),供其他 Pod 访问。
- 仅限集群内部通信,无法从外部直接访问。
使用场景
- 微服务间通信:例如,前端服务通过 ClusterIP 访问后端数据库或 API。
- 内部工具服务:如 Prometheus 监控服务、日志收集服务等内部组件之间的通信。
配置示例
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80 # Service 暴露的端口
targetPort: 8080 # Pod 的实际端口
type: ClusterIP # 默认类型,可省略
2. NodePort
作用
- 在每个节点上开放一个静态端口(NodePort,默认范围 30000-32767),外部流量通过
<NodeIP>:<NodePort>
访问集群。 - 底层通过 ClusterIP 实现,最终流量转发到 Pod。
使用场景
- 开发和测试环境:快速暴露服务进行调试。
- 内部网络访问:当集群节点在私有网络中,且需要通过节点 IP 直接访问服务时。
配置示例
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app # 匹配后端 Pod 的标签
ports:
- port: 8080 # Service 在集群内部对外暴露的端口
targetPort: 80 # Pod 的目标端口
nodePort: 30080 # 节点对集群外部暴露的端口(可选,若不指定会自动分配30000-32767之间的端口)
访问方式:
- 集群内部:通过
my-nodeport-service:80
访问 - 集群外部:通过任意节点 IP + 对应的 nodePort(如
192.168.1.100:30080
)
3. LoadBalancer
作用
- 在云服务商(如 AWS、GCP、Azure)中自动创建外部负载均衡器,直接将外部流量路由到 Service。
- 底层通常基于 NodePort 或 ClusterIP 实现。
使用场景
- 生产环境:需要通过公网 IP 或域名访问服务(如 Web 应用)。
- 云原生应用:依赖云服务商的负载均衡能力(如自动扩缩容、健康检查)。
配置示例
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
输出示例(AWS):
status:
loadBalancer:
ingress:
- ip: 203.0.113.45 # 云服务商分配的公网 IP
4. Headless Service(无头服务)
作用
- 不分配 ClusterIP,直接返回后端 Pod 的 IP 列表。
- 需配合
StatefulSet
使用,支持稳定的网络标识(如mysql-0.mysql-headless.default.svc.cluster.local
)。
使用场景
- 有状态应用:如 MySQL、Redis 集群,需要直接访问特定 Pod。
- 自定义负载均衡:客户端需自行选择 Pod(如通过 DNS 轮询或客户端负载均衡)。
配置示例
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
clusterIP: None # 设置为 None 表示 Headless
5. ExternalName
作用
- 将 Service 映射到外部的 DNS 名称,绕过 Kubernetes 集群直接访问外部服务。
使用场景
- 集成遗留系统:将内部服务与外部 API(如第三方支付接口)统一管理。
- 简化配置:避免为外部服务单独部署代理。
配置示例
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: ExternalName
externalName: api.external.com # 外部服务的 DNS 名称
ports:
- port: 80
Service 类型对比
类型 | 访问方式 | 适用场景 | 外部可达性 |
---|---|---|---|
ClusterIP | 集群内部 IP | 微服务间通信 | ❌ 否 |
NodePort | <NodeIP>:<NodePort> |
开发测试、内部网络访问 | ✅ 是 |
LoadBalancer | 云服务商分配的公网 IP | 生产环境公网访问 | ✅ 是 |
Headless | 直接访问 Pod IP 或 DNS | 有状态应用、自定义负载均衡 | ❌ 否 |
ExternalName | 外部 DNS 名称 | 集成外部系统 | ✅ 是 |
如何选择 Service 类型?
- 内部服务通信 → ClusterIP(默认)。
- 开发和测试环境快速暴露 → NodePort。
- 生产环境公网访问 → LoadBalancer(云环境)或 Ingress(HTTP/HTTPS)。
- 有状态应用直接访问 → Headless + StatefulSet。
- 集成外部服务 → ExternalName。
高级用法
1. 多端口 Service
apiVersion: v1
kind: Service
metadata:
name: multi-port-service
spec:
selector:
app: my-app
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
- name: https
protocol: TCP
port: 443
targetPort: 8443
2. 组合使用 Ingress + Service
- Ingress 管理 HTTP/HTTPS 路由,Service 管理后端流量。
- 通过 Ingress 暴露多个 Service。
服务发现的原理
前面说到有了Service后,无论Pod如何变化,Service都能够发现到Pod。如果调用kubectl describe命令查看Service的信息,您会看到如下信息。
$ kubectl describe svc nginx
Name: nginx
......
Endpoints: 172.16.2.132:80,172.16.3.6:80,172.16.3.7:80
......
可以看到一个Endpoints,Endpoints同样也是Kubernetes的一种资源对象,可以查询得到。Kubernetes正是通过Endpoints监控到Pod的IP,从而让Service能够发现Pod。
$ kubectl get endpoints
NAME ENDPOINTS AGE
nginx 172.16.2.132:80,172.16.3.6:80,172.16.3.7:80 5h48m
这里的172.16.2.132:80是Pod的IP:Port,通过如下命令可以查看到Pod的IP,与上面的IP一致。
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-869759589d-dnknn 1/1 Running 0 5h40m 172.16.3.7 192.168.0.212
nginx-869759589d-fcxhh 1/1 Running 0 5h40m 172.16.3.6 192.168.0.212
nginx-869759589d-r69kh 1/1 Running 0 5h40m 172.16.2.132 192.168.0.94

如果删除一个Pod,Deployment会将Pod重建,新的Pod IP会发生变化。再次查看Endpoints,会发现Endpoints的内容随着Pod发生了变化。
下面进一步了解这又是如何实现的。
Kubernetes集群架构中Node节点上有一个组件 kube-proxy,实际上Service相关的事情都由节点上的kube-proxy处理。在Service创建时Kubernetes会分配IP给Service,同时通过API Server通知所有kube-proxy有新Service创建了,kube-proxy收到通知后通过iptables记录Service和IP/端口对的关系,从而让Service在节点上可以被查询到。
下图是一个实际访问Service的图示,Pod X访问Service(10.247.124.252:8080),在往外发数据包时,在节点上根据iptables规则目的IP:Port被随机替换为Pod1的IP:Port,从而通过Service访问到实际的Pod。除了记录Service和IP/端口对的关系,kube-proxy还会监控Service和Endpoint的变化,从而保证Pod重建后仍然能通过Service访问到Pod。

Ingress
Ingress 是Kubernetes中的一种资源对象,用于管理对集群的外部访问。
Ingress 资源支持以下功能:
- 基于内容的路由:
- 基于主机的路由。例如,将主机标头 foo.example.com 的请求路由到一组服务,将主机标头 bar.example.com 路由到另一组服务。
- 基于路径的路由。 例如,将 /serviceA 开头的 URI 的请求路由到服务 A,将 /serviceB 开头的 URI 的请求路由到服务 B。
为什么需要Ingress
Service是基于四层TCP和UDP协议转发的,而Ingress可以基于七层的HTTP和HTTPS协议转发,可以通过域名和路径做到更细粒度的划分,如下图所示

Ingress工作机制
要想使用Ingress功能,必须在Kubernetes集群上安装Ingress Controller。Ingress Controller有很多种实现,最常见的就是Kubernetes官方维护的 Nginx Ingress Controller;
外部请求首先到达Ingress Controller,Ingress Controller根据Ingress的路由规则,查找到对应的Service,进而通过Endpoint查询到Pod的IP地址,然后将请求转发给Pod。

路由到多个服务
Ingress可以同时路由到多个服务,配置如下所示。
spec:
rules:
- host: foo.bar.com # host地址
http:
paths:
- path: "/foo"
backend:
serviceName: s1
servicePort: 80
- path: "/bar"
backend:
serviceName: s2
servicePort: 80
一般情况下,Ingress主要是一个用于Kubernetes集群业务的入口。是业务能够正常提供服务的核心,所以在生产环境中,推荐使用单独的服务器作为Ingress Controller。Controller可以使用Traefik、Istio、Nginx 、HAProxy 等作为 Ingress Controller 。本文主要讲解 Ingress Nginx ,这也是Kubernetes官方提供的Ingress Controller。
创建 Nginx Ingress
1.23及以上版本集群:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-test
spec:
rules:
- host: ''
http:
paths:
- path: /
backend:
service:
name: <your_service_name> #替换为您的目标服务名称
port:
number: <your_service_port> #替换为您的目标服务端口
pathType: ImplementationSpecific
ingressClassName: nginx # 表示使用Nginx Ingress
创建并查看
kubectl create -f ingress-test.yaml
kubectl get ingress
在浏览器中输入访问地址“http://121.**.**.**:80”进行验证。
注意事项
如创建Ingress过程中指定了host地址,将无法通过IP访问。
DNS
CoreDNS
创建集群时会自动安装CoreDNS插件,CoreDNS是用来做集群内部域名解析。
在kube-system命名空间下可以查看到CoreDNS的Pod
$ kubectl get po --namespace=kube-system
NAME READY STATUS RESTARTS AGE
coredns-7689f8bdf-295rk 1/1 Running 0 9m11s
coredns-7689f8bdf-h7n68 1/1 Running 0 11m
CoreDNS安装成功后会成为DNS服务器,当创建Service后,CoreDNS会将Service的名称与IP记录起来,这样Pod就可以通过向CoreDNS查询Service的名称获得Service的IP地址。
就绪探针 ReadinessProbe
一个新Pod创建后,Service就能立即选择到它,并会把请求转发给Pod,那问题就来了,通常一个Pod启动是需要时间的,如果Pod还没准备好(可能需要时间来加载配置或数据,或者可能需要执行一个预热程序之类),这时把请求转给Pod的话,Pod也无法处理,造成请求失败。
Kubernetes解决这个问题的方法就是给Pod加一个业务就绪探针Readiness Probe,当检测到Pod就绪后才允许Service将请求转给Pod。
Readiness Probe同样是周期性的检测Pod,然后根据响应来判断Pod是否就绪,与存活探针(Liveness Probe)相同,就绪探针也支持如下三种类型。
- HTTP GET:往容器的IP:Port发送HTTP GET请求,如果Probe收到2xx或3xx,说明已经就绪。
- TCP Socket:尝试与容器建立TCP连接,如果能建立连接说明已经就绪。
- Exec:Probe执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明已经就绪。
HTTP GET
Readiness Probe的配置与存活探针(livness probe)一样,都是在Pod Template的containers里面,如下所示,这个Readiness Probe向Pod发送HTTP请求,当Probe收到2xx或3xx返回时,说明Pod已经就绪。
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
readinessProbe: # readinessProbe
httpGet: # HTTP GET定义
path: /read
port: 80
TCP Socket
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
readinessProbe: # readinessProbe
tcpSocket: # TCP Socket定义
port: 80
Exec
Exec方式与HTTP GET方式一致,如下所示,这个探针执行 ls /ready
命令,如果这个文件存在,则返回0,说明Pod就绪了,否则返回其他状态码。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
readinessProbe: # Readiness Probe
exec: # 定义 ls /ready 命令
command:
- ls
- /ready
imagePullSecrets:
- name: default-secret
CNI 插件
在Kubernetes云原生容器网络方面,Calico完全遵循CNI的标准,Flannel 的简单成为初始用户的首选,Calico则是以性能及灵活性成为另一个不错的选择。当前Flannel与Calico两大插件占据容器网络插件90%以上的份额。相比Flannel插件,Calico的功能更为全面,不仅提供主机和Pod之间的网络连接,还涉及网络安全和管理。

- Overlay 模式的典型特征是容器独立于主机的 IP 段,这个 IP 段进行跨主机网络通信时是通过在主机之间创建隧道的方式,将整个容器网段的包全都封装成底层的物理网络中主机之间的包。该方式的好处在于它不依赖于底层网络;
- 路由模式中主机和容器也分属不同的网段,它与 Overlay 模式的主要区别在于它的跨主机通信是通过路由打通,无需在不同主机之间做一个隧道封包。但路由打通就需要部分依赖于底层网络,比如说要求底层网络有二层可达的一个能力;
- Underlay 模式中容器和宿主机位于同一层网络,两者拥有相同的地位。容器之间网络的打通主要依靠于底层网络。因此该模式是强依赖于底层能力的。
了解了以上三种常用的实现模式之后,再根据自己的环境、需求判断可由哪一种模式进行实现,再在对应的模式中去找 CNI 插件。
Flannel
由 CoreOS 开发的项目 Flannel,可能是最直接和最受欢迎的 CNI 插件。它是容器编排系统中最成熟的网络结构示例之一。
Calico
Calico 是 Kubernetes 生态系统中另一种流行的网络选择。虽然 Flannel 被公认为是最简单的选择,但 Calico 以其性能、灵活性而闻名。Calico 的功能更为全面,不仅提供主机和 pod 之间的网络连接,还涉及网络安全和管理。Calico CNI 插件在 CNI 框架内封装了 Calico 的功能。
资源管理
Pod
Pod是Kubernetes创建或部署的最小单位。一个Pod封装一个或多个容器(container)、存储资源(volume)、一个独立的网络IP以及管理控制容器运行方式的策略选项。
Pod使用主要分为两种方式:
- Pod中运行一个容器。这是Kubernetes最常见的用法,您可以将Pod视为单个封装的容器,但是Kubernetes是直接管理Pod而不是容器。
- Pod中运行多个需要耦合在一起工作、需要共享资源的容器。通常这种场景下应用包含一个主容器和几个辅助容器(SideCar Container),如图所示,例如主容器为一个web服务器,从一个固定目录下对外提供文件服务,而辅助容器周期性的从外部下载文件存到这个固定目录下。

实际使用中很少直接创建Pod,而是使用Kubernetes中称为Controller的抽象层来管理Pod实例,例如Deployment和Job。Controller可以创建和管理多个Pod,提供副本管理、滚动升级和自愈能力。通常,Controller会使用Pod Template来创建相应的Pod。
创建Pod
kubernetes中资源可以使用 YAML或JSON 描述,如下示例描述了一个名为nginx的Pod,这个Pod中包含一个名为container-0的容器,使用nginx:alpine镜像,使用的资源为100m core CPU、200Mi内存。
apiVersion: v1 # Kubernetes的API Version
kind: Pod # Kubernetes的资源类型
metadata:
name: nginx # Pod的名称
spec: # Pod的具体规格(specification)
containers:
- image: nginx:alpine # 使用的镜像为 nginx:alpine
name: container-0 # 容器的名称
resources: # 申请容器所需的资源
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
如上面YAML的注释,YAML描述文件主要为如下部分:
- metadata:一些名称/标签/namespace等信息。
- spec:Pod实际的配置信息,包括使用什么镜像,volume等。
Pod定义好后就可以使用kubectl创建:
$ kubectl create -f nginx.yaml
pod/nginx created
查看Pod
Pod创建完成后,使用kubectl get pods命令查询Pod的状态
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 40s
可以看到此处nginx这个Pod的状态为Running,表示正在运行;READY为1/1,表示这个Pod中有1个容器,其中1个容器的状态为Ready。
可以使用kubectl get命令查询具体Pod的配置信息,如下所示,-o yaml表示以YAML格式返回,还可以使用-o json,以JSON格式返回。
# 查询具体Pod的配置信息
$ kubectl get pod nginx -o yaml
# 还可以使用kubectl describe命令查看Pod的详情
$ kubectl describe pod nginx
删除Pod
删除pod时,Kubernetes终止Pod中所有容器。 Kubernetes向进程发送SIGTERM信号并等待一定的秒数(默认为30)让容器正常关闭。如果它没有在这个时间内关闭,Kubernetes会发送一个SIGKILL信号杀死该进程。
# 按名称删除
$ kubectl delete po nginx
# 同时删除多个Pod
$ kubectl delete po pod1 pod2
# 根据Label删除Pod
$ kubectl delete po -l app=nginx
设置环境变量
环境变量是容器运行环境中设定的一个变量。
环境变量为应用提供极大的灵活性,您可以在应用程序中使用环境变量,在创建容器时为环境变量赋值,容器运行时读取环境变量的值,从而做到灵活的配置,而不是每次都重新编写应用程序制作镜像。
环境变量的使用方法如下所示,配置spec.containers.env字段即可。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
env: # 环境变量
- name: env_key
value: env_value
imagePullSecrets:
- name: default-secret
执行如下命令查看容器中的环境变量,可以看到env_key这个环境变量,其值为env_value
$ kubectl exec -it nginx -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=nginx
TERM=xterm
env_key=env_value
环境变量还可以引用ConfigMap和Secret。
容器启动命令
启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。比如MySQL类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的MySQL服务器运行之前做完。这些操作,可以在制作镜像时通过在Dockerfile文件中设置ENTRYPOINT或CMD来完成,其将会在容器启动时执行。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
实际使用时,只需配置Pod的 containers.command
参数,该参数是list类型,第一个参数为执行命令,后面均为命令的参数。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
command: # 启动命令
- top
- "-b"
imagePullSecrets:
- name: default-secret
容器的生命周期
Kubernetes提供了容器生命周期钩子,在容器的生命周期的特定阶段执行调用,比如容器在停止前希望执行某项操作,就可以注册相应的钩子函数。目前提供的生命周期钩子函数如下所示。
- 启动后处理(PostStart):容器启动后触发。
- 停止前处理(PreStop):容器停止前触发。
实际使用时,只需配置Pod的lifecycle.postStart
或 lifecycle.preStop
参数,如下所示。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
lifecycle:
postStart: # 启动后处理
exec:
command:
- "/postStart.sh"
preStop: # 停止前处理
exec:
command:
- "/preStop.sh"
imagePullSecrets:
- name: default-secret
配置CPU资源
为容器设置 CPU request(请求) 和 CPU limit(限制)。 容器使用的 CPU 不能超过所配置的限制。 如果系统有空闲的 CPU 时间,则可以保证给容器分配其所请求数量的 CPU 资源。
指定 CPU 请求和 CPU 限制
要为容器指定 CPU 请求,请在容器资源清单中包含 resources: requests
字段。 要指定 CPU 限制,请包含 resources:limits
。
在本练习中,你将创建一个具有一个容器的 Pod。容器将会请求 0.5 个 CPU,而且最多限制使用 1 个 CPU。 这是 Pod 的配置文件:
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo
namespace: cpu-example
spec:
containers:
- name: cpu-demo-ctr
image: vish/stress
resources:
limits:
cpu: "1"
requests:
cpu: "0.5"
说明:CPU 使用率低于 1.0 的另一种可能的解释是,节点可能没有足够的 CPU 资源可用。
CPU 单位
CPU 资源以 CPU 单位度量。Kubernetes 中的一个 CPU 等同于:
- 1 个 AWS vCPU
- 1 个 GCP核心
- 1 个 Azure vCore
- 裸机上具有超线程能力的英特尔处理器上的 1 个超线程
小数值是可以使用的。一个请求 0.5 CPU 的容器保证会获得请求 1 个 CPU 的容器的 CPU 的一半。 你可以使用后缀 m
表示毫。例如 100m
CPU、100 milliCPU 和 0.1 CPU 都相同。 精度不能超过 1m。
CPU 请求只能使用绝对数量,而不是相对数量。0.1 在单核、双核或 48 核计算机上的 CPU 数量值是一样的。
设置超过节点能力的 CPU 请求
CPU 请求和限制与都与容器相关, Pod 对 CPU 用量的请求等于 Pod 中所有容器的请求数量之和。 同样,Pod 的 CPU 资源限制等于 Pod 中所有容器 CPU 资源限制数之和。
如果我们设置容器请求 100 个 CPU,超出集群中任何节点的容量。那么查看Pod 状态时你会发现, Pod 状态为 Pending。也就是说,Pod 未被调度到任何节点上运行, 并且 Pod 将无限期地处于 Pending 状态。
如果不指定 CPU 限制
如果你没有为容器指定 CPU 限制,则会发生以下情况之一:
- 容器在可以使用的 CPU 资源上没有上限。因而可以使用所在节点上所有的可用 CPU 资源。
- 容器在具有默认 CPU 限制的namespace 中运行,系统会自动为容器设置默认限制。 集群管理员可以使用 LimitRange 指定 CPU 限制的默认值。
设置 CPU 限制但未设置 CPU 请求
如果你为容器指定了 CPU 限制值但未为其设置 CPU 请求,Kubernetes 会自动为其 设置与 CPU 限制相同的 CPU 请求值。类似的,如果容器设置了内存限制值但未设置 内存请求值,Kubernetes 也会为其设置与内存限制值相同的内存请求。
CPU 请求和限制的初衷
通过配置你的集群中运行的容器的 CPU 请求和限制,你可以有效利用集群上可用的 CPU 资源。 通过将 Pod CPU 请求保持在较低水平,可以使 Pod 更有机会被调度。 通过使 CPU 限制大于 CPU 请求,你可以完成两件事:
- Pod 可能会有突发性的活动,它可以利用碰巧可用的 CPU 资源。
- Pod 在突发负载期间可以使用的 CPU 资源数量仍被限制为合理的数量。
配置内存资源
要为容器指定内存请求,请在容器资源清单中包含 resources: requests
字段。 同理,要指定内存限制,请包含 resources: limits
。
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
namespace: mem-example
spec:
containers:
- name: memory-demo-ctr
image: polinux/stress
resources:
requests:
memory: "100Mi"
limits:
memory: "200Mi"
配置额超出实际内存
如果创建的 Pod,配置的内存请求超过了你集群中的任意一个节点所拥有的内存,如配置1000G,则该Pod 处于 PENDING 状态。 这意味着,该 Pod 没有被调度至任何节点上运行,并且它会无限期的保持该状态。
内存单位
内存资源的基本单位是字节(byte)。你可以使用这些后缀之一,将内存表示为 纯整数或定点整数:E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki。
存活探针(Liveness Probe)
Kubernetes提供了自愈的能力,具体就是能感知到容器崩溃,然后能够重启这个容器。但是有时候例如Java程序内存泄漏了,程序无法正常工作,但是JVM进程却是一直运行的,对于这种应用本身业务出了问题的情况,Kubernetes提供了Liveness Probe机制,通过检测容器响应是否正常来决定是否重启,这是一种很好的健康检查机制。
建议每个Pod都定义Liveness Probe,否则Kubernetes无法感知Pod是否正常运行。
Kubernetes支持如下三种探测机制:
- HTTP GET:向容器发送HTTP GET请求,如果Probe收到2xx或3xx,说明容器是健康的。
- TCP Socket:尝试与容器指定端口建立TCP连接,如果连接成功建立,说明容器是健康的。
- Exec:Probe执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明容器是健康的。
与存活探针对应的还有一个就绪探针(Readiness Probe),下面会单独介绍。
HTTP GET
HTTP GET方式是最常见的探测方法,其具体机制是向容器发送HTTP GET请求,如果Probe收到2xx或3xx,说明容器是健康的,定义方法如下所示。
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
spec:
containers:
- name: liveness
image: nginx:alpine
livenessProbe: # liveness probe
httpGet: # HTTP GET定义
path: /
port: 80
imagePullSecrets:
- name: default-secret
下面测试:
# 创建这个Pod
$ kubectl create -f liveness-http.yaml
pod/liveness-http created
# 查看Pod详情
$ kubectl describe po liveness-http
TCP Socket
TCP Socket尝试与容器指定端口建立TCP连接,如果连接成功建立,说明容器是健康的,定义方法如下所示。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-tcp
spec:
containers:
- name: liveness
image: nginx:alpine
livenessProbe: # liveness probe
tcpSocket:
port: 80
imagePullSecrets:
- name: default-secret
Exec
Exec即执行具体命令,具体机制是Probe执行容器中的命令并检查命令退出的状态码,如果状态码为0则说明健康,定义方法如下所示。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: nginx:alpine
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe: # liveness probe
exec: # Exec定义
command:
- cat
- /tmp/healthy
imagePullSecrets:
- name: default-secret
上面定义在容器中执行 cat /tmp/healthy
命令,如果成功执行并返回0,则说明容器是健康的。上面定义中,30秒后命令会删除/tmp/healthy,这会导致Liveness Probe判定Pod处于不健康状态,然后会重启容器。
Label
Label:组织Pod的利器
当资源变得非常多的时候,如何分类管理就非常重要了,Kubernetes提供了一种机制来为资源分类,那就是Label(标签)。Label非常简单,但是却很强大,Kubernetes中几乎所有资源都可以用Label来组织。
Label的具体形式是key-value的标记对,可以在创建资源的时候设置,也可以在后期添加和修改。
以Pod为例,当Pod变得多起来后,就显得杂乱且难以管理,如下图所示:

如果我们为Pod打上不同标签,那情况就完全不同了,如下图所示

添加Label
Label的形式为key-value形式,使用非常简单,如下,为Pod设置了app=nginx和env=prod两个Label。
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels: # 为Pod设置两个Label
app: nginx
env: prod
spec:
containers:
Pod有了Label后,在查询Pod的时候带上–show-labels就可以看到Pod的Label。
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 50s app=nginx,env=prod
还可以使用-L只查询某些固定的Label
$ kubectl get pod -L app,env
NAME READY STATUS RESTARTS AGE APP ENV
nginx 1/1 Running 0 1m nginx prod
对已存在的Pod,可以直接使用kubectl label命令直接添加Label
$ kubectl label pod nginx creation_method=manual
pod/nginx labeled
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 50s app=nginx, creation_method=manual,env=prod
修改Label
对于已存在的Label,如果要修改的话,需要在命令中带上–overwrite,如下所示。
$ kubectl label pod nginx env=debug --overwrite
pod/nginx labeled
命名空间
Label虽然好,但只用Label的话,那Label会非常多,有时候会有重叠,而且每次查询之类的动作都带一堆Label非常不方便。Kubernetes提供了Namespace来做资源组织和划分,使用多Namespace可以将包含很多组件的系统分成不同的组。Namespace也可以用来做多租户划分,这样多个团队可以共用一个集群,使用的资源用Namespace划分开。
不同的Namespace下的资源名称可以相同,Kubernetes中大部分资源可以用Namespace划分,不过有些资源不行,例如Node、PV等,它们属于全局资源,不属于某一个Namespace,后面会说明。
通过如下命令可以查询到当前集群下的Namespace。
$ kubectl get ns
NAME STATUS AGE
default Active 36m
kube-node-realease Active 36m
kube-public Active 36m
kube-system Active 36m
到目前为止,我们都是在 default
Namespace下操作,当使用kubectl get 而不指定Namespace时,默认为default 。可以将开发环境、测试环境的业务分别放在不同的命名空间。命名空间按创建类型分为两大类:集群默认创建、用户创建。
集群默认创建的:集群在启动时会默认创建4个命名空间:
- default
- kube-public
- kube-system
- kube-node-lease
default:所有未指定Namespace的对象都会被分配在default命名空间。
kube-public:此命名空间下的资源可以被所有人访问(包括未认证用户),用来部署公共插件、容器模板等。
kube-system:所有由Kubernetes系统创建的资源都处于这个命名空间。
kube-node-lease:
每个节点在该命名空间中都有一个关联的“Lease”对象,该对象由节点定期更新。NodeStatus和NodeLease都被视为来自节点的心跳,在v1.13之前的版本中,节点的心跳只有NodeStatus,NodeLease特性从v1.13开始引入。NodeLease比NodeStatus更轻量级,该特性在集群规模扩展性和性能上有明显提升。
用户创建的:
用户可以按照需要创建命名空间,例如开发环境、联调环境和测试环境分别创建对应的命名空间。或者按照不同的业务创建对应的命名空间,例如系统若分为登录和游戏服务,可以分别创建对应命名空间。
创建Namespace
使用如下方式定义Namespace:
apiVersion: v1
kind: Namespace
metadata:
name: custom-namespace
使用kubectl命令创建
$ kubectl create -f custom-namespace.yaml
namespace/custom-namespace created
您还可以使用kubectl create namespace 命令创建。
$ kubectl create namespace custom-namespace
namespace/custom-namespace created
设置资源配额及限制
默认情况下,运行中的Pod可以无限制的使用Node节点上的CPU和内存,这意味着任意一个Pod都可以无节制地使用集群的计算资源,某个命名空间的Pod可能会耗尽集群的所有资源。
kubernetes在一个物理集群上提供了多个虚拟集群,这些虚拟集群被称为命名空间。命名空间可用于多种工作用途,满足多用户的使用需求,通过为每个命名空间配置资源额度可以有效限制资源滥用,从而保证集群的可靠性。
可以为命名空间配置包括CPU、内存、Pod数量等资源的额度,更多信息请参见Resource Quotas。
Namespace的隔离说明
Namespace只能做到组织上划分,对运行的对象来说,它不能做到真正的隔离。举例来说,如果两个Namespace下的Pod知道对方的IP,而Kubernetes依赖的底层网络没有提供Namespace之间的网络隔离的话,那这两个Pod就可以互相访问。
可观测性
Metrics
Dashboard
Dashboard 是一个 Kubernetes 的 Web 控制台界面。
Prometheus
部署 Prometheus
如果要手动在 Kubernetes 中处理安装 Prometheus 的每一个细节还是挺麻烦的,在官方的Kube-Prometheus项目里提供了明确的操作步骤。不过,如果只是通过 Prometheus Operator 的 Bundle 包安装 Prometheus 则非常简单。
首先从以下地址中获取 Prometheus Operator 的源码:
$ git clone https://github.com/prometheus-operator/prometheus-operator.git
安装里面的bundle.yaml
,然后就完成了:
$ kubectl apply -f bundle.yaml
卸载时,同样根据bundle.yaml
删除即可:
$ kubectl delete -f bundle.yaml
弹性伸缩
前面介绍了Deployment这类控制器来控制Pod的副本数量,通过调整replicas 的大小就可以达到给应用手动扩缩容的目的。但是在某些实际场景下,手动调整一是繁琐,二是速度没有那么快,尤其是在应对流量洪峰需要快速弹性时无法做出快速反应。
Kubernetes支持Pod和集群节点的自动弹性伸缩,通过设置弹性伸缩规则,当外部条件(如CPU使用率)达到一定条件时,根据规则自动伸缩Pod和集群节点。
Metrics Server 和 Prometheus
想要做到自动弹性伸缩,先决条件就是能感知到各种运行数据,例如集群节点、Pod、容器的CPU、内存使用率等等。而这些数据的监控能力Kubernetes也没有自己实现,而是通过其他项目来扩展Kubernetes的能力。
- Metrics Server是Kubernetes集群范围资源使用数据的聚合器。Metrics Server从kubelet公开的Summary API中采集度量数据,能够收集包括了Pod、Node、容器、Service等主要Kubernetes核心资源的度量数据,且对外提供一套标准的API。
- Prometheus是一套开源的系统监控报警框架,能够采集丰富的Metrics(度量数据),目前已经基本是Kubernetes的标准监控方案。
使用HPA(Horizontal Pod Autoscaler)配合Metrics Server可以实现基于CPU和内存的自动弹性伸缩,再配合Prometheus还可以实现自定义监控指标的自动弹性伸缩。
HPA工作机制
HPA(Horizontal Pod Autoscaler)是用来控制Pod水平伸缩的控制器,HPA周期性检查Pod的度量数据,计算满足HPA资源所配置的目标数值所需的副本数量,进而调整目标资源(如Deployment)的replicas字段。

HPA可以配置单个和多个度量指标,配置单个度量指标时,只需要对Pod的当前度量数据求和,除以期望目标值,然后向上取整,就能得到期望的副本数。例如有一个Deployment控制有3个Pod,每个Pod的CPU使用率是70%、50%、90%,而HPA中配置的期望值是50%,计算期望副本数=(70 + 50 + 90)/50 = 4.2,向上取整得到5,即期望副本数就是5。
如果是配置多个度量指标,则会分别计算单个度量指标的期望副本数量,然后取其中最大值,就是最终的期望副本数量。
使用HPA
创建一个HPA,期望CPU的利用率为70%,副本数的范围是1-10
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: scale
namespace: default
spec:
maxReplicas: 10 # 目标资源的最大副本数量
minReplicas: 1 # 目标资源的最小副本数量
metrics: # 度量指标,期望CPU的利用率为70%
- resource:
name: cpu
targetAverageUtilization: 70
type: Resource
scaleTargetRef: # 目标资源
apiVersion: apps/v1
kind: Deployment
name: nginx-deployment
创建HPA后查看
$ kubectl create -f hpa.yaml
horizontalpodautoscaler.autoscaling/celue created
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
scale Deployment/nginx-deployment 0%/70% 1 10 4 18s
可以看到,TARGETS的期望值是70%,而实际是0%,这就意味着HPA会做出缩容动作,期望副本数量=(0+0+0+0)/70=0,但是由于最小副本数为1,所以Pod数量会调整为1。等待一段时间,可以看到Pod数量变为1。
Cluster AutoScaler
HPA是针对Pod级别的,但是如果集群的资源不够了,那就只能对节点进行扩容了。集群节点的弹性伸缩本来是一件非常麻烦的事情,但是好在现在的集群大多都是构建在云上,云上可以直接调用接口添加删除节点,这就使得集群节点弹性伸缩变得非常方便。
Cluster Autoscaler是Kubernetes提供的集群节点弹性伸缩组件,根据Pod调度状态及资源使用情况对集群的节点进行自动扩容缩容。由于要调用云上接口实现弹性伸缩,这就使得在不同环境上的实现与使用各不相同,这里不详细介绍。
安全管理
Kubernetes中所有的访问,无论外部内部,都会通过API Server处理,访问Kubernetes资源前需要经过认证与授权。
- Authentication:用于识别用户身份的认证,Kubernetes分外部服务帐号和内部服务帐号,采取不同的认证机制。
- Authorization:用于控制用户对资源访问的授权,对访问的授权目前主要使用RBAC机制。

ServiceAccount
ServiceAccount同样是Kubernetes中的资源,与Pod、ConfigMap类似,且作用于独立的命名空间,也就是ServiceAccount是属于命名空间级别的,创建命名空间时会自动创建一个名为default的ServiceAccount。
查看ServiceAccount 列表
$ kubectl get sa
NAME SECRETS AGE
default 1 30d
查看ServiceAccount 详情:
$ kubectl describe sa default
Name: default
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: default-token-vssmw
Tokens: default-token-vssmw
Events: <none>
创建ServiceAccount:
$ kubectl create serviceaccount sa-example
serviceaccount/sa-example created
在Pod中使用ServiceAccount:
apiVersion: v1
kind: Pod
metadata:
name: sa-example
spec:
serviceAccountName: sa-example
containers:
- image: nginx:alpine
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
RBAC
Kubernetes中完成授权工作的就是RBAC机制,RBAC授权规则是通过四种资源来进行配置。
- Role:角色,其实是定义一组对Kubernetes资源(命名空间级别)的访问规则。
- RoleBinding:角色绑定,定义了用户和角色的关系。
- ClusterRole:集群角色,其实是定义一组对Kubernetes资源(集群级别,包含全部命名空间)的访问规则。
- ClusterRoleBinding:集群角色绑定,定义了用户和集群角色的关系。
Role和ClusterRole指定了可以对哪些资源做哪些动作,RoleBinding和ClusterRoleBinding将角色绑定到特定的用户、用户组或ServiceAccount上。如下图所示

安装部署
我们可以使用两种方式安装K8s集群:
- kubeadm 方式
- 二进制方式
kubeadm 是一个简单易用的安装工具,可用于快速搭建Kubernetes集群,目前是比较方便和推荐的方式。二进制安装方式调试Bug比较方便,有助于清楚了解K8s组件的原理,对于全面理解Kubernetes会有帮助。
本文基于 kubeadm 方式进行集群部署。
初始化集群前的准备
修改 host文件
修改 host文件:
cat <<EOF >> /etc/hosts
192.168.88.4 k8s-master01
192.168.88.5 k8s-node01
EOF
配置主机名
hostnamectl set-hostname k8s-master01
关闭Swap分区
Kubernetes 就在它的文档中明确声明了它默认不支持Swap 分区,在未关闭 Swap 分区的机器中,集群将直接无法启动。关闭 Swap 的命令为:
swapoff -a
上面这个命令是一次性的,只在当前这次启动中生效,要彻底关闭 Swap 分区,使用以下命令直接完成修改:
# 备份一下
yes | cp /etc/fstab /etc/fstab_bak
# 进行修改
cat /etc/fstab_bak | grep -v swap > /etc/fstab
统一 cgroup
由于 Kubernetes 与 Docker 默认的 cgroup(资源控制组)驱动程序并不一致,Kubernetes 默认为systemd
,而 Docker 默认为cgroupfs
。在这里我们要修改 Docker 或者 Kubernetes 其中一个的 cgroup 驱动,以便两者统一。
对于使用 systemd 作为引导系统的 Linux 的发行版,使用 systemd 作为 Docker 的 cgroup 驱动程序可以服务器节点在资源紧张的情况表现得更为稳定。这里选择修改各个节点上 Docker 的 cgroup 驱动为systemd
,具体操作为编辑(无则新增)/etc/docker/daemon.json
文件,加入以下内容即可:
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
然后重新启动 Docker 容器:
systemctl daemon-reload && systemctl restart docker
从 v1.22 开始,在使用 kubeadm 创建集群时,如果用户没有在 KubeletConfiguration
下设置 cgroupDriver
字段,kubeadm 默认使用 systemd
。
如果你将 systemd
配置为 kubelet 的 cgroup 驱动,你也必须将 systemd
配置为容器运行时的 cgroup 驱动。kubelet 与 容器运行时的cgroup 驱动必须保持一致。
禁用 SELinux
# 将 SELinux 设置为 permissive 模式(相当于将其禁用)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
设置yum源为国内
# AnolisOS 8设置默认yum源
wget -O /etc/yum.repos.d/AnolisOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
开放防火墙端口
# 永久打开3690/TCP端口,必须reload才能生效
firewall-cmd --permanent --add-port=6443/tcp && \
firewall-cmd --permanent --add-port=10250/tcp && firewall-cmd --reload
如果不开放这几个端口,在初始化Kubernates集群时会警告:
[WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster may not function correctly
安装容器运行时
docker 作为容器运行时
设置yum源为国内:
# 设置 docker yum源
wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装
# 在AnolisOS 8 上安装docker
yum install -y docker-ce-20.10.9-3.el8
# 启动docker服务
systemctl start docker
# 设置自启动
systemctl enable docker
Containerd 作为容器运行时
Dockershim 在 Kubernetes v1.24 版本已经被移除。如果安装的Kubernetes 为1.24或更高版本,需要选择Containerd 或 CRI-O 作为容器运行时;如果版本低于1.24,Runtime选择Docker 和 Containerd均可。
Kubelet 之前使用的是一个名为 dockershim 的模块,用以实现对 Docker 的 CRI 支持。简而言之,Docker 并不支持 CRI(容器运行时接口)这一 Kubernetes 运行时 API,而 Kubernetes 用户一直以来所使用的其实是名为 dockershim 的桥接服务。
Docker 为什么会被弃用?
第一,Kubernetes 只能与 CRI 通信,因此要与 Docker 通信,就必须使用桥接服务 dockershim 。
第二,在 整个架构中,k8s并不需要docker服务的全部功能,Docker 网络与存储卷都被排除在外。而这些用不到的功能本身就可能带来安全隐患。事实上,你拥有的功能越少,攻击面也就越小。因此,我们需要考虑使用替代方案,即 CRI 运行时。
由于Kubernetes 1.24以上版本将不再直接支持Docker,因此需要将Kubernetes的Runtime改为Containerd。因为安装Docker时会自动安装Containerd,并且后面的课程也要使用到Docker,所以还是在每个节点安装Docker。
安装K8s系统组件
你需要在每台机器上安装以下的软件包:
kubeadm
:用来初始化集群的指令。kubelet
:在集群中的每个节点上用来启动 Pod 和容器等。kubectl
:用来与集群通信的命令行工具。
kubeadm
kubeadm 用于轻松启动安全的 Kubernetes 集群
创建具有一个控制平面节点的双机集群
(控制集群)和一个工作节点
(您的工作负载,如 Pod 和 Deployments 运行的地方)。
步骤:
- 在第一台机器上部署控制平面
- 在第二台机器上部署工作负载
- 然后,您可以根据需要在任意多台其他机器上重复第二步。
命令:
命令 | 说明 |
---|---|
init | 运行此命令以设置 Kubernetes 控制平面 |
join | 在任何你想加入现有集群的机器上运行这个 |
config | 管理 kubeadm 集群的配置,持久保存在集群的 ConfigMap 中 |
version | 打印 kubeadm 的版本 |
certs | 与处理 kubernetes 证书相关的命令 |
reset | 尽最大努力恢复通过“kubeadm init”或“kubeadm join”对此主机所做的更改 |
upgrade | 使用此命令将您的集群顺利升级到更新版本 |
常用命令:
kubeadm reset -f
kubectl get nodes --show-labels
kubectl get pod --all-namespaces
kubectl logs -f -n kube-system
kubelet
kubelet
是 Kubernetes 中的一个核心组件,它在每个节点上运行并管理该节点上的容器和 Pod。
作为 Kubernetes 节点上的代理程序,kubelet
负责与 Kubernetes 控制平面通信,并根据指令和配置来维护节点上的容器和 Pod 状态。它在节点上运行的主要职责包括:
Pod 启动和监控:
kubelet
负责启动和监控在节点上运行的 Pod。它从 Kubernetes API 服务器接收要在节点上运行的 Pod 的指令,并确保这些 Pod 按照规定的状态和配置运行。如果有任何问题,kubelet
会尝试重新启动故障的容器或 Pod。容器运行时管理:
kubelet
与容器运行时(如 Docker、containerd 等)交互,通过容器运行时启动、停止和监控容器的运行状态。它会监测容器的健康状态、资源使用情况,并将这些信息反馈给 Kubernetes 控制平面。资源管理和调度:
kubelet
负责监控节点的资源使用情况,并将这些信息报告给 Kubernetes 控制平面。基于这些信息,控制平面可以进行资源分配和调度决策,将 Pod 调度到适合的节点上。与其他组件的通信:
kubelet
与其他 Kubernetes 组件进行通信,例如网络插件(CNI 插件)和存储卷插件(CSI 插件)。它通过与这些组件的交互来确保容器网络和存储的正确配置和运行。
总之,kubelet
在 Kubernetes 集群中的每个节点上运行,并负责节点级别的容器和 Pod 管理。它与 Kubernetes 控制平面交互,将节点上的状态报告给控制平面,并执行相应的操作以维持集群的正常运行状态。
kubeadm 不能帮你安装或者管理 kubelet
或 kubectl
, 所以你需要确保它们与通过 kubeadm 安装的控制平面的版本相匹配。 如果不这样做,则存在发生版本偏差的风险,可能会导致一些预料之外的错误和问题。
# 设置 kubernetes yum源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
EOF
# 安装Kubernates 组件
yum install -y kubelet-1.23.17-0 kubeadm-1.23.17-0 kubectl-1.23.17-0
注:使用 yum –showduplicates list kubelet 可以查看可用的kubelet 版本
集群初始化配置文件(可选)
使用如下命令自动生成 kubeadm 的集群初始化配置文件 kubeadm-config.yaml
mkdir k8s && cd k8s
kubeadm config print init-defaults > kubeadm-config.yaml
kubeadm-config.yaml 文件内容:
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: abcdef.0123456789abcdef
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.88.4
bindPort: 6443
nodeRegistration:
criSocket: /var/run/dockershim.sock
imagePullPolicy: IfNotPresent
name: node
taints: null
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: 192.168.88.8:16443
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: registry.k8s.io
kind: ClusterConfiguration
kubernetesVersion: 1.23.17
networking:
dnsDomain: cluster.local
podSubnet: 172.16.0.0/12
serviceSubnet: 192.168.0.0/16
scheduler: {}
这是一个Kubernetes集群初始化配置文件,用于使用kubeadm工具初始化一个Kubernetes集群。下面是对配置文件内容的详细讲解:
apiVersion: kubeadm.k8s.io/v1beta3
: 指定了配置文件使用的Kubeadm API版本。bootstrapTokens
: 定义了引导令牌(bootstrap token),用于节点的引导和身份验证。groups
: 指定了引导令牌所属的组,这里使用了默认组system:bootstrappers:kubeadm:default-node-token
。token
: 定义了引导令牌的值。ttl
: 指定了引导令牌的生存时间。usages
: 定义了引导令牌的用途,包括签名(signing)和身份验证(authentication)。
kind: InitConfiguration
: 指定了配置文件的类型为初始化配置,用于初始化集群。localAPIEndpoint
: 定义了本地API端点的配置。advertiseAddress
: 指定了API服务器广告的地址,这里是10.0.0.201
。bindPort
: 指定了API服务器绑定的端口,这里是6443
。
nodeRegistration
: 定义了节点注册的配置。criSocket
: 指定了容器运行时(CRI)的套接字路径。imagePullPolicy
: 指定了镜像拉取策略,这里是如果不存在则拉取(IfNotPresent)。name
: 指定了节点的名称。taints
: 定义了节点的污点,这里是空值(null),表示没有定义污点。
apiServer
: 定义了API服务器的配置。certSANs
: 指定了证书的Subject Alternative Names(SANs),这里包括10.0.0.236
。timeoutForControlPlane
: 指定了控制平面超时时间。
apiVersion: kubeadm.k8s.io/v1beta3
: 指定了配置文件使用的Kubeadm API版本。certificatesDir
: 指定了证书的存储目录。clusterName
: 指定了集群的名称。controlPlaneEndpoint
: 定义了控制平面的终结点,包括IP地址和端口。controllerManager
: 定义了控制器管理器的配置。dns
: 定义了DNS的配置。etcd
: 定义了etcd的配置。
local
: 定义了本地etcd的配置。dataDir
: 指定了etcd的数据目录。
imageRepository
: 指定了镜像仓库的地址。kind: ClusterConfiguration
: 指定了配置文件的类型为集群配置,用于配置
预拉取镜像(可选)
在运行 kubeadm init
之前可以先执行 kubeadm config images pull
来测试与 gcr.io
的连接,kubeadm config images pull尝试是否可以拉取镜像,如果你的服务器再国内,由于某些原因,是无法访问”k8s.gcr.io”, “gcr.io”, “quay.io”.
查询当前版本需要哪些镜像:
# 查看需要的镜像
kubeadm config images list --kubernetes-version v1.23.17
registry.k8s.io/kube-apiserver:v1.23.17
registry.k8s.io/kube-controller-manager:v1.23.17
registry.k8s.io/kube-scheduler:v1.23.17
registry.k8s.io/kube-proxy:v1.23.17
registry.k8s.io/pause:3.6
registry.k8s.io/etcd:3.5.6-0
registry.k8s.io/coredns/coredns:v1.8.6
写脚本获取镜像
# 写脚本获取镜像:(master上执行,node节点上只需要两个镜像和calico的相关镜像)
vi kubeadm-images.sh
# 使用如下脚本下载国内镜像,并修改tag为google的tag
#!/bin/bash
set -e
KUBE_VERSION=v1.23.17
KUBE_PAUSE_VERSION=3.6
ETCD_VERSION=3.5.6-0
CORE_DNS_VERSION=1.8.6
GCR_URL=registry.k8s.io
ALIYUN_URL=registry.cn-hangzhou.aliyuncs.com/google_containers
images=(kube-proxy:${KUBE_VERSION}
kube-scheduler:${KUBE_VERSION}
kube-controller-manager:${KUBE_VERSION}
kube-apiserver:${KUBE_VERSION}
pause:${KUBE_PAUSE_VERSION}
etcd:${ETCD_VERSION}
coredns:${CORE_DNS_VERSION})
for imageName in ${images[@]} ; do
docker pull $ALIYUN_URL/$imageName
docker tag $ALIYUN_URL/$imageName $GCR_URL/$imageName
docker rmi $ALIYUN_URL/$imageName
done
然后执行该脚本就可以提前下载这些镜像,节省初始化时间。
# 修改tag
docker registry.k8s.io/kube-proxy:v1.23.17 registry.k8s.io/kube-proxy:v1.23.17 && \
docker registry.k8s.io/kube-apiserver:v1.23.17 registry.k8s.io/kube-apiserver:v1.23.17 && \
docker registry.k8s.io/kube-scheduler:v1.23.17 registry.k8s.io/kube-scheduler:v1.23.17 && \
docker tag registry.k8s.io/kube-controller-manager:v1.23.17 registry.k8s.io/kube-controller-manager:v1.23.17 && \
docker tag registry.k8s.io/pause:3.6 registry.k8s.io/pause:3.6 && \
docker tag registry.k8s.io/coredns:1.8.6 registry.k8s.io/coredns/coredns:v1.8.6 && \
docker tag registry.k8s.io/etcd:3.5.6-0 registry.k8s.io/etcd:3.5.6-0
#修改完tag后就可以删除掉旧镜像了
docker rmi k8s.gcr.io/kube-proxy:v1.23.17 && \
docker rmi k8s.gcr.io/kube-apiserver:v1.23.17 && \
docker rmi k8s.gcr.io/kube-scheduler:v1.23.17 && \
docker rmi k8s.gcr.io/kube-controller-manager:v1.23.17 && \
docker rmi k8s.gcr.io/pause:3.6 && \
docker rmi k8s.gcr.io/coredns:1.8.6 && \
docker rmi k8s.gcr.io/etcd:3.5.6-0
初始化集群控制平面
#
kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address 192.168.88.4 --kubernetes-version=v1.23.17 --image-repository=registry.aliyuncs.com/google_containers
# 或使用配置文件初始化
kubeadm init --config ./kubeadm-config.yaml --upload-certs
完整的输出结果:
[root@k8s-master01 k8s]# kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address 192.168.88.4 --kubernetes-version=v1.23.17 --image-repository=registry.aliyuncs.com/google_containers
[init] Using Kubernetes version: v1.23.17
[preflight] Running pre-flight checks
[WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster may not function correctly
[WARNING Service-Docker]: docker service is not enabled, please run 'systemctl enable docker.service'
[WARNING FileExisting-tc]: tc not found in system path
[WARNING Service-Kubelet]: kubelet service is not enabled, please run 'systemctl enable kubelet.service'
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-master01 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.88.4]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-master01 localhost] and IPs [192.168.88.4 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-master01 localhost] and IPs [192.168.88.4 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 7.005280 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.23" in namespace kube-system with the configuration for the kubelets in the cluster
NOTE: The "kubelet-config-1.23" naming of the kubelet ConfigMap is deprecated. Once the UnversionedKubeletConfigMap feature gate graduates to Beta the default name will become just "kubelet-config". Kubeadm upgrade will handle this transition transparently.
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-master01 as control-plane by adding the labels: [node-role.kubernetes.io/master(deprecated) node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-master01 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: 2mg11q.zfogmlmo7v4165gf
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.88.4:6443 --token 2mg11q.zfogmlmo7v4165gf \
--discovery-token-ca-cert-hash sha256:d88bb2352f547cbdddc4ede27575448b636fc60450ac7f36442e72be7dfad37d
使用 Kubernetes 前需要为当前用户先配置好 admin.conf 文件。切换至需配置的用户后,进行如下操作:
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubeadm join 192.168.88.4:6443 --token 0ayw8v.354ztsn34kdo31ng \
--discovery-token-ca-cert-hash sha256:9bfecac4250aba9373bd570d07ead3b9e9d771c9fea2b8be1c72945f199916ce
可以通过cluster-info
和get nodes
子命令来查看一下集群的状态
启动kubelet
# 查看服务状态
systemctl status kubelet
# 查看实时日志
journalctl -fu kubelet
kubectl 命令自动补全
开启 kubectl 命令的自动补全功能:
# for centos
yum -y install bash-completion
在文件 ~/.bashrc 中导入(source)补全脚本:
$ echo 'source <(kubectl completion bash)' >>~/.bashrc
退出当前会话:
$ logout
生效配置:
$ source ~/.bashrc
三个网段
在 Kubernetes 中,通常有三个重要的网络段(CIDR)与集群相关:
Pod 网络段(Pod Network CIDR):这是用于分配给 Pod 的 IP 地址范围。Pod 是 Kubernetes 中最小的调度单位,每个 Pod 都有自己的 IP 地址。Pod 网络段用于为集群中的 Pod 分配唯一的 IP 地址,并确保 Pod 之间可以相互通信。默认情况下,Pod 网络段是从
10.244.0.0/16
子网中分配的,但可以根据需要进行自定义配置。Service 网络段(Service Cluster IP Range):这是用于分配给 Kubernetes Service 的 IP 地址范围。Service 是一种抽象的服务资源,它提供了一种稳定的网络端点,用于访问一组运行在多个 Pod 上的应用。Service 网络段用于为 Service 分配一个虚拟的 IP 地址,以及为 Service 提供的每个端口分配 Cluster IP。默认情况下,Service 网络段是从
10.96.0.0/12
子网中分配的,但也可以进行自定义配置。集群网络段(Cluster Network CIDR):这是用于集群内部通信的 IP 地址范围。集群网络段用于分配给各个节点(Node)以及其他 Kubernetes 组件,如 kube-proxy、kube-dns 等。它们使用这个网络段来进行节点之间的通信、网络策略的实施等。默认情况下,集群网络段是从
10.244.0.0/16
子网中分配的,但可以根据需要进行自定义配置。
这些网络段的选择和配置可以根据实际需求进行调整和更改,以满足特定集群的网络要求。需要注意的是,确保这些网络段不会与已有的网络冲突,并且在集群中的各个节点上正确配置和路由这些网络段,以确保集群正常运行和通信。
安装 CNI 插件
CNI 即“容器网络接口”,在 2016 年,CoreOS 发布了 CNI 规范。2017 年 5 月,CNI 被 CNCF 技术监督委员会投票决定接受为托管项目,从此成为不同容器编排工具(Kubernetes、Mesos、OpenShift)可以共同使用的、解决容器之间网络通讯的统一接口规范。
部署 Kubernetes 时,我们可以有两种网络方案使得以后受管理的容器之间进行网络通讯:
- 使用 Kubernetes 的默认网络
- 使用 CNI 及其插件
Kubernetes 目前支持的 CNI 插件有:Calico、Flannel、Kube-router、Cilium、Weave Net 等多种,每种网络提供了不同的特性。
这里直接使用 Flannel 是较为合适的,它是最精简的 CNI 插件之一,没有安全特性的支持,主机压力小,安装便捷,效率也不错,使用以下命令安装 Flannel 网络:
curl --insecure -sfL https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml | kubectl apply -f -
开始安装网络插件 calico
# 在master节点上下载calico.yaml
curl https://docs.projectcalico.org/v3.18/manifests/calico.yaml -O && \
mv calico.yaml calico-3.18.yaml
# 替换默认pod-cidr
POD_CIDR="10.244.0.0" && \
sed -i -e "s?192.168.0.0/16?$POD_CIDR/16?g" calico-3.18.yaml
# 启动服务
kubectl apply -f calico-3.18.yaml
docker pull calico/cni:v3.18.1 && \
docker pull calico/pod2daemon-flexvol:v3.18.1 && \
docker pull calico/node:v3.18.1
flannel默认pod网络地址10.244.0.0/16;calico默认pod 网络地址192.168.0.0/16
移除 Master 节点上的污点(可选)
污点(Taint)是 Kubernetes Pod 调度中的概念,在这里通俗地理解就是 Kubernetes 决定在集群中的哪一个节点建立新的容器时,要先排除掉带有特定污点的节点,以避免容器在 Kubernetes 不希望运行的节点中创建、运行。默认情况下,集群的 Master 节点是会带有特定污点的,以避免容器分配到 Master 中创建。
但对于许多学习 Kubernetes 的同学来说,并没有多么宽裕的机器数量,往往是建立单节点集群或者最多只有两、三个节点,这样 Master 节点不能运行容器就显得十分浪费了。如需移除掉 Master 节点上所有的污点,在 Master 节点上执行以下命令即可:
kubectl taint nodes --all node-role.kubernetes.io/master-
在Master节点移除污点之后,就可以在Master上部署容器了。
将其他 Node 节点加入到 Kubernetes 集群中
在安装 Master 节点时候,输出的最后一部分内容会类似如下所示:
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 10.3.7.5:6443 --token ejg4tt.y08moym055dn9i32 \
--discovery-token-ca-cert-hash sha256:9d2079d2844fa2953d33cc0da57ab15f571e974aa40ccb50edde12c5e906d513
这部分内容是告诉用户,集群的 Master 节点已经建立完毕,其他节点的机器可以使用“kubeadm join”命令加入集群。这些机器只要完成 kubeadm、kubelet、kubectl 的安装即可、其他的所有步骤,如拉取镜像、初始化集群等,都不需要去做,就是可以使用该命令加入集群了。需要注意的是,该 Token 的有效时间为 24 小时,如果超时,使用以下命令重新获取:
#查看已有token
kubeadm token list
#生成新的token
kubeadm token create --print-join-command
其他
k3s
一键安装命令
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -
安装过程中可能会遇到这样的错误:(container-selinux版本不够)
[INFO] Finding available k3s-selinux versions
Error:
Problem: cannot install the best candidate for the job
- nothing provides container-selinux >= 3:2.191.0-1 needed by k3s-selinux-1.6-1.el9.noarch from rancher-k3s-common-stable
(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)
解决方案,安装即可:
dnf install -y https://mirrors.nju.edu.cn/centos-stream/9-stream/AppStream/x86_64/os/Packages/container-selinux-2.235.0-1.el9.noarch.rpm
重新执行安装命令,如果看到下面的输出,则表示安装成功;
Complete!
[INFO] Creating /usr/local/bin/kubectl symlink to k3s
[INFO] Creating /usr/local/bin/crictl symlink to k3s
[INFO] Skipping /usr/local/bin/ctr symlink to k3s, command exists in PATH at /usr/bin/ctr
[INFO] Creating killall script /usr/local/bin/k3s-killall.sh
[INFO] Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO] env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO] systemd: Creating service file /etc/systemd/system/k3s.service
[INFO] systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO] systemd: Starting k3s
执行 k3s
或 kubectl
命令验证是否安装成功。
配置镜像加速器
cat >> /etc/rancher/k3s/registries.yaml <<EOF
mirrors:
"docker.io":
endpoint:
- "https://docker-0.unsee.tech"
- "https://docker.m.daocloud.io"
EOF
# 重启 k3s
systemctl restart k3s
Minikube
Kubernetes是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用。而 minikube是一个工具,让用户能够在本地轻松运行一个单节点的Kubernetes集群,主要用于开发和测试。
Minikube VS Kubernetes
相同点
- 基于相同的 Kubernetes 核心
- Minikube 是 Kubernetes 的轻量级实现,完全兼容 Kubernetes API。所有在 Minikube 上运行的
kubectl
命令、YAML 配置、Deployment/Service 等资源,与生产级 Kubernetes 集群的行为一致。
- 支持相同的核心功能
- Pod、Deployment、Service、ConfigMap、Secret、PersistentVolume(PV)、Namespace 等核心资源对象均可在 Minikube 中使用。
- 支持
kubectl
、helm
、kustomize
等工具链。
- 开发与学习用途
- 两者都可用于学习 Kubernetes 概念和验证应用部署逻辑。
不同点
定位与使用场景
特性 | Minikube | Kubernetes(生产集群) |
---|---|---|
目标用户 | 开发者、学习者 | 运维团队、企业生产环境 |
用途 | 本地开发、测试、调试 | 大规模容器编排、生产负载管理 |
集群规模 | 单节点集群(仅 1 个 Node) | 多节点集群(Master + 多个 Node) |
资源消耗 | 低(适合笔记本电脑) | 高(需要专用服务器或云资源) |
Minikube 和生产集群有何不同?
- Minikube 将所有控制平面组件(如 etcd、API Server)运行在单节点上,而生产集群通常为多节点高可用架构。
- 存储和网络插件在 Minikube 中被简化(如
storage-provisioner
仅用于本地开发)。
建议:
用 Minikube 做本地开发和测试,用生产级 Kubernetes(如 EKS、AKS、自建集群)部署实际业务。两者的 YAML 配置可高度复用,但需注意网络、存储等环境差异。
驱动 Driver
Minikube 可使用的驱动有多个可选项,其中
Windows 环境下,下面两个是官方推荐的首选:
Linux 环境下,下面两个是官方推荐的首选:
常用命令
集群管理
# 使用默认驱动(通常是 Docker)启动集群
minikube start
# 指定驱动(如 Hyper-V、Docker)
minikube start --driver=hyperv
minikube start --driver=docker
# 指定资源(CPU、内存)
minikube start --cpus=4 --memory=8192
minikube kubectl -- version #查看 Kubernetes 版本
minikube status # 查看集群状态
minikube ip # 获取集群 IP
minikube dashboard # 打开 Kubernetes Dashboard
minikube service list # 列出所有服务
minikube config view # 查看集群配置
minikube config set driver docker # 设置默认驱动
# 调整资源限制
minikube config set cpus 4
minikube config set memory 8192
minikube pause # 暂停集群
minikube unpause # 恢复集群
minikube stop # 停止集群
minikube delete # 删除集群
minikube update-check # 更新 Minikube
# 通用选项:打印详细日志,这个对调试问题非常有用
minikube start --alsologtostderr -v=3
资源管理
# 构建本地镜像
docker build -t my-app:latest .
# 将镜像加载到 Minikube
minikube image load my-app:latest
# 访问 LoadBalancer 服务
minikube tunnel # 启动隧道(模拟 LoadBalancer)
kubectl get svc # 查看服务 IP
#挂载本地目录
minikube mount /path/to/local:/minikube-mount
#查看组件状态
minikube addons list # 列出所有插件
minikube addons enable ingress # 启用 Ingress 插件
# 查看 Minikube 日志
minikube logs
# 进入 Minikube 虚拟机
minikube ssh
多集群管理
# 创建新集群
minikube start -p my-cluster
# 切换集群
minikube profile my-cluster
# 列出所有集群
minikube profile list
最佳实践
# windows环境下,启动集群
minikube start --driver=hyperv --cpus=4 --memory=8192 --image-mirror-country=cn --alsologtostderr -v=3
# 查看pod
> kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-76fccbbb6b-27tgc 1/1 Running 0 117s
kube-system coredns-76fccbbb6b-nbzvx 1/1 Running 0 116s
kube-system etcd-minikube 1/1 Running 1 (2m24s ago) 11m
kube-system kube-apiserver-minikube 1/1 Running 1 (2m14s ago) 11m
kube-system kube-controller-manager-minikube 1/1 Running 1 (2m24s ago) 11m
kube-system kube-proxy-wx46t 1/1 Running 0 117s
kube-system kube-scheduler-minikube 1/1 Running 1 (2m24s ago) 11m
kube-system storage-provisioner 1/1 Running 0 115s
注:上面的
--alsologtostderr -v=3
选项是用来输出启动日志的,方便排查问题。我在Win11环境下启动多次都失败,最后试了好几次才成功,应该和国内的网络环境有关系。
系统组件
为何这些组件都在 kube-system
命名空间?
kube-system
是 Kubernetes 保留的命名空间,专门用于运行集群级别的核心组件。- 用户工作负载应部署在其他命名空间(如
default
)。
1.CoreDNS
- 作用:Kubernetes 的 集群 DNS 服务,负责为 Pod 和 Service 提供域名解析。
- 功能:
- 将 Service 名称(如
redis.default.svc.cluster.local
)解析为对应的 ClusterIP。 - 支持自定义 DNS 记录(通过
Corefile
配置)。
- 将 Service 名称(如
- 为什么需要:Pod 之间通过 Service 名称通信时依赖 DNS 解析。
2. etcd
- 作用:Kubernetes 的 分布式键值存储数据库,存储集群所有状态数据。
- 存储内容:
- 集群配置(如 Nodes、Pods、Secrets、ConfigMaps 等元数据)。
- 资源对象的期望状态(Desired State)。
- 为什么需要:Kubernetes 控制平面依赖 etcd 实现数据持久化和一致性。
3. kube-apiserver
- 作用:Kubernetes 的 API 服务器,是集群的“前端”和控制平面的核心入口。
- 功能:
- 接收并验证所有 REST API 请求(如
kubectl
命令)。 - 协调集群状态(将用户指令同步到 etcd)。
- 接收并验证所有 REST API 请求(如
- 为什么需要:所有组件(包括用户)必须通过 API Server 与集群交互。
4. kube-controller-manager
- 作用:运行 控制器(Controller) 的守护进程,负责维护集群的期望状态。
- 核心控制器:
- Node Controller:监控 Node 状态。
- Deployment Controller:管理 Pod 副本数。
- Service Controller:创建/更新 LoadBalancer 和 NodePort。
- 为什么需要:确保实际状态与用户定义的期望状态一致。
5. kube-scheduler
- 作用:Pod 的 调度器,决定将 Pod 分配到哪个 Node 上运行。
- 调度策略:
- 资源需求(CPU/内存)。
- 亲和性(Affinity)、污点(Taint)等约束。
- 为什么需要:优化资源利用并满足 Pod 的运行条件。
6. kube-proxy
- 作用:维护集群的 网络规则,实现 Service 的流量转发。
- 功能:
- 为 Service 创建 iptables/IPVS 规则,将请求转发到后端 Pod。
- 支持 ClusterIP、NodePort、LoadBalancer 等 Service 类型。
- 为什么需要:确保 Pod 之间的网络通信和外部访问。
7. storage-provisioner
- 作用:Minikube 的 动态存储卷配置器,自动创建 PersistentVolume(PV)。
- 功能:
- 当创建 PersistentVolumeClaim(PVC)时,自动创建本地存储卷。
- 仅限 Minikube 环境使用(生产环境需替换为 CSI 驱动)。
- 为什么需要:简化本地开发中的持久化存储管理。
Helm 包管理器
Helm 是 Kubernetes 包管理器。Helm 是 CNCF 的毕业项目,由 Helm 社区维护。
Helm和Kubernetes之间的关系可以如下类比:
- Helm <–> Kubernetes
- Apt <–> Ubuntu
- Yum <–> CentOS
- Pip <–> Python
Containerd 和 CRI-O
Containerd 并非一个全新技术,它是由 Docker 公司开源,作为 docker 的核心组件存在。2016年12月,Docker公司宣布将containerd从Docker Engine中分离,并捐赠到一个新的开源社区独立发展和运营。
containerd并不是直接面向最终用户的,而是主要用于集成到更上层的系统里,比如Swarm, Kubernetes, Mesos等容器编排系统。containerd以Daemon的形式运行在系统上,通过unix domain docket暴露很底层的gRPC API,上层系统可以通过这些API管理机器上的容器。每个containerd只负责一台机器,Pull镜像,对容器的操作(启动、停止等),网络,存储都是由containerd完成。具体运行容器由 runC 负责,实际上只要是符合OCI规范的容器都可以支持。
其工作流程简单来说是这样的:
- Kubernetes 运行一个容器时会调用容器运行时接口(CRI),这个接口的实现有 containerd、CRI-O。
- 容器运行时可以完成容器的创建、运行、销毁等实际工作
- Docker 使用的是 containerd 作为其运行时;Kubernetes 支持 containerd,CRI-O 等多种容器运行时。
- 这些容器运行时都遵循了 OCI 规范,并通过 runc 来实现与操作系统内核交互来完成容器的创建和运行

OCI 和 runc
在 2015 年 6 月, Docker ,CoreOS 和其他一些公司共同成立了 OCI (开放容器计划) 组织,其最主要的内容有两个:
- 容器运行时规范
- 容器镜像规范
Docker 将 runc 运行时捐赠给了 OCI ,作为容器运行时规范的基础实现,托管在GitHub上, 也就是现在大家看到的 runc 了。2016 年 6 月,runc发布 v1.0-rc1版本,经过长达五年的长跑,从 rc1 一直到 rc95,2021 年 6 月,runc发布 v1.0 版本。
扩展接口
Kubernetes 作为云原生应用的基础调度平台,相当于云原生的操作系统,为了便于系统的扩展,Kubernetes 中开放的以下接口,可以分别对接不同的后端,来实现自己的业务逻辑:
- 容器运行时接口(CRI):提供计算资源
- 容器网络接口(CNI):提供网络资源
- 容器存储接口(CSI):提供存储资源
证书
Kubernetes 需要 PKI 证书才能进行基于 TLS 的身份验证。如果你是使用 kubeadm 安装的 Kubernetes, 则会自动生成集群所需的证书。你还可以生成自己的证书。
[root@k8s-master01 k8s]# tree /etc/kubernetes/pki
/etc/kubernetes/pki
├── apiserver.crt
├── apiserver.key
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
├── apiserver-kubelet-client.crt
├── apiserver-kubelet-client.key
├── ca.crt
├── ca.key
├── etcd
│ ├── ca.crt
│ ├── ca.key
│ ├── healthcheck-client.crt
│ ├── healthcheck-client.key
│ ├── peer.crt
│ ├── peer.key
│ ├── server.crt
│ └── server.key
├── front-proxy-ca.crt
├── front-proxy-ca.key
├── front-proxy-client.crt
├── front-proxy-client.key
├── sa.key
└── sa.pub
根证书又名自签名证书,也就是自己给自己颁发的证书。CA(Certificate Authority)被称为证书授权中心,k8s中的ca证书就是根证书。
先分类:
- 密钥对:sa.key、sa.pub
- 根证书:ca.crt、etcd/ca
- 私钥 : ca.key 等
首先其它证书都是由CA根证书颁发的,kubernetes与etcd使用了不同的CA, 很重要的一点是证书是用于客户端校验还是服务端校验。 下面一个一个来看:
根证书
pki/ca.crt
pki/ca.key
你可以使用 check-expiration
子命令来检查证书何时过期
kubeadm certs check-expiration
输出:
[root@k8s-master01 ~]# kubeadm certs check-expiration
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Jun 08, 2024 01:50 UTC 362d ca no
apiserver Jun 08, 2024 01:50 UTC 362d ca no
apiserver-etcd-client Jun 08, 2024 01:50 UTC 362d etcd-ca no
apiserver-kubelet-client Jun 08, 2024 01:50 UTC 362d ca no
controller-manager.conf Jun 08, 2024 01:50 UTC 362d ca no
etcd-healthcheck-client Jun 08, 2024 01:50 UTC 362d etcd-ca no
etcd-peer Jun 08, 2024 01:50 UTC 362d etcd-ca no
etcd-server Jun 08, 2024 01:50 UTC 362d etcd-ca no
front-proxy-client Jun 08, 2024 01:50 UTC 362d front-proxy-ca no
scheduler.conf Jun 08, 2024 01:50 UTC 362d ca no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Jun 06, 2033 01:50 UTC 9y no
etcd-ca Jun 06, 2033 01:50 UTC 9y no
front-proxy-ca Jun 06, 2033 01:50 UTC 9y no
更新证书
Kubernetes证书通常在一年后到期。Kubeadm可以使用kubeadm alpha certs renew
命令更新证书;你应该只在控制平面节点上运行这些命令。
RedHat OpenShift
OpenShift 是一种基于 Kubernetes 的容器应用平台,用于构建、部署和管理容器化应用程序。它由 Red Hat 公司开发和维护,并且是开源的。
OpenShift 提供了一整套工具和功能,使开发人员能够快速、简化地构建和部署应用程序,同时提供可扩展性、弹性和高可用性的运行环境。它的主要特点包括:
容器编排:OpenShift 使用 Kubernetes 作为底层的容器编排引擎,提供了强大的容器调度和管理功能。开发人员可以通过 OpenShift 对容器进行自动化的部署、扩展和管理,从而简化了应用程序的开发和运维。
多租户支持:OpenShift 支持多租户模式,可以在同一个 OpenShift 集群中隔离和管理多个项目或团队的应用程序。每个租户都拥有自己的资源和权限,可以独立地管理和扩展应用程序。
构建与部署:OpenShift 提供了集成的构建和部署功能,支持多种构建策略(如源代码构建、镜像构建)和持续集成/持续部署(CI/CD)流程。开发人员可以根据需求自动构建、测试和部署应用程序,从而实现快速迭代和交付。
监控和日志:OpenShift 提供了集成的监控和日志功能,可以实时监控应用程序的性能指标、容器状态和日志输出。开发人员可以通过 OpenShift 的监控和日志系统来诊断问题、优化应用程序性能。
扩展性和生态系统:OpenShift 具有良好的扩展性和可定制性,支持插件和扩展,以满足不同的业务需求。同时,OpenShift 生态系统也非常丰富,提供了许多预构建的应用程序模板、服务和工具,方便开发人员快速构建和集成应用程序。
总而言之,OpenShift 是一个全面的容器应用平台,通过结合 Kubernetes 的容器编排和管理能力,为开发人员提供了一种简化和加速应用程序开发、部署和管理的解决方案。它在构建和管理容器化应用程序的过程中提供了丰富的功能和工具,以满足企业级应用程序的需求。
OpenShift 与 Kubernetes 的联系和区别
OpenShift 和 Kubernetes 之间存在密切的联系,同时也有一些区别。下面是它们之间的联系和区别:
联系:
基于 Kubernetes:OpenShift 是基于 Kubernetes 的平台,它使用 Kubernetes 作为底层的容器编排和管理引擎。OpenShift 扩展了 Kubernetes 的功能,提供了更高级的开发、部署和管理工具。
容器编排和管理:OpenShift 和 Kubernetes 都专注于容器编排和管理。它们提供了自动化的容器部署、扩展和管理功能,使开发人员能够轻松地构建和管理容器化应用程序。
基础设施抽象化:OpenShift 和 Kubernetes 都致力于抽象化基础设施层。它们将底层的服务器、网络和存储资源进行抽象,使开发人员可以专注于应用程序的开发而不用担心基础设施的细节。
区别:
完整的平台功能:相较于 Kubernetes,OpenShift 是一个更加完整的容器应用平台。它提供了更丰富的功能和工具,包括集成的构建和部署功能、多租户支持、监控和日志系统、开发者工具等。OpenShift 提供了更高级的抽象层,使开发人员能够更轻松地构建、部署和管理应用程序。
安全和认证:OpenShift 强调安全性和身份验证,提供了更多的安全控制和认证机制。它提供了内置的身份验证、访问控制和安全策略,以确保应用程序和数据的安全。
商业支持:OpenShift 是由 Red Hat 公司开发和维护的商业产品,提供商业支持和服务。相比之下,Kubernetes 是一个开源项目,可以由任何人自由使用和贡献,但没有官方的商业支持。
综上所述,OpenShift 是基于 Kubernetes 的容器应用平台,提供了更完整、更丰富的功能和工具,以简化应用程序的开发、部署和管理。它扩展了 Kubernetes 的能力,特别强调安全性和开发者友好性,并提供商业支持。Kubernetes 则是一个开源的容器编排和管理系统,提供了核心的容器编排功能,可以在各种环境中部署和使用。
CoreOS
创立初心
2013 年,两位俄勒冈州立大学多年前的校友 Alex Polvi 和 Brandon Philips 在硅谷的一间车库里面成立了 CoreOS,这既是他们的产品名也是公司名。CoreOS (后改名为 Container Linux) 的定位是一款为容器而生,并支持平滑升级的轻量级 Linux 发行版。
- 为容器而生:2013 年两位创始人就看到了未来是容器的时代,他们相信需要一款只运行容器的操作系统。
- 支持平滑升级:Google 的 Chromium OS 的一大革新技术是支持定期自动升级,这样能够把安全漏洞的修复及时推送给用户。而服务器上的 Linux 发行版,少则半年,多则数年才能得到更新。CoreOS 想把 Chromium OS 的这一特性带到服务端,所以他们 fork 了 Chromium OS,删掉了无关的代码开始开发 CoreOS。
CoreOS 是 CNCF 2015 年创立时的最初始成员之一,当时他们公司成立才两年。
产品
自创立 CoreOS 之后,这家公司的产出和取得的成就只能用一路开挂这个词来形容:
- Etcd - 【CNCF 毕业项目】 当初为 CoreOS 设计的分布式版 /etc 配置模块,etcd 名字就是 /etc (Linux 的配置目录) + distribution 首字母组成。现在已成为了享誉世界的开源分布式 KV 数据库,Kubernetes默认的 KV 存储组件。
- Kubernetes - 【CNCF 毕业项目】 2014 开始 CoreOS 就深度参与 Kubernets 的开发迭代,成为了除 Google 之外的第二大贡献者。
- CNI - 【CNCF 毕业项目】 之前在 CNCF 那篇文章提到过的通用的容器网络规范,就是由 CoreOS 捐赠的。
- Flannel - CoreOS 当初为 Kubernets 设计的网络基础组件。
- Operator Framework - 【CNCF 孵化中项目】 CoreOS 2016 年提出了 Kubernets Operator 的模式,2018 年推出 Operator Framework 项目帮助开发者快速编写 Operator,后来捐赠给 CNCF,目前处于孵化阶段。
被收购
2018 年 1 月 30 号,RedHat 以 2.5 亿美金的价格全资收购 CoreOS(当时 CoreOS 的员工才 130 人)。 收购后,CoreOS 的 Container Linux 和 Atomic 合并进 Red Hat Enterprise Linux,CoreOS 的 Tectonic 合入 OpenShift,Quay.io 则作为 RedHat 的子产品继续存在。
README
修订:
银法王 2022-06-26
银法王 2023-06-01
银法王 2023-07-15 完善结构内容
银法王 2025-03-15 完善结构内容
参考:
官网教程
《云原生Kubernates全栈架构师实战》
《凤凰架构》