• 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,默认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: > #设置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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    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 根据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>      # 资源限制和资源请求的设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    案例

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

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-base
      namespace: dev
      labels:
        user: test
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      - name: busybox
        image: busybox:1.30
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-imagepullpolicy
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        imagePullPolicy: Never # 用于设置镜像拉取策略
      - name: busybox
        image: busybox:1.30
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

    默认值说明:

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

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

    启动命令

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

    在这里插入图片描述

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-command
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      - 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;"] #执行cmd命令使得容器有进程在运行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    特别说明:
    通过上面发现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-env
      namespace: dev
    spec:
      containers:
      - name: busybox
        image: busybox:1.30
        command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]
        env: # 设置环境变量列表
        - name: "username"
          value: "admin"
        - name: "password"
          value: "123456"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    进入容器测试

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

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

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

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-ports
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports: # 设置容器暴露的端口列表
        - name: nginx-port
          containerPort: 80
          protocol: TCP
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    资源配额

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

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

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-resources
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        resources: # 资源配额
          limits:  # 限制资源(上限)
            cpu: "2" # CPU限制,单位是core数
            memory: "10Gi" # 内存限制
          requests: # 请求资源(下限)
            cpu: "1"  # CPU限制,单位是core数
            memory: "10Mi"  # 内存限制
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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-initcontainer
      namespace: dev
    spec:
      containers:
      - name: main-container
        image: nginx:1.17.1
        ports: 
        - name: nginx-port
          containerPort: 80
      initContainers:
      - name: test-mysql
        image: busybox:1.30
        command: ['sh', '-c', 'until ping 192.168.10.101 -c 1 ; do echo waiting for mysql...; sleep 2; done;']
      - name: test-redis
        image: busybox:1.30
        command: ['sh', '-c', 'until ping 192.168.10.102 -c 1 ; do echo waiting for reids...; sleep 2; done;']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    钩子函数

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

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

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

    Exec命令

    在容器内执行一次命令

      lifecycle:
        postStart: 
          exec:
            command:
            - cat
            - /tmp/healthy
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    TCPSocket

    在当前容器尝试访问指定的socket

      lifecycle:
        postStart:
          tcpSocket:
            port: 8080
    
    • 1
    • 2
    • 3
    • 4
    HTTPGet

    在当前容器中向某url发起http请求

      lifecycle:
        postStart:
          httpGet:
            path: / #URI地址
            port: 80 #端口号
            host: 192.168.5.3 #主机地址
            scheme: HTTP #支持的协议,http或者https
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    案例

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-hook-exec
      namespace: dev
    spec:
      containers:
      - name: main-container
        image: nginx:1.17.1
        ports:
        - name: nginx-port
          containerPort: 80
        lifecycle:
          postStart: 
            exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容
              command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
          preStop:
            exec: # 在容器停止之前停止nginx服务
              command: ["/usr/sbin/nginx","-s","quit"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行命令测试

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

    容器探测用于检测容器中的应用是否正常工作,是保障业务可用性的一种传统机制,如果结果探测实例的状态不符合预期,那么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。最小值是1
       successThreshold     <integer>  # 连续探测成功多少次才被认定为成功。默认是1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    Exec命令

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

      livenessProbe:
        postStart: 
          exec:
            command:
            - cat
            - /tmp/healthy
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    TCPSocket

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

      livenessProbe:
        postStart:
          tcpSocket:
            port: 8080
    
    • 1
    • 2
    • 3
    • 4
    HTTPGet

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

      livenessProbe:
        postStart:
          httpGet:
            path: / #URI地址
            port: 80 #端口号
            host: 192.168.5.3 #主机地址
            scheme: HTTP #支持的协议,http或者https
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    案例

    创建pod-liveness-exec.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-liveness-exec
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports: 
        - name: nginx-port
          containerPort: 80
        livenessProbe:
          exec:
            command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    由于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-restartpolicy
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - name: nginx-port
          containerPort: 80
        livenessProbe:
          httpGet:
            scheme: HTTP
            port: 80
            path: /hello
      restartPolicy: Never # 设置重启策略为Never
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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-nodename
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      nodeName: node1 # 指定调度到node1节点上
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    执行命令测试

    #创建Pod
    kubectl create -f pod-nodename.yaml
    #查看Pod调度到NODE属性,确实是调度到了node1节点上
    kubectl get pods pod-nodename -n dev -o wide   
    
    • 1
    • 2
    • 3
    • 4
    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
    
    • 1
    • 2

    创建一个pod-nodeselector.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeselector
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      nodeSelector: 
        nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    执行命令测试

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

    在上面的定向调度使用过程中发现存在一个缺点,假如调度的目标不存在那么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.nodeAffinity
      requiredDuringSchedulingIgnoredDuringExecution  Node节点必须满足指定的所有规则才可以,相当于硬限制
        nodeSelectorTerms  节点选择列表
          matchFields   按节点字段列出的节点选择器要求列表
          matchExpressions   按节点标签列出的节点选择器要求列表(推荐)
            key    键
            values 值
            operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
      preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
        preference   一个节点选择器项,与相应的权重相关联
          matchFields   按节点字段列出的节点选择器要求列表
          matchExpressions   按节点标签列出的节点选择器要求列表(推荐)
            key    键
            values 值
            operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
    	weight 倾向权重,在范围1-100。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    关系符的使用说明:
    
    - matchExpressions:
      - key: nodeenv              # 匹配存在标签的key为nodeenv的节点
        operator: Exists
      - key: nodeenv              # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
        operator: In
        values: ["xxx","yyy"]
      - key: nodeenv              # 匹配标签的key为nodeenv,且value大于"xxx"的节点
        operator: Gt
        values: "xxx"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    硬限制

    使用requiredDuringSchedulingIgnoredDuringExecution

    创建pod-nodeaffinity-required.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeaffinity-required
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      affinity:  #亲和性设置
        nodeAffinity: #设置node亲和性
          requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
            nodeSelectorTerms:
            - matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签
              - key: nodeenv
                operator: In
                values: ["xxx","yyy"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    软限制

    使用preferredDuringSchedulingIgnoredDuringExecution

    创建pod-nodeaffinity-preferred.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeaffinity-preferred
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      affinity:  #亲和性设置
        nodeAffinity: #设置node亲和性
          preferredDuringSchedulingIgnoredDuringExecution: # 软限制
          - weight: 1
            preference:
              matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签(当前环境没有)
              - key: nodeenv
                operator: In
                values: ["xxx","yyy"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行命令测试

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

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

    podAffinity

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

    pod.spec.affinity.podAffinity
      requiredDuringSchedulingIgnoredDuringExecution  硬限制
        namespaces       指定参照pod的namespace
        topologyKey      指定调度作用域
        labelSelector    标签选择器
          matchExpressions  按节点标签列出的节点选择器要求列表(推荐)
            key    键
            values 值
            operator 关系符 支持In, NotIn, Exists, DoesNotExist.
          matchLabels    指多个matchExpressions映射的内容
      preferredDuringSchedulingIgnoredDuringExecution 软限制
        podAffinityTerm  选项
          namespaces      
          topologyKey
          labelSelector
            matchExpressions  
              key    键
              values 值
              operator
            matchLabels 
        weight 倾向权重,在范围1-100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    硬限制

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-podaffinity-target
      namespace: dev
      labels:
        podenv: pro #设置标签
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      nodeName: node1 # 将目标pod名确指定到node1上
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行命令启动

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

    创建pod-podaffinity-required.yaml

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-podaffinity-required
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      affinity:  #亲和性设置
        podAffinity: #设置pod亲和性
          requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
          - labelSelector:
              matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签
              - key: podenv
                operator: In
                values: ["xxx","yyy"]
            topologyKey: kubernetes.io/hostname
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行命令测试

    # 启动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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    软限制

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

    podAntiAffinity

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

    硬限制

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

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

    创建pod-podantiaffinity-required.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-podantiaffinity-required
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      affinity:  #亲和性设置
        podAntiAffinity: #设置pod亲和性
          requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
          - labelSelector:
              matchExpressions: # 匹配podenv的值在["pro"]中的标签
              - key: podenv
                operator: In
                values: ["pro"]
            topologyKey: kubernetes.io/hostname
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行命令测试

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

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

    污点(容忍)调度
    污点(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-
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    容忍(Toleration)

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

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

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

    容忍某个污点yaml配置如下

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-toleration
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
      tolerations:      # 添加容忍
      - key: "tag"        # 要容忍的污点的key
        operator: "Equal" # 操作符
        value: "test"    # 容忍的污点的value
        effect: "NoExecute"   # 添加容忍的规则,这里必须和标记的污点规则相同
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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: # 选择器,通过它指定该控制器管理哪些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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里面,需要新了解的配置项就是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-replicaset
      namespace: dev
    spec:
      replicas: 3
      selector: 
        matchLabels:
          app: nginx-pod
      template:
        metadata:
          labels:
            app: nginx-pod
        spec:
          containers:
          - name: nginx
            image: nginx:1.17.1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    扩缩容
    # 方式一:
    # 编辑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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    镜像升级
    # 方式一:
    # 编辑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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    删除
    #方式一:
    # 使用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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    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 # 暂停部署,默认是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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    创建

    创建pc-deployment.yaml文件

    apiVersion: apps/v1
    kind: Deployment      
    metadata:
      name: pc-deployment
      namespace: dev
    spec: 
      replicas: 3
      selector:
        matchLabels:
          app: nginx-pod
      template:
        metadata:
          labels:
            app: nginx-pod
        spec:
          containers:
          - name: nginx
            image: nginx:1.17.1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    扩缩容
    #方式一:
    # 变更副本数量为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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    镜像更新

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

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

    重建更新

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

    编辑pc-deployment.yaml

    spec:
      strategy: # 策略
        type: Recreate # 重建更新
    
    • 1
    • 2
    • 3

    执行命令测试

    # 更新配置
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    滚动更新

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

    编辑pc-deployment.yaml

    spec:
      strategy: # 策略
        type: RollingUpdate # 滚动更新策略
        rollingUpdate:
          maxSurge: 25% 
          maxUnavailable: 25%
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行命令测试

    # 更新配置
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在镜像更新时通过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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    金丝雀发布

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    删除
    kubectl delete -f pc-deployment.yaml
    
    • 1
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    准备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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    部署HPA

    创建pc-hpa.yaml文件

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
      name: pc-hpa
      namespace: dev
    spec:
      minReplicas: 1  #最小pod数量
      maxReplicas: 10 #最大pod数量
      targetCPUUtilizationPercentage: 3 # CPU使用率指标3%
      scaleTargetRef:   # 指定要控制的nginx信息
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    执行命令测试

    # 创建hpa
    kubectl create -f pc-hpa.yaml
    # 查看hpa
    kubectl get hpa -n dev
    
    • 1
    • 2
    • 3
    • 4

    使用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: # 选择器,通过它指定该控制器管理哪些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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    创建

    创建pc-daemonset.yaml

    apiVersion: apps/v1
    kind: DaemonSet      
    metadata:
      name: pc-daemonset
      namespace: dev
    spec: 
      selector:
        matchLabels:
          app: nginx-pod
      template:
        metadata:
          labels:
            app: nginx-pod
        spec:
          containers:
          - name: nginx
            image: nginx:1.17.1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    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的次数。默认值: 1
      parallelism: 1 # 指定job在任一时刻应该并发运行Pods的数量。默认值: 1
      activeDeadlineSeconds: 30 # 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
      backoffLimit: 6 # 指定job失败后进行重试的次数。默认是6
      manualSelector: true # 是否可以使用selector选择器选择pod,默认是false
      selector: # 选择器,通过它指定该控制器管理哪些pod
        matchLabels:      # Labels匹配规则
          app: counter-pod
        matchExpressions: # Expressions匹配规则
          - {key: app, operator: In, values: [counter-pod]}
      template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
        metadata:
          labels:
            app: counter-pod
        spec:
          restartPolicy: Never # 重启策略只能设置为Never或者OnFailure
          containers:
          - name: counter
            image: busybox:1.30
            command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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-job
      namespace: dev
    spec:
      manualSelector: true
      selector:
        matchLabels:
          app: counter-pod
      template:
        metadata:
          labels:
            app: counter-pod
        spec:
          restartPolicy: Never
          containers:
          - name: counter
            image: busybox:1.30
            command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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: # 为失败的任务执行保留的历史记录数,默认为1
      successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3
      startingDeadlineSeconds: # 启动作业错误的超时时长
      jobTemplate: # job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义
        metadata:
        spec:
          completions: 1
          parallelism: 1
          activeDeadlineSeconds: 30
          backoffLimit: 6
          manualSelector: true
          selector:
            matchLabels:
              app: counter-pod
            matchExpressions: 规则
              - {key: app, operator: In, values: [counter-pod]}
          template:
            metadata:
              labels:
                app: counter-pod
            spec:
              restartPolicy: Never 
              containers:
              - name: counter
                image: busybox:1.30
                command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

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

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

    创建

    创建pc-cronjob.yaml

    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: pc-cronjob
      namespace: dev
      labels:
        controller: cronjob
    spec:
      schedule: "*/1 * * * *"
      jobTemplate:
        metadata:
        spec:
          template:
            spec:
              restartPolicy: Never
              containers:
              - name: counter
                image: busybox:1.30
                command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行命令测试

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Service类型

    Service的yaml配置清单

    kind: Service  # 资源类型
    apiVersion: v1  # 资源版本
    metadata: # 元数据
      name: service # 资源名称
      namespace: dev # 命名空间
    spec: # 描述
      selector: # 标签选择器,用于确定当前service代理哪些pod
        app: nginx
      type: # Service类型,指定service的访问方式
      clusterIP:  # 虚拟服务的ip地址
      sessionAffinity: # session亲和性,支持ClientIP、None两个选项
      ports: # 端口信息
        - protocol: TCP 
          port: 3017  # service端口
          targetPort: 5003 # pod端口
          nodePort: 31122 # 主机端口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 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-deployment
      namespace: dev
    spec: 
      replicas: 3
      selector:
        matchLabels:
          app: nginx-pod
      template:
        metadata:
          labels:
            app: nginx-pod
        spec:
          containers:
          - name: nginx
            image: nginx:1.17.1
            ports:
            - containerPort: 80
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行命令

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

    在不同的机器上使用命令进入容器,修改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
    exit
    
    kubectl exec -it pc-deployment-6696798b78-f7w2g -n dev /bin/sh
    echo "nginx IP => 10.244.1.142" > /usr/share/nginx/html/index.html
    exit
    
    kubectl exec -it pc-deployment-6696798b78-q9dqw -n dev /bin/sh
    echo "nginx IP => 10.244.1.141" > /usr/share/nginx/html/index.html
    exit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    测试是否能够正常访问

    curl 10.244.2.43
    curl 10.244.1.142
    curl 10.244.1.141
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    ClusterIP类型Service

    创建service-clusterip.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: service-clusterip
      namespace: dev
    spec:
      selector:
        app: nginx-pod
      clusterIP: 10.97.97.97 # service的ip地址,如果不写,默认会生成一个
      type: ClusterIP
      ports:
      - port: 80  # Service端口       
        targetPort: 80 # pod端口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    执行如下命令

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

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

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

    在这里插入图片描述

    负载分发策略

    对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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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-headliness
      namespace: dev
    spec:
      selector:
        app: nginx-pod
      clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
      type: ClusterIP
      ports:
      - port: 80    
        targetPort: 80
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    执行如下命令

    # 创建service
    kubectl  create -f service-headliness.yaml
    
    • 1
    • 2

    创建成功后因为通过域名访问就需要域名解析器,随便进入一台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-nodeport
      namespace: dev
    spec:
      selector:
        app: nginx-pod
      type: NodePort # service类型
      ports:
      - port: 80
        nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配
        targetPort: 80
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    执行如下命令

    # 创建service
    kubectl create -f service-nodeport.yaml
    # 查看service
    kubectl get svc -n dev -o wide
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    LoadBalancer类型的Service

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

    ExternalName类型的Service

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

    apiVersion: v1
    kind: Service
    metadata:
      name: service-externalname
      namespace: dev
    spec:
      type: ExternalName # service类型
      externalName: www.baidu.com  #改成ip地址也可以
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    # 创建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.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    准备service和pod

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

    在这里插入图片描述

    创建nginx-tomcat.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      namespace: dev
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx-pod
      template:
        metadata:
          labels:
            app: nginx-pod
        spec:
          containers:
          - name: nginx
            image: nginx:1.17.1
            ports:
            - containerPort: 80
    
    ---
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: tomcat-deployment
      namespace: dev
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: tomcat-pod
      template:
        metadata:
          labels:
            app: tomcat-pod
        spec:
          containers:
          - name: tomcat
            image: tomcat:8.5-jre10-slim
            ports:
            - containerPort: 8080
    
    ---
    
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
      namespace: dev
    spec:
      selector:
        app: nginx-pod
      clusterIP: None
      type: ClusterIP
      ports:
      - port: 80
        targetPort: 80
    
    ---
    
    apiVersion: v1
    kind: Service
    metadata:
      name: tomcat-service
      namespace: dev
    spec:
      selector:
        app: tomcat-pod
      clusterIP: None
      type: ClusterIP
      ports:
      - port: 8080
        targetPort: 8080
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    执行如下命令

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

    创建ingress-http.yaml

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-http
      namespace: dev
    spec:
      rules:
      - host: nginx.test.com
        http:
          paths:
          - path: /
            backend:
              serviceName: nginx-service
              servicePort: 80
      - host: tomcat.test.com
        http:
          paths:
          - path: /
            backend:
              serviceName: tomcat-service
              servicePort: 8080
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行如下命令

    # 创建
    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 查看效果了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    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
    
    • 1
    • 2
    • 3
    • 4

    创建ingress-https.yaml

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-https
      namespace: dev
    spec:
      tls:
        - hosts:
          - nginx.test.com
          - tomcat.test.com
          secretName: tls-secret # 指定秘钥
      rules:
      - host: nginx.test.com
        http:
          paths:
          - path: /
            backend:
              serviceName: nginx-service
              servicePort: 80
      - host: tomcat.test.com
        http:
          paths:
          - path: /
            backend:
              serviceName: tomcat-service
              servicePort: 8080
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    执行如下命令

    # 创建
    kubectl create -f ingress-https.yaml
    # 查看
    kubectl get ing ingress-https -n dev
    # 查看详情
    kubectl describe ing ingress-https -n dev
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    数据存储

    在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,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-emptydir
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
        volumeMounts:  # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx
        - name: logs-volume
          mountPath: /var/log/nginx
      - name: busybox
        image: busybox:1.30
        command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容
        volumeMounts:  # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs
        - name: logs-volume
          mountPath: /logs
      volumes: # 声明volume, name为logs-volume,类型为emptyDir
      - name: logs-volume
        emptyDir: {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    执行如下命令

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    HostPath

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

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

    在这里插入图片描述

    创建一个volume-hostpath.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: volume-hostpath
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: logs-volume
          mountPath: /var/log/nginx
      - name: busybox
        image: busybox:1.30
        command: ["/bin/sh","-c","tail -f /logs/access.log"]
        volumeMounts:
        - name: logs-volume
          mountPath: /logs
      volumes:
      - name: logs-volume
        hostPath: 
          path: /root/logs
          type: DirectoryOrCreate  # 目录存在就使用,不存在就先创建后使用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    关于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/
    # 同样的道理,如果在此目录下创建一个文件,到容器中也是可以看到的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在node上安装nfs工具

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

    创建pod配置文件

    创建volume-nfs.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: volume-nfs
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: logs-volume
          mountPath: /var/log/nginx
      - name: busybox
        image: busybox:1.30
        command: ["/bin/sh","-c","tail -f /logs/access.log"] 
        volumeMounts:
        - name: logs-volume
          mountPath: /logs
      volumes:
      - name: logs-volume
        nfs:
          server: 192.168.10.100  #nfs服务器地址
          path: /root/data/nfs #共享文件路径
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    执行如下命令

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

    高级存储

    前面已经学习了使用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: 2Gi
      accessModes:  # 访问模式
      storageClassName: # 存储类别
      persistentVolumeReclaimPolicy: # 回收策略
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    参数说明
    • 存储类型

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

    • 存储能力(capacity)

       目前只支持存储空间的设置( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置
      
      • 1
    • 访问模式(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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建yaml

    创建pv.yaml

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name:  pv1
    spec:
      capacity: 
        storage: 1Gi
      accessModes:
      - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      nfs:
        path: /root/data/pv1
        server: 192.168.10.100
    
    ---
    
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name:  pv2
    spec:
      capacity: 
        storage: 2Gi
      accessModes:
      - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      nfs:
        path: /root/data/pv2
        server: 192.168.10.100
        
    ---
    
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name:  pv3
    spec:
      capacity: 
        storage: 3Gi
      accessModes:
      - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      nfs:
        path: /root/data/pv3
        server: 192.168.10.100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    执行如下命令

    # 创建 pv
    kubectl create -f pv.yaml
    # 查看pv
    kubectl get pv -o wide
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    PVC

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

    PVC的yaml配置清单

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc
      namespace: dev
    spec:
      accessModes: # 访问模式
      selector: # 采用标签对PV选择
      storageClassName: # 存储类别
      resources: # 请求空间
        requests:
          storage: 5Gi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    参数说明
    • 访问模式(accessModes)

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

    • 选择条件(selector)

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

    • 存储类别(storageClassName)

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

    • 资源请求(Resources )

      描述对存储资源的请求

    PV绑定PVC

    创建pvc.yaml,申请PV

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc1
      namespace: dev
    spec:
      accessModes: 
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc2
      namespace: dev
    spec:
      accessModes: 
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc3
      namespace: dev
    spec:
      accessModes: 
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    # 创建pvc
    kubectl create -f pvc.yaml
    # 查看pvc
    kubectl get pvc  -n dev -o wide
    # 查看pv
    kubectl get pv -o wide
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    Pod使用PV

    创建pods.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod1
      namespace: dev
    spec:
      containers:
      - name: busybox
        image: busybox:1.30
        command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
        volumeMounts:
        - name: volume
          mountPath: /root/
      volumes:
        - name: volume
          persistentVolumeClaim:
            claimName: pvc1
            readOnly: false
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod2
      namespace: dev
    spec:
      containers:
      - name: busybox
        image: busybox:1.30
        command: ["/bin/sh","-c","while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]
        volumeMounts:
        - name: volume
          mountPath: /root/
      volumes:
        - name: volume
          persistentVolumeClaim:
            claimName: pvc2
            readOnly: false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    执行如下命令

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    生命周期

    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: configmap
      namespace: dev
    data:
      info: |
        username:admin
        password:123456
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    # 创建configmap
    kubectl create -f configmap.yaml
    # 查看configmap详情
    kubectl describe cm configmap -n dev
    
    • 1
    • 2
    • 3
    • 4

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-configmap
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        volumeMounts: # 将configmap挂载到目录
        - name: config
          mountPath: /configmap/config
      volumes: # 引用configmap
      - name: config
        configMap:
          name: configmap
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行如下命令

    # 创建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的内容, 容器中的值也会动态更新
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    Secret

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

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

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

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

    apiVersion: v1
    kind: Secret
    metadata:
      name: secret
      namespace: dev
    type: Opaque
    data:
      username: YWRtaW4=
      password: MTIzNDU2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行如下命令

    # 创建secret
    kubectl create -f secret.yaml
    # 查看secret详情
    kubectl describe secret secret -n dev
    
    • 1
    • 2
    • 3
    • 4

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

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-secret
      namespace: dev
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        volumeMounts: # 将secret挂载到目录
        - name: config
          mountPath: /secret/config
      volumes:
      - name: config
        secret:
          secretName: secret
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行如下命令

    # 创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    安全认证

    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机构下发根证书、服务端证书及私钥给申请者
      
      • 1
    2. 客户端和服务端的双向认证

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

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

    注意: 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: dev
      name: authorization-role
    rules:
    - apiGroups: [""]  # 支持的API组列表,"" 空字符串,表示核心API群
      resources: ["pods"] # 支持的资源对象列表
      verbs: ["get", "watch", "list"] # 允许的对资源对象的操作方法列表
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    # ClusterRole可以对集群范围内资源、跨namespaces的范围资源、非资源类型进行授权
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
     name: authorization-clusterrole
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "watch", "list"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    • apiGroups: 支持的API组列表

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

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

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

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

    # RoleBinding可以将同一namespace中的subject绑定到某个Role下,则此subject即具有该Role定义的权限
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      name: authorization-role-binding
      namespace: dev
    subjects:
    - kind: User
      name: test
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: Role
      name: authorization-role
      apiGroup: rbac.authorization.k8s.io
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    # ClusterRoleBinding在整个集群级别和所有namespaces将特定的subject与ClusterRole绑定,授予权限
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
     name: authorization-clusterrole-binding
    subjects:
    - kind: User
      name: test
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: authorization-clusterrole
      apiGroup: rbac.authorization.k8s.io
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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-ns
      namespace: dev
    subjects:
    - kind: User
      name: test
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: authorization-clusterrole
      apiGroup: rbac.authorization.k8s.io
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    案例

    在前面演示时我们都使用的是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:6443
    
    kubectl config set-credentials devman --embed-certs=true --client-certificate=/etc/kubernetes/pki/devman.crt --client-key=/etc/kubernetes/pki/devman.key
    
    kubectl 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

    创建dev-role.yaml

    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      namespace: dev
      name: dev-role
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "watch", "list"]
      
    ---
    
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      name: authorization-role-binding
      namespace: dev
    subjects:
    - kind: User
      name: devman
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: Role
      name: dev-role
      apiGroup: rbac.authorization.k8s.io
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    执行如下命令

    kubectl create -f dev-role.yaml
    
    • 1

    测试

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

    准入控制

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

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

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

    只有当所有的准入控制器都检查通过之后,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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

    在这里插入图片描述

    创建访问账户,获取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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    在这里插入图片描述

  • 相关阅读:
    uniapp 实现拍照 相册选取选视频和照片
    产业互联网只有找到富有自身鲜明特色的实现方案,才能真正成就自我
    ZYNQ使用AXI4-HP接口总线读取DDR中的数据
    生产者消费者模型
    【算法】动态规划
    二.STM32F030C8T6 MCU开发之 NVIC中断配置
    高仿Android网易云音乐OkHttp+Retrofit+RxJava+Glide+MVC+MVVM
    垃圾分类热词获取易语言代码
    计算机中丢失VCRUNTIME140_1,总结vcruntime140_1.dll的五种修复方法
    kali下使用docker建立dvwa靶机
  • 原文地址:https://blog.csdn.net/weixin_44642403/article/details/134008999