k8s-资源详解(四)

pod详解

1.Pod的介绍

1.1Pod的结构

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

  1. 用户程序所在的容器,数量可多可少。
  2. Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
  • 可以以它为依据,评估整个Pod的健康状况。
  • 可以在根容器上设置IP地址,其它容器都共享此IP(Pod的IP),以实现Pod内部的网络通信(这里是Pod内部的通讯,Pod之间的通讯采用虚拟二层网络技术来实现,我们当前环境使用的是Flannel)。

1.2Pod的yaml配置说明:

apiVersion: v1     #必选,版本号,例如v1
kind: Pod         #必选,资源类型,例如 Pod
metadata:         #必选,元数据
  name: string     #必选,Pod名称
  namespace: string  #Pod所属的命名空间,默认为"default"
  labels:           #自定义标签列表
    - name: string                 
spec:  #必选,Pod中容器的详细定义
  containers:  #必选,Pod中容器列表
  - name: string   #必选,容器名称
    image: string  #必选,容器的镜像名称
    imagePullPolicy: [ Always|Never|IfNotPresent ]  #获取镜像的策略 
    command: [string]   #容器的启动命令列表,如不指定,使用打包时使用的启动命令
    args: [string]      #容器的启动命令参数列表
    workingDir: string  #容器的工作目录
    volumeMounts:       #挂载到容器内部的存储卷配置
    - name: string      #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
      mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
      readOnly: boolean #是否为只读模式
    ports: #需要暴露的端口库号列表
    - name: string        #端口的名称
      containerPort: int  #容器需要监听的端口号
      hostPort: int       #容器所在主机需要监听的端口号,默认与Container相同
      protocol: string    #端口协议,支持TCP和UDP,默认TCP
    env:   #容器运行前需设置的环境变量列表
    - name: string  #环境变量名称
      value: string #环境变量的值
    resources: #资源限制和请求的设置
      limits:  #资源限制的设置
        cpu: string     #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
        memory: string  #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
      requests: #资源请求的设置
        cpu: string    #Cpu请求,容器启动的初始可用数量
        memory: string #内存请求,容器启动的初始可用数量
    lifecycle: #生命周期钩子
		postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
		preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
    livenessProbe:  #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
      exec:         #对Pod容器内检查方式设置为exec方式
        command: [string]  #exec方式需要制定的命令或脚本
      httpGet:       #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
        path: string
        port: number
        host: string
        scheme: string
        HttpHeaders:
        - name: string
          value: string
      tcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式
         port: number
       initialDelaySeconds: 0       #容器启动完成后首次探测的时间,单位为秒
       timeoutSeconds: 0          #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
       periodSeconds: 0           #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
       successThreshold: 0
       failureThreshold: 0
       securityContext:
         privileged: false
  restartPolicy: [Always | Never | OnFailure]  #Pod的重启策略
  nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
  nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
  imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
  - name: string
  hostNetwork: false   #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
  volumes:   #在该pod上定义共享存储卷列表
  - name: string    #共享存储卷名称 (volumes类型有很多种)
    emptyDir: {}       #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
    hostPath: string   #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
      path: string                #Pod所在宿主机的目录,将被用于同期中mount的目录
    secret:          #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
      scretname: string  
      items:     
      - key: string
        path: string
    configMap:         #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
      name: string
      items:
      - key: string
        path: string

查看每种资源的可配置项

语法

# 查看某种资源可以配置的一级配置
kubectl explain 资源类型 
# 查看属性的子属性
kubectl explain 资源类型.分级属性

示例:查看资源类型为pod的可配置项

[root@master ~]# kubectl explain pod

2.Pod的配置

2.1配置说明

# 返回的重要属性
KIND:     Pod
VERSION:  v1
RESOURCE: containers <[]Object>   # 数组,代表可以有多个容器FIELDS:
  name  <string>     # 容器名称
  image <string>     # 容器需要的镜像地址
  imagePullPolicy  <string> # 镜像拉取策略 
  command  <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
  args   <[]string> # 容器的启动命令需要的参数列表 
  env    <[]Object> # 容器环境变量的配置
  ports  <[]Object>  # 容器需要暴露的端口号列表
  resources <Object> # 资源限制和资源请求的设置

2.2基本配置

创建pod-base.yaml文件,这里只是初步的简单定义配置,实际生产配置远不止这些

apiVersion: v1
kind: Pod
metadata:
  name: pod-base
  namespace: dev
  labels:
    user: kele
spec:
#containers是集合 可以通过-创建多个对象
  containers:
    - name: nginx # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
    - name: busybox # 容器名称
      image: busybox:1.30 # 容器需要的镜像地址

创建Pod:         

[root@master ~]# kubectl apply -f pod-base.yaml

 查看Pod状况:

# 此时已经运行起来了一个基本的Pod,虽然它暂时有问题
[root@master ~]# kubectl describe pod pod-base -n dev

2.3 镜像拉取策略 

创建pod-imagepullpolicy.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-imagepullpolicy
  namespace: dev
  labels:
    user: kele
spec:
  containers:
    - name: nginx # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: Always # 用于设置镜像的拉取策略
    - name: busybox # 容器名称
      image: busybox:1.30 # 容器需要的镜像地址

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

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

默认值说明:

  • 如果镜像tag为具体的版本号,默认策略是IfNotPresent。
  • 如果镜像tag为latest(最终版本),默认策略是Always。

创建Pod:

[root@master ~]# kubectl apply -f pod-imagepullpolicy.yaml

 查看Pod详情:

[root@master ~]# kubectl describe pod pod-imagepullpolicy -n dev

 2.4启动命令

  • 在前面的案例中,一直有一个问题没有解决,就是busybox容器一直没有成功运行,那么到底是什么原因导致这个容器的故障的呢?
  • 原来busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command的配置。创建pod-

command.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-command
  namespace: dev
  labels:
    user: kele
spec:
  containers:
    - name: nginx # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent # 设置镜像拉取策略
    - name: busybox # 容器名称
      image: busybox:1.30 # 容器需要的镜像地址
      command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done;"]

command:用于在Pod中的容器初始化完毕之后执行的命令。

这里稍微解释下command中的命令的意思:

  • "/bin/sh","-c":使用sh执行命令。
  • touch /tmp/hello.txt:创建一个/tmp/hello.txt的文件。
  • while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done:每隔3秒,向文件写入当前时间

创建Pod:

[root@master ~]# kubectl apply -f pod-command.yaml

查看Pod状态:

[root@master ~]# kubectl get pod pod-command -n dev

进入Pod中的busybox容器命令,并且查看command命令执行的内容

# 在容器中执行命令
# kubectl exec -it pod的名称 -n 命名空间 -c 容器名称 /bin/sh
[root@master ~]# kubectl exec -it pod-command -n dev -c busybox /bin/sh

[root@master ~]# tail -f /tmp/hello.txt

2.5环境变量

建议通过配置文件的形势或者系统环境变量的配置

2.6端口设置

一般发布容器后,通过系统暴露的端口映射到容器端口进行访问,不推荐直接将系统ip和端口配置给容器

配置说明

KIND:     Pod
VERSION:  v1
RESOURCE: ports <[]Object>
FIELDS:
  name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的
  containerPort <integer> # 容器要监听的端口(0<x<65536)
  hostPort <integer> # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副本(一般省略)
  hostIP <string>  # 要将外部端口绑定到的主机IP(一般省略)
  protocol <string>  # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”

创建pod-ports.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-ports
  namespace: dev
  labels:
    user: kele
spec:
  containers:
    - name: nginx # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      ports:
        - name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
          containerPort: 80 # 容器要监听的端口 (0~65536)
          protocol: TCP # 端口协议

创建Pod

kubectl create -f pod-ports.yaml

 2.7资源配额

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

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

可以通过上面的两个选项设置资源的上下限。

创建pod-resoures.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-resoures
  namespace: dev
  labels:
    user: kele
spec:
  containers:
    - name: nginx # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      ports: # 端口设置
        - name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
          containerPort: 80 # 容器要监听的端口 (0~65536)
          protocol: TCP # 端口协议
      resources: # 资源配额
        limits: # 限制资源的上限
          cpu: "2" # CPU限制,单位是core数
          memory: "10Gi" # 内存限制
        requests: # 限制资源的下限
          cpu: "1" # CPU限制,单位是core数 
          memory: "10Mi" # 内存限制

3.pod生命周期

一般将Pod对象从创建到终止的这段时间范围称为Pod的生命周期,它主要包含下面的过程:

1.Pod创建过程。

2.运行初始化容器(init container)过程。

3.运行主容器(main container):

  • 容器启动后钩子(post start)、容器终止前钩子(pre stop)。
  • 容器的存活性探测(liveness probe)、就绪性探测(readiness probe)。

4.Pod终止过程

在整个生命周期中,Pod会出现5种状态(相位),分别如下:

  • 挂起(Pending):API Server已经创建了Pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中。
  • 运行中(Running):Pod已经被调度到某节点,并且所有容器都已经被kubelet创建完成。
  • 成功(Succeeded):Pod中的所有容器都已经成功终止并且不会被重启。
  • 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态。
  • 未知(Unknown):API Server无法正常获取到Pod对象的状态信息,通常由于网络通信失败所导致。

 3.1pod的创建和销毁过程

3.1.1创建过程
  1.  用户通过kubectl或其他的api客户端提交需要创建的Pod信息给API Server。
  2.  API Server开始生成Pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端。
  3.  API Server开始反映etcd中的Pod对象的变化,其它组件使用watch机制来跟踪检查API Server上的变动。
  4.  Scheduler发现有新的Pod对象要创建,开始为Pod分配主机并将结果信息更新至API Server。
  5.  Node节点上的kubelet发现有Pod调度过来,尝试调度Docker启动容器,并将结果回送至API Server。
  6. API Server将接收到的Pod状态信息存入到etcd中。

结论:通过操作master节点来控制node节点上的pod创建,其中核心为apiserver,etcd记录pod状态信息,其他组件通过订阅的方式来监听apiserver

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

 3.2初始化容器(initContainers)

初始化容器是在Pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:启动的前置条件必须都满足才启动容器

  • 初始化容器必须运行完成直至结束,如果某个初始化容器运行失败,那么kubernetes需要重启它直至成功完成。
  • 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行,可以理解为

初始化容器有很多的应用场景,下面列出的是最常见的几个,:

  • 提供主容器镜像中不具备的工具程序或自定义代码。
  • 初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足。
3.2.1测试案例:

场景

  • 假设要以主容器来运行Nginx,但是要求在运行Nginx之前要能够连接上MySQL和Redis所在的服务器。
  • 为了简化测试,事先规定好MySQL和Redis所在的IP地址分别为192.168.79.203和192.168.79.204(注意,这两个IP都不能ping通,因为环境中没有这两个IP)。

 1.创建pod-initcontainer.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-initcontainer
  namespace: dev
  labels:
    user: kele
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
      resources:
        limits:
          cpu: "2"
          memory: "1Gi"
        requests:
          cpu: "1"
          memory: "10Mi"
  initContainers: # 初始化容器配置
    - name: test-mysql
      image: busybox:1.30
      command: ["sh","-c","until ping 192.168.79.203 -c 1;do echo waiting for mysql ...;sleep 2;done;"]
      securityContext:
        privileged: true # 使用特权模式运行容器
    - name: test-redis
      image: busybox:1.30
      command: ["sh","-c","until ping 192.168.79.204 -c 1;do echo waiting for redis ...;sleep 2;done;"]
      securityContext:
        privileged: true # 使用特权模式运行容器

 command内容说明

--until ping 192.168.79.203 -c 1:执行ping ip一次

--do echo waiting for mysql ...;sleep 2 :如果ping不通 则执行输出语句,并且等待2s再继续执行ping

--done:如果ping成功则结束

command: ["sh","-c","until ping 192.168.79.203 -c 1;do echo waiting for mysql ...;sleep 2;done;"]

2.创建Pod:

[root@master local]#kubectl describe pod pod-initcontainer -n dev

3.查看Pod状态:

[root@master local]#kubectl describe pod pod-initcontainer -n dev

4.删除pod 

[root@master local]# kubectl delete pod pod-initcontainer -n dev

3.3钩子函数 (lifecycle)

钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。

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

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

钩子处理器支持使用下面的三种方式定义动作:

1.exec命令:容器内执行一次命令。

spec:
  containers:
      lifecycle:
         postStart: 
            exec:
               command:
                 - cat
                 - /tmp/healthy

2.tcpSocket:当前容器尝试访问指定的socket。

spec:
  containers:
       lifecycle:
          postStart:
             tcpSocket:
                port: 8080

3.httpGet:当前容器中向某url发起HTTP请求。

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

3.4容器探测 (liveness)

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

  1. liveness probes:存活性探测,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器
  2. readiness probes:就绪性探测,用于检测应用实例是否可以接受请求,如果不能,k8s不会转发流量

扩展:

k8s在1.16版本之后新增了startupProbe探针,用于判断容器内应用程序是否已经启动。如果配置了startupProbe探针,就会先禁止其他的探针,直到startupProbe探针成功为止,一旦成功将不再进行探测。

定义说明:

上面两种探针目前均支持三种探测方式:

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

spec:
  containers: # 容器配置
    livenessProbe:
      exec:
        command:
          -	cat
          -	/tmp/healthy

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

spec:
  containers: # 容器配置
    livenessProbe:
      tcpSocket:
         port: 8080

3.httpGet:调用容器内web应用的URL,如果返回的状态码在200和399之前,则认为程序正常,否则不正常。

spec:
  containers: # 容器配置
   livenessProbe:
      httpGet:
         path: / #URI地址
         port: 80 #端口号
         host: 127.0.0.1 #主机地址
         scheme: HTTP #支持的协议,http或者https
实践案例:

创建pod-liveness.yaml文件,分别列举了三种方式,先注释其他两种进行可以逐个实践

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness
  namespace: dev
  labels:
    user: kele
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
      livenessProbe: # 存活性探针
        #1.exec方式
        exec:
          command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令,必须失败,因为根本没有这个文件
        #2.Socket方式
        #tcpSocket:
        # port: 80
        #3.httpGet方式
        #httpGet:
        # path: / #URI地址
        # port: 80 #端口号
        # host: 127.0.0.1 #主机地址
        # scheme: HTTP #支持的协议,http或者https

exec方式

1.创建pod

kubectl create -f pod-liveness.yaml

2. 查看Pod

kubectl get pod -n dev

可以看到虽然pod状态属于正常运行,但是restarts=2.说明启动时再重启,接下来查看详情

3.查看Pod详情

kubectl describe pod pod-liveness -n dev

 重启的原因为nginx容器中,没有hello.txt文件,livenessProbe存活性探针探测失败进行重启

4.正确案例,将yaml中command修改成["/bin/ls","/tmp/"]

livenessProbe: # 存活性探针
        #1.exec方式
        exec:
          command: ["/bin/ls","/tmp/"]

5.删除重建pod

 kubectl delete pod pod-liveness -n dev
kubectl create -f pod-liveness.yaml

容器探测其他配置: 

查看livenessProbe的子属性,会发现除了这三种方式,还有一些其他的配置。

kubectl explain pod.spec.containers.livenessProbe

initialDelaySeconds    # 容器启动后等待多少秒执行第一次探测
timeoutSeconds      # 探测超时时间。默认1秒,最小1秒
periodSeconds       # 执行探测的频率。默认是10秒,最小1秒
failureThreshold    # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold    # 连续探测成功多少次才被认定为成功。默认是1

3.5重启策略(restartPolicy)

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

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

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

apiVersion: v1
kind: Pod
metadata:
  name: pod-restart-policy
  namespace: dev
  labels:
    user: kele
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
      livenessProbe: # 存活性探测
        httpGet:
          port: 80
          path: /hello
          host: 127.0.0.1
          scheme: HTTP
  restartPolicy: Never # 重启策略(默认:Always)

4、Pod的调度

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

  • 自动调度:运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。
  • 定向调度:NodeName、NodeSelector。
  • 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity。
  • 污点(容忍)调度:Taints、Toleration。

4.1定向调度

定向调度,指的是利用在Pod上声明的nodeName(节点名称)或nodeSelector(节点标签),以此将Pod调度到期望的Node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过Pod运行失败而已。

4.1.1nodeName

通过节点名称来选定pod在哪个节点创建,会直接跳过Scheduler的调度逻辑

创建一个pod-nodename.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: dev
  labels:
    user: kele
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeName: node1 # 指定调度到node1节点上

创建pod

kubectl create -f pod-nodename.yaml

查看pod详情

kubectl get pod pod-nodename -n dev -o wide
4.1.2nodeSelector

使用nodeSelector时需要先给node节点添加上标签,该方案通过label-selector机制实现的,在Pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,该匹配规则是强制约束

给node节点添加标签:

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

 创建pod-nodeselector.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeSelector:
    nodeenv: pro # 指定调度到具有nodeenv=pro的Node节点上

创建pod

kubectl create -f pod-nodeselector.yaml

4.2亲和性调度(Affinity)

相对于定向调度,k8s还提供了亲和性调度(Affinity)的方案比较灵活。它在nodeSelector的基础之上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有Node也可以调度到不满足条件的节点上。

Affinity主要分为三类:
  • nodeAffinity(node亲和性):以Node为目标,解决Pod可以调度到那些Node的问题。
  • podAffinity(pod亲和性):以Pod为目标,解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。
  • podAntiAffinity(pod反亲和性):以Pod为目标,解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。

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

  • 亲和性:如果两个应用频繁交互,那么就有必要利用亲和性让两个应用尽可能的靠近,这样可以较少因网络通信而带来的性能损耗。
  • 反亲和性:当应用采用多副本部署的时候,那么就有必要利用反亲和性让各个应用实例打散分布在各个Node上,这样可以提高服务的高可用性。

Pod控制器的详解

在kubernetes中,按照Pod的创建方式可以将其分为两类(一般用控制器创建pod):

  • 自主式Pod:kubernetes直接创建出来的Pod,这种Pod删除后就没有了,也不会重建。
  • 控制器创建Pod:通过Pod控制器创建的Pod,这种Pod删除之后还会自动重建。

 在kubernetes中,有很多类型的Pod控制器,每种都有自己的适合的场景,常见的有下面这些: 

  • ReplicationController:比较原始的Pod控制器,已经被废弃,由ReplicaSet替代。
  • ReplicaSet:保证指定数量的Pod运行,并支持Pod数量变更,镜像版本变更。
  • Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级、版本回退。
  • Horizontal Pod Autoscaler:可以根据集群负载自动调整Pod的数量,实现削峰填谷。
  • DaemonSet:在集群中的指定Node上都运行一个副本,一般用于守护进程类的任务。
  • Job:它创建出来的Pod只要完成任务就立即退出,用于执行一次性任务。
  • CronJob:它创建的Pod会周期性的执行,用于执行周期性的任务。
  • StatefulSet:管理有状态的应用。

 1.ReplicaSet(RS)

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

1.1ReplicaSet的yaml资源清单文件:

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

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

  • replicas:指定副本数量,其实就是当然rs创建出来的Pod的数量,默认为1.
  • selector:选择器,它的作用是建立Pod控制器和Pod之间的关联关系,采用了Label Selector机制(在Pod模块上定义Label,在控制器上定义选择器,就可以表明当前控制器能管理哪些Pod了)。
  • template:模板,就是当前控制器创建Pod所使用的模板,里面其实就是前面学过的Pod的定义。

1.2创建ReplicaSet

创建pc-replicaset.yaml文件

apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型
metadata: # 元数据
  name: pc-replicaset # rs名称
  namespace: dev # 命名类型
spec: # 详细描述
  replicas: 3 # 副本数量
  selector: # 选择器,通过它指定该控制器可以管理哪些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
  template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx # 容器名称
          image: nginx:1.17.1 # 容器需要的镜像地址
          ports:
            - containerPort: 80 # 容器所监听的端口

创建命令

kubectl create -f pc-replicaset.yaml

 查看

 kubectl get rs pc-replicaset -n dev -o wide

查看当前控制器创建出来的Pod

kubectl get pod -n dev

1.3 扩缩容

方式一:编辑rs的副本数量,修改spec:replicas:6

kubectl edit rs pc-replicaset -n dev

方式二:使用scale命令实现扩缩容,后面加上--replicas=n直接指定目标数量

kubectl scale rs pc-replicaset --replicas=2 -n dev

 但以上两种方式都为手动扩缩容 ,实际生产环境的场景中流量激增可能是一瞬间的,需要系统自动检测进行动态扩缩容,请看HPA章节

1.4镜像升级

方式一:编辑rs的容器镜像,修改spec:containers:image为nginx:1.17.2。

kubectl edit rs pc-replicaset -n dev

 方式二:使用set命令实现镜像升级

语法:
kubectl set image rs rs名称 容器名称=镜像版本 -n 命名空间

命令
kubectl set image rs pc-replicaset nginx=nginx:1.17.1 -n dev

1.5删除ReplicaSet

使用kubectl delete rs 命令会删除ReplicaSet和其管理的Pod 

# 在k8s删除ReplicaSet前,会将ReplicaSet的replicas调整为0,等到所有的Pod被删除后,再执行ReplicaSet对象的删除
kubectl delete rs pc-replicaset -n dev

更推荐使用yaml直接删除

kubectl delete -f pc-replicaset.yaml

 2.Deployment(Deploy)

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

Deployment的主要功能如下:

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

 2.1Deployment的yaml资源清单

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

2.2创建Deployment

创建pc-deployment.yaml文件

apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
  name: pc-deployment # deployment的名称
  namespace: dev # 命名类型
spec: # 详细描述
  replicas: 3 # 副本数量
  selector: # 选择器,通过它指定该控制器可以管理哪些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
  template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx # 容器名称
          image: nginx:1.17.1 # 容器需要的镜像地址
          ports:
            - containerPort: 80 # 容器所监听的端口

创建Deployment

kubectl create -f pc-deployment.yaml

 查看Deployment和pod

kubectl get deploy pc-deployment -n dev

kubectl get pod -n dev

2.3扩缩容

使用scale命令实现扩缩容

kubectl scale deploy pc-deployment --replicas=5 -n dev

编辑Deployment的副本数量,修改spec:replicas:4

kubectl edit deployment pc-deployment -n dev

但以上两种方式都为手动扩缩容 ,实际生产环境的场景中流量激增可能是一瞬间的,需要系统自动检测进行动态扩缩容,请看HPA章节

2.4 镜像/容器更新 

Deployment支持两种镜像更新的策略:重建更新滚动更新(默认),可以通过strategy选项进行配置。

配置说明:

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

在创建出新的Pod之前会先杀掉所有已经存在的Pod

编辑pc-deployment.yaml文件,在spec节点下添加更新策略

apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
  name: pc-deployment-recreate # deployment的名称
  namespace: dev # 命名类型
spec: # 详细描述
  replicas: 3 # 副本数量
  strategy: # 镜像更新策略
    type: Recreate # Recreate:在创建出新的Pod之前会先杀掉所有已经存在的Pod
  selector: # 选择器,通过它指定该控制器可以管理哪些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
  template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx # 容器名称
          image: nginx:1.17.1 # 容器需要的镜像地址
          ports:
            - containerPort: 80 # 容器所监听的端口

升级nginx镜像 ,同时node节点的本地镜像仓库中也下载了新版的nginx

kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev

 查看升级过程

kubectl get pod -n dev -w

2.4.2滚动更新 

 编辑pc-deployment-rollingupdate.yaml文件,在spec节点下添加更新策略

apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
  name: pc-deployment-rollingupdate # deployment的名称
  namespace: dev # 命名类型
spec: # 详细描述
  replicas: 3 # 副本数量
  strategy: # 镜像更新策略
    type: RollingUpdate # RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  selector: # 选择器,通过它指定该控制器可以管理哪些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
  template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx # 容器名称
          image: nginx:1.17.1 # 容器需要的镜像地址
          ports:
            - containerPort: 80 # 容器所监听的端口

 镜像升级

kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev

 查看升级过程

kubectl get pod -n dev -w

滚动更新的过程:

 镜像更新中rs的变化

# 查看rs,发现原来的rs依旧存在,只是Pod的数量变为0,而后又产生了一个rs,Pod的数量变为3
# 其实这就是deployment能够进行版本回退的奥妙所在
kubectl get rs -n dev

2.5版本回退 

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

# 版本升级相关功能
kubetl rollout [参数] deploy xx  # 支持下面的选择
# status 显示当前升级的状态
# history 显示升级历史记录
# pause 暂停版本升级过程
# resume 继续已经暂停的版本升级过程
# restart 重启版本升级过程
# undo 回滚到上一级版本 (可以使用--to-revision回滚到指定的版本)

查看当前升级版本的状态

kubectl rollout status deployment pc-deployment-rollingupdate -n dev

查看升级历史记录

 kubectl rollout history deployment pc-deployment-rollingupdate -n dev

版本回退

# 可以使用-to-revision=1回退到1版本,如果省略这个选项,就是回退到上个版本,即2版本
kubectl rollout undo deployment pc-deployment-rollingupdate --to-revision=1 -n dev

deployment之所以能够实现版本的回退,就是通过记录下历史的ReplicaSet来实现的,一旦想回滚到那个版本,只需要将当前版本的Pod数量降为0,然后将回退版本的Pod提升为目标数量即可。 

2.6金丝雀发布 

  • Deployment支持更新过程中的控制,如暂停更新操作(pause)或继续更新操作(resume)。
  • 例如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求到新版本的Pod应用,继续观察能够稳定的按照期望的方式运行,如果没有问题之后再继续完成余下的Pod资源的滚动更新,否则立即回滚操作。

更新Deployment的版本,并配置暂停Deployment:

[root@master ~]# kubectl set image deployment pc-deployment-rollingupdate nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment-rollingupdate -n dev
  • 说明:先下载并创建nginx新版本pod,并且使用pause命令创建一个新pod后先暂停版本升级过程,旧的三个pod仍然在正常运行,等待用resume命令继续执行 

查看暂停升级过程吗,在没有执行resume命令之前只创建了一个新pod

 确保更新的Pod没问题之后,继续更新:

kubectl rollout resume deployment pc-deployment-rollingupdate -n dev

全量升级后只留下3个pod,会自动删除一个旧pod

3.Horizontal Pod Autoscaler(HPA) 

已经可以通过手动执行kubectl scale命令实现Pod的扩缩容,但是这显然不符合k8s的定位目标–自动化和智能化。k8s期望可以通过监测Pod的使用情况,实现Pod数量的自动调整,于是就产生了HPA这种控制器。

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

 

3.1安装metrics-server(v0.3.6) 

metrics-server可以用来收集集群中的资源使用情况,监视pod负载量

获取metrics-server,需要注意使用的版本

wget https://github.com/kubernetes-sigs/metrics-server/archive/v0.3.6.tar.gz

解压v0.3.6.tar.gz文件

tar -zxvf v0.3.6.tar.gz

修改metrics-server-0.3.6/deploy/1.8+/metrics-server-deployment.yaml文件

vim metrics-server-0.3.6/deploy/1.8+/metrics-server-deployment.yaml
#添加下面选项
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6 
args:
  - --kubelet-insecure-tls 
  - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP

修改时注意使用空格键,tab会报错 

安装metrics-server 

kubectl apply -f ./

 查看metrics-server生成的Pod:

kubectl get pod -n kube-system

 查看资源使用情况

kubectl top node


kubectl top pod -n kube-system

如果出现异常:Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io),请查看:解决方案
 

3.2准备Deployment和Service

创建nginx.yaml文件

apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
  name: nginx # deployment的名称
  namespace: dev # 命名类型
spec: # 详细描述
  selector: # 选择器,通过它指定该控制器可以管理哪些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
  template: # 模块 当副本数据不足的时候,会根据下面的模板创建Pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx # 容器名称
          image: nginx:1.17.1 # 容器需要的镜像地址
          ports:
            - containerPort: 80 # 容器所监听的端口
          resources: # 资源限制
            requests:
              cpu: "100m" # 100m表示100millicpu,即0.1个CPU

执行创建Deployment 

kubectl create -f nginx.yaml

查看Deployment和Pod

kubectl get pod,deploy -n dev

创建Service

kubectl expose deployment nginx --name=nginx --type=NodePort --port=80 --target-port=80 -n dev

查看Service

kubectl get svc -n dev

3.3部署HPA

创建pc-hpa.yaml文件

apiVersion: autoscaling/v1 # 版本号
kind: HorizontalPodAutoscaler # 类型
metadata: # 元数据
  name: pc-hpa # deployment的名称
  namespace: dev # 命名类型
spec:
  minReplicas: 1 # 最小Pod数量
  maxReplicas: 10 # 最大Pod数量
  targetCPUUtilizationPercentage: 3 # CPU使用率指标
  scaleTargetRef:  # 指定要控制的Nginx的信息
    apiVersion: apps/v1
    kind: Deployment
    name: nginx

 创建hpa

kubectl create -f pc-hpa.yaml

查看

kubectl get hpa -n dev
 4.4测试

存在问题:教程访问的时候master节点ip,当前就群中无法通过master节点访问

使用测试工具进行压测 postman、apifox、Jmeter都可以访问node节点,设置请求数10000http://192.168.79.182:30184/

观察hpa的变化 

kubectl get hpa -n dev -w

观察Deployment的变化,扩容之后当经历一段时间访问压力没那么大之后,就会进行缩容,可以看到图中最后又退回成1个nginx容器

 

观察pod变化

kubectl get pod -n dev -w

 

Service详解 

Service介绍

k8s提供了Service资源,Service会对提供同一个服务的多个Pod进行聚合,并且提供一个统一固定ip入口地址,通过访问Service的入口地址就能访问到后面的Pod服务。

Service类型

spec.type的说明:

  • ClusterIP:默认值,它是kubernetes系统自动分配的虚拟IP,只能在集群内部访问。
  • NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务。
  • LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境的支持。
  • ExternalName:把集群外部的服务引入集群内部,直接使用。

Ingress介绍

Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点:

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

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

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

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

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

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

 Ingress实践

后续更新....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值