kubectl 概述
(1) comand:指定要对资源执行的操作,例如 create、get、describe edit(对线上操作) 和 delete
(2) TYPE:指定资源类型,资源类型是大小写敏感的,开发者能够以单数、复数和缩略的 形式。例如:
(3)NAME:指定资源的名称,名称也大小写敏感的。如果省略名称,则会显示所有的资源,
例如:
(4)flags:指定可选的参数。例如,可用-s 或者–server 参数指定 Kubernetes APIserver 的地址和端口。
kubectl --help 获取信息
kubectl get --help 获取get信息
kubectl子命令使用分类
基础部署命令
create:通过文件名或标准输入创建资源
expose:将一个资源公开为一个新的service
run:在集群中运行一个特定的pod
set:在对象上设置特定的功能
get:显示一个或多个资源
explain:文档参考资料
edit:使用默认编辑器编辑一个资源
delete:通过文件名、标准输入、资源名称或标签选择器来删除资源
apply:通过文件名或标准输入对资源应用配置
监控管理命令
rollout:管理资源的发布
rolling-update:对给定的复制控制器滚动更新
scale:扩容或缩容pod数量,depoloyment、RC、replicaset或job
autoscale:创建一个自动选择扩容或缩容并设置Pod数量
certificate:修改证书资源
cluster-info 显示集群信息
top:显示资源(cpu、内存、存储)使用,需要heapster Metrics组件运行
cordon:标记节点不可调度
uncordon:标记节点可以调度
drain:驱逐节点上的应用,准备下线维护
taint:修改节点taint标记
故障和调试命令
describe:显示特定资源或资源组的详细信息以及pod创建过程(可以监控pod创建的每一步)
logs:在一个Pod中打印一个容器日志。如果pod不只一个容器,容器名称是可选的
attach:附加到一个运行的容器
exec:执行命令到容器(一般添加-it -c可以指定容器)
port-forward:转发一个或多个本地端口到一个pod
proxy:运行一个proxy到k8s API server
cp:拷贝文件或目录到容器中
auth:检查授权
其他命令:
高级命令
patch:使用补丁修改、更新资源的字段
replace:通过文件名或标准输入替换一个资源
convert:不同API版本之间转换配置文件
设置命令:
lable:更新资源上的标签
annotate:更新资源上的注释
completion:用于实现kubectl工具自动补全
其他命令
api-versions:打印受支持的API版本
config:修改kubeconfig文件(用于访问API,比如配置认证信息)
plugin:运行一个命令行插件
version:打印客户端和服务版本信息
kubectl常见参数:
-n 指定命名空间
-n kube-system
-l 指定标签
-l key/key=value
-o 指定输出形式
-o yaml
-o wide
-o json
-w 持续监控
资源清单YAML 文件概述
概念:k8s中所有内容都抽象为资源,资源实例化之后,叫做对象
名称空间级别作用域是一个namespace的角度去说明,都是可以被ns隔离的,集群资源是站在集群管理的角度,如果不限制命名空间的资源限制,任何一个命名空间都可能会把整个集群资源占满,元数据型资源不直接作用资源,只是一个数据值
YAML 文件概述
k8s 集群中对资源管理和资源对象编排部署都可以通过声明样式(YAML)文件来解决,也就是可以把需要对资源对象操作编辑到 YAML 格式文件中,我们把这种文件叫做资源清单文件,通过 kubectl 命令直接使用资源清单文件就可以实现对大量的资源对象进行编排部署了。
YAML 文件书写格式
YAML 介绍
YAML :仍是一种标记语言。为了强调这种语言以数据做为中心,而不是以标记语言为重点。
YAML 是一个可读性高,用来表达数据序列的格式。
YAML 基本语法
- 使用空格做为缩进,字符后需要一个空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- 低版本缩进时不允许使用 Tab 键,只允许使用空格
*---表示新文件的开始 - 使用#标识注释,从这个字符一直到行尾,都会被解释器忽略
api版本查看:
kubectl api-versions
v1省略了一个默认的core核心组,其他都是“/”实现具体版本,不同版本
文件组成部分:
控制器定义、被控制对象
控制器:我要控制什么 被控制对象:具体如何被控制
大致流程:API版本、资源类型、资源元数据、资源规格、模板
一个pod所使用的资源对象
字段配置格式
apiVersion <string> #表示字符串类型
metadata <Object> #表示需要嵌套多层字段
labels <map[string]string> #表示由k:v组成的映射
finalizers <[]string> #表示字串列表
ownerReferences <[]Object> #表示对象列表
hostPID <boolean> #布尔类型
priority <integer> #整型
name <string> -required- #如果类型后面接 -required-,表示为必填字段
小练习
如何记忆关键字:
如何记忆关键字:
kubectl explain 资源
如何记住顶级关键字下的关键字:
kubectl explain 关键字.关键字
如何快速编写YAML文件:
使用kubectl create命令直接生成yam文件
kubectl create deployment web --image=nginx -o yaml --dry-run >k8s-master.yaml
cat k8s-master.yaml
cat k8s-master.yaml
使用kubectl get导出从已经部署好的应用
kubectl get deploy nginx -o yaml > nginx.yaml
pod知识
Pod 概述
Pod 是 k8s 系统中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最
小资源对象模型,也是在 k8s 上运行容器化应用的资源对象,其他的资源对象都是用来支
撑或者扩展 Pod 对象功能的,比如控制器对象是用来管控 Pod 对象的,Service 或者
Ingress 资源对象是用来暴露 Pod 引用对象的,PersistentVolume 资源对象是用来为 Pod
提供存储等等,k8s 不会直接处理容器,而是 Pod,Pod 是由一个或多个 container 组成
Pod 是 Kubernetes 的最重要概念,每一个 Pod 都有一个特殊的被称为”根容器“的 Pause
容器。Pause 容器对应的镜像属于 Kubernetes 平台的一部分,除了 Pause 容器,每个 Pod
还包含一个或多个紧密相关的用户业务容器
pod存在的意义
pod启动生态(重点):
pod生命周期
Pause容器进行初始化网络栈和共享网络卷之后,初始化容器initC必须有终止逻辑,必须是线性启动,一般用来先编译,执行一些容器启动前检测脚本,如果检测失败同时删除所有initC以及pause容器,重新创建pod,当然也可以跳过initC检测,mainC阶段首先执行启动前命令(不一定在mainC前就会启动完成),然后启动mainC的启动命令,mainC并行启动,主容器至少为1个,在容器执行期间,可以启动就绪探针(不会立刻检测,一般会有多个检测周期进行判断)和存活探针用来测试TCP、HTTP(判断是否在200<=值<400)协议或者代码是否执行成功,如果失败重启容器,关闭前先执行关闭前命令,然后才kill掉mainC
InitC
初始化容器的用途
- Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码;
- Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低;
- Init容器可以以root身份运行,执行一些高权限命令;
- Init容器相关操作执行完成以后即退出,不会给业务容器带来安全隐患。
初始化容器和普通容器的区别
Init 容器与普通的容器非常像,除了如下几点:
- 它们总是运行到成功完成;
- 上一个运行完成才会运行下一个;
- 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,但是Pod 对应的 restartPolicy 值为 Never,Kubernetes 不会重新启动 Pod。
- Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe
在主应用启动之前,做一些初始化的操作,比如创建文件、修改内核参数、等待依赖程序启动或其他需要在主程序启动之前需要做的工作
初始化容器和PostStart区别
PostStart:依赖主应用的环境,而且并不一定先于Command运行
InitContainer:不依赖主应用的环境,可以有更高的权限和更多的工具,一定会在主应用启动之前完成
初始化容器示例
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test-init
name: test-init
namespace: kube-public
spec:
replicas: 1
selector:
matchLabels:
app: test-init
template:
metadata:
labels:
app: test-init
spec:
initContainers: # 预先创建一个文件
- command:
- sh
- -c
- touch /mnt/test-init.txt
image: nginx
imagePullPolicy: IfNotPresent
name: init-touch
volumeMounts:
- name: data
mountPath: /mnt
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: test-init
volumeMounts:
- name: data
mountPath: /mnt
volumes:
- name: data
emptyDir: {}
K8s临时容器EphemeralContainer
传统物理机和容器经常会遇到的黑客侵入问题,原因:容器具有root权限,或安装了wget,curl,netstat等工具
临时容器具有Debug工具,可以在pod中注入一个含有root用户的容器
开启临时容器(以下为二进制方式,kubeadmin自己修改manifaset)
# master节点:
vi /usr/lib/systemd/system/kube-apiserver.service
--feature-gates=EphemeralContainers=true
vi /usr/lib/systemd/system/kube-controller-manager.service
--feature-gates=EphemeralContainers=true
vi /usr/lib/systemd/system/kube-scheduler.service
--feature-gates=EphemeralContainers=true
# 所有节点
vi /usr/lib/systemd/system/kube-proxy.service
--feature-gates=EphemeralContainers=true
vi /etc/kubernetes/kubelet-conf.yml
featureGates:
EphemeralContainers: true
重启所有服务kube-apiserver kube-sechduler kube-controller-manager kubelet kube-proxy
注入容器,并打开一个控制台
容器:kubectl debug redis-new-5b577b46c7-2jv4j -ti --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools
节点:kubectl debug node/k8s-node01 -it --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools
启动策略和健康检查:
实验测试:验证initC和MainC以及启动前命令和关闭前命令的实现机制:
app.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: nginx
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
STATUS:0/2表示有两个InitC尚未完成,当然READY指的是MainC的状态尚未完成,即使InitC完成之后也需要等待就绪探测成功,MainC才会进入READY状态
kubectl describe pod myapp-pod
查看进度发现镜像已经获取成功,正在创建init-myservice的InitC容器,但是一直都没有创建完成,因为nslookup命令并没有成功执行
kubectl logs myapp-pod -c init-myservice
myservice.yaml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
kubectl create -f myservice.yaml
成功创建myservice之后,第一个InitC创建完成,进入第二个InitC的创建:
kubectl describe pod myapp-pod
mydb.yaml
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
kubectl create -f mydb.yaml
查看进度:
kubectl describe pod myapp-pod
kubectl get pods -o wide
通过标签建立svc和pod的关系:
容器检查
一般方式:通过running查看
一个pod中有可能有多个mainC,如果不止一个MainC有就绪探测,直到最后一个MainC就绪时才可通过被访问,如果没有就绪探测,默认为已经就绪
探针:
在 Kubernetes 中,liveness probe、readiness probe 和 startup probe 是由 kubelet
直接调用的,这些探针是 Kubernetes 对容器的健康和启动状态进行监控的机制,由 kubelet
负责实现和执行。当容器启动后,kubelet
会周期性地执行这些探针的检查,并根据探针的结果来判断容器的状态。
具体来说:
- Liveness Probe (存活性探针):
- 用于判断容器是否仍然存活。如果 liveness probe 失败,
kubelet
将杀死容器,并根据容器的重启策略采取相应的操作。
- 用于判断容器是否仍然存活。如果 liveness probe 失败,
- Readiness Probe (就绪性探针):
- 用于判断容器是否已经准备好接收流量。如果 readiness probe 失败,容器将被从 Service 的负载均衡池中剔除,防止接收新的流量。
- Startup Probe (启动探针):
- 用于判断容器是否已经完成启动过程。与 liveness probe 不同,startup probe 只在容器启动时执行一次。如果 startup probe 失败,
kubelet
将重启容器。
- 用于判断容器是否已经完成启动过程。与 liveness probe 不同,startup probe 只在容器启动时执行一次。如果 startup probe 失败,
这里强调一些startup Probe
startup Probe
主要用于以下几个方面:
- 避免过早启动报告就绪:
- 有些应用在启动时需要一些时间来准备好接收流量。使用
Startup Probe
可以避免容器在尚未准备好的情况下报告为就绪。这有助于防止容器在启动过程中接收流量,从而避免对应用的影响。
- 有些应用在启动时需要一些时间来准备好接收流量。使用
- 控制容器的启动时间:
Startup Probe
允许设置一个超时时间,如果在指定时间内探针一直失败,那么容器将被认为启动失败,从而可能导致容器的重启或其他容器的处理。
- 确保应用启动成功:
Startup Probe
的目标是确保应用在启动时成功初始化。这对于一些需要在启动时执行特定初始化操作的应用程序非常有用。
三种探测方式的参数:
livenessProbe:
- 首先是这三个参数
httpGet
tcpSocket
exec
- initialDelaySeconds: 容器启动后等待多少秒才开始执行探针检查。适用于需要一定时间才能准备好的应用程序。
- timeoutSeconds: 探针请求的超时时间。如果超时则被视为失败。
- periodSeconds: 探针执行的周期,即多长时间检查一次容器的健康状态。
- successThreshold: 连续成功的探测次数,达到该次数后才认为容器健康(通常为 1)。
- failureThreshold: 连续失败的探测次数,达到该次数后认为容器不健康,Kubernetes 会重启容器(默认值为 3)。
readinessProbe:
- 首先是这三个参数
httpGet
tcpSocket
exec
- initialDelaySeconds:在启动容器后,Kubernetes 等待多长时间后才开始进行健康检查。通常用于给应用程序一些时间来初始化。
- timeoutSeconds:指定健康检查的超时时间。如果健康检查在该时间内没有返回结果,将视为失败。
- periodSeconds:多久检查一次容器的健康状态(以秒为单位)。
- successThreshold:在认为容器就绪之前,成功的检查次数需要达到的次数。
- failureThreshold:在容器被认为不再就绪之前,失败的检查次数可以达到的次数。
Startup Probe:
- initialDelaySeconds: 启动探针的初始延迟时间,以秒为单位,表示在容器启动后,开始进行探测的时间。
- timeoutSeconds: 每次探测的超时时间,以秒为单位。如果探测未在此时间内完成,将被视为失败。
- periodSeconds: 探测的间隔时间,以秒为单位,表示在每次探测之间的间隔时间。
- failureThreshold: 连续失败的探测次数,达到这个次数后,容器将被认为启动失败。默认值是 3。
- successThreshold: 连续成功的探测次数,表示在认为容器已经成功启动前,需要成功的探测次数。默认值是 1。
- exec / httpGet / tcpSocket: 探测的具体方式,可以通过执行命令、发送 HTTP 请求或检测 TCP 连接来实现。
就绪探测案例:
readinessProbe-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
labels:
app: myapp
spec:
containers:
- name: readiness-httpget-container
image: wangyanglinux/myapp:v1
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
这里index1.html为了故意让报错404
kubectl describe pod readiness-httpget-pod
改正:
kubectl exec -it readiness-httpget-pod sh
kubectl expose pod readiness-httpget-pod --type=NodePort --port=80
存活探测案例:
1、通过命令返回状态码测试存活检测(exec 测试文件是否存在)
livenessProbe-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: wangyanglinux/myapp:v1
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
解读:此实验60秒后将不满足存活条件
kubectl create -f livenessProbe-exec.yaml
60秒后健康检查失败,重启容器:
2、测试主页curl访问,如果主页无法访问(httpGet方法),容器重建
livenessProbe-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: wangyanglinux/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: 80
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 3
kubectl create -f livenessProbe-httpget.yaml
尝试让容器内部httpget请求失败:
kubectl exec -it liveness-httpget-pod sh 将index.html文件替换掉
kubectl describe pod liveness-httpget-pod
3、测试TCP端口(TCP socket)
livenessProbe-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v1
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
timeoutSeconds: 1
starup
至于k8s 1.6中增加的【starup】探针是用于判定应用程序容器什么时候启动了,而启用这个探针主要目的是希望容器在启动成功后再进行存活性和就绪检查,确保这些存活、就绪探测器不会影响应用程序的启动。这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。
用最简单的话概括liveness和startup的区别就是,startup是从0到1的检测过程,探测成功后再交由liveness接管,而liveness是从1到0的检测过程。
pod钩子(启动后,终止前):
容器启动钩子(post start),容器终止前钩子(pre stop)(这两个钩子的作用就是,如果你想让容器启动之后做一些事情,你可以传递一些参数或者命令给容器启动钩子这个节点,当然如果你想在终止之前执行一些命令,你可以传递一些参数和一些命令给终止前这个节点)
启动、退出动作
vi pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: wangyanglinux/myapp:v1
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh", "-c", "echo Hello from the poststop handler > /usr/share/message"]
开一个终端,进入容器
写一个死循环
删除pod,在死循环终端查看
kubectl delete pods lifecycle-demo
pod 通过 yaml编写实现机制简介:
共享网络
Pause也可以称为info容器
案例
共享存储
当一个node宕机,master使用跟另外一个node创建容器时会拉取一个新的容器,新的容器中没有node1中的旧的数据此时就会出现数据丢失,可以使用volumn实现数据持久化
镜像拉取策略
调度、资源限制
requests:pod初始化时需要什么配置的CPU和内存,调度进行判断
比如一个pod需要2c4g的配置,可以设定1c2g那个node不需要调度,4c16g的node只需要2c4g就够了
实例:
limits最大:表示限制的最大值,这个pod不能占用超过多少内存和CPU
创建pod调度流程
master节点响应过程:先创建一个pod,然后通过apiserver进入etcd,然后将创建信息写入到etcd中,再次返回apiserver
scheduler:通过实时监控apiserver而获得新的pod创建信息,当有新的pod创建时,scheduler通过调度算法将pod分配给node,之后写入到etcd中,最后再通过apiserver返回
pod创建过程:kubelet通过apiserver入口进入,读取etcd分配给自己的任务信息,然后执行容器的创建,最后将创建信息返回给apiserver,并写入到etcd中进行存储,最后通过apiserver返回
podyaml总结
apiVersion: v1 # 【必须】版本号
kind: Pod # 【必选】Pod
metadata: # 【必选-Object】元数据
name: String # 【必选】 Pod的名称
namespace: String # 【必选】 Pod所属的命名空间
labels: # 【List】 自定义标签列表
- name: String
annotations: # 【List】 自定义注解列表
- name: String
spec: # 【必选-Object】 Pod中容器的详细定义
containers: # 【必选-List】 Pod中容器的详细定义
- name: String # 【必选】 容器的名称
image: String # 【必选】 容器的镜像名称
imagePullPolicy: [Always | Never | IfNotPresent] # 【String】 镜像拉取策略
command: [String] # 【List】 容器的启动命令列表,如果不指定,则使用镜像打包时使用的启动命令
args: [String] # 【List】 容器的启动命令参数列表
workingDir: String # 容器的工作目录
volumeMounts: # 【List】 挂载到容器内部的存储卷配置
- name: String # 引用Pod定义的共享存储卷的名称,需使用volumes[]部分定义的共享存储卷名称
mountPath: Sting # 存储卷在容器内mount的绝对路径,应少于512个字符
readOnly: Boolean # 是否为只读模式,默认为读写模式
ports: # 【List】 容器需要暴露的端口号列表
- name: String # 端口的名称
containerPort: Int # 容器需要监听的端口号
hostPort: Int # 容器所在主机需要监听的端口号,默认与containerPort相同。设置hostPort时,同一台宿主机将无法启动该容器的第二份副本
protocol: String # 端口协议,支持TCP和UDP,默认值为TCP
env: # 【List】 容器运行前需设置的环境变量列表
- name: String # 环境变量的名称
value: String # 环境变量的值
resources: # 【Object】 资源限制和资源请求的设置
limits: # 【Object】 资源限制的设置
cpu: String # CPU限制,单位为core数,将用于docker run --cpu-shares参数
memory: String # 内存限制,单位可以为MB,GB等,将用于docker run --memory参数
requests: # 【Object】 资源限制的设置
cpu: String # cpu请求,单位为core数,容器启动的初始可用数量
memory: String # 内存请求,单位可以为MB,GB等,容器启动的初始可用数量
# 【Object】 对Pod内各容器健康检查的设置,当探测无响应几次之后,系统将自动重启该容器。
# 可以设置的方法包括:exec、httpGet和tcpSocket。对一个容器只需要设置一种健康检查的方法
livenessProbe:
exec: # 【Object】 对Pod内各容器健康检查的设置,exec方式
command: [String] # exec方式需要指定的命令或者脚本
httpGet: # 【Object】 对Pod内各容器健康检查的设置,HTTGet方式。需要指定path、port
path: String
port: Number
host: String
scheme: String
httpHeaders:
- name: String
value: String
tcpSocket: # 【Object】 对Pod内各容器健康检查的设置,tcpSocket方式
port: Number
initialDelaySeconds: Number # 容器启动完成后首次探测的时间,单位为s
timeoutSeconds: Number # 对容器健康检查的探测等待响应的超时时间设置,单位为s,默认值为1s。
periodSeconds: Number # 对容器健康检查的定期探测时间设置,单位为s,默认10s探测一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: Boolean
# Pod的重启策略 一旦终止运行,都将重启 | 终止后kubelet将报告给master,不会重启
# 只有Pod以非零退出码终止时,kubelet才会重启该容器。如果容器正常终止(退出码为0),则不会重启。
restartPolicy: [Always | Never | OnFailure]
nodeSelector: object # 设置Node的Label,以key:value格式指定,Pod将被调度到具有这些Label的Node上
imagePullSecrets: # 【Object】 pull镜像时使用的Secret名称,以name:secretkey格式指定
- name: String
# 是否使用主机网络模式,默认值为false。设置为true表示容器使用宿主机网络,不再使用docker网桥,该Pod将无法在同一台宿主机上启动第二个副本
hostNetwork: Boolean
volumes: # 【List】 在该Pod上定义的共享存储卷列表
- name: String # 共享存储卷的名称,volume的类型有很多emptyDir,hostPath,secret,nfs,glusterfs,cephfs,configMap
emptyDir: {} # 【Object】 类型为emptyDir的存储卷,表示与Pod同生命周期的一个临时目录,其值为一个空对象:emptyDir: {}
hostPath: # 【Object】 类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: String # Pod所在主机的目录,将被用于容器中mount的目录
secret: # 【Object】类型为secret的存储卷,表示挂载集群预定义的secret对象到容器内部
secretName: String
items:
- key: String
path: String
configMap: # 【Object】 类型为configMap的存储卷,表示挂载集群预定义的configMap对象到容器内部
name: String
items:
- key: String
path: String
调度的灵活性详细讲解:
灵活调度使用需求案例:
亲和性详解:
概念:
Scheduler
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有很多要考虑的问题:
公平:如何保证每个节点都能被分配资源
资源高效利用:集群所有资源最大化被使用
效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
灵活:允许用户根据自己的需求控制调度的逻辑
Scheduler 是作为单独的程序运行的,启动之后会一直监听 API Server,获取 PodSpec.NodeName
为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上
调度过程
调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为预选;然后对通过的节点按照优先级排序,这个是优选;最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误
预选 有一系列的算法可以使用:
PodFitsResources:节点上剩余的资源是否大于 pod 请求的资源
PodFitsHost:如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
PodFitsHostPorts:节点上已经使用的 port 是否和 pod 申请的 port 冲突
PodSelectorMatches:过滤掉和 pod 指定的 label 不匹配的节点
NoDiskConflict:已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
如果在 预选 过程中没有合适的节点,pod 会一直在pending状态,不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 优选
过程:按照优先级大小对节点排序
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。通过算法对所有的优先级项目和权重进行计算,得出最终的结果。这些优先级选项包括:
LeastRequestedPriority
:通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点BalancedResourceAllocation
:节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用ImageLocalityPriority
:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高
自定义调度器(运维开发)
除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过 spec:schedulername
参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。比如下面的 pod 选择 test-scheduler
进行调度,而不是默认的 default-scheduler
亲和性:
亲和性提出背景:
Pod和节点之间的关系
- 某些Pod优先选择有ssd=true标签的节点,如果没有在考虑部署到其它节点;
- 某些Pod需要部署在ssd=true和type=physical的节点上,但是优先考虑部署在满足ssd=true条件的节点上;
Pod和Pod之间的关系
- 同一个应用的Pod不同的副本或者同一个项目的应用尽量或必须不部署(反亲和力)在同一个节点或者符合某个标签的一类节点上或者不同的区域;
- 相互依赖的两个Pod尽量或必须部署(亲和力)在同一个节点上或者同一个域内。
常见案例:
提高可用率--部署至不同宿主机的必要性:
假如所有pod应用部署在node01上,node01发生故障,则会导致应用挂掉,部署在不同节点上,使业务高可用概率大大提升
提高可用率--部署至不同地区机房/机柜
提高可用率—尽量把同一个项目的不同的应用平摊在多个节点中,降低影响范围
Affinity亲和力:
- NodeAffinity:节点亲和力/反亲和力
- PodAffinity:Pod亲和力
- PodAntiAffinity:Pod反亲和力
节点亲和性
pod.spec.nodeAffinity
preferredDuringSchedulingIgnoredDuringExecution:软策略
requiredDuringSchedulingIgnoredDuringExecution:硬策略
键值运算关系
- **In:label 的值在某个列表中**
- **NotIn:label 的值不在某个列表中**
- **Gt:label 的值大于某个值**
- **Lt:label 的值小于某个值**
- **Exists:某个 label 存在**
- **DoesNotExist:某个 label 不存在**
节点亲和力配置样例模版:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和力配置
nodeSelectorTerms: # 节点选择器配置,可以配置多个matchExpressions(满足其一),每个matchExpressions下可以配置多个key、value类型的选择器(都需要满足),其中values可以配置多个(满足其一),如下node必须满足kubernetes.io/e2e-az-name和subnet的key都可以满足,value满足条件之一即可
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- az-2
- matchExpressions:
- key: subnet
operator: In
values:
- subnet-172-16-1
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 # 软亲和力的权重,权重越高优先级越大,范围1-100
preference:
matchExpressions: # 软亲和力配置项,和weight同级,可以配置多个,matchExpressions和硬亲和力一致
- key: another-node-label-key
operator: In # 标签匹配的方式 In:相当于key = value的形式 NotIn:相当于key != value的形式 Exists:节点存在label的key为指定的值即可,不能配置values字段 DoesNotExist:节点不存在label的key为指定的值即可,不能配置values字段 Gt:大于value指定的值 Lt:小于value指定的值
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: nginx
练习:选择标签中不存在k8s-node02的节点匹配
required-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: wangyanglinux/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- k8s-node2
kubectl describe pod affinity
练习:node1为hdd硬盘,node2为ssd硬盘,需要安装一个服务必须满足使用ssd盘
打标签:
kubectl label node k8s-node1 disktype=hdd
kubectl label node k8s-node2 disktype=ssd
required-disk.yaml
apiVersion: v1
kind: Pod
metadata:
name: diskaffinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: wangyanglinux/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
kubectl describe pod diskaffinity
证明:硬策略必须满足,否则会长时间等待
kubectl delete pods --all
required-disk.yaml
将会处于Pending状态:
练习:软策略
preferred-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: wangyanglinux/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node3
虽然没有k8s-node3,但是依然可以匹配
软策略和硬策略同时满足:
doubel-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: wangyanglinux/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node1
硬限制:只有node2可以满足ssd盘
软限制:尽量在node1
最终结果:在node2
Pod 亲和性
*pod.spec.affinity.podAffinity/podAntiAffinity
preferredDuringSchedulingIgnoredDuringExecution:软策略**
requiredDuringSchedulingIgnoredDuringExecution:硬策略**
拓扑域TopologyKey详解:
topologyKey:拓扑域,主要针对宿主机、机房、不同区域的部署。用label进行判断,不同的key和不同的value是属于不同的拓扑域,先查找pod,然后查找和需求pod相同的node节点上部署新的pod,尽量不输在不同value的主机、机房、区域上(具体实验见最后yaml样例)
Pod亲和力和反亲和力样例模版:
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector: # labelSelector:Pod选择器配置,可以配置多个(多个需求建议多个value即可)
matchExpressions: # 和节点亲和力配置一致
- key: security
operator: In # 配置和节点亲和力一致,但是没有Gt和Lt
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone # 匹配的拓扑域的key,也就是节点上label的key,key和value相同的为同一个域,可以用于标注不同的机房和地区(先查找pod,然后查找和需求pod相同的node节点上部署新的pod)。
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
namespaces: # 和哪个命名空间的Pod进行匹配,为空为当前命名空间
- default
topologyKey: failure-domain.beta.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: nginx
实验一:验证亲和性
实验:让topolog-pod-3寻找labels为pod-1的pod,然后通过已找到的pod拥有的topologyKey的key值相同的node, 创建topolog-pod-3在和pod-1相同的node上
实验:让topolog-pod-3寻找labels为pod-1的pod,然后通过已找到的pod拥有的topologyKey的key值相同的node, 创建topolog-pod-3在和pod-1相同的node上
pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: topolog-pod
labels:
app: pod-1
spec:
containers:
- name: pod-1
image: wangyanglinux/myapp:v1
topologyKey.yaml
apiVersion: v1
kind: Pod
metadata:
name: topolog-pod-3
labels:
app: topolog-pod
spec:
containers:
- name: pod-3
image: wangyanglinux/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: kubernetes.io/hostname
这个yaml文件翻译成人话就是:寻找与标签app=pod-1的pod所在的node的标签key=kubernetes.io/hostname的值(value)相同的node
结果:pod都在node1上
实验二:验证是通过topologyKey根据pod所在的node的lablel去匹配(status )
kubectl delete pod --all
添加新标签status=okstatus:
kubectl label node k8s-node1 status=okstatus
kubectl label node k8s-node2 status=okstatus
查看标签:
pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: pod-1
spec:
containers:
- name: pod-1
image: wangyanglinux/myapp:v1
topologyKey-labeltest.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-4
labels:
app: pod-4
spec:
containers:
- name: pod-4
image: wangyanglinux/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: status
---
apiVersion: v1
kind: Pod
metadata:
name: pod-5
labels:
app: pod-5
spec:
containers:
- name: pod-5
image: wangyanglinux/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: status
pod-4与pod-5分布在不同的node,因为node1与node2的status的value相同都是okstatus
修改:
kubectl delete pod pod-4 pod-5
topologyKey-labeltest.yamltopologyKey: disktype
重新执行yaml文件,因为node1和node2 disktype不相同,要匹配和node1相同的disktype,新pod以只能匹配到node1上
反亲和性podAntiAffinity:
podAntiAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-6
labels:
app: pod-6
spec:
containers:
- name: pod-6
image: wangyanglinux/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: status
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: status
---
apiVersion: v1
kind: Pod
metadata:
name: pod-7
labels:
app: pod-7
spec:
containers:
- name: pod-7
image: wangyanglinux/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: status
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: kubernetes.io/hostname
pod-6亲和性node-1、2就status标签都满足,所以pod-6可以在node1上,pod-7软反亲尽量不和pod-01所在的node的kubernetes.io/hostname标签的value相同,所以选node2
污点:
Taint 和 Toleration
节点亲和性,是 pod 的一种属性(偏好或硬性要求),它使 pod 被吸引到一类特定的节点。Taint 则相反,它使 节点 能够 排斥 一类特定的 pod
Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod 上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上
污点(Taint)
污点 ( Taint ) 的组成
使用 kubectl taint
命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去
每个污点的组成如下:
key=value:effect
每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。当前 taint effect 支持如下三个选项:NoSchedule
:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上(已经调度完成的不受影响)PreferNoSchedule
:表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上NoExecute
:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去
k8s-master本身具有污点,不会被调度
内置污点:
当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:
node.kubernetes.io/not-ready
:节点未准备好。这相当于节点状况Ready
的值为 "False
"。node.kubernetes.io/unreachable
:节点控制器访问不到节点. 这相当于节点状况Ready
的值为 "Unknown
"。node.kubernetes.io/memory-pressure
:节点存在内存压力。node.kubernetes.io/disk-pressure
:节点存在磁盘压力。node.kubernetes.io/pid-pressure
: 节点的 PID 压力。node.kubernetes.io/network-unavailable
:节点网络不可用。node.kubernetes.io/unschedulable
: 节点不可调度。node.cloudprovider.kubernetes.io/uninitialized
:如果 kubelet 启动时指定了一个“外部”云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。
在节点被驱逐时,节点控制器或者 kubelet 会添加带有 NoExecute
效果的相关污点。 如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。
在某些情况下,当节点不可达时,API 服务器无法与节点上的 kubelet 进行通信。 在与 API 服务器的通信被重新建立之前,删除 Pod 的决定无法传递到 kubelet。 同时,被调度进行删除的那些 Pod 可能会继续运行在分区后的节点上。
污点的设置、查看和去除
设置污点
kubectl taint nodes node1 key1=value1:NoSchedule
节点说明中,查找 Taints 字段
kubectl describe node node-name
去除污点
kubectl taint nodes node1 key1:NoSchedule-
实验:让master 可以被调度:
kubectl taint node k8s-master node-role.kubernetes.io/master=:NoSchedule-
vi DaemonSet.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
containers:
- name: daemonset-example
image: wangyanglinux/myapp:v1
kubectl apply -f DaemonSet.yaml
让master尽量不被调度(当集群规模较小时,优化操作之一):
kubectl taint node k8s-master node-role.kubernetes.io/master:PreferNoSchedule
kubectl create deployment nginx --image=nginx --replicas=10
添加一个污点(引入容忍知识):
删除其他污点(添加value和不添加结果相同,如果不加类型“NoSchedule”,则三种类型的污点都会被删除):
kubectl taint node k8s-master node-role.kubernetes.io/master:NoSchedule-
kubectl taint node k8s-master node-role.kubernetes.io/master:PreferNoSchedule-
kubectl taint node k8s-master MySchedule=test:NoSchedule
污点覆盖:
kubectl taint node k8s-master MySchedule=test:NoSchedule --overwrite
kubectl describe node k8s-master
kubectl create -f DaemonSet.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
containers:
- name: daemonset-example
image: wangyanglinux/myapp:v1
kubectl get pod -n kube-system | grep flannel
可以明显发现,master即使有污点,但是flannel依然所有节点都有pod,为什么呢?
容忍(Tolerations)
设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。 但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上
添加容忍:
pod.spec.tolerations
样例:
#一般参数
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
===================
#添加驱散时间(时间范围内暂时容忍)
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600 #可以用于快速故障恢复
===================
#简写,省略value,不完全匹配兼容任意key
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
灵活选择:
当不指定 key 值时,表示容忍所有类型的污点和所有方式(不推荐):
tolerations:
- operator: "Exists"
当不指定 effect 值时,表示容忍所有的污点作用
有多个 Master 存在时,防止资源浪费,可以如下设置
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
DaemonSet.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
tolerations:
- key: "MySchedule"
operator: "Equal"
value: "test"
effect: "NoSchedule"
containers:
- name: daemonset-example
image: wangyanglinux/myapp:v1
kubectl create -f DaemonSet.yaml
查看flannel的容忍
kubectl get pods kube-flannel-ds-4jdrk -n kube-system -o yaml | grep -C 10 tolerations
指定调度节点(强制匹配),类似于节点亲和性,但是创建更快,消耗资源更少,灵活性更低
Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb
labels:
app: myweb
spec:
replicas: 4
selector:
matchLabels:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
nodeName: k8s-node1
containers:
- name: myweb
image: wangyanglinux/myapp:v1
ports:
- containerPort: 80
kubectl create -f deployment.yaml
Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束
kubectl get nodes --show-labels
deployment-1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb-1
labels:
app: myweb-1
spec:
replicas: 4
selector:
matchLabels:
app: myweb-1
template:
metadata:
labels:
app: myweb-1
spec:
nodeSelector:
disktype: ssd
containers:
- name: myweb-1
image: wangyanglinux/myapp:v1
ports:
- containerPort: 80
指定标签disktype为ssd的pod
利用内置污点快速迁移pod
生产环境中日过一台node不可用,默认300秒才会打上污点并驱逐pod,但是300秒时间较长,可以通过修改污点驱逐间隔实现快速恢复业务应用
任意查看一个pod
在kube-controller-manager.service配置文件中,会有一台node健康检查间隔和不可用等待时间
业务恢复时间:修改内置污点10秒,最终周期为40s+10s+健康检查等其他机制所需时间
亲和性其他样例:
同一个应用部署在不同的宿主机:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: must-be-diff-nodes
name: must-be-diff-nodes
namespace: kube-public
spec:
replicas: 3
selector:
matchLabels:
app: must-be-diff-nodes
template:
metadata:
labels:
app: must-be-diff-nodes
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- must-be-diff-nodes # 因为自己的标签就是此标签,所以可以实现不同的pod部署到不同的node
topologyKey: kubernetes.io/hostname # 匹配的拓扑域的key,也就是节点上label的key,key和value相同的为同一个域,可以用于标注不同的机房和地区
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: must-be-diff-nodes
同一个应用不同副本固定节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
nodeSelector:
app: store
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
应用和缓存尽量部署在同一个域内
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.16-alpine
尽量调度到需求配置的服务器上尽量部署在ssd=true(优先级较高)和type=physical(优先级较低)的节点上,且尽量不要部署在gpu资源的节点上去(优先级较低)
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: prefer-ssd
name: prefer-ssd
namespace: kube-public
spec:
replicas: 3
selector:
matchLabels:
app: prefer-ssd
template:
metadata:
creationTimestamp: null
labels:
app: prefer-ssd
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: ssd
operator: In
values:
- "true"
- key: gpu
operator: NotIn
values:
- "true"
weight: 100
- preference:
matchExpressions:
- key: type
operator: In
values:
- physical
weight: 10
containers:
- env:
- name: TZ
value: Asia/Shanghai
- name: LANG
value: C.UTF-8
image: nginx
imagePullPolicy: IfNotPresent
name: prefer-ssd
同一个应用多区域部署(不同node打不同地域的regin标签进行试验)
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: must-be-diff-zone
name: must-be-diff-zone
namespace: kube-public
spec:
replicas: 3
selector:
matchLabels:
app: must-be-diff-zone
template:
metadata:
labels:
app: must-be-diff-zone
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- must-be-diff-zone
topologyKey: region
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: must-be-diff-zone
pod常见的异常状态以及可能原因
1 Pending状态
Pending状态:
- 说明Pod的YAML文件已提交给Kubernetes
- API对象已经被创建并保存在Etcd当中
原因:这个Pod里有些容器因为某种原因而不能被顺利创建。
可能原因:
- 调度不成功
- 可以通过命令查看到当前Pod的事件,进而判断为什么没有调度。
kubectl describe pod {podname}
- 资源不足
- 原因:集群内所有的Node都不满足该Pod请求的CPU、内存、GPU等资源
- 解决方法:增加资源配置/优化容器资源使用方式
- HostPort 已被占用
- 解决方法:使用Service对外开放服务端口
2 Waiting/ContainerCreating状态
首先通过 命令查看当前Pod的事件
kubectl describe pod {podname}
可能的原因有:
- 镜像拉取失败:比如镜像地址配置错误、拉取不了国外镜像源(gcr.io)、私有镜像密钥配置错误、镜像太大导致拉取超时 (可以适当调整kubelet的-image-pull-progress-deadline和-runtime-request-timeout选项)等。
- CNI网络错误:检查CNI网络插件的配置,比如:无法配置Pod 网络、无法分配IP地址。
- 容器无法启动:检查是否打包了正确的镜像或者是否配置了正确的容器参数
- Failed create pod sandbox:查看kubelet日志,原因可能是磁盘坏道(input/output error)。
3 CrashLoopBackOff状态
处于CrashLoopBackOff状态
说明容器曾经启动了,但又异常退出。1.查看容器的日志,查看退出原因
kubectl logs {podname} kubectl logs --previous {podname}
2.进入容器查看
kubectl exec {mypodname} -c {containername} -it -- bash
3.ssh登录Node查看
4 ImagePullBackOff状态
处于ImagePullBackOff状态
原因:是镜像名称配置错误或者私有镜像的密钥配置错误导致。
5 Error状态
Pod处于Error状态,说明Pod启动过程中发生了错误。
6 其他状态说明
CrashLoopBackOff: #容器退出,kubelet正在将它重启
InvalidImageName: #无法解析镜像名称
ImageInspectError: #无法校验镜像
ErrImageNeverPull: #策略禁止拉取镜像
ImagePullBackOff: #正在重试拉取
RegistryUnavailable: #连接不到镜像中心
ErrImagePull: #通用的拉取镜像出错
CreateContainerConfigError: #不能创建kubelet使用的容器配置
CreateContainerError: #创建容器失败
m.internalLifecycle.PreStartContainer #执行hook报错
RunContainerError: #启动容器失败
PostStartHookError: #执行hook报错
ContainersNotInitialized: #容器没有初始化完毕
ContainersNotReady: #容器没有准备完毕
ContainerCreating: #容器创建中
PodInitializing:pod #初始化中
DockerDaemonNotReady: #docker还没有完全启动
NetworkPluginNotReady: #网络插件还没有完全启动
Evicte: #pod被驱赶
pod自动重启的可能原因
1 Xms超出了k8s分配
在没有给jvm指定内存大小的情况下,机器物理内存很大时,jvm默认占用的内存Xms超出了k8s分配给pod的内存,导致pod内存溢出,从而k8s不断重启pod。
或者:运行过程中,jvm不断申请内存直到最大heap内存Xmx,Xmx超出了k8s分配给pod的内存,从而k8s自动重启pod。
解决方法:在启动的脚本中设置jvm内存-Xms、-Xmx参数
例如:java -Xms1024m -Xmx1024m -jar test.jar
2 docker容器的内存限制
设置了docker容器的内存限制,制作的镜像未对JVM进行配置,
JVM 会默认设置堆栈的大小。
这样,当jvm占用内存超过docker容器限制时,就会出现container 被docker killed情况。
解决方法:一样是设置jvm内存-Xms、-Xmx参数
注意要小于docker容器的内存限制。
3 出现OOMKilled事件
pod运行过程中出现了OOMKilled事件
即pod运行过程内存需求持续增加,超过为pod设置的内存大小时,pod会被重启。
解决方法:将pod的内存配置项的值修改大点。
例如之前是1/2,可改为2/4
4 镜像拉取失败:如果镜像拉取失败,Pod将会被重启
5、livenessProb 检查失败
发布者:LJH,转发请注明出处:https://www.ljh.cool/7975.html