小阿轩yx-Kubernetes Pod调度基础
前言
传统架构中,考虑或面临一个问题
- 应用需要部署在哪里?应用下载在哪里运行?有一个服务不可访问了去哪里排查?
使用 Kubernetes 部署应用后
- 无需再关心应用到底部署在那台服务器,也无需登录某台服务器排查应用问题
- 使用 Kubernetes 提供的 Kubectl 或 Dashboard 即可快速定位到应用,之后便可以进行执行命令、查看日志、监控等操作
- 可以用 Pod 单独部署,也可以使用更高级的资源调度方法,如 Deployment、StatefulSet
Pod 控制器
Replication Controller 和 ReplicaSet
Replication Controller(复制控制器或 rc)
- 用来确保 Pod 副本数达到期望值
- 可以确保一个或多个同类 Pod 总是可用的
- 如果存在的 Pod 数量大于设定的值,Replication controller 将终止额外的 Pod
- 如果太少,Replication controller 将会启动更多的 Pod 用于保证达到期望值
- ReplicatiorController 类似于进程管理程序,但是 Replication controller 不是监视单个节点上的各个进程,而是监视多个节点上的多个 Pod
与手动创建 Pod 不同的是
- 用 Replication controller 维护的 Pod 在失败、删除或终止时会自动替换。
- 因此,即使应用程序只需要一个 Pod,也应该使用 Replication controller 或其他方式管理。
将 images 镜像通过 Xftp 上传至三台主机(master、node01、node02)
再将资源清单文件通过 Xftp 上传至 master 主节点
三台主机导入镜像
主机一
[root@k8s-master ~]# cd images/
[root@k8s-master images]# bash imp_docker_img
主机二
[root@k8s-node01 ~]# cd images/
[root@k8s-node01 images]# bash imp_docker_img
主机三
[root@k8s-node02 ~]# cd images/
[root@k8s-node02 images]# bash imp_docker_img
Replication Controller 使用示例
编辑 Replication Controller 文件
在 master 节点
[root@k8s-master ~]# vim replicationcontroller-nginx.yam1
apiVersion:v1
kind: Replicationcontroller
metadata:
name:nginx
spec:
replicas:3
selector:
app: nginx
template:
metadata:
name:nginx
labels :
app:nginx
spec:
containers:
- name:nginx
image: nginx:1.7.9
ports:
- containerPort:80
创建 Replication Controller
[root@k8s-master ~]# ku apply -f replicationcontroller-nginx.yaml
查看状态
[root@k8s-master ~]# ku get pod
NAME READY STATUS RESTARTS AGE
nginx-9g78r 1/1 Running 0 11m
nginx-bbtgj 1/1 Running 0 11m
nginx-w28bg 1/1 Running 0 11m
删除一个 pod
[root@k8s-master ~]# ku delete pod nginx-9g78r -n default
查看状态
[root@k8s-master ~]# ku get pod
NAME READY STATUS RESTARTS AGE
nginx-bbtqj 1/1 Running 0 11m
nginx-qqjs4 1/1 ContainerCreating 0 11m
nginx-w28bg 1/1 Running 0 11m
删除 Replication Controller
[root@k8s-master ~]# ku delete -f replicationcontroller-nginx.yaml
Replicaset(复制集或 rs)
是支持基于集合的标签选择器的下一代 Replication Controller
主要用于
- Deployment 协调创建
- 删除
- 更新 Pod
和 Replication Controller 唯一的区别是ReplicaSet 支持标签选择器。
实际应用中
- 虽然 Replicaset 可以单独使用,但是一般建议使用 Deployment 来自动管理 ReplicaSet
- 除非自定义的 Pod 不需要更新或有其他编排等
定义一个 Replicaset 的实例
编辑 ReplicaSet 文件
[root@k8s-master ~]# vim replicaset-example.yaml
apiVersion: apps/v1
kind: Replicaset
metadata:
name:frontend
labels:
app: guestbook
tier:frontend
spec:
# modify replicas according to your case
replicas:3
selector:
matchLabels :
tier:frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels :
app:guestbook
tier:frontend
spec:
containers:
- name:php-redis
image: nginx:1.7.9
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET HOSTS FROM
value:dns
# If your cluster config does not include a dns service, then to
# instead access environment variables to find service host
# info, comment out the 'value: dns' line above, and uncomment the
# line below.
# value:env
ports:
- containerPort:80
- requests:代表容器启动请求的资源限制,分配的资源必须要达到此要求
- limits:代表最多可以请求多少资源
- 单位 m:CPU 的计量单位叫毫核(m)。一个节点的 CPU 核心数量乘以 1800,得到的就是节点总的 CPU 总数量。如,一个节点有两个核,那么该节点的 cPu总量为 2000m。该容器启动时请求 100/2000 的核心(5%)
创建 rs 文件
[root@k8s-master ~]# ku create -f replicaset-example.yaml
查看状态
[root@k8s-master ~]# ku get pod
删除文件
[root@k8s-master ~]# ku delete -f replicaset-example.yaml
标签与标签选择器
标签
- 用来标识 K8S 对象的一组附加在其上的键值对,通过标签我们可以方便地筛选或排除组对象。
- 可以通过用户的意愿组织集群中的对象之间的结构,而不需要对集群进行修改。
- 在同一个对象之下标签的 Key 值必须唯一的。
名称方面
- 标签名不得多于 63个字符且必须由字母或数字开头或结尾
- 可以包含字母、数字、-、-、·;标签前缀是可选的,必须以 DNS 子域名的方式指定
标签选择器
可以用来选择一组对象(标签并不能唯一标识一个对象)
APIServer 支持两种标签选择器
- 基于等式的标签选择器
- 基于集合的标签选器
基于等式的标签选择
- 在这种选择方式下可以使用=、=、!=三种操作符来进行选择,前两个的含义是一样的,都代表相等,第三种代表不等。选择条件可以通过,叠加
基于集合的标签选择
- 可以同时选择一组对象。
- 支持的操作符有:in、notin、exists。
具体的使用方法
- 选择 date 包含有值为 day1、day2、day3 的标签:date in(day1,day2,day3)
- 选择 name 值不为 build、pipline 的标签:name notin(build,pipline)
- 选择所有包含 test 的标签:test
- 选择所有不包含 test 的标签:!test
基于集合的标签选择器
- 也支持使用“,”分隔以同时叠加选择,相同意义上的选择条件在这两种选择方式之间是等价的。
标签与标签选择器举例
基于等式的标签选择器
selector:
component:redis
基于集合的标签选择器
selector:
matchLabels:
component:redis
matchExpressions:
- {key: tier, operator:In,values:[cache]}
- {key:environment,operator:NotIn,values:[dev]}
matchlabels
- 是{key,value}对的映射。
- 映射中的单个{key,value}等价于matchexpressions 的元素,其键字段为“key”,运算符为“in”,值数组仅包含“value”。
- 是 pod 选择器需求的列表。
- 有效的运算符包括 in、notin、exists 和doesnotexist。对于in 和 notin,设置的值必须为非空。
matchlabels 和 matchexpressions中的所有要求都被放在一起-必须满足所有这些要求才能匹配
无状态应用管理 Deployment
定义
无状态服务(stateless service)
- 对单次请求的处理,不依赖其他请求
- 也就是说,处理次请求所需的全部信息,要么都包含在这个请求里
- 要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息。
- 就是没有特殊状态的服务,各个请求对于服务器来说统一无差别处理,请求自身携带了所有服务端所需要的所有参数(服务端自身不存储跟请求相关的任何数据,不包括数据库存储信息)
- 不会在本地存储持久化数据,多个服务实例对于同一个用户请求的响应结果是完全致的。
- 这种多服务实例之间是没有依赖关系,比如 web 应用,在 k8s 控制器中动态启停无状态服务的 pod 并不会对其它的 pod 产生影响。
- 无状态服务内的多个 Pod 的名称是随机的,pod 被重新启动调度后,它的名称与 IP 都会发生变
- 无状态服务内的多个 Pod 背后是共享存储的。
如果在启动一个服务时,不依赖于该服务之前的运行状态,或者不依赖于其他服务,这个服务就是无状态服务;反之,就是有状态服务
无状态服务特点
- 数据方面:无状态服务不会在本地存储持久化数据,多个实例可以共享相同的持久化数据
- 结果方面:多个服务实例对于同一个用户请求的响应结果是完全一致的
- 关系方面:这种多服务实例之间是没有依赖关系
- 影响方面:在 k8s 控制器中动态启停无状态服务的 pod 并不会对其它的 pod 产生影响
- 示例方面:nginx实例,tomcat 实例,web应用
- 资源方面:相关的k8s资源有:ReplicaSet、Replicationcontroller、Deployment
创建方式
- Deployment 被设计用来管理无状态服务的 pod
每个 pod 完全一致
原因如下
- 无状态服务内的多个 Pod 创建的顺序是没有顺序的
- 无状态服务内的多个 Pod 的名称是随机的
- pod 被重新启动调度后,它的名称与 IP 都会发生变化
- 无状态服务内的多个 Pod 背后是共享存储的
扩缩容方式
随机缩容
- 由于是无状态服务,所以这些控制器创建的 pod 序号都是随机值。
- 并且在缩容也是随机,并不会明确缩容某一个 pod。
- 因为所有实例得到的返回值都是一样,所以缩容任何一个 pod 都可以。
Deployment
- 用来管理 RS,并为 Pod 和 RS 提供声明性更新,以及许多其他的新的功能,生产环境中使用 Deployment 替代 RS。
- 一般用于部署公司的无状态服务,因为企业内部现在都是以微服务为主,微服务实现无状态化也是最佳实践。
- 可以利用 Deployment 的高级功能做到无缝迁移、自动扩容缩容、自动灾难恢复、一键回滚等功能。
- Deployment 被设计用来管理无状态服务的 pod,每个 pod 完全一致无状态服务内的多个 Pod 创建的顺序是没有顺序的。
创建 Deployment
编写 Deployment 文件
[root@k8s-master ~]# vim nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name:nginx-deployment
labels :
name:nginx-deployment
spec:
replicas :2
selector:
matchLabels :
app:nginx
template :
metadata:
labels :
app: nginx
spec:
containers :
- name:nginx
image: nginx:1.7.9
ports:
- name:nginx
containerPort:80
- 从 1.16 开始,彻底废弃了其他的 apiVersion,只能使用 app/v1
- replicas:pod 的副本数
- selector:定义 Deployment 如何找到要管理的 Pod,与 template 的 labe1 标签对应,app/v1selector:必须指定
template
- app:nginx 使用 label 标记 pod
- spec:定义 pod 的详细信息
- nginx 表示 pod 运行一个名字为 nginx 的容器name:
- image:运行此 pod 使用的镜像
- port:容器用于发送和接收流量的端口
使用 kubectl create 创建此 Deployment
[root@k8s-master ~]# ku create -f nagin-deployment.yaml
查看状态
[root@k8s-master ~]# ku get pod
使用 rollout 查看整个 Deployment 的创建过程状态
[root@k8s-master ~]# ku rollout status deployment/nginx-deployment
查看这个 Deployment 对应的 RS
[root@k8s-master ~]# ku get rs -l app=nginx
查看此 Deployment 创建的 pod
[root@k8s-master ~]# ku get pods --show-labels
更新 Deployment
- 通过 Deployment 部署应用后
- 如果需要对 Deployment 文件的配置文件或者镜像版本进行更新,更改后该 Deployment 会创建新的 Repliccaset,之后会对管理的 Pod 进行滚动升级。
更新 pod 的 image
[root@k8s-master ~]# ku set image deployment nginx-deployment nginx=nginx:1.9.1 --record
或
[root@k8s-master ~]# ku set image deployment nginx-deployment nginx=nginx:1.12.0 --record
或直接修改 deployment文件,效果相同
[root@k8s-master ~]# ku edit deployment.v1.apps/nginx-deployment
查看更新过程
[root@k8s-master ~]# ku rollout status deployment.v1.apps/nginx-deployment
查看更新过程
[root@k8s-master ~]# ku rollout status deployment.v1.apps/nginx-deployment
此时的 RS 有新的和旧的
[root@k8s-master ~]# ku get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-6cf9b75cddA 0 0 0 3m57S
nginx-deployment-7569c477b6 0 0 0 3m8s
nginx-deployment-96bfc8b67 0 0 0 3m4s
通过 describe 查看 deployment 的详细信息
[root@k8s-master ~]# ku describe deploy nginx-deployment
回滚 deployment
- 当更新的版本不稳定或者配置不合理时,可以对其进行回滚操作。
多更新几次 deployment
[root@k8s-master ~]# ku set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record
[root@k8s-master ~]# ku set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record
查看更新历史
[root@k8s-master ~]# ku rollout history deployment/nginx-deployment
- 最后的一个版本是当前版本
查看某次更新的详情
[root@k8s-master ~]# ku rollout history deployment/nginx-deployment --revision=2
回滚到指定版本
[root@k8s-master ~]# ku rollout undo deployment/nginx-deployment --to-revision=2
查看
[root@k8s-master ~]# ku rollout history deployment/nginx-deployment
回滚到上次版本
[root@k8s-master ~]# ku rollout undo deployment/nginx-deployment
查看
[root@k8s-master ~]# ku rollout history deployment/nginx-deployment
扩容 deployment
调整 pod 的副本数
[root@k8s-master ~]# ku scale deployment.v1.apps/nginx-deployment --replicas=3
查看
[root@k8s-master ~]# ku get pods
暂停和恢复 deployment 更新
- 可以先暂停更新,然后对 deployment 进行相关的操作
- 可以进行多次更新或修改,此时只是设置出更新的参数,但实际整体的 depolyment 更新是停滞状承
最后可以统一开始更新
暂停 deployment 更新
[root@k8s-master ~]# ku rollout pause deployment/nginx-deployment
恢复 deployment 更新
[root@k8s-master ~]# ku rollout resume deployment.vl.apps/nginx-deployment
删除 Deployment
[root@k8s-master ~]# ku delete -f nginx-deployment.yaml
有状态应用管理 statefulset
- 常常用于实现事务
定义
StatefulSet(有状态集,缩写为 sts)
- 常用于部署有状态的且需要有序启动的应用程序。
- 一个 statefulset 管理着基于相同容器规范的 Pod。
与 Deployment 不同的是
- statefulset 为每个 Pod 维护了一个标识。
- 这些 Pod 是根据相同规范创建的,但是不可互换,每个 Pod 都有一个持久的标识符,在重新调度时也会被保留。
有状态服务
容器数据持久化保持
- 有状态服务可以说是 需要数据存储功能的服务、或者指多线程类型的服务,队列等。(mysq1数据库、kafka、zookeeper等)
- 每个实例都需要有自己独立的持久化存储,并且在 k8s 中是通过申明模板来进行定义。持久卷申明模板在创建 pod 之前创建,绑定到 pod 中,模板可以定义多个。
有状态服务的特征
- 数据方面:有状态服务需要在本地存储持久化数据,典型的应用是分布式数据库
- 结果方面:实例之间,请求结果可能存在不一致2
- 关系方面:分布式节点实例之间有依赖的拓扑关系,比如主从关系。
- 影响方面:如果 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(崩溃)
无状态服务和有状态服务的比较
无状态服务
- 服务不依赖自身的状态,实例的状态数据可以维护在内存中。
- 任何一个请求都可以被任意一个实例处理。
- 不存储状态数据,实例可以水平拓展,通过负载均衡将请求分发到各个节点。
- 在一个封闭的系统中,只存在一个数据闭环。
- 通常存在于单体架构的集群中。
有状态服务
- 服务本身依赖或者存在局部的状态数据,这些数据需要自身持久化或者可以通过其他节点恢复。
- 一个请求只能被某个节点(或者同等状态下的节点)处理。
- 存储状态数据,实例的拓展需要整个系统参与状态的迁移。
- 在一个封闭的系统中,存在多个数据闭环,需要考虑这些闭环的数据一致性问题。
- 通常存在于分布式架构中。
编写 statefulSet 资源文件
定义一个 statefulSet 资源文件
[root@k8s-master ~]# vim nginx-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-svc
spec:
selector:
app: redis-sts
ports:
- port:6379
protocol: TCP
targetPort:6379
apiVersion: apps/v1
kind: statefulset
metadata:
name:redis-sts
spec:
serviceName:redis-svc
replicas:2
selector:
matchLabels:
app: redis-sts
template:
metadata:
labels:
app: redis-sts
spec:
containers:
- image:redis:5-alpine
name: redis
ports:
- containerPort:6379
- kind:service 定义了一个名字为redis-svc 的服务
- kind:statefulset 定义了一个名字为redis-sts 的 statefulset,replicas 表示部署 Pod的副本数。
创建 statefulset
[root@k8s-master ~]# ku create -f redis-statefulset.yaml
查看 statefilset 状态
[root@k8s-master ~]# ku get sts
查看群集状态
[root@k8s-master ~]# ku get service
查看详细信息
[root@k8s-master ~]# ku get po -l app=redis-sts
NAME READY STATUS RESTARTS AGE
redis-sts-0 1/1 Running 0 5m38s
redis-sts-1 1/10 Running 0 4m55s
-l:指定标签(label)
- 注意 NAME 列,名称是 sts 的name-序号,这里序号越小则说明创建的越早从 AGE 列也可以看出来,这就解决了有状态应用中的启动顺序问题
statefulset 扩容
扩容
将副本数修改为3
[root@k8s-master ~]# ku scale sts redis-sts --replicas=3
查看状态
[root@k8s-master ~]# ku get pod
NAME READY STATUS RESTARTS AGE
redis-sts-0 1/1 Running 0 7m50s
redis-sts-1 1/1 Running 0 7m7s
redis-sts-2 1/1 Running 0 6s
缩容
打开第二个终端,动态显示缩容流程
[root@k8s-master ~]# ku get pods -w -l app=redis-sts
- -w:动态监听所有的 pod
在原来第一个终端修改副本数为 2
[root@k8s-master ~]# ku patch sts redis-sts -p '{"spec":{"replicas":2}}
- 此时可以查看第二个终端上的变化
在第一个终端上查看最终结果
[root@k8s-master ~]# ku get pods -l app=redis-sts
非级联删除 statefulset
删除 Statefulset 有两种方式
- 级联删除
- 非级联删除
使用非级联方式删除 statefulset 时
- statefulset 的 Pod 不会被删除。
使用级联方式删除Statefulset 时
- statefulset 和它的 Pod 都会被删除。
采用非级联删除
[root@k8s-master ~]# ku delete statefulsetredis-sts--cascade=false
查看
[root@k8s-master ~]# ku get sts
查看管理的 pod
[root@k8s-master ~]# ku get pod
- 发现 pod 并没有被删除
删除 pod
[root@k8s-master ~]# ku delete po redis-sts-0
[root@k8s-master ~]# ku delete po redis-sts-1
级联删除 statefulset
先创建出 statefulset
[root@k8s-master ~]# kubectl create -f redis-statefulset.yaml
- 如果提示服务已存在,先删除
[root@k8s-master ~]# kubectl delete -f redis-statefulset.yaml
级联删除
[root@k8s-master ~]# kubectl delete statefulset redis-sts
查看
[root@k8s-master ~]# kubectl get po
- 两个 pod 全都没了
删除 redis 服务
[root@k8s-master ~]# kubectl delete -f redis-statefulset.yaml
守护进程集 Daemonset
- Daemonset (守护进程集,缩写为ds)和守护进程类似
什么是 Daemonset
- 有时候我们需要在每个Kubernetes 节点或符合条件的节点上都部署某个应用,那么就可以使用 Kubernetes 的 DaemonSet 调度 Pod。
- Daemonset确保全部(或符合条件)的节点上运行一个Pod 副本。
- 新的节点加入集群时,也会为他们新增一个 Pod,当节点从集群中移除时,这些 Pod会被回收,删除 Daemonset 将会删除它创建的所有的 Pod。
定义一个 Daemonset
[root@k8s-master ~]# cat daemonset-nginx.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: pod-controller
namespace: dev
labels:
controller:daemonset
spec:
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app:nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
创建 Daemonset
[root@k8s-master ~]# ku create namespace dev
或
[root@k8s-master ~]# ku create -f daemonset-nginx.yaml
查看 Daemonset
[root@k8s-master ~]# ku get ds -n dev -o wide
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR
pod-controller 3 3 3 3 3 <none> 3m8s nginx
nginx:latest app=nginx-pod
查看 pod 所在的节点
[root@k8s-master ~]# ku get pod n dev -o wide
删除 Daemonset
[root@k8s-master ~]# ku delete ds pod-controller -n dev
CronJob
- Cronjob(计划任务,缩写为cj)用于以时间为基准的周期性地执行任务,这些自动化任务和运行在 Linux系统上的 cronJob 一样。
创建 cronJob
编辑 cronjob 文件
[root@k8s-master ~]# cat cronjob-example.yaml
#1.21版本以上改为batch/v1
apiversion: batch/v1
kind: cronJob
metadata:
name: hello
spec:
schedule:"*/1 ** * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:v1
args:
- /bin/bash
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
- 这个案例会在每分钟执行一次计划任务,并输出当前时间和“Hello from the Kubernetes cluster”3
创建 cronjob
[root@k8s-master ~]# ku create -f cronjob-example.yaml
查看
[root@k8s-master ~]# ku get cj
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * *0 False 0 48s 94s
等待一会后可以查看生成的 pod
[root@k8s-master ~]# ku get jobs
NAME COMPLETIONS DURATION AGE
hello-27743514 1/1 20s 74s
hel1o-27743515 0/1 14s 14s
查看生成的 pod
[root@k8s-master ~]# ku get pod
查看 pod 的执行日志
[root@k8s-master ~]# ku logs -f hello-27743522-crnf8
Sat Oct 108:02:16 UTC 2022
Hello from the Kubernetes cluster
删除 Cronjob
[root@k8s-master ~]# ku delete cronjob hello
小阿轩yx-Kubernetes Pod调度基础