Kubernetes Pod管理


在之前的文章中我们分享了 DockerDocker Compose 的使用,前者通过容器化技术简化了服务管理,而后者则提供 YAML 方式进一步简化 Docker 维护成本。

而今天则让我们聚焦于 K8S(Kubernetes),其提供了容器化编排能力,在规模化服务运维上提供了强力保障。

随着 Kubernetes 的生态的发展,其已然成为当下服务运维的主流选择,那么今天让我们一同学习如何基于 K8S 实现服务的部署与管理。

一、集群搭建

1. 环境配置

开始前需先行配置相关的环境,通过下面命令关闭 swap,完成后可通过 free -m 验证。

swapoff -a
sed -i '/swap/d' /etc/fstab

同时,若在测试环境则建议先行关闭服务器防火墙避免后续访问异常。

systemctl stop firewalld
systemctl disable firewalld

2. Minikube

在后续的教程中都是基于 Docker 实现,因此部署 Kubernetes 前需确保 Docker 已安装并运行,可参考:Docker基础入门

完成后便可着手部署本地 Kubernetes 服务,这里选择 Minikube 搭建服务集群。

CentOS7 服务器为例,访问 GitHub 下载 minikube-linux-amd64 二进制文件并上传至服务器,或使用 curl 命令在服务器上通过网络下载:

curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64

下载完成后则可进行安装,完成后输入 minikube 若显示 help 命令说明安装成功。

install minikube-linux-amd64 /usr/local/bin/minikube

服务安装后便可启动 Kubernetes 服务,其中 --driver=docker 表示基于 Docker 服务。

同时 minikube 默认不支持 root 账户运行,因此需 --force 强制启动进而跳过校验。

minikube start --driver=docker --force

若需以普通用户身份启动,可通过下述命令新建用户后启动。

# 创建用户并加入组
useradd budai
usermod -aG docker budai

# 切换用户并启动
su budai
minikube start --driver=docker

完成上述步骤后通过 minikube status 查看服务状态,返回下述结果则说明成功。

[root@localhost ~]# minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

为了更方便后续的使用,这边需要为 kubectl 命令创建短链别名。通过 vim .bashrc 编辑环境变量,在配置中加入 alias kubectl="minikube kubectl --" 后保存退出。

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias kubectl="minikube kubectl --"

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

保存退出后通过 source 命令刷新配置,便可直接使用 kubectl 命令。

source .bashrc

二、命名空间

1. 基础介绍

在负载允许的条件下我们常会将多个应用部署于同一环境,为了能方便管理的不同应用,数据环境隔离则显得尤为重要。同样,在 Kubernetes 中提供了命名空间 namespace 用于实现环境隔离。

默认下所有的操作都在 default 命名空间下,通过 get namespace 可以查看当前所有命名空间。

[root@localhost app]# kubectl get namespace
NAME                   STATUS   AGE
default                Active   9d
ingress-nginx          Active   9m39s
kube-node-lease        Active   9d
kube-public            Active   9d
kube-system            Active   9d
kubernetes-dashboard   Active   3d7h

namespace 的创建相对可谓十分简单,新建 namespace-dev.yaml 文件如下:

apiVersion: v1
kind: Namespace
metadata:
  name: dev

完成后通过 apply -f 命名便可根据配置中定义创建对应的命名空间。

kubectl apply -f namespace-dev.yaml

namespace 的使用也并不复杂,在命令中通过 -n 指定便可。

例如下述命令默认查询 default 下的所有 pod 节点:

[root@localhost app]# kubectl get pod 

若需要查询刚刚创建的 dev 命名空间通过 -n dev 即可:

[root@localhost app]# kubectl get pod -n dev

三、POD管理

1. 基础介绍

Docker 服务中我们最熟悉的莫过于 container 容器了,而在 KubernetesPod 为最小的可调度、可运行单元,是一个或多个容器的组合。

当启动 Pod 时会为其分配单独 IP,不同 Pod 之间可通过此分配的 IP 进行通讯访问。

较为经典的场景即日志监控,将日志服务与业务系统部署于同一 Pod 中,当系统输出日志信息后由日志服务推送数据至其它 Pod 用于进一步加工处理。

2. 配置格式

KubernetesPod 服务的管理与 Docker Compose 类似通过 YAML 文件实现。

下面让我们来看一个具体的 Pod 其相应 test-pod.yaml 文件配置,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  labels:
    app: test-label
spec:
  restartPolicy: Always
  containers:
    - name: k8s-test
      image: k8s-test:1.0
      imagePullPolicy: IfNotPresent

对于上述 yaml 各项参数作用参考下表,详细说明可访问官网查看:Pod

属性 作用
kind 配置类型。
metadata.name 用于指定 Pod 名称。
metadata.labels 用于指定 Pod 标签。
spec.restartPolicy Pod 重启策略。
spec.containers 用于配置 Pod 中运行的容器信息。

其中标签 (labels) 最核心的作用便是用于资源选择,让其它服务能够选中 Pod,后续会以具体示例演示。

同时,containers 容器中配置的镜像拉取默认规则为读取 Docker Hub,通过 imagePullPolicy 便可指定拉取策略,可选值如下:

属性 作用
Always 默认值,总是拉取镜像。
IfNotPresent 当本地不存在时拉取镜像。
Never 只使用本地镜像,从不拉取。

除此之外,Pod 其余常见命令参考下表:

命令 作用
kubectl get pod 列举当前所有 Pod。
kubectl get pod -o wide 列举当前所有 Pod 详细信息。
kubectl get pod --show-labels 列举当前所有 Pod 及其 label。
kubectl delete pod pod-name 删除指定 Pod。
kubectl logs --follow pod-name 查看指定 Pod 日志。

同时与 Docker 中的 exec 类似,Pod 也支持类似语法访问 Pod,其语法如下:

kubectl exec -it <pod-name> -- <command>

如进入 test-pod 节点内部其相对应的命令如下:

kubectl exec -it test-pod -- /bin/bash

3. 服务示例

接下来以具体的 Pod 演示,准备 Spring Boot 项目定义接口返回当前节点的 Host name,代码如下:

@RestController
@RequestMapping("/api")
public TestResource {

    @GetMapping("hello")
    public String hello() throws Exception {
        InetAddress inet = InetAddress.getLocalHost();
        return inet.getHostName() + " say Hello!"
    }
}

完成后将工程打包为 Docker Image,可参考:Maven打包部署教程,制作好镜像后通过 load 命令加载至 Minikube 中。

# 构建镜像
docker build -t k8s-test:1.0 .

# 加载镜像
minikube image load k8s-test:1.0

# 列举镜像
minikube image list

查看 minikube 镜像时除了上述命令也可通过 minikube ssh 进入实例后使用普通 docker 命令查看。

镜像加载完毕后便可通过 kubectl apply -f 命令启动上述 yaml 定义的 Pod 服务。

# 启动 Pod 实例
kubectl apply -f test-pod.yaml  

此时便可通过 kubectl get pod 查看已有的 Pod,如下 Running 则表示以启动运行。

[root@localhost ~]# kubectl get pod
NAME       READY   STATUS    RESTARTS   AGE
test-pod   1/1     Running   0          1s

使用 -o wide 参数可查看 Pod 详细信息,通过 IP 测试之前服务声明的接口可以看到能够正常响应。

至此,我们便完成了最基础的 Kubernetes 服务的启动运行。

[root@localhost ~]# kubectl get pod -o wide
NAME       READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
test-pod   1/1     Running   0          1s      10.244.0.4   minikube   <none>           <none>

[root@localhost ~]# kubectl exec -it test-pod -- curl http://10.244.0.4:9090/api/hello
test-pod say Hello!

四、POD进阶

在上面我们完成了基础 Pod 服务启动,而 Kubernetes 还提供了一系列配置参数,那么就让我们一探究竟。

1. 资源限制

在服务的运行过程中通常涉及两个核心指标:CPUMemory,针对这两个参数 Kubernetes 也提供了相应的可配参数,默认情况下 Kubernetes 不会对 Pod 可用资源进行限制,

仍以之前配置为例,对应资源的限制通过 resources 标签指定,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  labels:
    app: test-label
spec:
  restartPolicy: Always
  containers:
    - name: k8s-test
      image: k8s-test:1.0
      imagePullPolicy: IfNotPresent
      resources:
        requests:
          cpu: "250m"
          memory: "512Mi"
        limits:
          cpu: "500m"
          memory: "1024Mi"

可以看到针对 CPU 与内存的配置提供了 requestslimits 两类配置项,参考下表:

需注意其中 requests 配置的服务时启动预申请资源大小,在达到 requests 设定值后若服务器资源空闲 Pod 会尝试使用更多资源。而 limits 则为服务可用资源上限,达到该阈值将强制触发 Pod 服务重启。

同时,对于 CPU 与内存在实现效果上略有不同,CPU 限制由 CPU Throttling 管理,当负载到达 limits 值将强制重启。而当内存达到阈值时 Pod 并不一定会立刻重启,而会在集群内可分配内存不足时触发。

参数 描述
resources.requests Pod 启动预申请资源。
resources.limits 可用上限,达到时将触发 Pod 重启。

resources 不仅影响 Pod 运行状态,其同时也会影响到 Pod 的流量调度,即便以默认的同权重负载均衡下,Kubernetes 则会转而优先将请求等流转到资源占用更低的 Pod 副本实例。

另外较为特殊的是 KubernetesCPU 资源的度量衡不是常用的核心数 (C) 而是以毫核 (M) 形式,其换算公式为 1C = 1000M。原因在于分布式场景下通常单个 Pod 的负载不高,引入更小的度量衡更便于监控。

Kubernetes 中通过 top 命名查看 Pod 负载信息,若在 Minikube 下需先启用 metrics-server

minikube addons enable metrics-server

在启动之后便可查看 Pod 负载,若首次执行提示下述信息为正常现象,因为监控信息为周期性进行获取,故首次启动需等待数分钟,只要 metrics 服务显示 Running 运行中便可。

# 需等待数分钟后重试
[root@localhost ~]# kubectl top pod
error: Metrics API not available

[root@localhost ~]# kubectl get pods -n kube-system | grep metrics
metrics-server-85b7d694d7-hpsgf    0/1     Running   0             29m

等待片刻之后再次执行命令,便可看到已经输出 Pod 的资源服务信息,其表示 test-pod 服务占用 2 毫核 CPU 资源与 12MB 内存资源。

[root@localhost ~]# kubectl top pod
NAME      CPU(cores)   MEMORY(bytes)
test-pod  2m           12Mi

2. 重启策略

不同于手动方式的服务部署,Kubernetes 支持监控服务状态并实现异常自启。

Pod 中的容器退出时,Kubernetes 会以指数级回退延迟机制(10 秒、20 秒、40 秒…)重启容器, 上限为 300 秒。当容器顺利执行了 10 分钟,Kubernetes 就会重置该容器的重启延迟计时器。

对应 Pod 实例的重启策略配置通过 restartPolicy 配置,可选参数如下:

属性 作用
Always 默认值,只要容器终止就自动重启。
OnFailure 只有在容器错误退出(退出状态非零)时才重新启动容器。
Never 不会自动重启已终止的容器。

需注意这里重启策略是针对实例中容器而言,即当 Pod 内进程异常时重启 Pod 服务,而手动的删除 Pod 并不会触发自动重新拉取并启动 Pod

下面以示例进行介绍,首先通过 -watch 来监听 Pod 的变化情况。

kubectl get pods -watch

新建命令窗口通过 exec 查看服务进程,并使用 kill <pid> 命令关闭进程。

# 查看进程
[root@localhost app]# kubectl exec -it test-pod -- ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1 15.6  2.9 3167616 122228 ?      Ssl  12:23   0:03 java -jar /k8s-test.jar
root          30  0.0  0.0  10864  2476 pts/0    Rs+  12:24   0:00 ps aux

# 关闭进程
[root@localhost app]# kubectl exec -it test-pod -- kill 1

回到刚才的 watch 窗口,可以看到在首次启动后状态为 Running,而当手动 kill 转为 Error 后便自动触发重启,服务再次变为 Running 可用。

[root@localhost app]# kubectl get pods -w
NAME       READY   STATUS    RESTARTS   AGE
test-pod   0/1     Pending   0          0s
test-pod   0/1     Pending   0          0s
test-pod   0/1     ContainerCreating   0          0s
test-pod   1/1     Running             0          1s
test-pod   0/1     Error               0          41s
test-pod   1/1     Running             1 (1s ago)   42s

除了 restartPolicy 参数在 Pod 重启时还有令一关键配置:terminationGracePeriodSeconds

其作用在于当接收到关闭指令时等待多少秒后强制执行,作用类型与 kill -9kill -15 的关系,即预留时间给服务缓冲,默认为 30s

如下配置即设置为接收关闭指令后等待 60s 后强制执行,避免业务执行过程突然中断造成数据异常。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  labels:
    app: test-label
spec:
  terminationGracePeriodSeconds: 60
  containers:
    - name: k8s-test
      image: k8s-test:1.0

3. 服务分布

在生产环境下 Kubernetes 通常以多节点集群的形式部署,对于 Pod 服务的部署也常涉及倾向性问题。

通俗而言,通常会有将某些 Pod 尽可能部署在同一节点或避免与某些 Pod 共处一节点的诉求,而这则可通过 podAffinitypodAntiAffinity 实现。

最常见的应用常见即通过 podAntiAffinity 设置多个高负载的 Pod 应用尽可能分布于集群内不同节点,避免出现某个节点负载过高而其它节点却一直处理低负载的情况。

如下示例通过 label 配置其下 Pod 尽可能分布到不同节点实现服务均衡。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  labels:
    app: test-label
spec:
  containers:
    - name: k8s-test
      image: k8s-test:1.0
  affinity:      
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: test-label
          topologyKey: kubernetes.io/hostname

在配置执行策略时提供了下述两类参数,更推荐第二个避免节点资源不足导致服务无法正常启动。

配置项 作用
requiredDuringSchedulingIgnoredDuringExecution 强制要求互斥,当节点不够时启动将卡住。
preferredDuringSchedulingIgnoredDuringExecution 优先服务互斥,若不满足仍可继续启动服务。

4. 目录挂载

Pod 服务中,容器的每次删除重建并不会保留已有的数据文件,每次执行 delete pod 后其容器中的所有内载文件将删除且无法找回。

因此,部署服务时对于重要数据同样需配置目录映射,在 Docker 中若需实现容器与宿主机的目录映射通过 -v 参数便可实现,而在 Pod 中配置方式与其相似。

还是以刚刚的 test-pod.yaml 文件为例,若需要将宿主机的 /home/data 目录与容器目录 /app/data 相映射,其配置如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  labels:
    app: test-label
spec:
  restartPolicy: Always
  containers:
    - name: k8s-test
      image: k8s-test:1.0
      imagePullPolicy: IfNotPresent
      # 容器目录
      volumeMounts:
        - name: host-data
          mountPath: /app/data
    # 宿主机目录
    volumes:
        - name: host-data
          hostPath:
            path: /home/data
            type: DirectoryOrCreate

其中 volumes 用于定义宿主机目录信息,而 volumeMounts 相对应为容器目录信息,二者通过 volumes.namevolumeMounts.name 实现关联。

同时,由于我们是基于 Minikube 所以这边需要增加一步操作将宿主机与 Minikube 进行映射:

minikube mount <宿主机目录>:<Minikube 目录>

通过上述的配置即可实现 Pod 的目录映射,但这种方式强依赖于服务节点,在集群模式下的 Kubernetes 不同节点可能分步在不同服务器上,因此映射目录并不互通,若 Pod 服务位于不同节点映射将失效。

针对这类场景,Kubernetes 为集群化部署提供了 PersistentVolumeClaim 持久化配置方式,将数据独立存储管理,如下配置中声明了空间大小为 5GB 的持久化目录。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

通过 get pvc 命令便可看到上述配置所定义的文件服务已成功运行。

需注意 pvc 默认不支持创建时拷贝文件,若需实现对应效果可通过 InitContainer 启动创建一次性 Pod 在启动时手动写入数据至 pvc,此时数据便可一直存在供其它服务使用。

[root@localhost app]# kubectl apply -f test-pvc.yaml 
persistentvolumeclaim/my-pvc created

[root@localhost app]# kubectl get pvc
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
my-pvc   Bound    pvc-567c103e-b68f-40d7-931d-e2ed54345e73   5Gi        RWO            standard       <unset>                 2m44s

而后同样通过 volumesvolumeMounts 即可实现目录映射,此时 Pod 下所声明目录下产生的所有文件内容都将同步拷贝映射至文件服务中,即便删除 test-pod 节点数据仍会持久化保存于 my-pvc 当中。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  labels:
    app: test-label
spec:
  restartPolicy: Always
  containers:
    - name: k8s-test
      image: k8s-test:1.0
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - name: pers-data
          mountPath: /app/data
    volumes:
        - name: pers-data
          persistentVolumeClaim:
            claimName: my-pvc

五、Deployment

1. 基础介绍

在上述的介绍中,我们成功启动了单个 Pod 实例,但在真正的应用场景,常伴随着大批量的集群化服务。

上述的方式虽然达到效果,但显然效率过于低下,因此 Kubernetes 中提供了 Deployment 提供高度集成的集群化 Pod 服务管理。

同样的,以具体的 test-deployment.yaml 配置示例进行介绍说明,文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-label
  template:
    metadata:
      labels:
        app: test-label
    spec:
      containers:
        - name: k8s-test
          image: k8s-test:1.0
          imagePullPolicy: IfNotPresent

配置中 kind 指定为 Deployment,配置说明参考下表,详细说明可访问官网:Deployment

方法 作用
replicas 集群副本实例数量。
selector.matchLabels.app 指定被当前 Deployment 管理的 Pod。
template.metadata.labels.app 当前 Deployment 创建 Pod 时设置的标签。

其中容器信息 containers 配置项与先前并无差异,这里着重说明 label 的配置项。

正如最开始提到的,label 用于 selector 的资源选择,当 K8S 中运行多个 Pod 实例时,selector.matchLabels.app 用于标识哪些 Pod 受当前 Deployment 的管理。而 template.metadata.labels.app 则作用于 Deployment 创建 Pod 时为这些 Pod 指定标签。

通常情况下,同一份 Deployment 配置文件下 selector.matchLabels.apptemplate.metadata.labels.app 值需保持一致。

2. 副本集群

在高可用的模式下,部署常采用分布式的形式存在,通过多实例保证在部分宕机的下服务仍保持可用。

在传统的服务部署模式下,需要手动启动多个实例服务,通过 Nginx 等代理工具实现负载均衡。

Deployment 则可通过 replicas 简化多个实例启动这一步骤,由 Kubernetes 自动拉起多个节点。

还是以示例进行演示,让我们先删除之前创建的 Pod 实例。

[root@localhost app]# kubectl get pod
NAME       READY   STATUS    RESTARTS   AGE
test-pod   1/1     Running   0          6d22h

[root@localhost app]# kubectl delete pod test-pod
pod "test-pod" deleted from default namespace

完成后修改上述的配置文件的中 replicas = 3 后与之前一样通过 apply -f 启动 Deployment

启动后可通过 get deployment 查看对应实例信息,同时 get pod 也可以看到 3Pod 实例也已启动。

通过 Deploymentreplicas 在集群化服务下我们不再需要手动逐一创建实例,通过配置便可一键启动。同时,kubelet 将监控 Pod 运行情况,当 Pod 不足 replicas 数量时会自动启动新的 Pod 实例。

# 启动 Deployment
[root@localhost app]# kubectl apply -f test-deployment.yaml 
deployment.apps/test-deployment created

# 查看 Deployment
[root@localhost app]# kubectl get deployment
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
test-deployment   3/3     3            3           3s

# 查看 Pod
[root@localhost app]# kubectl get pod -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
test-deployment-6ccd7f9d44-8nkzg   1/1     Running   0          7s    10.244.0.10   minikube   <none>           <none>
test-deployment-6ccd7f9d44-nnkch   1/1     Running   0          7s    10.244.0.12   minikube   <none>           <none>
test-deployment-6ccd7f9d44-pb2gb   1/1     Running   0          7s    10.244.0.11   minikube   <none>           <none>

例如手动删除 8nkzg 后缀的 Pod 实例后可以看到 Deployment 又自动启动了 pgxdg 后缀的实例。

如此一来,即便出现某个 Pod 宕机下线,Kubernetes 也能够立即发现且自动拉起服务,配合刚刚提到的重启策略 (restartPolicy),二者搭配便可实现服务集群的高可用。

[root@localhost app]# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
test-deployment-6ccd7f9d44-8nkzg   1/1     Running   0          6m27s
test-deployment-6ccd7f9d44-nnkch   1/1     Running   0          6m27s
test-deployment-6ccd7f9d44-pb2gb   1/1     Running   0          6m27s

# 手动删除 Pod
[root@localhost app]# kubectl delete pod test-deployment-6ccd7f9d44-8nkzg
pod "test-deployment-6ccd7f9d44-8nkzg" deleted from default namespace

[root@localhost app]# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
test-deployment-6ccd7f9d44-nnkch   1/1     Running   0          6m41s
test-deployment-6ccd7f9d44-pb2gb   1/1     Running   0          6m41s
test-deployment-6ccd7f9d44-pgxdg   1/1     Running   0          3s

3. 滚动升级

基于 replicas 的多副本模式下,Kubernetes 提供滚动升级的能力。即多个实例节点并不会采取一次性下线重启,避免造成短期内的服务不可用,而是分阶段的服务替换升级。

还是以刚才的 test-deployment.yaml 为例,加入 spec.strategy 控制服务升级策略。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deployment
spec:
  replicas: 3
  strategy:
     rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: test-label
  template:
    metadata:
      labels:
        app: test-label
    spec:
      containers:
        - name: k8s-test
          image: k8s-test:1.0
          imagePullPolicy: IfNotPresent

Deployment 中针对 strategy 分别提供了下述两类配置策略:

  • Recreate:在启动新的 Pod 节点前删除所有旧 Pod 实例,期间服务将不可用。
  • RollingUpdate:分步删除与新增 Pod 节点,保证整个过程仍有可用服务。

启动 Recreate 相对较好理解,也是服务升级中最常用的方式,这里着重介绍滚动升级 (RollingUpdate)

从上面的 yaml 配置中可以看到其提供了两个参数,更详细的介绍可参考官网:deployment-strategy

方法 作用
maxSurge 最大峰值,指定可以的超出 replicas 的 Pod 数。
maxUnavailable 最大不可用,指定更新过程中不可用的 Pod 的上限。

以上述的配置为例,replicas = 3maxSurge = 1,即表示整个升级过程最多允许创建 4 (replicas + maxSurge)Pod,同时仅允许 1 (maxUnavailable)Pod 节点不可用,其升级过程如下:

同时,在 Deployment 中当每次修改 spec.template 中的配置都将触发 Pod 更新。因此,在针对服务的部署更新中,我们只需重新打包镜像后修改 yamlcontainers.image 为新版后执行 apply -f 便可。

如此一来,在版本迭代发布时便无需手动繁杂的针对多个 Pod 手动进行更新发布,极大降低的工作量。

4. 服务回滚

使用 Deployment 的另一好处在于对每一次发布更新都会生成相应记录,默认下记录最近 10 次发布历史,Kubernetes 也基于此提供服务回滚的能力。

仍以刚才的工程为例,将其打包为 k8s-test:2.0 版本镜像,并修改 test-deployment.yaml 为内容为 image: k8s-test:2.0 后执行 apply -f 启动。

通过 -w 查看 Pod 的变化也恰好与刚才预期的滚动更新策略匹配,其按照之前预期的分节点停服重启。

[root@localhost app]# kubectl get pods -w
NAME                               READY   STATUS    RESTARTS   AGE
test-deployment-6ccd7f9d44-m5php   1/1     Running   0          13s
test-deployment-6ccd7f9d44-qpx2v   1/1     Running   0          13s
test-deployment-6ccd7f9d44-sn88q   1/1     Running   0          13s

test-deployment-7bc4f88555-k256h   0/1     Pending   0          0s
test-deployment-7bc4f88555-k256h   0/1     Pending   0          0s
test-deployment-6ccd7f9d44-sn88q   1/1     Terminating   0          4m33s
test-deployment-6ccd7f9d44-sn88q   1/1     Terminating   0          4m33s
test-deployment-7bc4f88555-dpp96   0/1     Pending       0          0s
test-deployment-7bc4f88555-k256h   0/1     ContainerCreating   0          0s
test-deployment-7bc4f88555-dpp96   0/1     Pending             0          0s
test-deployment-7bc4f88555-dpp96   0/1     ContainerCreating   0          0s
test-deployment-6ccd7f9d44-sn88q   0/1     Error               0          4m33s
test-deployment-7bc4f88555-k256h   1/1     Running             0          1s
test-deployment-7bc4f88555-dpp96   1/1     Running             0          1s
test-deployment-6ccd7f9d44-qpx2v   1/1     Terminating         0          4m34s
test-deployment-6ccd7f9d44-sn88q   0/1     Error               0          4m34s
test-deployment-6ccd7f9d44-sn88q   0/1     Error               0          4m34s
test-deployment-7bc4f88555-cq6q2   0/1     Pending             0          0s
test-deployment-7bc4f88555-cq6q2   0/1     Pending             0          0s
test-deployment-6ccd7f9d44-qpx2v   1/1     Terminating         0          4m34s
test-deployment-7bc4f88555-cq6q2   0/1     ContainerCreating   0          0s
test-deployment-6ccd7f9d44-m5php   1/1     Terminating         0          4m34s
test-deployment-6ccd7f9d44-m5php   1/1     Terminating         0          4m34s
test-deployment-6ccd7f9d44-qpx2v   0/1     Error               0          4m34s
test-deployment-6ccd7f9d44-m5php   0/1     Error               0          4m35s
test-deployment-7bc4f88555-cq6q2   1/1     Running             0          1s
test-deployment-6ccd7f9d44-m5php   0/1     Error               0          4m35s
test-deployment-6ccd7f9d44-m5php   0/1     Error               0          4m35s
test-deployment-6ccd7f9d44-qpx2v   0/1     Error               0          4m35s
test-deployment-6ccd7f9d44-qpx2v   0/1     Error               0          4m35s

完成更新升级之后,利用 rollout status 便可看到本次发布服务升级已经成功。

[root@localhost app]# kubectl rollout status deployment test-deployment
deployment "test-deployment" successfully rolled out

若需查看对应 Deployment 所有发布记录则可通过 rollout history 参数查看。

在下述中可以看到 test-deployment 一共经历了两次发布,首次创建以及刚刚的滚动升级。

[root@localhost app]# kubectl rollout history deployment test-deployment
deployment.apps/test-deployment 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

假设此时发现服务出现异常需回滚至上一版本,则可通过 rollout undo 回滚至指定版本。

例如下示例指定 --to-revision=1 回滚至最原始版本,查看 history 也可看到生成了对应的升级记录。

# 回滚服务
[root@localhost app]# kubectl rollout undo deployment test-deployment --to-revision=1
deployment.apps/test-deployment rolled back

# 查看历史
[root@localhost app]# kubectl rollout history deployment test-deployment
deployment.apps/test-deployment 
REVISION  CHANGE-CAUSE
2         <none>
3         <none>

六、容器探针

在上述的 Deployment 我们实现了服务的部署更新,而 K8S 其强大之处在于通过容器探针提供了服务启停与运行时的监控能力。

所谓容器探针,即在更新及运行过程中实时监控容器运行状态,根据探针结果执行一系列策略如自动重启等,下面就让我们一起了解其提供了哪些探针服务及其相应的作用。

1. 就绪探针

Kubernetes 所提供的探针服务,接下来让我们先来看就绪探针 (readinessProbe) 其对应的作用。

故名思意其用于检测更新发布过程中服务是否已经可用,在多副本 (replicas) 的负载均衡场景下,只有当就绪探针返回成功才会将流量路由至该 Pod 节点。

首先在之前的 Spring Boot 工程中新增健康检测接口,在生产环境中可使用 Actuator 健康检测。

@RestController
@RequestMapping("/api")
public TestResource {

    @GetMapping("health")
    public String health() {
        return "success"
    }
}

修改之前的 test-deployment.yaml 文件,通过 readinessProbe 配置就绪探针策略。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-label
  template:
    metadata:
      labels:
        app: test-label
    spec:
      containers:
        - name: k8s-test
          image: k8s-test:1.0
          imagePullPolicy: IfNotPresent
          readinessProbe:
            httpGet:
              path: /api/health
              port: 9090
            initialDelaySeconds: 10
            periodSeconds: 30
            timeoutSeconds: 10
            successThreshold: 3
            failureThreshold: 3
            terminationGracePeriodSeconds: 30

探针配置的类型除了 httpGet 接口外,还支持 tcpSocketgrpcexec 等形式,各项效果类型这里不展开介绍,详细配置可阅览官网教程:Container Probes

针对探针的其余参数这里列举几项常见配置,详细内容同样参考官网:Probes Config

参数 作用
initialDelaySeconds 启动后要等待多少秒后才启动探针。
periodSeconds 探测的间隔(单位是秒),默认是 10 秒,最小值是 1。
timeoutSeconds 超时后等待多少秒,默认值是 1 秒,最小值是 1。
successThreshold 指定探针连续成功多少次才算真的成功,默认值是 1。
failureThreshold 探针在失败次数阈值,默认值为 3,最小值为 1。
terminationGracePeriodSeconds 被终止时给容器优雅退出的最长等待时间(秒),默认 30 秒。

以下述就绪探针配置为例,即在服务启动 10s (initialDelaySeconds) 后启动,每隔 5s (periodSeconds) 进行重试,当响应超过 10s 则为失败。

若连续 3次 (successThreshold) 成功则表明服务可用,若超过 3次 (failureThreshold) 失败则将触发服务重启,此时向容器发起重启指定并等待 30s (terminationGracePeriodSeconds) ,若此时未能重启成功将执行服务强制重启。

readinessProbe:
    initialDelaySeconds: 10
    periodSeconds: 5
    timeoutSeconds: 10
    successThreshold: 3
    failureThreshold: 3
    terminationGracePeriodSeconds: 30

2. 存活探针

存活探针与就绪探针类似,前者作用于服务运行期间,而后者则生效于服务启动期间。

存活探针通过 livenessProbe 参数进行配置,其配置方式与上述就绪探针一致这里不在重复介绍。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-label
  template:
    metadata:
      labels:
        app: test-label
    spec:
      containers:
        - name: k8s-test
          image: k8s-test:1.0
          imagePullPolicy: IfNotPresent
          livenessProbe:
            httpGet:
              path: /api/health
              port: 9090
            initialDelaySeconds: 3
            periodSeconds: 3

3. 启动探针

启动探针 (startupProbe) 顾名思义用于指示容器中的应用是否已经启动,若配置了启动探针则所有其他探针都会被禁用,直到此探针成功为止。

如果启动探测失败,kubelet 将杀死容器,而容器依其重启策略进行重启,若容器没有提供启动探测,则默认状态为 Success

在实际的服务部署中,相对上述提到的两类探针服务,启动探针通常并不是我们关心的重点,其使用频率相对前二者也更低。

同时,其使用与就绪探针和存活探针并无差异,故这里不再介绍,详情可参考官网:StartupProbe


文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录