pod详解

pod k8s中服务执行的最小单位,接下来详细的说明跟pod相关的自定义配置和原理机制。

pod 生命周期

执行 kubectl get pod 查看pod的信息,可以看到有个状态信息,里面表示了当前pod的状态。

1
2
3
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-65959796fb-j49cr 1/1 Running 0 77m

pod 有多种声明周期状态,下面详细说明

pod 重启策略

pod 内有个 RestartPolicy 字段用于控制pod 内所有容器的重启策略;

几种类型说明:

默认值为Always。

  • Always:当容器失效时,由kubelet自动重启该容器。

  • OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。

  • Never:不论容器运行状态如何,kubelet都不会重启该容器。

pod 健康检查机制

如何定义pod 是否监控,这个定义k8s 无法为我们做这个判断,难度pod中的业务容器正确运行就算健康吗?这些健康的定义有时候需要我们根据业务的定义来做自定义的判断

k8s 提供了两类探针来检查

  • LivenessProbe

判断容器状态是存活就为可用状态,如果没配置就一直认为是可用状态。

  • ReadinessProbe

用于判断服务是否可用来判断服务是否可用;

对于上面的2种方式,都可以通过三种操作方式来实现

  1. ExecAction 通过在容器内执行命令的方式,返回0 表示健康,否则不健康

    示例:

    • 创建一个pod
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    apiVersion: v1
    kind: Pod
    metadata:
    labels:
    test: liveness
    name: liveness-exec
    spec:
    containers:
    - name: liveness
    image: centos:7
    args:
    - /bin/sh
    - -c
    - touch /healthy; sleep 30; rm -rf /healthy; sleep 600
    livenessProbe:
    exec:
    command:
    - cat
    - /healthy
    initialDelaySeconds: 5
    periodSeconds: 5


    执行apply 创建pod

    1
    kubectl apply -f exec-action.yml 

    因为默认的重启策略是 restart 。查看pod 的详细信息可以看到

    可以看到pod 被重启;

  2. TCPSocketAction 向容器内建立tcp连接,如果端口正常表示容器正常

​ 配置

1
2
3
livenessProbe:
tcpSocket:
port: 80

监听特定的端口

  1. 向特定方法发送http 请求,如果请求正常表示pod正常

    ​ 示例:表示向特定端口请求返回正常,表示服务正常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    apiVersion: v1
    kind: Pod
    metadata:
    labels:
    test: liveness
    name: liveness-http
    spec:
    containers:
    - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
    httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: Custom-Header
    value: Awesome
    initialDelaySeconds: 3
    periodSeconds: 3

pod 的配置管理 configMap

configmap 为容器内的配置的集中化管理。

configMap 是一个key:value 格式的键值对,一般存储安全性不高的配置信息,(安全性要求高的密码等信息使用secret 存储)。

一般由这么几种常用的方式

  • 在容器命令和参数内
  • 容器的环境变量
  • 在只读卷里面添加一个文件,让应用来读取
  • 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap
  1. 有一个spring的配置文件

  1. 从文件中生成配置

    1
    kubectl create configmap application --from-file=./application.yml
  2. 查看已经创建的configmap

    1
    2
    kubectl get configmap

  3. 查看详细的信息

    1
    2
    kubectl describe configmap application

  1. 支持直接使用键值对生成configmap ,这种是不使用文件的方式直接写入配置

     
    1
    2
    kubectl create configmap log_config --from-literal=log-level=info --from-literal=log-path=/log

  1. pod 中使用configmap作为环境变量

k8s中支持一个envFrom 的属性,支持直接将一个配置文件的key vlaue作为环境变量

创建一个打印env的pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: env-test-pod
spec:
containers:
- name: configtest
image: busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- configMapRef:
name: log-config
restartPolicy: Never

envFrom. configMapRef 表示从哪个配置源中获取环境变量

执行此文件

1
kubectl apply -f env-pod.yaml

会看到pod在执行,执行完毕后变成完成状态.

查看日志

1
kubectl logs env-test-pod

可以看到配置的环境变量已经在pod中了

  1. 文件挂载的方式使用configmap

    编写一个pod 来读取挂载的文件

    1
    vim configmap-mount.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    apiVersion: v1
    kind: Pod
    metadata:
    name: configmap-mount-pod
    spec:
    containers:
    - name: configtest
    image: busybox
    command: [ "/bin/sh", "-c", "cat /usr/application.yml" ]
    volumeMounts:
    - name: application
    mountPath: /usr
    volumes:
    - name: application
    configMap:
    name: application
    restartPolicy: Never

    主要通过挂载的方式 ,查看挂载的信息。

    1
    2
    kubectl apply -f configmap-mount.yaml 

    执行完毕后,查看日志

    1
    2
    kubectl logs configmap-mount-pod

configMap 的使用限制

  • ConfigMap必须在Pod之前创建。
  • ConfigMap受Namespace限制,只有处于相同Namespace中的 Pod才可以引用它。
  • kubelet只支持可以被API Server管理的Pod使用ConfigMap
  • 在Pod对ConfigMap进行挂载(volumeMount)操作时,在容器内部只能挂载为“目录”,无法挂载为“文件”。

pod 的安全配置管理 Secret

Secret的主要作用是保管私密数据,比如密 码、OAuth Tokens、SSH Keys等信息。将这些私密信息放在Secret对象上存储比configmap安全。

Kubernetes 提供若干种内置的类型,用于一些常见的使用场景。 针对这些类型,Kubernetes 所执行的合法性检查操作以及对其所实施的限制各不相同。

官网详细链接 https://kubernetes.io/zh/docs/concepts/configuration/secret/

以 Opaque Secret 类型的数据作为pod的环境变量

1
kubectl create secret generic test-secret

编辑信息:

1
2
kubectl edit secret test-secret

以为类型是 Opaque 可以设置任意数据,其他类型格式有限制

注意对应的vlaue 需要经过base64编码

pod 通过挂载的方式使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-s-container
image: busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- secretRef:
name: test-secret
restartPolicy: Never

1
kubectl apply -f secret-env.yml

查看日志信息:

pod 调度

有时候我们需要将特定的pod调度到特定的node 上,或者优先调度到特定的node 上。我们也可以指定一些策略来调整和控制。
pod一般由deployment 来进行管理,而deployment中的pod template 中可以通过配置pod的调度相关的策略;

NodeSelector 调度

针对资源对象可以添加一些标签,可以通过标签选择器来选定测定的资源。下面以NodeSelector 来指定pod的部署的node节点;

给node1 打上特定的标签

1
2
kubectl label node node1 disk=ssd

标记node1 的节点 磁盘是ssd 的,现在需要将nginx 的一个部署都调度到磁盘为ssd的上面;

编辑部署

1
2
kubectl edit deploy  nginx-deployment

编辑 deploy中的pod 模板节点的 template.spec.containers.nodeSelector

可以看到pod的分布

1
2
3
4

kubectl scale --replicas=5 deploy/nginx-deployment
kubectl get pod -o wide

Node亲和性调度 (NodeAffinity属性)

(官网链接)https://kubernetes.io/zh/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/

上面的nodeSelector是强制性的策略,是一定要选择到特定的节点的。
k8s同样提供了亲和性的策略,可以具有一定的容错性。会更加的灵活和可靠。

其中提供了多种策略。

强制调度 requiredDuringSchedulingIgnoredDuringExecution

此策略表示必须,修改配置此策略

kubectl edit deploy nginx-deployment

同样会看到节点调用到node1 的节点上;

优先调度 preferredDuringSchedulingIgnoredDuringExecution

不会强制,但会优先调度到某个节点;

当配置不存在的属性的时候会分散调度

配合优先调度ssd

会发现都调度到node1 上了

Pod 亲和性调度 (PodAffinity属性)

NodeAffinity 是为了解决pod 要部署到哪个node 上的。基于的标签是pod的标签;

给目前运行的nginx 设置pod 标签

强制亲和性 requiredDuringSchedulingIgnoredDuringExecution

新创建一个tomcat 部署

1
2
3
kubectl create deploy tomcat8  --image=tomcat:8.0  --dry-run=client -o yaml


生成文件,并配置 requiredDuringSchedulingIgnoredDuringExecution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: tomcat8
name: tomcat8
spec:
replicas: 3
selector:
matchLabels:
app: tomcat8
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: tomcat8
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: disk
containers:
- image: tomcat:8.0
name: tomcat
resources: {}
status: {}


应用此配置,可以看到已经被调度到 node 节点的相同的disk属性的 pod 上了;

topologyKey 的含义是:指的是一个集群中的节点、机架、区域等概念,通过 Kubernetes内置节点标签中的key来进行声明。这个key的名字为 topologyKey,意为表达节点所属的topology范围。其实是node 节点属性;

柔软亲和性 preferredDuringSchedulingIgnoredDuringExecution

和 requiredDuringSchedulingIgnoredDuringExecution的作用一样,区别是 此策略并不是强制的。

Pod 反亲和性调度 (PodAntiAffinity属性)

和pod 亲和性相反,指的是尽量不和某个pod在同一个node上。

以刚才的例子,修改成反亲和性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38


apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: tomcat9
name: tomcat9
spec:
replicas: 3
selector:
matchLabels:
app: tomcat9
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: tomcat9
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: disk
containers:
- image: tomcat:9.0
name: tomcat
resources: {}
status: {}


tomcat9的区别只是配置成了反亲和性

污点和容忍度 Taints和Tolerations

Taint需要和Toleration配合使用,让Pod避开那些不合适的Node。

Taints 是node的属性;Tolerations 是pod的属性;

具有污点的节点是要避开的,而有对特定污点容忍度的pod可以运行在特定的节点上。

向node1 添加一个污点

1
kubectl taint nodes node1 key1=value1:NoSchedule

1
2
3
//去除污点
kubectl taint nodes node1 key1=value1:NoSchedule-

会发现 污点后创建的pod自动避开了污点

给deploy设置容忍度

1
kubectl edit deploy nginx

可以看到pod已经优先调度到node1 具有污点的节点上了;

effect 使用 NoSchedule 表示不调度,或者 PreferNoSchedule 表示尽量不调度。

除了我们自定义的污点,k8s也内置了系统运行情况的污点。

  • node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态 Ready 的值为 “False“。
  • node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 “Unknown“。
  • node.kubernetes.io/memory-pressure:节点存在内存压力。
  • node.kubernetes.io/disk-pressure:节点存在磁盘压力。
  • node.kubernetes.io/pid-pressure: 节点的 PID 压力。
  • node.kubernetes.io/network-unavailable:节点网络不可用。
  • node.kubernetes.io/unschedulable: 节点不可调度。
  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 “外部” 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

默认情况下,我们的应用可能因为某个节点有污点而离开,可以有时候比如网络污点是可能恢复的,所以可以配置

tolerationSeconds 来定义在污点能最多待多长时间。

初始化容器 init container

在pod中除了pause 容器和业务容器,还可以定义初始化容器。

初始化容器 可以理解为在pod中的业务容器执行前执行的容器,此容器执行完毕后,业务容器才开始启动。

初始化容器可以做一些资源加载 等相关的操作,为了业务容器而服务。

init container 是一定会执行完毕的,只有执行完毕 ,业务容器才能执行。

init container 重启策略是Never,如果业务容器执行失败,则表示pod执行失败

当定义多个init container 时 ,会一个一个按照顺序执行

下面以nginx为例演示init container

  1. 编辑 nginx的deploy信息

    1
    2
    kubectl edit deploy nginx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57     spec:
    158 containers:
    159 - image: nginx:1.17
    160 imagePullPolicy: Always
    161 name: nginx
    162 resources: {}
    163 terminationMessagePath: /dev/termination-log
    164 terminationMessagePolicy: File
    165 volumeMounts:
    166 - mountPath: /usr/tmp ## 给业务容器挂载路径
    167 name: tmpdir
    168 dnsPolicy: ClusterFirst
    169 initContainers:
    170 - command:
    171 - sh
    172 - -c
    173 - echo echo container running ! && sleep 15
    162 resources: {}
    163 terminationMessagePath: /dev/termination-log
    164 terminationMessagePolicy: File
    165 volumeMounts:
    166 - mountPath: /usr/tmp
    167 name: tmpdir
    168 dnsPolicy: ClusterFirst
    169 initContainers:
    170 - command:
    171 - sh
    172 - -c
    173 - echo echo container running ! && sleep 15 #初始化容器1 执行打印和睡眠
    174 image: busybox
    175 imagePullPolicy: Always
    176 name: echo-container
    177 resources: {}
    178 terminationMessagePath: /dev/termination-log
    179 terminationMessagePolicy: File
    180 - command: # 初始化容器2 向指定的目录写个文件
    181 - sh
    182 - -c
    183 - 'touch /usr/tmp/a '
    184 image: busybox
    185 imagePullPolicy: Always
    186 name: create-file
    187 resources: {}
    188 terminationMessagePath: /dev/termination-log
    189 terminationMessagePolicy: File
    190 volumeMounts:
    191 - mountPath: /usr/tmp
    192 name: tmpdir
    193 restartPolicy: Always
    194 schedulerName: default-scheduler
    195 securityContext: {}
    196 terminationGracePeriodSeconds: 30
    197 volumes: # 定义容器挂载
    198 - emptyDir: {}
    199 name: tmpdir

可以在状态的这里看到初始化到第一个容器了。

初始化容器写入的文件也被写入。