引言: 补充上篇

在这里插入图片描述

一、资源

概念:

在我们深入了解Kubernetes资源之前,不妨先阐述一下“资源”一词在这里指什么。我们在Kubernetes集群中创建的任何东西都被视为资源:部署(deployment)、pod和服务等

集群管理的一个方面是将这些资源自动分配给在pod中运行的容器,那样在理想情况下,每个容器都有它所需的资源,不多也不少。

本文侧重介绍集群上运行的容器的逻辑资源。开发人员每天处理的常见Kubernetes资源分这四种:CPU、内存、暂态存储和扩展资源。针对每种资源,我们将介绍如何在Kubernetes中衡量它、介绍如何监控每种特定资源,并强调优化资源使用的一些最佳实践。

不妨深入探讨每一种主要的Kubernetes资源类型。

二、CPU

Kubernetes集群通常运行在多台机器上,每台机器都有多个CPU核心,组合起来就是可用核心的总数,比如4台机器乘以4个核心,核心总数为16个。

我们不需要使用全部数量的核心。我们可以以1/1000的增量指定CPU核心的任何部分(比如,半个核心或500毫核CPU)。

Kubernetes容器在Linux内核上运行,因此可以指定cGroup以限制资源。Linux调度程序将使用的CPU时间(由内部时间片定义)与设定的限制进行比较,以决定是否在下一个时间片中运行容器。我们可以使用kubectl top命令来查询CPU资源,为pod或节点调用它。

我们可以通过改进算法和编码,或者通过编译器优化,使程序在容器中运行起来更高效,从而优化处理器时间的使用。集群用户对预编译容器的速度或效率并没有太大影响。

三、内存

Kubernetes集群中的机器也都有内存,加起来就是集群内存总数。比如说,4台机器乘以32GiB就是128GiB。

内核级控制主内存,类似使用cGroup控制CPU时间。如果容器中的例程请求的内存分配量超出硬性限制,就会显示内存不足错误。

优化资源使用在很大程度上取决于应用程序的开发工作。一个步骤是提高垃圾收集频率,以防止基于堆的镜像分配的内存超过硬性限制。同样,kubectl top命令可以提供有关内存使用的信息。

四、静态存储

另外一种Kubernetes资源类型是暂态存储。这是在pod生命周期结束后不会存活的挂载存储。Kubernetes经常使用暂态存储来缓存或日志,但从不将其用于重要数据,比如用户记录。我们可以像对待主内存那样请求或限制暂态存储,但它常常不是同样受限制的资源。

五、扩展资源

集群用户还可以使用扩展资源类型来定义自己的资源类型(按照集群或节点)。一旦我们定义了类型,并指定了可用单元,就可以使用请求和限制,就像我们使用的内置资源一样。

六、资源请求与限制

注意,资源请求和限制是我们讨论的暂态存储和扩展资源的关键。这是由于最终用户可以在应用程序的部署清单中指定资源请求和限制,该清单对Kubernetes应如何处理容器或pod明确了一些规则。

请求表明了容器应该拥有多少资源。它们帮助调度程序根据请求的资源量和那些节点上的可用资源,将pod分配给节点。

限制用于表明对容器可以使用多少资源设置了硬性上限,在操作系统层面执行。请求和限制是可选的,但如果我们不指定限制,容器可以使用节点的大部分资源,这可能会带来负面的成本或性能影响。因此,我们须谨慎行事。

请记住:虽然一个pod可能包含多个容器,但通常每个pod只有一个容器。我们将资源分配给容器,但pod的所有容器耗用节点层面的公共资源池。

七、考虑服务质量

到目前为止我们描述的资源系统是管理计算资源的一种相当简单的方法。Kubernetes还提供了简单的服务质量(QoS)系统。

QoS描述了技术系统在硬件有限的情况下,在保持最佳总体质量的同时提供不同服务级别的方法。Kubernetes QoS系统为pod分配三个级别中的一个:有保证(Guaranteed)、突发式(Burstable)和尽力(BestEffort)。参阅Kubernetes说明文档(https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/),了解如何分配这些级别及它们如何影响pod调度。

在pod的生命周期内,Guaranteed级别提供了请求和限制的资源,不多也不少,适合在恒定负载下运行的监控系统等应用。

Burstable服务级别适合基本使用模式的pod:由于需求增加,这些pod使用的资源有时超过基准。这个级别非常适合数据库或Web服务器,它们的负载取决于入站请求的数量。

最后,BestEffort并不保证资源可用性。因此,它最适合批处理作业之类需要时可以重复的应用,或者非任务关键型的暂存环境。

八、资源方面小结

Kubernetes集群维护CPU时间、内存、暂态存储和扩展资源等硬件资源,并将它们分配给运行中的容器。借助请求和限制体系,运维人员可以根据单个容器定制资源分配,然后让Kubernetes系统将它们适当地分配给节点。

扩展资源使我们能够定义自己的资源类型,并以类似的方式使用它们。Kubernetes还根据请求和限制,为pod分配服务质量级别。然后,它使用这些级别做出调度和终止决策。

Kubernetes资源优化对于兼顾成本和最终用户体验至关重要

九、pod的特性

Pod是k8s系统中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最小资源对象模型,也是在k8s上运行容器化应用的资源对象,其他的资源对象都是用来支撑或者扩展Pod对象功能的,比如控制器对象是用来管控Pod对象的,Service或者Ingress资源对象是用来暴露Pod引用对象的,PersistentVolume资源对象是用来为Pod提供存储等等,k8s不会直接处理容器,而是Pod,Pod是由一个或者多个container组成的。

9.1 pod 简介

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。

Pod 的组成与容器的关系:
在这里插入图片描述
Pod可以被理解成一群可以共享网络、存储和计算资源的容器化服务的集合。再打个形象的比喻,在同一个Pod里的几个Docker服务/程序,好像被部署在同一台机器上,可以通过localhost互相访问,并且可以共用Pod里的存储资源(这里是指Docker可以挂载Pod内的数据卷,数据卷的概念,后文会详细讲述,暂时理解为“需要手动mount的磁盘”)。笔者总结Pod如下图,可以看到:同一个Pod之间的Container可以通过localhost互相访问,并且可以挂载Pod内所有的数据卷;但是不同的Pod之间的Container不能用localhost访问,也不能挂载其他Pod的数据卷。

Pod容器与Node的关系:
在这里插入图片描述

9.2 为什么需要pod

1、更利于扩展
k8s不仅仅支持Docker容器,也支持rkt甚至用户自定义容器,为什么会有这么多不同的容器呢,因为容器并不是真正的虚拟机,docker的一些概念和误区总结,此外,Kubernetes不依赖于底层某一种具体的规则去实现容器技术,而是通过CRI这个抽象层操作容器,这样就会需要pod这样一个东西,pod内部再管理多个业务上紧密相关的用户业务容器,就会更有利用业务扩展pod而不是扩展容器。

2、更容易定义一组容器的状态

如果我们没有使用pod,而是直接使用一组容器去跑一个业务呢,那么当其中一个或者若干个容器出现问题呢,我们如何去定义这一组容器的状态呢,通过pod这个概念,这个问题就可以很好的解决,一组业务容器跑在一个k8s的pod中,这个pod中会有一个pause容器,这个容器与其他的业务容器都没有关系,以这个pause容器的状态来代表这个pod的状态.

3、利于容器间文件共享,以及通信。
pod里的多个业务容器共享pause容器的ip和存储卷Volume,pod中的其他容器共享pause容器的ip地址和存储,这样就做到了文件共享和互信。

9.3 Pod 特性:

1 资源共享:IP和Volume
一个Pod里的多个容器可以共享存储和网络IP,可以看作一个逻辑的主机。共享的如 namespace,cgroups或者其他的隔离资源。

多个容器共享同一个network namespace,由此在一个Pod里的多个容器共享Pod的IP和端口namespace,所以一个Pod内的多个容器之间可以通过localhost来进行通信,所需要注意的是不同容器要注意不要有端口冲突即可。不同的Pod有不同的IP,不同Pod内的多个容器之前通信,不可以使用IPC(如果没有特殊指定的话)通信,通常情况下使用Pod的IP进行通信。

k8s要求底层网络支持集群内任意两个pod直接的TCP/IP直接通信,这通常才有虚拟二层网络技术来实现,例如Flannel,Openswitch等。

一个Pod里的多个容器可以共享存储卷,这个存储卷会被定义为Pod的一部分,并且可以挂载到该Pod里的所有容器的文件系统上。

2 生命周期短暂
Pod属于生命周期比较短暂的组件,比如,当Pod所在节点发生故障,那么该节点上的Pod会被调度到其他节点,但需要注意的是,被重新调度的Pod是一个全新的Pod,跟之前的Pod没有半毛钱关系。

3 平坦的网络
K8s集群中的所有Pod都在同一个共享网络地址空间中,也就是说每个Pod都可以通过其他Pod的IP地址来实现访问。

9.4 Pod使用和管理

核心原则是:将多个应用分散到多个Pod中 原因:基于资源的合理应用;扩缩容,不同应用应该有不同的扩缩容策略等。

如果容器之间不是必须运行在一起的话,那么就放到不同的Pod里
如果容器之前是相互独立的组件,那么就放到不同的Pod里
如果容器之前扩缩容策略不一样,那么就放到不同的Pod里

结论:单Pod单容器应用,除非特殊原因。

你很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kubernetes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。

注意:重启Pod中的容器跟重启Pod不是一回事。Pod只提供容器的运行环境并保持容器的运行状态,重启容器不会造成Pod重启。

Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod的。

9.5 Pod和Controller

Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。

包含一个或者多个Pod的Controller示例:

Deployment
StatefulSet
DaemonSet
通常,Controller会用你提供的Pod Template来创建相应的Pod。

在用户定义范围内,如果pod增多,则ReplicationController会终止额外的pod,如果减少,RC会创建新的pod,始终保持在定义范围。例如,RC会在Pod维护(例如内核升级)后在节点上重新创建新Pod。

9.6 Pod定义

对Pod的定义可以通过Yaml或Json格式的配置文件来完成。关于Yaml或Json中都能写哪些参数,参考官网http://kubernetes.io/docs/user-guide/pods/multi-container/

Pod的yaml整体文件内容及功能注解如下:

yaml格式的pod定义文件完整内容:

apiVersion: v1       #必选,版本号,例如v1
kind: Pod       #必选,Pod
metadata:       #必选,元数据
  name: string       #必选,Pod名称
  namespace: string    #必选,Pod所属的命名空间
  labels:      #自定义标签
    - name: string     #自定义标签名字
  annotations:       #自定义注释列表
    - name: string
spec:         #必选,Pod中容器的详细定义
  containers:      #必选,Pod中容器列表
  - name: string     #必选,容器名称
    image: string    #必选,容器的镜像名称
    imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
    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     #内存清楚,容器启动的初始可用数量
    livenessProbe:     #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
      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的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
    nodeSelector: obeject  #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
    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的存储卷,挂载集群与定义的secre对象到容器内部
        scretname: string  
        items:     
        - key: string
          path: string
      configMap:     #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
        name: string
        items:
        - key: string
          path: string   

举个栗子


metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}

9.7 pod 使用

在使用docker时,我们可以使用docker run命令创建并启动一个容器,而在Kubernetes系统中对长时间运行的容器要求是:其主程序需要一直在前台运行。如果我们创建的docker镜像的启动命令是后台执行程序,例如Linux脚本:

nohup ./startup.sh &

则kubelet创建包含这个容器的pod后运行完该命令,即认为Pod执行结束,之后根据RC中定义的pod的replicas副本数量生产一个新的pod,而一旦创建出新的pod,将在执行完命令后陷入无限循环的过程中,这就是Kubernetes需要我们创建的docker镜像以一个前台命令作为启动命令的原因。

对于无法改造为前台执行的应用,也可以使用开源工具supervisor辅助进行前台运行的功能。

Pod可以由一个或多个容器组合而成

场景1:单个应用多个容器
spring boot web:

apiVersion:v1
kind: Pod
metadata:
  name:  springbootweb
  label:
    name:  test
spec:
  containers:
  - name: springbootweb
    image: registry.xxxx.com/springboot:latest
    ports:
    - containerPort: 9081

kubectl create -f springboot-deployment.yml

[root@k8s-master pod]# kubectl get pods
NAME            READY     STATUS    RESTARTS   AGE
springbootweb   0/1       Pending   0          1m

kubectl get pods -o wide

加入 –o wide参数 查看额外信息:包括node和ip

pod处于pending的原因:通过kubectl describe pods springbootweb进一步查找问题。

可以看到pod的镜像信息写错了:

在这里插入图片描述
先删除pod,然后再创建:kubectl delete pod springbootweb
在这里插入图片描述
由于创建的端口号是9081,可以直接访问:curl 10.0.86.2:9081

curl 10.0.86.2:9081

Hello world

场景2:Pod不同应用多个容器组合而成
例如:两个容器应用的前端frontend和redis为紧耦合的关系,应该组合成一个整体对外提供服务,则应该将这两个打包为一个pod.

在这里插入图片描述
配置文件frontend-localredis-pod.yaml如下:

apiVersion:v1
kind: Pod
metadata:
  name: redis-php
  label:
    name: redis-php
spec:
  containers:
  - name: frontend
    image: kubeguide/guestbook-php-frontend:localredis
    ports:
    - containersPort: 80
  - name: redis-php
    image:kubeguide/redis-master
    ports:
    - containersPort: 6379

属于一个Pod的多个容器应用之间相互访问只需要通过localhost就可以通信,这一组容器被绑定在一个环境中。

使用kubectl create创建该Pod后,get Pod信息可以看到如下图:

#kubectl get gods
NAME READY STATUS RESTATS AGE

redis-php 2/2Running 0 10m

可以看到READY信息为2/2,表示Pod中的两个容器都成功运行了.

9.8 集群外部访问Pod

上面的例子,在k8s集群的安装有kube-proxy的node节点上,可以直接通过curl 10.0.86.2:9081 访问集群的pod。但在集群外的客户端系统无法通过Pod的IP地址或者Service的虚拟IP地址和虚拟端口号访问到它们。为了让外部客户端可以访问这些服务,可以将Pod或Service的端口号映射到宿主机,以使得客户端应用能够通过物理机访问容器应用。

1、将容器应用的端口号映射到物理机

apiVersion:v1
kind: Pod
metadata:
  name:  springbootweb
  label:
    name:  test
spec:
  containers:
  - name: springbootweb
    image: registry.xxxx.com/springboot:latest
    ports:
    - containerPort:9081
      hostPort: 9082

(2)通过设置Pod级别的hostNetwork-true,该Pod中所有容器的端口号都将被直接映射到物理机上。设置hostNetwork-true时需要注意,在容器的ports定义部分如果不指定hostPort,则默认hostPort等于containerPort,如果指定了hostPort,则hostPort必须等于containerPort的值。

apiVersion:v1
kind: Pod
metadata:
  name:  springbootweb
  label:
    name:  test
spec:
  hostNetwork: true
  containers:
  - name: springbootweb
    image: registry.xxxx.com/springboot:latest
    ports:
    - containerPort:9081

9.9 静态Pod

静态pod是由kubelet进行管理的仅存在于特定Node的Pod上,他们不能通过API Server进行管理,无法与ReplicationController、Deployment或者DaemonSet进行关联,并且kubelet无法对他们进行健康检查。静态Pod总是由kubelet进行创建,并且总是在kubelet所在的Node上运行。

创建静态Pod有两种方式:配置文件或者HTTP方式

1)配置文件方式
首先,需要设置kubelet的启动参数"–config",指定kubelet需要监控的配置文件所在的目录,kubelet会定期扫描该目录,并根据目录中的 .yaml或 .json文件进行创建操作

假设配置目录为/etc/kubelet.d/配置启动参数:–config=/etc/kubelet.d/,然后重启kubelet服务后,再宿主机受用docker ps或者在Kubernetes Master上都可以看到指定的容器在列表中

由于静态pod无法通过API Server直接管理,所以在master节点尝试删除该pod,会将其变为pending状态,也不会被删除

#kubetctl delete pod static-web-node1
pod "static-web-node1"deleted

#kubectl get pods

NAME READY STATUS RESTARTS AGE

static-web-node1 0/1Pending 0 1s

要删除该pod的操作只能在其所在的Node上操作,将其定义的.yaml文件从/etc/kubelet.d/目录下删除

#rm -f /etc/kubelet.d/static-web.yaml

#docker ps

9.10 Pod容器共享Volume

Volume类型包括:emtyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、scsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume等等,可以定义多个Volume,每个Volume的name保持唯一。在同一个pod中的多个容器能够共享pod级别的存储卷Volume。Volume可以定义为各种类型,多个容器各自进行挂载操作,讲一个Volume挂载为容器内需要的目录。

如下图:

在这里插入图片描述
如上图中的Pod中包含两个容器:tomcat和busybox,在pod级别设置Volume “app-logs”,用于tomcat想其中写日志文件,busybox读日志文件。

配置文件如下:

apiVersion:v1
kind: Pod
metadata:
  name: redis-php
  label:
    name: volume-pod
spec:
  containers:
  - name: tomcat
    image: tomcat
    ports:
    - containersPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath:/usr/local/tomcat/logs
  - name: busybox
    image:busybox
    command: ["sh","-C","tail -f /logs/catalina*.log"]
  volumes:
  - name: app-logs
    emptyDir:{}

busybox容器可以通过kubectl logs查看输出内容

#kubectl logs volume-pod -c busybox

tomcat容器生成的日志文件可以登录容器查看

#kubectl exec -ti volume-pod -c tomcat – ls /usr/local/tomcat/logs

9.11 Pod的配置管理

应用部署的一个最佳实践是将应用所需的配置信息于程序进行分离,这样可以使得应用程序被更好的复用,通过不用配置文件也能实现更灵活的功能。将应用打包为容器镜像后,可以通过环境变量或外挂文件的方式在创建容器时进行配置注入。ConfigMap是Kubernetes v1.2版本开始提供的一种统一集群配置管理方案。

9.12 ConfigMap:容器应用的配置管理

容器使用ConfigMap的典型用法如下:

生产为容器的环境变量。

设置容器启动命令的启动参数(需设置为环境变量)。

以Volume的形式挂载为容器内部的文件或目录。

ConfigMap以一个或多个key:value的形式保存在Kubernetes系统中共应用使用,既可以用于表示一个变量的值,也可以表示一个完整的配置文件内容。

通过yuaml配置文件或者直接使用kubelet create configmap 命令的方式来创建ConfigMap

9.13 ConfigMap的创建

举个小例子cm-appvars.yaml来描述将几个应用所需的变量定义为ConfigMap的用法:

vim cm-appvars.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir:/var/data

执行kubectl create命令创建该ConfigMap

#kubectl create -f cm-appvars.yaml

configmap "cm-appvars.yaml"created

查看建立好的ConfigMap:

#kubectl get configmap
kubectl describe configmap cm-appvars
kubectl get configmap cm-appvars -o yaml

另:创建一个cm-appconfigfile.yaml描述将两个配置文件server.xml和logging.properties定义为configmap的用法,设置key为配置文件的别名,value则是配置文件的文本内容:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: cm-appvars
    data:
      key-serverxml:
        <?xml Version='1.0'encoding='utf-8'?>
        <Server port="8005"shutdown="SHUTDOWN">
        .....
          </service>
        </Server>
      key-loggingproperties:
        "handlers=lcatalina.org.apache.juli.FileHandler,
        ...."

在pod "cm-test-app"定义中,将configmap "cm-appconfigfile"中的内容以文件形式mount到容器内部configfiles目录中。

Pod配置文件cm-test-app.yaml内容如下:

#vim cm-test-app.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cm-test-app
spec:
  containers:
  - name: cm-test-app
    image: tomcat-app:v1
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: serverxml                          #引用volume名
      mountPath:/configfiles                       #挂载到容器内部目录
        configMap:
          name: cm-test-appconfigfile                  #使用configmap定义的的cm-appconfigfile
          items:
          - key: key-serverxml                     #将key=key-serverxml
            path: server.xml                           #value将server.xml文件名进行挂载
          - key: key-loggingproperties                 #将key=key-loggingproperties    
            path: logging.properties                   #value将logging.properties文件名进行挂载

创建该Pod:

#kubectl create -f cm-test-app.yaml
Pod "cm-test-app"created

登录容器查看configfiles目录下的server.xml和logging.properties文件,他们的内容就是configmap “cm-appconfigfile”中定义的两个key的内容

#kubectl exec -ti cm-test-app – bash
root@cm-rest-app:/# cat /configfiles/server.xml
root@cm-rest-app:/# cat /configfiles/logging.properties

9.14 使用ConfigMap的条件限制

使用configmap的限制条件如下:

configmap必须在pod之间创建
configmap也可以定义为属于某个Namespace,只有处于相同namespaces中的pod可以引用
configmap中配额管理还未能实现
kubelet只支持被api server管理的pod使用configmap,静态pod无法引用
在pod对configmap进行挂载操作时,容器内部职能挂载为目录,无法挂载文件。

9.15 Pod生命周期和重启策略

Pod在整个生命周期过程中被定义为各种状态,熟悉Pod的各种状态有助于理解如何设置Pod的调度策略、重启策略

Pod的状态包含以下几种,如图:
在这里插入图片描述
Pod的重启策略(RestartPolicy)应用于Pod内所有的容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某哥容器异常退出或者健康检查石柏师,kubelet将根据RestartPolicy的设置进行相应的操作

Pod的重启策略包括Always、OnFailure及Nerver,默认值为Always。

kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5分钟,并且成功重启后的10分钟后重置该事件。

Pod的重启策略和控制方式息息相关,当前可用于管理Pod的控制器宝库ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod),每种控制器对Pod的重启策略要求如下:

RC和DaemonSet:必须设置为Always,需要保证该容器持续运行
Job:OnFailure或Nerver,确保容器执行完成后不再重启
kubelet:在Pod失效时重启他,不论RestartPolicy设置什么值,并且也不会对Pod进行健康检查

9.16 Pod健康检查

对Pod的健康检查可以通过两类探针来检查:LivenessProbe和ReadinessProbe

LivenessProbe探针:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet杀掉该容器,并根据容器的重启策略做响应处理
ReadinessProbe探针:用于判断容器是否启动完成(ready状态),可以接受请求。如果ReadinessProbe探针探测失败,则Pod的状态被修改。Endpoint Controller将从service的Endpoint中删除包含该容器所在的Pod的Endpoint。
kubelet定制执行LivenessProbe探针来诊断容器的健康状况。LivenessProbe有三种事项方式。

1)ExecAction:在容器内部执行一个命令,如果该命令的返回值为0,则表示容器健康。例:

apiVersion:v1
kind: Pod
metadata:
  name: liveness-exec
  label:
    name: liveness
spec:
  containers:
  - name: tomcat
    image: grc.io/google_containers/tomcat
    args:
    -/bin/sh
    - -c
    -echo ok >/tmp.health;sleep10; rm -fr /tmp/health;sleep600
    livenessProbe:
      exec:
        command:
        -cat
        -/tmp/health
      initianDelaySeconds:15
      timeoutSeconds:1

(2)TCPSocketAction:通过容器ip地址和端口号执行TCP检查,如果能够建立tcp连接表明容器健康。例:

kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    livenessProbe:
      tcpSocket:
        port: 80
      initianDelaySeconds:30
      timeoutSeconds:1

3)HTTPGetAction:通过容器Ip地址、端口号及路径调用http get方法,如果响应的状态吗大于200且小于400,则认为容器健康。例:

apiVersion:v1
kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    livenessProbe:
      httpGet:
        path:/_status/healthz
        port: 80
      initianDelaySeconds:30
      timeoutSeconds:1

对于每种探针方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们含义如下:

initialDelaySeconds: 启动容器后首次监控检查的等待时间,单位秒
timeouSeconds: 健康检查发送请求后等待响应的超时时间,单位秒。当发生超时就被认为容器无法提供服务无,该容器将被重启
九.玩转Pod调度
在Kubernetes系统中,Pod在大部分场景下都只是容器的载体而已,通常需要通过RC、Deployment、DaemonSet、Job等对象来完成Pod的调度和自动控制功能。

9.17 RC、Deployment:全自动调度

RC的主要功能之一就是自动部署容器应用的多份副本,以及持续监控副本的数量,在集群内始终维护用户指定的副本数量。

在调度策略上,除了使用系统内置的调度算法选择合适的Node进行调度,也可以在Pod的定义中使用NodeName、NodeSelector或NodeAffinity来指定满足条件的Node进行调度。

1)NodeName
Pod.spec.nodeName用于强制约束将Pod调度到指定的Node节点上,这里说是“调度”,但其实指定了nodeName的Pod会直接跳过Scheduler的调度逻辑,直接写入PodList列表,该匹配规则是强制匹配。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: springbootweb
  name: springbootweb-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: springbootweb
  template:
    metadata:
      labels:
        app: springbootweb
    spec:
      nodeName: node23.turing.com
      containers:
      - image: registry.tuling123.com/springboot:latest
        imagePullPolicy: IfNotPresent
        name: springbootweb
        ports:
        - containerPort: 9081
          hostPort: 9981     
      imagePullSecrets:
      - name: registry-key-secret

2)NodeSelector:定向调度
  Kubernetes Master上的scheduler服务(kube-Scheduler进程)负责实现Pod的调度,整个过程通过一系列复杂的算法,最终为每个Pod计算出一个最佳的目标节点,通常我们无法知道Pod最终会被调度到哪个节点上。实际情况中,我们需要将Pod调度到我们指定的节点上,可以通过Node的标签和pod的nodeSelector属性相匹配来达到目的。

Pod.spec.nodeSelector是通过kubernetes的label-selector机制进行节点选择,由scheduler调度策略MatchNodeSelector进行label匹配,调度pod到目标节点,该匹配规则是强制约束。

启用节点选择器的步骤为:

首先通过kubectl label命令给目标Node打上标签

kubectl label nodes =

例:#kubectllabel nodes k8s-node-1 zonenorth

然后在Pod定义中加上nodeSelector的设置,例:

apiVersion:v1
kind: Pod
metadata:
  name: redis-master
  label:
    name: redis-master
spec:
  replicas: 1
  selector:
    name: redis-master
    template:
      metadata:
        labels:
          name: redis-master
      spec:
        containers:
        - name: redis-master
          images: kubeguide/redis-master
          ports:
          - containerPort: 6379
        nodeSelector:
          zone: north

运行kubectl create -f命令创建Pod,scheduler就会将该Pod调度到拥有zone=north标签的Node上。 如果多个Node拥有该标签,则会根据调度算法在该组Node上选一个可用的进行Pod调度。

需要注意的是:如果集群中没有拥有该标签的Node,则这个Pod也无法被成功调度。

3)NodeAffinity:亲和性调度
该调度策略是将来替换NodeSelector的新一代调度策略。由于NodeSelector通过Node的Label进行精确匹配,所有NodeAffinity增加了In、NotIn、Exists、DoesNotexist、Gt、Lt等操作符来选择Node。调度侧露更加灵活。

9.18 DaemonSet:特定场景调度

DaemonSet用于管理集群中每个Node上仅运行一份Pod的副本实例,如图:

在这里插入图片描述
这种用法适合一些有下列需求的应用:

在每个Node上运行个以GlusterFS存储或者ceph存储的daemon进程
在每个Node上运行一个日志采集程序,例如fluentd或者logstach
在每个Node上运行一个健康程序,采集Node的性能数据。
DaemonSet的Pod调度策略类似于RC,除了使用系统内置的算法在每台Node上进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node范围来进行调度。

Pod的扩容和缩容
在实际生产环境中,我们经常遇到某个服务需要扩容的场景,也有可能因为资源精确需要缩减资源而需要减少服务实例数量,此时我们可以Kubernetes中RC提供scale机制来完成这些工作。

以redis-slave RC为例,已定义的最初副本数量为2,通过kubectl scale命令可以将Pod副本数量

#kubectl scale rc redis-slave --replicas=3
ReplicationController"redis-slave" scaled
#kubectl get pods

NAME READY STATUS RESTARTS AGE
redis-slave-1sf23 1/1Running 0 1h
redis-slave-54wfk 1/1Running 0 1h
redis-slave-3da5y 1/1Running 0 1h

除了可以手工通过kubectl scale命令完成Pod的扩容和缩容操作以外,新版本新增加了Horizontal Podautoscaler(HPA)的控制器,用于实现基于CPU使用路进行启动Pod扩容缩容的功能。该控制器基于Mastger的kube-controller-manager服务启动参数 --horizontal-pod-autoscler-sync-period定义的时长(默认30秒),周期性监控目标Pod的Cpu使用率并在满足条件时对ReplicationController或Deployment中的Pod副本数量进行调整,以符合用户定义的平均Pod Cpu使用率,Pod Cpu使用率来源于heapster组件,所以需预先安装好heapster。

9.19 Pod的滚动升级

当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后重新拉取镜像并启动。如果集群规模较大,因服务全部停止后升级的方式将导致长时间的服务不可用。由此,Kubernetes提供了rolling-update(滚动升级)功能来解决该问题。

滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建一个新的RC,然后自动控制旧版本的Pod数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,最终实现Pod的升级。需要注意的是,系统要求新的RC需要与旧的RC在相同的Namespace内,即不能把别人的资产转到到自家名下。
例:将redis-master从1.0版本升级到2.0:

apiVersion: v1
kind: replicationController
metadata:
  name: redis-master-v2
  labels:
    name: redis-master
    Version: v2
spec:
  replicas: 1
  selector:
    name: redis-master
    Version: v2
  template:
    labels:
      name: redis-master
      Version: v2
    spec:
      containers:
      - name: master
        images: kubeguide/redis-master:2.0
        ports:
        - containerPort: 6379

需要注意的点:

RC的name不能与旧的RC名字相同
在sele中应至少有一个label与旧的RC的label不同,以标识为新的RC。本例中新增了一个名为version的label与旧的RC区分
运行kubectl rolling-update来完成Pod的滚动升级:

#kubectl rolling-update redis-master -f redis-master-controller-v2.yaml

另一种方法就是不使用配置文件,直接用kubectl rolling-update加上–image参数指定新版镜像名来完成Pod的滚动升级

#kubectl rolling-update redis-master --image=redis-master:2.0

与使用配置文件的方式不同的是,执行的结果是旧的RC被删除,新的RC仍然使用就的RC的名字。

如果在更新过程总发现配置有误,则用户可以中断更新操作,并通过执行kubectl rolling-update-rollback完成Pod版本的回滚。

总结:

人已麻,再见

在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐