https://www.yuque.com/fairy-era/yg511q/od4gy0#72eaf3c6
在k8s集群中部署一个nginx,测试集群是否正常工作

通过expose暴露外部访问
查看service

浏览器访问节点ip:30355

Kubernetes中,所有的内容都抽象为资源
用户可以在Kubernetes集群中部署各种服务。所谓的部署服务,其实就是在Kubernetes集群中运行一个个的容器,并将指定的程序跑在容器中。
Kubernetes的最小管理单元是Pod而不是容器,所以只能将容器放在
Pod中,而Kubernetes一般也不会直接管理Pod,而是通过Pod控制器来管理Pod的。Pod提供服务之后,就需要考虑如何访问Pod中的服务,Kubernetes提供了
Service资源实现这个功能。当然,如果Pod中程序的数据需要持久化,Kubernetes还提供了各种
存储系统。
学习kubernets的核心,就是学习如何对集群中的Pod、Pod控制器、Service、存储等各种资源进行操作。
一些命令

-o wide 展示更多信息
-o json json格式详细信息
-o yaml yaml格式相信信息

创建和删除命名空间


describe中的event


scheduler分发pod到node
容器的创建和运行是由kubelet执行的
声明式管理资源
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:1.17.1
kubectl apply -f nginxpod.yaml
kubectl get -f nginxpod.yaml
kubectl delete -f nginxpod.yaml
实现在node节点上执行kubectl
kubectl的运行需要配置文件:$HOME/.kube,如果想要在node节点上运行此命令,需要将master节点的.kube文件夹复制到node节点上,即在master节点上执行下面的操作:
scp -r $HOME/.kube k8s-node1:$HOME

namespace


pod
Pod可以认为是容器的封装,一个Pod中可以存在一个或多个容器

run创建的是pod,没有控制器(老版本使用run是有控制器的)



通过pod自身的ip访问pod

删除pod,由于没有控制器,不会再次创建

yaml描述
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
kubectl apply -f pod-nginx.yaml
kubectl delete -f pod-nginx.yaml
label
打标签

更新标签

筛选带指定标签的pod

删除标签

使用yaml声明标签
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
Deployment(Pod控制器)
Pod控制器用于Pod的管理,确保Pod资源符合预期的状态,当Pod的资源出现故障的时候,会尝试进行重启或重建Pod。
创建deploy(pod)


使用yml
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

删除deploy会同时删除pod
用Deployment来创建一组Pod提供服务,每个Pod都会分配一个单独的Pod的IP地址,但是却存在如下的问题:
- Pod的IP会随着Pod的重建产生变化
- Pod的IP仅仅是集群内部可见的虚拟的IP,外部无法访问
Service来解决这个问题

Service可以看做是一组同类的Pod对外的访问接口,借助Service,应用可以方便的实现服务发现和负载均衡
service也是通过标签选择器机制关联pod
CLusterIP类型只能集群内部访问

--type=NodePort 支持集群外部访问
PORT(S)中80后的端口即为外部访问端口,可以通过node节点的ip+这个端口访问pod

pod
pod的结构

Pause容器是每个Pod都会有的一个根容器,它的作用有两个:
评估整个Pod的健康状况
可以在根容器上设置IP地址,其它容器都共享此IP,以实现Pod内部的网络通信
yml模板
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 资源类型.属性

在kubernetes中基本所有资源的一级属性都是一样的,主要包含5个部分:
- apiVersion <string>:版本,有kubernetes内部定义,版本号必须用
kubectl api-versions查询。 - kind <string>:类型,有kubernetes内部定义,类型必须用
kubectl api-resources查询。 - metadata <Object>:元数据,主要是资源标识和说明,常用的有
name、namespace、labels等。 - spec <Object>:描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述。
- status <Object>:状态信息,里面的内容不需要定义,由kubernetes自动生成。
在上面的属性中,spec是接下来研究的重点,继续看下它的常见子属性:
- containers <[]Object>:容器列表,用于定义容器的详细信息。
- nodeName <String>:根据nodeName的值将Pod调度到指定的Node节点上。
- nodeSelector <map[]> :根据NodeSelector中定义的信息选择该Pod调度到包含这些Label的Node上。
- hostNetwork <boolean>:是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络(要注意端口冲突)。
- volumes <[]Object> :存储卷,用于定义Pod上面挂载的存储信息。
- restartPolicy <string>:重启策略,表示Pod在遇到故障的时候的处理策略。
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> # 资源限制和资源请求的设置
containers基本配置
apiVersion: v1
kind: Pod
metadata:
name: pod-base
namespace: dev
labels:
user: yimin
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
- name: busybox # 容器名称
image: busybox:1.30 # 容器需要的镜像地址
imagePullPolicy
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
labels:
user: xudaxian
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: IfNotPresent # 用于设置镜像的拉取策略
- name: busybox # 容器名称
image: busybox:1.30 # 容器需要的镜像地址
- Always:总是从远程仓库拉取镜像
- IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像
- Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错
如果镜像tag为具体的版本号,默认策略是IfNotPresent。
如果镜像tag为latest,默认策略是Always。
https://www.yuque.com/fairy-era/yg511q/od4gy0
command
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
labels:
user: xudaxian
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已经可以完成启动命令和传递参数的功能,为什么还要提供一个args选项,用于传递参数?
其实和Docker有点关系,kubernetes中的command和args两个参数其实是为了实现覆盖Dockerfile中的ENTRYPOINT的功能:
● 如果command和args均没有写,那么用Dockerfile的配置。
● 如果command写了,但是args没有写,那么Dockerfile默认的配置会被忽略,执行注入的command。
● 如果command没有写,但是args写了,那么Dockerfile中配置的ENTRYPOINT命令会被执行,使用当前args的参数。
● 如果command和args都写了,那么Dockerfile中的配置会被忽略,执行command并追加上args参数。
env
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
labels:
user: xudaxian
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:
- name: "username"
value: "admin"
- name: "password"
value: "123456"
kubectl exec -it pod-env -n dev -c busybox -it /bin/sh
echo $username
echo $password
containerPort
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
labels:
user: xudaxian
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.17.1 # 容器需要的镜像地址
imagePullPolicy: IfNotPresent # 设置镜像拉取策略
ports:
- name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
containerPort: 80 # 容器要监听的端口 (0~65536)
protocol: TCP # 端口协议
可以通过pod的ip+containerPort访问容器
resources:对容器资源占用做限制
limits:用于限制运行的容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动
apiVersion: v1
kind: Pod
metadata:
name: pod-resoures
namespace: dev
labels:
user: xudaxian
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" # 内存限制
pod生命周期

- 初始化容器(init container)
- 运行主容器(main container)
容器启动后钩子(post start)、容器终止前钩子(pre stop)
容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
● 在整个生命周期中,Pod会出现5种状态
○ 挂起(Pending):API Server已经创建了Pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中。
○ 运行中(Running):Pod已经被调度到某节点,并且所有容器都已经被kubelet创建完成。
○ 成功(Succeeded):Pod中的所有容器都已经成功终止并且不会被重启。
○ 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态。
○ 未知(Unknown):API Server无法正常获取到Pod对象的状态信息,通常由于网络通信失败所导致。
Pod的创建过程
① 用户通过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中。
初始化容器
初始化容器是在Pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:
① 初始化容器必须运行完成直至结束,如果某个初始化容器运行失败,那么kubernetes需要重启它直至成功完成。
② 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行。
假设要以主容器来运行Nginx,但是要求在运行Nginx之前要能够连接上MySQL和Redis所在的服务器。
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
labels:
user: xudaxian
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 192.168.18.103 -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.18.104 -c 1;do echo waiting for redis ...;sleep 2;done;"]
kubectl create -f pod-initcontainer.yaml
kubectl describe pod pod-initcontainer -n dev
此时pod会卡在运行test-mysql(ping不通)
钩子函数
○ post start:容器创建之后执行,如果失败会重启容器。
○ pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。
钩子处理器支持使用下面的三种方式定义动作
……
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
……
……
lifecycle:
postStart:
tcpSocket:
port: 8080
……
……
lifecycle:
postStart:
httpGet:
path: / #URI地址
port: 80 #端口号
host: 192.168.109.100 #主机地址
scheme: HTTP #支持的协议,http或者https
……
例子:在nginx容器启动后修改主页内容
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
labels:
user: xudaxian
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"
lifecycle: # 生命周期配置
postStart: # 容器创建之后执行,如果失败会重启容器
exec: # 在容器启动的时候,执行一条命令,修改掉Nginx的首页内容
command: ["/bin/sh","-c","echo postStart ... > /usr/share/nginx/html/index.html"]
preStop: # 容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
exec: # 在容器停止之前停止Nginx的服务
command: ["/usr/sbin/nginx","-s","quit"]
kubectl apply -f pod-hook-exec.yml
[root@node1 ~]# kubectl get pod pod-hook-exec -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec 1/1 Running 0 66s 10.244.1.4 node1 <none> <none>
[root@node1 ~]# curl 10.244.1.4
postStart ...
容器探测
容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。kubernetes提供了两种探针来实现容器探测,分别是:
○ liveness probes:存活性探测,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器。
○ readiness probes:就绪性探测,用于检测应用实例是否可以接受请求,如果不能,k8s不会转发流量。
exec方式
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-exec
namespace: dev
labels:
user: xudaxian
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
livenessProbe: # 存活性探针
exec:
command: ["/bin/cat","/tmp/hello.txt"] # 执行查看文件的命令,一定失败,因为没有这个文件
上述命令返回码非0,存活性探测会认为不健康,会一直重启容器(默认重启策略)
tcpSocket方式
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-tcpsocket
namespace: dev
labels:
user: xudaxian
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
livenessProbe: # 存活性探针
tcpSocket:
port: 8080 # 尝试访问8080端口,一定失败,因为Pod内部只有一个Nginx容器,而且只是监听了80端口
httpGet方式
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
labels:
user: xudaxian
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
livenessProbe: # 存活性探针
httpGet: # 其实就是访问http://127.0.0.1:80/hello
port: 80 # 端口号
scheme: HTTP # 支持的协议,HTTP或HTTPS
path: /hello # URI地址
host: 127.0.0.1 # 主机地址
livenessProbe的其他可选项
kubectl explain pod.spec.containers.livenessProbe
exec
tcpSocket
httpGet
initialDelaySeconds # 容器启动后等待多少秒执行第一次探测
timeoutSeconds # 探测超时时间。默认1秒,最小1秒
periodSeconds # 执行探测的频率。默认是10秒,最小1秒
failureThreshold # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold # 连续探测成功多少次才被认定为成功。默认是1
重启策略
在容器探测中,一旦探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由Pod的重启策略决定的,Pod的重启策略有3种,分别如下:
○ Always:容器失效时,自动重启该容器,默认值。
○ OnFailure:容器终止运行且退出码不为0时重启。
○ Never:不论状态如何,都不重启该容器。
例子:指定重启策略为Never
apiVersion: v1
kind: Pod
metadata:
name: pod-restart-policy
namespace: dev
labels:
user: xudaxian
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 # 重启策略


Pod的调度
默认情况下,Pod在哪个Node节点上运行,是由Scheduler组件计算出来的。
在实际使用中,我们想控制某些Pod到达某些节点上,那么应该怎么做?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式。
○ 自动调度:运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。
○ 定向调度:NodeName、NodeSelector。
○ 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity。
○ 污点(容忍)调度:Taints、Toleration。
nodeName(强制调度)
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
labels:
user: xudaxian
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeName: k8s-node1 # 指定调度到k8s-node1节点上
nodeSelector(强制调度)
# 先给node1打标签
kubectl label node k8s-node1 nodeenv=pro
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节点上,即node1上
虽然定向调度的两种方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用的Node列表也不行。
亲和性调度在nodeSelector的基础之上进行了扩展,优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使得调度更加灵活。(保证pod能够运行)
关于亲和性和反亲和性的使用场景的说明:
亲和性:如果两个应用频繁交互,那么就有必要利用亲和性让两个应用尽可能的靠近,这样可以较少因网络通信而带来的性能损耗。
反亲和性:当应用采用多副本部署的时候,那么就有必要利用反亲和性让各个应用实例打散分布在各个Node上,这样可以提高服务的高可用性。
nodeAffinity例子
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
nodeAffinity: # node亲和性配置
requiredDuringSchedulingIgnoredDuringExecution: # Node节点必须满足指定的所有规则才可以,硬规则,类似于定向调度
nodeSelectorTerms: # 节点选择列表
- matchExpressions:
- key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是"xxx"或"yyy"的节点
operator: In #关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
values:
- "xxx"
- "yyy"
此时apply创建pod,再describe查看pod,会观察到pod运行失败,因为没有节点上有nodeenv=xxx或yyy的标签


而使用preferredDuringSchedulingIgnoredDuringExecution时,当没有节点满足条件时,总会选择其中一个节点运行pod(不会因为匹配不到节点导致运行失败运行失败)
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-preferred
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
nodeAffinity: # node亲和性配置
preferredDuringSchedulingIgnoredDuringExecution: # 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
- preference: # 一个节点选择器项,与相应的权重相关联
matchExpressions:
- key: nodeenv
operator: In
values:
- "xxx"
- "yyy"
weight: 1 #倾向权重,在范围1-100。
podAffinity
以运行的Pod为参照,实现让新创建的Pod和参照的Pod在一个区域
先创建一个参照pod,设置标签 podenv=pro
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
podenv: pro # 设置标签
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeName: k8s-node1 # 将目标pod定向调度到k8s-node1
创建目标pod,调度到具有podenv=pro或yyy的pod所在的node上,即node1
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-requred
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
podAffinity: # Pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 该Pod必须和拥有标签podenv=xxx或者podenv=yyy的Pod在同一个Node上,显然没有这样的Pod
- key: podenv
operator: In
values:
- "pro"
- "yyy"
topologyKey: kubernetes.io/hostname #以Node节点为区分范围
podAntiAffinity和podAffinity作用相反,让新创建的Pod和参照的Pod不在一个区域,此时参照pod在node1,按照以下配置,目标pod会被调度到node2
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-requred
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
podAntiAffinity: # Pod反亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions:
- key: podenv
operator: In
values:
- "pro"
topologyKey: kubernetes.io/hostname
污点
前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加
污点属性,拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。污点的格式为:
key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:PreferNoSchedule:尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可以调度。NoSchedule:不会把Pod调度到具有该污点的Node上,但是不会影响当前Node上已经存在的Pod。NoExecute:不会把Pod调度到具有该污点的Node上,同时也会将Node上已经存在的Pod驱逐。
例子
设置污点,尽量不要调度过来
kubectl taint node k8s-node1 tag=xudaxian:PreferNoSchedule
此时创建pod1,如果node2可用,将会调度到node2,否则只能调度到node1
kubectl run pod1 --image=nginx:1.17.1 -n dev
为k8s-node1取消污点(PreferNoSchedule),并设置污点(NoSchedule)
kubectl taint node k8s-node1 tag:PreferNoSchedule-
kubectl taint node k8s-node1 tag=xudaxian:NoSchedule
此时创建pod2,如果node2可用,将会调度到node2,否则运行失败,pod2将一直处于pending状态
kubectl run pod2 --image=nginx:1.17.1 -n dev
为k8s-node1取消污点(NoSchedule),并设置污点(NoExecute)
kubectl taint node k8s-node1 tag:NoSchedule-
kubectl taint node k8s-node1 tag=xudaxian:NoExecute
此时创建pod3,如果node2可用,将会调度到node2,否则运行失败,pod3将一直处于pending状态,并且原node1上的pod会被删除
kubectl run pod3 --image=nginx:1.17.1 -n dev
使用kubeadm搭建的集群,默认就会给Master节点添加一个污点标记,所以Pod默认不会调度到Master节点上。
在Node上添加污点用来拒绝Pod调度,但是如果就是想让一个Pod调度到一个有污点的Node上去,这时就需要使用到容忍。
污点就是拒绝,容忍就是忽略,Node通过污点拒绝Pod调度上去,Pod通过容忍忽略拒绝。
在上面的污点中,已经给k8s-node1打上了NoExecute的污点,Pod是调度不上去的,此时可以通过在Pod中添加容忍,将Pod调度上去。
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
tolerations: # 容忍
- key: "tag" # 要容忍的污点的key
operator: Equal # 操作符
value: "xudaxian" # 要容忍的污点的value
effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
Pod控制器的介绍
Pod的创建方式有两种:
○ 自主式Pod:即直接创建Pod,这种Pod删除后就没有了。
○ 控制器创建Pod:通过Pod控制器创建的Pod,这种Pod删除之后还会自动重建。
Pod控制器是管理Pod的中间层,它就会创建出满足条件的Pod并确保每一个Pod处于期望的状态。如果Pod在运行中出现故障,控制器会基于指定的策略重启或重建Pod。
常见的pod控制器有下面这些:
○ ReplicationController:比较原始的Pod控制器,已经被废弃,由ReplicaSet替代。
○ ReplicaSet:保证指定数量的Pod运行。
○ Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级、版本回退。
○ Horizontal Pod Autoscaler:可以根据集群负载自动调整Pod的数量,实现削峰填谷。
○DaemonSet:在集群中的指定Node上都运行一个副本,一般用于守护进程类的任务。
○ Job:它创建出来的Pod只要完成任务就立即退出,用于执行一次性任务。
○ CronJob:它创建的Pod会周期性的执行,用于执行周期性的任务。
○ StatefulSet:管理有状态的应用。
replicaset
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
为了更好的解决服务编排的问题,kubernetes在v1.2版本开始,引入了Deployment控制器。值得一提的是,Deployment控制器并不直接管理Pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment的功能比ReplicaSet强大。
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
使用scale扩容或缩容,或直接修改yml中的replicas,再重新apply
kubectl scale deploy pc-deployment --replicas=5 -n dev
strategy指定镜像的更新策略,可选Recreate和rollingUpdate,默认rollingUpdate
Recreate:在创建出新的Pod之前会先杀掉所有已经存在的Pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod
strategy:
type: RollingUpdate
rollingUpdate: #当type为RollingUpdate的时候生效,用于为rollingUpdate设置参数,支持两个属性:
maxUnavailable: 25% #用来指定在升级过程中不可用的Pod的最大数量,默认为25%。
maxSurge: 25% #用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。
镜像升级
kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev
或修改yml中的iamge,重新apply
版本回退
kubetl rollout 参数 deploy xx # 支持下面的选择
# status 显示当前升级的状态
# history 显示升级历史记录
# pause 暂停版本升级过程
# resume 继续已经暂停的版本升级过程
# restart 重启版本升级过程
# undo 回滚到上一级版本 (可以使用--to-revision回滚到指定的版本)
关于滚动更新和版本回退:https://www.jianshu.com/p/0043c2508ef2
Horizontal Pod Autoscaler(HPA)
手动执行kubectl scale命令实现Pod的扩缩容不符合kubernetes的定位目标–自动化和智能化。kubernetes可以通过监测Pod的使用情况,实现Pod数量的自动调整,于是就产生了HPA这种控制器。
HPA可以获取每个Pod的利用率,然后和HPA中定义的指标进行对比,计算出需要伸缩的具体值,实现Pod的数量的调整。
安装metrics-server(监控集群中的资源使用情况)
wget https://github.com/kubernetes-sigs/metrics-server/archive/v0.3.6.tar.gz
tar -zxvf v0.3.6.tar.gz
cd metrics-server-0.3.6/deploy/1.8+/
vim metrics-server-deployment.yaml

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: metrics-server
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: metrics-server
namespace: kube-system
labels:
k8s-app: metrics-server
spec:
selector:
matchLabels:
k8s-app: metrics-server
template:
metadata:
name: metrics-server
labels:
k8s-app: metrics-server
spec:
hostNetwork: true
serviceAccountName: metrics-server
volumes:
# mount in tmp so we can safely use from-scratch images and/or read-only containers
- name: tmp-dir
emptyDir: {}
containers:
- name: metrics-server
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
imagePullPolicy: Always
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
volumeMounts:
- name: tmp-dir
mountPath: /tmp
#报错时执行
kubectl delete -f ./
sed -i 's#rbac.authorization.k8s.io/v1beta1#rbac.authorization.k8s.io/v1#' auth-reader.yaml
sed -i 's#rbac.authorization.k8s.io/v1beta1#rbac.authorization.k8s.io/v1#' auth-delegator.yaml
sed -i 's#apiregistration.k8s.io/v1beta1#apiregistration.k8s.io/v1#' metrics-apiservice.yaml
kubectl apply -f ./

查看资源使用情况

创建deployment
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
创建service
kubectl expose deployment nginx --name=nginx --type=NodePort --port=80 --target-port=80 -n dev
部署HPA,关联deployment,实现自动扩缩容
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
DaemonSet
每向集群中添加一个节点,指定的Pod副本也将添加到该节点上。
当节点从集群中移除的时候,Pod也会被回收。
apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型
metadata: # 元数据
name: # 名称
namespace: #命名空间
labels: #标签
controller: daemonset
spec: # 详情描述
revisionHistoryLimit: 3 # 保留历史版本
updateStrategy: # 更新策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxUnavailable: 1 # 最大不可用状态的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
Job
Job会记录成功结束的Pod数量。
当成功结束的Pod达到指定的数量时,Job将完成执行。
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
name: # 名称
namespace: #命名空间
labels: # 标签
controller: job
spec: # 详情描述
completions: 1 # 指定Job需要成功运行Pod的总次数,默认为1
parallelism: 1 # 指定Job在任一时刻应该并发运行Pod的数量,默认为1
activeDeadlineSeconds: 30 # 指定Job可以运行的时间期限,超过时间还没结束,系统将会尝试进行终止
backoffLimit: 6 # 指定Job失败后进行重试的次数,默认为6
manualSelector: true # 是否可以使用selector选择器选择Pod,默认为false
selector: # 选择器,通过它指定该控制器管理那些Pod
matchLabels: # Labels匹配规则
app: counter-pod
matchExpressions: # Expressions匹配规则
- key: app
operator: In
values:
- counter-pod
template: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never # 重启策略只能设置为Never或OnFailure
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done"]
CronJob控制器借助Job控制器管理Pod,可以在特定的时间点反复去执行Job任务。
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据
name: pc-cronjob # 名称
namespace: dev #命名空间
spec: # 详情描述
schedule: "*/1 * * * * " # cron格式的作业调度运行时间点,用于控制任务任务时间执行
jobTemplate: # job控制器模板,用于为cronjob控制器生成job对象,下面其实就是job的定义
metadata: {}
spec:
template: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板
spec:
restartPolicy: Never # 重启策略只能设置为Never或OnFailure
containers:
- name: counter
image: busybox:1.30
command: [ "/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 2;done" ]
StatefulSet创建的pod有唯一标识
apiVersion: v1
kind: Service
metadata:
name: service-headliness
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None # 将clusterIP设置为None,即可创建headliness Service,StatefulSet需要用到
type: ClusterIP
ports:
- port: 80 # Service的端口
targetPort: 80 # Pod的端口
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pc-statefulset
namespace: dev
spec:
replicas: 3
serviceName: service-headliness
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
