小阿轩yx-Kubernetes高级调度
前言
- 前面 Kubernetes 的调度基础实现了见表格自己的服务器部署到 Kubernetes
- 在生产环境中,调度远比想象的要复杂
比如
- 某些程序只能部署在固定的几台机器上
- 某些机器只能部署指定的 Pod
- 节点挂了怎么快速修复
- 节点挂了如何让影响最小
- 在线 Debug 没有命令怎么办
这些只是一些需求,真实情况更为复杂,这就需要更高级的调度方式、更实用的功能解决这些问题
Kubernetes 原生有很多功能均能解决这些问题
初始化容器 InitContainer
- 顾名思义,初始化容器是用来初始化操作的
- 很多情况,程序的启动需要依赖各类配置、资源
- 但又不能继承在原有的启动命令或镜像中,因为程序的镜像可能并没有加载配置命令,此时 InitContainer 就起到了很大作用
InitContainer 的基本概念
- 是 Kubernetes 的初始化容器(也称之为 Init 容器)
- 是一种特殊的容器,在 Pod 内的应用容器启动之前运行
- 可以包括一些应用镜像不存在的使用工具和安装脚本,在程序启动时进行初始化
- 每个 Pod 可包含多个容器
- 同时 Pod 也可以有一个或多个先于应用程序启动的 Init 容器
- 在 Pod 定义中和 container 同级,按顺序逐个执行
- 等所有 Init 容器运行完成时,Kubernetes 才会启动 Pod 内的普通容器
Init 容器
与普通容器非常像,除了如下几点
- 它们总是运行到完成
- 上一个运行完成才会运行下一个
- 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,如果 Pod 对应的 restartPolicy 值为 Never,Kubernetes 则不会重启 Pod
- 支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置
对资源请求和限制的处理稍有不同,Init 容器不支持
- lifecycle
- livenessProbe
- readinessProbe
- startupProbe
为 Pod 设置 Init 容器需要在 Pod 的 spec 中添加 initContainer 字段,该字段和应用的 containers 属组同级相邻,配置方式和 containers 中的普通容器差不多
考虑到这些问题和需求,Kubernetes 引入了初始化容器的概念
Init 容器具有与应用容器分离的单独镜像,启动相关代码的优势
- Init 容器可以包含安装过程中应用容器中不存在的实用工具或个性化代码。
- Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
- Init 容器可以以 root 身份运行,执行一些高权限命令。
- Init 容器相关操作执行完成后就会退出,不会给业务容器带来安全隐患。
由于Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用程序的启动,直到满足一组先决条件,Pod 内的所有容器才会并行启动
首先将 images 镜像文件通过 Xftp 上传至 master、node01、node02(101、102、103)
将资源清单单独上传至主节点 master(101)
这一步开启会话同步
进入镜像文件三个节点同时导入镜像
主机一
[root@k8s-master ~]# cd images/
[root@k8s-master images]# bash imp_docker_img.sh
主机二
[root@k8s-node01 ~]# cd images/
[root@k8s-node01 images]# bash imp_docker_img.sh
主机三
[root@k8s-node02 ~]# cd images/
[root@k8s-node02 images]# bash imp_docker_img.sh
延迟指定时间后启动
创建一个 pod,initcontainers 指定初始化容器
command: ["sh","-c",“sleep 15"]
- 表示初始化容器需要休眠 15 秒。
root@k8s-master ~]# cat init01.yml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: initc01
name: initc01
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: n1
resources: {}
initcontainers:
- name: initc01
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 15"]
dnsPolicy: clusterFirst
restartPolicy: Never
status: {}
创建 init01 文件
root@k8s-master ~]# kubectl create -f init01.yaml
不断查看 Pod 启动状态,15秒后会发现 Pod 开始启动
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
init01 0/1 Init:0/1 0 2s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
init01 0/1 Init:0/1 0 8s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
init01 0/1 Init:0/1 0 9s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
init01 0/1 Init:0/1 0 16s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
init01 0/1 PodInitializing 0 17s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
init01 0/1 Running 0 18s
使用初始化容器修改内核参数
- 实际上修改的就是物理机的内核参数,为了安全性,一般不允许在容器里修改内核参数
Seccomp
- 代表安全计算(Securecomputing)模式
- 控制了容器能做哪些操作添加 securitycontext 参数之后就可以修改内核参数了
创建一个 pod
- initcontainers 初始化容器里的 securitycontext:privileged:true 表示该容器具有特权
- 可以执行命令 "sh","-c",“/sbin/sysct1 -w vm.swappiness=0"
- vm.swappiness 设置为 0 表示尽量少使用 swap 内存
编辑 init02 配置文件
[root@k8s-master ~]# cat init02.yml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels :
run: initc02
name: initc02
spec:
terminationGracePeriodseconds: 0
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: n1
resources: {}
initContainers:
- name: initc02
image: alpine
imagePullPolicy: IfNotPresent
command: ["sh","-c","/sbin/sysctl -w vm.swappiness=0"]
securityContext:
privileged: true
dnsPolicy: clusterFirst
restartPolicy: Never
status: {}
应用一个 init02 文件
[root@k8s-master ~]# kubectl apply -f init02.yaml
获取状态信息
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
initc 1/1 Running 0 7m7s
查看 Pod 中的容器
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
initc 1/1 Running 0 5m18s 10.244.85.195 k8s-node01 <none> <none>
查看 k8s-node01 机器上的 swappiness 值是否为0
[root@k8s-master ~]# cat /proc/sys/vm/swappiness
0
等待依赖服务启动
- 有时某些服务需要依赖其他组件才能启动,比如后端应用需要数据库启动之后,应用才能正常启动。
- 此时需要检测数据库实例是否正常,等待数据库可以正常使用时,在启动后端应用,此时可以使用初始化容器进行控制。
创建第一个 Pod
该 Pod 内要依赖两个条件才能启动
- 利用 busybox 容器检测 redis-service 服务是否生成
- 检测 mysql-server 服务是否生成
[root@k8s-master ~]# cat myapp.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginX
labels :
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
initcontainers:
- name: init-redis
image: busybox:1.28
command: ['sh','-c', 'until nslookup redis-server; do echo waiting for redis; sleep 2; done;']
- name: init-mysql
image: busybox:1.28
command: ['sh','-c','until nslookup mysql-server; do echo waiting for mysql; sleep 2; done;']
创建 myapp 文件
[root@k8s-master ~]# kubectl create -f myapp.yaml
获取状态信息
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:0/2 0 8s
创建第一个被依赖的 service
[root@k8s-master ~]# cat redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels :
app: redis
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels :
app: redis
spec:
containers:
- image: redis:5.0
imagePullPolicy: IfNotPresent
name:redis
---
apiVersion: v1
kind: Service
metadata:
labels :
app: redis
name: redis-service
spec:
ports:
- port: 6379
protocol: TCP
targetPort: 6379
selector:
app: redis
type: NodePort
创建 redis-deeployment 文件
[root@k8s-master ~]# kubectl create -f redis-deployment.yaml
deployment.apps/redis created
service/redis-service created
获取服务信息
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d20h
redis-service NodePort 10.104.3.33 <none> 6379:32077/TCP 84s
获取 Pod 状态信息
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:1/2 0 2m20s
redis-56bcf55554-mtrqx 1/1 Running 0 8s
创建第二个被依赖的 service
[root@k8s-master ~]# cat mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: mysql
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- env:
- name:MYSOL_ROOT_PASSWORD
value: 'moonfdd'
image: 'mysql:8.0'
imagePullPolicy: IfNotPresent
name: mysql
volumeMounts:
- mountPath: /var/lib/mysq1
name: volv
volumes:
- hostPath:
path: /root/k8s/moonfdd/mysql/var/lib/mysq1
type: DirectoryOrCreate
name: volv
---
apiVersion: v1
kind: service
metadata:
labels:
app: mysql
name: mysql-service
spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: mysql
type: NodePort
创建 mysql-deployment 文件
[root@k8s-master ~]# kubectl create -f mysql-deployment.yaml
deployment.apps/mysql created
service/mysql-service created
获取 svc 服务状态
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d20h
redis-service NodePOrt 10.101.159.200 <none> 3306:31452/TCP 6s
redis-service NodePort 10.104.3.33 <none> 6379:32077/TCP 84s
获取 Pod 状态
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-dc77b5fb8-zczrz 0/1 Running 2(23s ago) 39s
nginx 0/1 Init:1/2 0 2m20s
redis-56bcf55554-mtrqx 1/1 Running 0 8s
- 到这里可以发现,当把 redis-service 和 mysql-service 两个服务创建出来后,nginx 的 Pod 就正常运行了。
pause 容器
特点
- 镜像非常小,目前在 700 KB 左右
背景
- 像 Pod 这样一个东西,本身是一个逻辑概念
那在机器上是怎么实现的呢?
- 说 Pod 要解决这个问题核心
- 在于如何让一个 Pod 里的多个容器之间最高效的共享某些资源和数据
Pod 的设计要解决的核心问题
- 因为容器之间原本是被 Linux Namespace 和 cgroups 隔开的,所以现在实际要解决的是怎么去打破这个隔离,然后共享某些事情和某些信息。
具体的解法分为两个部分
- 网络
- 存储
Pause 容器就是为解决 Pod 中的网络问题而生的。
实现
Pod 里的多个容器怎么去共享网络?
- 有一个 Pod,其中包含了一个容器 A 和一个容器 B,它们两个就要共享 Networkamespace。
- Kubernetes 里的解法是这样的:它会在每个 Pod 里,额外起一个 Infra(基础)
- container 小容器来共享整个Pod的 Network Namespace。
Infra container
- 一个非常小的镜像,大概 700KB 左右,是一个 C 语言写的、永远处于 “暂停” 状态的容器。
- 由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。
- 所以一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。
即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。这就是 Pod 解决网络共享的一个解法。
在 Pod 里面
- 一定有一个 IP 地址,是这个 Pod 的 Network Namespace 对应的地址,也是这个Infra container 的 IP 地址。
- 所以大家看到的都是一份,而其他所有网络资源,都是一个 Pod 一份,并且被 Pod 中的所有容器共享。这就是 Pod 的网络实现方式。
- 由于需要有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。
- 并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。
- 这也是为什么在 Kubernetes 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。
查看 nginx 的 Pod 运行的主机
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS AGE IP RESTARTS NODE NOMINATED NODE READINESS GATES
mysql-dc77b5fb8-zczrz 1/1 Running 0 10.244.85.208 k8s-node01 <none> <none>
nginx 1/1 Running 0 10.244.85.206 k8s-node01 <none> <none>
redis-56bcf55554-mtrqx 1/1 Running 0 10.244.58.197 k8s-node02 <none> <none>
在 k8s-node01 主机上查看 Pod 中的 pause 容器
[root@k8s-master ~]# docker ps | grep nginx
8feed68c83d1 a99a39d070bf "/docker-entrypoint..." 54 seconds ago Up 53 seconds k8s nginx nginx default 8a0b9ac3-3cab-4ae6-94e2-624046f5aa87 _0 9ee9ad88890b registry.aliyuncs.com/google containers/pause:3.6 "/pause" 54 seconds ago Up 53 seconds k8s_PoD_nginx_default_8a0b9ac3-3cab-4ae6-94e2-624046f5aa87_0
临时容器 Ephemeral Containers
- 生产环境中,为了优化镜像的体积和提高镜像的安全性,并不会在容器中安装太多高危工具。
- 比如curl、wget、dig 以及常用的 net-tools 等。
这样做虽然提高了镜像的安全性,但是也带来了一些不便
- 无法査看容器内的进程情况
- 无法査看容器内的链接情况
- 服务出了问题无法很方便的进行排査
上述操作并非经常使用,没有必要从一开始就安装这些工具,但是等到用他们的时候再安装也是一件很麻烦的事情,那该如何处理这个问题呢?
- 1.16 版本后,Kubernetes 引入了 Ephemeral containers 的概念,可以不用安装第三方工具即可实现在线 Debug 操作。
概念
与其他容器的不同之处
- 临时容器是被临时添加到 Pod 上,用于在线调试应用的
- 它永远不会自动重启,因此不适用与构建应用程序,
- 临时容器的声明和普通容器类似,但是临时容器没有端口配置,因此不能使用像 ports、livenessprobe、readnessProbe、resources 这样的字段
- 临时容器只是用来调试程序的,它的状态不会影响其他正常容器,所以它并不需要这些字段配置
临时容器使用 API 中一种特殊的 Ephemeral containers 处理器进行创建,而不是直接添加到pod.spec 字段,因此无法使用 kubectl edit 来添加临时容器
与常规容器一样,将临时容器添加到 Pod后,将不能更改或删除临时容器,但是当添加了临时容器的 Pod 重启后,临时容器就会被销毁。
临时容器是为了调试程序而设计的,添加临时容器时,最好使用一个包含所有常用工具的镜像进行创建。
当业务容器崩溃或容器镜像不包含调试工具而导致 kubectl exec 不可用时,临时容器对于交互故障排査和在线 Debug 很有用。
尤其是在使用像不包含任何 shell 和其它工具的 destroless 镜像作为基础镜像时,虽然可以减少攻击面和漏洞,但对于问题的排査会变得尤为棘手,此时临时容器就可以发挥很大的作用,带来诸多便利性。
开启临时容器功能
- 开启临时容器比较简单,只需要在 featrue-gates 字段添加 Ephemeral Containers=true,然后使用临时容器的资源开启共享命名空间 shareProcessNamespace: true
在所有 Kubernetes Node 节点配置 kubectl 参数
[root@k8s-node01 ~]# vim /etc/kubernetes/kubelet.conf
#在文件末尾添加
dratureGates:
EphemeraContainers: true
修改完成后重启 kubectl 进程
[root@k8s-node01 ~]# systemctl daemon-reload
[root@k8s-node01 ~]# systemctl restart kubelet
在所有 master 节点配置 APIServer 组件
[root@k8s-node01 ~]# vim /usr/lib/systemd/system/kubelet.service
[Service]
ExecStart=/usr/bin/kubelet --feature-gates=EphmemralContainers=true
Restart=always
StartLimitInterval=0
RestartSec=10
- --feature-gates=EphmemralContainers=true
[root@k8s-master ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=192.168.10.101
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
#在 command 字段末尾增加
- --feature-gates=EphemeralContainers=true
- - --feature-gates=EphemeralContainers=true
[root@k8s-master ~]# vim /etc/kubernetes/manifests/kube-scheduler.yaml
spec:
containers:
- command:
- kube-scheduler
- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
- --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
- --bind-address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
#在 command 字段末尾增加
- --feature-gates=EphemeralContainers=true
- - --feature-gates=EphemeralContainers=true
[root@k8s-master ~]# vim /etc/sysconfig/kubelet
#添加
KUBELET_EXTRA_ARGS="--feature-gates=EphemeralContainers=true"
- KUBELET_EXTRA_ARGS="--feature-gates=EphemeralContainers=true"
修改完成后重启 kubectl 进程
[root@k8s-master ~]# systemctl daemon-reload
[root@k8s-master ~]# systemctl restart kubelet
临时容器的使用
创建一个 tomcat 的资源清单
[root@k8s-master ~]# cat pod-tomcat.yaml
apiVersion: v1
kind: Pod
metadata:
name: tomcat-test
namespace: default
labels:
app: tomcat
spec:
containers:
- name: tomcat-java
ports:
- containerPort: 8080
image: kubeguide/tomcat-app:v1
imagePullPolicy: IfNotPresent
创建 Pod
[root@k8s-master ~]# kubectl apply -f pod-tomcat.yaml
查看状态
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
tomcat-test 1/1 Running 0 64s
为 tomcat 的 pod 创建临时容器
[root@k8s-master ~]# kubectl debug -it tomcat-test --image=busybox:1.28 --target=tomcat-java
//执行后稍等一会,能够进入 tomcat 容器的交互界面
/ #ps -ef | grep tomcat
1 root 0:02/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
-Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.classLoaderLogManager
-Djdk.tls.ephemeralDHKeysize=2048-Djava.endorsed.dirs=/usr/local/tomcat/endorsed
-classpath/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
-Dcatalina.base=/usr/local/tomcat-Dcatalina.home=/usr/local/tomcat
-Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
41 root 0:00 grep tomcat
查看 tomcat-test 这个 pod 是否已经有临时部署
root@k8s-master ~]# kubectl describe pods tomcat-test
Containers:
tomcat-java:
Container ID: docker://d37b1f054d9c206df7847d3c51b51cb208e7b142a492597851888f8c83eda1fa
Image: kubeguide/tomcat-app:v1
Image ID: docker-pullable: //kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc526b96ac69ac4
Port: 8080/TCP
Host Port: 0/TCP
State: Running
started: Sun,09 Jul 2023 17:37:57 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts :
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4ggbn (ro)
Ephemeral containers:
debugger-czh68:
Container ID:docker://eaf268329bb4f26545b4ddbdfdc4361bcfb2906ec7a86d3a466c104ba8a837d5
Image: busybox:1.28
Image ID:
docker-pullable://busvbox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
Port: <none>
Host Port: <none>
State: Terminated
Reason : Completed
Exit Code: 0
Started: Sun, 09 Jul 2023 17:38:49 +0800
Finished: Sun,09 Jul 2023 17:41:09 +0800
自动扩缩容 HPA
- 集群安装的过程中,我们可以安装一个叫 Metrics Server 的组件
- 该组件在集群中负责采集 Pod和 Node 的度量值指标,比如 Pod 的 CPU、内存使用率和节点的内存、CPU 使用率
- 而且安装的 Dashboard可以展示 CPU、内存信息也是依靠 Metrics Server 的。
- 该组件不仅仅是用来展示数据的,还可以使用 Metrics Server 提供的数据结合 Kubernetes 的 HPA 功能实现 Pod 的自动扩缩容。
什么是 HPA
- (Horizontal Pod Autoscaler,水平 Pod 自动伸缩器)
- 可以根据观察到的 CPU、内存使用率或自定义度量标准来自动扩展或缩容 Pod 的数量。
- HPA 不适用于无法缩放的对象,比如 Daemonset
- HPA 控制器会定期调整 RC或 Deployment 的副本数,以使观察到的平均 CPU 利用率与用户指定的目标相匹配。
- HPA 需要 Metrics Server 获取度量指标
HPA 实践 -- 实现 Web 服务器的自动伸缩特性
生产环境中,总会有一些意想不到的事情发生
- 比如公司网站流量突然升高,
- 此时之前创建的 Pod已不足以支撑所有的访问
- 而运维人员也不可能 24 小时守着业务服务
这时就可以通过配置 HPA,实现负载过高的情况下自动扩容 Pod 副本数以分摊高并发的流量,当流量恢复正常后,HPA 会自动缩减 Pod 的数量。
测试实现一个 web 服务器的自动伸缩特性
首先用 Deployment 启动一个 Nginx 服务(需配置 request 参数)
[root@k8s-master ~]# vim nginx-deployment.yaml
apiVersion: apps/v1kind: Deployment
metadata:
name: nginx-server
labels:
name: nginx-server
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginX
spec:
containers:
- name: nginx
resources:
requests:
cpu: 10m
image: nginx:1.7.9
ports:
- name: nginx
containerPort: 80
创建文件
[root@k8s-master ~]# kubectl create -f nginx-deployment.yaml
配置 nginx-server 的 Service
[root@k8s-master ~]# kubectl expose deployment nginx-server --port=80
使用 kubectl autoscale 命令创建 HPA
[root@k8s-master ~]# kubectl autoscale deployment nginx-server --cpu-percent=10 --min=1 --max=10
- 此 HPA 将根据 CPU的使用率自动增加和减少副本数量,上述设置的是 CPU 使用率超过 10%(--cpu-percent 参数指定)就会增加 Pod 的数量,以保持所有 Pod 的平均 CPU 利用率为 18%,允许最大的 Pod 数量为 10(--max),最少的 Pod 数为1(--min)。
查看当前 HPA 的状态
- 因为未对其发送任何需求,所以当前 PCU 使用率为 0%
[root@k8s-master ~]# kubectl get hpa
NAME REFERECE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 0%/10% 1 10 2 3m41s
- 如果看不到 TARGET 的 CPU 利用率,可以多等十几秒。
查看当前 Nginx 的 Service 地址
[root@k8s-master ~]# kubectl get service -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h
nginx-server ClusterIP 10.107.217.171 <none> 80/TCP 43s
压力测试(用新终端)
[root@k8s-master ~]# while true;do wget -q -o- http://10.107.217.170 >/dev/null;done
查看 HPA 状态
- 等待一分钟左右,再次査看 HPA,可以看到 Pod 的 CPU 已经升高。
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 55%/10% 1 10 2 3m51s
再次查看 Pod,可以看到已经扩容(Pod 副本增加到了10个)
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-server-6ddcfd4c8f-2k2nb 1/1 Running 0 5m46s
nginx-server-6ddcfd4c8f-42wfb 1/1 Running 0 84s
nginx-server-6ddcfd4c8f-8w7hj 1/1 Running 0 4m6s
nginx-server-6ddcfd4c8f-bw7hj 1/1 Running 0 90s
nginx-server-6ddcfd4c8f-f4s85 1/1 Running 0 60s
nginx-server-6ddcfd4c8f-gvc5s 1/1 Running 0 114s
nginx-server-6ddcfd4c8f-fjf36 1/1 Running 0 125s
nginx-server-6ddcfd4c8f-faw49 1/1 Running 0 46s
nginx-server-6ddcfd4c8f-htd5w 1/1 Running 0 49s
nginx-server-6ddcfd4c8f-ued36 1/1 Running 0 100s
停止压力测试
- Ctr1+c 终止终端的测试,等待一分钟左右,再次查看HPA 状态
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 2%/10% 1 10 10 6m49s
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 0%/10% 1 10 10 7m9s
查看 pod 的副本数
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-server-6ddcfd4c8f-8wt72 1/1 Running 8 11m
- 需要多等待几分钟后,才能看到缩容后的pod
清除
[root@k8s-master ~]# kubectl delete -f nginx-deployment.yaml
[root@k8s-master ~]# kubectl delete -f mysql-deployment.yaml
[root@k8s-master ~]# kubectl delete -f redis-deployment.yaml
[root@k8s-master ~]# kubectl delete -f inito1.yml
[root@k8s-master ~]# kubectl delete -f inito2.yml
Taint 和 Toleration
生产环境中经常有的需求
- master 节点只部署系统组件容器,比如 Calico、Metrics、Server 、Dashboard 等,不应该部署业务应用
- 新添加节点不应该立即就允许部署业务容器,也就是新节点需要经过完整性即稳定性测试才可以被允许调度
- 某些节点可能需要进行系统升级或者其他维护,可能会引起节点上的容器不可用,此时需要将该节点上的 Pod 漂移至其他节点,再进行维护。
- 有一些 GPU 服务器或其他专用节点服务器,除了指定的 Pod 之外,并不想让他们部署其他的 Pod。
对这样的需求,Kubernetes 抽象了污点(Taint)和容忍(Toleration)的概念,可以非常方便的熟实现这些需求
污点和容忍的基本概念
Taint(污点)
- 作用在节点上,能够使节点排斥一类特定的Pod。
Toleration(容忍)
- 作用在 Pod 上,也就是可以兼容某类污点。
Taint(污点)和 Toleration(容忍)
- 可以作用于 node 和 pod 上,其目的是优化 pod 在集群间的调度,这跟节点亲和性类似,只不过它们作用的方式相反,具有 taint 的 node 和 pod 是互斥关系,而具有节点亲和性关系的 node 和 pod 是相吸的。
- 另外还有可以给 node 节点设置 label,通过给 pod 设置nodeselector 将 pod 调度到具有匹配标签的节点上。
Taint 和 Toleration 相互配合
- 可以用来避免 Pod 被分配到不合适的节点上。
- 每个节点上都可以应用一个或多个 taint ,表示对于那些不能容忍这些 taint 的 Pod,是不会被该节点接受的。
- 如果将 toleration 应用于 Pod 上,则表示这些 Pod 可以(但不一定)被调度到具有匹配 taint 的节点上。
污点的使用
- 如果一个节点被标记为有污点,意味着不允许 pod 调度到该节点,除非 pod 也被标记为可以容忍污点节点。
- 使用 kubeadm 部署的 k8s 集群的时候应该会发现,应用是不会调度到 master 节点的。
- 因为 kubeadm 部署的 k8s 集群默认给 master 节点加了 Taints(污点)。
给节点 node01 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule。
表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点。
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key01=value1:NoSchedule
查看节点是否为污点
[root@k8s-master ~]# kubectl describe node k8s-node01 | grep Taints
若要移除上述命令所添加的污点,在后面加上一个横杠即可
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key01=value1:NoSchedule-
- 每个污点有一个 key 和 value 作为污点的标签
- 其中 value 可以为空,effect 描述污点的作用:key=value:effect
当前 taint effect 支持三个选项
- NoSchedule:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上 ,不会驱逐已存在该节点上的 Pod 。
- PreferNoSchedule:表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上 ,不会驱逐已存在该节点上的 Pod。
- NoExecute:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去,若节点上设置了污点并用的是 NoExecute 策略,node 节点上的 pod 会被全部
- 驱逐,但是如果是 Deployment 或者 statefulset 资源类型,为了维持副本数量则会在别的Node 上再创建新的 Pod。
容忍的定义
- 设置了污点的 Node 将根据 taint 的 effect: NoSchedule、PreferNoschedule、NoExecute 和 Pod之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。
我们可以在 Pod 上设置容忍(Toleration)
- 意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。
示例
设置污点
[root@k8s-master ~]# kubectl taint nodes k8s-node01 check=mycheck:NoExecute
[root@k8s-master ~]# kubectl taint nodes k8s-node02 check=mycheck:NoExecute
创建测试模板
[root@k8s-master ~]# vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp01
labels:
app: myapp01
spec:
containers:
- name: with-node-affinity
image: nginx:1.7.9
运行
[root@k8s-master ~]# kubectl apply -f pod1.yaml
两个 Node 上都设置了污点后,此时 Pod 将无法创建成功
[root@k8s-master ~]# kubectl get pods -o wide
删除 Pod
[root@k8s-master ~]# kubectl delete -f pod1.yaml
修改测试模板
在 pod1.yaml 的基础上增加容忍参数
[root@k8s-master ~]# vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp01
labels:
app: myapp01
spec:
containers:
- name: with-node-affinity
image: nginx:1.7.9
tolerations:
- key: check
operator: Equal
value: mycheck
effect: NoExecute
tolerationSeconds: 60
- tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间,即 60 秒后该 pod 会被驱逐掉,默认的是一天。
- tolerations:
- - key: check
- operator: Equal
- value: mycheck
- effect: NoExecute
- tolerationSeconds: 60
在 pod 中定义容忍度的说明
- 可以在 Podspec 中定义 Pod 的容忍度。
下面两个容忍度均与上面使用 kubectl taint 命令创建的污点相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度都能够被分配到 k8s-node01
tolerations:
- key: check
operator: Equal
value: mycheck
effect: NoExecute
tolerationSeconds: 60
tolerations:
- key: check
operator: "Exists"
effect: NoExecute
- #其中的 key、vaule、effect 都要与 Node 上设置的 taint 保持一致
- #operator 的值为 Exists 将会忽略 value 值,即存在即可,为 Equal 时需要指定污点的 value。
- #value:mycheck 污点的 check 名称,指明需要容忍的污点
- #effect:NoExecute 驱逐行为,设置了tolerationseconds,就按照其设定的时间后驱逐 Pod
- #tolerationseconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间,即 60 秒后该 pod 会被驱逐掉,默认的是一天。
运行新的 pod
[root@k8s-master ~]# kubectl apply -f pod2.yaml
设置了容忍之后 Pod 创建成功
[root@k8s-master ~]# kubectl get pods -o wide
完成实验后将污点取消
[root@k8s-master ~]# kubectl taint node k8s-node01 check=mycheck:NoExecute-
[root@k8s-master ~]# kubectl taint node k8s-node02 check=mycheck:NoExecute-
容忍的基本用法
容忍污点的条件
- pod 的 Toleration 声明中的 key 和 effect 需要与 Taint 的设置保持一致,并且满足以下条件之一
#不完全匹配,operator 的值为 Exists,这时无需指定 value
tolerations:
- key: check
operator: "Exists"
effect: NoExecute
- operator: "Exists"
#完全匹配,operator 的值为 Equal 并且 value 相等(可以设置多个tolerations)
tolerations:
- key: check
operator: Equal
value: mycheck
effect: NoExecute
- operator: Equal
- value: mycheck
#如果不指定 operator 则默认值为 Equal
tolerations:
- key: check
operator: Equal
value: mycheck
effect: NoExecute
- value: mycheck
还有两个特例
- # 空的 key 、value、effect 配合 Exists 操作符能够匹配所有的键和值,即能容忍所有污点。
tolerations:
operator: "Exists"
- operator: "Exists"
# 空的 effect 匹配所有的 effect,即能容忍任何 key 名为 check 的污点.
tolerations:
- key: check
operator: "Exists"
- operator: "Exists"
effect 的取值说明
- NoSchedule:如果一个 pod 没有声明容忍这个 Taint,则系统不会把该 Pod 调度到有这个Taint 的 node 上
- PreferNoSchedule:Noschedule 的软限制版本,如果一个 Pod 没有声明容忍这个 Taint,则系统会尽量避免把这个 pod 调度到这一节点上去,但不是强制的
- NoExecute:定义 pod 的驱逐行为,以应对节点故障
NoExecute 这个 Taint 效果对节点上正在运行的 pod 有以下影响
- 没有设置 Toleration 的 Pod 会被立刻驱逐
- 配置了对应 Toleration 的 pod,如果没有为 tolerationseconds 赋值,则会一直留在这节点中
- 配置了对应 Toleration 的 pod 且指定了 tolerationseconds 值,则会在指定时间后驱逐
一些特殊的设置
#有多个 Master 存在时,为了防止资源浪费,可以将 master 节点设置为PreferNoSchedule 污点,让Pod 可在 Master 上临时创建:
[root@k8s-master ~]# kubectl taint node k8s-master node-role.kubernetes,io/master=:PreferNoschedule
#如果某个 Node 更新升级系统组件,为了防止业务长时间中断,可以先在该 Node 设置 NoExecute 污点,把该 Node 上的 Pod 都驱逐出去
[root@k8s-master ~]# kubectl taint node k8s-node01 check=mycheck:NoExecute
#此时如果别的 Node 资源不够用,可临时给 Master 设置 PreferNoSchedule 污点,让 Pod 可在Master 上临时创建
[root@k8s-master ~]# kubectl taint node k8s-master node-role.kubernetes.io/master=:PreferNoSchedule
#待所有 Node 的更新操作都完成后,再去除污点kubectl taint node node01
[root@k8s-master ~]# check=mycheck:NoExecute-
多污点与多容忍配置
- 系统允许在同一个 node 上设置多个 taint,也可以在 pod 上设置多个 Toleration
- Kubernetes 调度器处理多个 Taint 和 Toleration 能够匹配的部分,剩下的没有忽略掉的Taint 就是对 Pod 的效果了。
几种特殊情况
- 如果剩余的 Taint 中存在 effect=NoSchedule,则调度器不会把该 pod 调度到这一节点上。
- 如果剩余的 Taint 中没有 NoSchedule 的效果,但是有 PreferNoSchedule 效果,则调度器会尝试不会将 pod 指派给这个节点。
- 如果剩余 Taint 的效果有 NoExecute 的,并且这个 pod 已经在该节点运行,则会被驱逐;如果没有在该节点运行,也不会再被调度到该节点上。
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key1=value1:NoSchedule
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key1=valuel:NoExecute
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key2=value2:NoSchedule
去除污点
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key1=value1:NoSchedule-
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key1=valuel:NoExecute-
[root@k8s-master ~]# kubectl taint nodes k8s-node01 key2=value2:NoSchedule-
警戒(cordon)和转移(drain)
警戒
- 指将 Node 标记为不可调度的状态
- 这样就不会让新创建的 Pod 在此 Node 上运行
设置警戒
[root@k8s-master ~]# kubectl cordon k8s-node01
该 node 将会变为 SchedulingDisabled 状态
[root@k8s-master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane,master 4d12h v1.23.0
k8s-node01 Ready,SchedulingDisabled <none> 4d12h v1.23.0
k8s-node02 Ready <none> 4d12h v1.23.0
kubectl uncordon 将 Node 标记为可调度的状态
[root@k8s-master ~]# kubectl uncordon k8s-node01
node/k8s-node01 uncordoned
获取 node 节点状态
[root@k8s-master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane,master 4d12h v1.23.0
k8s-node01 Ready <none> 4d12h v1.23.0
k8s-node02 Ready <none> 4d12h v1.23.0
驱逐(转移)pod
- kubectl drain 可以让 Node 节点开始释放所有 pod,并且不接收新的 pod 进程。
drain
- 本意排水,意思是将出问题的 Node 下的 Pod 转移到其它 Node 下运行
[root@k8s-master ~]# kubectl drain k8s-node01 --ignore-daemonsets --delete-local-data --dorce
- -ignore-daemonsets:无视Daemonset管理下的Pod
- -delete-local-data:如果有 mount local volume 的 pod,会强制杀掉该 pod
- -force:强制释放不是控制器管理的 Pod,例如 kube-proxy
执行 drain 命令,会自动做了两件事情
- 设定此 node 为不可调度状态(cordon)
- evict(驱逐)了 Pod
实验完成后,将节点设置为可调度
[root@k8s-master ~]# kubectl uncordon k8s-node01
K8s 亲和性和非亲和性
关于 K8S 对 Pod 的调度,通常情况下 Pod 被分配到哪些 Node 是不需要我们操心的,这个过程会由scheduler 自动实现。
但有时,我们需要让 Pod 按照我们的预想运行在 Node 上(例如某些应用“必须/或者尽量”跑在具有 SSD 存储的节点上,有些彼此相关的 Pod 应用应该跑在同一个节点上)。
为此,k8s为我们提供了这样的策略,我们可以通过使用“亲和性/非亲和性”制定一些规则来实现我们的需求。
亲和性调度可以分为两种方式
- 硬策略
- 软策略
硬策略
- 可以理解为必须
- 如果没有满足条件的节点的话,就不断重试直到满足条件为止。
对应的配置规则
requiredDuringSchedulingIgnoredDuringExecution
软策略
- 可以理解为尽量
- 如果现在没有满足调度要求的节点的话,pod 就会忽略这条规则,继续完成调度的过程,说白了就是满足条件最好了,没有的话也无所谓。
对应的配置规则
preferredDuringSchedulingIgnoredDuringExecution
亲和性的配置规则
分为
- Node 亲和性
- Pod 亲和性
- Pod 反亲和性
nodeAffinity
- 节点亲和性
- 用来控制 Pod 要部署在哪些节点上,以及不能部署在哪些节点上的,这个是 Pod 与 Node 之间匹配规则的。
podAffinity
- pod 亲和性
- 这个是 Pod 与 Pod 之间匹配规则的。
podAntiAffinity
- pod 反亲和性
- 与 podAffinity 相反,用来指定 Pod 不要部署到哪些节点上。
节点硬亲和性
- 可以通过 pod.spec.affinity.nodeAffinity 字段定义,nodeAffinity 字段中支持使用 matchExpressions 属性构建更为复杂的标签选择器。
设置节点标签
- 首先对两个 node 加标签,设置两个 node 标签分别为 node1 和 node2 。
加标签的语法
kubectl label nodes <node-name><label-key>=<label-value>
[root@k8s-master ~]# kubectl label nodes k8s-node01 type=node01
[root@k8s-master ~]# kubectl label nodes k8s-node02 type=node02
如果需要重命名 node 标签,可以使用命令
[root@k8s-master ~]# kubectl label nodes k8s-node01 type=node1 --overwrite
- --overwrite
如果要删除 node 标签(标签名为“type”),可使用命令
[root@k8s-master ~]# kubectl label node k8s-node01 type-
- type-
查看节点标签
[root@k8s-master ~]# kubectl get node --show-labels
设置 pod 亲和性为 type=node1
[root@k8s-master ~l# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node1 的节点上
apiVersion:v1
kind: Pod
metadata:
name: pod01
spec:
#亲和性调度
affinity:
#节点亲和性调度
nodeAffinity:
# 硬策略
requiredDuringSchedulingIgnoredDuringExecution:
#指定亲和性
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In, values: ["node01"]}
containers:
- name: pod01
image: nginx:1.7.9
- values: ["node01"] node1 为节点的标签,该 pod 要调度到标签为 node1 的节点,即 k8s-node01。
部署 pod
[root@k8s-master ~l# kubectl apply -f test01.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod01 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod01 1/1 Running 0 9s 10.244.85.203 k8s-node01 <none> <none>
设置 pod 亲和性为 type=node02
[root@k8s-master ~l# vim test02.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node02 的节点上
apiVersion: v1
kind: Pod
metadata:
name: pod02
spec:
#亲和性调度
affinity:
#节点亲和性调度
nodeAffinity:
# 硬策略
requiredDuringSchedulingIgnoredDuringExecution:
#指定亲和性
nodeSelectorTerms:
- matchExpressions:
- {key: type,operator: In,values: ["node02"]}
containers:
- name: pod02
image: nginx:1.7.9
- values: ["node02"] node02 为节点的标签,该 pod 要调度到标签为 node02 的节点,即 k8s-node02
部署 pod
[root@k8s-master ~l# kubectl apply -f test02.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod01 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod02 1/1 Running 0 5s 10.244.58.195 k8s-node02 <none> <none>
节点软亲和性
- 为节点选择提供了一种柔性的控制器逻辑
- 当条件不满足时,也能够接受被编排在其他不符合条件的节点之上。
- 同时还为每种倾向性提供了 weight 属性以便用于自定义优先级,范围是1-100,越大越优先。
为 pod 设置软亲和性
[root@k8s-master ~l# vim test03.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod03
spec:
#亲和性调度
affinity:
#节点亲和性调度
nodeAffinity:
# 软策略
preferredDuringSchedulingIgnoredDuringExecution:
#指定亲和性及权重
- weight: 60
preference:
matchExpressions:
- {key: type,operator: In,values: ["node01"]}
- weight: 30
preference:
matchExpressions:
- {key: tpye, operator: In,values: ["node02"]}
containers:
- name: pod03
image: nginx:1.7.9
部署 pod
[root@k8s-master ~l# kubectl apply -f test03.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod03 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod03 1/1 Running 0 15s 10.244.85.204 k8s-node01 <none> <none>
- 可以看到,由于 Pod 对 node01 的亲和性,被部署到了 node01 上。
node 软亲和中的权重值不影响 pod 的调度,谁在前面,就亲和谁。
pod 软亲和中的权重值是会影响调度到的节点的,pod 软亲和中谁的权重高就调度到谁那里。
Pod 硬亲和度
- 出于某些需求,将一些 Pod 对象组织在相近的位置(同一节点、机架、区域等),此时这些 pod 对象间的关系为 pod 亲和性。
- Pod 的亲和性调度也存在硬亲和性和软亲和性的区别,他们表示的约束意义同节点亲和性相似。
- Pod 硬亲和性调度也使用 requiredDuringSchedulingIgnoreDuringExecution 属性进行定义。
创建两个基础 Pod,并对它打标签,分别部署到 node01 和 node02 上。
部署第一个基础 Pod
[root@k8s-master ~]# vim test04.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod04
labels:
app:pod04
spec:
affinity:
nodeAffinity:
#pod 硬亲和
requiredDuringschedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In,values: ["node01"]}
containers:
- name: pod04
image: nginx:1.7.9
- pod04 的标签是 pod04,所在的节点标签是 node01
部署
[root@k8s-master ~l# kubectl apply -f test04.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod03 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod04 1/1 Running 0 20s 10.244.85.205 k8s-node01 <none> <none>
部署第二个 Pod
[root@k8s-master ~]# vim test05.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod05
labels:
app: pod05
spec:
affinity:
nodeAffinity:
requiredDuringschedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In,values: ["node02"]}
containers:
- name: pod05
image: nginx:1.7.9
- pod05 的标签是 pod05,所在的节点标签是 node02
部署
[root@k8s-master ~l# kubectl apply -f test05.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod05 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod05 1/1 Running 0 10s 10.244.85.196 k8s-node02 <none> <none>
- 可以看到,一个位于 node01,一个位于 node02。
创建一个亲和 pod05 的 pod
[root@k8s-master ~]# vim test06.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod06
spec:
affinity:
podAffinity:
requiredDuringschedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: type, operator: In,values: ["node05"]}
topologyKey: kubernetes.io/hostname
containers:
- name: pod06
image: nginx:1.7.9
matchExpressions 的 values 值为 pod 的标签。是哪个 pod 的标签,就亲和到哪个 pod 所在的 node 节点
- pod04 的标签是 pod05,所在的节点标签是 node01
- pod05 的标签是 pod05,所在的节点标签是 node02
部署此 pod
[root@k8s-master ~l# kubectl apply -f test06.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod05 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod04 1/1 Running 0 6m56s 10.244.85.203 k8s-node01 <none> <none>
pod05 1/1 Running 0 5m42s 10.244.58.195 k8s-node02 <none> <none>
pod06 1/1 Running 0 18s 10.244.58.196 k8s-node02 <none> <none>
此时,pod05 和 pod06 两个 pod 位于同一个 node 节点。
- pod05 1/1 Running 0 5m42s 10.244.58.195 k8s-node02 <none> <none>
- pod06 1/1 Running 0 18s 10.244.58.196 k8s-node02 <none> <none>
Pod 软亲和度
- Pod 软亲和调度使用方法与 Node 软亲和调度方法类似。
编辑 pod07 的 pod
[root@k8s-master ~]# vim test07.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod07
spec:
#亲和性调度
affinity:
# pod 亲和性调度
podAffinity:
# 软策略
preferredDuringSchedulingIgnoredDuringExecution:
#指定亲和性及权重
- weight:80
#亲和标签
podAffinityTerm:
#标签选择
labelselector:
#正则匹配配
matchExpressions:
- {key:app,operator: In,values: ["pod05"]}
topologyKey: kubernetes.io/hostname
- weight: 20
#亲和标签
podAffinityTerm:
#标签选择
labelselector:
#正则匹配
matchExpressions:
- {key: app,operator: In,values: ["pod04"]}
topologyKey: kubernetes.io/hostname
containers:
- name: pod07
image: nginx:1.7.9
- node 软亲和中的权重值不影响 pod 的调度,谁在前面,就亲和谁。
- pod 软亲和中的权重值是会影响调度到的节点的,pod 软亲和中谁的权重高就调度到谁那里。
- values: ["pod05"] 是 pod05 的 pod 的标签
- values: ["pod04"] 是 pod04 的 pod 的标签
- 正则匹配了 pod 的标签“pod05”,带有 pod05 标签的 pod 是 pod05,该 pod 在 node02 上,于是 pode7的 pod 也调度到了 node02 上。
创建文件
[root@k8s-master ~]# kubectl create -f test07.yaml
获取节点信息
[root@k8s-master ~l# kubectl get pod pod05 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod07 1/1 Running 0 90s 10.244.85.204 k8s-node02 <none> <none>
- pod04 的标签是 webweb,所在的节点标签是 node1
- pod05 的标签是 web,所在的节点标签是 node2
编辑 pod08 的 pod
- 修改 yaml 文件中的权重,使其与 pod04 亲和性更高,因为 pod04 在 node01 上,pod08 也会被部署到node01 上
[root@k8s-master ~]# vim test08.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod08
spec:
#亲和性调度
affinity:
# pod 亲和性调度
podAffinity:
# 软策略
preferredDuringSchedulingIgnoredDuringExecution:
#指定亲和性及权重
- weight: 20
#亲和标签
podAffinityTerm:
#标签选择
labelselector:
#正则匹配配
matchExpressions:
- {key:app,operator: In,values: ["pod05"]}
topologyKey: kubernetes.io/hostname
- weight: 80
#亲和标签
podAffinityTerm:
#标签选择
labelselector:
#正则匹配
matchExpressions:
- {key: app, operator: In,values: ["pod04"]}
topologyKey: kubernetes.io/hostname
containers:
- name: pod08
image: nginx:1.7.9
- 匹配了 “pod04” 标签的亲和度对应的权重值更高,按照此权重,pod08 会被调度到标签为“pod84”的pod 所在的 node 节点,即 node01
- pod04 的标签是 pod04,所在的节点标签是 node01
- pod05 的标签是 pod05,所在的节点标签是 node02
创建文件
[root@k8s-master ~]# kubectl create -f test08.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod05 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod08 1/1 Running 0 2m14s 10.244.85.205 k8s-node01 <none> <none>
pod 反亲和
[root@k8s-master ~]# vim test09.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod09
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelselector:
matchExpressions:
- {key: app,operator: In,values: ["pod05"]}
topologyKey: kubernetes.io/hostname
containers:
- name: poda9
image: nginx:1.7.9
创建文件
[root@k8s-master ~]# kubectl create -f test09.yaml
查看节点信息
[root@k8s-master ~l# kubectl get pod pod09 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READIESS GATES
pod09 1/1 Running 0 16s 10.244.85.206 k8s-node01 <none> <none>
- 査看 pod09 所在的节点,因为设置的反亲和 pod05,所以,pod05 在node02的话,pod89 就会在 node01
小阿轩yx-Kubernetes高级调度