Python3 - k8s之深入理解 Pod
文章目录
一、 静态 Pod
在Kubernetes集群中除了我们经常使用到的普通的 Pod 外,还有一种特殊的 Pod,叫做Static Pod
,就是我们说的静态 Pod,静态 Pod 有什么特殊的地方呢?
静态 Pod 直接由特定节点上的kubelet
进程来管理,不通过 master 节点上的apiserver
。无法与我们常用的控制器Deployment
或者DaemonSet
进行关联,它由kubelet
进程自己来监控,当pod
崩溃时重启该pod
,kubelete
也无法对他们进行健康检查。静态 pod 始终绑定在某一个kubelet
,并且始终运行在同一个节点上。 kubelet
会自动为每一个静态 pod 在 Kubernetes 的 apiserver 上创建一个镜像 Pod(Mirror Pod),因此我们可以在 apiserver 中查询到该 pod,但是不能通过 apiserver 进行控制(例如不能删除)。
创建静态 Pod 有两种方式:配置文件和 HTTP 两种方式
1.1 配置文件
配置文件就是放在特定目录下的标准的 JSON 或 YAML 格式的 pod 定义文件。用kubelet --pod-manifest-path=<the directory>
来启动kubelet
进程,kubelet 定期的去扫描这个目录,根据这个目录下出现或消失的 YAML/JSON 文件来创建或删除静态 pod。
比如我们在 node01 这个节点上用静态 pod 的方式来启动一个 nginx 的服务。我们登录到node01节点上面,可以通过下面命令找到kubelet对应的启动配置文件
[root@k8s-node01 ~]# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Mon 2022-04-04 18:32:25 CST; 2 days ago
Docs: https://kubernetes.io/docs/
Main PID: 13026 (kubelet)
Tasks: 43
Memory: 74.3M
CGroup: /system.slice/kubelet.service
└─13026 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --co...
Apr 05 14:50:38 k8s-node01 kubelet[13026]: I0405 14:50:38.856208 13026 reconciler.go:301] Volume detached for volume "default-token-86...ePath ""
Apr 05 14:50:38 k8s-node01 kubelet[13026]: I0405 14:50:38.856215 13026 reconciler.go:301] Volume detached for volume "default-token-86...ePath ""
Apr 05 14:50:58 k8s-node01 kubelet[13026]: E0405 14:50:58.336985 13026 remote_runtime.go:295] ContainerStatus "d07a5775c1b727189585ff1...4ee747c0
Apr 05 14:50:58 k8s-node01 kubelet[13026]: I0405 14:50:58.389579 13026 reconciler.go:181] operationExecutor.UnmountVolume started for volume "...
Apr 05 14:50:58 k8s-node01 kubelet[13026]: I0405 14:50:58.396048 13026 operation_generator.go:831] UnmountVolume.TearDown succeeded for volume...
Apr 05 14:50:58 k8s-node01 kubelet[13026]: I0405 14:50:58.489791 13026 reconciler.go:301] Volume detached for volume "default-token-86...ePath ""
Apr 05 14:51:11 k8s-node01 kubelet[13026]: E0405 14:51:11.156490 13026 fsHandler.go:118] failed to collect filesystem stats - rootDisk...dd5b0f0b
Apr 05 14:51:12 k8s-node01 kubelet[13026]: E0405 14:51:12.094784 13026 fsHandler.go:118] failed to collect filesystem stats - rootDisk...da84c631
Apr 05 14:51:12 k8s-node01 kubelet[13026]: E0405 14:51:12.137411 13026 fsHandler.go:118] failed to collect filesystem stats - rootDisk...7698bddd
Apr 05 14:51:12 k8s-node01 kubelet[13026]: E0405 14:51:12.359154 13026 fsHandler.go:118] failed to collect filesystem stats - rootDisk...f845d50d
Hint: Some lines were ellipsized, use -l to show in full.
配置文件路径为:
[root@k8s-node01 ~]# cat /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
打开这个文件我们可以看到其中有一条如下的环境变量配置: Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"
所以如果我们通过kubeadm
的方式来安装的集群环境,对应的kubelet
已经配置了我们的静态 Pod 文件的路径,那就是/etc/kubernetes/manifests
,所以我们只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可:
如果你的 kubelet 启动参数中没有配置上面的--pod-manifest-path
参数的话,那么添加上这个参数然后重启 kubelet 即可。
# 创建yaml文件
[root@k8s-node01 ~]# cd /etc/kubernetes/manifests/
[root@k8s-node01 manifests]# ls
[root@k8s-node01 manifests]# vim static-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: static-pod1
labels:
app: static
spec:
containers:
- name: web
image: nginx
ports:
- name: webport
containerPort: 80
# 查看一下, 自动生成了容器
[root@k8s-node01 manifests]# docker ps | grep static
096f86b8540c nginx "/docker-entrypoint.…" 44 seconds ago Up 44 seconds k8s_web_static-pod1-k8s-node01_default_19ab912487f4709fa8ae7020401ef414_0
8ab8ee6de0fd registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 47 seconds ago Up 47 seconds k8s_POD_static-pod1-k8s-node01_default_19ab912487f4709fa8ae7020401ef414_0
1.2 通过 HTTP 创建静态 Pods
kubelet 周期地从–manifest-url=
参数指定的地址下载文件,并且把它翻译成 JSON/YAML 格式的 pod 定义。此后的操作方式与–pod-manifest-path=
相同,kubelet 会不时地重新下载该文件,当文件变化时对应地终止或启动静态 pod。
1.3 静态pods的动态增加和删除
如何删除或重启呢? 直接移除/etc/kubernetes/manifests/
文件夹下的yaml即可, 恢复则是重新放进来
运行中的kubelet周期扫描配置的目录(我们这个例子中就是/etc/kubernetes/manifests)下文件的变化,当这个目录中有文件出现或消失时创建或删除pods。
[root@k8s-node01 manifests]# mv static-pod.yaml /tmp/
[root@k8s-node01 manifests]# docker ps | grep static
[root@k8s-node01 manifests]#
[root@k8s-node01 manifests]# mv /tmp/static-pod.yaml ./
[root@k8s-node01 manifests]# docker ps | grep static
96ae6e7c78e2 nginx "/docker-entrypoint.…" 1 second ago Up 1 second k8s_web_static-pod1-k8s-node01_default_19ab912487f4709fa8ae7020401ef414_0
14b6fcd7e257 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 4 seconds ago Up 4 seconds k8s_POD_static-pod1-k8s-node01_default_19ab912487f4709fa8ae7020401ef414_0
其实我们用 kubeadm 安装的集群,master 节点上面的几个重要组件都是用静态 Pod 的方式运行的,我们登录到 master 节点上查看/etc/kubernetes/manifests
目录:
[root@k8s-master01 ~]# cd /etc/kubernetes/manifests/
[root@k8s-master01 manifests]# lsshell
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
那么在 k8s-master01 下创建会怎样呢?
[root@k8s-master01 ~]# cd /etc/kubernetes/manifests/
[root@k8s-master01 manifests]# vim static-pod2.yaml
[root@k8s-master01 manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
harry-nginx-6f9f8d4465-mr2ft 1/1 Running 0 2d1h
harry-nginx-6f9f8d4465-tfj78 1/1 Running 0 2d1h
my-nginx-576bb7cb54-k4gj6 1/1 Running 0 47h
static-pod1-k8s-node01 1/1 Running 0 4m50s
static-pod2-k8s-master01 1/1 Running 0 7s
testservice-754455d66-m64nw 1/1 Running 2 2d13h
现在明白了吧,这种方式也为我们将集群的一些组件容器化提供了可能,因为这些 Pod 都不会受到 apiserver 的控制,不然我们这里kube-apiserver
怎么自己去控制自己呢?万一不小心把这个 Pod 删掉了呢?所以只能有kubelet
自己来进行控制,这就是我们所说的静态 Pod。
二、 Pod Hook
我们知道Pod
是Kubernetes
集群中的最小单元,而 Pod 是有容器组组成的,所以在讨论 Pod 的生命周期的时候我们可以先来讨论下容器的生命周期。
实际上 Kubernetes 为我们的容器提供了生命周期钩子的,就是我们说的Pod Hook
,Pod Hook 是由 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。我们可以同时为 Pod 中的所有容器都配置 hook。
Kubernetes 为我们提供了两种钩子函数:
- PostStart:这个钩子在容器创建后立即执行。但是,并不能保证钩子将在容器
ENTRYPOINT
之前运行,因为没有参数传递给处理程序。主要用于资源部署、环境准备等。不过需要注意的是如果钩子花费太长时间以至于不能运行或者挂起, 容器将不能达到running
状态。 - PreStop:这个钩子在容器终止之前立即被调用。它是阻塞的,意味着它是同步的, 所以它必须在删除容器的调用发出之前完成。主要用于优雅关闭应用程序、通知其他系统等。如果钩子在执行期间挂起, Pod阶段将停留在
running
状态并且永不会达到failed
状态。
如果PostStart
或者PreStop
钩子失败, 它会杀死容器。所以我们应该让钩子函数尽可能的轻量。当然有些情况下,长时间运行命令是合理的, 比如在停止容器之前预先保存状态。
另外我们有两种方式来实现上面的钩子函数:
- Exec - 用于执行一段特定的命令,不过要注意的是该命令消耗的资源会被计入容器。
- HTTP - 对容器上的特定的端点执行
HTTP
请求。
2.1 环境准备
以下示例中,定义了一个Nginx Pod,其中设置了PostStart
钩子函数,即在容器创建成功后,写入一句话到/usr/share/message
文件中。
---
apiVersion: v1
kind: Pod
metadata:
name: hook-demo
labels:
app: hook
spec:
containers:
- name: hook-demo
image: nginx
ports:
- name: webport
containerPort: 80
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo hello from the postStart Handler > /usr/share/message"]
[root@k8s-master01 kubeadm]# kubectl apply -f pod-hook1.yaml
pod/hook-demo created
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hook-demo 1/1 Running 0 7s
2.2 优雅删除资源对象
当用户请求删除含有 pod 的资源对象时(如Deployment等),K8S 为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S提供两种信息通知:
- 默认:K8S 通知 node 执行
docker stop
命令,docker 会先向容器中PID
为1的进程发送系统信号SIGTERM
,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL
的系统信号强行 kill 掉进程。 - 使用 pod 生命周期(利用
PreStop
回调函数),它执行在发送终止信号之前。
默认所有的优雅退出时间都在30秒内。kubectl delete 命令支持 --grace-period=<seconds>
选项,这个选项允许用户用他们自己指定的值覆盖默认值。值’0’代表 强制删除 pod. 在 kubectl 1.5 及以上的版本里,执行强制删除时必须同时指定 --force --grace-period=0
。
强制删除一个 pod 是从集群状态还有 etcd 里立刻删除这个 pod。 当 Pod 被强制删除时, api 服务器不会等待来自 Pod 所在节点上的 kubelet 的确认信息:pod 已经被终止。在 API 里 pod 会被立刻删除,在节点上, pods 被设置成立刻终止后,在强行杀掉前还会有一个很小的宽限期。
以下示例中,定义了一个Nginx Pod,其中设置了PreStop
钩子函数,即在容器退出之前,优雅的关闭 Nginx:
方法1 强制删除pod:
[root@k8s-master01 kubeadm]# kubectl delete pod hook-demo --grace-period=0 --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "hook-demo" force deleted
方式2 优雅的删除pod:
---
apiVersion: v1
kind: Pod
metadata:
name: hook-demo2
labels:
app: hook
spec:
containers:
- name: hook-demo2
image: nginx
ports:
- name: webport
containerPort: 80
volumeMounts:
- name: message
mountPath: /usr/share
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "echo hello from the postStart Handler > /usr/share/message"]
volumes:
- name: message
hostPath:
path: /tmp
[root@k8s-master01 kubeadm]# kubectl apply -f pod-hook2.yaml
pod/hook-demo2 created
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hook-demo 1/1 Running 0 17m
hook-demo2 1/1 Running 0 7s
另外Hook
调用的日志没有暴露个给 Pod 的 event,所以只能通过describe
命令来获取,如果有错误将可以看到FailedPostStartHook
或FailedPreStopHook
这样的 event。
2.3 Pod健康检查
liveness probe
(存活探针)和readiness probe
(可读性探针)
上面我们和大家一起学习了Pod
中容器的生命周期的两个钩子函数,PostStart
与PreStop
,其中PostStart
是在容器创建后立即执行的,而PreStop
这个钩子函数则是在容器终止之前执行的。除了上面这两个钩子函数以外,还有一项配置会影响到容器的生命周期的,那就是健康检查的探针。
在Kubernetes
集群当中,我们可以通过配置liveness probe
(存活探针)和readiness probe
(可读性探针)来影响容器的生存周期。
- kubelet 通过使用 liveness probe 来确定你的应用程序是否正在运行,通俗点将就是是否还活着。一般来说,如果你的程序一旦崩溃了, Kubernetes 就会立刻知道这个程序已经终止了,然后就会重启这个程序。而我们的 liveness probe 的目的就是来捕获到当前应用程序还没有终止,还没有崩溃,如果出现了这些情况,那么就重启处于该状态下的容器,使应用程序在存在 bug 的情况下依然能够继续运行下去。
- kubelet 使用 readiness probe 来确定容器是否已经就绪可以接收流量过来了。这个探针通俗点讲就是说是否准备好了,现在可以开始工作了。只有当 Pod 中的容器都处于就绪状态的时候 kubelet 才会认定该 Pod 处于就绪状态,因为一个 Pod 下面可能会有多个容器。当然 Pod 如果处于非就绪状态,那么我们就会将他从我们的工作队列(实际上就是我们后面需要重点学习的 Service)中移除出来,这样我们的流量就不会被路由到这个 Pod 里面来了。
和前面的钩子函数一样的,我们这两个探针的支持两种配置方式:
* exec:执行一段命令
* http:检测某个 http 请求
* tcpSocket:使用此配置, kubelet 将尝试在指定端口上打开容器的套接字。如果可以建立连接,容器被认为是健康的,如果不能就认为是失败的。实际上就是检查端口
好,我们先来给大家演示下存活探针的使用方法,首先我们用exec
执行命令的方式来检测容器的存活,如下:
[root@k8s-master01 kubeadm]# vim liveness-exec.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
labels:
app: liveness
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
我们这里需要用到一个新的属性:livenessProbe
,下面通过exec
执行一段命令,其中periodSeconds
属性表示让kubelet
每隔5秒执行一次存活探针,也就是每5秒执行一次上面的cat /tmp/healthy
命令,如果命令执行成功了,将返回0,那么kubelet
就会认为当前这个容器是存活的并且很监控,如果返回的是非0值,那么kubelet
就会把该容器杀掉然后重启它。另外一个属性initialDelaySeconds
表示在第一次执行探针的时候要等待5秒,这样能够确保我们的容器能够有足够的时间启动起来。大家可以想象下,如果你的第一次执行探针等候的时间太短,是不是很有可能容器还没正常启动起来,所以存活探针很可能始终都是失败的,这样就会无休止的重启下去了,对吧?所以一个合理的initialDelaySeconds
非常重要。
[root@k8s-master01 kubeadm]# kubectl apply -f liveness-exec.yaml
pod/liveness-exec created
另外我们在容器启动的时候,执行了如下命令:
~ /bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
意思是说在容器最开始的30秒内有一个/tmp/healthy
文件,在这30秒内执行cat /tmp/healthy
命令都会返回一个成功的返回码。30秒后,我们删除这个文件,现在执行cat /tmp/healthy
是不是就会失败了,这个时候就会重启容器了。
我们来创建下该Pod
,在30秒内,查看Pod
的Event
:
~ kubectl describe pod liveness-exec
我们可以观察到容器是正常启动的,在隔一会儿,比如40s后,再查看下Pod
的Event
,在最下面有一条信息显示 liveness probe
失败了,容器被删掉并重新创建。
然后通过kubectl get pod liveness-exec
可以看到RESTARTS
值加1了。
同样的,我们还可以使用HTTP GET
请求来配置我们的存活探针,我们这里使用一个liveness
镜像来验证演示下,
---
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
labels:
name: liveness
spec:
containers:
- name: liveness
image: cnych/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
同样的,根据periodSeconds
属性我们可以知道kubelet
需要每隔3秒执行一次liveness probe
,该探针将向容器中的 server 的8080端口发送一个 HTTP GET 请求。如果 server 的 /healthz 路径的 handler 返回一个成功的返回码,kubelet
就会认定该容器是活着的并且很健康,如果返回失败的返回码,kubelet
将杀掉该容器并重启它。。initialDelaySeconds
指定kubelet
在该执行第一次探测之前需要等待3秒钟。
通常来说,任何大于200小于400的返回码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。
我们可以来查看下上面的healthz
的实现
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})
大概意思就是最开始前10s返回状态码200,10s过后就返回500的status_code
了。所以当容器启动3秒后,kubelet
开始执行健康检查。第一次健康监测会成功,因为是在10s之内,但是10秒后,健康检查将失败,因为现在返回的是一个错误的状态码了,所以kubelet
将会杀掉和重启容器。
同样的,我们来创建下该Pod
测试下效果,10秒后,查看 Pod 的 event,确认liveness probe
失败并重启了容器。
~ kubectl describe pod liveness-http
然后我们来通过端口的方式来配置存活探针,使用此配置,kubelet
将尝试在指定端口上打开容器的套接字。 如果可以建立连接,容器被认为是健康的,如果不能就认为是失败的。
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hook-demo 1/1 Running 0 148m
liveness-exec 0/1 CrashLoopBackOff 7 14m
liveness-http 0/1 CrashLoopBackOff 6 7m7s
---
apiVersion: v1
kind: Pod
metadata:
name: liveness-readiness
labels:
name: liveness-readiness
spec:
containers:
- name: liveness-readiness
image: cnych/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
我们可以看到,TCP 检查的配置与 HTTP 检查非常相似,只是将httpGet
替换成了tcpSocket
。 而且我们同时使用了readiness probe
和liveness probe
两种探针。 容器启动后5秒后,kubelet
将发送第一个readiness probe
(可读性探针)。 该探针会去连接容器的8080端,如果连接成功,则该 Pod 将被标记为就绪状态。然后Kubelet
将每隔10秒钟执行一次该检查。
除了readiness probe
之外,该配置还包括liveness probe
。 容器启动15秒后,kubelet
将运行第一个 liveness probe
。 就像readiness probe
一样,这将尝试去连接到容器的8080端口。如果liveness probe
失败,容器将重新启动。
有的时候,应用程序可能暂时无法对外提供服务,例如,应用程序可能需要在启动期间加载大量数据或配置文件。 在这种情况下,您不想杀死应用程序,也不想对外提供服务。 那么这个时候我们就可以使用readiness probe
来检测和减轻这些情况。 Pod中的容器可以报告自己还没有准备,不能处理Kubernetes服务发送过来的流量。
从上面的YAML
文件我们可以看出readiness probe
的配置跟liveness probe
很像,基本上一致的。唯一的不同是使用readinessProbe
而不是livenessProbe
。两者如果同时使用的话就可以确保流量不会到达还未准备好的容器,准备好过后,如果应用程序出现了错误,则会重新启动容器。
另外除了上面的initialDelaySeconds
和periodSeconds
属性外,探针还可以配置如下几个参数:
* timeoutSeconds:探测超时时间,默认1秒,最小1秒。
* successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是 1,但是如果是`liveness`则必须是 1。最小值是 1。
* failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是 3,最小值是 1。
这就是liveness probe
(存活探针)和readiness probe
(可读性探针)的使用方法。在Pod
的生命周期当中,我们已经学习了容器生命周期中的钩子函数和探针检测,接下来讲解Pod
层面生命周期的一个阶段:初始化容器。
三、初始化容器
上面我们学习了容器的健康检查的两个探针:liveness probe
(存活探针)和readiness probe
(可读性探针)的使用方法,我们说在这两个探针是可以影响容器的生命周期的,包括我们之前提到的容器的两个钩子函数PostStart
和PreStop
。我们今天要给大家介绍的是Init Container
(初始化容器)。
Init Container
就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行,只有所有的Init Container
执行完后,主容器才会被启动。我们知道一个Pod
里面的所有容器是共享数据卷和网络命名空间的,所以Init Container
里面产生的数据可以被主容器使用到的。
是不是感觉Init Container
和之前的钩子函数有点类似啊,只是是在容器执行前来做一些工作,是吧?从直观的角度看上去的话,初始化容器的确有点像PreStart
,但是钩子函数和我们的Init Container
是处在不同的阶段的,我们可以通过下面的图来了解下:
从上面这张图我们可以直观的看到PostStart
和PreStop
包括liveness
和readiness
是属于主容器的生命周期范围内的,而Init Container
是独立于主容器之外的,当然他们都属于Pod
的生命周期范畴之内的,现在我们应该明白Init Container
和钩子函数之类的区别了吧。
另外我们可以看到上面我们的Pod
右边还有一个infra
的容器,这是一个什么容器呢?我们可以在集群环境中去查看下人任意一个Pod
对应的运行的Docker
容器,我们可以发现每一个Pod
下面都包含了一个pause-amd64
的镜像,这个就是我们的infra
镜像,我们知道Pod
下面的所有容器是共享同一个网络命名空间的,这个镜像就是来做这个事情的,所以每一个Pod
当中都会包含一个这个镜像。
最开始 Pod 启动不起来就是因为这个 infra 镜像没有被拉下来,因为默认该镜像是需要到谷歌服务器上拉取的,所以需要提前拉取到节点上面。
我们说Init Container
主要是来做初始化容器工作的,那么他有哪些应用场景呢?
- 等待其他模块Ready:这个可以用来解决服务之间的依赖问题,比如我们有一个 Web 服务,该服务又依赖于另外一个数据库服务,但是在我们启动这个 Web 服务的时候我们并不能保证依赖的这个数据库服务就已经启动起来了,所以可能会出现一段时间内 Web 服务连接数据库异常。要解决这个问题的话我们就可以在 Web 服务的 Pod 中使用一个 InitContainer,在这个初始化容器中去检查数据库是否已经准备好了,准备好了过后初始化容器就结束退出,然后我们的主容器 Web 服务被启动起来,这个时候去连接数据库就不会有问题了。
- 做初始化配置:比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。
- 其它场景:如将 pod 注册到一个中央数据库、配置中心等。
3.1 init-pod
我们先来给大家演示下服务依赖的场景下初始化容器的使用方法,如下Pod
的定义方法
---
apiVersion: v1
kind: Pod
metadata:
name: init-pod
labels:
app: init
spec:
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookpup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookpup mydb; do echo waiting for mydb; sleep 2; done;']
containers:
- name: main-container
image: busybox
command: ["sh", "-c", "echo The app is running! && sleep 3600"]
Service
的对应YAML
内容:
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 6378
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hook-demo 1/1 Running 0 3h23m
init-pod 1/1 Running 0 15m
我们可以先创建上面的Pod
,然后查看下Pod
的状态,然后再创建下面的Service
,对比下前后状态。
我们在Pod
启动过程中,初始化容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。如果由于运行时或失败退出,导致容器启动失败,它会根据Pod
的restartPolicy
指定的策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时会使用 RestartPolicy 策略。
在所有的初始化容器没有成功之前,Pod
将不会变成 Ready
状态。正在初始化中的Pod
处于Pending
状态,但应该会将条件Initializing
设置为 true。
3.2 初始化配置pod
接下来我们再来尝试创建一个做初始化配置工作的Pod
:
---
apiVersion: v1
kind: Pod
metadata:
name: init-demo
labels:
app: init
spec:
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- https://www.baidu.com
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
我们可以看到这里又出现了volumes
,spec.volumes
指的是Pod
中的卷,spec.containers.volumeMounts
,是将指定的卷 mount 到容器指定的位置,相当于docker里面的-v 宿主机目录:容器目录
,我们前面用到过hostPath
,我们这里使用的是emptyDir{}
,这个就相当于一个共享卷,是一个临时的目录,生命周期等同于Pod
的生命周期。
初始化容器执行完,会下载一个 html 文件映射到emptyDir{},而主容器也是和 spec.volumes 里的emptyDir{} 进行映射,所以nginx容器的
/usr/share/nginx/html`目录下会映射 index.html 文件。
我们来创建下该Pod
[root@k8s-master01 kubeadm]# kubectl create -f initconfig.yaml
[root@k8s-master01 kubeadm]# kubectl get pod init-demo
NAME READY STATUS RESTARTS AGE
init-demo 1/1 Running 0 5m33s
[root@k8s-master01 kubeadm]# kubectl exec -it init-demo -- /bin/bash
root@init-demo:/# cd /usr/share/nginx/html/
root@init-demo:/usr/share/nginx/html# ls
index.html
root@init-demo:/usr/share/nginx/html# cat index.html
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');
</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
root@init-demo:/usr/share/nginx/html#
如果我们看到有百度相关的信息那么证明我们上面的初始化的工作就完成了。
这就是我们初始化容器的使用方法,到这里我们就把Pod
的整个生命周期当中的几个主要阶段讲完了,第一个是容器的两个钩子函数:PostStart
和PreStop
,还有就是容器健康检查的两个探针:liveness probe
和readiness probe
,以及本次的Init Container
。下面开始我们来讲解一些常用的控制器和Pod
的结合。
四、常见对象操作
4.1 使用Replication Controller、Replica Set 管理Pod
前面我们学习了Pod
的一些基本使用方法,而且前面我们都是直接来操作的Pod
,假如我们现在有一个Pod
正在提供线上的服务,我们来想想一下我们可能会遇到的一些场景:
- 某次运营活动非常成功,网站访问量突然暴增
- 运行当前
Pod
的节点发生故障了,Pod
不能正常提供服务了
第一种情况,可能比较好应对,一般活动之前我们会大概计算下会有多大的访问量,提前多启动几个Pod
,活动结束后再把多余的Pod
杀掉,虽然有点麻烦,但是应该还是能够应对这种情况的。
第二种情况,可能某天夜里收到大量报警说服务挂了,然后起来打开电脑在另外的节点上重新启动一个新的Pod
,问题也很好的解决了。
如果我们都人工的去解决遇到的这些问题,似乎又回到了以前刀耕火种的时代了是吧,如果有一种工具能够来帮助我们管理Pod
就好了,Pod
不够了自动帮我新增一个,Pod
挂了自动帮我在合适的节点上重新启动一个Pod
,这样是不是遇到上面的问题我们都不需要手动去解决了。
幸运的是,Kubernetes
就为我们提供了这样的资源对象:
- Replication Controller:用来部署、升级
Pod
- Replica Set:下一代的
Replication Controller
- Deployment:可以更加方便的管理
Pod
和Replica Set
4.2 Replication Controller(RC)
Replication Controller
简称RC
,RC
是Kubernetes
系统中的核心概念之一,简单来说,RC
可以保证在任意时间运行Pod
的副本数量,能够保证Pod
总是可用的。如果实际Pod
数量比指定的多那就结束掉多余的,如果实际数量比指定的少就新启动一些Pod
,当Pod
失败、被删除或者挂掉后,RC
都会去自动创建新的Pod
来保证副本数量,所以即使只有一个Pod
,我们也应该使用RC
来管理我们的Pod
。
我们想想如果现在我们遇到上面的问题的话,可能除了第一个不能做到完全自动化,其余的我们是不是都不用担心了,运行Pod
的节点挂了,RC
检测到Pod
失败了,就会去合适的节点重新启动一个Pod
就行,不需要我们手动去新建一个Pod
了。如果是第一种情况的话在活动开始之前我们给Pod
指定10个副本,结束后将副本数量改成2,这样是不是也远比我们手动去启动、手动去关闭要好得多,而且我们后面还会给大家介绍另外一种资源对象HPA
可以根据资源的使用情况来进行自动扩缩容,这样以后遇到这种情况,我们就真的可以安心的去睡觉了。
现在我们来使用RC
来管理我们前面使用的Nginx
的Pod
,YAML
文件如下:
---
apiVersion: v1
kind: ReplicationController
metadata:
name: rc-demo
labels:
app: rc
spec:
replicas: 3
template:
metadata:
labels:
app: rc
spec:
containers:
- name: nginx-demo
image: nginx
ports:
- containerPort: 80
上面的YAML
文件相对于我们之前的Pod
的格式:
- kind:
ReplicationController
- spec.replicas: 指定
Pod
副本数量,默认为1 - spec.selector:
RC
通过该属性来筛选要控制的Pod
- spec.template: 这里就是我们之前的
Pod
的定义的模块,但是不需要apiVersion
和kind
了 - spec.template.metadata.labels: 注意这里的
Pod
的labels
要和spec.selector
相同,这样RC
就可以来控制当前这个Pod
了。
这个YAML
文件中的意思就是定义了一个RC
资源对象,它的名字叫rc-demo
,保证一直会有3个Pod
运行,Pod
的镜像是nginx
镜像。
注意
spec.selector
和spec.template.metadata.labels
这两个字段必须相同,否则会创建失败的,当然我们也可以不写spec.selector
,这样就默认与Pod
模板中的metadata.labels
相同了。所以为了避免不必要的错误的话,不写为好。
[root@k8s-master01 kubeadm]# kubectl create -f rc-demo.yaml
replicationcontroller/rc-demo created
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
rc-demo-54b47 1/1 Running 0 16s
rc-demo-s995l 1/1 Running 0 16s
rc-demo-vpnvl 1/1 Running 0 16s
[root@k8s-master01 kubeadm]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
rc-demo-54b47 1/1 Running 0 103s 10.244.1.86 k8s-node01
rc-demo-s995l 1/1 Running 0 103s 10.244.2.93 k8s-node02
rc-demo-vpnvl 1/1 Running 0 103s 10.244.1.85 k8s-node01
# 查看rc
[root@k8s-master01 kubeadm]# kubectl get rc
NAME DESIRED CURRENT READY AGE
rc-demo 3 3 3 5m3s
# 然后我们通过RC来修改下Pod的副本数量为2:
[root@k8s-master01 kubeadm]# kubectl apply -f rc-demo.yaml
# 或者直接编辑
[root@k8s-master01 kubeadm]# kubectl edit rc rc-demo
而且我们还可以用RC
来进行滚动升级,比如我们将镜像地址更改为nginx:1.7.9
:
[root@k8s-master01 kubeadm]# kubectl rolling-update rc-demo --image=nginx:1.7.9
但是如果我们的Pod
中多个容器的话,就需要通过修改YAML
文件来进行修改了:
[root@k8s-master01 kubeadm]# kubectl rolling-update rc-demo -f rc-demo.yaml
如果升级完成后出现了新的问题,想要一键回滚到上一个版本的话,使用RC
只能用同样的方法把镜像地址替换成之前的,然后重新滚动升级。
4.3 Replication Set(RS)
Replication Set
简称RS
,随着Kubernetes
的高速发展,官方已经推荐我们使用RS
和Deployment
来代替RC
了,实际上RS
和RC
的功能基本一致,目前唯一的一个区别就是RC
只支持基于等式的selector
(env=dev或environment!=qa),但RS
还支持基于集合的selector
(version in (v1.0, v2.0)),这对复杂的运维管理就非常方便了。
kubectl
命令行工具中关于RC
的大部分命令同样适用于我们的RS
资源对象。不过我们也很少会去单独使用RS
,它主要被Deployment
这个更加高层的资源对象使用,除非用户需要自定义升级功能或根本不需要升级Pod
,在一般情况下,我们推荐使用Deployment
而不直接使用Replica Set
。
最后我们总结下关于RC
/RS
的一些特性和作用吧:
- 大部分情况下,我们可以通过定义一个
RC
实现的Pod
的创建和副本数量的控制 RC
中包含一个完整的Pod
定义模块(不包含apiversion
和kind
)RC
是通过label selector
机制来实现对Pod
副本的控制的- 通过改变
RC
里面的Pod
副本数量,可以实现Pod
的扩缩容功能 - 通过改变
RC
里面的Pod
模板中镜像版本,可以实现Pod
的滚动升级功能(但是不支持一键回滚,需要用相同的方法去修改镜像地址)
好,我就给大家介绍了使用RC
或者RS
来管理我们的Pod
,下面来给大家介绍另外一种更加高级也是现在推荐使用的一个资源对象Deployment
。
五、 Deployment的使用
前面我们学习了Replication Controller
和Replica Set
两种资源对象,RC
和RS
的功能基本上是差不多的,唯一的区别就是RS
支持集合的selector
。我们也学习到了用RC
/RS
来控制Pod
副本的数量,也实现了滚动升级Pod
的功能。现在看上去似乎一切都比较完美的运行着,但是我们上面最后也提到了现在我们推荐使用Deployment
这种控制器了,而不是我们之前的RC
或者RS
,这是为什么呢?
没有对比就没有伤害对吧,我们来对比下二者之间的功能吧,首先RC
是Kubernetes
的一个核心概念,当我们把应用部署到集群之后,需要保证应用能够持续稳定的运行,RC
就是这个保证的关键,主要功能如下:
- 确保
Pod
数量:它会确保Kubernetes
中有指定数量的Pod
在运行,如果少于指定数量的Pod
,RC
就会创建新的,反之这会删除多余的,保证Pod
的副本数量不变。 - 确保
Pod
健康:当Pod
不健康,比如运行出错了,总之无法提供正常服务时,RC
也会杀死不健康的Pod
,重新创建新的。 - 弹性伸缩:在业务高峰或者低峰的时候,可以用过
RC
来动态的调整Pod
数量来提供资源的利用率,当然我们也提到过如果使用HPA
这种资源对象的话可以做到自动伸缩。 - 滚动升级:滚动升级是一种平滑的升级方式,通过逐步替换的策略,保证整体系统的稳定性,这个我们上面已经给大家演示过了。
Deployment
同样也是Kubernetes
系统的一个核心概念,主要职责和RC
一样的都是保证Pod
的数量和健康,二者大部分功能都是完全一致的,我们可以看成是一个升级版的RC
控制器,那Deployment
又具备那些新特性呢?
RC
的全部功能:Deployment
具备上面描述的RC
的全部功能- 事件和状态查看:可以查看
Deployment
的升级详细进度和状态 - 回滚:当升级
Pod
的时候如果出现问题,可以使用回滚操作回滚到之前的任一版本 - 版本记录:每一次对
Deployment
的操作,都能够保存下来,这也是保证可以回滚到任一版本的基础 - 暂停和启动:对于每一次升级都能够随时暂停和启动
作为对比,我们知道
Deployment
作为新一代的RC
,不仅在功能上更为丰富了,同时我们也说过现在官方也都是推荐使用Deployment
来管理Pod
的,比如一些官方组件kube-dns
、kube-proxy
也都是使用的Deployment
来管理的,所以当大家在使用的使用也最好使用Deployment
来管理Pod
。
5.1 创建
可以看出一个Deployment拥有多个Replica Set,而一个Replica Set拥有一个或多个Pod。一个Deployment控制多个rs主要是为了支持回滚机制,每当Deployment操作时,Kubernetes会重新生成一个Replica Set并保留,以后有需要的话就可以回滚至之前的状态。 下面创建一个Deployment,它创建了一个Replica Set来启动3个nginx pod,yaml文件如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
# 创建
[root@k8s-master01 kubeadm]# kubectl create -f deploy-demo.yaml
deployment.apps/nginx-deploy created
# 查询一下
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deploy-54f57cf6bf-9lbns 1/1 Running 0 50s
nginx-deploy-54f57cf6bf-h9wxl 1/1 Running 0 50s
nginx-deploy-54f57cf6bf-mb2cc 1/1 Running 0 50s
[root@k8s-master01 kubeadm]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 3/3 3 3 116s
[root@k8s-master01 kubeadm]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deploy-54f57cf6bf 3 3 3 2m31s
上面的Deployment的yaml文件中的replicas:3
将会保证我们始终有3个POD在运行。
由于Deployment
和RC
的功能大部分都一样的,我们上面已经和大家演示了大部分功能了,我们这里重点给大家演示下Deployment
的滚动升级和回滚功能。
5.2 滚动升级
现在我们将刚刚保存的yaml文件中的nginx镜像修改为nginx:1.13.3
,然后在spec下面添加滚动升级策略:
minReadySeconds: 5
strategy:
# indicate which strategy we want for rolling update
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
- minReadySeconds:
- Kubernetes在等待设置的时间后才进行升级
- 如果没有设置该值,Kubernetes会假设该容器启动起来后就提供服务了
- 如果没有设置该值,在某些极端情况下可能会造成服务不正常运行
- maxSurge:
- 升级过程中最多可以比原先设置多出的POD数量
- 例如:maxSurage=1,replicas=5,则表示Kubernetes会先启动1一个新的Pod后才删掉一个旧的POD,整个升级过程中最多会有5+1个POD。
- maxUnavaible:
- 升级过程中最多有多少个POD处于无法提供服务的状态
- 当
maxSurge
不为0时,该值也不能为0 - 例如:maxUnavaible=1,则表示Kubernetes整个升级过程中最多会有1个POD处于无法服务的状态。
修改后如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-demo
spec:
replicas: 3
revisionHistoryLimit: 15
minReadySeconds: 5
strategy:
type: RollingUpdate
rpllingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
# 应用更新一下
[root@k8s-master01 kubeadm]# kubectl apply -f deploy-demo.yaml --record=true
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.apps/nginx-deploy configured
然后我们可以使用rollout
命令:
-
查看状态:
[root@k8s-master01 kubeadm]# kubectl rollout status deployment/nginx-deploy deployment "nginx-deploy" successfully rolled out
-
暂停升级
[root@k8s-master01 kubeadm]# kubectl rollout pause deployment deploy-demo.yaml
-
继续升级
[root@k8s-master01 kubeadm]# kubectl rollout resume deployment deploy-demo.yaml
升级结束后,继续查看rs的状态:
[root@k8s-master01 kubeadm]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-54f57cf6bf 3 3 3 12m
查看升级过程:
[root@k8s-master01 kubeadm]# kubectl rollout status deployment/nginx-deploy Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated... Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated... Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated... Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated... Waiting for deployment "nginx-deploy" rollout to finish: 2 old replicas are pending termination... Waiting for deployment "nginx-deploy" rollout to finish: 2 of 3 updated replicas are available... Waiting for deployment "nginx-deploy" rollout to finish: 2 of 3 updated replicas are available... deployment "nginx-deploy" successfully rolled out
根据AGE我们可以看到离我们最近的当前状态是:3,和我们的yaml文件是一致的,证明升级成功了。用
describe
命令可以查看升级的全部信息:
[root@k8s-master01 kubeadm]# kubectl describe deployment/nginx-deploy
Name: nginx-deploy
Namespace: default
CreationTimestamp: Thu, 07 Apr 2022 16:58:23 +0800
Labels: app=nginx-demo
Annotations: deployment.kubernetes.io/revision: 2
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx-demo"},"name":"nginx-deploy","namespace":"...
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 5
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deploy-6fcf476c4 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 16m deployment-controller Scaled up replica set nginx-deploy-54f57cf6bf to 3
Normal ScalingReplicaSet 2m23s deployment-controller Scaled up replica set nginx-deploy-6fcf476c4 to 1
Normal ScalingReplicaSet 2m23s deployment-controller Scaled down replica set nginx-deploy-54f57cf6bf to 2
Normal ScalingReplicaSet 2m23s deployment-controller Scaled up replica set nginx-deploy-6fcf476c4 to 2
Normal ScalingReplicaSet 2m16s deployment-controller Scaled down replica set nginx-deploy-54f57cf6bf to 0
Normal ScalingReplicaSet 2m16s deployment-controller Scaled up replica set nginx-deploy-6fcf476c4 to 3
5.3 回滚Deployment
[root@k8s-master01 kubeadm]# kubectl rollout history deployment nginx-deploy
deployment.apps/nginx-deploy
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 kubectl apply --filename=deploy-demo.yaml --record=true
4 kubectl apply --filename=deploy-demo.yaml --record=true
从上面的结果可以看出在执行Deployment
升级的时候最好带上record
参数,便于我们查看历史版本信息。
默认情况下,所有通过kubectl xxxx --record
都会被kubernetes
记录到etcd
进行持久化,这无疑会占用资源,最重要的是,时间久了,当你kubectl get rs
时,会有成百上千的垃圾RS
返回给你,那时你可能就眼花缭乱了。
上生产时,我们最好通过设置Deployment的.spec.revisionHistoryLimit
来限制最大保留的revision number
,比如15个版本,回滚的时候一般只会回滚到最近的几个版本就足够了。其实rollout history
中记录的revision
都和ReplicaSets
一一对应。如果手动delete
某个ReplicaSet,对应的rollout history
就会被删除,也就是还说你无法回滚到这个revison
了。
rollout history
和ReplicaSet
的对应关系,可以在kubectl describe rs $RSNAME
返回的revision
字段中得到,这里的revision
就对应着rollout history
返回的revison
。
同样我们可以使用下面的命令查看单个revison
的信息:
[root@k8s-master01 kubeadm]# kubectl rollout history deployment nginx-deploy --revision=3
deployment.apps/nginx-deploy with revision #3
Pod Template:
Labels: app=nginx
pod-template-hash=6757fb8478
Annotations: kubernetes.io/change-cause: kubectl apply --filename=deploy-demo.yaml --record=true
Containers:
nginx:
Image: nginx:1.10.0
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
假如现在要直接回退到当前版本的前一个版本:
# 回退到当前版本的前一个版本
[root@k8s-master01 kubeadm]# kubectl rollout undo deployment nginx-deploy
deployment.apps/nginx-deploy rolled back
当然也可以用revision
回退到指定的版本:
[root@k8s-master01 kubeadm]# kubectl rollout undo deployment nginx-deploy --to-revision=1
deployment.apps/nginx-deploy rolled back
现在可以用命令查看Deployment现在的状态了。
[root@k8s-master01 kubeadm]# kubectl get rs
NAME DESIRED CURRENT READY AGE
harry-nginx-6f9f8d4465 2 2 2 2d7h
my-nginx-576bb7cb54 1 1 1 2d20h
nginx-deploy-54f57cf6bf 3 3 3 29m
nginx-deploy-6757fb8478 0 0 0 8m17s
nginx-deploy-6fcf476c4 0 0 0 14m
nginx-deploy-bb4469db5 0 0 0 6m53s
testservice-754455d66 1 1 1 2d19h
# 删除指定rs
[root@k8s-master01 kubeadm]# kubectl delete rs nginx-deploy-6fcf476c4
replicaset.apps "nginx-deploy-6fcf476c4" deleted
# rs和history一一对应
[root@k8s-master01 kubeadm]# kubectl rollout history deployment nginx-deploy
deployment.apps/nginx-deploy
REVISION CHANGE-CAUSE
4 kubectl apply --filename=deploy-demo.yaml --record=true
5 kubectl apply --filename=deploy-demo.yaml --record=true
6 <none>
六、Pod 自动扩缩容
在前面的介绍中,我们提到过通过手工执行kubectl scale
命令和在Dashboard
上操作可以实现Pod
的扩缩容,但是这样毕竟需要每次去手工操作一次,而且指不定什么时候业务请求量就很大了,所以如果不能做到自动化的去扩缩容的话,这也是一个很麻烦的事情。如果Kubernetes
系统能够根据Pod
当前的负载的变化情况来自动的进行扩缩容就好了,因为这个过程本来就是不固定的,频繁发生的,所以纯手工的方式不是很现实。
幸运的是Kubernetes
为我们提供了这样一个资源对象:Horizontal Pod Autoscaling
(Pod水平自动伸缩),简称HPA
。HAP
通过监控分析RC
或者Deployment
控制的所有Pod
的负载变化情况来确定是否需要调整Pod
的副本数量,这是HPA
最基本的原理。
HPA
在kubernetes
集群中被设计成一个controller
,我们可以简单的通过kubectl autoscale
命令来创建一个HPA
资源对象,HPA Controller
默认30s轮询一次(可通过kube-controller-manager
的标志--horizontal-pod-autoscaler-sync-period
进行设置),查询指定的资源(RC或者Deployment)中Pod
的资源使用率,并且与创建时设定的值和指标做对比,从而实现自动伸缩的功能。
当你创建了HPA
后,HPA
会从Heapster
或者用户自定义的RESTClient
端获取每一个一个Pod
利用率或原始值的平均值,然后和HPA
中定义的指标进行对比,同时计算出需要伸缩的具体值并进行相应的操作。目前,HPA
可以从两个地方获取数据:
- Heapster:仅支持
CPU
使用率 - 自定义监控:我们到后面的监控的部分再给大家讲解这部分的使用方法
本小节来给大家介绍从Heapster
获取监控数据来进行自动扩缩容的方法,所以首先我们得安装Heapster
,前面我们在kubeadm
搭建集群的博文中,实际上我们已经默认把Heapster
相关的镜像都已经拉取到节点上了,所以接下来我们只需要部署即可,我们这里使用的是Heapster
1.4.2 版本的,前往Heapster
的github
页面:
https://github.com/kubernetes-retired/heapster/tree/v1.4.2/deploy/kube-config/influxdb
6.1 安装 influxdb
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: monitoring-influxdb
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
k8s-app: influxdb
template:
metadata:
labels:
task: monitoring
k8s-app: influxdb
spec:
containers:
- name: influxdb
image: mirrorgooglecontainers/heapster-influxdb-amd64:v1.3.3
volumeMounts:
- mountPath: /data
name: influxdb-storage
volumes:
- name: influxdb-storage
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
task: monitoring
kubernetes.io/cluster-service: 'true'
kubernetes.io/name: monitoring-influxdb
name: monitoring-influxdb
namespace: kube-system
spec:
ports:
- port: 8086
targetPort: 8086
selector:
k8s-app: influxdb
6.2 安装heapster
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: heapster
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: heapster-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: heapster
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: heapster
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
k8s-app: heapster
template:
metadata:
labels:
task: monitoring
k8s-app: heapster
spec:
serviceAccountName: heapster
containers:
- name: heapster
image: mirrorgooglecontainers/heapster-amd64:v1.5.2
imagePullPolicy: IfNotPresent
command:
- /heapster
- --sink=influxdb:http://monitoring-influxdb.kube-system.svc:8086
- --source=kubernetes:https://kubernetes.default?kubeletHttps=true&kubeletPort=10250&insecure=true
---
apiVersion: v1
kind: Service
metadata:
labels:
task: monitoring
kubernetes.io/cluster-service: 'true'
kubernetes.io/name: Heapster
name: heapster
namespace: kube-system
spec:
ports:
- port: 80
targetPort: 8082
selector:
k8s-app: heapster
[root@k8s-master01 heapster]# kubectl create -f heapster.yaml
serviceaccount/heapster created
clusterrolebinding.rbac.authorization.k8s.io/heapster-admin created
deployment.apps/heapster created
service/heapster created
6.3 安装grafana
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: monitoring-grafana
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
k8s-app: grafana
template:
metadata:
labels:
task: monitoring
k8s-app: grafana
spec:
containers:
- name: grafana
image: mirrorgooglecontainers/heapster-grafana-amd64:v4.4.3
ports:
- containerPort: 3000
protocol: TCP
volumeMounts:
- mountPath: /var
name: grafana-storage
env:
- name: INFLUXDB_HOST
value: monitoring-influxdb
- name: GF_SERVER_HTTP_PORT
value: "3000"
- name: GF_AUTH_BASIC_ENABLED
value: "false"
- name: GF_AUTH_ANONYMOUS_ENABLED
value: "true"
- name: GF_AUTH_ANONYMOUS_ORG_ROLE
value: Admin
- name: GF_SERVER_ROOT_URL
value: /
volumes:
- name: grafana-storage
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
kubernetes.io/cluster-service: 'true'
kubernetes.io/name: monitoring-grafana
name: monitoring-grafana
namespace: kube-system
spec:
ports:
- port: 80
targetPort: 3000
selector:
k8s-app: grafana
# https://github.com/kubernetes-retired/heapster/blob/v1.4.2/deploy/kube-config/influxdb/grafana.yaml
[root@k8s-master01 heapster]# kubectl apply -f grafana.yaml
deployment.apps/monitoring-grafana created
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
The Service "monitoring-grafana" is invalid:
* spec.ports[0].name: Required value
* spec.ports[1].name: Required value
# 查看是否安装成功
^C[root@k8s-master01 heapster]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
heapster-6c9b7d6f65-zg866 1/1 Running 0 3m39s
monitoring-grafana-6ccbd5776c-8nqtp 1/1 Running 0 2m43s
monitoring-influxdb-55f468d9fd-5wjv4 1/1 Running 0 4m39s
# 查看heapster日志确认是否有报错
[root@k8s-master01 heapster]# kubectl logs -f heapster-684b58b45c-rbtrn -n kube-system
I0408 04:11:30.508236 1 heapster.go:78] /heapster --sink=influxdb:http://monitoring-influxdb.kube-system.svc:8086 --source=kubernetes:https://kubernetes.default?kubeletHttps=true&kubeletPort=10250&insecure=true
I0408 04:11:30.508277 1 heapster.go:79] Heapster version v1.5.2
I0408 04:11:30.508482 1 configs.go:61] Using Kubernetes client with master "https://kubernetes.default" and version v1
I0408 04:11:30.508493 1 configs.go:62] Using kubelet port 10250
I0408 04:11:30.519315 1 influxdb.go:312] created influxdb sink with options: host:monitoring-influxdb.kube-system.svc:8086 user:root db:k8s
I0408 04:11:30.519336 1 heapster.go:202] Starting with InfluxDB Sink
I0408 04:11:30.519342 1 heapster.go:202] Starting with Metric Sink
I0408 04:11:30.529888 1 heapster.go:112] Starting heapster on port 8082
I0408 04:12:05.043415 1 influxdb.go:274] Created database "k8s" on influxDB server at "monitoring-influxdb.kube-system.svc:8086"
6.5 HPA
我们将该目录下面的yaml
文件保存到我们的集群上,然后使用kubectl
命令行工具创建即可,另外创建完成后,如果需要在Dashboard
当中看到监控图表,我们还需要在Dashboard
中配置上我们的heapster-host
。
同样的,我们来创建一个Deployment
管理的Nginx
Pod,然后利用HPA
来进行自动扩缩容。定义Deployment
的YAML
文件如下:(hap-demo.yaml)
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-demo
labels:
app: hpa
spec:
replicas: 1
revisionHistoryLimit: 15
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
resources:
requests:
cpu: 100m
ports:
- containerPort: 80
# 创建
[root@k8s-master01 heapster]# kubectl create -f hpa-demo.yaml
deployment.apps/hpa-demo created
[root@k8s-master01 heapster]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
hpa-demo 1/1 1 1 92s
[root@k8s-master01 heapster]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hpa-demo-7f9cc99d8b-rp9qv 1/1 Running 0 13s
查看静态目录:
systemctl status kubelet
[root@k8s-master01 ~]# cd /etc/kubernetes/manifests/
[root@k8s-master01 manifests]# ls
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml static-pod2.yaml
为不安装metric-server,我们做一下修改:
[root@k8s-master01 manifests]# vim kube-controller-manager.yaml
11 containers:
12 - command:
13 - kube-controller-manager
14 - --horizontal-pod-autoscaler-use-rest-clients=false # 在这里增加这一行
现在我们来创建一个HPA
,可以使用kubectl autoscale
命令来创建:
# 创建hpa
[root@k8s-master01 heapster]# kubectl autoscale deployment hpa-demo --min=1 --max=10 --cpu-percent=5
horizontalpodautoscaler.autoscaling/hpa-demo autoscaled
此命令创建了一个关联资源 hpa-nginx-deploy 的HPA
,最小的 pod 副本数为1,最大为10。HPA
会根据设定的 cpu使用率(10%)动态的增加或者减少pod数量。
当然出来使用kubectl autoscale
命令来创建外,我们依然可以通过创建YAML
文件的形式来创建HPA
资源对象。如果我们不知道怎么编写的话,可以查看上面命令行创建的HPA
的YAML
文件:
# 查询一下
[root@k8s-master01 heapster]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-demo Deployment/hpa-demo <unknown>/5% 1 10 1 23s
# 查看详细的
[root@k8s-master01 heapster]# kubectl describe hpa hpa-demo
Name: hpa-demo
Namespace: default
Labels: <none>
Annotations: <none>
CreationTimestamp: Fri, 08 Apr 2022 13:02:23 +0800
Reference: Deployment/hpa-demo
Metrics: ( current / target )
resource cpu on pods (as a percentage of request): <unknown> / 5%
Min replicas: 1
Max replicas: 10
Deployment pods: 1 current / 0 desired
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True SucceededGetScale the HPA controller was able to get the target's current scale
ScalingActive False FailedGetResourceMetric the HPA was unable to compute the replica count: missing request for cpu
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedGetResourceMetric 9s (x3 over 39s) horizontal-pod-autoscaler missing request for cpu
Warning FailedComputeMetricsReplicas 9s (x3 over 39s) horizontal-pod-autoscaler invalid metrics (1 invalid out of 1), first error is: failed to get cpu utilization: missing request for cpu
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-demo
labels:
app: hpa
spec:
replicas: 1
revisionHistoryLimit: 15
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
resources:
requests:
cpu: 100m
ports:
- containerPort: 80
# 压力测试一下
[root@k8s-master01 heapster]# kubectl run -i --tty test-hpa --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # while true; do wget -q -O- http://10.244.1.111; done;
同时我们查看相关资源hpa-nginx-deploy的副本数量,副本数量已经从原来的1变成了3。
NAME READY UP-TO-DATE AVAILABLE AGE
hpa-demo 3/3 1 1 35m
[root@k8s-master01 heapster]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-demo Deployment/hpa-demo 36%/5% 1 10 1 18m
同样的这个时候我们来关掉busybox
来减少负载,然后等待一段时间观察下HPA
和Deployment
对象
NAME READY UP-TO-DATE AVAILABLE AGE
hpa-demo 1/1 1 1 35m
[root@k8s-master01 heapster]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-demo Deployment/hpa-demo 0%/5% 1 10 1 18m
hpa-deployment整合一下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-demo
labels:
app: hpa
spec:
replicas: 1
revisionHistoryLimit: 15
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
resources:
requests:
cpu: 100m
ports:
- containerPort: 80
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: hpa-demo
namespace: default
spec:
maxReplicas: 10 #资源最大副本数
minReplicas: 1 #资源最小副本数
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment #需要伸缩的资源类型
name: hpa-demo #需要伸缩的资源名称
targetCPUUtilizationPercentage: 90 #触发伸缩的cpu使用率
当前的HPA
只有CPU
使用率这一个指标,还不是很灵活的,在后面的学习中我们来根据我们自定义的监控来自动对Pod
进行扩缩容。
七、Job 和 Cronjob 的使用
上面我们学习了Pod
自动伸缩的方法,我们使用到了HPA
这个资源对象,我们在后面的学习中还会和大家接触到HPA
的。今天我们来给大家介绍另外一类资源对象:Job,我们在日常的工作中经常都会遇到一些需要进行批量数据处理和分析的需求,当然也会有按时间来进行调度的工作,在我们的Kubernetes
集群中为我们提供了Job
和CronJob
两种资源对象来应对我们的这种需求。
Job
负责处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod
成功结束。而CronJob
则就是在Job
上加上了时间调度。官网文档: https://kubernetes.io/zh/docs/concepts/workloads/controllers/job/
7.1 Job
我们用Job
这个资源对象来创建一个任务,我们定一个Job
来执行一个倒计时的任务,定义YAML
文件:
---
apiVersion: batch/v1beta1
kind: Job
metadata:
name: job-demo
spec:
template:
metadata:
name: job-demo
spec:
containers:
- name: counter
image: busybox
command:
- "bin/sh"
- "-c"
- "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
restartPolicy: Never
backoffLimit: 4
# 创建job
[root@k8s-master01 kubeadm]# kubectl create -f job-demo.yaml
job.batch/job-demo created
# 查看job
[root@k8s-master01 kubeadm]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
job-demo 1/1 4s 9s
# 查看详细描述
[root@k8s-master01 kubeadm]# kubectl describe job job-demo
Name: job-demo
Namespace: default
Selector: controller-uid=5eb4b70e-59e1-4b47-8c73-12e53adb8825
Labels: controller-uid=5eb4b70e-59e1-4b47-8c73-12e53adb8825
job-name=job-demo
Annotations: <none>
Parallelism: 1
Completions: 1
Start Time: Fri, 08 Apr 2022 15:57:39 +0800
Completed At: Fri, 08 Apr 2022 15:57:43 +0800
Duration: 4s
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=5eb4b70e-59e1-4b47-8c73-12e53adb8825
job-name=job-demo
Containers:
counter:
Image: busybox
Port: <none>
Host Port: <none>
Command:
bin/sh
-c
for i in 9 8 7 6 5 4 3 2 1; do echo $i; done
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 2m47s job-controller Created pod: job-demo-ntp5d
注意查看我们的Pod
的状态,同样我们可以通过kubectl logs
来查看当前任务的执行结果。
7.2 CronJob
CronJob
其实就是在Job
的基础上加上了时间调度,我们可以:在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。这个实际上和我们Linux
中的crontab
就非常类似了。
一个CronJob
对象其实就对应中crontab
文件中的一行,它根据配置的时间格式周期性地运行一个Job
,格式和crontab
也是一样的。
crontab
的格式如下:
分 时 日 月 星期 要运行的命令 第1列分钟0~59 第2列小时0~23) 第3列日1~31 第4列月1~12 第5列星期0~7(0和7表示星期天) 第6列要运行的命令
现在,我们用CronJob
来管理我们上面的Job
任务,
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob-demo
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
name: job-demo
spec:
containers:
- name: counter
image: busybox
command:
- "bin/sh"
- "-c"
- "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
restartPolicy: OnFailure
# 创建
[root@k8s-master01 kubeadm]# vim cronjob-demo.yaml
[root@k8s-master01 kubeadm]# kubectl create -f cronjob-demo.yaml
cronjob.batch/cronjob1 created
[root@k8s-master01 kubeadm]# kubectl get cronjobs
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob1 */1 * * * * False 0 <none> 12s
hello * * * * * False 0 60s 100s
[root@k8s-master01 kubeadm]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
cronjob1-1649469540 1/1 13s 24s
hello-1649469480 1/1 11s 84s
hello-1649469540 1/1 1s 24s
job-demo 1/1 4s 18h
# 查看进度
[root@k8s-master01 kubeadm]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cronjob1-1649469540-jtwl7 0/1 Completed 0 88s
cronjob1-1649469600-6wwhc 0/1 Completed 0 28s
注意:如何出现 error: unable to recognize “cronjob.yaml”: no matches for kind “CronJob” in version “batch/v1beta1” 错误, 解决方法如下
vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --runtime-config=batch/v1=true
或者
- --runtime-config=batch/v1beta1=true
systemctl restart kubelet
kubectl api-versions | grep batch/v2
当然,也可以用kubectl run
来创建一个CronJob
:
一旦不再需要 Cron Job,简单地可以使用 kubectl 命令删除它:
[root@k8s-master01 kubeadm]# kubectl delete cronjob cronjob1
cronjob.batch "cronjob1" deleted
这将会终止正在创建的 Job。然而,运行中的 Job 将不会被终止,不会删除 Job 或 它们的 Pod。为了清理那些 Job 和 Pod,需要列出该 Cron Job 创建的全部 Job,然后删除它们:
[root@k8s-master01 kubeadm]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
hello-1649470200 1/1 1s 2m46s
hello-1649470260 1/1 1s 106s
hello-1649470320 1/1 1s 46s
job-demo 1/1 4s 18h
[root@k8s-master01 kubeadm]# kubectl delete jobs hello-1649470200 hello-1649470260 ..
一旦 Job 被删除,由 Job 创建的 Pod 也会被删除。注意,所有由名称为 “hello” 的 Cron Job 创建的 Job 会以前缀字符串 “hello-” 进行命名。如果想要删除当前 Namespace 中的所有 Job,可以通过命令 kubectl delete jobs --all 立刻删除它们。
好啦🌶, 关于pod就先介绍到这里, 喜欢就点个❤吧~ ☕️