跳转至

4.最小调度单元 Pod

docker调度的是容器,在k8s集群中,最小的调度单元是Pod(豆荚)

为什么引入Pod

  • 与容器引擎解耦

Docker、Rkt。平台设计与引擎的具体的实现解耦

  • 多容器共享网络|存储|进程 空间, 支持的业务场景更加灵活
docker run -d -p 3306:3306 --name mysql  -v /opt/mysql:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci


docker run -d -p 8002:8002 --name myblog -e MYSQL_HOST=172.16.200.22 -e MYSQL_USER=root -e MYSQL_PASSWD=123456  myblog:v1

使用yaml格式定义Pod

myblog/one-pod/pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    env:
    - name: MYSQL_HOST   #  指定root用户的用户名
      value: "127.0.0.1"
    - name: MYSQL_PASSWD
      value: "123456"
    ports:
    - containerPort: 8002
  - name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
    ports:
    - containerPort: 3306
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123456"
    - name: MYSQL_DATABASE
      value: "myblog"

使用json格式定义Pod

myblog/one-pod/pod.json

{
    "apiVersion": "v1",     
    "kind": "Pod",
    "metadata": {
        "name": "myblog",
        "namespace": "myblog",
        "labels": {
            "component": "myblog"
        }
    },
    "spec": {
        "containers": [
            {
                "name": "myblog",
                "image": "registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1",
                "env": [
                    {
                        "name": "MYSQL_HOST",
                        "value": "127.0.0.1"
                    },
                    {
                        "name": "MYSQL_PASSWD",
                        "value": "123456"
                    }
                ],
                "ports": [
                    {
                        "containerPort": 8002
                    }
                ]
            },
            {
                "name": "mysql",
                ...
            }
        ]
    }
}
apiVersion 含义
alpha 进入K8s功能的早期候选版本,可能包含Bug,最终不一定进入K8s
beta 已经过测试的版本,最终会进入K8s,但功能、对象定义可能会发生变更。
stable 可安全使用的稳定版本
v1 stable 版本之后的首个版本,包含了更多的核心对象
apps/v1 使用最广泛的版本,像Deployment、ReplicaSets都已进入该版本

资源类型与apiVersion对照表

[root@k8s-master01 ~]# kubectl get apiservices

NAME                                   SERVICE                      AVAILABLE   AGE
v1.                                    Local                        True        161d
v1.admissionregistration.k8s.io        Local                        True        161d
v1.apiextensions.k8s.io                Local                        True        161d
v1.apps                                Local                        True        161d
v1.authentication.k8s.io               Local                        True        161d
v1.authorization.k8s.io                Local                        True        161d
v1.autoscaling                         Local                        True        161d
v1.batch                               Local                        True        161d
v1.certificates.k8s.io                 Local                        True        161d
v1.coordination.k8s.io                 Local                        True        161d
v1.crd.projectcalico.org               Local                        True        161d
v1.discovery.k8s.io                    Local                        True        161d
v1.enterprise.gloo.solo.io             Local                        True        155d
v1.events.k8s.io                       Local                        True        161d
v1.gateway.solo.io                     Local                        True        155d
v1.gloo.solo.io                        Local                        True        155d
v1.kuboard.cn                          Local                        True        30d
v1.networking.k8s.io                   Local                        True        161d
v1.node.k8s.io                         Local                        True        161d
v1.policy                              Local                        True        161d
v1.rbac.authorization.k8s.io           Local                        True        161d
v1.scheduling.k8s.io                   Local                        True        161d
v1.storage.k8s.io                      Local                        True        161d
v1alpha1.ratelimit.solo.io             Local                        True        155d
v1beta1.batch                          Local                        True        161d
v1beta1.discovery.k8s.io               Local                        True        161d
v1beta1.events.k8s.io                  Local                        True        161d
v1beta1.flowcontrol.apiserver.k8s.io   Local                        True        161d
v1beta1.metrics.k8s.io                 kube-system/metrics-server   True        29d
v1beta1.node.k8s.io                    Local                        True        161d
v1beta1.policy                         Local                        True        161d
v1beta1.storage.k8s.io                 Local                        True        161d
v1beta2.flowcontrol.apiserver.k8s.io   Local                        True        161d
v2.autoscaling                         Local                        True        161d
v2beta1.autoscaling                    Local                        True        161d
v2beta2.autoscaling                    Local                        True        161d

Kind apiVersion
ClusterRoleBinding rbac.authorization.k8s.io/v1
ClusterRole rbac.authorization.k8s.io/v1
ConfigMap v1
CronJob batch/v1beta1
DaemonSet extensions/v1beta1
Node v1
Namespace v1
Secret v1
PersistentVolume v1
PersistentVolumeClaim v1
Pod v1
Deployment v1、apps/v1、apps/v1beta1、apps/v1beta2
Service v1
Ingress extensions/v1beta1
ReplicaSet apps/v1、apps/v1beta2
Job batch/v1
StatefulSet apps/v1、apps/v1beta1、apps/v1beta2

快速获得资源和版本信息

$ kubectl explain pod
$ kubectl explain Pod.apiVersion

创建和访问Pod

使用上面的pod.yaml 创建

## 创建namespace, namespace是逻辑上的资源池
$ kubectl create namespace myblog

## 使用指定文件创建Pod
$ kubectl create -f pod.yaml

## 查看pod,可以简写po
## 所有的操作都需要指定namespace,如果是在default命名空间下,则可以省略
$ kubectl -n myblog get pods -o wide
NAME     READY   STATUS    RESTARTS   AGE    IP             NODE
myblog   2/2     Running   0          3m     10.244.1.146   k8s-node1

## 使用Pod Ip访问服务,3306和8002
$ curl 10.244.1.146:8002/blog/index/

## 进入容器,执行初始化, 不必到对应的主机执行docker exec
$ kubectl -n myblog exec -ti myblog -c myblog bash
/ # env
/ # python3 manage.py migrate

$ kubectl -n myblog exec -ti myblog -c mysql bash
/ # mysql -p123456

## 再次访问服务,3306和8002
$ curl 10.244.1.146:8002/blog/index/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h3>我的博客列表:</h3>


    </br>
    </br>
    <a href=" /blog/article/edit/0 ">写博客</a>

</body>

Pause(Infra)容器

$ docker ps -a |grep myblog  ## 发现有三个容器
## 其中包含mysql和myblog程序以及Pause(Infra)容器

## 为了实现Pod内部的容器可以通过localhost通信,每个Pod都会启动Pause(Infra)容器容器,然后Pod内部的其他容器的网络空间会共享该Pause(Infra)容器的网络空间(Docker网络的container模式),Pause(Infra)容器只需要hang住网络空间,不需要额外的功能,因此资源消耗极低。

## 登录master节点,查看pod内部的容器ip均相同,为pod ip
$ kubectl -n myblog exec -ti myblog -c myblog bash
/ # ifconfig
$ kubectl -n myblog exec -ti myblog -c mysql bash
/ # ifconfig

pod容器命名: k8s_<container_name>_<pod_name>_<namespace>_<pod-UUID>

查看Pod详细信息

## 查看pod调度节点及pod_ip
$ kubectl -n myblog get pods -o wide

## 查看完整的yaml
$ kubectl -n myblog get po myblog -o yaml

## 查看pod的明细信息及事件
$ kubectl -n myblog describe pod myblog

## 查看pod中容器的日志
#进入Pod内的容器
$ kubectl -n <namespace> exec <pod_name> -c <container_name> -ti /bin/sh

#查看Pod内容器日志,显示标准或者错误输出日志
$ kubectl -n <namespace> logs -f <pod_name> -c <container_name>

在线更新Pod配置

$ kubectl apply -f demo-pod.yaml

删除Pod服务

#根据文件删除
$ kubectl delete -f demo-pod.yaml

#根据pod_name删除
$ kubectl -n <namespace> delete pod <pod_name>

Pod环境变量

变量值几种定义方式:

  • 自定义变量值
  • 变量值从Pod属性获取
  • 变量值从Secret获取
  • 变量值从ConfigMap获取

自定义变量值

apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    env:
    - name: MYSQL_HOST   #  指定root用户的用户名
      value: "127.0.0.1"
    - name: MYSQL_PASSWD
      value: "123456"
    ports:
    - containerPort: 8002

变量值从Pod属性获取

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-fieldref
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
          printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
          sleep 10;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - 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
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
  restartPolicy: Never

变量值从Secret获取

Secret 是K8S资源的一种,请看这个链接XXXXXXX

一般用于 用户名密码的凭证

$ cat secret.yaml 
apiVersion: v1
kind: Secret
metadata:
  name: myblog
  namespace: myblog
type: Opaque
data:
  MYSQL_USER: cm9vdA==          #注意加-n参数, echo -n root | base64
  MYSQL_PASSWD: MTIzNDU2        #注意加-n参数, echo -n 123456 | base64


$ kubect apply -f  secret.yaml 

$ cat mysql-with-config.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mysql
  namespace: myblog
  labels:
    component: mysql
spec:
  hostNetwork: true     # 声明pod的网络模式为host模式,效果同docker run --net=host
  volumes: 
  - name: mysql-data
    hostPath: 
      path: /opt/mysql/data
  nodeSelector:   # 使用节点选择器将Pod调度到指定label的节点
    component: mysql
  containers:
  - name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
    ports:
    - containerPort: 3306
    env:
    - name: MYSQL_USER
      valueFrom:
        secretKeyRef:
          name: myblog
          key: MYSQL_USER
    - name: MYSQL_ROOT_PASSWORD
      valueFrom:
        secretKeyRef:
          name: myblog
          key: MYSQL_PASSWD
    - name: MYSQL_DATABASE
      value: "myblog"
    volumeMounts:
    - name: mysql-data
      mountPath: /var/lib/mysql

变量值从ConfigMap获取

ConfigMap 是K8S资源的一种,请看这个链接XXXXXXX

一般用于配置文件的保存


$ cat configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: myblog
  namespace: myblog
data:
  MYSQL_HOST: "172.16.200.21"
  MYSQL_PORT: "3306"


$ kubectl apply -f configmap.yaml


$ cat myblog-with-config.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    imagePullPolicy: IfNotPresent
    env:
    - name: MYSQL_HOST
      valueFrom:
        configMapKeyRef:
          name: myblog
          key: MYSQL_HOST
    - name: MYSQL_PORT
      valueFrom:
        configMapKeyRef:
          name: myblog
          key: MYSQL_PORT
    ports:
    - containerPort: 8002


Pod数据持久化

若删除了Pod,由于mysql的数据都在容器内部,会造成数据丢失,因此需要数据进行持久化。

  • 这里使用hostPath挂载,需要使用 nodeSelector 给节点打上标签

其他持久化方式,使用PV+PVC连接分布式存储解决方案 等,[这里只简要说下hostPath的持久化方式,K8S的存储请看这个链接XXXXXXX]

  • ceph
  • glusterfs
  • nfs

myblog/one-pod/pod-with-volume.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  volumes:
  - name: mysql-data
    hostPath:
      path: /opt/mysql/data
  nodeSelector:   # 使用节点选择器将Pod调度到指定label的节点
    component: mysql
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    env:
    - name: MYSQL_HOST   #  指定root用户的用户名
      value: "127.0.0.1"
    - name: MYSQL_PASSWD
      value: "123456"
    ports:
    - containerPort: 8002
  - name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
    ports:
    - containerPort: 3306
    volumeMounts:
    - name: mysql-data
      mountPath: /var/lib/mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123456"
    - name: MYSQL_DATABASE
      value: "myblog"

保存文件为pod-with-volume.yaml,执行创建

## 若存在旧的同名服务,先删除掉,后创建
$ kubectl -n myblog delete pod myblog

## 创建
$ kubectl create -f pod-with-volume.yaml

## 此时pod状态Pending
$ kubectl -n myblog get pod
NAME     READY   STATUS    RESTARTS   AGE
myblog   0/2     Pending   0          32s

## 查看原因,提示调度失败,因为节点不满足node selector
$ kubectl -n myblog describe pod myblog
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  12s (x2 over 12s)  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.


## 为节点打标签
$ kubectl label node k8s-node1 component=mysql


## 再次查看,已经运行成功
$ kubectl -n myblog get po
NAME     READY   STATUS    RESTARTS   AGE     IP             NODE
myblog   2/2     Running   0          3m54s   10.244.1.150   k8s-node1

## 到k8s-node1节点上,查看/opt/mysql/data
$ ll /opt/mysql/data/
total 188484
-rw-r----- 1 polkitd input       56 Mar 29 09:20 auto.cnf
-rw------- 1 polkitd input     1676 Mar 29 09:20 ca-key.pem
-rw-r--r-- 1 polkitd input     1112 Mar 29 09:20 ca.pem
drwxr-x--- 2 polkitd input     8192 Mar 29 09:20 sys
...

## 执行migrate,创建数据库表,然后删掉pod,再次创建后验证数据是否存在
$ kubectl -n myblog exec -ti myblog python3 manage.py migrate

## 访问服务,正常
$ curl 10.244.1.150:8002/blog/index/ 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h3>我的博客列表:</h3>


    </br>
    </br>
    <a href=" /blog/article/edit/0 ">写博客</a>

</body>

## 删除pod
$ kubectl delete -f pod-with-volume.yaml

## 再次创建Pod,观察mysql数据有没有持久化成功
$ kubectl create -f pod-with-volume.yaml

## 查看pod ip并访问服务
$ kubectl -n myblog get po -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP             NODE  
myblog   2/2     Running   0          7s    10.244.1.151   k8s-slave1

## 服务访问,依旧正常
$ curl 10.244.1.151:8002/blog/index/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h3>我的博客列表:</h3>


    </br>
    </br>
    <a href=" /blog/article/edit/0 ">写博客</a>

</body>

其他持久化方式,使用PV+PVC连接分布式存储解决方案 等,[这里只简要说下hostPath的持久化方式,K8S的存储请看这个链接XXXXXXX]

  • ceph
  • glusterfs
  • nfs

健康检查+重启策略(应用自修复)

检测容器服务是否健康的手段,若不健康,会根据设置的重启策略(restartPolicy)进行操作,两种检测机制可以分别单独设置,若不设置,默认认为Pod是健康的。

健康检查有以下两种机制:

  • livenessProbe(存活检查,存活性探测):如果检查失败,将杀死容器,根据Pod的重启策略(restartPolicy)来操作。

  • readinessProbe(就绪检查,可用性探测):如果检查失败,Kubernetes会把Pod从service endpoints中剔除。

livenessProbe探针

存活性探测:用于判断容器是否存活,即Pod是否为running状态,如果LivenessProbe探针探测到容器不健康,则kubelet将kill掉容器,并根据容器的重启策略是否重启,如果一个容器不包含LivenessProbe探针,则Kubelet认为容器的LivenessProbe探针的返回值永远成功。

...
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    livenessProbe:
      httpGet:
        path: /blog/index/
        port: 8002
        scheme: HTTP
      initialDelaySeconds: 10  # 容器启动后第一次执行探测是需要等待多少秒
      periodSeconds: 10     # 执行探测的频率
      timeoutSeconds: 2     # 探测超时时间
...

ReadinessProbe探针

可用性探测:用于判断容器是否正常提供服务,即容器的Ready是否为True,是否可以接收请求,如果ReadinessProbe探测失败,则容器的Ready将为False, Endpoint Controller 控制器将此Pod的Endpoint从对应的service的Endpoint列表中移除,不再将任何请求调度此Pod上,直到下次探测成功。(剔除此pod不参与接收请求不会将流量转发给此Pod)

...
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    readinessProbe: 
      httpGet: 
        path: /blog/index/
        port: 8002
        scheme: HTTP
      initialDelaySeconds: 10 
      timeoutSeconds: 2
      periodSeconds: 10
...

三种检查方法

  • exec:通过执行命令来检查服务是否正常,返回值为0则表示容器健康
  • httpGet方式:通过发送http请求检查服务是否正常,返回200-399状态码则表明容器健康
  • tcpSocket:通过容器的IP和Port执行TCP检查,如果能够建立TCP连接,则表明容器健康

案例1img

使用livenessProbe,定义一个HTTP的健康检测方式

K8S将在Pod开始启动5s(initialDelaySeconds)后利用HTTP访问8002端口的/blog/index/,如果超过2s或者返回码不在200~399内,则健康检查失败

apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  volumes:
  - name: mysql-data
    hostPath:
      path: /opt/mysql/data
  nodeSelector:   # 使用节点选择器将Pod调度到指定label的节点
    component: mysql
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    livenessProbe:
      httpGet:
        path: /blog/index/
        port: 8002
        scheme: HTTP
      initialDelaySeconds: 5  # 容器启动后第一次执行探测是需要等待多少秒
      periodSeconds: 5         # 执行探测的频率
      timeoutSeconds: 2         # 探测超时时间
    env:
    - name: MYSQL_HOST   #  指定root用户的用户名
      value: "127.0.0.1"
    - name: MYSQL_PASSWD
      value: "123456"
    ports:
    - containerPort: 8002
  - name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
    ports:
    - containerPort: 3306
    volumeMounts:
    - name: mysql-data
      mountPath: /var/lib/mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123456"
    - name: MYSQL_DATABASE
      value: "myblog"

案例2img

使用livenessProbe,定义一个tcpSocket 的健康检测方式

apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  volumes:
  - name: mysql-data
    hostPath:
      path: /opt/mysql/data
  nodeSelector:   # 使用节点选择器将Pod调度到指定label的节点
    component: mysql
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    livenessProbe:
      tcpSocket:
        port: 8002
      initialDelaySeconds: 5  # 容器启动后第一次执行探测是需要等待多少秒
      periodSeconds: 5         # 执行探测的频率
      timeoutSeconds: 2         # 探测超时时间
    env:
    - name: MYSQL_HOST   #  指定root用户的用户名
      value: "127.0.0.1"
    - name: MYSQL_PASSWD
      value: "123456"
    ports:
    - containerPort: 8002
  - name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
    ports:
    - containerPort: 3306
    volumeMounts:
    - name: mysql-data
      mountPath: /var/lib/mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123456"
    - name: MYSQL_DATABASE
      value: "myblog"

案例2img

使用livenessProbe,定义一个exec 的健康检测方式

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
  • initialDelaySeconds:容器启动后第一次执行探测时需要等待多少秒。
  • periodSeconds:执行探测的频率。默认是10秒,最小1秒。
  • timeoutSeconds:探测超时时间。默认1秒,最小1秒。
  • successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。
  • failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3,最小值是1。

重启策略(restartPolicy)

  Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。 Pod的重启策略如下:

  • Always:当容器终止退出后,总是重启容器,默认策略
  • OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。
  • Never:当容器终止退出,从不重启容器。

演示重启策略:

apiVersion: v1
kind: Pod
metadata:
  name: test-restart-policy
spec:
  restartPolicy: OnFailure
  containers:
  - name: busybox
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 10 && exit 0
  1. 使用默认的重启策略,即 restartPolicy: Always ,无论容器是否是正常退出,都会自动重启容器
  2. 使用OnFailure的策略时
  3. 如果把exit 1,去掉,即让容器的进程正常退出的话,则不会重启
  4. 只有非正常退出状态才会重启
  5. 使用Never时,退出了就不再重启

可以看出,若容器正常退出,Pod的状态会是Completed,非正常退出,状态为CrashLoopBackOff

镜像拉取策略

spec:
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    imagePullPolicy: IfNotPresent


设置镜像的拉取策略,默认为IfNotPresent

  • Always,总是拉取镜像,即使本地有镜像也从仓库拉取
  • IfNotPresent ,本地有则使用本地镜像,本地没有则去仓库拉取
  • Never,只使用本地镜像,本地没有则报错

Pod资源限制

  为了保证充分利用集群资源,且确保重要容器在运行周期内能够分配到足够的资源稳定运行,因此平台需要具备Pod的资源限制的能力。 对于一个pod来说,资源最基础的2个的指标就是:CPU内存。Kubernetes提供了个采用requestslimits 两种类型参数对资源进行预分配和使用限制。

容器使用的最小资源需求,作为容器调度时资源分配的依据:【最小】
  • resources.requests.cpu
  • resources.requests.memory
容器资源限制:【最大】
  • resources.limits.cpu
  • resources.limits.memory

myblog/one-pod/pod-with-resourcelimits.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myblog
  namespace: myblog
  labels:
    component: myblog
spec:
  volumes:
  - name: mysql-data
    hostPath:
      path: /opt/mysql/data
  nodeSelector:   # 使用节点选择器将Pod调度到指定label的节点
    component: mysql
  containers:
  - name: myblog
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/myblog:v1
    env:
    - name: MYSQL_HOST   #  指定root用户的用户名
      value: "127.0.0.1"
    - name: MYSQL_PASSWD
      value: "123456"
    ports:
    - containerPort: 8002
    resources:
      requests:
        memory: 100Mi
        cpu: 50m
      limits:
        memory: 500Mi
        cpu: 100m
  - name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/fxkjnj-study/mysql:5.7-utf8
    ports:
    - containerPort: 3306
    resources:
      requests:
        memory: 100Mi
        cpu: 50m
      limits:
        memory: 500Mi
        cpu: 100m
    volumeMounts:
    - name: mysql-data
      mountPath: /var/lib/mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123456"
    - name: MYSQL_DATABASE
      value: "myblog"

requests

  • 容器使用的最小资源需求,作用于schedule阶段,作为容器调度时资源分配的判断依赖
  • 只有当前节点上可分配的资源量 >= request 时才允许将容器调度到该节点
  • request参数不限制容器的最大可使用资源
  • requests.cpu被转成docker的--cpu-shares参数,与cgroup cpu.shares功能相同 (无论宿主机有多少个cpu或者内核,--cpu-shares选项都会按照比例分配cpu资源)
  • requests.memory没有对应的docker参数,仅作为k8s调度依据

limits

  • 容器能使用资源的最大值
  • 设置为0表示对使用的资源不做限制, 可无限的使用
  • 当pod 内存超过limit时,会被oom
  • 当cpu超过limit时,不会被kill,但是会限制不超过limit值
  • limits.cpu会被转换成docker的–cpu-quota参数。与cgroup cpu.cfs_quota_us功能相同
  • limits.memory会被转换成docker的–memory参数。用来限制容器使用的最大内存

 对于 CPU,我们知道计算机里 CPU 的资源是按“时间片”的方式来进行分配的,系统里的每一个操作都需要 CPU 的处理,所以,哪个任务要是申请的 CPU 时间片越多,那么它得到的 CPU 资源就越多。

然后还需要了解下 CGroup 里面对于 CPU 资源的单位换算:

1 CPU =  1000 millicpu(1 Core = 1000m)

这里的 m 就是毫、毫核的意思,Kubernetes 集群中的每一个节点可以通过操作系统的命令来确认本节点的 CPU 内核数量,然后将这个数量乘以1000,得到的就是节点总 CPU 总毫数。比如一个节点有四核,那么该节点的 CPU 总毫量为 4000m。

docker run命令和 CPU 限制相关的所有选项如下:

选项 描述
--cpuset-cpus="" 允许使用的 CPU 集,值可以为 0-3,0,1
-c,--cpu-shares=0 CPU 共享权值(相对权重)
cpu-period=0 限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000]
--cpu-quota=0 限制 CPU CFS 配额,必须不小于1ms,即 >= 1000,绝对限制
docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash

将 CFS 调度的周期设为 50000,将容器在每个周期内的 CPU 配额设置为 25000,表示该容器每 50ms 可以得到 50% 的 CPU 运行时间。

注意:若内存使用超出限制,会引发系统的OOM机制,因CPU是可压缩资源,不会引发Pod退出或重建

pod状态与生命周期

Pod的状态如下表所示:

状态值 描述
Pending API Server已经创建该Pod,等待调度器调度
ContainerCreating 拉取镜像启动容器中
Running Pod内容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态
Succeeded|Completed Pod内所有容器均已成功执行退出,且不再重启
Failed|Error Pod内所有容器均已退出,但至少有一个容器退出为失败状态
CrashLoopBackOff Pod内有容器启动失败,比如配置文件丢失导致主进程启动失败
Unknown 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致

生命周期示意图:

启动和关闭示意:

初始化容器:

  • 验证业务应用依赖的组件是否均已启动
  • 修改目录的权限
  • 调整系统参数

应用场景:

  • 环境检查:例如确保应用容器依赖的服务启动后再启动应用容器
  • 初始化配置:例如给应用容器准备配置文件,下载代码等

案例:

本例中你将创建一个包含一个应用容器和一个 Init 容器的 Pod。Init 容器在应用容器启动前运行完成。

下面是 Pod 的配置文件:

cat > init-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  hostNetwork: true  # 声明pod的网络模式为host模式,效果同docker run --net=host
  initContainers:
  - name: download
    image: busybox
    command:
    - wget
    - "-O"
    - "/opt/index.html"
    - http://fxkjnj.com
    volumeMounts:
    - name: workdir
      mountPath: "/opt"
  containers:
  - name: nginx
    image: nginx:1.16.0
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  volumes:
  - name: workdir
    emptyDir: {}
EOF


#创建pod测试
[root@k8s-master init-pod]# kubectl apply -f init-pod.yaml 
pod/init-demo created

#可以看到在初始化容器
[root@k8s-master init-pod]# kubectl get pods
NAME        READY   STATUS            RESTARTS   AGE
init-demo   0/1     PodInitializing   0          12s

#查看到pod ip
[root@k8s-master init-pod]# kubectl get pods -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
init-demo   1/1     Running   0          59s   172.16.200.22   k8s-node2   <none>           <none>


浏览器访问: http://172.16.200.22

image-20220513173443061

验证Pod生命周期:

cat >  demo-pod-start.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: demo-start-stop
  namespace: myblog
  labels:
    component: demo-start-stop
spec:
  initContainers:
  - name: init
    image: busybox
    command: ['sh', '-c', 'echo $(date +%s): INIT >> /loap/timing']
    volumeMounts:
    - mountPath: /loap
      name: timing
  containers:
  - name: main
    image: busybox
    command: ['sh', '-c', 'echo $(date +%s): START >> /loap/timing;
sleep 10; echo $(date +%s): END >> /loap/timing;']
    volumeMounts:
    - mountPath: /loap 
      name: timing
    livenessProbe:
      exec:
        command: ['sh', '-c', 'echo $(date +%s): LIVENESS >> /loap/timing']
    readinessProbe:
      exec:
        command: ['sh', '-c', 'echo $(date +%s): READINESS >> /loap/timing']
    lifecycle:
      postStart:
        exec:
          command: ['sh', '-c', 'echo $(date +%s): POST-START >> /loap/timing']
      preStop:
        exec:
          command: ['sh', '-c', 'echo $(date +%s): PRE-STOP >> /loap/timing']
  volumes:
  - name: timing
    hostPath:
      path: /tmp/loap
EOF

创建pod测试:

$ kubectl create -f demo-pod-start.yaml

## 查看demo状态
$ kubectl -n myblog get pod -o wide -w

## 查看调度节点的/tmp/loap/timing
$ cat /tmp/loap/timing
1585424708: INIT
1585424746: START
1585424746: POST-START
1585424754: READINESS
1585424756: LIVENESS
1585424756: END


须主动杀掉 Pod 才会触发 pre-stop hook,如果是 Pod 自己 Down 掉,则不会执行 pre-stop hook