kubernetes pod调度基础
目录
Replication Controller 和 ReplicaSet
标签与标签选择器
无状态应用管理Deployment
有状态应用管理StatefulSet
守护进程集DaemonSet
Replication Controller 和 ReplicaSet
RC用来确保Pod副本数达到期望值,这样可以确保一个或多七个同类Pod总是可用的
如果存在的Pod数量大于设定的值,Replication Controller将终止额外的Pod,如果太少,
Replication Controller将会启动更多的Pod用于保证达到期望值,与手动创建Pod不同的是,用Replication Controller维护的Pod在失败、删除或终止时会自动替换。因此,即使应用程序只需要一个Pod,也应该使用Replication Controller或其他方式管管理。Replication Controller类似于进程管理程序,但是Replication Controller不是监视单个节点上的各个进程,而是监视多个节点上的多个Pod。
创建名为replication.yaml
的文件,内容如下:
apiVersion: v1
kind: ReplicationController
metadata:name: nginx
spec:replicas: 3selector:app: nginxtemplate:metadata:name: nginxlabels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80
- 创建 Replication Controller:执行命令
kubectl apply -f replication.yaml
,创建名为nginx
的 Replication Controller。 - 查看状态:使用命令
kubectl describe replicationcontrollers nginx
查看其状态,初始可能显示pods status: 0 running / 3 waiting / 0 succeeded / 0 failed
,稍后会变为pods status: 3 running / 0 waiting / 0 succeeded / 0 failed
。 - 列出相关 Pod:通过命令
pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items[*].metadata.name}) && echo $pods
,可列出该 Replication Controller 管理的 Pod 名称。 -
ReplicaSet 实验案例1
- 案例目的:创建一个 ReplicaSet,同样用于管理 Nginx 应用的 Pod 副本,维持 3 个副本,并展示其标签选择器的灵活性。
- 操作步骤:
- 创建配置文件:创建
replicaset.yaml
文件,内容如下: -
apiVersion: apps/v1 kind: ReplicaSet metadata:name: nginx - replicaset spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80
- 创建 ReplicaSet:执行命令
kubectl apply -f replicaset.yaml
,创建名为nginx - replicaset
的 ReplicaSet。 - 查看状态:使用
kubectl describe replicaset nginx - replicaset
查看状态,确认其是否成功创建并维持 3 个 Pod 副本。 - 测试标签选择器:可以尝试修改
matchLabels
中的标签,或者添加更多标签选择条件,如matchExpressions
,然后重新应用配置文件,观察 Pod 的创建和管理情况。例如,若添加一个tier in (front - end)
的标签选择条件,只有符合该条件的 Pod 才会被该 ReplicaSet 管理。
- 创建配置文件:创建
标签与标签选择器
标签
标签是用来标识K8S对象的一组附加在其上的键值对,通过标签我我们可以方便地筛选或排除一组对象借鉴资料中的话来讲,集群中的应用部署或是批处理的程序部署通常都是多维度的,为了实现对这些对象的管理,往往需要对某一特定维度的对象进行操作,而标签可可以通过用户的意愿组织集群中的对象之间的结构,而不需要对集群进行修改在同一个对象之下标签的Key值必须唯一的。名称方面,标签名不得多于63个字符且必须由字母或数字开头或结尾,可以包含字母、数字、-、、、等字符;标签前缴是可选的,必须以DNS子域名的方式指定,例如:kubernetes.io,后用/将其与标签名分隔。通常情请况下,若不使用标签前缀,那么该标签的Key将被视为专属于用户的,在K8S的系统组件向对象添加标:签时,必须指定前缀。在标签值方面,若标签值不为空,则其长度不得多于63个字符且必须由字母或数字开头或结尾,可以包含字母、数字、-、等字符。
标签选择器
标签选择器可以用来选择一组对象(标签并不能唯一标识一个对象),APIServer支持两种标签选择器:基于等式的标签选择器与基于集合的标签选器:
基于等式的标签选择方式:在这种选择方式下可以使用=、==、!=三种操作符来进行选择,前两个的含义是一样的,都代表相等,第三种代表不等。选择条件可以通过,叠加,例如date=day1,name!=build代表选择date值为day1且name值不为build的对象。
基于集合的标签选择方式:这种选择器可以同时选择一组对象只。支持的操作符有:in、notin、exists具体的使用方法为:选择date包含有值为day1、day2、day3的标签:date in(dayy1,day2,day3选择name值不为build、pipline的标签:name notin(build, pipline)
选择所有包含test的标签:test基于集合的标签选择器也支持使用","分隔以同时叠加选择,相同意义上的选择条件在这两种选择方式之间是等价的。
标签等式的标签选择器
matchlabels是{key,value}对的映射。matchlabels 映射中中的单个{key,value}等价于
matchexpressions的元素,其键字段为"key",运算符为"in",值数组仅包含"value"
matchexpressions是pod选择器需求的列表。有效的运算符包括in、notin、exists 和doesnotexist.对于in和notin,设置的值必须为非空。matchlabels和mattchexpressions中的所有要求都被放在一起,必须满足所有这些要求才能匹配。
ReplicaSet
Replicaset(复制集,RS)是支持基于集合的标签选择器的下一代Replication Controller,它
主要用于Deployment协调创建、删除和更新Pod,和Repliication Controller唯一的区别是,ReplicaSet支持标签选择器。在实际应用中,虽然Replicasset可以单独使用,但是一般建议使用Deployment来自动管理Replicaset,除非自定义的Pod不需要更新或有其他编排等
Pod 标签示例
apiVersion: v1
kind: Pod
metadata:name: nginx-weblabels:# 应用标识app: nginx # 应用版本version: v1 # 服务层级tier: frontend # 环境类型environment: prod
spec:# 容器配置...
Service 标签示例
apiVersion: v1
kind: Service
metadata:name: nginx-servicelabels:# 关联应用app: nginx # 服务类型service: web
spec:# 服务配置...
创建带标签的 Pod
# pod-with-labels.yaml
apiVersion: v1
kind: Pod
metadata:name: frontend-podlabels:app: myapptier: frontendenv: test
spec:containers:- name: nginximage: nginx
无状态应用管理Deployment
无状态服务(stateless service)对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求里,要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息。这种服务叫做无状态服务。
无状态服务:就是没有特殊状态的服务,各个请求对于服务器来说统无差别处理,请求自身携带了所有服务端所需要的所有参数(服务端自身不存储跟请求相关的任何数据,不包括数据库存储信息)
无状态服务的优点
数据方面:无状态服务不会在本地存储持久化数据.多个实例可以共享享相同的持久化数据
结果方面:多个服务实例对于同一个用户请求的响应结果是完全一致的
关系方面:这种多服务实例之间是没有依赖关系
影响方面:在k8s控制器中动态启停无状态服务的pod并不会对其它的pod产生影响
示例方面:nginx实例,tomcat实例,web应用
资源方面:相关的k8s资源有:ReplicaSet、ReplicationCointroller、Deployment
创建方式:Deployment被设计用来管理无状态服务的pod
每个pod完全一致,原因如下:
无状态服务内的多个Pod创建的顺序是没有顺序的
无状态服务内的多个Pod的名称是随机的
pod被重新启动调度后,它的名称与IP都会发生变化
无状态服务内的多个Pod背后是共享存储的
(8)扩缩容方式:随机缩容
由于是无状态服务,所以这些控制器创建的pod序号都是阿随机值。并且在缩容也是随机,并不会明缩容某一个pod。因为所有实例得到的返回值都是一样,所以缩容任何一个pod都可以。无状态服务不会在本地存储持久化数据。多个服务实例对于同同一个用户请求的响应结果是完全一致的。这种多服务实例之间是没有依赖关系,比如web应用,在k8s控制器中动态启停无状态服务的pod并不会对其它的pod产生影响。
Deployment被设计用来管理无状态服务的pod,每个pod完全一致
无状态服务内的多个Pod创建的顺序是没有顺序的。
无状态服务内的多个Pod的名称是随机的.pod被重新启动调度后,它的名称与IP都会发生变化无状态服务内的多个Pod背后是共享存储的。
编写一个名为nginx - deployment.yaml
的文件,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx - deploymentlabels:app: nginx
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.14.2ports:- containerPort: 80
此 YAML 文件定义了一个名为nginx - deployment
的 Deployment,它将创建 3 个 Pod 副本,每个 Pod 运行nginx:1.14.2
镜像,并暴露 80 端口。
部署 Nginx Deployment
使用以下命令在 Kubernetes 集群中创建 Deployment:
kubectl apply -f nginx - deployment.yaml
3. 查看 Deployment 状态
使用以下命令查看 Deployment 的详细信息:
kubectl describe deployment nginx - deployment
也可使用以下命令查看 Deployment 创建的 Pod:
kubectl get pods - l app = nginx
4. 更新 Nginx Deployment
假设要将 Nginx 镜像更新到新版本nginx:1.20.0
,编辑nginx - deployment.yaml
文件,将image
字段的值修改为nginx:1.20.0
,然后再次应用配置:
kubectl apply -f nginx - deployment.yaml
Kubernetes 会自动执行滚动更新,逐步用新版本的 Pod 替换旧版本的 Pod。
5. 回滚 Deployment
如果更新后出现问题,可使用以下命令回滚到上一个版本:
kubectl rollout undo deployment nginx - deployment
若要回滚到指定版本,先查看 Deployment 的历史版本:
kubectl rollout history deployment nginx - deployment
然后根据版本号回滚,例如回滚到版本 3:
kubectl rollout undo deployment nginx - deployment --to - revision = 3
6. 扩展和缩减 Deployment
可根据负载情况调整 Pod 的副本数量。例如,将副本数量扩展到 5:
kubectl scale deployment nginx - deployment --replicas = 5
查看 Deployment 状态,确认副本数已更新:
kubectl get deployment nginx - deployment
若要缩减副本数,如减至 2 个,执行:
kubectl scale deployment nginx - deployment --replicas = 2
7. 删除 Deployment
当不再需要该 Deployment 时,可使用以下命令将其删除:
kubectl delete deployment nginx - deployment
这会删除 Deployment 及其管理的所有 Pod。
有状态应用管理StatefulSet
StatefulSet(有状态集,缩写为sts)常用于部署有状态的且需要有序启动的应用程序。比如在生产环境中,可以部署Elasticsearch集群、MongoDB集群或者需要持久化的RabbitMQ集群、Redis集
群、Kafka集群和Zookeeper集群等。
一个StatefulSet管理着基于相同容器规范的Pod。与Deployment不同的是,Statefulset为每个Pod维护了一个标识。这些Pod是根据相同规范创建的,但是不可互换,每个Pod都有一个持久的标识符,在重新调度时也会被保留。
有状态服务的特征
数据方面:有状态服务需要在本地存储持久化数据,典型的应用是分布式数据库
结果方面:实例之间,请求结果可能存在不一致
关系方面:分布式节点实例之间有依赖的拓扑关系,比如主从关关系。
影响方面:如果K8S停止分布式集群中任一实例pod,就可能会导致数据丢失或者集群的crash(崩
示例方面:mysql数据库、kafka、zookeeper、Redis主从架构
资源方面:statefulSet
创建方式:statefulSet管理Stateful管理有状态的应用,Pod有如下特征:
唯一性:每个Pod会被分配一个唯一序号.
顺序性:Pod启动,更新,销毁是按顺序进行.
稳定的网络标识:Pod主机名,DNS地址不会随着Pod被重新调度而发生变化
稳定的持久化存储:Pod被重新调度后,仍然能挂载原有的PV,从而保证了数据的完整性和一致性
有状态服务的应用场景
数据方面:有状态服务需要在本地存储持久化数据,典型的应用是分布式数据库
结果方面:实例之间,请求结果可能存在不一致
关系方面:分布式节点实例之间有依赖的拓扑关系,比如主生从关系
影响方面:如果K8S停止分布式集群中任一实例pod,就可能会导致数据丢失或者集群的crash(崩溃
示例方面:mysql数据库、kafka、zookeeper、Redis主从架构
资源方面:statefulSet
创建方式:statefulSet管理
Stateful管理有状态的应用,Pod有如下特征:
唯一性:每个Pod会被分配一个唯一序号.
顺序性:Pod启动,更新,销毁是按顺序进行.
稳定的网络标识:Pod主机名,DNS地址不会随着Pod被重新调度而发生变化
稳定的持久化存储:Pod被重新调度后,仍然能挂载原有的PV,从而保证了数据的完整性和一致性
有状态服务的应用场景
有状态的pod是用来运行有状态应用的,所以其在数据卷上不存储的数据非常重要,在Statefulset缩容时删除这个声明将是灾难性的,特别是对于Statefulset来说,缩容就像减少其replicas数值一样简单。基于这个原因,当需要释放特定的持久卷时,需要手动删除对应的持久卷声明。有状态服务需要在本地存储持久化数据,典型的是分布式数据库的应用,分布式节点实例之间有依赖
的拓扑关系.比如,主从关系。如果K8S停止分布式集群中任一实例pod,就可能会导致数据丢失或者集群的crash(崩溃)有状态服务可以说是需要数据存储功能的服务、或者指多线程类型的服务,队列等。(mysql数据
库、kafka、zookeeper等)有状态服务常常用于实现事务(并不是唯一办法,下文有另外的方案)。举一个常见的例子,在商城里购买一件商品。需要经过放入购物车、确认订单、付款等多个步骤。由于HTTP协议本身是无状态的,所以为了实现有状态服务,就需要通过一些额外的方案。比如最常见的session,将用户挑选的商品(购物车),保存到session中,当付款的时候,再从购物车里取收出商品信息
无状态服务和有状态服务的比较无状态服务
服务不依赖自身的状态,实例的状态数据可以维护在内存中。
任何一个请求都可以被任意一个实例处理。
不存储状态数据,实例可以水平拓展,通过负载均衡将请求分发到各个节点。
在一个封闭的系统中,只存在一个数据闭环。
通常存在于单体架构的集群中。
有状态服务
服务本身依赖或者存在局部的状态数据,这些数据需要自身持久化或者可以通过其他节点恢复。一个请求只能被某个节点(或者同等状态下的节点)处理。
存储状态数据,实例的拓展需要整个系统参与状态的迁移。
在一个封闭的系统中,存在多个数据闭环,需要考虑这些闭环的数据一致性问题
通常存在于分布式架构中。
首先需要创建一个 StorageClass,为 StatefulSet 提供动态存储:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: mysql - storage
provisioner: kubernetes.io/aws - ebs # 根据你的集群环境选择合适的存储插件
parameters:type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:- debug
将上述内容保存为mysql - storageclass.yaml
,然后执行:
kubectl apply -f mysql - storageclass.yaml
2. 创建 Headless Service
为 StatefulSet 创建一个 Headless Service,为 Pod 提供稳定的网络标识:
apiVersion: v1
kind: Service
metadata:name: mysql - servicelabels:app: mysql
spec:ports:- port: 3306name: mysqlclusterIP: None # 指定为Headless Serviceselector:app: mysql
保存为mysql - service.yaml
并执行:
kubectl apply -f mysql - service.yaml
3. 创建 StatefulSet
创建一个 StatefulSet 来管理 MySQL 实例:
apiVersion: apps/v1
kind: StatefulSet
metadata:name: mysql - statefulset
spec:serviceName: "mysql - service"replicas: 3selector:matchLabels:app: mysqltemplate:metadata:labels:app: mysqlspec:containers:- name: mysqlimage: mysql:8.0ports:- containerPort: 3306name: mysqlenv:- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql - secretkey: root - passwordvolumeMounts:- name: mysql - datamountPath: /var/lib/mysqlvolumeClaimTemplates:- metadata:name: mysql - dataspec:accessModes: [ "ReadWriteOnce" ]storageClassName: "mysql - storage"resources:requests:storage: 10Gi
注意,这里引用了一个名为mysql - secret
的 Secret,需要提前创建:
kubectl create secret generic mysql - secret --from - literal = root - password = your - password
将 StatefulSet 配置保存为mysql - statefulset.yaml
并执行:
kubectl apply -f mysql - statefulset.yaml
4. 验证 StatefulSet
查看 StatefulSet 状态:
kubectl get statefulset mysql - statefulset
查看创建的 Pod:
kubectl get pods - l app = mysql
你会看到类似以下格式的 Pod 名称:mysql - statefulset - 0
、mysql - statefulset - 1
、mysql - statefulset - 2
。
查看 PVC 和 PV:
kubectl get pvc
kubectl get pv
5. 连接到 MySQL 实例
可以通过 Pod 名称连接到特定的 MySQL 实例。例如,连接到mysql - statefulset - 0
:
kubectl exec - it mysql - statefulset - 0 -- mysql - uroot - p
输入之前设置的密码your - password
即可登录。
6. 扩展和缩减 StatefulSet
扩展 MySQL 实例到 5 个:
kubectl scale statefulset mysql - statefulset --replicas = 5
缩减回 3 个:
kubectl scale statefulset mysql - statefulset --replicas = 3
注意,StatefulSet 的缩容是有序的,会从最大索引的 Pod 开始删除。
7. 更新 StatefulSet
如果需要更新 MySQL 版本,编辑mysql - statefulset.yaml
文件,修改image
字段,然后应用更改:
kubectl apply -f mysql - statefulset.yaml
默认情况下,StatefulSet 使用OnDelete
更新策略,需要手动删除 Pod 才能触发更新。也可以将更新策略改为RollingUpdate
:
updateStrategy:type: RollingUpdate
8. 删除 StatefulSet
删除 StatefulSet 时,PVC 不会自动删除,以保护数据:
kubectl delete statefulset mysql - statefulset
如果需要删除 PVC 和 PV,可执行:
kubectl delete pvc - l app = mysql
守护进程集DaemonSet
什么是Daemonset
有时候我们需要在每个Kubernetes节点或符合条件的节点点上都部署某个应用,那么就可以使用Kubernetes的DaemonSet调度 Pod。DaemonSet确保全部(或符合条件)的节点上运行一个Pod副本。
当有新的节点加入集群时,也会为他们新增一个Pod,当节点从集群中移除时,这些Pod会被回收,删除DaemonSet将会删除它创建的所有的Pod。
创建 DaemonSet YAML 文件
编写一个名为fluentd - daemonset.yaml
的文件,内容如下
apiVersion: apps/v1
kind: DaemonSet
metadata:name: fluentd - loggingnamespace: kube - systemlabels:k8s - app: fluentd - log - collector
spec:selector:matchLabels:name: fluentd - loggingtemplate:metadata:labels:name: fluentd - loggingspec:tolerations:- key: node - role.kubernetes.io/control - planeeffect: NoSchedule- key: node - role.kubernetes.io/mastereffect: NoSchedulecontainers:- name: fluentdimage: fluent/fluentd:v1.14 - debianslimresources:limits:memory: 200Mirequests:cpu: 100mmemory: 200MivolumeMounts:- name: varlogmountPath: /var/log- name: varlibdockercontainersmountPath: /var/lib/docker/containersreadOnly: trueterminationGracePeriodSeconds: 30volumes:- name: varloghostPath:path: /var/log- name: varlibdockercontainershostPath:path: /var/lib/docker/containers
此 YAML 文件定义了一个名为fluentd - logging
的 DaemonSet,它会在每个节点上运行一个 Fluentd 容器,用于收集容器日志
部署 DaemonSet
使用以下命令在 Kubernetes 集群中创建 DaemonSet:
kubectl apply -f fluentd - daemonset.yaml
3. 查看 DaemonSet 状态
使用以下命令查看 DaemonSet 的详细信息:
kubectl describe daemonset fluentd - logging - n kube - system
查看已调度的 Pod 数量:
kubectl get daemonset fluentd - logging - n kube - system
输出类似于:
NAME DESIRED CURRENT READY UP - TO - DATE AVAILABLE NODE SELECTOR AGE
fluentd - logging 3 3 3 3 3 <none> 10m
这里的DESIRED
表示集群中的节点数,CURRENT
和READY
表示实际运行的 Pod 数。
4. 查看运行的 Pod
使用以下命令查看 DaemonSet 创建的 Pod:
kubectl get pods - n kube - system - l name = fluentd - logging
每个 Pod 会在不同的节点上运行,名称类似于:fluentd - logging - xyz12
。
5. 节点选择器和容忍度
如果需要将 DaemonSet 限制在特定节点上运行,可以添加nodeSelector
字段。例如,只在标记为storage - node = true
的节点上运行:
spec:template:spec:nodeSelector:storage - node: "true"
DaemonSet 默认会调度到所有节点,包括 master 节点。如果不想在 master 节点上运行,可以添加容忍度(tolerations),如示例中所示。
6. 更新 DaemonSet
如果需要更新 Fluentd 版本,编辑fluentd - daemonset.yaml
文件,修改image
字段,然后再次应用配置:
kubectl apply -f fluentd - daemonset.yaml
Kubernetes 会自动执行滚动更新,逐个替换旧版本的 Pod。
7. 回滚 DaemonSet
如果更新后出现问题,可使用以下命令回滚到上一个版本:
kubectl rollout undo daemonset fluentd - logging - n kube - system
8. 删除 DaemonSet
当不再需要该 DaemonSet 时,可使用以下命令将其删除:
kubectl delete daemonset fluentd - logging - n kube - system
这会删除 DaemonSet 及其管理的所有 Pod。