Kubernetes-进阶(Pod生命周期/调度/控制器,Ingress代理,数据存储PV/PVC)

Kubernetes-进阶

Pod详解

每个Pod中都可以包含一个或多个容器,这些容器可以分两类

  • 用户程序所在容器,数量用户决定
  • Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个
    1. 可以以它为依据,评估整个Pod的健康状态
    2. 可以在根容器上设置Ip地址,其它容器都可以通过此IP(Pod IP),就行Pod内部的网络通信Pod与Pod之间的网络通信是通过虚拟二层网络技术实现的,我们当前的环境用的是Flannel

在这里插入图片描述

Pod定义

以下是Pod的yaml中可配置的信息

使用命令kubectl explain pod可以看到pod的可配置的属性
使用命令kubectl explain pod.metadata可以看到pod.metadata的可配置的属性

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,默认TCPenv:   #容器运行前需设置的环境变量列表- 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、portpath: stringport: numberhost: stringscheme: stringHttpHeaders:- name: stringvalue: stringtcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式port: numberinitialDelaySeconds: 0       #容器启动完成后首次探测的时间,单位为秒timeoutSeconds: 0          #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒periodSeconds: 0           #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次successThreshold: 0failureThreshold: 0securityContext:privileged: falserestartPolicy: [Always | Never | OnFailure]  #Pod的重启策略nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定- name: stringhostNetwork: 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: stringpath: stringconfigMap:         #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部name: stringitems:- key: stringpath: string

Pod配置

Pod一级属性

属性名说明
apiVersion 版本,由kubernetes内部定义,版本号必须可以用 kubectl api-versions 查询到
kind 类型,由kubernetes内部定义,版本号必须可以用 kubectl api-resources 查询到
metadata 元数据,主要是资源标识和说明,常用的有name、namespace、labels等
spec 描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述
status 状态信息,里面的内容不需要定义,由kubernetes自动生成

spec属性(重点)

属性名说明
containers <[]Object>容器列表,用于定义容器的详细信息
nodeName 根据nodeName的值将pod调度到指定的Node节点上
nodeSelector <map[]>根据NodeSelector中定义的信息选择将该Pod调度到包含这些label的Node 上
hostNetwork 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes <[]Object>存储卷,用于定义Pod上面挂在的存储信息
restartPolicy 重启策略,表示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>      # 资源限制和资源请求的设置

案例

创建pod-base.yaml文件,内容如下:

使用kubectl apply/delete启动(更新)/删除 Pod

apiVersion: v1
kind: Pod
metadata:name: pod-basenamespace: devlabels:user: test
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30

上面定义了一个比较简单Pod的配置,里面有两个容器:

  • nginx:用1.17.1版本的nginx镜像创建,(nginx是一个轻量级web容器)
  • busybox:用1.30版本的busybox镜像创建,(busybox是一个小巧的linux命令集合)
镜像拉取

创建pod-imagepullpolicy.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: Never # 用于设置镜像拉取策略- name: busyboximage: busybox:1.30

imagePullPolicy,用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:

  • Always:总是从远程仓库拉取镜像(一直远程下载)
  • IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就本地 本地没远程下载)
  • Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错 (一直使用本地)

默认值说明:

如果镜像tag为具体版本号, 默认策略是:IfNotPresent

如果镜像tag为:latest(最终版本) ,默认策略是always

启动命令

在说启动命令之前,先说说在前面执行的2个脚本可以冲READY中看到只启动了一个,通过使用kubectl describe pod <pod命令> -n dev命令可以看到busybox启动不了,这是因为busybox启动后没有进程在运行所以启动后就自动关闭了,那么如何让容器启动后不会自动关闭呢,写过Dockerfile的小伙伴应该就清楚只需要在后面执行一个CMD命令即可

在这里插入图片描述

创建pod-command.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-commandnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"] #执行cmd命令使得容器有进程在运行

command,用于在pod中的容器初始化完毕之后运行一个命令。

“/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中的busybox容器,查看文件内容
# 补充一个命令: kubectl exec  pod名称 -n 命名空间 -it -c 容器名称 /bin/sh  在容器内部执行命令
# 使用这个命令就可以进入某个容器的内部,然后进行相关操作了
# 比如,可以查看txt文件的内容
[root@k8s-master01 pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
#进入容器后执行
tail -f /tmp/hello.txt

特别说明:
通过上面发现command已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个args选项,用于传递参数呢?这其实跟docker有点关系,kubernetes中的command、args两项其实是实现覆盖Dockerfile中ENTRYPOINT的功能。
1 如果command和args均没有写,那么用Dockerfile的配置。
2 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command
3 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数
4 如果command和args都写了,那么Dockerfile的配置被忽略,执行command并追加上args参数

环境变量

创建pod-env.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-envnamespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]env: # 设置环境变量列表- name: "username"value: "admin"- name: "password"value: "123456"

进入容器测试

# 进入容器,输出环境变量
kubectl exec pod-env -n dev -c busybox -it /bin/sh
#进入容器后执行
echo $username
echo $password
端口设置

使用kubectl explain pod.spec.containers.ports命令查看可配置项

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-portsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: # 设置容器暴露的端口列表- name: nginx-portcontainerPort: 80protocol: TCP

访问容器中的程序需要使用的是Podip:containerPort

资源配额

容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes提供了对内存和cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:

  • limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启
  • requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动

创建pod-resources.yaml,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-resourcesnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1resources: # 资源配额limits:  # 限制资源(上限)cpu: "2" # CPU限制,单位是core数memory: "10Gi" # 内存限制requests: # 请求资源(下限)cpu: "1"  # CPU限制,单位是core数memory: "10Mi"  # 内存限制

Pod生命周期

现在以一般的Pod对象从创建至终的这段实际范围称为pod的生命周期,它主要包含以下的过程

  1. pod创建过程
  2. 运行初始化容器(init container)过程
  3. 运行主容器(main container)过程
    • 容器启动后钩子(post start)、容器终止前钩子(pre stop)
    • 容器的存活性探测(livenes porbe)、就绪性探测(readiness probe
  4. pod终止

在这里插入图片描述

在整个的生命周期中pod会出现5种状态

状态说明
挂起(Pending)apiService已经创建了pod资源对象,但它尚未被调度卧槽或者仍处于下载镜像的过程中
运行种(Running)pod已经被调度至某个节点,并且所有容器都已经被kubelet创建完成
成功(Succeeded)pod中的所有容器都已经成功终止并且不会被重启
失败(Failed)所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
未知(Unknown)apiserver无法正常获取到pod对象的状态信息,通常由网络通讯失败所导致
创建过程
  1. 用户通过kubectl或者其它api客户端提交需要创建的pod信息给apiServer
  2. apiServer开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息到客户端
  3. apiServer开始反映etcd中的pod对象变化,其它组件使用watch机制来跟踪检查apiServer上变动
  4. cheduler发现有新的pod对象要创建,开始为Pod分配主机并将结果更新至apiServer
  5. node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果送会至apiServer
  6. apiServer将接收到的pod状态信息存入etcd中

在这里插入图片描述

终止过程
  1. 用户向apiServer发生删除pod对象命令
  2. apiServer中的pod对象信息会随时间的推移而更新,宽限期内(默认30s),pod被视为dead
  3. 将pod标记为terminating状态
  4. kubelet在监控到pod对象转为terminating状态的同时启动pod关闭过程
  5. 端点控制器监控到pod对象的关闭行为是将其所有匹配到此端点的service资源从端点列表中移除
  6. 如果当前pod对象定义了preStop钩子处理器,则在标记为terminating后即同步的方式启动执行
  7. pod对象中的容器进程收到停止信号
  8. 宽限期结束后,若pod中还存在运行的进程,那么pod对象会收到立刻终止的信号
  9. kubelet请求apiserver将此pod资源宽限期设置为0从而完成删除操作,此时pod对应用户已经不可见
初始化容器

初始化容器是在pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它有2大特性

  1. 初始化容器必须完成直到结束,若某个初始化容器运行失败,那么kubernetes需要重启它直到成功完成
  2. 初始化容器必须按照定义的顺序执行,当且仅当前一个成功后,后面的一个才能运行

初始化容器有很多应用场景,下面是常见的场景

  1. 提供主容器镜像中不具备的工具程序或自定义代码
  2. 初始化容器要先于运用容器串行启动并运行完成,因此可以作为后续容器启动的依赖条件是否满足判断

案例

以下设计一个案例,假设主容器运行nginx,但是在nginx运行之前需要确保能够先连接上mysql和redis服务器

假如:mysql 192.168.10.101 redis 192.168.10.102

创建pod-initcontainer.yaml,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-initcontainernamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80initContainers:- name: test-mysqlimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.10.101 -c 1 ; do echo waiting for mysql...; sleep 2; done;']- name: test-redisimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.10.102 -c 1 ; do echo waiting for reids...; sleep 2; done;']

执行命令测试

# 创建pod
kubectl create -f pod-initcontainer.yaml
# 查看pod状态
# 发现pod卡在启动第一个初始化容器过程中,后面的容器不会运行
kubectl describe pod  pod-initcontainer -n dev
# 动态查看pod
kubectl get pods pod-initcontainer -n dev -w
# 接下来新开一个shell,为当前服务器新增两个ip,观察pod的变化
[root@k8s-master01 ~]# ifconfig ens33:1 192.168.5.14 netmask 255.255.255.0 up
[root@k8s-master01 ~]# ifconfig ens33:2 192.168.5.15 netmask 255.255.255.0 up

在这里插入图片描述

钩子函数

Kubernetes在主容器的启动之后和停止之前提供两个钩子函数

  • post start:容器创建之后执行,如果失败了会重启容器
  • pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作

钩子处理支持以下3种方式

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.5.3 #主机地址scheme: HTTP #支持的协议,http或者https

案例

创建pod-hook-exec.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-hook-execnamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80lifecycle:postStart: exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]preStop:exec: # 在容器停止之前停止nginx服务command: ["/usr/sbin/nginx","-s","quit"]

执行命令测试

# 创建pod
kubectl create -f pod-hook-exec.yaml
# 查看pod
kubectl get pods  pod-hook-exec -n dev -o wide  
# 访问
curl 10.244.22.48
容器探测

容器探测用于检测容器中的应用是否正常工作,是保障业务可用性的一种传统机制,如果结果探测实例的状态不符合预期,那么kubernetes就会把该问题实例"摘除",不承担业务流量kubernetes提供了两种探针来实现容器探测

  1. liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器

  2. readiness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,k8s不会转发流量

livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器

探针3种方式

#查看探测可配置参数
kubectl explain pod.spec.containers.livenessProbe
FIELDS:exec <Object>  tcpSocket    <Object>httpGet      <Object>initialDelaySeconds  <integer>  # 容器启动后等待多少秒执行第一次探测timeoutSeconds       <integer>  # 探测超时时间。默认1秒,最小1秒periodSeconds        <integer>  # 执行探测的频率。默认是10秒,最小1秒failureThreshold     <integer>  # 连续探测失败多少次才被认定为失败。默认是3。最小值是1successThreshold     <integer>  # 连续探测成功多少次才被认定为成功。默认是1
Exec命令

在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常

  livenessProbe:postStart: exec:command:- cat- /tmp/healthy
TCPSocket

将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常

  livenessProbe:postStart:tcpSocket:port: 8080
HTTPGet

调用容器内Web应用的URL,如果返回的状态码在200和399之间,则认为程序正常,否则不正常

  livenessProbe:postStart:httpGet:path: / #URI地址port: 80 #端口号host: 192.168.5.3 #主机地址scheme: HTTP #支持的协议,http或者https

案例

创建pod-liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-liveness-execnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令

执行命令测试

# 创建Pod
kubectl create -f pod-liveness-exec.yaml
# 查看Pod详情
kubectl describe pods pod-liveness-exec -n dev
# 观察上面的信息就会发现nginx容器启动之后就进行了健康检查
# 检查失败之后,容器被kill掉,然后尝试进行重启(这是重启策略的作用,后面讲解)
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
kubectl get pods pod-liveness-exec -n dev

由于nginx不存在hello.txt文件可以看到pod在不断重启

在这里插入图片描述

重启策略

在上一节中,一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由pod的重启策略决定的,pod的重启策略有 3 种,分别如下:

  • Always :容器失效时,自动重启该容器,这也是默认值。
  • OnFailure : 容器终止运行且退出码不为0时重启
  • Never : 不论状态为何,都不重启该容器

重启策略适用于pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大延迟时长。

创建pod-restartpolicy.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-restartpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /hellorestartPolicy: Never # 设置重启策略为Never
# 创建Pod
kubectl create -f pod-restartpolicy.yaml
# 查看Pod详情,发现nginx容器失败
kubectl  describe pods pod-restartpolicy  -n dev
# 多等一会,再观察pod的重启次数,发现一直是0,并未重启   
kubectl  get pods pod-restartpolicy -n dev

Pod调度

默认情况下一个Pod在那个Node节点上运行是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的但是在实际使用中这并不满足需求,因为很多情况下我们想控制Pod部署在某个节点上,这就需要了解和使用Pod的调度规则,Kubernetes提供四大类的调度方式

  1. 自动调度(默认):运行在哪个节点上完全由Scheduler经过计算得出
  2. 定向调度:NodeName、NodeSelector
  3. 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
  4. 污点(容忍)调度:Taints、Toleration
定向调度

定向调度是一种强制性调度,指的是利用在pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上

注意,由于调度是强制性的,意味着即使要调度的目标Node不存在,也会进行调度,只不过pod运行失败

nodeName

NodeName用于强制约束将Pod调度到指定的Name的Node节点上,这种方式直接通过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点

创建一个pod-nodename.yaml文件

apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1 # 指定调度到node1节点上

执行命令测试

#创建Pod
kubectl create -f pod-nodename.yaml
#查看Pod调度到NODE属性,确实是调度到了node1节点上
kubectl get pods pod-nodename -n dev -o wide   
nodeSelector

NodeSelector用于强制约束将Pod调度到指定标签的node节点上,它是通过kubernetes的label-selector机制实现的,在pod创建之前会由scheduler使用MatchNodeSelector调度策略进行label匹配,找到目标node然后将pod调度到目标node节点上

首先需要为node节点添加标签

kubectl label nodes node1 nodeenv=pro
kubectl label nodes node2 nodeenv=test

创建一个pod-nodeselector.yaml文件

apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeSelector: nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上

执行命令测试

#创建Pod
kubectl create -f pod-nodeselector.yaml
#查看Pod调度到NODE属性,确实是调度到了node1节点上
kubectl get pods pod-nodeselector -n dev -o wide
亲和性调度

在上面的定向调度使用过程中发现存在一个缺点,假如调度的目标不存在那么pod就不会启动成功,基于这个问题Kubernetes还提供了亲和性调度(Affinity),它在NodeSelector的基础之上进行了扩展,可以通过配置的形式实现优先满足条件的Node进行调度,如果没有也可以调度到不满足条件的Node上,使得调度更加灵活

Affinit主要分3类

  1. nodeAffinity(node亲和性):以node为目标,解决pod可以调度到那些node的问题
  2. podAffinity(pod亲和性):以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
  3. podAntiAffinity(pod反亲和性):以pod为目标,解决pod不能和那些已存在pod部署在同一个拓扑域种的问题

关于亲和性(反亲和性)使用场景的说明:

亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗

反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性

nodeAffinity

首先来看一下NodeAffinity的可配置项

pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringExecution  Node节点必须满足指定的所有规则才可以,相当于硬限制nodeSelectorTerms  节点选择列表matchFields   按节点字段列出的节点选择器要求列表matchExpressions   按节点标签列出的节点选择器要求列表(推荐)key    键values 值operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, LtpreferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)preference   一个节点选择器项,与相应的权重相关联matchFields   按节点字段列出的节点选择器要求列表matchExpressions   按节点标签列出的节点选择器要求列表(推荐)key    键values 值operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Ltweight 倾向权重,在范围1-100。
关系符的使用说明:- matchExpressions:- key: nodeenv              # 匹配存在标签的key为nodeenv的节点operator: Exists- key: nodeenv              # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点operator: Invalues: ["xxx","yyy"]- key: nodeenv              # 匹配标签的key为nodeenv,且value大于"xxx"的节点operator: Gtvalues: "xxx"
硬限制

使用requiredDuringSchedulingIgnoredDuringExecution

创建pod-nodeaffinity-required.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置nodeAffinity: #设置node亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制nodeSelectorTerms:- matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签- key: nodeenvoperator: Invalues: ["xxx","yyy"]

执行命令测试

# 创建pod
kubectl create -f pod-nodeaffinity-required.yaml
# 查看pod状态 (运行失败)
kubectl get pods pod-nodeaffinity-required -n dev -o wide
# 查看Pod的详情
# 发现调度失败,提示node选择失败
kubectl describe pod pod-nodeaffinity-required -n dev#接下来,停止pod
kubectl delete -f pod-nodeaffinity-required.yaml
# 修改文件,将values: ["xxx","yyy"]------> ["pro","yyy"]
vim pod-nodeaffinity-required.yaml
# 再次启动
kubectl create -f pod-nodeaffinity-required.yaml
# 此时查看,发现调度成功,已经将pod调度到了node1上
kubectl get pods pod-nodeaffinity-required -n dev -o wide
软限制

使用preferredDuringSchedulingIgnoredDuringExecution

创建pod-nodeaffinity-preferred.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-preferrednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置nodeAffinity: #设置node亲和性preferredDuringSchedulingIgnoredDuringExecution: # 软限制- weight: 1preference:matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签(当前环境没有)- key: nodeenvoperator: Invalues: ["xxx","yyy"]

执行命令测试

# 创建pod
kubectl create -f pod-nodeaffinity-preferred.yaml
# 查看pod状态 (运行成功)
kubectl get pod pod-nodeaffinity-preferred -n dev

NodeAffinity规则设置的注意事项:
1 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上
2 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
3 如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的才能匹配成功
4 如果一个pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略此变化

podAffinity

首先来看一下podAffinity的可配置项

pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringExecution  硬限制namespaces       指定参照pod的namespacetopologyKey      指定调度作用域labelSelector    标签选择器matchExpressions  按节点标签列出的节点选择器要求列表(推荐)key    键values 值operator 关系符 支持In, NotIn, Exists, DoesNotExist.matchLabels    指多个matchExpressions映射的内容preferredDuringSchedulingIgnoredDuringExecution 软限制podAffinityTerm  选项namespaces      topologyKeylabelSelectormatchExpressions  key    键values 值operatormatchLabels weight 倾向权重,在范围1-100

topologyKey用于指定调度时作用域,例如:
如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围
如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分

硬限制

首先创建一个参照Pod,pod-podaffinity-target.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-targetnamespace: devlabels:podenv: pro #设置标签
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1 # 将目标pod名确指定到node1上

执行命令启动

# 启动目标pod
kubectl create -f pod-podaffinity-target.yaml
# 查看pod状况
kubectl get pods  pod-podaffinity-target -n dev

创建pod-podaffinity-required.yaml

新Pod必须要与拥有标签nodeenv=xxx或者nodeenv=yyy的pod在同一Node上,显然现在没有这样pod

apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置podAffinity: #设置pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签- key: podenvoperator: Invalues: ["xxx","yyy"]topologyKey: kubernetes.io/hostname

执行命令测试

# 启动pod
kubectl create -f pod-podaffinity-required.yaml
# 查看pod状态,发现未运行
kubectl get pods pod-podaffinity-required -n dev
# 查看详细信息
kubectl describe pods pod-podaffinity-required  -n dev# 接下来修改  values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必须要与拥有标签nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
vim pod-podaffinity-required.yaml
# 然后重新创建pod,查看效果
kubectl delete -f  pod-podaffinity-required.yaml
kubectl create -f pod-podaffinity-required.yaml
# 发现此时Pod运行正常
kubectl get pods pod-podaffinity-required -n dev
软限制

软限制就不进行演示了,在使用方法大同小异

podAntiAffinity

PodAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod跟参照pod不在一个区域中的功能,它的配置方式和选项跟PodAffinty是一样的,这里不再做详细解释,直接做一个测试案例

硬限制

继续使用上个案例中目标pod

#可以看到pod-podaffinity-target有一个podenv标签,并且当前在node1节点上
kubectl get pods -n dev -o wide --show-labels

创建pod-podantiaffinity-required.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置podAntiAffinity: #设置pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配podenv的值在["pro"]中的标签- key: podenvoperator: Invalues: ["pro"]topologyKey: kubernetes.io/hostname

执行命令测试

# 创建pod
kubectl create -f pod-podantiaffinity-required.yaml
# 查看pod
# 发现调度到了node2上
kubectl get pods pod-podantiaffinity-required -n dev -o wide
软限制

软限制就不进行演示了,在使用方法大同小异

污点(容忍)调度
污点(Taints)

前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否允许Pod调度过来。

Node被设置上污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。

污点的格式为:key=value:effect, key和value是污点的标签,effect描述污点的作用,支持如下三个选项:

  • PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可调度
  • NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但不会影响当前Node上已存在的Pod
  • NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离

使用kubectl设置和去除污点的命令示例如下:

# 设置污点
kubectl taint nodes node1 key=value:effect
# 去除污点
kubectl taint nodes node1 key:effect-
# 去除所有污点
kubectl taint nodes node1 key-

使用kubeadm搭建的集群,默认就会给master节点添加一个污点标记,所以pod就不会调度到master节点上.

容忍(Toleration)

上面介绍了污点的作用,我们可以在node上添加污点用于拒绝pod调度上来,但是如果就是想将一个pod调度到一个有污点的node上去,这时候应该怎么做呢?这就要使用到容忍

下面看一下容忍的详细配置

FIELDS:key       # 对应着要容忍的污点的键,空意味着匹配所有的键value     # 对应着要容忍的污点的值operator  # key-value的运算符,支持Equal和Exists(默认)effect    # 对应污点的effect,空意味着匹配所有影响tolerationSeconds   # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间

容忍某个污点yaml配置如下

apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1tolerations:      # 添加容忍- key: "tag"        # 要容忍的污点的keyoperator: "Equal" # 操作符value: "test"    # 容忍的污点的valueeffect: "NoExecute"   # 添加容忍的规则,这里必须和标记的污点规则相同

Pod控制器

Pod控制器是管理pod的中间层,使用了pod控制器之后,我们只需要告诉pod控制器想要多少个什么样的pod就可以了,塌就会创建出满足条件的pod并确保每一个pod处于用户期望的状态,如果pod在运行中出现故障,控制器会基于策略重建pod

在Kubernetes中,pod控制器的创建方式可发两类

  1. 自主式pod:Kubernetes直接创建出来的pod,这种pod删除后就没有了,也不会重建
  2. 控制器创建pod:通过控制器创建的pod,这种pod删除了之后还会自动重建

在Kubernets种,有很多类型的pod控制器,每种都有自己合适的场景,常见有如下

控制器说明
ReplicationController比较原始的pod控制器,已经被废弃,由ReplicaSet替代
ReplicaSet保证副本数量一直维持在期望值,并支持pod数量扩缩容,镜像版本升级
Deployment通过控制ReplicaSet来控制Pod,并支持滚动升级、回退版本
Horizontal Pod Autoscaler可以根据集群负载自动水平调整Pod的数量,实现削峰填谷
DaemonSet在集群中的指定Node上运行且仅运行一个副本,一般用于守护进程类的任务
Job它创建出来的pod只要完成任务就立即退出,不需要重启或重建,用于执行一次性任务
Cronjob它创建的Pod负责周期性任务控制,不需要持续后台运行
StatefulSet管理有状态应用
ReplicaSet(RS)

ReplicaSet的主要作用是保证一定数量的pod正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对pod数量的扩缩容和镜像版本的升降级

yaml配置模板

apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型       
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: #标签controller: rs
spec: # 详情描述replicas: 3 # 副本数量selector: # 选择器,通过它指定该控制器管理哪些podmatchLabels:      # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80

在这里面,需要新了解的配置项就是spec下面几个选项:

  • replicas:指定副本数量,其实就是当前rs创建出来的pod的数量,默认为1

  • selector:选择器,它的作用是建立pod控制器和pod之间的关联关系,采用的Label Selector机制

    在pod模板上定义label,在控制器上定义选择器,就可以表明当前控制器能管理哪些pod了

  • template:模板,就是当前控制器创建pod所使用的模板板,里面其实就是前一章学过的pod的定义

创建

创建pc-replicaset.yaml文件

apiVersion: apps/v1
kind: ReplicaSet   
metadata:name: pc-replicasetnamespace: dev
spec:replicas: 3selector: matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1
# 创建rs
kubectl create -f pc-replicaset.yaml
# 查看rs
# DESIRED:期望副本数量  
# CURRENT:当前副本数量  
# READY:已经准备好提供服务的副本数量
kubectl get rs pc-replicaset -n dev -o wide
# 查看当前控制器创建出来的pod
# 这里发现控制器创建出来的pod的名称是在控制器名称后面拼接了-xxxxx随机码
kubectl get pod -n dev
扩缩容
# 方式一:
# 编辑rs的副本数量,修改spec:replicas: 6即可
kubectl edit rs pc-replicaset -n dev
# 查看pod
kubectl get pods -n dev# 方式二:
# 当然也可以直接使用命令实现
# 使用scale命令实现扩缩容, 后面--replicas=n直接指定目标数量即可
kubectl scale rs pc-replicaset --replicas=2 -n dev
# 命令运行完毕,立即查看,发现已经有4个开始准备退出了
# 稍等片刻,就只剩下2个了
kubectl get pods -n dev
镜像升级
# 方式一:
# 编辑rs的容器镜像 - image: nginx:1.17.2
kubectl edit rs pc-replicaset -n dev
# 再次查看,发现镜像版本已经变更了
kubectl get rs -n dev -o wide# 方式二:
# 同样的道理,也可以使用命令完成这个工作
# kubectl set image rs rs名称 容器=镜像版本 -n namespace
kubectl set image rs pc-replicaset nginx=nginx:1.17.1  -n dev
# 再次查看,发现镜像版本已经变更了
kubectl get rs -n dev -o wide
删除
#方式一:
# 使用kubectl delete命令会删除此RS以及它管理的Pod
# 在kubernetes删除RS前,会将RS的replicasclear调整为0,等待所有的Pod被删除后,在执行RS对象的删除
kubectl delete rs pc-replicaset -n dev
kubectl get pod -n dev -o wide#方式二:
# 如果希望仅仅删除RS对象(保留Pod),可以使用kubectl delete命令时添加--cascade=false选项(不推荐)。
kubectl delete rs pc-replicaset -n dev --cascade=false
kubectl get pods -n dev#方式三:
# 也可以使用yaml直接删除(推荐)
kubectl delete -f pc-replicaset.yaml
Deployment(Deploy)

为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理pod,而是通过管理ReplicaSet来简介管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大。

Deployment主要功能有下面几个:

  • 支持ReplicaSet的所有功能
  • 支持发布的停止、继续
  • 支持滚动更新和回滚版本

yaml配置模板

apiVersion: apps/v1 # 版本号
kind: Deployment # 类型       
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: #标签controller: deploy
spec: # 详情描述replicas: 3 # 副本数量revisionHistoryLimit: 3 # 保留历史版本paused: false # 暂停部署,默认是falseprogressDeadlineSeconds: 600 # 部署超时时间(s),默认是600strategy: # 策略type: RollingUpdate # 滚动更新策略rollingUpdate: # 滚动更新maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: # 选择器,通过它指定该控制器管理哪些podmatchLabels:      # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
创建

创建pc-deployment.yaml文件

apiVersion: apps/v1
kind: Deployment      
metadata:name: pc-deploymentnamespace: dev
spec: replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1

执行命令测试

# 创建deployment
kubectl create -f pc-deployment.yaml --record=true
# 查看deployment
# UP-TO-DATE 最新版本的pod的数量
# AVAILABLE  当前可用的pod的数量
kubectl get deploy pc-deployment -n dev
# 查看rs
# 发现rs的名称是在原来deployment的名字后面添加了一个10位数的随机串
kubectl get rs -n dev
# 查看pod
kubectl get pods -n dev
扩缩容
#方式一:
# 变更副本数量为5个
kubectl scale deploy pc-deployment --replicas=5  -n dev
# 查看deployment
kubectl get deploy pc-deployment -n dev
# 查看pod
kubectl get pods -n dev#方式二:
# 编辑deployment的副本数量,修改spec:replicas: 4即可
kubectl edit deploy pc-deployment -n dev
# 查看pod
kubectl get pods -n dev
镜像更新

deployment支持两种更新策略:重建更新滚动更新,可以通过strategy指定策略类型,支持两个属性:

strategy:指定新的Pod替换旧的Pod的策略, 支持两个属性:type:指定策略类型,支持两种策略Recreate:在创建出新的Pod之前会先杀掉所有已存在的PodRollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本PodrollingUpdate:当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性:maxUnavailable:用来指定在升级过程中不可用Pod的最大数量,默认为25%。maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。

重建更新

在更新式,重建更新策略会将原来运行的pod全部删除,然后重新创建新的pod

编辑pc-deployment.yaml

spec:strategy: # 策略type: Recreate # 重建更新

执行命令测试

# 更新配置
kubectl apply -f pc-deployment.yaml
# 观察升级过程(新开窗口)
kubectl get pods -n dev -w
# 变更镜像
kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev

滚动更新

在更新式,滚动更新策略会根据配置的rollingUpdate先启动对应数量新的成功后停止对应数量的旧的,不断重复直到所有旧的被替换

编辑pc-deployment.yaml

spec:strategy: # 策略type: RollingUpdate # 滚动更新策略rollingUpdate:maxSurge: 25% maxUnavailable: 25%

执行命令测试

# 更新配置
kubectl apply -f pc-deployment.yaml
# 观察升级过程(新开窗口)
kubectl get pods -n dev -w
# 变更镜像
kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev

在镜像更新时通过kubectl get rs -n dev -w完成rs可以发现在每次更新时rs都创建一个新的rs并且创建新pod,旧的rs的pod会被删除

之所以这样设计是为了后续的deployment版本回退,当更新时出现问题只需要切换rs就能够实现版本回退

在这里插入图片描述

版本回退

deployment支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看.

kubectl rollout: 版本升级相关功能,支持下面的选项:

  • status 显示当前升级状态
  • history 显示 升级历史记录
  • pause 暂停版本升级过程
  • resume 继续已经暂停的版本升级过程
  • restart 重启版本升级过程
  • undo 回滚到上一级版本(可以使用–to-revision回滚到指定版本)
# 查看当前升级版本的状态
kubectl rollout status deploy pc-deployment -n dev
# 查看升级历史记录
kubectl rollout history deploy pc-deployment -n dev# 版本回滚
# 这里直接使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本,就是2版本
kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
# 查看发现,通过nginx镜像版本可以发现到了第一版
kubectl get deploy -n dev -o wide
# 查看rs,发现第一个rs中有4个pod运行,后面两个版本的rs中pod为运行
# 其实deployment之所以可是实现版本的回滚,就是通过记录下历史rs来实现的,
# 一旦想回滚到哪个版本,只需要将当前版本pod数量降为0,然后将回滚版本的pod提升为目标数量就可以了
kubectl get rs -n dev
金丝雀发布

Deployment控制器支持控制更新过程中的控制,如“暂停(pause)”或“继续(resume)”更新操作。

比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。

# 更新deployment的版本,并配置暂停deployment
kubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment  -n dev
#观察更新状态
kubectl rollout status deploy pc-deployment -n dev 
# 监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
kubectl get rs -n dev -o wide
kubectl get pods -n dev
# 确保更新的pod没问题了,继续更新
kubectl rollout resume deploy pc-deployment -n dev
# 查看最后的更新情况
kubectl get rs -n dev -o wide
kubectl get pods -n dev
删除
kubectl delete -f pc-deployment.yaml
Horizontal Pod Autoscaler(HPA)

在前面的课程种,我们可以通过手工执行kubectl scale命令实现Pod扩容,但是这显然不够智能,Kubernetes期望可以通过检测Pod的使用情况,实现pod数量的自动调整,于是产生了HPA这种控制器

HPA可以获取每个pod利用率,然后和HPA中设定的指标进行对比,同时计算出需要伸缩的具体值,最后实现pod的数量的调整,其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析目标pod的负载变化情况,来确认是否需要针对性的调整目标pod的副本数

准备工作
安装metrics-server
# 安装git
yum install git -y
# 获取metrics-server, 注意使用的版本
git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server
# 修改deployment, 注意修改的是镜像和初始化参数
cd /root/metrics-server/deploy/1.8+/
#添加下面选项
#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
vim metrics-server-deployment.yaml# 安装metrics-server
kubectl apply -f ./
# 查看pod运行情况
kubectl get pod -n kube-system
# 使用kubectl top node 查看资源使用情况
kubectl top nodes
kubectl top pod -n kube-system

在这里插入图片描述

准备deployment和servie

执行如下命令

# 使用命令模式创建deployment
kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev
# 创建service
kubectl expose deployment nginx --type=NodePort --port=80 -n dev
# 查看
kubectl get deployment,pod,svc -n dev
部署HPA

创建pc-hpa.yaml文件

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:name: pc-hpanamespace: dev
spec:minReplicas: 1  #最小pod数量maxReplicas: 10 #最大pod数量targetCPUUtilizationPercentage: 3 # CPU使用率指标3%scaleTargetRef:   # 指定要控制的nginx信息apiVersion: apps/v1kind: Deploymentname: nginx

执行命令测试

# 创建hpa
kubectl create -f pc-hpa.yaml
# 查看hpa
kubectl get hpa -n dev

使用Jmeter进行压测,可发现当压力增加后容器也不断增加最多增加10个

在这里插入图片描述

DaemonSet(DS)

DaemonSet类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景,也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。

DaemonSet控制器的特点:

  • 每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
  • 当节点从集群中移除时,Pod 也就被垃圾回收了

DaemonSet的yaml配置清单

apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型       
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: #标签controller: daemonset
spec: # 详情描述revisionHistoryLimit: 3 # 保留历史版本updateStrategy: # 更新策略type: RollingUpdate # 滚动更新策略rollingUpdate: # 滚动更新maxUnavailable: 1 # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: # 选择器,通过它指定该控制器管理哪些podmatchLabels:      # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
创建

创建pc-daemonset.yaml

apiVersion: apps/v1
kind: DaemonSet      
metadata:name: pc-daemonsetnamespace: dev
spec: selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1

执行命令测试

# 创建daemonset
kubectl create -f  pc-daemonset.yaml
# 查看daemonset
kubectl get ds -n dev -o wide
# 查看pod,发现在每个Node上都运行一个pod
kubectl get pods -n dev -o wide
# 删除daemonset
kubectl delete -f pc-daemonset.yaml
Job

Job,主要用于负责**批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)**任务。Job特点如下:

  • 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量
  • 当成功结束的pod达到指定的数量时,Job将完成执行

Job的yaml配置清单

apiVersion: batch/v1 # 版本号
kind: Job # 类型       
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: #标签controller: job
spec: # 详情描述completions: 1 # 指定job需要成功运行Pods的次数。默认值: 1parallelism: 1 # 指定job在任一时刻应该并发运行Pods的数量。默认值: 1activeDeadlineSeconds: 30 # 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。backoffLimit: 6 # 指定job失败后进行重试的次数。默认是6manualSelector: true # 是否可以使用selector选择器选择pod,默认是falseselector: # 选择器,通过它指定该控制器管理哪些podmatchLabels:      # Labels匹配规则app: counter-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [counter-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: counter-podspec:restartPolicy: Never # 重启策略只能设置为Never或者OnFailurecontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]

restartPolicy关于重启策略设置的说明:
如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变
如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1
如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always

创建

创建pc-job.yaml

apiVersion: batch/v1
kind: Job      
metadata:name: pc-jobnamespace: dev
spec:manualSelector: trueselector:matchLabels:app: counter-podtemplate:metadata:labels:app: counter-podspec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]

执行命令测试

# 创建job
kubectl create -f pc-job.yaml
# 查看job
kubectl get job -n dev -o wide  -w
# 通过观察pod状态可以看到,pod在运行完毕任务后,就会变成Completed状态
kubectl get pods -n dev -w
# 接下来,调整下pod运行的总数量和并行数量 即:在spec下设置下面两个选项
#  completions: 6 # 指定job需要成功运行Pods的次数为6
#  parallelism: 3 # 指定job并发运行Pods的数量为3
#  然后重新运行job,观察效果,此时会发现,job会每次运行3个pod,总共执行了6个pod
kubectl get pods -n dev -w
# 删除job
kubectl delete -f pc-job.yaml
Cronjob

CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务

CronJob的yaml配置清单

apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型       
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: #标签controller: cronjob
spec: # 详情描述schedule: # cron格式的作业调度运行时间点,用于控制任务在什么时间执行concurrencyPolicy: # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业failedJobHistoryLimit: # 为失败的任务执行保留的历史记录数,默认为1successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3startingDeadlineSeconds: # 启动作业错误的超时时长jobTemplate: # job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义metadata:spec:completions: 1parallelism: 1activeDeadlineSeconds: 30backoffLimit: 6manualSelector: trueselector:matchLabels:app: counter-podmatchExpressions: 规则- {key: app, operator: In, values: [counter-pod]}template:metadata:labels:app: counter-podspec:restartPolicy: Never containers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]

需要重点解释的几个选项:
schedule: cron表达式,用于指定任务的执行时间

concurrencyPolicy:
Allow: 允许Jobs并发运行(默认)
Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行
Replace: 替换,取消当前正在运行的作业并用新作业替换它

创建

创建pc-cronjob.yaml

apiVersion: batch/v1beta1
kind: CronJob
metadata:name: pc-cronjobnamespace: devlabels:controller: cronjob
spec:schedule: "*/1 * * * *"jobTemplate:metadata:spec:template:spec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]

执行命令测试

# 创建cronjob
kubectl create -f pc-cronjob.yaml
# 查看cronjob
kubectl get cronjobs -n dev
# 查看job
kubectl get jobs -n dev
# 查看pod
kubectl get pods -n dev
# 删除cronjob
kubectl  delete -f pc-cronjob.yaml

Service详解

Kubernetes流量负载组件:Service(4层路由的负载)和Ingress(7层路由的负载)

在Kubernetes中,pod是应用程序的载体,通过访问pod的ip来访问程序,但是pod的ip地址都是随机生成的,为了解决这个问题Kubernetes提供了Service资源,Service资源会对相同一个服务多个pod进行聚合,并提供一个统一的入口地址,通过Service的入口地址就能访问到后面的pod服务

在这里插入图片描述

Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个Kube-proxy服务,当创建Service的时候会通过api-Servee想etcd写入创建Service信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则

在这里插入图片描述

10.97.97.97:80 是service提供的访问入口
当访问这个入口的时候,可以发现后面有三个pod的服务在等待调用,
kube-proxy会基于rr(轮询)的策略,将请求分发到其中一个pod上去
这个规则会同时在集群内的所有节点上都生成,所以在任何一个节点上访问都可以。
ipvsadm -Ln

在这里插入图片描述

kube-proxy工作模式

userspace

userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。 该模式下,kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低

在这里插入图片描述

iptables

iptables模式下,kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。 该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。

在这里插入图片描述

ipvs

ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法

在这里插入图片描述

# 此模式必须安装ipvs内核模块,否则会降级为iptables
# 开启ipvs
kubectl edit cm kube-proxy -n kube-system
# 修改mode: "ipvs"
kubectl delete pod -l k8s-app=kube-proxy -n kube-system
# 查看ipvs信息
ipvsadm -Ln

Service类型

Service的yaml配置清单

kind: Service  # 资源类型
apiVersion: v1  # 资源版本
metadata: # 元数据name: service # 资源名称namespace: dev # 命名空间
spec: # 描述selector: # 标签选择器,用于确定当前service代理哪些podapp: nginxtype: # Service类型,指定service的访问方式clusterIP:  # 虚拟服务的ip地址sessionAffinity: # session亲和性,支持ClientIP、None两个选项ports: # 端口信息- protocol: TCP port: 3017  # service端口targetPort: 5003 # pod端口nodePort: 31122 # 主机端口
  • ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
  • NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
  • LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
  • ExternalName: 把集群外部的服务引入集群内部,直接使用

Service使用

环境准备

首先我们需要安装如下配置准备3个pod,通过Service访问pod服务再通过切换不同类型的Service来测试不同类型的Service的区别

pod程序pod控制器pod暴露端口
nignxdeploy80
nignxdeploy80
nignxdeploy80

创建deployment.yaml

apiVersion: apps/v1
kind: Deployment      
metadata:name: pc-deploymentnamespace: dev
spec: replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80

执行命令

# 创建pod
kubectl create -f deployment.yaml
# 查看pod详情
kubectl get pods -n dev -o wide --show-labels

在不同的机器上使用命令进入容器,修改inde.heml页面内容,方便后期测试时知道访问的是那个nginx

# 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的IP地址不一致)
kubectl exec -it pc-deployment-6696798b78-7lms6 -n dev /bin/sh 
echo "nginx IP => 10.244.2.43" > /usr/share/nginx/html/index.html
exitkubectl exec -it pc-deployment-6696798b78-f7w2g -n dev /bin/sh
echo "nginx IP => 10.244.1.142" > /usr/share/nginx/html/index.html
exitkubectl exec -it pc-deployment-6696798b78-q9dqw -n dev /bin/sh
echo "nginx IP => 10.244.1.141" > /usr/share/nginx/html/index.html
exit

在这里插入图片描述

测试是否能够正常访问

curl 10.244.2.43
curl 10.244.1.142
curl 10.244.1.141

在这里插入图片描述

ClusterIP类型Service

创建service-clusterip.yaml

apiVersion: v1
kind: Service
metadata:name: service-clusteripnamespace: dev
spec:selector:app: nginx-podclusterIP: 10.97.97.97 # service的ip地址,如果不写,默认会生成一个type: ClusterIPports:- port: 80  # Service端口       targetPort: 80 # pod端口

执行如下命令

# 创建service
kubectl create -f service-clusterip.yaml
# 查看service
kubectl get svc -n dev -o wide
# 查看service的详细信息
# 在这里有一个Endpoints列表,里面就是当前service可以负载到的服务入口
kubectl describe svc service-clusterip -n dev

在这里插入图片描述

测试,查看kube-poxy是否创建了相应的ipvs规则

# 查看ipvs的映射规则
ipvsadm -Ln | grep 10.97.97.97 -A 3
# 访问10.97.97.97:80观察效果
curl 10.97.97.97:80

在这里插入图片描述

负载分发策略

对Service的访问被分发到了后端的Pod上去,目前kubernetes提供了两种负载分发策略:

  • 如果不定义,默认使用kube-proxy的策略,比如随机、轮询(上面的rr就是轮询)

  • 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上

    此模式可以使在spec中添加sessionAffinity: ClientIP选项

#修改原有的配置文件
#添加sessionAffinity: ClientIP
vim service-clusterip.yaml
#应用配置文件
kubectl apply -f service-clusterip.yaml
# 查看ipvs的映射规则
ipvsadm -Ln | grep 10.97.97.97 -A 3
# 访问10.97.97.97:80观察效果
curl 10.97.97.97:80

ipvsadm可以看到在rr后面多了persistent 10800(持久化10800秒)

可以发现亲和性已改为ClientIP

通过curl发全球可以发现请求转发都是基于session的,在有效期内都是访问的相同的nginx

在这里插入图片描述

在这里插入图片描述

HeadLiness类型的Service

在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。

创建service-headliness.yaml

apiVersion: v1
kind: Service
metadata:name: service-headlinessnamespace: dev
spec:selector:app: nginx-podclusterIP: None # 将clusterIP设置为None,即可创建headliness Servicetype: ClusterIPports:- port: 80    targetPort: 80

执行如下命令

# 创建service
kubectl  create -f service-headliness.yaml

创建成功后因为通过域名访问就需要域名解析器,随便进入一台pod查看以下域名解析器的地址

在这里插入图片描述

使用dig命令查看域名解析dig @10.96.0.10 service-headliness.dev.svc.cluster.local

在这里插入图片描述

NodePort类型的Service

在之前的样例中,创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过NodeIp:NodePort来访问service了。

创建service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:name: service-nodeportnamespace: dev
spec:selector:app: nginx-podtype: NodePort # service类型ports:- port: 80nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配targetPort: 80

执行如下命令

# 创建service
kubectl create -f service-nodeport.yaml
# 查看service
kubectl get svc -n dev -o wide

在这里插入图片描述

LoadBalancer类型的Service

LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。

ExternalName类型的Service

ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。

apiVersion: v1
kind: Service
metadata:name: service-externalnamenamespace: dev
spec:type: ExternalName # service类型externalName: www.baidu.com  #改成ip地址也可以
# 创建service
kubectl  create -f service-externalname.yaml
# 域名解析
dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.

Ingress介绍

在前面课程中已经提到,Service对集群之外暴露服务的主要方式有两种:NotePort和LoadBalancer,但是这两种方式,都有一定的缺点:

  • NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显
  • LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持

基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示:

在这里插入图片描述

实际上,Ingress相当于一个7层的负载均衡器,是kubernetes对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解成在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的反向代理配置 , 然后对外部提供服务。在这里有两个核心概念:

  • ingress:kubernetes中的一个对象,作用是定义请求如何转发到service的规则
  • ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如Nginx, Contour, Haproxy等等

Ingress(以Nginx为例)的工作原理如下:

  1. 用户编写Ingress规则,说明哪个域名对应kubernetes集群中的哪个Service
  2. Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx反向代理配置
  3. Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态更新
  4. 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求转发规则

在这里插入图片描述

环境准备

搭建ingress环境

# 创建文件夹
mkdir ingress-controller
cd ingress-controller/
# 获取ingress-nginx,本次案例使用的是0.30版本
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
# 创建ingress-nginx
kubectl apply -f ./
# 查看ingress-nginx
kubectl get pod -n ingress-nginx
# 查看service
kubectl get svc -n ingress-nginx

准备service和pod

为了接下来的实验我们准备如下实现模型

在这里插入图片描述

创建nginx-tomcat.yaml

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentnamespace: dev
spec:replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80---apiVersion: apps/v1
kind: Deployment
metadata:name: tomcat-deploymentnamespace: dev
spec:replicas: 3selector:matchLabels:app: tomcat-podtemplate:metadata:labels:app: tomcat-podspec:containers:- name: tomcatimage: tomcat:8.5-jre10-slimports:- containerPort: 8080---apiVersion: v1
kind: Service
metadata:name: nginx-servicenamespace: dev
spec:selector:app: nginx-podclusterIP: Nonetype: ClusterIPports:- port: 80targetPort: 80---apiVersion: v1
kind: Service
metadata:name: tomcat-servicenamespace: dev
spec:selector:app: tomcat-podclusterIP: Nonetype: ClusterIPports:- port: 8080targetPort: 8080

执行如下命令

# 创建
kubectl create -f nginx-tomcat.yaml
# 查看
kubectl get svc -n dev
Http代理

创建ingress-http.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: ingress-httpnamespace: dev
spec:rules:- host: nginx.test.comhttp:paths:- path: /backend:serviceName: nginx-serviceservicePort: 80- host: tomcat.test.comhttp:paths:- path: /backend:serviceName: tomcat-serviceservicePort: 8080

执行如下命令

# 创建
kubectl create -f ingress-http.yaml
# 查看
kubectl get ing ingress-http -n dev
# 查看详情
kubectl describe ing ingress-http  -n dev
# 接下来,在本地电脑上配置host文件,解析上面的两个域名到192.168.10.100(master)上
# 然后,就可以分别访问tomcat.test.com:32519  和  nginx.test.com:32519 查看效果了

在这里插入图片描述

Https代理

创建证书

# 生成证书
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=test.com"
# 创建密钥
kubectl create secret tls tls-secret --key tls.key --cert tls.crt

创建ingress-https.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: ingress-httpsnamespace: dev
spec:tls:- hosts:- nginx.test.com- tomcat.test.comsecretName: tls-secret # 指定秘钥rules:- host: nginx.test.comhttp:paths:- path: /backend:serviceName: nginx-serviceservicePort: 80- host: tomcat.test.comhttp:paths:- path: /backend:serviceName: tomcat-serviceservicePort: 8080

执行如下命令

# 创建
kubectl create -f ingress-https.yaml
# 查看
kubectl get ing ingress-https -n dev
# 查看详情
kubectl describe ing ingress-https -n dev

在这里插入图片描述

数据存储

在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。

Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。

kubernetes的Volume支持多种类型,比较常见的有下面几个:

  • 简单存储:EmptyDir、HostPath、NFS
  • 高级存储:PV、PVC
  • 配置存储:ConfigMap、Secret

基础存储

EmptyDir

EmptyDir是最基础的Volume类型,一个EmptyDir就是Host上的一个空目录,EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为Kubernetes会自动分配一个目录,当Pod销毁时EmptyDir中的数据也会被永久删除

EmptyDir用途如下

  • 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
  • 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)

案例演示

接下来,通过一个容器之间文件共享的案例来使用一下EmptyDir。

在一个Pod中准备两个容器nginx和busybox,然后声明一个Volume分别挂在到两个容器的目录中,然后nginx容器负责向Volume中写日志,busybox中通过命令将日志内容读到控制台。

在这里插入图片描述

创建一个volume-emptydir.yaml

apiVersion: v1
kind: Pod
metadata:name: volume-emptydirnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80volumeMounts:  # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容volumeMounts:  # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs- name: logs-volumemountPath: /logsvolumes: # 声明volume, name为logs-volume,类型为emptyDir- name: logs-volumeemptyDir: {}

执行如下命令

# 创建Pod
kubectl create -f volume-emptydir.yaml
# 查看pod
kubectl get pods volume-emptydir -n dev -o wide
# 通过podIp访问nginx
curl 10.244.1.150
# 通过kubectl logs命令查看指定容器的标准输出
kubectl logs -f volume-emptydir -n dev -c busybox

在这里插入图片描述

HostPath

前面提到的EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。

HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依据可以存在于Node主机上。

在这里插入图片描述

创建一个volume-hostpath.yaml

apiVersion: v1
kind: Pod
metadata:name: volume-hostpathnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80volumeMounts:- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","tail -f /logs/access.log"]volumeMounts:- name: logs-volumemountPath: /logsvolumes:- name: logs-volumehostPath: path: /root/logstype: DirectoryOrCreate  # 目录存在就使用,不存在就先创建后使用

关于type的值的一点说明:

类型说明
DirectoryOrCreate目录存在就使用,不存在就先创建后使用
Directory目录必须存在
FileOrCreate文件存在就使用,不存在就先创建后使用
File文件必须存在
Socketunix套接字必须存在
CharDevice字符设备必须存在
BlockDevice块设备必须存在

执行如下代码

# 创建Pod
kubectl create -f volume-hostpath.yaml
# 查看Pod
kubectl get pods volume-hostpath -n dev -o wide
#访问nginx
curl 10.244.2.51
# 接下来就可以去host的/root/logs目录下查看存储的文件了
###  注意: 下面的操作需要到Pod所在的节点运行(案例中是node1)
ls /root/logs/
# 同样的道理,如果在此目录下创建一个文件,到容器中也是可以看到的

在这里插入图片描述

NFS

HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用NFS、CIFS。

NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问。

在这里插入图片描述

准备nsf服务器

这里为了方便测试直接将master作为nfs服务器

# 在nfs上安装nfs服务
yum install nfs-utils -y
# 准备一个共享目录
mkdir /root/data/nfs -pv
# 编辑/etc/exports文件(nfs默认读取的一个配置文件)
# 添加内容如下 /root/data/nfs     192.168.10.0/24(rw,no_root_squash)
# 将共享目录以读写权限暴露给192.168.10.0/24网段中的所有主机
vim /etc/exports
more /etc/exports
# 启动nfs服务
systemctl restart nfs

在node上安装nfs工具

# 在node上安装nfs服务,注意不需要启动
yum install nfs-utils -y

创建pod配置文件

创建volume-nfs.yaml

apiVersion: v1
kind: Pod
metadata:name: volume-nfsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80volumeMounts:- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","tail -f /logs/access.log"] volumeMounts:- name: logs-volumemountPath: /logsvolumes:- name: logs-volumenfs:server: 192.168.10.100  #nfs服务器地址path: /root/data/nfs #共享文件路径

执行如下命令

# 创建pod
kubectl create -f volume-nfs.yaml
# 查看pod
kubectl get pods volume-nfs -n dev
# 查看nfs服务器上的共享目录,发现已经有文件了
ls /root/data/nfs

高级存储

前面已经学习了使用NFS提供存储,此时就要求用户会搭建NFS系统,并且会在yaml配置nfs。由于kubernetes支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用, kubernetes引入PV和PVC两种资源对象。

PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由kubernetes管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。

PVC(Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC其实就是用户向kubernetes系统发出的一种资源需求申请。

在这里插入图片描述

使用了PV和PVC之后,工作可以得到进一步的细分:

  • 存储:存储工程师维护
  • PV: kubernetes管理员维护
  • PVC:kubernetes用户维护
PV

PV的yaml配置清单

apiVersion: v1  
kind: PersistentVolume
metadata:name: pv2
spec:nfs: # 存储类型,与底层真正存储对应capacity:  # 存储能力,目前只支持存储空间的设置storage: 2GiaccessModes:  # 访问模式storageClassName: # 存储类别persistentVolumeReclaimPolicy: # 回收策略
参数说明
  • 存储类型

    底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置都有所差异

  • 存储能力(capacity)

     目前只支持存储空间的设置( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置
    
  • 访问模式(accessModes)

    用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

    • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
    • ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载
    • ReadWriteMany(RWX):读写权限,可以被多个节点挂载

    需要注意的是,底层不同的存储类型可能支持的访问模式不同

  • 回收策略(persistentVolumeReclaimPolicy)

    当PV不再被使用了之后,对其的处理方式。目前支持三种策略:

    • Retain (保留) 保留数据,需要管理员手工清理数据
    • Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
    • Delete (删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务

    需要注意的是,底层不同的存储类型可能支持的回收策略不同

  • 存储类别

    PV可以通过storageClassName参数指定一个存储类别

    • 具有特定类别的PV只能与请求了该类别的PVC进行绑定
    • 未设定类别的PV则只能与不请求任何类别的PVC进行绑定
  • 状态(status)

    一个 PV 的生命周期中,可能会处于4中不同的阶段:

    • Available(可用): 表示可用状态,还未被任何 PVC 绑定
    • Bound(已绑定): 表示 PV 已经被 PVC 绑定
    • Released(已释放): 表示 PVC 被删除,但是资源还未被集群重新声明
    • Failed(失败): 表示该 PV 的自动回收失败
创建PV

以下使用nfs作为存储,演示PV的使用,创建3个PV,对应NFS中的3个暴露的路径

环境准备

# 创建目录
mkdir /root/data/{pv1,pv2,pv3} -pv
# 暴露服务,在/etc/exports添加如下内容
#/root/data/pv1     192.168.10.0/24(rw,no_root_squash)
#/root/data/pv2     192.168.10.0/24(rw,no_root_squash)
#/root/data/pv3     192.168.10.0/24(rw,no_root_squash)
vim /etc/exports
# 重启服务
systemctl restart nfs

创建yaml

创建pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:name:  pv1
spec:capacity: storage: 1GiaccessModes:- ReadWriteManypersistentVolumeReclaimPolicy: Retainnfs:path: /root/data/pv1server: 192.168.10.100---apiVersion: v1
kind: PersistentVolume
metadata:name:  pv2
spec:capacity: storage: 2GiaccessModes:- ReadWriteManypersistentVolumeReclaimPolicy: Retainnfs:path: /root/data/pv2server: 192.168.10.100---apiVersion: v1
kind: PersistentVolume
metadata:name:  pv3
spec:capacity: storage: 3GiaccessModes:- ReadWriteManypersistentVolumeReclaimPolicy: Retainnfs:path: /root/data/pv3server: 192.168.10.100

执行如下命令

# 创建 pv
kubectl create -f pv.yaml
# 查看pv
kubectl get pv -o wide

在这里插入图片描述

PVC

PVC是对管理员创建好的PV资源的申请,用来声明对存储空间、访问模式、存储类别需求信息

PVC的yaml配置清单

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvcnamespace: dev
spec:accessModes: # 访问模式selector: # 采用标签对PV选择storageClassName: # 存储类别resources: # 请求空间requests:storage: 5Gi
参数说明
  • 访问模式(accessModes)

用于描述用户应用对存储资源的访问权限

  • 选择条件(selector)

    通过Label Selector的设置,可使PVC对于系统中己存在的PV进行筛选

  • 存储类别(storageClassName)

    PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出

  • 资源请求(Resources )

    描述对存储资源的请求

PV绑定PVC

创建pvc.yaml,申请PV

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc1namespace: dev
spec:accessModes: - ReadWriteManyresources:requests:storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc2namespace: dev
spec:accessModes: - ReadWriteManyresources:requests:storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc3namespace: dev
spec:accessModes: - ReadWriteManyresources:requests:storage: 1Gi
# 创建pvc
kubectl create -f pvc.yaml
# 查看pvc
kubectl get pvc  -n dev -o wide
# 查看pv
kubectl get pv -o wide

在这里插入图片描述

Pod使用PV

创建pods.yaml

apiVersion: v1
kind: Pod
metadata:name: pod1namespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]volumeMounts:- name: volumemountPath: /root/volumes:- name: volumepersistentVolumeClaim:claimName: pvc1readOnly: false
---
apiVersion: v1
kind: Pod
metadata:name: pod2namespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]volumeMounts:- name: volumemountPath: /root/volumes:- name: volumepersistentVolumeClaim:claimName: pvc2readOnly: false

执行如下命令

# 创建pod
kubectl create -f pods.yaml
# 查看pod
kubectl get pods -n dev -o wide
# 查看pvc
kubectl get pvc -n dev -o wide
# 查看pv
kubectl get pv -n dev -o wide
# 查看nfs中的文件存储
more /root/data/pv1/out.txt
more /root/data/pv2/out.txt

在这里插入图片描述

生命周期

PVC和PV是一一对应的,PV和PVC之间的相互作用遵循以下生命周期:

  • 资源供应:管理员手动创建底层存储和PV

  • 资源绑定:用户创建PVC,kubernetes负责根据PVC的声明去寻找PV,并绑定

    在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的

    • 一旦找到,就将该PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了
    • 如果找不到,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合其要求的PV

    PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了

  • 资源使用:用户可在pod中像volume一样使用pvc

    Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。

  • 资源释放:用户删除pvc来释放pv

    当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为“已释放”,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用。

  • 资源回收:kubernetes根据pv设置的回收策略进行资源的回收

    对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用

在这里插入图片描述

配置存储

ConfigMap

ConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的

创建configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:name: configmapnamespace: dev
data:info: |username:adminpassword:123456

接下来,使用此配置文件创建configmap

# 创建configmap
kubectl create -f configmap.yaml
# 查看configmap详情
kubectl describe cm configmap -n dev

接下来创建一个pod-configmap.yaml,将上面创建的configmap挂载进去

apiVersion: v1
kind: Pod
metadata:name: pod-configmapnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts: # 将configmap挂载到目录- name: configmountPath: /configmap/configvolumes: # 引用configmap- name: configconfigMap:name: configmap

执行如下命令

# 创建pod
kubectl create -f pod-configmap.yaml
# 查看pod
kubectl get pod pod-configmap -n dev
#进入容器 执行如下命令 more /configmap/config/info
kubectl exec -it pod-configmap -n dev /bin/sh
# 可以看到映射已经成功,每个configmap都映射成了一个目录
# key--->文件     value---->文件中的内容
# 此时如果更新configmap的内容, 容器中的值也会动态更新
Secret

在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象。它主要用于存储敏感信息,例如密码、秘钥、证书等等

首先使用base64对数据进行编码

echo -n 'admin' | base64 #准备username YWRtaW4=
echo -n '123456' | base64 #准备password MTIzNDU2

接下来编写secret.yaml,并创建Secret

apiVersion: v1
kind: Secret
metadata:name: secretnamespace: dev
type: Opaque
data:username: YWRtaW4=password: MTIzNDU2

执行如下命令

# 创建secret
kubectl create -f secret.yaml
# 查看secret详情
kubectl describe secret secret -n dev

创建pod-secret.yaml,将上面创建的secret挂载进去

apiVersion: v1
kind: Pod
metadata:name: pod-secretnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts: # 将secret挂载到目录- name: configmountPath: /secret/configvolumes:- name: configsecret:secretName: secret

执行如下命令

# 创建pod
kubectl create -f pod-secret.yaml
# 查看pod
kubectl get pod pod-secret -n dev
# 进入容器,查看secret信息,发现已经自动解码了
# more /secret/config/username && more /secret/config/password
kubectl exec -it pod-secret /bin/sh -n dev

安全认证

Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作

客户端

在Kubernetes集群中,客户端通常有两类:

  • User Account:一般是独立于kubernetes之外的其他服务管理的用户账号。
  • Service Account:kubernetes管理的账号,用于为Pod中的服务进程在访问Kubernetes时提供身份标识。

在这里插入图片描述

认证、授权与准入控制

ApiServer是访问及管理资源对象的唯一入口。任何一个请求访问ApiServer,都要经过下面三个流程:

  • Authentication(认证):身份鉴别,只有正确的账号才能够通过认证
  • Authorization(授权): 判断用户是否有权限对访问的资源执行特定的动作
  • Admission Control(准入控制):用于补充授权机制以实现更加精细的访问控制功能。

在这里插入图片描述

认证管理

Kubernetes集群安全的最关键点在于如何识别并认证客户端身份,它提供了3种客户端身份认证方式:

  1. HTTP Base认证:通过用户名+密码的方式认证

    这种认证方式是把“用户名:密码”用BASE64算法进行编码后的字符串放在HTTP请求中的Header Authorization域里发送给服务端。服务端收到后进行解码,获取用户名及密码,然后进行用户身份认证的过程。

  2. HTTP Token认证:通过一个Token来识别合法用户

    这种认证方式是用一个很长的难以被模仿的字符串–Token来表明客户身份的一种方式。每个Token对应一个用户名,当客户端发起API调用请求时,需要在HTTP Header里放入Token,API Server接到Token后会跟服务器中保存的token进行比对,然后进行用户身份认证的过程。

  3. HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式

这种认证方式是安全性最高的一种方式,但是同时也是操作起来最麻烦的一种方式。

HTTPS认证大体分为3个过程:

  1. 证书申请和下发

    HTTPS通信双方的服务器向CA机构申请证书,CA机构下发根证书、服务端证书及私钥给申请者
    
  2. 客户端和服务端的双向认证

      1> 客户端向服务器端发起请求,服务端下发自己的证书给客户端,客户端接收到证书后,通过私钥解密证书,在证书中获得服务端的公钥,客户端利用服务器端的公钥认证证书中的信息,如果一致,则认可这个服务器2> 客户端发送自己的证书给服务器端,服务端接收到证书后,通过私钥解密证书,在证书中获得客户端的公钥,并用该公钥认证证书信息,确认客户端是否合法
    
  3. 服务器端和客户端进行通信

      服务器端和客户端协商好加密方案后,客户端会产生一个随机的秘钥并加密,然后发送到服务器端。服务器端接收这个秘钥后,双方接下来通信的所有内容都通过该随机秘钥加密
    

注意: Kubernetes允许同时配置多种认证方式,只要其中任意一个方式认证通过即可

授权管理

授权发生在认证成功之后,通过认证就可以知道请求用户是谁, 然后Kubernetes会根据事先定义的授权策略来决定用户是否有权限访问,这个过程就称为授权。

每个发送到ApiServer的请求都带上了用户和资源的信息:比如发送请求的用户、请求的路径、请求的动作等,授权就是根据这些信息和授权策略进行比较,如果符合策略,则认为授权通过,否则会返回错误。

API Server目前支持以下几种授权策略:

  • AlwaysDeny:表示拒绝所有请求,一般用于测试
  • AlwaysAllow:允许接收所有请求,相当于集群不需要授权流程(Kubernetes默认的策略)
  • ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
  • Webhook:通过调用外部REST服务对用户进行授权
  • Node:是一种专用模式,用于对kubelet发出的请求进行访问控制
  • RBAC:基于角色的访问控制(kubeadm安装方式下的默认选项)

RBAC(Role-Based Access Control) 基于角色的访问控制,主要是在描述一件事情:给哪些对象授予了哪些权限

其中涉及到了下面几个概念:

  • 对象:User、Groups、ServiceAccount
  • 角色:代表着一组定义在资源上的可操作动作(权限)的集合
  • 绑定:将定义好的角色跟用户绑定在一起

在这里插入图片描述

RBAC引入了4个顶级资源对象:

  • Role(ns级别)、ClusterRole(集群级别):角色,用于指定一组权限
  • RoleBinding(ns级别)、ClusterRoleBinding(集群级别):角色绑定,用于将角色(权限)赋予给对象
Role、ClusterRole

一个角色就是一组权限的集合,这里的权限都是许可形式的(白名单)

# Role只能对命名空间内的资源进行授权,需要指定nameapce
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:namespace: devname: authorization-role
rules:
- apiGroups: [""]  # 支持的API组列表,"" 空字符串,表示核心API群resources: ["pods"] # 支持的资源对象列表verbs: ["get", "watch", "list"] # 允许的对资源对象的操作方法列表
# ClusterRole可以对集群范围内资源、跨namespaces的范围资源、非资源类型进行授权
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:name: authorization-clusterrole
rules:
- apiGroups: [""]resources: ["pods"]verbs: ["get", "watch", "list"]

需要详细说明的是,rules中的参数:

  • apiGroups: 支持的API组列表

    "","apps", "autoscaling", "batch"
    
  • resources:支持的资源对象列表

    "services", "endpoints", "pods","secrets","configmaps","crontabs","deployments","jobs",
    "nodes","rolebindings","clusterroles","daemonsets","replicasets","statefulsets",
    "horizontalpodautoscalers","replicationcontrollers","cronjobs"
    
  • verbs:对资源对象的操作方法列表

    "get", "list", "watch", "create", "update", "patch", "delete", "exec"
    
RoleBinding、ClusterRoleBinding

角色绑定用来把一个角色绑定到一个目标对象上,绑定目标可以是User、Group或者ServiceAccount

# RoleBinding可以将同一namespace中的subject绑定到某个Role下,则此subject即具有该Role定义的权限
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:name: authorization-role-bindingnamespace: dev
subjects:
- kind: Username: testapiGroup: rbac.authorization.k8s.io
roleRef:kind: Rolename: authorization-roleapiGroup: rbac.authorization.k8s.io
# ClusterRoleBinding在整个集群级别和所有namespaces将特定的subject与ClusterRole绑定,授予权限
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:name: authorization-clusterrole-binding
subjects:
- kind: Username: testapiGroup: rbac.authorization.k8s.io
roleRef:kind: ClusterRolename: authorization-clusterroleapiGroup: rbac.authorization.k8s.io
RoleBinding引用ClusterRole进行授权

RoleBinding可以引用ClusterRole,对属于同一命名空间内ClusterRole定义的资源主体进行授权。

一种很常用的做法就是,集群管理员为集群范围预定义好一组角色(ClusterRole),然后在多个命名空间中重复使用这些ClusterRole。这样可以大幅提高授权管理工作效率,也使得各个命名空间下的基础性授权规则与使用体验保持一致。

# 虽然authorization-clusterrole是一个集群角色,但是因为使用了RoleBinding
# 所以test只能读取dev命名空间中的资源
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:name: authorization-role-binding-nsnamespace: dev
subjects:
- kind: Username: testapiGroup: rbac.authorization.k8s.io
roleRef:kind: ClusterRolename: authorization-clusterroleapiGroup: rbac.authorization.k8s.io
案例

在前面演示时我们都使用的是kubernetes提供的默认账号admin,admin账号权限极高能够操作所有的命名空间,在企业中开发中通常需要缩减权限只需要给开发人员配置只能操作相应的命名空间即可

创建账号

# 1) 创建证书
cd /etc/kubernetes/pki/
(umask 077;openssl genrsa -out devman.key 2048)# 2) 用apiserver的证书去签署
# 2-1) 签名申请,申请的用户是devman,组是devgroup
openssl req -new -key devman.key -out devman.csr -subj "/CN=devman/O=devgroup"     
# 2-2) 签署证书
openssl x509 -req -in devman.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out devman.crt -days 3650# 3) 设置集群、用户、上下文信息
kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.10.100:6443kubectl config set-credentials devman --embed-certs=true --client-certificate=/etc/kubernetes/pki/devman.crt --client-key=/etc/kubernetes/pki/devman.keykubectl config set-context devman@kubernetes --cluster=kubernetes --user=devman# 切换账户到devman
kubectl config use-context devman@kubernetes# 查看dev下pod,发现没有权限
kubectl get pods -n dev# 切换到admin账户
kubectl config use-context kubernetes-admin@kubernetes

创建Role和RoleBinding,为devman用户授权

创建dev-role.yaml

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:namespace: devname: dev-role
rules:
- apiGroups: [""]resources: ["pods"]verbs: ["get", "watch", "list"]---kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:name: authorization-role-bindingnamespace: dev
subjects:
- kind: Username: devmanapiGroup: rbac.authorization.k8s.io
roleRef:kind: Rolename: dev-roleapiGroup: rbac.authorization.k8s.io

执行如下命令

kubectl create -f dev-role.yaml

测试

# 切换账户到devman
kubectl config use-context devman@kubernetes
# 再次查看
kubectl get pods -n dev
# 为了不影响后面的学习,切回admin账户
kubectl config use-context kubernetes-admin@kubernetes

准入控制

通过了前面的认证和授权之后,还需要经过准入控制处理通过之后,apiserver才会处理这个请求。

准入控制是一个可配置的控制器列表,可以通过在Api-Server上通过命令行设置选择执行哪些准入控制器:

--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds

只有当所有的准入控制器都检查通过之后,apiserver才执行该请求,否则返回拒绝。

当前可配置的Admission Control准入控制如下:

  • AlwaysAdmit:允许所有请求
  • AlwaysDeny:禁止所有请求,一般用于测试
  • AlwaysPullImages:在启动容器之前总去下载镜像
  • DenyExecOnPrivileged:它会拦截所有想在Privileged Container上执行命令的请求
  • ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。
  • Service Account:实现ServiceAccount实现了自动化
  • SecurityContextDeny:这个插件将使用SecurityContext的Pod中的定义全部失效
  • ResourceQuota:用于资源配额管理目的,观察所有请求,确保在namespace上的配额不会超标
  • LimitRanger:用于资源限制管理,作用于namespace上,确保对Pod进行资源限制
  • InitialResources:为未设置资源请求与限制的Pod,根据其镜像的历史资源的使用情况进行设置
  • NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒绝。当删除一个namespace时,系统将会删除该namespace中所有对象。
  • DefaultStorageClass:为了实现共享存储的动态供应,为未指定StorageClass或PV的PVC尝试匹配默认的StorageClass,尽可能减少用户在申请PVC时所需了解的后端存储细节
  • DefaultTolerationSeconds:这个插件为那些没有设置forgiveness tolerations并具有notready:NoExecute和unreachable:NoExecute两种taints的Pod设置默认的“容忍”时间,为5min
  • PodSecurityPolicy:这个插件用于在创建或修改Pod时决定是否根据Pod的security context和可用的PodSecurityPolicy对Pod的安全策略进行控制

DashBoard

之前在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的。其实,为了提供更丰富的用户体验,kubernetes还开发了一个基于web的用户界面(Dashboard)。用户可以使用Dashboard部署容器化的应用,还可以监控应用的状态,执行故障排查以及管理kubernetes中各种资源。

部署Dashboard

# 下载yaml
wget  https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
# 修改kubernetes-dashboard的Service类型
#kind: Service
#apiVersion: v1
#metadata:
#  labels:
#    k8s-app: kubernetes-dashboard
#  name: kubernetes-dashboard
#  namespace: kubernetes-dashboard
#spec:
#  type: NodePort  # 新增
#  ports:
#    - port: 443
#      targetPort: 8443
#      nodePort: 30009  # 新增
#  selector:
#    k8s-app: kubernetes-dashboard
# 部署
kubectl create -f recommended.yaml
# 查看namespace下的kubernetes-dashboard下的资源
kubectl get pod,svc -n kubernetes-dashboard

注意这里使用浏览器要使用火狐,用谷歌浏览器可能会有问题

在这里插入图片描述

创建访问账户,获取token

# 创建账号
kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
# 授权
kubectl create clusterrolebinding dashboard-admin-rb --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin
# 查看相关密钥
kubectl get secrets -n kubernetes-dashboard | grep dashboard-admin
# 使用describe查看密钥中的token内容
kubectl describe secrets dashboard-admin-token-xtlcw -n kubernetes-dashboard

登录成功后即可看到kubernetes的信息

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/116477.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

itbuilder软件在线设计数据库模型,AI与数据库擦出的火花

今天要介绍一款强大的软件&#xff0c;它就是itBuilder软件&#xff0c;一款在线设计数据库模型软件&#xff0c;借助人工智能提高效率&#xff0c;可以生成CRUD代码并推送至开发工具中&#xff1b;它涵盖了几乎所有语言&#xff0c;如Java、Python、JavaScript等&#xff0c;并…

4种实用的制作URL 文件的方法

很多小伙伴有自己的博客、淘宝或者共享文件网站&#xff0c;想要分享、推广自己的网址做成url文件&#xff0c;让别人点击这个url文件直接访问自己的网站。URL文件其实就一个超级链接&#xff0c;制作的方法很多&#xff0c;这里列举4种。 收藏网站直接拖拽 1.第一种&#xf…

drf-过滤、排序、异常处理、自封装Response

过滤 过滤就是根据路由url?后的信息过滤出符合&#xff1f;后条件的数据而非全部&#xff0c;比如…/?nameweer就是只查name是weer的数据&#xff0c;其余不返回。 1、安装&#xff1a;pip3 install django-filter2、注册&#xff1a;在settings.py中的app中注册django-filt…

服务器数据恢复-某银行服务器硬盘数据恢复案例

服务器故障&分析&#xff1a; 某银行的某一业务模块崩溃&#xff0c;无法正常使用。排查服务器故障&#xff0c;发现运行该业务模块的服务器中多块硬盘离线&#xff0c;导致上层应用崩溃。 故障服务器内多块硬盘掉线&#xff0c;硬盘掉线数量超过服务器raid阵列冗余级别所允…

【T3】畅捷通T3备份账套提示:超时已过期,错误‘53‘文件不存在。

【问题描述】 针对畅捷通T3软件&#xff0c;进行账套备份&#xff08;账套输出&#xff09;的时候&#xff0c; 先是提示”超时已过期“&#xff1b; 点击确定后&#xff0c;再次提示&#xff1a;运行时错误53&#xff0c;文件未找到。 最终导致账套备份/输出失败。 【解决…

windows10下pytorch环境部署留念

pytorch环境部署留念 第一步&#xff1a;下载安装anaconda 官网地址 &#xff08;也可以到清华大学开源软件镜像站下载&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/&#xff09; 我安装的是下面这个&#xff0c;一通下一步就完事儿。 第二步&#x…

Java中配置RabbitMQ基本步骤

在Java中配置RabbitMQ&#xff0c;需要遵循以下步骤&#xff1a; 1.添加依赖 在项目的pom.xml文件中添加RabbitMQ的Java客户端依赖&#xff1a; <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><versio…

NLP入门——语言结构/语言建模

一、Linguistics 语言学 wordsmorphology 形态学&#xff1a;词的构成和内部结构研究。如英语的dog、dogs和dog-catcher有相当的关系morpheme 语素&#xff1a;最小的语法单位&#xff0c;是最小的音义结合体lexeme 词位&#xff1a;词的意义的基本抽象单位&#xff0c;是一组…

Scala入门到放弃—02—函数

文章目录 函数方法定义默认参数命名参数可变参数条件语句循环表达式 函数 方法定义 def 方法名(参数: 参数类型): 返回值类型 {//方法体//最后一行作为返回值(不需要使用return) } def max(x: Int, y: Int): Int {if(x > y)xelse y }package org.example object App {de…

2.6.C++项目:网络版五子棋对战之数据管理模块-游戏房间管理模块的设计

文章目录 一、意义二、功能三、作用四、游戏房间类基本框架五、游戏房间管理类基本框架七、游戏房间类代码八、游戏房间管理类代码 一、意义 对匹配成功的玩家创建房间&#xff0c;建立起一个小范围的玩家之间的关联关系&#xff01; 房间里一个玩家产生的动作将会广播给房间里…

基本的爬虫工作原理

爬虫是一种自动化程序&#xff0c;能够模拟人类的浏览行为&#xff0c;从网络上获取数据。爬虫的工作原理主要包括网页请求、数据解析和数据存储等几个步骤。本文将详细介绍爬虫的基本工作原理&#xff0c;帮助读者更好地理解和应用爬虫技术。 首先&#xff0c;爬虫的第一步是…

I/O模型之非阻塞IO

简介 五种IO模型   阻塞IO   非阻塞IO   信号驱动IO   IO多路转接    异步IO 代码书写 非阻塞IO 再次理解IO 什么是IO&#xff1f;什么是高效的IO&#xff1f; 为了理解后面的一个问题&#xff0c;我们首先要再重新理解一下什么是IO 在之前的网络介绍中&#xff…

CUDA学习笔记(二)CUDA简介

本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/&#xff0c;仅用于学习。 CUDA是并行计算的平台和类C编程模型&#xff0c;我们能很容易的实现并行算法&#xff0c;就像写C代码一样。只要配备的NVIDIA GPU&#xff0c;就可以在许多设备上运行你的并行程序&#xf…

浅谈uniapp中开发安卓原生插件

其实官方文档介绍的比较清楚而且详细,但是有时候他太墨迹,你一下子找不到自己想要的,所以我总结了一下开发的提纲,也是为了自己方便下次使用。 1.第一步,下载官方提供的Android的示例工程,然后倒入UniPlugin-Hello-AS工程请在App离线SDK中查找,之后Android studio,编译运行项目…

自编efi文件测试vmware虚拟机如何进入UEFI环境

同事突然让帮忙编一下UEFI&#xff0c;之前完全没有接触过&#xff0c;在此粗鲁记录其过程。 UEFI的开源框架是edk2&#xff0c;开发环境配置起来还是有些麻烦&#xff0c;完全按照文档编译不过&#xff0c;经人帮助总算编译通过&#xff0c;但如何测试又是问题&#xff1b;网…

【T+】畅捷通T+增加会计科目提示执行超时已过期。

【问题描述】 在畅捷通T软件中&#xff0c; 增加会计科目的时候提示&#xff1a; 通过DataTable插入ext扩展表出错:执行超时已过期。 完成操作之前已超时或服务器未响应。 操作已被用户取消。 语句已终止。 【解决方法】 【方法一】 注销用户登录&#xff0c;回到软件登录界面…

FFmpeg和rtsp服务器搭建视频直播流服务

下面使用的是ubuntu的&#xff0c;window系统可以参考&#xff1a; 通过rtsp-simple-server和ffmpeg实现录屏并发布视频直播_rtsp simple server_病毒宇宇的博客-CSDN博客 一、安装rtsp-simple-server &#xff08;1&#xff09;下载rtsp-simple-server 下载地址&#xff1a;R…

vsCode 格式化配置

学习目标&#xff1a; 基于 vsCode 配置格式化工具&#xff0c;提高&#xff08;React、Vue &#xff09;开发效率  1. vsCode 安装 prettier 插件并启用  2. 修改配置文件 setting.json setting.json 位置&#xff1a; 依次点击 替换内容&#xff1a;↓ {"git.enab…

智加科技与东风柳汽达成深度合作 自动驾驶重卡计划2024年初量产交付

&#xff08;2023年10月19日&#xff0c;苏州&#xff09;全球领先的重卡自动驾驶技术公司智加科技与东风柳汽宣布&#xff0c;双方共同开发的自动驾驶重卡H7计划2024年初实现量产交付。未来&#xff0c;双方将携手推出安全可靠、高性价比、性能卓越的自动驾驶重卡产品&#xf…

STM32 HAL高级定时器正交编码模式案例

STM32 HAL高级定时器正交编码模式案例 &#x1f516;基于stm32F030RBT6单片机采用高级定时器1&#xff0c;编码器模式&#xff0c;测试EC11编码器。 &#x1f3ac;EC11测试效果&#xff1a; &#x1f33f;STM32定时器编码器有3种映射模式: ✨本次采用的是上面的模式3&#x…