当前位置:   article > 正文

k8s技术预研7--深入掌握Kubernetes Pod_kubectl get pods 筛选 未运行的pod

kubectl get pods 筛选 未运行的pod

目录
1、Yaml格式的Pod定义文件完整模板详解
2、Pod的基本用法
    2.1 由1个容器组成的Pod示例
    2.2 由两个为紧耦合关系的容器打包组成的Pod示例
3、静态Pod
    3.1 配置文件方式
    3.2 HTTP方式
4、Pod容器共享Volume
5、Pod的配置管理
    5.1 ConfigMap概述
    5.2 使用ConfigMap的局限
    5.3 创建ConfigMap资源对象
    5.4 在Pod中使用ConfigMap
    5.5 其它注意事项
6、在容器内获取Pod信息(Downward API)
    6.1 环境变量方式——将Pod信息注入为环境变量
    6.2 环境变量方式——将容器资源信息注入为环境变量
    6.3 Volume挂载方式实现注入
7、Pod生命周期和重启策略
    7.1 Pod的状态
    7.2 Pod的重启策略
    7.3 常见的状态转换场景
8、Pod健康检查
    8.1 LivenessProbe
    8.2 LivenessProbe三种实现方式
    8.3 ReadinessProbe
9、Pod调度管理
    9.1 Deployment/RC:全自动调度
    9.2 NodeSelector: 定向调度
    9.3 NodeAffinity:Node亲和性调度
    9.4 PodAffinity:Pod亲和与互斥调度策略
    3)Pod的互斥性调度
    9.5 Taints和Tolerations(污点和容忍)
    9.6 DaemonSet:在每一个Node上调度一个Pod
    9.7 Job:批处理调度
    9.8 Cronjob:定时任务
    9.9 自定义调度器
10、Init Container(初始化容器)
11、Pod的升级和回滚
    11.1 Deployment
    11.2 创建 Deployment
    11.3 更新Deployment
    11.4 回退Deployment
    11.5 Deployment 扩容
    11.6 暂停和恢复Deployment
    11.7 Deployment 状态
    11.8 金丝雀 Deployment
    11.9 编写 Deployment Spec
    11.10 其他管理对象的更新策略
12、Pod的自动扩容和缩容模式——HPA
13、StatefulSet
    13.1 StatefulSet(有状态系统服务设计)
    13.2 使用 StatefulSet 搭建 MongoDB 集群


本文内容除第13章节外,已经基于k8s v1.8.8进行了验证测试。

1、Yaml格式的Pod定义文件完整模板详解

apiVersion: 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         //容器中的镜像
    imagesPullPolicy: [Always|Never|IfNotPresent]//获取镜像的策略,默认值为Always,每次都尝试重新下载镜像
    command: [String]     //容器的启动命令列表(不配置的话使用镜像内部的命令)
    args: [String]        //启动参数列表
    workingDir: String    //容器的工作目录
    volumeMounts:         //挂载到到容器内部的存储卷设置
    - name: String
      mountPath: String   //存储卷在容器内部Mount的绝对路径
      readOnly: boolean   //默认值为读写
    ports:                //容器需要暴露的端口号列表
    - name: String
      containerPort: int  //容器要暴露的端口
      hostPort: int       //容器所在主机监听的端口(容器暴露端口映射到宿主机的端口,设置hostPort时同一台宿主机将不能再启动该容器的第2份副本)
      protocol: String    //TCP和UDP,默认值为TCP
    env:                  //容器运行前要设置的环境列表
    - name: String
      value: String
    resources:            
      limits:             //资源限制,容器的最大可用资源数量
        cpu: Srting
        memory: String
      requeste:           //资源限制,容器启动的初始可用资源数量
        cpu: String
        memory: String
    livenessProbe:         //pod内容器健康检查的设置
      exec:
        command: [String]  //exec方式需要指定的命令或脚本
      httpGet:             //通过httpget检查健康
        path: String
        port: number
        host: String
        scheme: Srtring
        httpHeaders:
        - name: Stirng
          value: String
      tcpSocket:           //通过tcpSocket检查健康
        port: number
      initialDelaySeconds: 0//首次检查时间
      timeoutSeconds: 0     //检查超时时间
      periodSeconds: 0      //检查间隔时间
      successThreshold: 0
      failureThreshold: 0
      securityContext:      //安全配置
        privileged: falae
    restartPolicy: [Always|Never|OnFailure]//重启策略,默认值为Always
    nodeSelector: object    //节点选择,表示将该Pod调度到包含这些label的Node上,以key:value格式指定
    imagePullSecrets:
    - name: String
    hostNetwork: false      //是否使用主机网络模式,弃用Docker网桥,默认否
  volumes:                  //在该pod上定义共享存储卷列表
  - name: String
    emptyDir: {}            //是一种与Pod同生命周期的存储卷,是一个临时目录,内容为空
    hostPath:               //Pod所在主机上的目录,将被用于容器中mount的目录
      path: string
    secret:                 //类型为secret的存储卷
      secretName: String
      item:
      - key: String
        path: String
    configMap:             //类型为configMap的存储卷
      name: String
      items:
      - key: String
        path: String

详细说明表:
属性名称
取值类型
是否必选
取值说明
version
String
Required
版本号,例如 v1
kind
String
Required
Pod
metadata
Object
Required
元数据
metadata.name
String
Required
Pod 的名称,命名规范需符合 RFC 1035 规范
metadata.namespace
String
Required
Pod 所属的命名空间,默认值为 default
metadata.labels[]
List

自定义标签列表
metadata.annotation[]
List

自定义注解列表
Spec
Object
Required
Pod 中容器的详细定义
spec.containers[]
List
Required
Pod 中的容器列表
spec.containers[].name
String
Required
容器的名称,需符合 RFC 1035 规范
spec.containers[].image
String
Required
容器的镜像名称
spec.containers[].imagePullPolicy
String

获取进行的策略,可选值包括:Always、Never、InfNotPresent,默认值为 Always。
Always:表示每次都尝试重新下载镜像。
Never:表示仅使用本地镜像。
InfNotPresent:表示如果本地有该镜像,则使用本地镜像,本地不存在时下载镜像。
spec.containers[].command[]
List

容器的启动命令列表,如果不指定,则使用镜像打包时使用的启动命令
spec.containers[].args[]
List

容器的启动命令参数列表
spec.containers[].workingDir
String

容器的工作目录
spec.containers[].volumeMounts[]
List

挂载到容器内部的存储卷配置
spec.containers[].volumeMounts[].name
String

引用 Pod 定义的共享存储的名称,需使用 volumes[] 部分定义的共享存储卷名称。
spec.containers[].volumeMounts[].mountPath
String

存储卷在容器内 Mount 的绝对路径,应少于 512 个字符
spec.containers[].volumeMounts[].readOnly
Boolean

是否为只读模式,默认值为读写模式
spec.containers[].ports[]
List

容器是否需要暴露的端口号列表
spec.containers[].ports[].name
String

端口的名称
spec.containers[].ports[].containerPort
Int

容器需要监听的端口号
spec.containers[].ports[].hostPort
Int

容器所在主机需要监听的端口号,默认与 containerPort 相同。设置 hostPort 时,同一台宿主机将无法启动该容器的第二份副本
spec.containers[].ports[].protocol
Srting

端口协议,支持 TCP 和 UDP,默认值为 TCP
spec.containers[].env[]
List

容器运行前需设置的环境变量列表
spec.containers[].env[].name
String

环境变量的名称
spec.containers[].env[].valume
String

环境变量的值
spec.containers[].resources
Object

资源限制和资源请求的设置,可以阅读   Kubernetes Resources
spec.containers[].resources.limits
Object

资源限制的设置
spec.containers[].resources.limits.cpu
String

CPU 限制,单位 core 数,将用于 docker run --cpu-shares 参数
spec.containers[].resources.limits.memory
String

内存现在,单位可以为 MiB/GiB 等,将用于 docker run --memory 参数
spec.containers[].resources.requests
Object

资源限制的设置
spec.containers[].resources.requests.cpu
String

CPU 请求,单位为 core 数,容器启动的初始可用数量
spec.containers[].resources.requests.memory
String

内存请求,单位可以为 MiB、GiB 等,容器启动的初始可用数量
spec.volumes[]
List

在该 Pod 上定义的共享存储卷列表
spec.volumes[].name
String

共享存储卷的名称,在一个 Pod 中每个存储卷定义一个名称,应符合 RFC 1035 规范。容器定义部分的 containers[].volumeMounts[].name 将引用该共享存储卷的名称。
Volume 的类型包括:emptyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、iscsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume,可以定义多个 volume,每个 volume 的 name 保持唯一。
spec.volumes[].emptyDir
Object

类型为 emptyDir 的存储卷,表示与 Pod 同生命周期的一个临时目录,其值为一个空对象:emptyDir: {}
spec.volumes[].hostPath
Object

类型为 hostPath 的存储卷,表示挂载 Pod 所在宿主机的目录,通过 volumes[].hostPath 指定
spec.volumes[].hostPath.path
String

Pod 所在宿主机的目录,将被用于容器中 mount 的目录
spec.volumes[].secret
Object

类型为 secret 的存储卷,表示瓜子集群预定义的 secret 对象到容器内部
spec.volumes[].configMap
Object

类型为 configMap 的存储卷,表示挂载集群预定义的 configMap 对象到容器内部
spec.volumes[].livenessProbe
Object

对 Pod 内各容器监控检查的设置,当探测无响应几次之后,系统将自动重启该容器,可以设置的方法包括:exec、httpGet 和 tcpSocket。对一个容器仅需设置一种健康检查方法。可参见: Kubernetes Healthcheck
spec.volumes[].livenessProbe.exec
Object

对 Pod 内各容器监控检查的设置,exec 方式
spec.volumes[].livenessProbe.exec.command[]
String

exec 方式需要制定的命令或脚本
spec.volumes[].livenessProbe.httpGet
Object

对 Pod 内各容器健康检查的设置,HTTPGet 方式。需制定 path、port
spec.volumes[].livenessProbe.tcpSocket
Object

对 Pod 内各容器健康检查的设置,tcpSocket 方式。
spec.volumes[].livenessProbe.initialDelaySeconds
Number

容器启动完成后进行首次探测的时间,单位为 s
spec.volumes[].livenessProbe.timeoutSeconds
Number

对容器健康检查的探测等待相应的超时时间设置,单位为 s,默认值为 1s。超过该超时时间的设置,将认为该容器不健康,将重启该容器。
spec.volumes[].livenessProbe.periodSeconds
Number

对容器健康检查的定期探测时间设置,单位为 s,默认为 10s 探测一次
spec.restartPolicy
String

Pod 的重启策略,可选值为 Always、OnFailure、Never,默认值为 Always。
Always:Pod 一旦终止运行,则无论容器是如何种植的,kubelet 都将重启它。
OnFailure:只有 Pod 以非零退出码终止时,kubelet 才会重启该容器。如果容器正常结束(退出码为 0),则 kubelet 将不会重启它。
Never:Pod 终止后,kubelet 将退出码报告给 Master,不会再重启该 Pod
spec.nodeSelector
Object

设置 NodeSelector 表示将该 Pod 调度到包含这些 label 的 Node 上,以 key:value 格式指定
spec.imagePullSecrets
Object

Pull 镜像时使用的 secret 名称,以 name:secretkey 格式指定
spec.hostNetwork
Boolean

是否使用主机网络模式,默认值为 false,如果设置为 true,则表示容器使用宿主机网络,不再使用 Docker 网桥,该 Pod 将无法在同一台宿主机上启动第 2 个副本

2、Pod的基本用法

在k8s中对运行容器的要求为:容器的主程序需要一直在前台运行,而不是后台运行。应用需要改造成前台运行的方式。如果改造应用上存在困难,则可以通过supervisor的进程管理工具,即supervisor在前台运行,应用程序由supervisor管理在后台运行。
如果我们创建的Docker镜像的启动命令是后台执行程序,则在kubelet创建包含这个容器的pod之后运行完该命令,即认为Pod已经结束,将立刻销毁该Pod。如果为该Pod定义了RC,则创建、销毁会陷入一个无限循环的过程中。
Pod可以由1个或多个容器组成。
2.1 由1个容器组成的Pod示例
frontend-env-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: frontend
  labels:
    name: frontend
spec:
  containers:
  - name: frontend
    image: kubeguide/guestbook-php-frontend
    env:
    - name: GET_HOSTS_FROM
      value: env
    ports:
    - containerPort: 80
执行创建命令,观察创建pod的结果:
[root@bogon ~]# kubectl create -f frontend-env-pod.yaml
pod "frontend" created
[root@bogon ~]# kubectl get pods
NAME           READY     STATUS              RESTARTS   AGE
frontend       0/1       ContainerCreating   0          8s
mysql-tjvjl    1/1       Running             1          8d
mysql2-lmxwm   1/1       Running             1          8d

[root@bogon ~]# kubectl get pods
NAME           READY     STATUS    RESTARTS   AGE
frontend       1/1       Running   0          46m
mysql-tjvjl    1/1       Running   1          8d
mysql2-lmxwm   1/1       Running   1          8d

[root@bogon ~]# kubectl describe pod frontend
Name:         frontend
Namespace:    default
Node:         10.0.2.6/10.0.2.6
Start Time:   Mon, 05 Mar 2018 09:08:54 +0800
Labels:       name=frontend
Annotations:  <none>
Status:       Running
IP:           172.17.0.4
Containers:
  frontend:
    Container ID:   docker://c835a9e2b824ed4ddf667634ef7ec461787286fb2b65a1b3dc6f55b043b120c4
    Image:          kubeguide/guestbook-php-frontend
    Image ID:       docker-pullable://kubeguide/guestbook-php-frontend@sha256:195181e0263bcee4ae0c3e79352bbd3487224c0042f1b9ca8543b788962188ce
    Port:           80/TCP
    State:          Running
      Started:      Mon, 05 Mar 2018 09:11:47 +0800
    Ready:          True
    Restart Count:  0
    Environment:
      GET_HOSTS_FROM:  env
    Mounts:            <none>
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:         <none>
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     <none>
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  46m   default-scheduler  Successfully assigned frontend to 10.0.2.6
  Normal  Pulling    46m   kubelet, 10.0.2.6  pulling image "kubeguide/guestbook-php-frontend"
  Normal  Pulled     44m   kubelet, 10.0.2.6  Successfully pulled image "kubeguide/guestbook-php-frontend"
  Normal  Created    44m   kubelet, 10.0.2.6  Created container
  Normal  Started    44m   kubelet, 10.0.2.6  Started container

2.2 由两个为紧耦合关系的容器打包组成的Pod示例
例如,当frontend和redis两个容器应用为紧耦合关系,此时应组成一个整体对外提供服务,应选择将二者打包为一个Pod。同个Pod中的多个容器之间互相访问可以通过localhost来通信。
在下面的例子中,frontend容器中的应用可以直接通过访问localhost:6379对同属一个Pod内的redis-master服务进行访问。
frontend-localredis-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: redis-php
  labels:
    name: redis-php
spec:
  containers:
  - name: frontend
    image: kubeguide/guestbook-php-frontend:localredis
    ports:
    - containerPort: 80
  - name: redis
    image: kubeguide/redis-master
    ports:
    - containerPort: 6379
执行创建命令,观察创建pod的结果:
[root@bogon ~]# kubectl create -f frontend-localredis-pod.yaml
pod "redis-php" created

[root@bogon ~]# kubectl get pods
NAME           READY     STATUS    RESTARTS   AGE
frontend       1/1       Running   0          1h
mysql-tjvjl    1/1       Running   1          8d
mysql2-lmxwm   1/1       Running   1          8d
redis-php      2/2       Running   0          16m

[root@bogon ~]# kubectl describe pod redis-php
Name:         redis-php
Namespace:    default
Node:         10.0.2.6/10.0.2.6
Start Time:   Mon, 05 Mar 2018 10:00:00 +0800
Labels:       name=redis-php
Annotations:  <none>
Status:       Running
IP:           172.17.0.5
Containers:
  frontend:
    Container ID:   docker://ef8b0ef47c41b7e77a3ab905081d18c29ed613b0a7e355887a667b54e9c0b63f
    Image:          kubeguide/guestbook-php-frontend:localredis
    Image ID:       docker-pullable://kubeguide/guestbook-php-frontend@sha256:37c2c1dcfcf0a51bf9531430fe057bcb1d4b94c64048be40ff091f01e384f81e
    Port:           80/TCP
    State:          Running
      Started:      Mon, 05 Mar 2018 10:00:03 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
  redis:
    Container ID:   docker://9cba46735bd24e4a75570c7025729433ff10f5cc6b13613e7e525865a05b6c8d
    Image:          kubeguide/redis-master
    Image ID:       docker-pullable://kubeguide/redis-master@sha256:e11eae36476b02a195693689f88a325b30540f5c15adbf531caaecceb65f5b4d
    Port:           6379/TCP
    State:          Running
      Started:      Mon, 05 Mar 2018 10:04:03 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:         <none>
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     <none>
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  16m   default-scheduler  Successfully assigned redis-php to 10.0.2.6
  Normal  Pulling    16m   kubelet, 10.0.2.6  pulling image "kubeguide/guestbook-php-frontend:localredis"
  Normal  Pulled     15m   kubelet, 10.0.2.6  Successfully pulled image "kubeguide/guestbook-php-frontend:localredis"
  Normal  Created    15m   kubelet, 10.0.2.6  Created container
  Normal  Started    15m   kubelet, 10.0.2.6  Started container
  Normal  Pulling    15m   kubelet, 10.0.2.6  pulling image "kubeguide/redis-master"
  Normal  Pulled     11m   kubelet, 10.0.2.6  Successfully pulled image "kubeguide/redis-master"
  Normal  Created    11m   kubelet, 10.0.2.6  Created container
  Normal  Started    11m   kubelet, 10.0.2.6  Started container
登录到容器frontend中,查看在redis容器中的6379服务端口,看上去就像是自己本地系统的服务监听端口一样:
[root@bogon ~]# kubectl exec -it redis-php -c frontend /bin/bash
root@redis-php:/var/www/html# ss -an|grep 6379
\tcp    LISTEN     0      128                    *:6379                  *:*
tcp    LISTEN     0      128                   :::6379                 :::*
root@redis-php:/var/www/html#

3、静态Pod

静态 pod 在特定的节点上直接通过 kubelet 守护进程进行管理,API 服务无法管理。它没有跟任何的副本控制器进行关联,kubelet 守护进程对它进行监控,如果崩溃了,kubelet 守护进程会重启它。
Kubelet 通过 Kubernetes API 服务为每个静态 pod 创建 镜像 pod,这些镜像 pod 对于 API 服务是可见的,但是不受它控制。
静态 pod 能够通过两种方式创建:配置文件或者 HTTP。

3.1 配置文件方式
  • 配置文件要求放在指定目录,需要是 json 或者 yaml 格式描述的标准的 pod 定义文件。使用 kubelet --pod-manifest-path=<the directory> 启动 kubelet 守护进程,它就会定期扫描目录下面 yaml/json 文件的出现/消失,从而执行 pod 的创建/删除。
  • 静态Pod无法通过API Server删除(若删除会变成pending状态),如需删除该Pod则将yaml或json文件从这个目录中删除。

例如,配置目录为/etc/kubelet.d/,为kubelet添加启动参数:--pod-manifest-path=/etc/kubelet.d/,在该目录下放入static-web.yaml。
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    name: static-web
spec:
  containers:
  - name: static-web
    image: nginx
    ports:
    - name: web
      containerPort: 80

放入上面的yaml文件后,即可从k8s上看到正在创建名为static-web-10.0.2.6的Pod:
[root@bogon ~]# kubectl get pods
NAME                  READY     STATUS              RESTARTS   AGE
frontend              1/1       Running             1          11h
mysql-tjvjl           1/1       Running             2          8d
mysql2-lmxwm          1/1       Running             2          8d
redis-php             2/2       Running             2          10h
static-web-10.0.2.6   0/1       ContainerCreating   0          13s
[root@bogon ~]# kubectl get pods
NAME                  READY     STATUS    RESTARTS   AGE
frontend              1/1       Running   1          11h
mysql-tjvjl           1/1       Running   2          8d
mysql2-lmxwm          1/1       Running   2          8d
redis-php             2/2       Running   2          10h
static-web-10.0.2.6   1/1       Running   0          4m

3.2 HTTP方式
Kubelet 定期的从参数 --manifest-url=<URL> 配置的地址下载文件,并将其解析为 json/yaml 格式的 pod 描述。它的工作原理与从 --pod-manifest-path=<directory> 中发现文件执行创建/更新静态 pod 是一样的,即,文件的每次更新都将应用到运行中的静态 pod 中。

4、Pod容器共享Volume

同一个Pod中的多个容器可以共享Pod级别的存储卷Volume,Volume可以定义为各种类型,多个容器各自进行挂载,将Pod的Volume挂载为容器内部需要的目录。
例如:Pod级别的Volume:”app-logs”,用于tomcat向其中写日志文件,busybox读日志文件。

pod-volumes-applogs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
    image: tomcat
    ports:
    - containerPort: 8080   
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  - name: busybox
    image: busybox
    command: ["sh","-c","tail -f /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:
  - name: app-logs
    emptyDir: {}
创建并通过容器“busybox”挂载的共享卷来查看tomcat应用的日志:
[root@bogon ~]# kubectl create -f pod-volumes-applogs.yaml
pod "volume-pod" created
[root@bogon ~]# kubectl get pods
NAME           READY     STATUS    RESTARTS   AGE
frontend       1/1       Running   1          12h
mysql-tjvjl    1/1       Running   2          8d
mysql2-9bfph   1/1       Running   0          21m
volume-pod     2/2       Running   0          6s

[root@bogon ~]# kubectl logs volume-pod -c busybox
05-Mar-2018 13:27:20.781 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/docs] has finished in [12] ms
05-Mar-2018 13:27:20.781 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/examples]
05-Mar-2018 13:27:21.069 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/examples] has finished in [287] ms
05-Mar-2018 13:27:21.069 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/host-manager]
05-Mar-2018 13:27:21.106 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/host-manager] has finished in [36] ms
05-Mar-2018 13:27:21.106 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/manager]
05-Mar-2018 13:27:21.122 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/manager] has finished in [16] ms
05-Mar-2018 13:27:21.126 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
05-Mar-2018 13:27:21.171 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]
05-Mar-2018 13:27:21.175 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 684 ms
查看容器“tomcat”中的应用日志目录,以及日志文件内容:
[root@bogon ~]# kubectl exec -it volume-pod -c tomcat -- ls /usr/local/tomcat/logs
catalina.2018-03-05.log      localhost_access_log.2018-03-05.txt
host-manager.2018-03-05.log  manager.2018-03-05.log
localhost.2018-03-05.log

[root@bogon ~]# kubectl exec -it volume-pod -c tomcat -- tail /usr/local/tomcat/logs/catalina.2018-03-05.log
05-Mar-2018 13:27:20.781 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/docs] has finished in [12] ms
05-Mar-2018 13:27:20.781 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/examples]
05-Mar-2018 13:27:21.069 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/examples] has finished in [287] ms
05-Mar-2018 13:27:21.069 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/host-manager]
05-Mar-2018 13:27:21.106 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/host-manager] has finished in [36] ms
05-Mar-2018 13:27:21.106 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/manager]
05-Mar-2018 13:27:21.122 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/manager] has finished in [16] ms
05-Mar-2018 13:27:21.126 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
05-Mar-2018 13:27:21.171 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]
05-Mar-2018 13:27:21.175 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 684 ms

5、Pod的配置管理

应用部署的一个最佳实践是将应用所需的配置信息与程序进行分离。将应用打包为容器镜像后,可以通过环境变量或外挂文件的方式,在创建容器时进行配置注入。
Kubernetes v1.2的版本提供统一的集群配置管理方案–ConfigMap。ConfigMap用于保存配置数据的键值对,可以用来保存单个属性,也可以用来保存配置文件。ConfigMap跟secret很类似,但它可以更方便地处理不包含敏感信息的字符串。
5.1 ConfigMap概述
典型使用场景:
  • 生成为容器内的环境变量。
  • 设置容器启动命令的启动参数(需设置为环境变量)。
  • 以Volume的形式挂载为容器内部的文件或目录。
ConfigMap以一个或多个key:value的形式保存在kubernetes系统中供应用使用,既可以表示一个变量的值(例如:apploglevel=info),也可以表示完整配置文件的内容(例如server.xml=<?xml...>...)。
可以通过yaml配置文件或直接使用kubectl create configmap命令行的方式来创建ConfigMap。

5.2 使用ConfigMap的局限
  • ConfigMap 必须在pod之前创建
  • ConfigMap也可以定于属于某个NameSpace,只有处于相同NameSpace的pod可以应用它
  • ConfigMap中的配额管理还未实现
  • 如果是volume的形式挂载到容器内部,只能挂载到某个目录下,该目录下原有的文件会被覆盖掉
  • 静态不能用configmap(静态pod 不受API server 管理)

5.3 创建ConfigMap资源对象
1)通过yaml配置文件方式创建
[root@bogon ~]# more cm-appvars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir: /var/date

创建并查看结果:
[root@bogon ~]# kubectl create -f cm-appvars.yaml
configmap "cm-appvars" created
[root@bogon ~]# kubectl get configmap
NAME         DATA      AGE
cm-appvars   2         16s
[root@bogon ~]# kubectl describe configmap cm-appvars
Name:         cm-appvars
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
apploglevel:
----
info
appdatadir:
----
/var/date
Events:  <none>
[root@bogon ~]# kubectl get configmap cm-appvars -o yaml
apiVersion: v1
data:
  appdatadir: /var/date
  apploglevel: info
kind: ConfigMap
metadata:
  creationTimestamp: 2018-03-05T16:02:05Z
  name: cm-appvars
  namespace: default
  resourceVersion: "522740"
  selfLink: /api/v1/namespaces/default/configmaps/cm-appvars
  uid: 8cb9bada-208e-11e8-9f54-080027cf1a4c


2)通过yaml配置文件方式直接将一个应用的配置文件定义为一个ConfigMap
定义一个ConfigMap 配置文件 cm-jdbcproperties.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-jdbcproperties
data:
  key-jdbcproperties: |
    JDBC_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver
    JDBC_URL=jdbc:mysql://localhost:3306/bz_argon?useUnicode=true&characterEncoding=utf8
    JDBC_USER_NAME=root
    JDBC_PASSWORD=123456
    JDBC_INITIALSIZE=10
    JDBC_MAXACTIVE=20
    JDBC_MAXIDLE=20
    JDBC_MINIDLE=10
    JDBC_MAXWAIT=60000
    JDBC_VALIDATIONQUERY=SELECT 1 FROM DUAL
    JDBC_TESTONBORROW=false
    JDBC_TESTONRETURN=false
    JDBC_TESTWHILEIDLE=true
    JDBC_TIMEBETWEENEVICTIONRUNSMILLIS=6000
    JDBC_MINEVICTABLEIDLETIMEMILLIS=25200000
    JDBC_REMOVEABANDONED=true
    JDBC_REMOVEABANDONEDTIMEOUT=1800
    JDBC_LOGABANDONED=true

创建并查看结果:
[root@bogon ~]# kubectl create -f cm-jdbcproperties.yaml
configmap "cm-jdbcproperties" created
[root@bogon ~]# kubectl get configmap cm-jdbcproperties
NAME                DATA      AGE
cm-jdbcproperties   1         29s
[root@bogon ~]# kubectl describe configmap cm-jdbcproperties
Name:         cm-jdbcproperties
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
key-jdbcproperties:
----
JDBC_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver
JDBC_URL=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
JDBC_USER_NAME=root
JDBC_PASSWORD=123456
JDBC_INITIALSIZE=10
JDBC_MAXACTIVE=20
JDBC_MAXIDLE=20
JDBC_MINIDLE=10
JDBC_MAXWAIT=60000
JDBC_VALIDATIONQUERY=SELECT 1 FROM DUAL
JDBC_TESTONBORROW=false
JDBC_TESTONRETURN=false
JDBC_TESTWHILEIDLE=true
JDBC_TIMEBETWEENEVICTIONRUNSMILLIS=6000
JDBC_MINEVICTABLEIDLETIMEMILLIS=25200000
JDBC_REMOVEABANDONED=true
JDBC_REMOVEABANDONEDTIMEOUT=1800
JDBC_LOGABANDONED=true
Events:  <none>

3)通过kubectl命令行方式创建
通过--from-file参数从文件中进行创建,可以指定key的名称,也可以在一个命令行中创建包含多个key的ConfigMap。
kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source

通过--from-file参数从目录中进行创建,该目录下的每个配置文件名被设置为key,文件内容被设置为value。
kubectl create configmap NAME --from-file=config-files-dir
通过--from-literal从文本中进行创建,直接将指定的key=value创建为ConfigMap的内容。
kubectl create configmap NAME --from-literal=key1=value1 --from-literal=key2=value2

例如:
$ kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
$ kubectl create configmap env-config --from-literal=log_level=INFO


5.4 在Pod中使用ConfigMap
容器应用对ConfigMap的使用有两种方法:
  • 通过环境变量获取ConfigMap中的内容。
  • 通过Volume挂载的方式将ConfigMap中的内容挂载为容器内部的文件或目录。

1)通过环境变量使用ConfigMap方式一
定义一个Pod:cm-test-pod.yaml,使用上面定义过的"cm-appvars"
apiVersion: v1
kind: Pod
metadata:
name: cm-test-pod
spec:
  containers:
  - name: cm-test
    image: busybox
    command: [ "/bin/sh", "-c", "env|grep APP" ]
    env:
    - name: APPLOGLEVEL    #定义环境变量名称
      valueFrom:
        configMapKeyRef:
          name: cm-appvars   # 使用上面定义过的"cm-appvars"  ConfigMap
          key: apploglevel
     - name: APPDATADIR     #定义环境变量名称
      valueFrom:
        configMapKeyRef:
          name: cm-appvars   # 使用上面定义过的"cm-appvars"  ConfigMap
          key: appdatadir
创建:
[root@bogon ~]# kubectl create -f cm-test-pod.yaml
[root@bogon ~]# kubectl get pods
NAME           READY     STATUS      RESTARTS   AGE
cm-test-pod    0/1       Completed   2          28s

查看cm-test-pod是否已经获取到了指定的环境变量:
[root@bogon ~]# kubectl logs cm-test-pod
APPDATADIR=/var/date
APPLOGLEVEL=info


2) 通过环境变量使用ConfigMap方式二
从k8s 1.6开始,新引入了一个字段envFrom,实现在Pod环境内将ConfigMap中所有定义的key=value自动生成为环境变量。
方法如下。
定义一个Pod:cm-test-pod2.yaml,使用上面定义过的"cm-appvars"
apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod2
spec:
  containers:
  - name: cm-test
    image: busybox
    command: [ "/bin/sh", "-c", "env" ]
    envFrom:
    - configMapRef
      name: cm-appvars    #根据cm-appvars中的key=value自动生成环境变量
  restartPolicy: Never

再例如:
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type
      envFrom:
        - configMapRef:
            name: env-config
  restartPolicy: Never


3)使用volume将ConfigMap作为文件或目录直接挂载
在5.3第2段内容中我们定义了一个cm-jdbcproperties.yaml文件。在这里我们将该ConfigMap中的内容以文件的形式mount到容器内部的/configfiles目录中去。
定义一个Pod:cm-test-app.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cm-test-app
spec:
  containers:
  - name: cm-test-app
    image: tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: jdbcproperties    #引用volume的名称
      mountPath: /configfiles    #挂载到容器内的目录
  volumes:
  - name: jdbcproperties    #定义volume的名称
    configMap:
      name: cm-jdbcproperties    #使用ConfigMap "cm-jdbcproperties"
      items:
      - key: key-jdbcproperties    #key=key-jdbcproperties
        path: jdbc.properties      #value将以jdbc.properties的文件名进行挂载

创建Pod并进入容器查看配置文件:
[root@bogon ~]# kubectl create -f cm-test-app.yaml

[root@bogon ~]# kubectl exec -it cm-test-app -c cm-test-app /bin/bash

root@cm-test-app:/# cd configfiles/
root@cm-test-app:/configfiles# ls
jdbc.properties
root@cm-test-app:/configfiles# more jdbc.properties
JDBC_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver
JDBC_URL=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
JDBC_USER_NAME=root
JDBC_PASSWORD=123456
......


5.5 其它注意事项
  • 如果是volume的形式挂载到容器内部,只能挂载到某个目录下,该目录下原有的文件会被覆盖掉。
  • 如果在引用ConfigMap时不指定"items",则使用volumeMount方式时,其中每一个key-value键值对都会生成一个文件,key为文件名,value为内容

6、在容器内获取Pod信息(Downward API)

Downward API可以使用以下2种方式把Pod信息注入容器内部:
  • 环境变量:用于单个变量,可以将Pod信息和Container信息注入容器内部;
  • Volume挂载:将数组类信息生成为文件,挂载到容器内部;

6.1 环境变量方式——将Pod信息注入为环境变量
下面例子中通过Downward API将Pod的IP、名称和所在Namespace注入容器的环境变量中。
dapi-test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
  restartPolicy: Never
执行创建Pod命令:
[root@bogon ~]# kubectl create -f dapi-test-pod.yaml
pod "dapi-test-pod" created
查看Pod内容器打印的环境变量日志:
[root@bogon ~]# kubectl logs dapi-test-pod
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.10.10.1:443
HOSTNAME=dapi-test-pod
SHLVL=1
HOME=/root
MY_POD_NAMESPACE=default
MY_POD_IP=172.17.0.8
MY_POD_NAME=dapi-test-pod
...............

注:valueFrom是Downward API 的一种特殊语法,在前面的ConfigMap中也有使用到。

6.2 环境变量方式——将容器资源信息注入为环境变量
下面例子中通过Downward API将Container的资源请求和限制信息注入到容器的环境变量中,容器中使用printenv命令把这些环境变量信息打印出来。
dapi-test-pod-container-vars.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-container-vars
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: Never
      command: [ "/bin/sh", "-c" ]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 3600;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.memory
  restartPolicy: Never
创建pod和查看容器打印的环境变量信息:
[root@bogon ~]# kubectl create -f dapi-test-pod-container-vars.yaml

[root@bogon ~]# kubectl logs dapi-test-pod-container-vars

1
1
33554432
67108864

6.3 Volume挂载方式实现注入
在Pod定义文件中,在volumes中使用downwardAPI的特殊语法,通过items的设置,将会以path的名称生成文件。

下面的例子中,通过DownwardAPI将Pod的Label、Annotation列表通过Volume挂载为容器内的一个文件。容器应用使用echo命令将文件内容打印到标准输出中。
dapi-test-pod-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: Never
      command: [ "/bin/sh", "-c" ]
      args:
      - while true; do
          echo -en '\n';
          if [[ -e /etc/labels ]]; then
            echo -en '\n\n'; cat /etc/labels; fi;
          if [[ -e /etc/annotations ]]; then
            echo -en '\n\n'; cat /etc/annotations; fi;
          sleep 3600;
        done;
      volumeMounts:
      - name: podinfo
        mountPath: /etc
        readOnly: false
  volumes:
  - name: podinfo
    downwardAPI:
      items:
      - path: "labels"
        fieldRef:
          fieldPath: metadata.labels
      - path: "annotations"
        fieldRef:
          fieldPath: metadata.annotations
创建Pod并查看容器打印的日志:
[root@bogon ~]# kubectl create -f dapi-test-pod-volume.yaml
pod "dapi-test-pod-volume" created
[root@bogon ~]# kubectl get pods
NAME                           READY     STATUS             RESTARTS   AGE
cm-test-app                    1/1       Running            0          17h
cm-test-pod                    0/1       CrashLoopBackOff   222        18h
dapi-test-pod-container-vars   1/1       Running            0          10h
dapi-test-pod-volume           1/1       Running            0          6s
frontend                       1/1       Running            1          1d
mysql-tjvjl                    1/1       Running            2          9d
mysql2-9bfph                   1/1       Running            0          22h
volume-pod                     2/2       Running            0          22h
[root@bogon ~]# kubetl logs dapi-test-pod-volume
-bash: kubetl: command not found
[root@bogon ~]# kubectl logs dapi-test-pod-volume

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"
kubernetes.io/config.seen="2018-03-06T19:28:41.715324795+08:00"

DownwardAPI的使用价值:
为某些容器应用提供一些必需的应用初始化参数信息。例如在某些集群中,集群中的每个节点都需要将自身的标识及进程绑定的IP地址等信息事先写入配置文件中。容器中的进程在启动时读取这些信息,然后发布到某个类似服务注册中心的地方,以实现集群节点的自动发现功能。这正是DownwardAPI很好的一个使用场景。

7、Pod生命周期和重启策略

7.1 Pod的状态
状态值说明
PendingAPI Server已经创建了该Pod,但Pod中的一个或多个容器的镜像还没有创建,包括镜像下载过程
RunningPod内所有容器已创建,且至少一个容器处于运行状态、正在启动状态或正在重启状态
Completed
Pod内所有容器均成功执行退出,且不会再重启
FailedPod内所有容器均已退出,但至少一个容器退出失败
Unknown
由于某种原因无法获取Pod状态,例如网络通信不畅
7.2 Pod的重启策略
重启策略说明
Always当容器失效时,由kubelet自动重启该容器
OnFailure当容器终止运行且退出码不为0时,由kubelet自动重启该容器
Never不论容器运行状态如何,kubelet都不会重启该容器

Pod的重启策略受管理Pod的控制的直接影响,可以管理Pod的控制器有Replication Controller,Job,DaemonSet,及kubelet(静态Pod)。
  • RC和DaemonSet:必须设置为Always,需要保证该容器持续运行。
  • Job:OnFailure或Never,确保容器执行完后不再重启。
  • kubelet:在Pod失效的时候重启它,不论RestartPolicy设置为什么值,并且不会对Pod进行健康检查。

7.3 常见的状态转换场景
Pod包含的容器数
Pod当前的状态
发生事件
Pod的结果状态


   RestartPolicy=AlwaysRestartPolicy=OnFailureRestartPolicy=Never
包含一个容器Running容器成功退出RunningSucceededSucceeded
包含一个容器Running容器失败退出RunningRunningFailure
包含两个容器Running1个容器失败退出RunningRunningRunning
包含两个容器Running容器被OOM杀掉RunningRunningFailure

8、Pod健康检查

Pod的健康状态由两类探针来检查:LivenessProbe和ReadinessProbe。
8.1 LivenessProbe
  • 用于判断容器是否存活(running状态)。
  • 如果LivenessProbe探针探测到容器非健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应处理。
  • 如果容器不包含LivenessProbe探针,则kubelet认为该探针的返回值永远为“success”。

kubelet定期执行LivenessProbe探针来判断容器的健康状态。
LivenessProbe参数:
  • initialDelaySeconds:启动容器后首次进行健康检查的等待时间,单位为秒。
  • timeoutSeconds:健康检查发送请求后等待响应的时间,如果超时响应kubelet则认为容器非健康,重启该容器,单位为秒。

8.2 LivenessProbe三种实现方式
1)ExecAction
在一个容器内部执行一个命令,如果该命令状态返回值为0,则表明容器健康。
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - echo ok > /tmp/health;sleep 10;rm -fr /tmp/health;sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/health
      initialDelaySeconds: 15
      timeoutSeconds: 1

12345678910111213141516171819
2)TCPSocketAction
通过容器IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containnerPort: 80
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 15
      timeoutSeconds: 1

123456789101112131415
3)HTTPGetAction
通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于等于400,则认为容器健康。
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containnerPort: 80
    livenessProbe:
      httpGet:
        path: /_status/healthz
        port: 80
      initialDelaySeconds: 15
      timeoutSeconds: 1

8.3 ReadinessProbe
  • 用于判断容器是否启动完成(read状态),可以接受请求。
  • 如果ReadnessProbe探针检测失败,则Pod的状态将被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Endpoint。


9、Pod调度管理

在kubernetes系统中,pod在大部分场景下都只是容器的载体而已,通常需要通过Deployment,DaemonSet,Job等对象来完成Pod的调度与自动控制功能。
9.1 Deployment/RC:全自动调度
从调度策略上来说,这3个nginx Pod由系统全自动完成调度。它们各自最终运行在哪个节点上,完全由Master的Scheduler经过一系列算法计算得出,用记无法干预调度过程和结果。
示例:
Deployment文件nginx-deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

[root@bogon ~]# kubectl create -f nginx-deployment.yaml
deployment "nginx-deployment" created

[root@bogon ~]# kubectl get deploy
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           6s
查看已创建的ReplicaSet和Pod的信息:
[root@bogon ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-5b4b59b4b8   3         3         3         1m
[root@bogon ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-5b4b59b4b8-6l2rv   1/1       Running   0          50s
nginx-deployment-5b4b59b4b8-s4czw   1/1       Running   0          50s
nginx-deployment-5b4b59b4b8-w8sqn   1/1       Running   0          50s

9.2 NodeSelector: 定向调度
kubernetes中的Schduler 负责实现pode的调度,他会根据一些复杂的算法,把pod调度到某一个Node上,如果你想指定某个Pod需要指定在某个Node上则可以通过NodeSelector定向调度。
首先,需要为目标Node打上一些标签:
[root@bogon ~]# kubectl get nodes -o wide
NAME       STATUS    ROLES     AGE       VERSION   EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION               CONTAINER-RUNTIME
10.0.2.6   Ready     <none>    9d        v1.8.8    <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://17.12.0-ce
[root@bogon ~]# kubectl label nodes 10.0.2.6 zone=north
node "10.0.2.6" labeled
然后,在Pod的定义中加上nodeSelector的设置,例如:
[root@bogon ~]# more nodeselector-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
  nodeSelector:
    zone: north

[root@bogon ~]# kubectl create -f nodeselector-pod.yaml
pod "pod-with-healthcheck" created
[root@bogon ~]# kubectl get pods -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP           NODE
pod-with-healthcheck                1/1       Running   0          18s       172.17.0.7   10.0.2.6

如果我们给多个Node都定义了相同的标签,则scheduler将会根据调度算法从这组Node中挑选一个可用的进行Pod调度。
通过基于Node标签的调度试,我们可以把集群中具有不同特点的Node贴上不同的标签,如“role=frontend”,“role=backend”,“role=database”等标签。在部署应用时就可以根据应用的需求设置NodeSelector来进行指定Node范围的调度。
注:如果我们指定了Podr nodeSelector条件,但集群中不存在符合条件的Node,则最终这个Pod无法被成功调度。

Kubernetes为Node提供了一些预定义的内置标签可以使用:
  • kubernetes.io/hostname
  • failure-domain.beta.kubernetes.io/zone
  • failure-domain.beta.kubernetes.io/region
  • beta.kubernetes.io/instance-type
  • beta.kubernetes.io/os
  • beta.kubernetes.io/arch

9.3 NodeAffinity:Node亲和性调度
亲和性调度机制极大地扩展了Pod的调度能力,主要增强的功能有:
  • 更具表达力(不仅仅是全部符合的情况)
  • 可以使用软限制、优先采用等限制方式,这样调度器在无法满足优先需求的情况下,会退而求其次,继续运行该Pod
  • 可以依据节点上正在运行的其他Pod的标签来进行限制,而非节点本身的标签。
  • 随着节点亲和性越来越能够表达nodeSelector表达的功能,最终nodeSelector会被废弃。

NodeAffinity意为Node亲和性调度策略,较NodeSelector更为精确匹配。NodeAffinity是条件范围匹配,通过In(属于)、NotIn(不属于)、Exists(存在一个条件)、DoesNotExist(不存在)、Gt(大于)、Lt(小于)等操作符来选择Node,使调度更加灵活。

NodeAffinity目前有两种节点亲和性表达式:
  • RequiredDuringSchedulingIgnoredDuringExecution:必须满足指定的规则才可以调度Pod到Node上,功能上与nodeSelector很像,相当于硬限制。
  • PreferredDuringSchedulingIgnoredDuringExecution:强调优先满足指定规则,调度器会尝试调度Pod到Node上,但并不强求,相当于软限制。多个优先级规则还可以设置权重(weight)值,以定义执行的先后顺序。

NodeAffinity规则设置注意事项:
  • 如果同时设置了nodeSelector和nodeAffinity,则系统将需要同时满足两者的设置才能进行调度。
  • 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
  • 如果nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod。

示例:
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity-example
spec:
   affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - k8s-node01
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - k8s-master01
            - k8s-master02
  containers:
  - name: example
    image: gcr.io/google_containers/pause:2.0

定义描述:
  1. requiredDuringSchedulingIgnoredDuringExecution,硬约束,Pod只能调度到具有kubernetes.io/hostname=k8s-node01标签的Node节点。
  2. preferredDuringSchedulingIgnoredDuringExecution,软约束,k8s调度会尽量不调度Pod到具有kubernetes.io/hostname=k8s-master01或kubernetes.io/hostname=k8s-master02标签的Node节点。


9.4 PodAffinity:Pod亲和与互斥调度策略
podAffinity基于Pod的标签来选择Node,仅调度到满足条件Pod所在的Node上,支持podAffinity和podAntiAffinity。
匹配的表达式有,In, NotIn, Exists, DoesNotExist,通过该策略,可以更灵活地对Pod进行调度。例如,将多实例的Pod分散到不通的Node、尽量调度A-Pod到有B-Pod运行的Node节点上等等。
另外与Node-affinity不同的是,该策略是依据Pod的Label进行调度,所以会受到namespace约束。
与 node affinity 相似,pod affinity 也有 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,意义也和之前一样。如果有使用亲和性,在 affinity 下面添加 podAffinity 字段,如果要使用互斥性,在 affinity 下面添加 podAntiAffinity 字段。

podAffinity 配置规则描述:
  • requiredDuringSchedulingIgnoredDuringExecution,硬约束,一定要满足,Pod的亲和性调度必须要满足后续定义的约束条件。
  • preferredDuringSchedulingIgnoredDuringExecution,软约束,不一定满足,Pod的亲和性调度会尽量满足后续定义的约束条件。
  • topologyKey用于定于调度时作用于特指域,这是通过Node节点的标签来实现的,例如指定为kubernetes.io/hostname,那就是以Node节点为区分范围,如果指定为failure-domain.beta.kubernetes.io/zone,则以Node节点所属的域来区分。
  • 与 labelSelector 和 topologyKey 同级,还可以定义 namespaces 列表,表示匹配哪些 namespace 里面的 pod,默认情况下,会匹配定义的 pod 所在的 namespace;如果定义了这个字段,但是它的值为空,则匹配所有的 namespaces。

topologyKey的使用约束:
  • 除了podAntiAffinity的preferredDuringSchedulingIgnoredDuringExecution,其他模式下的topologyKey均不能为空
  • 如果Admission Controller中添加了LimitPodHardAntiAffinityTopology,那么podAntiAffinity的requiredDuringSchedulingIgnoredDuringExecution被强制约束为kubernetes.io/hostname。
  • 如果podAntiAffinity的preferredDuringSchedulingIgnoredDuringExecution中的topologyKey为空,则默认为适配kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region。
  • 如果不是以上情况,就可以采用任意合法的opologyKey了。

示例:
 1)先创建一个参照目标Pod
创建一个名为pod-flag的Pod,带有标签security=S1和app=nginx。后面的例子将使用pod-flag作为Pod亲和与互斥的目标Pod。
pod-flag.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-flag
  labels:
    security: "S1"
    app: "nginx"
spec:
  containers:
  - name: nginx
    image: nginx

2)Pod的亲和性调度
创建第2个Pod来说明Pod的亲和性调度,这里使用的亲和标签是security=S1,对应上面的Pod“pod-flag”,topologyKey的值被设置为“kubernetes.io/hostname”。
pod-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: gcr.io/google_containers/pause-amd64:3.0

创建pod-affinity后,可以使用kubectl get pods -o wide查看一下是不是和第一步骤中的pod-flag分配在同一个Node节点上了。

3)Pod的互斥性调度
创建第3个Pod,我们希望它不能与参照目标Pod运行在同一个Node上。
anti-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: anti-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
            topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - nginx
        topologyKey: kubernetes.io/hostname
  containers:
  - name: anti-affinity
    image: gcr.io/google_containers/pause-amd64:3.0
这里要求这个新Pod与security=S1的Pod为同一个zone,但是不与app=nginx的Pod为同一个Node。zone是事先给Node所打一标签,位于同一个zone中的多个Node会都打上一个相同值的zone标签。
创建之后,使用kubectl get pods -o wide来查看,是不是被调度到了同一zone中的其Node上去了。
注:上面第3步骤中的实验暂未通过测试,创建Pod报错显示为找不到符合匹配条件的Nodes,问题待解决。

注:列出 node 的时候指定 --show-labels 参数就能查看 node 都添加了哪些 label
[root@bogon ~]# kubectl get nodes --show-labels
NAME        STATUS    ROLES     AGE       VERSION   LABELS
10.0.2.10   Ready     <none>    32m       v1.8.8    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.2.10,zone=north
10.0.2.6    Ready     <none>    10d       v1.8.8    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.2.6,zone=north


9.5 Taints和Tolerations(污点和容忍)
概念:
  • Taint和Toleration是一种处理节点并且让Pod进行规避或者驱逐Pod的弹性处理方式。
  • Taints和Tolerations和搭配使用的,Taints定义在Node节点上,声明污点及标准行为,Tolerations定义在Pod,声明可接受得污点。
  • 在Node上设置一个或多个Taint之后,此节点已被key=value污染,Pod调度不允许(NoSchedule策略)或尽量不(PreferNoSchedule策略)调度到此节点,除非是能够容忍(Tolerations)key=value污点的Pod。

1)可以在命令行为Node节点添加Taints
kubectl taint nodes node1 key=value:NoSchedule

2)在Pod上声明Toleration,有两种声明的语法
tolerations:
- key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
或者:
tolerations:
- key: "key"
  operator: "Exists"
  effect: "NoSchedule"

3)Pod的Toleration声明中几个关键字的使用方法
  • Pod的Toleration声明中的key和effect需要与Taint的设置保持一致才有效;
  • 如果operator的值是Exists,则无须指定value;
  • 如果operator的值是Equal,则value需要与taint中的value设置相等;
  • 如果不指定operator,则默认值为Equal;
  • 空的key配合Exists,代表可以匹配上所有的键和值;
  • 空的effect,代表匹配所有的effect;

4)effect可以定义为
  • NoSchedule 表示不允许调度,已调度的不影响
  • PreferNoSchedule 表示尽量不调度
  • NoExecute 表示不允许调度,已调度的在tolerationSeconds(定义在Tolerations上)后删除

注:tolerationSeconds,表明一个pod在被驱逐前的宽限时间。如果在这个宽限期内,Taint被移除,则不会触发驱逐事件。

Node和Pod上都可以定义多个Taints和Tolerations,Scheduler会根据具体定义进行筛选,Node筛选Pod列表的时候,会保留Tolerations定义匹配的,过滤掉没有Tolerations定义的
过滤的过程是这样的:
  • 如果Node中存在一个或多个影响策略为NoSchedule的Taint,该Pod不会被调度到该Node
  • 如果Node中不存在影响策略为NoSchedule的Taint,但是存在一个或多个影响策略为PreferNoSchedule的Taint,该Pod会尽量不调度到该Node
  • 如果Node中存在一个或多个影响策略为NoExecute的Taint,该Pod不会被调度到该Node,并且会驱逐已经调度到该Node的Pod实例

5)Taint和Toleration的常见使用场景
独占节点
如果想拿出一部分节点,专门给一些特定应用使用,则可以为节点添加这样的Taint:
$ kubectl taint nodes nodename dedicated=groupName:NoSchedule
然后给这些应用的Pod加入对应的Toleration。
具有特殊硬件设备的节点
在集群里可能一小部分节点安装了特殊的硬件设备,如GPU芯片,自然会希望把不需要占用这类硬件的Pod排除在外。
可以用下面的命令为节点设置Taint:
$ kubectl taint nodes nodename special=true:NoSchedule
$ kubectl taint nodes nodename special=true:PreferNoSchedule
然后在Pod中利用对应的Toleration来保障特定的Pod能够使用特定的硬件。
定义Pod驱逐行为,以应对节点故障
在发生故障了的Node上添加一个NoExecute的Taint,正在该节点上运行的Pod将会:
  • 没有设置Toleration的Pod会被立刻驱逐;
  • 配置了对应的Toleration的Pod,如果没有为tolerationSeconds赋值,则会一直留在这一节点上;
  • 配置了对应的Toleration的Pod且为tolerationSeconds赋值,则会在指定的时间后驱逐;
  • 在k8s v1.6版本开始引入的一个Alpha功能,支持在节点故障时NodeController自动为Node设置Taint。这需要在Node节点启动kubelet服务时增加--feature-gates TaintBasedEvictions=true的参数。
注:在启用了TaintBasedEvictions功能后,为了避免一个网络波动就造成大量Pods被驱逐,尤其是当包含很多本地状态的应用时影响更糟,此时可以为这些Pod增加一个Toleration。类似下面这样:
tolerations:
- key: "node.alpha.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoSchedule"
  tolerationSeconds: 6000
对于Node未就绪状态,可以把key设置为node.alpha.kubernetes.io/notReady.

注:如果没有像上面那样为Pod定义"node.alpha.kubernetes.io/unreachable"或"node.alpha.kubernetes.io/notReady"的Toleration,那么系统会自动为其加入一个tolerationSeconds=300的默认值,以确保Pod在被驱逐前再运行5min。


9.6 DaemonSet:在每一个Node上调度一个Pod
DaemonSet,用于管理在集群中每个Node上只运行一份Pod的副本实例。
常见的使用场景有:
  • 在每个Node上运行一个GlusterFS存储或者Ceph存储的Daemon进程。
  • 在每个Node上运行一个日志采集程序,例如Fluentd或者Logstach。
  • 在每个Node上运行一个性能监控程序,采集该Node的运行性能数据,例如Prometheus Node Exporter、collectd、New Relic agent或者Ganglia gmond等。
DaemonSet的Pod调度策略与RC类似,除了使用内置的算法在每台Node上进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node范围进行调度。

示例:fluentd-ds.yaml  配置使得在每个节点上都有一个fluentd 容器
因为众所周知的原因,我们不能直接从google下载容器镜像,需要在每个Node节点上像下面这样处理一下。
docker pull docker.io/bigwhite/fluentd-elasticsearch:1.22
docker tag docker.io/bigwhite/fluentd-elasticsearch:1.22  gcr.io/google_containers/fluentd-elasticsearch:1.22

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd-cloud-logging
  namespace: kube-system
  labels:
    k8s-app: fluentd-cloud-logging
spec:
  template:
    metadata:
       namespace: kube-system
       labels:
         k8s-app: fluentd-cloud-logging
    spec:
      containers:
      - name: fluentd-cloud-logging
        image:  gcr.io/google_containers/fluentd-elasticsearch:1.22
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
        env:
        - name: FLUENTD_ARGS
          value: -q
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: false
        - name: containers
          mountPath: /var/lib/docker/containers
      volumes:
      - name: containers
        hostPath:
          path: /var/lib/docker/containers
      - name: varlog
        hostPath:
          path: /var/log


[root@bogon ~]# kubectl create -f fluentd-ds.yaml
daemonset "fluentd-cloud-logging" created

[root@bogon ~]# kubectl get ds --namespace=kube-system
NAME                    DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-cloud-logging   2         2         2         2            2           <none>          17s

[root@bogon ~]# kubectl get pods --namespace=kube-system
NAME                          READY     STATUS    RESTARTS   AGE
fluentd-cloud-logging-8rkvk   1/1       Running   0          27s
fluentd-cloud-logging-rr45f   1/1       Running   0          27s


9.7 Job:批处理调度
可以通过kubernetes Job资源对象来定义并启动一个批处理任务。批处理任务通常并行(或串行)启动多个计算进程去处理一批工作项(work item),处理完后,整个批处理任务结束。
批处理按任务实现方式不同分为以下几种模式:
  • Job Template Expansion模式
    一个Job对象对应一个待处理的Work item,有几个Work item就产生几个独立的Job,通过适用于Work item数量少,每个Work item要处理的数据量比较大的场景。例如有10个文件(Work item),每个文件(Work item)为100G。
  • Queue with Pod Per Work Item
    采用一个任务队列存放Work item,一个Job对象作为消费者去完成这些Work item,其中Job会启动N个Pod,每个Pod对应一个Work item。
  • Queue with Variable Pod Count
    采用一个任务队列存放Work item,一个Job对象作为消费者去完成这些Work item,其中Job会启动N个Pod,每个Pod对应一个Work item。但Pod的数量是可变的。



Job负责批量处理短暂的一次性任务 (short lived one-off tasks),即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。

Kubernetes支持以下几种Job:
  • 非并行Job:通常创建一个Pod直至其成功结束
  • 固定结束次数的Job:设置.spec.completions,创建多个Pod,直到.spec.completions个Pod成功结束
  • 带有工作队列的并行Job:设置.spec.Parallelism但不设置.spec.completions,当所有Pod结束并且至少一个成功时,Job就认为是成功

根据.spec.completions和.spec.Parallelism的设置,可以将Job划分为以下几种pattern:
Job类型
使用示例
行为
completions
Parallelism
一次性Job
数据库迁移
创建一个Pod直至其成功结束
1
1
固定结束次数的Job
处理工作队列的Pod
依次创建一个Pod运行直至completions个成功结束
2+
1
固定结束次数的并行Job
多个Pod同时处理工作队列
依次创建多个Pod运行直至completions个成功结束
2+
2+
并行Job
多个Pod同时处理工作队列
创建一个或多个Pod直至有一个成功结束
1
2+


Job Spec格式
  • spec.template格式同Pod
  • RestartPolicy仅支持Never或OnFailure
  • 单个Pod时,默认Pod成功运行后Job即结束
  • .spec.completions标志Job结束需要成功运行的Pod个数,默认为1
  • .spec.parallelism标志并行运行的Pod的个数,默认为1
  • spec.activeDeadlineSeconds标志失败Pod的重试最大时间,超过这个时间不会继续重试

Job Controller
负责根据Job Spec创建Pod,并持续监控Pod的状态,直至其成功结束。如果失败,则根据restartPolicy(只支持OnFailure和Never,不支持Always)决定是否创建新的Pod再次重试任务。
一个简单的例子:
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      name: pi
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

$ kubectl create -f ./job.yaml
job "pi" created

$ pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath={.items..metadata.name})
$ kubectl logs $pods
3.141592653589793238462643383279502...

固定结束次数的Job示例
apiVersion: batch/v1
kind: Job
metadata:
  name: busybox
spec:
   completions: 3
  template:
    metadata:
      name: busybox
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo", "hello"]
      restartPolicy: Never


[root@bogon ~]# kubectl get jobs -o wide
NAME      DESIRED   SUCCESSFUL   AGE       CONTAINERS   IMAGES           SELECTOR
busybox   3         3            6h        busybox      busybox:latest   controller-uid=cfdb73c4-22fa-11e8-af50-080027cf1a4c
pi        1         1            6h        pi           perl             controller-uid=53b4f513-22f8-11e8-af50-080027cf1a4c


9.8 Cronjob:定时任务
CronJob即定时任务,就类似于Linux系统的crontab,在指定的时间周期运行指定的任务。启用该功能需要在API Server中增加一个启动参数–runtime-config=batch/v2alpha1=true

CronJob Spec
  • .spec.schedule指定任务运行周期,格式同Cron
  • .spec.jobTemplate指定需要运行的任务,格式同Job
  • .spec.startingDeadlineSeconds指定任务开始的截止期限
  • .spec.concurrencyPolicy指定任务的并发策略,支持Allow、Forbid和Replace三个选项

cronjob.yaml
apiVersion: batch/v2alpha1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
$ kubectl create -f cronjob.yaml
cronjob "hello" created

创建和查看cronjob:
[root@bogon ~]# kubectl create -f cronjob.yaml
cronjob "hello" created

[root@bogon kubernetes]# kubectl get cronjob
NAME      SCHEDULE      SUSPEND   ACTIVE    LAST SCHEDULE   AGE
hello     */1 * * * *   False     1         Fri, 09 Mar 2018 10:41:00 +0800
在Node节点上查看:
[root@localhost ~]# docker ps  -a|grep busybox
0754b875faae        busybox                                    "/bin/sh -c 'date; e…"   58 seconds ago       Exited (0) 58 seconds ago                             k8s_hello_hello-1520563260-gplkd_default_50810739-2343-11e8-9bfe-080027cf1a4c_0
5dd44ef783af        busybox                                    "/bin/sh -c 'date; e…"   About a minute ago   Exited (0) About a minute ago                         k8s_hello_hello-1520563200-c6r8v_default_2c97e342-2343-11e8-9bfe-080027cf1a4c_0
39ce93274ccc        busybox                                    "/bin/sh -c 'date; e…"   2 minutes ago        Exited (0) 2 minutes ago                              k8s_hello_hello-1520563140-bm5pj_default_08c05c53-2343-11e8-9bfe-080027cf1a4c_0
跟踪查看一下job的执行结果:
[root@bogon kubernetes]# kubectl get jobs --watch
NAME               DESIRED   SUCCESSFUL   AGE
busybox            3         3            8h
hello-1520563260   1         1            2m
hello-1520563320   1         1            1m
hello-1520563380   1         1            45s
pi                 1         1            8h
hello-1520563440   1         0         0s
hello-1520563440   1         0         0s
hello-1520563440   1         1         7s
hello-1520563260   1         1         3m
hello-1520563260   1         1         3m
hello-1520563260   1         1         3m
hello-1520563500   1         0         0s
hello-1520563500   1         0         1s
hello-1520563500   1         1         7s
hello-1520563320   1         1         3m
hello-1520563320   1         1         3m
hello-1520563320   1         1         3m
hello-1520563560   1         0         0s
hello-1520563560   1         0         0s
hello-1520563560   1         1         8s
hello-1520563380   1         1         3m
hello-1520563380   1         1         3m
hello-1520563380   1         1         3m

[root@bogon kubernetes]# kubectl get pods --show-all|grep hello
hello-1520563620-nn2fp         0/1       Completed   0          2m
hello-1520563680-4xc2f         0/1       Completed   0          1m
hello-1520563740-22jbt         0/1       Completed   0          31s

当然,也可以用kubectl run来创建一个CronJob:
kubectl run hello --schedule="*/1 * * * *" --restart=OnFailure --image=busybox -- /bin/sh -c "date; echo Hello from the Kubernetes cluster"
$ kubectl get cronjob
NAME      SCHEDULE      SUSPEND   ACTIVE    LAST-SCHEDULE
hello     */1 * * * *   False     0         <none>
$ kubectl get jobs
NAME               DESIRED   SUCCESSFUL   AGE
hello-1202039034   1         1            49s
$ pods=$(kubectl get pods --selector=job-name=hello-1202039034 --output=jsonpath={.items..metadata.name} -a)
$ kubectl logs $pods
Mon Aug 29 21:34:09 UTC 2016Hello from the Kubernetes cluster

# 注意,删除cronjob的时候不会自动删除job,这些job可以用kubectl delete job来删除
$ kubectl delete cronjob hello
cronjob "hello" deleted



9.9 自定义调度器
从k8s v1.6版本开始,增加了Pod的自定义调度器功能。如果Pod中提供了自定义的调度器名称,那么默认的调度器就会忽略该Pod,转由指定的调度器完成Pod的调度。
apiVersion:v1
kind:Pod
metadata:
  name: annotation-second-scheduler
  labels:
    name: multischeduler-example
spec:
  schedulerName: my-scheduler
  containers:
  - name: pod-with-second-annotation-container
    image: nginx

可以使用任何语言来实现简单或复杂的自定义调度器。
下面是使用bash脚本进行实现,调度策略为随机选择一个Node。

#!/bin/bash
SERVER= 'localhost:8001'
while true;
do
  for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"');
  do
    NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
    NUMNODES=${#NODES[@]}
    CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
    curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind":"Binding", "metadata":{"name":"'$PODNAME'"}, "target":{"apiVersion":"v1", "kind":"Node", "name":"'$CHOSEN'"}}'
    echo "Assigned $PODNAME to $CHOSEN"
  done
  sleep 1
done


10、Init Container(初始化容器)

在很多应用场景中,应用在启动之前都需要进行一些初始化操作,例如:
  • 等待其他关联组件正确运行后;
  • 基于环境变量或配置模板生成配置文件;
  • 从远程数据库获取本地所城配置,或将自身注册到某个中央数据库中;
  • 下载相关依赖包,或者对系统进行一些预配置操作;
Init Container知识:
  • Init Container是在v1.3中引入,并在v1.5时被更新为Beta版本,用于在启动应用容器之前启动一个或多个“初始化”容器,完成应用容器所需的预置条件。
  • Init Container与应用容器本质上是一样的,但它们是仅运行一次就结束的任务,并且必须在成功执行完成后,系统才能继续执行下一个容器。
  • 根据pod的重启策略,当Init Container执行失败,在设置了RestartPolicy=Never时,Pod将会启动失败;而设置RestartPolicy=Always时,Pod将会被系统重启。

示例:一个运行nginx的应用容器,在启动之前使用Init Container为nginx创建一个index.html的主页文件。这里需要为Init Container与nginx设置一个共享的volume。
nginx-init-containers.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  annotations:
spec:
  #These containers are run during pod initialization
  initContainers:
  - name: install
    images: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}

创建:
[root@bogon ~]# kubectl create -f nginx-init-containers.yaml
pod "nginx" created

查看 nginx应用容器中的共享文件:
[root@bogon ~]# kubectl exec -it nginx cat /usr/share/nginx/html/index.html

init container 与应用容器的区别如下:
(1)init container 的运行方式与应用容器不同,它们必须先于应用容器执行完成,当设置了多个 init container 时,将按顺序逐个运行,并且只有前一个 init container 运行成功之后才能运行后一个 init container。当所有 init container 都成功运行后,Kubernetes 才会初始化 Pod 的各种信息,并开始创建和运行应用容器。
(2)在 init container 的定义中也可以设置资源限制、volume 的使用和安全策略,等等。但资源限制的设置与应用容器略有不同。
  • 如果多个 init container 都定义了资源请求/资源限制,则取最大的值作为所有 init container 的资源请求值/资源限制值。
  • Pod 的有效 (efective) 资源请求值/资源限制值取以下二者中的较大值。
    • 所有应用容器的资源请求值/资源限制值之和。
    • init container 的有效资源请求值/资源限制值。
  • 调度算法将基于 Pod 的有效资源请求值/资源限制值进行计算,也就是说 init container 可以为初始化操作预留系统资源,及时后续应用容器无须使用这些资源。
  • Pod 的有效 QoS 等级适用于 init container 和应用容器。
  • 资源配额和限制将根据 Pod 的有效资源请求/限制,与调度机制一致。
(3)init container 不能设置 readinessProbe 探针,因为必须在它们成功运行后才能继续运行 Pod 中定义的普通容器。

在 Pod 重新启动(Restart)时,init container 将会重新运行,场景的 Pod 重启场景如下:
  • init container 的镜像被更新时,init container 将会重新运行,导致 Pod 重启。
  • 仅更新应用容器的镜像则只会使得应用容器被重启。
  • Pod 的 infrastructure 容器(pause)更新时,Pod 将会重启。
  • 若 Pod 中的所有应用容器都停止了,并且 RestartPolicy=Always,则 Pod 将会重启。

11、Pod的升级和回滚

当集群中的某个服务需要升级时,需要停止目前与该服务相关的所有 Pod,然后下载新版本镜像并创建新的 Pod。如果集群规模较大,则这个工作就变成了一个挑战,而且先全部停止然后逐步升级的方式会导致较长时间的服务不可用。Kubernetes 提供了滚动升级功能来解决上述问题。
如果 Pod 是通过 Deployment 创建的,则用户可以在运行时修改 Deployment 的 Pod 定义(spec.template)或镜像名称,并应用到 Deployment 对象上,系统即可完成 Deployment 的自动更新操作。如果在更新过程中发生了错误,则还可以通过回滚(Rollback)操作恢复 Pod 的版本。

11.1 Deployment
Deployment为Pod和Replica Set(升级版的 Replication Controller)提供声明式更新。
注意:您不该手动管理由 Deployment 创建的 Replica Set,否则您就篡越了 Deployment controller 的职责!

Deployment的典型的用例如下:
  • 创建一个Deployment对象来生成对应的ReplicaSet,并完成Pod副本的创建过程。
  • 检查Deployment的状态来查看部署动作是否完成,Pod副本的数量是否达到预期的值。
  • 更新Deployment以创建新的Pod,通过修改Pod-Template-Spec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中。
  • 如果当前状态不稳定,则回滚到一个早先的Deployment版本。
  • 暂停Deployment,以便于一次性修改多个PodTemplateSpec的配置项,然后再恢复Deployment,进行新的发布。
  • 扩容Deployment以满足更高的负载。
  • 查看Deployment 的状态,以此作为发布是否成功的指标。
  • 清除旧的不必要的 ReplicaSets。

11.2 创建 Deployment
下面是一个 Deployment 示例,它创建了一个 ReplicaSet 来启动3个 nginx pod。先是初始时创建了一个副本的Pod,然后在后面又扩容到了3副本。
下载示例文件nginx-deployment.yaml并执行命令:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
执行创建命令:
[root@gqtest ~]# kubectl create -f nginx-deployment.yaml --record
deployment "nginx-deployment" created
注:--record选项可以记录当前命令执行记录,便于以后查看一个deployment revision中执行了哪些命令。
[root@gqtest ~]# kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            0           2m
看到available数量仍为0,继续查看一下该deployment的详细情况:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 0 available | 1 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
  Type        Status    Reason
  ----        ------    ------
  Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-4087004473 (1/1 replicas created)
Events:
  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  3m        3m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
上面输出信息没有异常,再查看一次该deployment的状态,如下所示,available的实例数量已经是1,说明经过一个创建过程(往往会因为要在线下载docker image镜像而需要等待一段时间),已经成功创建:
[root@gqtest ~]# kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           4m
查看Replica Set信息,可以到到ReplicaSet名称是在Deployment后添加一串数字:
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-4087004473   1         1         1         4m
继续查看pod的状态,如下所示:
[root@gqtest ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
mysql-mn49n                         1/1       Running   1          3d
myweb-k6fp0                         1/1       Running   1          3d
myweb-m9nv9                         1/1       Running   1          3d
nginx-deployment-4087004473-xdxhn   1/1       Running   0          7m

11.3 更新Deployment
Deployment的更新(rollout)当且仅当Deployment的pod template中的label发生更新或者镜像发生更改时,才会被触发。像Deployment扩容,不会触发rollout事件。
假如我们现在想要让 nginx pod 使用nginx:1.9.1的镜像来代替原来的nginx:1.7.9的镜像。
[root@gqtest ~]#  kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated

或者可以使用edit命令来编辑 Deployment,修改 .spec.template.spec.containers[0].image ,将nginx:1.7.9 改写成 nginx:1.9.1。
[root@gqtest ~]#  kubectl edit deployment/nginx-deployment
查看更新结果:
[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
[root@gqtest ~]# kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            0           18m
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   1         1         0         4m
nginx-deployment-4087004473   0         0         0         19m
从上面的输出信息中看到,正在创建一个新的ReplicaSet的过程中,同时已经将旧的缩容到了0个replica。
查看pod状态时,已经看不到旧的pod了:
[root@gqtest ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
mysql-mn49n                         1/1       Running   1          3d
myweb-k6fp0                         1/1       Running   1          3d
myweb-m9nv9                         1/1       Running   1          3d
nginx-deployment-3599678771-chzwr   1/1       Running   0          6m
再次执行查看Deployment更新结果的命令,显示已经成功完成:
[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   1         1         1         8m
nginx-deployment-4087004473   0         0         0         23m
最后查看一下该Deployment任务的详细信息:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 1 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
  Type        Status    Reason
  ----        ------    ------
  Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-3599678771 (1/1 replicas created)
Events:
  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
  10m        10m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
  10m        10m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0

在升级过程中,Deployment 能够保证服务不中断,并且副本数量始终维持为用户指定的数量(DESIRED)。
在 Deployment 的定义中,可以通过 spec.strategy 指定 Pod 更新的策略,目前支持两种策略:RollingUpdate(滚动更新)和  Recreate(重建),默认值为 RollingUpdate。在前面的例子中使用的就是 RollingUpdate 策略。
  • Recreate(重建):设置 spec.strategy.type=Recreate,表示 Deployment 在更新 Pod 时,会先杀掉所有正在运行的 Pod,然后创建新的 Pod。
  • RollingUpdate(滚动更新):设置 spec.strategy.type=RollingUpdate,表示 Deployment 会以滚动更新的方式来逐个更新 Pod 。同时,可以通过设置 spec.strategy.rollingUpdate 下的两个参数(maxUnavailable 和 maxSurge)来控制滚动更新的过程。


Rollover(多重更新)的说明:
假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的 Pod 都创建完成后才开始改变航道。

Label selector 更新:
上面的演示是基于image文件发生了更改的条件下的一个Deployment更新的样例。通常不鼓励更新 label selector,虽然技术上可行,但关联影响较多。

11.4 回退Deployment
可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。

只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。

我们先故意执行一个有错误的Deployment任务:
[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx: 1.99
deployment "nginx-deployment" image updated
我们故障写了一个不存在的镜像文件版本,Deployment rollout任务会补卡住,如下所示:
[root@gqtest ~]# kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
可以看到pod的状态处于"ImagePullBackOff"的错误状态:
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   0         0         0         23m
nginx-deployment-4087004473   0         0         0         38m
nginx-deployment-538426637    1         1         0         3m
[root@gqtest ~]# kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
^C[root@gqtest ~]# kubectl get pods
NAME                               READY     STATUS             RESTARTS   AGE
mysql-mn49n                        1/1       Running            1          3d
myweb-k6fp0                        1/1       Running            1          3d
myweb-m9nv9                        1/1       Running            1          3d
nginx-deployment-538426637-g55n4   0/1        ImagePullBackOff    0          3m
注:Deployment controller会自动停止坏的 rollout,并停止扩容新的 ReplicaSet。
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 0 available | 1 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
  Type        Status    Reason
  ----        ------    ------
  Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-538426637 (1/1 replicas created)
Events:
  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  40m        40m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
  25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
  25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0
  5m        5m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1
  5m        5m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0
我们需要回退到稳定版本的Deployment revision。
先查看一下Deployment的revision记录:
[root@gqtest ~]#  kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1        kubectl create -f nginx-deployment.yaml --record
2        kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3        kubectl set image deployment/nginx-deployment nginx=nginx:1.99
注:因为我们创建 Deployment 的时候使用了--recored参数可以记录命令,我们可以很方便的查看每次 revision 的变化。
查看单个revision 的详细信息:
[root@gqtest ~]#  kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
  Labels:    app=nginx
    pod-template-hash=3599678771
  Annotations:    kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
  Containers:
   nginx:
    Image:    nginx:1.9.1
    Port:    80/TCP
    Volume Mounts:    <none>
    Environment Variables:    <none>
  No volumes.
回退到历史版本
回退到上一个版本:
[root@gqtest ~]#  kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back
回退到一个指定的版本:
# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment "nginx-deployment" rolled back

查看下回退结果:
[root@gqtest ~]# kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           46m
查看下回退版本所产生的事件记录:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 1 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
  Type        Status    Reason
  ----        ------    ------
  Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-3599678771 (1/1 replicas created)
Events:
  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  46m        46m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
  32m        32m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0
  11m        11m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1
  11m        11m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0
  32m        2m        2    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
  2m        2m        1    {deployment-controller }            Normal        DeploymentRollback    Rolled back deployment "nginx-deployment" to revision 2
  2m        2m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-538426637 to 0

可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。

11.5 Deployment 扩容
使用以下命令将nginx-deployment扩容为3副本:
[root@gqtest ~]#  kubectl scale deployment nginx-deployment --replicas 3
deployment "nginx-deployment" scaled
[root@gqtest ~]# kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           51m
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   3         3         3         36m
nginx-deployment-4087004473   0         0         0         51m
nginx-deployment-538426637    0         0         0         16m
[root@gqtest ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
mysql-mn49n                         1/1       Running   1          3d
myweb-k6fp0                         1/1       Running   1          3d
myweb-m9nv9                         1/1       Running   1          3d
nginx-deployment-3599678771-hz7l6   1/1       Running   0          7m
nginx-deployment-3599678771-j136m   1/1       Running   0          1m
nginx-deployment-3599678771-kqwpf   1/1       Running   0          1m
查看该Deployment的事件记录:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        3 updated | 3 total | 3 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
  Type        Status    Reason
  ----        ------    ------
  Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-3599678771 (3/3 replicas created)
Events:
  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  52m        52m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
  37m        37m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0
  17m        17m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1
  17m        17m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0
  37m        7m        2    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
  7m        7m        1    {deployment-controller }            Normal        DeploymentRollback    Rolled back deployment "nginx-deployment" to revision 2
  7m        7m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-538426637 to 0
  1m        1m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 3

注:假如集群中启用了horizontal pod autoscaling,您可以给 Deployment 设置一个 autoscaler,基于当前 Pod的 CPU 利用率选择最少和最多的 Pod 数。

关于Deployment的比例扩容:
RollingUpdate Deployment 支持同时运行一个应用的多个版本。或者 autoscaler 扩 容 RollingUpdate Deployment 的时候,正在中途的 rollout(进行中或者已经暂停的),为了降低风险,Deployment controller 将会平衡已存在的活动中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。这被称为比例扩容。

11.6 暂停和恢复Deployment
可以在发出一次或多次更新前暂停一个 Deployment,然后再恢复它。这样您就能多次暂停和恢复 Deployment,在此期间进行一些修复工作,而不会发出不必要的 rollout。
[root@gqtest ~]#  kubectl rollout pause deployment/nginx-deployment
deployment "nginx-deployment" paused
[root@gqtest ~]# kubectl set image deploy/nginx-deployment nginx=nginx:1.9.2
deployment "nginx-deployment" image updated
即使执行了上面的镜像更改命令,也没有触发任何Deployment rollout的事件。而且可以继续做更多的修改,例如:
[root@gqtest ~]# kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=256Mi
deployment "nginx-deployment" resource requirements updated
注:-c选项的作用是指定容器唯一标识名
当各种变更都准备妥当后,我们恢复该deployment:
[root@gqtest ~]# kubectl rollout resume deploy nginx-deployment
deployment "nginx-deployment" resumed
查看一下deployment处理结果,使用-w选项(watch)跟踪输出如下所示:
[root@gqtest ~]# kubectl get rs -w
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-27997383     2         2         0         2m
nginx-deployment-3599678771   2         2         2         1h
nginx-deployment-4087004473   0         0         0         1h
nginx-deployment-538426637    0         0         0         59m
NAME                        DESIRED   CURRENT   READY     AGE
nginx-deployment-27997383   2         2         1         4m
nginx-deployment-3599678771   1         2         2         1h
nginx-deployment-3599678771   1         2         2         1h
nginx-deployment-27997383   3         2         1         4m
nginx-deployment-3599678771   1         1         1         1h
nginx-deployment-27997383   3         2         1         4m
nginx-deployment-27997383   3         3         1         4m
nginx-deployment-27997383   3         3         2         5m
nginx-deployment-3599678771   0         1         1         1h
nginx-deployment-3599678771   0         1         1         1h
nginx-deployment-3599678771   0         0         0         1h
可以看到已经成功完成了合并了多次更新内容后的deployment任务:
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-27997383     3         3         3         13m
nginx-deployment-3599678771   0         0         0         1h
nginx-deployment-4087004473   0         0         0         1h
nginx-deployment-538426637    0         0         0         1h

11.7 Deployment 状态
Deployment 在生命周期中有多种状态。在创建一个新的 ReplicaSet 的时候它可以是 progressing 状态, complete 状态,或者 fail to progress 状态。
进行中的 Deployment
Kubernetes 将执行过下列任务之一的 Deployment 标记为 progressing 状态:
  • Deployment 正在创建新的ReplicaSet过程中。
  • Deployment 正在扩容一个已有的 ReplicaSet。
  • Deployment 正在缩容一个已有的 ReplicaSet。
  • 有新的可用的 pod 出现。
可以使用kubectl rollout status命令监控 Deployment 的进度。

完成的 Deployment
Kubernetes 将包括以下特性的 Deployment 标记为 complete 状态:
  • Deployment 最小可用。最小可用意味着 Deployment 的可用 replica 个数等于或者超过 Deployment 策略中的期望个数。
  • 所有与该 Deployment 相关的replica都被更新到了指定版本,也就说更新完成。
  • 该 Deployment 中没有旧的 Pod 存在。
可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status将返回一个0值的 Exit Code。
[root@gqtest ~]# kubectl rollout  status  deploy/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@gqtest ~]# echo $?
0

版本记录的清理策略
可以设置 Deployment 中的 .spec.revisionHistoryLimit 项来指定保留多少旧的 ReplicaSet。 余下的将在后台被当作垃圾收集。默认的,所有的 revision 历史就都会被保留。在未来的版本中,将会更改为2。
注意: 将该值设置为0,将导致所有的 Deployment 历史记录都会被清除,该 Deployment 就无法再回退了。

11.8 金丝雀 Deployment
如果想要使用 Deployment 对部分用户或服务器发布 release,可以创建多个 Deployment,每个 Deployment 对应一个 release。

11.9 编写 Deployment Spec
在所有的 Kubernetes 配置中,Deployment 也需要apiVersion,kind和metadata这些配置项。
Deployment也需要 .spec section.
Pod Template
  • .spec.template 是 .spec中唯一要求的字段。
  • .spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段。
  • 为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了)和适当的重启策略。
  • .spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。
Replicas
  • .spec.replicas 是可以选字段,指定期望的pod数量,默认是1。
Selector
  • .spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。
  • 如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是 .spec.template.metadata.labels。
  • 在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。
注意: 不应该再创建其他label跟这个selector匹配的pod,或者通过其他Deployment,或者通过其他Controller,例如ReplicaSet和ReplicationController。否则该Deployment会被把它们当成都是自己创建的。Kubernetes不会阻止这么做。如果有多个controller使用了重复的selector,controller们就会互相打架并导致不正确的行为。
策略
  • .spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是"Recreate"或者是 "RollingUpdate"(按比例更新)。"RollingUpdate"是默认值。
  • Recreate Deployment,.spec.strategy.type==Recreate时,在创建出新的Pod之前会先杀掉所有已存在的Pod。
  • Rolling Update Deployment,.spec.strategy.type==RollingUpdate时,Deployment使用rolling update 的方式更新Pod 。可以指定maxUnavailable 和 maxSurge 来控制 rolling update 进程。
  • Max Unavailable,.spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。
  • Max Surge,.spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。
Progress Deadline Seconds
  • .spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing——表现为resource的状态中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。
  • 如果设置该参数,该值必须大于 .spec.minReadySeconds。
Min Ready Seconds
  • .spec.minReadySeconds是一个可选配置项,用来指定没有任何容器crash的Pod并被认为是可用状态的最小秒数。默认是0(Pod在ready后就会被认为是可用状态)。
Rollback To
  • .spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。
Revision
  • .spec.rollbackTo.revision是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到历史中最老的revision。
Revision History Limit
  • Deployment revision history存储在它控制的ReplicaSets中。
  • .spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦删除了旧的RepelicaSet,该Deployment就无法再回退到那个revison了。
  • 如果将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。
Paused
  • .spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。
  • Deployment被创建之后默认是非paused。


11.10 其他管理对象的更新策略
Kubernetes 从 v1.6 版本开始,对 DaemonSet 和 StatfulSet 的更新策略也开始引入类似于 Deployment 的滚动升级,通过不同的策略,自动完成应用的版本升级。
(1)DaemonSet  的更新策略
目前 DaemonSet 的升级策略包括两种:OnDelete 和 RollingUpdate
  • OnDelete:DaemonSet 的默认升级策略,与 v1.5 及之前版本的 Kubernetes 保持一致。当使用 OnDelete 作为升级策略时,在创建好新的 DaemonSet 配置后,新的 Pod 并不会被自动创建,直到用户手动删除旧版本的 Pod,才出发新建操作。
  • PollingUpdate:从 Kubernetes v1.6 版本开始引入。当使用 RollingUpdate 作为升级策略对 DaemonSet 进行更新时,旧版本的 Pod 将被自动杀掉,然后自动创建新版本的 DaemonSet Pod,整个过程与普通 Deployment 的滚动升级一样是可控的。不过有两点不同于普通 Pod 的滚动升级,一是目前 Kubernetes 还不支持查看和管理 DaemonSet 的更新历史记录;二是 DaemonSet 的回滚(Rollback)并不能如同 Deployment 一样直接通过命令来实现,而是必须通过再次提交旧版本配置的方式实现。
(2)StatefulSet  的更新策略
Kubernetes 从 v1.6 版本开始,针对 StatefulSet 的更新策略正逐渐向 Deployment 和 DaemonSet 的更新策略看起,也将实现 RollingUpdate、Paritioned 和 OnDelete 集中策略,目前是保证 StatefulSet 中各 Pod 有序地、逐个地更新,并且能够保留更新历史,也能回滚到某个历史版本。


12、Pod的自动扩容和缩容模式——HPA

Horizontal Pod Autoscaler
横向自动扩容功能,简称HPA,也是k8s系统中的一种资源对象。在v1.1版本中首次发布,在v1.2版本中升级为稳定版。在v1.6版本之前,仅支持使用CPU负载作为是否扩容的判定条件;自v1.6版本开始提供了根据应用自定义指标进行自动扩容和缩容的功能,不过目前仍为实验性质。

HPA 与之前的   RCDeployment  一样,也属于一种 Kubernetes 资料对象。通过追踪分析 RC 控制的所有目标 Pod 的负载变化情况,来确定是否需要针对性地调整目标 Pod 的副本数,这是 HPA 的实现原理。当前,HPA 可以有以下两种方式作为 Pod 负载的调度指标。
  • CPUUtilizationPercentage。
  • 应用程序自定义的度量指标,比如服务在每秒内的相应的请求数(TPS 或 QPS)。

可以通过yaml文件定义一个HPA对象,或者直接使用命令创建一个HPA对象。
yaml文件定义的样例:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  maxReplicas: 10
  minReplicas: 1
  scaleTargetRef:
    kink: Deployment
    name: php-apache
  targetCPUUtilizationPercentage: 90
注:当名为php-apache的deployment的Pods副本的CPU使用率超过90%时,会触发自动扩容行为。但扩容或缩容都必须满足的约束条件是Pod的副本数量要在1~10之间。
以上为命令行方式创建一个HPA:
[root@gqtest ~]# kubectl autoscale deployment nginx-deployment --min=1 --max=5 --cpu-percent=80
deployment "nginx-deployment" autoscaled

13、StatefulSet

13.1 StatefulSet(有状态系统服务设计)
在k8s v1.5中引入,在Kubernetes 1.7中还是beta特性。
现实中很多服务是有状态的,如MySQL集群、kafka集群、ZooKeeper集群等,这些应用集群有以下特点:
  • 每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现和通信;
  • 集群的规模是相对固定的,且不能随意变动;
  • 集群里每个节点都是有状态的,通常会持久化数据到永久存储中;
  • 如果磁盘损坏导致集群里某个节点无法正常运行,则集群功能会部分受损;

StatefulSet是Deployment/RC的一个特殊变种,有如下特性:
  • StatefulSet里每个Pod都有稳定、唯一的网络标识,可以用来发现集群内其他成员。假设StatefulSet名字叫kafka,那么第1个Pod会命名为kafka-0,第2个Pod叫kafka-1,以此类推。
  • StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
  • StatefulSet里的Pod采用稳定的持久化存储卷,通过PV/PVC实现,删除Pod时默认不会删除与StatefulSet相关的存储卷。
  • StatefulSet需要与Headless Service配合使用,需要在每个StatefulSet的定义中声明它属于哪个Headless Service。
Headless Service没有Cluster IP,当解析Headless Service的DNS域名时,得到的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上,又为受Headless Service控制的每个Pod实例创建了一个DNS域名,格式为:$(podname).$(headless service name)。
样例:一个3节点的kafka的StatefulSet集群
  • 该应用集群的headless service名称定义为kafka
  • 该应用集群的StatefulSet名字为kafka,则StatefulSet里的3个Pod的名称分别为:kafka-0,kafka-1,kafka-2
  • 该应用集群的StatefulSet中3个Pod的DNS名称应该是:kafka-0.kafka,kafka-1.kafka,kafka-2.kafka
可以在该应用集群的配置文件中直接使用上述DNS名称来代表相应的Pod。

13.2 使用 StatefulSet 搭建 MongoDB 集群
以 MongoDB 为例,使用 StatefulSet 完成 MongoDB 集群的创建,为每个 MongoDB 实例在共享存储中(NFS/Ceph/GlusterFS)申请一片存储空间,以实现一个无单点故障、高可用、可动态扩展的  MongoDB 集群。

前提条件
在创建 StatefulSet 之前,需要确保在 Kubernetes 集群中管理员已经创建好了共享存储,并能够与 StorageClass 对接,以实现动态存储供应的模式。

创建 StatefulSet
为了完成 MongoDB 集群的搭建,需要创建如下三个资源对象。
  • 一个 StorageClass,用于 StatefulSet 自动为各个应用 Pod 申请 PVC。
  • 一个 Headless Service,用于维护 MongoDB 集群的状态。
  • 一个 StatefulSet。
1)首先,保证拥有一个 StorageClass 对象
下面是基于Ceph实现的,不同的存储后端会有细节配置上的不同。
kube-ceph-storage-class.yaml
apiVersion: storage.k8s.io/v1  
kind: StorageClass  
metadata:  
  name: rbd  
provisioner:   ceph.com/rbd
parameters:  
  monitors: 10.140.0.4:6789,10.140.0.3:6789,10.140.0.2:6789
  adminId: admin  
  adminSecretName: ceph-admin-secret  
  adminSecretNamespace: kube-system  
  pool: kube  
  userId: admin  
  userSecretName: ceph-admin-secret  
  imageFormat: "2"  
  imageFeatures: layering

启动服务:
$ kubectl create -f kube-ceph-storage-class.yaml


2)接下来创建对应的Headless Service
mongo-sidecar 作为 MongDB 集群的管理者,将使用此 Headless Service 来维护各个 MongoDB 示例之间的集群关系,以及集群规模变化时的自动更新。
mongo-headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mongo
  labels:
    name: mongo
spec:
  ports:
  - port: 27017
    targetPort: 27017
  clusterIP: None
  selector:
    role: mongo


3)创建 StatefulSet
配置 mongo statefulset 配置文件:
statefulset-mongo.yaml
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: mongo
spec:
  serviceName: "mongo"
  replicas: 3
  template:
    metadata:
      role: mongo
      environment: test
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mongo
        image: mongo
        command:
        - mongod
        - "--replSet"
        - rs0
        - "--smallfiles"
        - "--noprealloc"
        ports:
        - containerPort: 27017
        volumeMounts:
        - name: mongo-persistent-storage
          mountPath: /data/db
      - name: mongo-sidecar
        image: cvallance/mongo-k8s-sidecar
        env:
        - name: MONGO_SIDECAR_POD_LABELS
          value: "role=mongo,environment=test"
        - name: KUBERNETES_MONGO_SERVICE_NAME
          value: "mongo"
    volumeClaimTemplates:
    - metadata:
        name: mongo-persistent-storage
        annotations:
          volume.beta.kubernetes.io/storage-class: "rbd"
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 10Gi

其中的主要配置说明如下:
(1)在该 StatefulSet 的定义中包括两个容器:mongo 和 mongo-sidecar。mongo 是主服务程序,mongo-sidecar 是将多个 mongo 示例进行集群设置的工具。mongo-sidecar 中的环境变量如下。
  • MONGO_SIDECAR_POD_LABELS:设置为 mongo 容器的标签,用于 sidecar 查询它所需要管理的 MongoDB 集群示例。
  • KUBERNETES_MONGO_SERVICE_NAME:它的值为 "mongo",表示 sidecar 将使用 "mongo" 这个服务名来完成 MongoDB 集群的设置。
(2)replicas:表示 3 这个 MongoDB 集群由 3 个 mongo 实例组成。
(3)volumeCliaiomTemplates:是 StatefulSet 最重要的存储设置。在 annotations 段设置 volume.beta.kubernetes.io/storage-class="rbd" 表示使用名为 “rbd” 的 StirageClass 自动为每个 mongo Pod 实例分配后端存储。resources.requests.storage=10Gi 表示为每个 mongo 实例分配 100Gi 的磁盘空间。
创建 StatefulSet:
$ kubectl apply -f statefulset-mongo.yaml

最终可以看到 StatefulSet 依次创建并启动 3 个 mongo Pod 实例,它们的名字依次为 mongo-0、mongo-1、mongo-2:
$ kubectl get pods -l role=mongo

StatefulSet 会用 volumeClaimTemplates 中的定义为每个 Pod 副本创建一个 PVC 实例,每个 PVC 的名称由 StatefulSet 定义中 volumeClaimTemplates 的名称和 Pod 副本的名字组合而成,查看系统中的 pvc,可以验证这一点:
$ kubectl get pvc

下面是 mongo-0 这个 Pod 中的 volumes 设置,可以看到系统自动为其挂载了对应的 PVC:
$ kubectl get pod mongo-0 -o yaml

至此,一个由 3 个实例组成的 MongoDB 集群就创建完成了。其中每个实例都拥有稳定的名称和独立的存储空间。

4)查看 MongoDB 集群状态
登入任意一个 mongo Pod,在 mongo 命令行界面用 rs.status() 命令查看 MongoDB 集群的状态,可以看到 mongo 集群已通过 sidecar 完成了创建。集群中包含了 3 个节点,每个节点的名称都是 StatefulSet 设置的 DNS 域名格式的网络标识名称:
  • mongo-0.mongo.default.svc.cluster.local
  • mongo-1.mongo.default.svc.cluster.local
  • mongo-2.mongo.default.svc.cluster.local
同时,可以看到 3 个 mongo 实例各自角色(PRIMARY 或 SECONDARY)也都进行了正确的设置。
$ kubectl exec -it mongo-0 -- mongo

对于需要访问这个 mongo 集群的 Kubernetes 集群内部客户端来说,可以通过 Headless Service “mongo” 获取到后端的所有 Endpoints 列表,并组合为数据库链接串,例如 “mongodb://mongo-0.mongo,mongo-1.mongo,mongo-2.mongo:27017/dbname_?”。

5)StatefulSet 的场景应用场景
下面对 MongoDB 集群场景的两种场景进行操作,说明 StatefulSet 对用状态应用的自动化管理功能。
MongoDB 集群的扩容
假设在系统运行过程中,3 个 mongo 实例不足以满足业务的需求,这时就需要用对 mongo 集群进行扩容。仅需要通过对 StatefulSet 进行 scale 操作,就能实现在 mongo 集群中自动添加新的 mongo 节点。
扩展实例命令:
$ kubectl scale --replicase=4 statefulset mongo

查看实例数量:
$ kubectl get po -l role=mongo

进入某个实例查看 mongo 集群的状态,可以看到第 4 个节点已经加入:
$ kubectl exec -it mongo-0 --mongo

同时,系统也为 mongo-3 分配了一个新的 PVC 用于保存数据。
自动故障恢复
假设在系统运行过程中,某个 mongo 实例或其所在主机发生故障,则 StatefulSet 将会自动重建该 mongo 实例,并保证其身份(ID)和使用的数据(PVC)不变。
以 mongo-0 实例发生故障为例,StatefulSet 将会自动重建 mongo-0 实例,并未其挂载之前分配的 PVC “mongo-persistent-storage-mongo-0”。服务 “mongo-0” 在重新启动后,原数据库中的数据不会丢失,可继续使用。
$ kubectl get po -l role=mongo
$ kubectl get pod mongo-0 -o yaml

进入某个实例查看 mongo 集群的状态,mongo-0 在发生故障前在集群中的角色为 PRIMARY,在其脱离集群后,mongo 集群会自动选出一个 SECONDARY 节点提升为 PRIMARY 节点(本例中为 mongo-2)。重启后的 mongo-0 则会称为一个新的 SECONDARY 节点。
$ kubectl exec -it mongo-0 -- mongo

从上面的例子中可以看到,Kubernetes 使用 StatefulSet 来搭建有状态的应用集群(MongoDB、MySQL 等),同部署无状态的应用一样简便。Kubernetes 能够保证 StatefulSet 中各应用实例在创建和运行的过程中,都具有固定的身份标识和独立的后端存储,还支持在运行时对集群规模进行扩容、保障集群的高可用等非常重要的功能。


参考资料:
《Kubernetes权威指南——从Docker到Kubernetes实践全接触》第2章

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/578893
推荐阅读
相关标签
  

闽ICP备14008679号