pod详解
1.Pod的介绍
1.1Pod的结构
每个Pod中都包含一个或者多个容器,这些容器可以分为两类:
- 用户程序所在的容器,数量可多可少。
- Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
- 可以以它为依据,评估整个Pod的健康状况。
- 可以在根容器上设置IP地址,其它容器都共享此IP(Pod的IP),以实现Pod内部的网络通信(这里是Pod内部的通讯,Pod之间的通讯采用虚拟二层网络技术来实现,我们当前环境使用的是Flannel)。
1.2Pod的yaml配置说明:
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,资源类型,例如 Pod
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #Pod所属的命名空间,默认为"default"
labels: #自定义标签列表
- name: string
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ] #获取镜像的策略
command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] #容器的启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷配置
- name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean #是否为只读模式
ports: #需要暴露的端口库号列表
- name: string #端口的名称
containerPort: int #容器需要监听的端口号
hostPort: int #容器所在主机需要监听的端口号,默认与Container相同
protocol: string #端口协议,支持TCP和UDP,默认TCP
env: #容器运行前需设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量的值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests: #资源请求的设置
cpu: string #Cpu请求,容器启动的初始可用数量
memory: string #内存请求,容器启动的初始可用数量
lifecycle: #生命周期钩子
postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
exec: #对Pod容器内检查方式设置为exec方式
command: [string] #exec方式需要制定的命令或脚本
httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always | Never | OnFailure] #Pod的重启策略
nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
- name: string
hostNetwork: false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes: #在该pod上定义共享存储卷列表
- name: string #共享存储卷名称 (volumes类型有很多种)
emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,将被用于同期中mount的目录
secret: #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
查看每种资源的可配置项
语法
# 查看某种资源可以配置的一级配置
kubectl explain 资源类型
# 查看属性的子属性
kubectl explain 资源类型.分级属性
示例:查看资源类型为pod的可配置项
[root@master ~]# kubectl explain pod
2.Pod的配置
2.1配置说明
# 返回的重要属性
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 数组,代表可以有多个容器FIELDS:
name <string> # 容器名称
image <string> # 容器需要的镜像地址
imagePullPolicy <string> # 镜像拉取策略
command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args <[]string> # 容器的启动命令需要的参数列表
env <[]Object> # 容器环境变量的配置
ports <[]Object> # 容器需要暴露的端口号列表
resources <Object> # 资源限制和资源请求的设置
2.2基本配置
创建pod-base.yaml文件,这里只是初步的简单定义配置,实际生产配置远不止这些
apiVersion: v1
kind: Pod
metadata:
name: pod-base
namespace: dev
labels:
user: kele
spec:
#containers是集合 可以通过-创建多个对象
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
- name: busybox # 容器名称
image: busybox:1.30 # 容器需要的镜像地址
创建Pod:
[root@master ~]# kubectl apply -f pod-base.yaml
查看Pod状况:
# 此时已经运行起来了一个基本的Pod,虽然它暂时有问题
[root@master ~]# kubectl describe pod pod-base -n dev
2.3 镜像拉取策略
创建pod-imagepullpolicy.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
labels:
user: kele
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: Always # 用于设置镜像的拉取策略
- name: busybox # 容器名称
image: busybox:1.30 # 容器需要的镜像地址
imagePullPolicy:用于设置镜像拉取的策略,kubernetes支持配置三种拉取策略:
- Always:总是从远程仓库拉取镜像(一直远程下载)。
- IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就用本地,本地没有就使用远程下载)。
- Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错(一直使用本地,没有就报错)。
默认值说明:
- 如果镜像tag为具体的版本号,默认策略是IfNotPresent。
- 如果镜像tag为latest(最终版本),默认策略是Always。
创建Pod:
[root@master ~]# kubectl apply -f pod-imagepullpolicy.yaml
查看Pod详情:
[root@master ~]# kubectl describe pod pod-imagepullpolicy -n dev
2.4启动命令
- 在前面的案例中,一直有一个问题没有解决,就是busybox容器一直没有成功运行,那么到底是什么原因导致这个容器的故障的呢?
- 原来busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command的配置。创建pod-
command.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
labels:
user: kele
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: IfNotPresent # 设置镜像拉取策略
- name: busybox # 容器名称
image: busybox:1.30 # 容器需要的镜像地址
command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done;"]
command:用于在Pod中的容器初始化完毕之后执行的命令。
这里稍微解释下command中的命令的意思:
- "/bin/sh","-c":使用sh执行命令。
- touch /tmp/hello.txt:创建一个/tmp/hello.txt的文件。
- while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done:每隔3秒,向文件写入当前时间
创建Pod:
[root@master ~]# kubectl apply -f pod-command.yaml
查看Pod状态:
[root@master ~]# kubectl get pod pod-command -n dev
进入Pod中的busybox容器命令,并且查看command命令执行的内容
# 在容器中执行命令
# kubectl exec -it pod的名称 -n 命名空间 -c 容器名称 /bin/sh
[root@master ~]# kubectl exec -it pod-command -n dev -c busybox /bin/sh
[root@master ~]# tail -f /tmp/hello.txt
2.5环境变量
建议通过配置文件的形势或者系统环境变量的配置
2.6端口设置
一般发布容器后,通过系统暴露的端口映射到容器端口进行访问,不推荐直接将系统ip和端口配置给容器
配置说明
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:
name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的
containerPort <integer> # 容器要监听的端口(0<x<65536)
hostPort <integer> # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副本(一般省略)
hostIP <string> # 要将外部端口绑定到的主机IP(一般省略)
protocol <string> # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”
创建pod-ports.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
labels:
user: kele
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: IfNotPresent # 设置镜像拉取策略
ports:
- name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
containerPort: 80 # 容器要监听的端口 (0~65536)
protocol: TCP # 端口协议
创建Pod
kubectl create -f pod-ports.yaml
2.7资源配额
容器中的程序要运行,肯定会占用一定的资源,比如CPU和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量的资源,导致其他的容器无法运行。针对这种情况,kubernetes提供了对内存和CPU的资源进行配额的机制,这种机制主要通过resources选项实现,它有两个子选项:
- limits:用于限制运行的容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启。
- requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
可以通过上面的两个选项设置资源的上下限。
创建pod-resoures.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-resoures
namespace: dev
labels:
user: kele
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: IfNotPresent # 设置镜像拉取策略
ports: # 端口设置
- name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
containerPort: 80 # 容器要监听的端口 (0~65536)
protocol: TCP # 端口协议
resources: # 资源配额
limits: # 限制资源的上限
cpu: "2" # CPU限制,单位是core数
memory: "10Gi" # 内存限制
requests: # 限制资源的下限
cpu: "1" # CPU限制,单位是core数
memory: "10Mi" # 内存限制
3.pod生命周期
一般将Pod对象从创建到终止的这段时间范围称为Pod的生命周期,它主要包含下面的过程:
1.Pod创建过程。
2.运行初始化容器(init container)过程。
3.运行主容器(main container):
- 容器启动后钩子(post start)、容器终止前钩子(pre stop)。
- 容器的存活性探测(liveness probe)、就绪性探测(readiness probe)。
4.Pod终止过程
在整个生命周期中,Pod会出现5种状态(相位),分别如下:
- 挂起(Pending):API Server已经创建了Pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中。
- 运行中(Running):Pod已经被调度到某节点,并且所有容器都已经被kubelet创建完成。
- 成功(Succeeded):Pod中的所有容器都已经成功终止并且不会被重启。
- 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态。
- 未知(Unknown):API Server无法正常获取到Pod对象的状态信息,通常由于网络通信失败所导致。
3.1pod的创建和销毁过程
3.1.1创建过程
- 用户通过kubectl或其他的api客户端提交需要创建的Pod信息给API Server。
- API Server开始生成Pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端。
- API Server开始反映etcd中的Pod对象的变化,其它组件使用watch机制来跟踪检查API Server上的变动。
- Scheduler发现有新的Pod对象要创建,开始为Pod分配主机并将结果信息更新至API Server。
- Node节点上的kubelet发现有Pod调度过来,尝试调度Docker启动容器,并将结果回送至API Server。
- API Server将接收到的Pod状态信息存入到etcd中。
结论:通过操作master节点来控制node节点上的pod创建,其中核心为apiserver,etcd记录pod状态信息,其他组件通过订阅的方式来监听apiserver
3.1.2销毁过程
- 用户向API Server发送删除Pod对象的命令。
- API Server中的Pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),Pod被视为dead。
- 将Pod标记为terminating状态。
- kubelete在监控到Pod对象转为terminating状态的同时启动Pod关闭过程。
- 端点控制器监控到Pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除。
- 如果当前Pod对象定义了preStop钩子处理器,则在其标记为terminating后会以同步的方式启动执行。
- Pod对象中的容器进程收到停止信号。
- 宽限期结束后,如果Pod中还存在运行的进程,那么Pod对象会收到立即终止的信号。
- kubectl请求API Server将此Pod资源的宽限期设置为0从而完成删除操作,此时Pod对于用户已经不可用了。
3.2初始化容器(initContainers)
初始化容器是在Pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:启动的前置条件必须都满足才启动容器
- 初始化容器必须运行完成直至结束,如果某个初始化容器运行失败,那么kubernetes需要重启它直至成功完成。
- 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行,可以理解为
初始化容器有很多的应用场景,下面列出的是最常见的几个,:
- 提供主容器镜像中不具备的工具程序或自定义代码。
- 初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足。
3.2.1测试案例:
场景
- 假设要以主容器来运行Nginx,但是要求在运行Nginx之前要能够连接上MySQL和Redis所在的服务器。
- 为了简化测试,事先规定好MySQL和Redis所在的IP地址分别为192.168.79.203和192.168.79.204(注意,这两个IP都不能ping通,因为环境中没有这两个IP)。
1.创建pod-initcontainer.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
labels:
user: kele
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
resources:
limits:
cpu: "2"
memory: "1Gi"
requests:
cpu: "1"
memory: "10Mi"
initContainers: # 初始化容器配置
- name: test-mysql
image: busybox:1.30
command: ["sh","-c","until ping 192.168.79.203 -c 1;do echo waiting for mysql ...;sleep 2;done;"]
securityContext:
privileged: true # 使用特权模式运行容器
- name: test-redis
image: busybox:1.30
command: ["sh","-c","until ping 192.168.79.204 -c 1;do echo waiting for redis ...;sleep 2;done;"]
securityContext:
privileged: true # 使用特权模式运行容器
command内容说明
--until ping 192.168.79.203 -c 1:执行ping ip一次
--do echo waiting for mysql ...;sleep 2 :如果ping不通 则执行输出语句,并且等待2s再继续执行ping
--done:如果ping成功则结束
command: ["sh","-c","until ping 192.168.79.203 -c 1;do echo waiting for mysql ...;sleep 2;done;"]
2.创建Pod:
[root@master local]#kubectl describe pod pod-initcontainer -n dev
3.查看Pod状态:
[root@master local]#kubectl describe pod pod-initcontainer -n dev
4.删除pod
[root@master local]# kubectl delete pod pod-initcontainer -n dev
3.3钩子函数 (lifecycle)
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
kubernetes在主容器启动之后和停止之前提供了两个钩子函数:
- post start:容器创建之后执行,如果失败会重启容器。
- pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。
钩子处理器支持使用下面的三种方式定义动作:
1.exec命令:容器内执行一次命令。
spec:
containers:
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
2.tcpSocket:当前容器尝试访问指定的socket。
spec:
containers:
lifecycle:
postStart:
tcpSocket:
port: 8080
3.httpGet:当前容器中向某url发起HTTP请求。
spec:
containers:
lifecycle:
postStart:
httpGet:
path: / #URI地址
port: 80 #端口号
host: 192.168.109.100 #主机地址
scheme: HTTP #支持的协议,http或者https
3.4容器探测 (liveness)
容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么kubernetes就会把该问题实例“摘除”,不承担业务流量。kubernetes提供了两种探针来实现容器探测,分别是:
- liveness probes:存活性探测,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器。
- readiness probes:就绪性探测,用于检测应用实例是否可以接受请求,如果不能,k8s不会转发流量。
扩展:
k8s在1.16版本之后新增了startupProbe探针,用于判断容器内应用程序是否已经启动。如果配置了startupProbe探针,就会先禁止其他的探针,直到startupProbe探针成功为止,一旦成功将不再进行探测。
定义说明:
上面两种探针目前均支持三种探测方式:
1.exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常。
spec:
containers: # 容器配置
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
2.tcpSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常。
spec:
containers: # 容器配置
livenessProbe:
tcpSocket:
port: 8080
3.httpGet:调用容器内web应用的URL,如果返回的状态码在200和399之前,则认为程序正常,否则不正常。
spec:
containers: # 容器配置
livenessProbe:
httpGet:
path: / #URI地址
port: 80 #端口号
host: 127.0.0.1 #主机地址
scheme: HTTP #支持的协议,http或者https
实践案例:
创建pod-liveness.yaml文件,分别列举了三种方式,先注释其他两种进行可以逐个实践
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness
namespace: dev
labels:
user: kele
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
livenessProbe: # 存活性探针
#1.exec方式
exec:
command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令,必须失败,因为根本没有这个文件
#2.Socket方式
#tcpSocket:
# port: 80
#3.httpGet方式
#httpGet:
# path: / #URI地址
# port: 80 #端口号
# host: 127.0.0.1 #主机地址
# scheme: HTTP #支持的协议,http或者https
exec方式
1.创建pod
kubectl create -f pod-liveness.yaml
2. 查看Pod
kubectl get pod -n dev
可以看到虽然pod状态属于正常运行,但是restarts=2.说明启动时再重启,接下来查看详情
3.查看Pod详情
kubectl describe pod pod-liveness -n dev
livenessProbe存活性探针探测失败进行重启
4.正确案例,将yaml中command修改成["/bin/ls","/tmp/"]
livenessProbe: # 存活性探针
#1.exec方式
exec:
command: ["/bin/ls","/tmp/"]
5.删除重建pod
kubectl delete pod pod-liveness -n dev
kubectl create -f pod-liveness.yaml
容器探测其他配置:
查看livenessProbe的子属性,会发现除了这三种方式,还有一些其他的配置。
kubectl explain pod.spec.containers.livenessProbe
initialDelaySeconds # 容器启动后等待多少秒执行第一次探测
timeoutSeconds # 探测超时时间。默认1秒,最小1秒
periodSeconds # 执行探测的频率。默认是10秒,最小1秒
failureThreshold # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold # 连续探测成功多少次才被认定为成功。默认是1
3.5重启策略(restartPolicy)
在容器探测中,一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由Pod的重启策略决定的,Pod的重启策略有3种,restartPolicy分别如下:
- Always:容器失效时,自动重启该容器,默认值。
- OnFailure:容器终止运行且退出码不为0时重启。
- Never:不论状态如何,都不重启该容器。
重启策略适用于Pod对象中的所有容器,首次需要重启的容器,将在其需要的时候立即进行重启,随后再次重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大的延迟时长。
apiVersion: v1
kind: Pod
metadata:
name: pod-restart-policy
namespace: dev
labels:
user: kele
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
livenessProbe: # 存活性探测
httpGet:
port: 80
path: /hello
host: 127.0.0.1
scheme: HTTP
restartPolicy: Never # 重启策略(默认:Always)
4、Pod的调度
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式。
- 自动调度:运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。
- 定向调度:NodeName、NodeSelector。
- 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity。
- 污点(容忍)调度:Taints、Toleration。
4.1定向调度
定向调度,指的是利用在Pod上声明的nodeName(节点名称)或nodeSelector(节点标签),以此将Pod调度到期望的Node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过Pod运行失败而已。
4.1.1nodeName
通过节点名称来选定pod在哪个节点创建,会直接跳过Scheduler的调度逻辑
创建一个pod-nodename.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
labels:
user: kele
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeName: node1 # 指定调度到node1节点上
创建pod
kubectl create -f pod-nodename.yaml
查看pod详情
kubectl get pod pod-nodename -n dev -o wide
4.1.2nodeSelector
使用nodeSelector时需要先给node节点添加上标签,该方案通过label-selector机制实现的,在Pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,该匹配规则是强制约束
给node节点添加标签:
kubectl label node node1 nodeenv=pro
kubectl label node node2 nodeenv=test
创建pod-nodeselector.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro的Node节点上
创建pod
kubectl create -f pod-nodeselector.yaml
4.2亲和性调度(Affinity)
相对于定向调度,k8s还提供了亲和性调度(Affinity)的方案比较灵活。它在nodeSelector的基础之上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有Node也可以调度到不满足条件的节点上。
Affinity主要分为三类:
- nodeAffinity(node亲和性):以Node为目标,解决Pod可以调度到那些Node的问题。
- podAffinity(pod亲和性):以Pod为目标,解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。
- podAntiAffinity(pod反亲和性):以Pod为目标,解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。
关于亲和性和反亲和性的使用场景的说明:
- 亲和性:如果两个应用频繁交互,那么就有必要利用亲和性让两个应用尽可能的靠近,这样可以较少因网络通信而带来的性能损耗。
- 反亲和性:当应用采用多副本部署的时候,那么就有必要利用反亲和性让各个应用实例打散分布在各个Node上,这样可以提高服务的高可用性。
Pod控制器的详解
在kubernetes中,按照Pod的创建方式可以将其分为两类(一般用控制器创建pod):
- 自主式Pod:kubernetes直接创建出来的Pod,这种Pod删除后就没有了,也不会重建。
- 控制器创建Pod:通过Pod控制器创建的Pod,这种Pod删除之后还会自动重建。
在kubernetes中,有很多类型的Pod控制器,每种都有自己的适合的场景,常见的有下面这些:
- ReplicationController:比较原始的Pod控制器,已经被废弃,由ReplicaSet替代。
- ReplicaSet:保证指定数量的Pod运行,并支持Pod数量变更,镜像版本变更。
- Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级、版本回退。
- Horizontal Pod Autoscaler:可以根据集群负载自动调整Pod的数量,实现削峰填谷。
- DaemonSet:在集群中的指定Node上都运行一个副本,一般用于守护进程类的任务。
- Job:它创建出来的Pod只要完成任务就立即退出,用于执行一次性任务。
- CronJob:它创建的Pod会周期性的执行,用于执行周期性的任务。
- StatefulSet:管理有状态的应用。
1.ReplicaSet(RS)
ReplicaSet的主要作用是保证一定数量的Pod能够正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障就会重启或重建。同时它还支持对Pod数量的扩缩容和版本镜像的升级
1.1ReplicaSet的yaml资源清单文件:
apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: #标签
controller: rs
spec: # 详情描述
replicas: 3 # 副本数量
selector: # 选择器,通过它指定该控制器管理哪些po
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
在这里,需要新了解的配置项就是spec下面几个选项:
- replicas:指定副本数量,其实就是当然rs创建出来的Pod的数量,默认为1.
- selector:选择器,它的作用是建立Pod控制器和Pod之间的关联关系,采用了Label Selector机制(在Pod模块上定义Label,在控制器上定义选择器,就可以表明当前控制器能管理哪些Pod了)。
- template:模板,就是当前控制器创建Pod所使用的模板,里面其实就是前面学过的Pod的定义。
1.2创建ReplicaSet
创建pc-replicaset.yaml文件
apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型
metadata: # 元数据
name: pc-replicaset # rs名称
namespace: dev # 命名类型
spec: # 详细描述
replicas: 3 # 副本数量
selector: # 选择器,通过它指定该控制器可以管理哪些Pod
matchLabels: # Labels匹配规则
app: nginx-pod
template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
ports:
- containerPort: 80 # 容器所监听的端口
创建命令
kubectl create -f pc-replicaset.yaml
查看
kubectl get rs pc-replicaset -n dev -o wide
查看当前控制器创建出来的Pod
kubectl get pod -n dev
1.3 扩缩容
方式一:编辑rs的副本数量,修改spec:replicas:6
kubectl edit rs pc-replicaset -n dev
方式二:使用scale命令实现扩缩容,后面加上--replicas=n直接指定目标数量
kubectl scale rs pc-replicaset --replicas=2 -n dev
但以上两种方式都为手动扩缩容 ,实际生产环境的场景中流量激增可能是一瞬间的,需要系统自动检测进行动态扩缩容,请看HPA章节
1.4镜像升级
方式一:编辑rs的容器镜像,修改spec:containers:image为nginx:1.17.2。
kubectl edit rs pc-replicaset -n dev
方式二:使用set命令实现镜像升级
语法:
kubectl set image rs rs名称 容器名称=镜像版本 -n 命名空间
命令
kubectl set image rs pc-replicaset nginx=nginx:1.17.1 -n dev
1.5删除ReplicaSet
使用kubectl delete rs 命令会删除ReplicaSet和其管理的Pod
# 在k8s删除ReplicaSet前,会将ReplicaSet的replicas调整为0,等到所有的Pod被删除后,再执行ReplicaSet对象的删除
kubectl delete rs pc-replicaset -n dev
更推荐使用yaml直接删除
kubectl delete -f pc-replicaset.yaml
2.Deployment(Deploy)
为了更好的解决服务编排的问题,kubernetes在v1.2版本开始,引入了Deployment控制器。值得一提的是,Deployment控制器并不直接管理Pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment的功能比ReplicaSet强大。
Deployment的主要功能如下:
- 支持ReplicaSet的所有功能。
- 支持发布的停止、继续。
- 支持版本滚动更新和版本回退。
2.1Deployment的yaml资源清单
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: #标签
controller: deploy
spec: # 详情描述
replicas: 3 # 副本数量
revisionHistoryLimit: 3 # 保留历史版本,默认为10
paused: false # 暂停部署,默认是false
progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数 maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
2.2创建Deployment
创建pc-deployment.yaml文件
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: pc-deployment # deployment的名称
namespace: dev # 命名类型
spec: # 详细描述
replicas: 3 # 副本数量
selector: # 选择器,通过它指定该控制器可以管理哪些Pod
matchLabels: # Labels匹配规则
app: nginx-pod
template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
ports:
- containerPort: 80 # 容器所监听的端口
创建Deployment
kubectl create -f pc-deployment.yaml
查看Deployment和pod
kubectl get deploy pc-deployment -n dev
kubectl get pod -n dev
2.3扩缩容
使用scale命令实现扩缩容
kubectl scale deploy pc-deployment --replicas=5 -n dev
编辑Deployment的副本数量,修改spec:replicas:4
kubectl edit deployment pc-deployment -n dev
但以上两种方式都为手动扩缩容 ,实际生产环境的场景中流量激增可能是一瞬间的,需要系统自动检测进行动态扩缩容,请看HPA章节
2.4 镜像/容器更新
Deployment支持两种镜像更新的策略:重建更新
和滚动更新(默认)
,可以通过strategy
选项进行配置。
配置说明:
strategy: 指定新的Pod替代旧的Pod的策略,支持两个属性
type: 指定策略类型,支持两种策略
Recreate:在创建出新的Pod之前会先杀掉所有已经存在的Pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod
rollingUpdate:当type为RollingUpdate的时候生效,用于为rollingUpdate设置参数,支持两个属性:
maxUnavailable:用来指定在升级过程中不可用的Pod的最大数量,默认为25%。
maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。
2.4.1重建更新
在创建出新的Pod之前会先杀掉所有已经存在的Pod
编辑pc-deployment.yaml文件,在spec节点下添加更新策略
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: pc-deployment-recreate # deployment的名称
namespace: dev # 命名类型
spec: # 详细描述
replicas: 3 # 副本数量
strategy: # 镜像更新策略
type: Recreate # Recreate:在创建出新的Pod之前会先杀掉所有已经存在的Pod
selector: # 选择器,通过它指定该控制器可以管理哪些Pod
matchLabels: # Labels匹配规则
app: nginx-pod
template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
ports:
- containerPort: 80 # 容器所监听的端口
升级nginx镜像 ,同时node节点的本地镜像仓库中也下载了新版的nginx
kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev
查看升级过程
kubectl get pod -n dev -w
2.4.2滚动更新
编辑pc-deployment-rollingupdate.yaml文件,在spec节点下添加更新策略
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: pc-deployment-rollingupdate # deployment的名称
namespace: dev # 命名类型
spec: # 详细描述
replicas: 3 # 副本数量
strategy: # 镜像更新策略
type: RollingUpdate # RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
selector: # 选择器,通过它指定该控制器可以管理哪些Pod
matchLabels: # Labels匹配规则
app: nginx-pod
template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
ports:
- containerPort: 80 # 容器所监听的端口
镜像升级
kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev
查看升级过程
kubectl get pod -n dev -w
滚动更新的过程:
镜像更新中rs的变化
# 查看rs,发现原来的rs依旧存在,只是Pod的数量变为0,而后又产生了一个rs,Pod的数量变为3
# 其实这就是deployment能够进行版本回退的奥妙所在
kubectl get rs -n dev
2.5版本回退
Deployment支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看:
# 版本升级相关功能
kubetl rollout [参数] deploy xx # 支持下面的选择
# status 显示当前升级的状态
# history 显示升级历史记录
# pause 暂停版本升级过程
# resume 继续已经暂停的版本升级过程
# restart 重启版本升级过程
# undo 回滚到上一级版本 (可以使用--to-revision回滚到指定的版本)
查看当前升级版本的状态
kubectl rollout status deployment pc-deployment-rollingupdate -n dev
查看升级历史记录
kubectl rollout history deployment pc-deployment-rollingupdate -n dev
版本回退
# 可以使用-to-revision=1回退到1版本,如果省略这个选项,就是回退到上个版本,即2版本
kubectl rollout undo deployment pc-deployment-rollingupdate --to-revision=1 -n dev
deployment之所以能够实现版本的回退,就是通过记录下历史的ReplicaSet来实现的,一旦想回滚到那个版本,只需要将当前版本的Pod数量降为0,然后将回退版本的Pod提升为目标数量即可。
2.6金丝雀发布
- Deployment支持更新过程中的控制,如暂停更新操作(pause)或继续更新操作(resume)。
- 例如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求到新版本的Pod应用,继续观察能够稳定的按照期望的方式运行,如果没有问题之后再继续完成余下的Pod资源的滚动更新,否则立即回滚操作。
更新Deployment的版本,并配置暂停Deployment:
[root@master ~]# kubectl set image deployment pc-deployment-rollingupdate nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment-rollingupdate -n dev
- 说明:先下载并创建nginx新版本pod,并且使用pause命令创建一个新pod后先暂停版本升级过程,旧的三个pod仍然在正常运行,等待用resume命令继续执行
查看暂停升级过程吗,在没有执行resume命令之前只创建了一个新pod
确保更新的Pod没问题之后,继续更新:
kubectl rollout resume deployment pc-deployment-rollingupdate -n dev
全量升级后只留下3个pod,会自动删除一个旧pod
3.Horizontal Pod Autoscaler(HPA)
已经可以通过手动执行kubectl scale
命令实现Pod的扩缩容,但是这显然不符合k8s的定位目标–自动化和智能化。k8s期望可以通过监测Pod的使用情况,实现Pod数量的自动调整,于是就产生了HPA这种控制器。
HPA可以获取每个Pod的利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA和之前的Deployment一样,也属于一种k8s资源对象,它通过追踪分析目标Pod的负载变化情况,来确定是否需要针对性的调整目标Pod的副本数
3.1安装metrics-server(v0.3.6)
metrics-server可以用来收集集群中的资源使用情况,监视pod负载量
获取metrics-server,需要注意使用的版本
wget https://github.com/kubernetes-sigs/metrics-server/archive/v0.3.6.tar.gz
解压v0.3.6.tar.gz文件
tar -zxvf v0.3.6.tar.gz
修改metrics-server-0.3.6/deploy/1.8+/metrics-server-deployment.yaml文件
vim metrics-server-0.3.6/deploy/1.8+/metrics-server-deployment.yaml
#添加下面选项
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
修改时注意使用空格键,tab会报错
安装metrics-server
kubectl apply -f ./
查看metrics-server生成的Pod:
kubectl get pod -n kube-system
查看资源使用情况
kubectl top node
kubectl top pod -n kube-system
如果出现异常:Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io),请查看:解决方案
3.2准备Deployment和Service
创建nginx.yaml文件
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: nginx # deployment的名称
namespace: dev # 命名类型
spec: # 详细描述
selector: # 选择器,通过它指定该控制器可以管理哪些Pod
matchLabels: # Labels匹配规则
app: nginx-pod
template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
ports:
- containerPort: 80 # 容器所监听的端口
resources: # 资源限制
requests:
cpu: "100m" # 100m表示100millicpu,即0.1个CPU
执行创建Deployment
kubectl create -f nginx.yaml
查看Deployment和Pod
kubectl get pod,deploy -n dev
创建Service
kubectl expose deployment nginx --name=nginx --type=NodePort --port=80 --target-port=80 -n dev
查看Service
kubectl get svc -n dev
3.3部署HPA
创建pc-hpa.yaml文件
apiVersion: autoscaling/v1 # 版本号
kind: HorizontalPodAutoscaler # 类型
metadata: # 元数据
name: pc-hpa # deployment的名称
namespace: dev # 命名类型
spec:
minReplicas: 1 # 最小Pod数量
maxReplicas: 10 # 最大Pod数量
targetCPUUtilizationPercentage: 3 # CPU使用率指标
scaleTargetRef: # 指定要控制的Nginx的信息
apiVersion: apps/v1
kind: Deployment
name: nginx
创建hpa
kubectl create -f pc-hpa.yaml
查看
kubectl get hpa -n dev
4.4测试
存在问题:教程访问的时候master节点ip,当前就群中无法通过master节点访问
使用测试工具进行压测 postman、apifox、Jmeter都可以访问node节点,设置请求数10000http://192.168.79.182:30184/
观察hpa的变化
kubectl get hpa -n dev -w
观察Deployment的变化,扩容之后当经历一段时间访问压力没那么大之后,就会进行缩容,可以看到图中最后又退回成1个nginx容器
观察pod变化
kubectl get pod -n dev -w
Service详解
Service介绍
k8s提供了Service资源,Service会对提供同一个服务的多个Pod进行聚合,并且提供一个统一固定ip入口地址,通过访问Service的入口地址就能访问到后面的Pod服务。
Service类型
spec.type的说明:
- ClusterIP:默认值,它是kubernetes系统自动分配的虚拟IP,只能在集群内部访问。
- NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务。
- LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境的支持。
- ExternalName:把集群外部的服务引入集群内部,直接使用。
Ingress介绍
Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点:
- NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显。
- LoadBalancer的缺点是每个Service都需要一个LB,浪费,麻烦,并且需要kubernetes之外的设备的支持。
基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求,工作机制大致如下图所示:
实际上,Ingress相当于一个七层的负载均衡器,是kubernetes对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解为Ingress里面建立了诸多映射规则,Ingress Controller通过监听这些配置规则并转化为Nginx的反向代理配置,然后对外提供服务。
- Ingress:kubernetes中的一个对象,作用是定义请求如何转发到Service的规则。
- Ingress Controller:具体实现反向代理及负载均衡的程序,对Ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现的方式有很多,比如Nginx,Contour,Haproxy等。
Ingress(以Nginx)的工作原理如下:
- 用户编写Ingress规则,说明那个域名对应kubernetes集群中的那个Service。
- Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx的反向代理配置。
- Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态更新。
- 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求规则。
Ingress实践
后续更新....