K8S之资源操作
K8S资源操作
Namespace
简介
名称空间是K8S中的重要一种资源环境,其主要作用提供了容器之间的系统资源隔离以及多租户之间的资源隔离。
默认情况下所有的pod资源都存储在默认的 default
这个名称空间之中,由于处在同一名称空间因此默认情况下所有的pod可以互相访问,但是实际环境之中可能由于不同的业务运行不同的pod且这些pod互不关联因此我们无需进行容器之间的互相访问,此时就可以将需要关联的容器划分到同一名称空间之中形成逻辑意义上的组,这样方便我们对不同的组资源进行隔离和管理。
可以通过kubernetes的授权机制,将不同的Namespace交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合K8S的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等等,来实现租户可用资源的管理。
应用示例
# 查看所有名称空间
[root@master ~]# kubectl get namespaces
'''
default:pod默认存放的名称空间
kube-node-lease:节点之间进行心跳检测
kube-public:可以被所有人访问包括未认证的用户
kube-system:K8S启动所需容器所处资源
'''
# 查看指定名称空间
[root@master ~]# kubectl get namespaces default
# 查看名称空间下所拥有的节点
[root@master ~]# kubectl get pods -n kube-system
'''
etcd-master:资源存储
kube-apiserver-master:指令入口
kube-scheduler-master:计算节点
kube-controller-manager-master:节点控制管理
'''
# 查看详情
[root@master ~]# kubectl describe namespaces default
'''
Status:
Active:表示名称空间在使用中
Terminating:标识名称空间在删除中,当名称空间之中容器过多删除过慢处于这个状态
No resource quota:针对名称空间做限制
No LimitRange resource:针对名称空间的组件做限制
'''
# 指定输出格式为yaml或者json等
[root@master ~]# kubectl get namespaces default -o yaml/json
# 创建名为dev的名称空间
[root@master ~]# kubectl create namespace dev
[root@master ~]# kubectl get namespace
# 删除名称空间
[root@master ~]# kubectl delete namespace dev
[root@master ~]# kubectl get namespaces
命令式对象配置
# 创建配置文件
cat > ns-dev.yaml << EOF
apiVersion: v1
# 创建种类名称空间
kind: Namespace
metadata:
# 名称空间命名dev
name: dev
EOF
# 创建名称空间
[root@master ~]# kubectl create -f ns-dev.yaml
[root@master ~]# kubectl get namespaces
# 删除名称空间
[root@master ~]# kubectl delete -f ns-dev.yaml
[root@master ~]# kubectl get namespaces
Pod
概述
-
Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
-
Pod可以认为是容器的封装,一个Pod中可以存在一个或多个容器。
应用示例
# 创建并且运行pod
[root@master ~]# kubectl run nginx --image=nginx:1.17.1 --port=80 --namespace dev
'''
run:pod控制器
image:容器镜像
port:暴露端口
namespace:运行名称空间
'''
# 查看详细信息
[root@master ~]# kubectl describe -n dev pods nginx
# 查看IP等信息
[root@master ~]# kubectl get -n dev pods nginx -o wide
# 访问容器
[root@master ~]# curl 10.244.22:80
# 删除容器[root@master ~]# kubectl delete pod nginx -n dev
命令式对象配置
# 创建配置文件
cat > pod-nginx.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
EOF
# 创建
[root@master ~]# kubectl create -f pod-nginx.yaml
[root@master ~]# kubectl get pods nginx -n dev
# 删除
[root@master ~]# kubectl delete -f pod-nginx.yaml
[root@master ~]# kubectl get pods -n dev
Label
概述
-
Label是kubernetes的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
-
可以通过Label实现资源的多纬度分组,以便灵活、方便地进行资源分配、调度、配置和部署等管理工作。
-
label特点
- 一个label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等。
- 一个资源对象可以定义任意数量的label,同一个label也可以被添加到任意数量的资源对象上去。
- label通常在资源对象定义时确定,当然也可以在对象创建后动态的添加或删除
● 版本标签:"version":"release","version":"stable"
● 环境标签:"environment":"dev","environment":"test","environment":"pro"
● 架构标签:"tier":"frontend","tier":"backend"
标签选择器
-
标签用于对pod进行分类定义,如果需要查找到这些pod就需要标签选择器类似于MySQL中的where条件
- label用于对pod进行定义标识
- label selector用于对被标识的pod进行查询和筛选
-
当前共有两种标签选择器
- 基于等式的label selector
- environment=dev:查询键值key=environment并且value=dev的pod
- environment!=dev:查询键值key=environment并且值value=!dev的pod
- 基于集合的label selector
- environment in (dev,test):选择key=environment并且value=dev或者value=test
- environment not in (dev,test):选择key=environment并且value!=dev或者value!=test
- 基于等式的label selector
-
标签选择器可以使用多个,此时多个标签选择器就行组合中间使用逗号分割
- name=salve,env!=production
- name not in (master,slave),env!=production
使用示例
# 查看pod标签
[root@master ~]# kubectl get pod nginx -n dev --show-labels
# 添加标签 version=1.0
[root@master ~]# kubectl label pod nginx version=1.0 -n dev
# 修改标签 --overwrite 覆盖原有标签
[root@master ~]# kubectl label pod nginx -n dev version=2.0 --overwrite
# 报错修改需要加上--overwrite
[root@master ~]# kubectl label pod nginx -n dev version=1.0
# 删除标签 key-
[root@master ~]# kubectl label pod nginx -n dev version-
# 查看所有pod节点
[root@master ~]# kubectl get pods -n dev --show-labels
# 标签选择器
[root@master ~]# kubectl get pods -l version=2.0,env=test -n dev --show-labels
# 集合选择器
[root@master ~]# kubectl get pod -l "version in (2.0,1.0)" -n dev --show-labels
命令式对象配置
# 创建文件
cat > pod-nginx.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
version: "3.0"
env: "test"
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
EOF
# 创建
[root@master ~]# kubectl create -f pod-nginx.yaml
# 查看
[root@master ~]# kubectl get pod -n dev --show-labels
# 删除
[root@master ~]# kubectl delete -f pod-nginx.yaml
[root@master ~]# kubectl get pod -n dev --show-labels
Deployment
概述
-
在kubernetes中,Pod是最小的控制单元,但是kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。
-
Pod控制器用于Pod的管理,确保Pod资源符合预期的状态,当Pod的资源出现故障的时候,会尝试进行重启或重建Pod。
-
在kubernetes中Pod控制器的种类有很多,本章节只介绍一种:Deployment。
应用示例
# 创建控制器命名为nginx
[root@master ~]# kubectl create deployment nginx --image=nginx:1.17.1 -n dev
[root@master ~]# kubectl get deployments -n dev
# 在控制器下创建pod --replicas指定pod数量
[root@master ~]# kubectl scale deployment nginx -n dev --replicas=4
[root@master ~]# kubectl get pods -n dev
# 查看deployment详细信息
[root@master ~]# kubectl describe deployments -n dev
# 删除控制器下所对应的pod节点
[root@master ~]# kubectl delete pods -n dev nginx-59975f44cf-kwr7w
# 在K8S中控制器会对容器进行监控 当容器出现故障会对容器重新创建
[root@master ~]# kubectl delete deployments nginx -n dev # 删除控制器
[root@master ~]# kubectl get deployments -n dev
命令式对象配置
cat > deploy-nginx.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx:1.17.1
name: nginx
ports:
- containerPort: 80
protocol: TCP
EOF
# 创建
[root@master ~]# kubectl create -f deploy-nginx.yaml
[root@master ~]# kubectl get deployments -n dev
# 删除
[root@master ~]# kubectl delete -f deploy-nginx.yaml
[root@master ~]# kubectl get deployments -n dev
Service
概述
-
上述可以使用控制器来创建一组Pod来提供高可用性的服务且每个Pod都会分配一个单独的Pod的IP地址,但是却存在如下的问题:
-
- Pod的IP会随着Pod的重建产生变化。
-
- Pod的IP仅仅是集群内部可见的虚拟的IP,外部无法访问
- 在K8S中为我们提供了service解决该问题,service可以看做一组pod对外访问的接口,借助service可以实现服务被访问以及负载均衡
应用示例
# 暴露service命名为svc-nginx 使用nginx控制器 -type=ClusterIP给服务暴露ip地址
[root@master ~]# kubectl expose --name=svc-nginx deployment nginx --type=ClusterIP --port=80 --target-port=80 -n dev
# 查看
[root@master ~]# kubectl get service -n dev
# 格式化输出
[root@master ~]# kubectl get service -n dev -o wide
# 访问集群 但是此时只允许集群内部访问
[root@master ~]# curl 10.102.7.34:80
# --type=NodePort暴露给外部
[root@master ~]# kubectl expose --name=svc-nginx1 -n dev deployment nginx --type=NodePort --port=8080 --target-port=80
[root@master ~]# kubectl get service -n dev -o wide
# 集群外部访问
curl http://10.1.1.2:30719
# 删除service
[root@master ~]# kubectl delete service -n dev svc-nginx
命令式对象配置方式
cat > svc-nginx.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
clusterIP: 10.109.179.231
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: nginx
type: ClusterIP
EOF
# 创建
[root@master ~]# kubectl create -f svc-nginx.yaml
[root@master ~]# kubectl get service -n dev
# 删除配置
[root@master ~]# kubectl delete -f svc-nginx.yaml
[root@master ~]# kubectl get service -n dev
Pod配置
容器属性是pod中非常重要的属性
[root@master k8s]# kubectl explain pod.spec.containers
'''
返回的重要属性
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 数组,代表可以有多个容器FIELDS:
name <string> # 容器名称
image <string> # 容器需要的镜像地址
imagePullPolicy <string> # 镜像拉取策略本地镜像或者远程仓库镜像
command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args <[]string> # 容器的启动命令需要的参数列表
env <[]Object> # 容器环境变量的配置
ports <[]Object> # 容器需要暴露的端口号列表
resources <Object> # 资源限制和资源请求的设置
'''
基本配置
# 创建yaml文件
cat > pod.base.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
# pod名称
name: pod-base
# 名称空间
namespace: dev
# 标签
labels:
user: SR
spec:
containers:
# 容器名称
- name: nginx
# 镜像
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod.base.yaml
# 查看pod
[root@master k8s]# kubectl get pod -n dev
# 查看pod详细信息
[root@master k8s]# kubectl describe -n dev pod pod-base
镜像拉取策略
# 创建pod-imagepullpolicy.yaml
cat > pod-imagepullpolicy.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: Always
- name: busybox
image: busybox:1.30
EOF
K8S镜像拉取策略支持三种
- Always:一直使用远程仓库拉取镜像
- IfNotPresent:本地仓库有镜像则使用本地仓库镜像否则就使用远程仓库镜像
- Never:一直使用本地仓库镜像本地不存在就报错
'''
默认值说明
1.如果tag为具体版本则默认为IfNotPresent
2.如果tag为最新版本则默认为Never
'''
创建pod
[root@master k8s]# kubectl apply -f pod-imagepullpolicy.yaml
[root@master k8s]# kubectl describe -n dev pod pod-imagepullpolicy
启动命令
在前面的案例中,一直有一个问题没有解决,就是busybox容器一直没有成功运行,那么到底是什么原因导致这个容器的故障的呢?
在busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command的配置。
cat > pod-command.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
labels:
user: SR
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;"]
EOF
# 创建pod
[root@master k8s]# kubectl apply -f pod-command.yaml
# 查看pod
[root@master k8s]# kubectl get pod -n dev
# 进入pod容器
[root@master k8s]# kubectl exec -it -n dev pod-command -c busybox /bin/sh
环境变量
- 用于给容器配置环境变量
- 对于环境变量更加推荐在配置文件配置环境变量
# 创建配置文件
cat > pod.env.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
labels:
user: SR
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;",
]
env:
# 设置环境变量的key/value值
- name: "username"
value: "SR"
- name: "password"
value: "root"
EOF
[root@master k8s]# kubectl create -f pod-env.yaml
# 查看pod节点
[root@master k8s]# kubectl get pod -n dev
# 进入容器查看环境变量
[root@master k8s]# kubectl exec -it -n dev pod-env -c busybox /bin/sh
端口配置
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”
# 创建配置文件
cat > pod-port.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: IfNotPresent # 设置镜像拉取策略
ports:
- name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
containerPort: 80 # 容器要监听的端口 (0~65536)
protocol: TCP # 端口协议
EOF
[root@master k8s]# kubectl create -f pod-ports.yaml
# 查看pod
[root@master k8s]# kubectl get pod -n dev
# 查看pod的IP和端口
[root@master k8s]# kubectl get pod -n dev pod-ports -o yaml
# 查看ip
[root@master k8s]# kubectl get pod -n dev pod-ports -o wide
# 访问内部服务
[root@master k8s]# curl 10.244.1.21:80
资源限制
-
容器中的程序要运行,肯定会占用一定的资源,比如CPU和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量的资源,导致其他的容器无法运行。针对这种情况,kubernetes提供了对内存和CPU的资源进行配额的机制,这种机制主要通过resources选项实现,它有两个子选项:
-
limits:用于限制运行的容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启。
-
requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
-
-
可以通过上述两个选项设置资源上下限
# 创建配置文件
cat > por-resources.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-resoures
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
resources:
# 配置上限
limits:
memory: "10G"
cpu: "4"
# 配置下限
requests:
memory: "100M"
cpu: "1"
EOF
[root@master k8s]# kubectl create -f pod-resoures.yaml
[root@master k8s]# kubectl get pod -n dev
# 查看容器IP
[root@master k8s]# kubectl get pod -n dev pod-resoures -o wide
# 访问容器
[root@master k8s]# curl 10.244.1.22:80
# 现在修改容器资源下限故意将资源超出虚拟机配置
requests:
memory: "10Gi"
cpu: "5"
# 删除原有pod
[root@master k8s]# kubectl delete -f pod-resoures.yaml
# 增加pod
[root@master k8s]# kubectl create -f pod-resoures.yaml
# 查看
[root@master k8s]# kubectl describe pod -n dev pod-resoures
## Pod生命周期
简介
我们一般将Pod从创建到终止这段时间称之为Pod生命周期,其主要包含如下过程
-
Pod创建过程
-
运行初始化容器过程(init container)
-
运行主容器过程(main container)
- 容器启动后钩子函数(post start),容器终止之前钩子函数(pre stop)
- 容器的存活性探测(liveness probe),容器就绪性探测(readiness probe)
-
Pod终止过程
在Pod创建过程中会经历如下状态
- 挂起(pending):Pod正在处于被调度到具体节点过程中或者拉取镜像的过程中
- 运行(running):Pod已经被调度到某个节点之中并且有kubelet创建完成
- 成功(Successded):Pod内部的所有容器都已经成功执行完毕并且不会再次重启
- 失败(Fail):Pod内部所有容器都已经终止但是有一个容器终止失败,即返回非0的状态
- 位置(Unknow):Apiserver无法获取pod信息一般由网络原因导致
创建和终止过程
创建过程
- apiserver接收客户端或者kubelet创建pod的请求
- apiserver将创建的pod信息存储到etcd中,然后将结果返回给客户端
- apiserver开始反映pod的状态变化,其余节点通过
watch
机制监听pod信息状态变化 - scheduler通过
watch
机制发现有pod需要被创建,通过一定算法计算出pod分配节点信息并且将结果返回给apiserver - node节点的kubelet接收创建信息启动docker并且将结果返回给apiserver
- apiserver将pod信息存储到etcd中
终止过程
- apiserver接收用户删除pod指令
- pod的状态信息会随着时间的推移而更新,在宽限其内(默认30S)状态为
dead
宽限期是因为pod中运行容器删除pod需要停止容器花费时间 - 将pod状态标记为
terminating
状态 - kubelet监控到pod对象转为
terminating
会启动关闭pod过程 - 端点控制器会将pod从所匹配的service资源删除,由于pod都不存在了也不需要与service关联了
- 如果pod定义了
pre stop
钩子函数,则在其状态为terminating
状态的时候同步执行钩子函数 - Pod对象中的容器进程收到停止信号
- 若在宽限期内Pod还有运行的容器,则Pod会收到立即中止的信号
- kubectl请求API Server将此Pod资源的宽限期设置为0从而完成删除操作,此时Pod对于用户已经不可用了
初始化容器
初始化容器即在容器启动之前需要进行的准备工作,主要具备两大特征
- 初始化容器必须运行完成直至结束,如果某个容器初始化失败,kubernetes会一直进行容器重启直至成功
- 初始化容器必须按照定义的顺序执行且只有当前容器执行完毕才能执行下一个容器
初始化容器运用场景
- 提供主容器镜像中不具备的工具程序或自定义代码
- 按照串行化启动容器,因此某个容器只有满足条件才可以启动成功
此时假如我们运行nginx容器,但是要求nginx启动之前必须先运行mysql以及redis容器成功启动才可以启动nginx容器
为了简化测试,事先规定好MySQL和Redis所在的IP地址分别为 10.1.1.10
和 10.1.1.20
(注意,这两个IP都不能ping通,因为环境中没有这两个IP)
# 创建配置文件
cat > pod-initcontainer.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
labels:
user: SR
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
resources:
limits:
cpu: "2"
memory: "10Gi"
requests:
cpu: "1"
memory: "10Mi"
initContainers: # 初始化容器配置
- name: test-mysql
image: busybox:1.30
command: ["sh","-c","until ping 10.1.1.10 -c 1;do echo waiting for mysql ...;sleep 2;done;"]
- name: test-redis
image: busybox:1.30
command: ["sh","-c","until ping 10.1.1.20 -c 1;do echo waiting for redis ...;sleep 2;done;"]
EOF
# 运行
[root@master k8s]# kubectl create -f pod-initcontainer.yaml
# 查看
[root@master k8s]# kubectl get pod -n dev
[root@master k8s]# kubectl describe -n dev pod pod-initcontainer
# 动态查看pod运行情况
[root@master k8s]# kubectl get pod pod-initcontainer -n dev -w
# 新开一个终端添加IP地址10.1.1.20
[root@master k8s]# ifconfig ens33:10 10.1.1.10 netmask 255.255.255.0 up
# 新开一个终端添加IP地址10.1.1.20
[root@master k8s]# ifconfig ens33:20 10.1.1.20 netmask 255.255.255.0 up
钩子函数
钩子函数意外我们可以在容器创建之后与销毁之前执行某些动作帮助我们更好的管理容器
钩子处理器支持三种方式定义动作
-
exec命令:在容器内执行命令
…… lifecycle: postStart: exec: command: - cat - /tmp/healthy ……
-
tcpsocket:在当前容器尝试访问指定的socket
…… lifecycle: postStart: tcpSocket: port: 8080 ……
-
httpGet:在当前容器中向某url发起HTTP请求
…… lifecycle: postStart: httpGet: path: / #URI地址 port: 80 #端口号 host: 192.168.109.100 #主机地址 scheme: HTTP #支持的协议,http或者https ……
接下来,以exec方式为例,演示下钩子函数的使用
# 创建文件
cat > pod-hook-exec.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "10Gi"
cpu: "2"
requests:
memory: "10Mi"
cpu: "1"
ports:
- containerPort: 80
protocol: TCP
lifecycle:
postStart:
exec:
# 容器启动之前执行命令
command:
# 修改nginx的默认首页
[
"/bin/sh",
"-c",
"echo postStart ... > /usr/share/nginx/html/index.html",
]
preStop:
# 容器停止执行命令
exec:
command: ["/usr/sbin/nginx", "-s", "quit"]
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-hook-exec.yaml
# 查看IP地址
[root@master k8s]# kubectl get -n dev pod pod-hook-exec -o wide
# 访问容器
[root@master k8s]# curl 10.244.1.29
容器探测
容器探测主要检测容器能否正常对外工作,如果经过探测容器的状态不符合预期,则容器会被K8S移除不承担业务流量,在K8S中提供了存活性探测与就绪性探测。
-
liveness(存活性探测):检测容器是否正常运行,如果运行异常则会将容器重启
-
readiness(就绪性探测):检测容器能否正常接收请求,如果不能则不转发流量
探测方式
- exec:在容器内部执行命令,如果容器返回为非0的状态码则容器异常会被重启
……
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
……
- tcpSocket:连接容器内部端口如果能正常连接则容器正常反之容器异常
……
livenessProbe:
tcpSocket:
port: 8080
……
- httpGet:对容器的url发起请求,如果请求状态码处于
200-399
之前则认为容器正常反之异常
……
livenessProbe:
httpGet:
path: / #URI地址
port: 80 #端口号
host: 127.0.0.1 #主机地址
scheme: HTTP #支持的协议,http或者https
……
探测实验:exec
# 创建文件
cat > pod-liveness-exec.yaml << EOF
kind: Pod
metadata:
name: pod-liveness-exec
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "10Gi"
cpu: "2"
requests:
memory: "10Mi"
cpu: "1"
ports:
- containerPort: 80
protocol: TCP
livenessProbe:
exec:
# 执行一个查看文件的命令,必须失败,因为根本没有这个文件
command: ["/bin/cat", "/tmp/hello.txt"]
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-liveness-exec.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-liveness-exec -w
# 查看详细信息
[root@master k8s]# kubectl describe -n dev pod pod-liveness-exec
# 查看pod节点信息
[root@master k8s]# kubectl get pod pod-liveness-exec -n dev
探测实验:tcpSocket
cat > pod-liveness-tcpsocket.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-tcpsocket
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "10Gi"
cpu: "2"
requests:
memory: "10Mi"
cpu: "1"
ports:
- containerPort: 80
protocol: TCP
livenessProbe:
# 探测8080端口但是容器内部未暴露8080因此容器异常
tcpSocket:
port: 8080
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-liveness-tcpsocket.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-liveness-tcpsocket -w
# 查看详细信息
[root@master k8s]# kubectl describe -n dev pod pod-liveness-tcpsocket
[root@master k8s]# kubectl get -n dev pod pod-liveness-tcpsocket
探测实验:httpGet
# 创建配置文件
cat > pod-liveness-httpget.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources:
limits:
memory: "10Gi"
cpu: "2"
requests:
memory: "10Mi"
cpu: "1"
ports:
- containerPort: 80
protocol: TCP
livenessProbe:
# 其实就是访问http://127.0.0.1:80/hello
httpGet:
port: 80
host: 127.0.0.1
# 访问路径
path: /hello
# 访问方式
scheme: HTTP
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-liveness-httpget.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-liveness-httpget -w
# 查看详细信息
[root@master k8s]# kubectl describe -n dev pod pod-liveness-httpget
[root@master k8s]# kubectl get -n dev pod pod-liveness-httpget
探测可选项
'''
在livenessProbe中包含一些可选项
initialDelaySeconds # 容器启动后等待多少秒执行第一次探测
timeoutSeconds # 探测超时时间。默认1秒,最小1秒
periodSeconds # 执行探测的频率。默认是10秒,最小1秒
failureThreshold # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold # 连续探测成功多少次才被认定为成功。默认是1
'''
重启策略
在容器探测中,一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由Pod的重启策略决定的,Pod的重启策略有3种,分别如下
- Always:默认重启策略,即容器失效时候自动重启容器
- OnFailure:容器终止运行且退出码不为0时候重启
- Never:绝不重启会停止容器
重启策略适用于Pod对象中的所有容器,首次需要重启的容器,将在其需要的时候立即进行重启,随后再次重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大的延迟时长。
# 创建文件
cat > pod-restart-policy.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-restart-policy
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "10Gi"
cpu: "2"
requests:
memory: "10Mi"
cpu: "1"
ports:
- containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
port: 80
host: 127.0.0.1
scheme: HTTP
path: /
restartPolicy: Never
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-restart-policy.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-restart-policy
# 查看创建过程
[root@master k8s]# kubectl describe -n dev pod pod-restart-policy
## Pod调度
简介
在默认情况下Pod是通过Scheduler通过一定的算法调度到相应的node节点上,这一过程人为是不能干扰的,但是在有时候我们希望控制pod所处节点位置,在K8S中为我们提供了Pod调度策略,我们可以通过Pod调度策略依据策略将Pod调度到指定的node节点上,在K8S中为我们提供了四种调度策略。
调度策略
- 自动调度:由Scheduler通过一定的算法自动调度到node节点上人为无法干预
- 定向调度:通过nodename/nodeselector等调度到指定的node节点上
- 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity。
- 污点(容忍)调度:Taints、Toleration
定向调度
简介
在Pod节点上声明nodename或者nodeselector,使用nodename或者nodeselector将pod调度到指定的node上,这里的调度是强制的如果声明的node节点不存在依旧会将pod调度到该node节点上,但是pod会运行失败而已
nodename
nodeName用于强制约束将Pod调度到指定的name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点
# 创建pod配置文件
cat > pod-nodename.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
imagePullPolicy: IfNotPresent
image: nginx:1.17.1
ports:
- containerPort: 80
protocol: TCP
# 指定将容器调度到node1节点
nodeName: node1
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-nodename.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-nodename -o wide
# 此时将nodename指定一个不存在的node节点上
nodeName: node3
# 查看node节点
[root@master k8s]# kubectl get -n dev pod pod-nodename
nodeselector
nodeSelector用于将Pod调度到添加了指定标签的Node节点上,它是通过kubernetes的label-selector机制实现的,换言之,在Pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将Pod调度到目标节点,该匹配规则是强制约束
# 给node添加标签
[root@master k8s]# kubectl label node node1 env=test
[root@master k8s]# kubectl label node node2 env=pro
# 查看标签
[root@master k8s]# kubectl get node --show-labels
# 创建配置文件
cat > pod-nodeselector << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
# 配置标签选择器
nodeSelector:
# 选择标签
env: test
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-nodeselector.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-nodeselector -o wide
# 将上述标签配置一个不存在的标签
env: dev
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-nodeselector -o wide
# 查看详情
[root@master k8s]# kubectl describe -n dev pod pod-nodeselector
亲和性调度
简介
在上述定向调度的时候如果node不满足pod选择条件,虽然pod依旧会被创建但是无法正常对外提供服务,即使集群中有可用的node节点依旧无法正常运行
在K8S中提供了容器亲和性调度,其在 nodeSelector
基础上进行了扩展,如果当前node节点满足pod创建要求则会被创建到满足要求的节点上,如果没有节点满足要求会退而求其次即使节点不满足条件依旧可以被创建,这样保证了我们pod能够正常被创建对外提供服务。
调度类型
- nodeAffinity(node亲和性):以node为目标,解决pod可以调度到那些node节点
- podAffinity(pod亲和性):以Pod为目标,解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。
- podAntiAffinity(pod反亲和性):以Pod为目标,解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。
关于亲和性和反亲和性的使用场景的说明:
亲和性:如果两个应用频繁交互,那么就有必要利用亲和性让两个应用尽可能的靠近,这样可以较少因网络通信而带来的性能损耗。
反亲和性:当应用采用多副本部署的时候,那么就有必要利用反亲和性让各个应用实例打散分布在各个Node上,这样可以提高服务的高可用性。
nodeAffinity详解
- 语法配置
pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的所有规则才可以,相当于硬限制
nodeSelectorTerms 节点选择列表
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
preference 一个节点选择器项,与相应的权重相关联
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 倾向权重,在范围1-100。
- 使用案例
- matchExpressions:
- key: env # 匹配存在标签的key为nodeenv的节点
operator: Exists
- key: env # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx","yyy"]
- key: env # 匹配标签的key为nodeenv,且value大于"xxx"的节点
operator: Gt
values: "xxx"
- 硬条件实验测试
# 创建配置文件
cat > pod-nodeaffinity-required << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
# 配置亲和度
affinity:
# 选择节点亲和度
nodeAffinity:
# 选择硬限制
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
# 选择节点标签
- matchExpressions:
# 匹配节点key=env value=xxx或者value=yyy的节点
- key: env
values:
- "xxx"
- "yyy"
operator: In
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-nodeaffinity-required.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-nodeaffinity-required -o wide
# 查看详情
[root@master k8s]# kubectl describe -n dev pod pod-nodeaffinity-required
# 将value中不存在的值修改为存在
'''
values:
- "test"
- "yyy"
'''
# 修改完毕之后查看pod节点
[root@master k8s]# kubectl get -n dev pod pod-nodeaffinity-required -o wide
- 软条件测试
cat > pod-nodeaffinity-preferred.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-preferred
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
protocol: TCP
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference: # 一个节点选择器项与相应的权重相关联
matchExpressions:
- key: env
# 创建不存在的value
values:
- "xxx"
- "yyy"
operator: In
weight: 1
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-nodeaffinity-preferred.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-nodeaffinity-preferred -o wide
nodeAffinity的注意事项:
如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都满足,Pod才能运行在指定的Node上。
如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功。
如果一个Pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的nodeAffinity的要求,则系统将忽略此变化。
podAffinity详解
podAffinity主要实现以运行的Pod为参照,实现让新创建的Pod和参照的Pod在一个区域的功能
pod.spec.affinity.podAffinity
requiredDuringSchedulingIgnoredDuringExecution # 硬限制
namespaces # 指定参照pod的namespace
topologyKey # 指定调度作用域
labelSelector # 标签选择器 选择参照pod
matchExpressions # 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator # 关系符 支持In, NotIn, Exists, DoesNotExist.
matchLabels # 指多个matchExpressions映射的内容
preferredDuringSchedulingIgnoredDuringExecution # 软限制
podAffinityTerm # 选项
namespaces
topologyKey # 位置拓扑键,用来判定拥有哪些标签的节点是同一位置以确定pod绑定在那个节点上
labelSelector
matchExpressions
key 键
values 值
operator
matchLabels
weight 倾向权重,在范围1-1
topologyKey用于指定调度的作用域,例如:
如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围。
如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分。
# 先创建参照pod
cat > pod-podaffinity-target.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
podenv: pro # 设置标签用来选择pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
protocol: TCP
nodeName: node1
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-podaffinity-target.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-podaffinity-target -o wide --show-labels
# 创建硬限制
cat > pod-podaffinity-requred.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-requred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
# 匹配Key=podenv value=xxx或者value=yyy由于值不存在因此不能被正确的调度到节点上
- key: podenv
values:
- "xxx"
- "yyy"
operator: In
# 以节点名字作为拓扑键
topologyKey: kubernetes.io/hostname
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-podaffinity-requred.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-podaffinity-requred -o wide
# 查看详情
[root@master k8s]# kubectl describe -n dev pod pod-podaffinity-requred
# 将值修改为正确的 pro存在
values:
- "pro"
- "yyy"
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-podaffinity-requred -o wide
cat > pod-podaffinity-preferred.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-preferred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
topologyKey: kubernetes.io/hostname
namespaces:
- "dev"
labelSelector:
matchExpressions:
- key: prodev
values:
- "xxx"
- "yyy"
operator: In
weight: 1
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-podaffinity-preferred.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-podaffinity-preferred -o wide
podAffinityTerm详解
podAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod和参照的Pod不在一个区域的功能
# 创建pod配置文件
cat > pod-podantiaffinity-requred.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-requred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
protocol: TCP
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: podenv
# 只要key!=podenv values!=pro即可
values:
- "pro"
operator: In
topologyKey: kubernetes.io/hostname
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-podantiaffinity-requred.yaml
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod-podantiaffinity-requred -o wide
污点和容忍
前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否运行Pod调度过来Node被设置了污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
污点的格式为:key=value:effect
,key和value是污点的标签,effect描述污点的作用,支持如下三个选项
- PreferNoSchedule:K8S尽量避免将pod调度到具有该污点的node上,除非没有其余可用节点才会调度到污染节点上
- NoSchedule:K8S不会将Pod调度到具有污染的node上,但是已经存在的node节点不会被影响
- NoExecute:K8S不会将pod调度到具有污染的node上同时已经存在的pod也会被驱逐
语法
# 设置污点
kubectl taint node xxx key=value:effect
# 删除污点
kubectl taint node xxx key:effect-
# 删除所有污点
kubectl taint node xxx key-
实验步骤
- 首先关闭node2保证只有一个节点
- 为node1节点设置一个污点:
user=SR:PreferNoSchedule
,然后创建Pod1(Pod1可以) - 修改node1节点污点为
user=SR:NoSchedule
,然后创建pod2(pod1可以正常运行,pod2创建失败) - 修改node1节点污点为
user=SR:NoExecute
, 然后创建pod3(三个都失败)
污点
# 停止node2
[root@node2 ~]# systemctl status kubelet.service
# 创建污点
[root@master k8s]# kubectl taint node node1 user=SR:PreferNoSchedule
# 创建pod
[root@master k8s]# kubectl run pod1 --image=1.17.1 -n dev
# 查看pod
[root@master k8s]# kubectl get -n dev pod pod1 -o wide
# 修改node1污染属性为NoSchedule
[root@master k8s]# kubectl taint node node1 user:PreferNoSchedule- # 删除原有属性
# 添加新属性
[root@master k8s]# kubectl taint node node1 user=SR:NoSchedule
# 创建pod2
[root@master k8s]# kubectl run pod2 --image=nginx:1.17.1 -n dev
# 查看pod
[root@master k8s]# kubectl get pod pod1 pod2 -n dev -o wide
# 查看详情
[root@master k8s]# kubectl describe -n dev pod pod2
# 修改node1节点属性为NoExecute
[root@master k8s]# kubectl taint node node1 user:NoSchedule-
# 创建污染属性
[root@master k8s]# kubectl taint node node1 user=SR:NoExecute
# 创建pod
[root@master k8s]# kubectl run pod3 -n dev --image=nginx:1.17.1
# 查看详情
[root@master k8s]# kubectl describe -n dev pod pod3
使用kubeadm搭建的集群,默认就会给Master节点添加一个污点标记,所以Pod就不会调度到Master节点上。
容忍
我们可以在Node上添加污点用来拒绝Pod调度上来,但是如果就是想让一个Pod调度到一个有污点的Node上去,我们可以通过容忍来允许pod添加到有污染的node节点上。
污点就是node拒绝pod加入节点,而容忍则是忽略某些pod允许加入节点
kubectl explain pod.spec.tolerations
......
FIELDS:
key # 对应着要容忍的污点的键,空意味着匹配所有的键
value # 对应着要容忍的污点的值
operator # key-value的运算符,支持Equal和Exists(默认)Exists对应的是key忽略value
effect # 对应污点的effect,空意味着匹配所有影响
tolerationSeconds # 容忍时间, 当effect为NoExecute时生效,表示已经存在的pod在Node上的停留时间
当operator为Equal的时候,如果Node节点有多个Taint,那么Pod每个Taint都需要容忍才能部署上去。
当operator为Exists的时候,有如下的三种写法:
容忍指定的污点,污点带有指定的effect:
容忍指定的污点,不考虑具体的effect:
容忍一切污点(慎用):
tolerations: # 容忍
- key: "tag" # 要容忍的污点的key
operator: Exists # 操作符
effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
tolerations: # 容忍
- key: "tag" # 要容忍的污点的key
operator: Exists # 操作符
tolerations: # 容忍
- operator: Exists # 容忍一切规则
在上面的污点中,已经给node1打上了NoExecute的污点,此时Pod是调度不上去的,此时可以通过在Pod中添加容忍,将Pod调度上去
# 添加配置文件
cat > pod-toleration.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
namespace: dev
labels:
user: SR
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
# 添加容忍
tolerations:
# user=SR作用在NoExecute可以被添加
- key: "user"
value: "SR"
operator: Equal
effect: NoExecute
EOF
# 创建pod
[root@master k8s]# kubectl create -f pod-toleration.yaml
# 查看pod
[root@master flannel]# kubectl get -n dev pod pod-toleration -o wide