Kubernetes目前大约每年发布三次,由于迭代速度快,目前市场上绝大部分的教程相对陈旧。更重要的是,从Kubernetes 1.20开始,Kubernetes官方宣布逐步弃用Docker作为容器运行时,并计划在Kubernetes 1.24版本中完全移除对Docker作为容器运行时的支持。这意味着,从Kubernetes 1.24版本开始,将不能使用Docker作为容器运行时来运行Kubernetes节点上的Pods。因此,市场上关于直接使用containerd容器运行时的新版Kubernetes教程几乎没有,更重要的是因为的Kubernetes涉及到镜像需要单独配置才能获取,这无疑拉高了初学者门槛。本教程采用互联网的形式进行发布,便于保持与Kubernetes最新版的同步,尽量自包含,便于读者学习、实践。
关键字:Kubernetes 1.32; containerd; nerdctl; debain 12
整体规划
为方便后续内容学习,本部分将基于【Kubernetes部署与运维07 Helm部署】中的环境开展Kubernetes的Pod配置、状态与静态Pod学习。整体规划如下:
| 虚拟机名称 | IP地址 | 主机名 | 域名 | CPU核心 | 内存 | 角色 |
|---|---|---|---|---|---|---|
k8s_Master1_2G | 192.168.152.200 | master1 | master.rz | 2 | 2GB | master |
K8s_Worker1_2G | 192.168.152.201 | worker1 | worker1.rz | 1 | 2GB | worker |
K8s_Worker2_2G | 192.168.152.202 | worker2 | worker2.rz | 1 | 2GB | worker |
参见【Kubernetes部署与运维02 Nerdctl Rootful部署】,Kubernetes基础环境各组件与版本信息如下: |
- nerdctl: v1.7.7
- containerd: v1.7.22
- runc: v1.1.14
- CNI plugins: v1.5.1
- BuildKit: v0.15.2
- Stargz Snapshotter: v0.15.1
- imgcrypt: v1.1.11
- RootlessKit: v2.3.1
- slirp4netns: v1.3.1
- bypass4netns: v0.4.1
- fuse-overlayfs: v1.13
- containerd-fuse-overlayfs: v1.0.8
- Kubo (IPFS): v0.29.0
- Tini: v0.19.0
- buildg: v0.4.1
Kubernetes版本号为1.32。
理论知识
Pod的资源请求与限制
【官方参考文档】
https://kubernetes.io/zh/docs/tasks/configure-pod-container/assign-memory-resource/
为了防止某个应用随运行时间的推移,造成内存等资源逐渐越占越多,以至于影响整个服务器或其他应用的资源使用(利如内存不足、CPU过载等),就需要对Pod所占用的资源在使用时加以一定的限制。资源可以进一步区分为可压缩资源与不可压缩资源,例如内存就属于不可压缩资源,而CPU则属于可压缩资源。
- 不可压缩资源,可通过
spec.containers.resources字段在Pod的配置文件中进行设定。 - 与不可压缩资源类似,可压缩资源可通过
spec.containers.resources字段在Pod的配置文件中进行设定。对于不可压缩资源而言,在Pod运行过程中如果容器的资源占用超过了相应的资源限制,则容器将被删除,并尝试重新启动一个全新的容器(这会反映到kubectl get pods命令输出中RESTARTS列的值的增加上)。而对于可压缩资源而言,当Pod运行过程中容器的资源占用超过了相应的资源限制之后,容器不会重启,依然运行,因为对应的资源是可被“压缩”的。
kube apply、edit与path
【官方参考文档】
https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#applyhttps://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#edithttps://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#patch
kubectl apply的作用是通过配置文件(例如yaml)或标准输入将配置信息应用于一个资源。该资源名必须被指定。若该资源不存在,则进行创建;若资源已存在,则尝试更新。
kubectl edit命令的作用是通过默认编辑器vim编辑资源。该命令可以编辑所有通过命令行工具获取的API资源。kubectl edit命令格式:
kubectl edit (RESOURCE/NAME | -f FILENAME)
kubectl patch命令的作用是使用合并补丁(例如JSON补丁)更新某资源的对应字段(field)。kubectl patch命令格式:
kubectl patch (-f FILENAME | TYPE NAME) [-p PATCH|--patch-file FILE]
Init容器
【官方参考文档】
https://kubernetes.io/zh/docs/concepts/workloads/pods/init-containers/
Init容器(Init Container)是一种特殊容器,其在Pod内的应用容器启动之前运行。Init容器可以包括一些应用镜像中不存在的实用工具和安装脚本。
每个Pod中可以包含多个容器,应用运行在这些容器里面,同时Pod也可以有一个或多个先于应用容器启动的Init容器。Init容器与普通的容器的区别:
- 每个
Init容器总是运行到完成; - 每个
Init容器都必须在下一个Init容器启动之前成功完成。
如果Pod的Init容器失败,kubelet会不断地重启该Init容器直到该容器成功为止。然而,如果Pod对应的restartPolicy值为Never,Kubernetes将不会重新启动Pod。
因为Init容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:
Init容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。例如,没有必要仅为了在安装过程中使用类似sed、awk、python或dig这样的工具而基于某个镜像来生成一个新的镜像;Init容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低;- 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像;
Init容器能以不同于Pod内应用容器的文件系统视图运行。因此,Init容器可以访问应用容器不能访问的Secret(Secret对象类型主要用于保存敏感信息)的权限;- 由于
Init容器必须在应用容器启动之前运行完成,因此Init容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动。
静态Pod
【官方参考文档】
https://kubernetes.io/zh/docs/concepts/workloads/pods/init-containers/
静态Pods(Static Pod)由特定节点上的kubelet守护进程直接管理,而不需要API Server进行管理。kubelet监视每个静态Pod(如果它崩溃,则重新启动它),静态Pods总是绑定到特定节点上的一个Kubelet。
kubelet守护进程会自动尝试在Kubernetes API Server上为每个静态Pod创建一个镜像Pod(可以理解为Linux系统中的软链接)。这意味着运行在节点上的Pod在API Server上是可见的,但不能从那里进行控制。使用kubeadm部署Kubernetes集群的静态Pod,其配置文件存储路径默认为:/etc/kubernetes/manifests/。kubelet守护进程会定期的扫描这个文件夹下的yaml/json文件来创建/删除静态Pod。
修改静态Pod配置文件默认存在路径的方式为:修改/var/lib/kubelet/config.yaml文件,更改其staticPodPath: /etc/kubernetes/manifests行。将/etc/kubernetes/manifests修改为所设置的存储路径即可。
Pod的阶段状态
【官方参考文档】
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/
在使用kubectl get pods命令的输出中,存在一列STATUS,该列的值表示Pod所处的阶段状态。Pod的阶段(Phase)是Pod在其生命周期中所处位置的简单宏观概述。该阶段并不是对容器或Pod状态的综合汇总,也不是为了成为完整的状态机。Pod阶段的数量和含义是严格定义的。Pod的阶段Phase状态可能值如下:
Pending(悬决):Pod已被Kubernetes系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待Pod被调度的时间和通过网络下载镜像的时间;Running(运行中):Pod已经绑定到了某个节点,Pod中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态;Succeeded(成功):Pod中的所有容器都已成功终止,并且不会再重启;Failed(失败):Pod中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止;Unknown(未知):因为某些原因无法取得Pod的状态。这种情况通常是因为与Pod所在主机通信失败。
如果某节点死掉或者与集群中其他节点失联,Kubernetes会实施一种策略,将失去的节点上运行的所有Pod的阶段状态设置为Failed。
案例实践
前期准备
同时开启K8s_Master1_2G、K8s_Worker1_2G、K8s_Worker2_2G三台虚拟机。
不可压缩资源请求与限制
【实践01-对Pod的不可压缩资源内存进行请求并限制】
创建Pod,并对其内存进行请求与限制,以保证该Pod在运行过程中不超过上限。
1)在主节点master1上的workloads-pods文件夹内创建memory-demo1.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: memory-demo1
spec:
containers:
- name: c-memory-demo
image: polinux/stress
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
要为容器指定资源限制,需要在Pod配置文件内resources字段中指定limits字段;而要指定资源请求,则要在resources字段中指定requests字段。该配置文件中对容器c-memory-demo的内存请求为100Mi,而其相应的内存限制上限为200Mi(运行过程中不允许超过上限)。
该Pod中的容器使用了polinux/stress镜像,其含有stress工具,以供进行压力测试。配置文件中的args字段部分提供了容器启动时的参数,"--vm-bytes", "150M"参数用于告知容器尝试分配150MiB内存。
2)使用kubectl create -f memory-demo1.yaml命令创建相应的Pod(名为memory-demo1):
root@master1:~/tutor/workloads-pods# ll
total 16
-rw-r--r-- 1 root root 307 Jan 31 11:18 memory-demo1.yaml
-rw-r--r-- 1 root root 108 Jan 31 09:48 pod1.yaml
-rw-r--r-- 1 root root 190 Jan 31 09:58 pod2.yaml
-rw-r--r-- 1 root root 145 Jan 31 10:12 pod3.yaml
root@master1:~/tutor/workloads-pods# kubectl create -f memory-demo1.yaml
pod/memory-demo1 created
root@master1:~/tutor/workloads-pods# kubectl get pods
NAME READY STATUS RESTARTS AGE
memory-demo1 1/1 Running 0 35s
root@master1:~/tutor/workloads-pods# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
memory-demo1 44m 150Mi
使用kubectl top pods命令查看memory-demo1资源的占用情况,memory-demo1占用了150Mi大小的内存,符合memory-demo1.yaml配置文件中约定的资源请求与限制的要求。
3)拷贝memory-demo1.yaml文件为memory-demo2.yaml:
root@master1:~/tutor/workloads-pods# cp memory-demo1.yaml memory-demo2.yaml
修改memory-demo2.yaml文件中Pod名为memory-demo2,并将参数args中的"150M"修改为"250M"。修改之后memory-demo2.yaml文件内容如下:
apiVersion: v1
kind: Pod
metadata:
name: memory-demo2
spec:
containers:
- name: c-memory-demo
image: polinux/stress
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
4)使用kubectl create -f memory-demo2.yaml命令创建相应的Pod(名为memory-demo2):
root@master1:~/tutor/workloads-pods# ll
total 20
-rw-r--r-- 1 root root 307 Jan 31 11:18 memory-demo1.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:29 memory-demo2.yaml
-rw-r--r-- 1 root root 108 Jan 31 09:48 pod1.yaml
-rw-r--r-- 1 root root 190 Jan 31 09:58 pod2.yaml
-rw-r--r-- 1 root root 145 Jan 31 10:12 pod3.yaml
root@master1:~/tutor/workloads-pods# kubectl create -f memory-demo2.yaml
pod/memory-demo2 created
5)使用watch -n1 "kubectl get pods; kubectl top pods"命令,观察输出:
root@master1:~/tutor/workloads-pods# watch -n1 "kubectl get pods; kubectl top pods"
【常识与技巧】
Linux命令watch命令可以将命令的输出结果输出到标准输出设备,多用于周期性执行命令/定时执行命令,以观察输出变动。参数-n1表示每隔1秒运行一次双引号(")中列出的命令,若未给出-n参数,则默认每隔2秒运行一次。双引号(")中列出的命令可以包含多个,各命令之间使用;分隔。watch命令使用Ctrl-C键组合退出。
watch命令的输出如下:
Every 1.0s: kubectl get pods; kubectl top pods master1: Fri Jan 31 11:31:52 2025
NAME READY STATUS RESTARTS AGE
memory-demo1 1/1 Running 0 8m51s
memory-demo2 0/1 CrashLoopBackOff 3 (28s ago) 80s
NAME CPU(cores) MEMORY(bytes)
memory-demo1 25m 150Mi
足够长的时间之后,watch命令的输出如下,可以观察到RESTARTS列的值不断增加,代表重启次数的增加,这是因为在Pod的配置文件中,限制了内存资源最高上限为200Mi,而在命令行中启动时指定了分配250Mi的内存。超出限制,容器启动失败,但是默认的重启策略为Always,所以再次启动,再次失败,不断循环。
Every 1.0s: kubectl get pods; kubectl top pods master1: Fri Jan 31 11:36:34 2025
NAME READY STATUS RESTARTS AGE
memory-demo1 1/1 Running 0 13m
memory-demo2 0/1 OOMKilled 6 (2m55s ago) 6m2s
NAME CPU(cores) MEMORY(bytes)
memory-demo1 26m 150Mi
kubectl get pods输出的RESTARTS列,memory-demo2的值为6,不断重启。
6)清除Pod,恢复工作目录:
root@master1:~/tutor/workloads-pods# kubectl delete pod --all
pod "memory-demo1" deleted
pod "memory-demo2" deleted
root@master1:~/tutor/workloads-pods# kubectl get pods
No resources found in default namespace.
root@master1:~/tutor/workloads-pods# cd
root@master1:~#
可以使用
--all参数,一次性删除指定命名空间下的所有Pod。kubectl delete pod --all未指定任何命名空间,因此删除默认命名空间default下所有Pod。
可压缩资源请求与限制
【实践02-对Pod的可压缩资源CPU进行请求并限制】
创建Pod,并对其CPU进行请求与限制,以保证该Pod在运行过程中不超过上限。
1)在主节点master1上的workloads-pods文件夹内创建cpu-demo.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo
spec:
containers:
- name: c-cpu-demo
image: vish/stress
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: "1"
requests:
cpu: "0.5"
args:
- -cpus
- "2"
Kubernetes中,1000m CPU = 1 CPU,单位m为milli-CPU(milli-,表示毫)。文件中请求为0.5 CPU,限制为1 CPU。参数args中CPU直接占用了2 CPU,超过限制!
2)使用kubectl create -f cpu-demo.yaml命令创建cpu-demo:
root@master1:~/tutor/workloads-pods# kubectl create -f cpu-demo.yaml
pod/cpu-demo created
3)使用watch -n1 "kubectl get pods; kubectl top pods"命令观察输出变动:
root@master1:~/tutor/workloads-pods# watch -n1 "kubectl get pods; kubectl top pods"
输出内容如下:
Every 1.0s: kubectl get pods; kubectl top pods master1: Fri Jan 31 11:50:13 2025
NAME READY STATUS RESTARTS AGE
cpu-demo 1/1 Running 0 59s
NAME CPU(cores) MEMORY(bytes)
cpu-demo 1000m 0Mi
cpu-demo处于Running状态,RESTARTS列值为0,未重启。同时,CPU回降(压缩)至1000m,符合可压缩资源限制的上限要求1 CPU。
4)清除Pod,恢复工作目录:
root@master1:~/tutor/workloads-pods# kubectl delete pod --all
pod "cpu-demo" deleted
root@master1:~/tutor/workloads-pods# kubectl get pods
No resources found in default namespace.
root@master1:~/tutor/workloads-pods# cd
root@master1:~#
登录Pod内的容器
【实践03-登录Pod内的容器】
Pod内含有一个或多个容器。类似docker exec命令可以登录容器一样,Kubernetes也提供了容器登录的命令kubectl exec,用于登录到Pod中的容器。kubectl exec命令格式:
kubectl exec (POD | TYPE/NAME) [-c CONTAINER] [flags] -- COMMAND [args...]
【官方参考文档】
https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#exec
1)在主节点master1上的workloads-pods文件夹内创建container-demo.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: container-demo
spec:
containers:
- name: c-nginx
image: nginx
imagePullPolicy: IfNotPresent
- name: c-tomcat
image: tomcat
imagePullPolicy: IfNotPresent
container-demo.yaml配置文件内设有两个容器c-nginx与c-tomcat。
2)创建对应的Pod:
root@master1:~/tutor/workloads-pods# ll
total 28
-rw-r--r-- 1 root root 218 Jan 31 11:55 container-demo.yaml
-rw-r--r-- 1 root root 229 Jan 31 11:45 cpu-demo.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:18 memory-demo1.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:29 memory-demo2.yaml
-rw-r--r-- 1 root root 108 Jan 31 09:48 pod1.yaml
-rw-r--r-- 1 root root 190 Jan 31 09:58 pod2.yaml
-rw-r--r-- 1 root root 145 Jan 31 10:12 pod3.yaml
root@master1:~/tutor/workloads-pods# kubectl create -f container-demo.yaml
pod/container-demo created
root@master1:~/tutor/workloads-pods# kubectl get pods
NAME READY STATUS RESTARTS AGE
container-demo 2/2 Running 0 4s
3)登录到Pod中,并随后退出:
root@master1:~/tutor/workloads-pods# kubectl exec container-demo -it -- bash
Defaulted container "c-nginx" out of: c-nginx, c-tomcat
root@container-demo:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@container-demo:/# exit
exit
root@master1:~/tutor/workloads-pods#
其中:
kubectl exec container-demo -it -- bash命令中的COMMAND(此处为bash)之前注意要有一个空格;container-demo中含有两个容器c-nginx与c-tomcat,在本次登录过程中未给出具体的容器名,仅使用Pod名container-demo,因此登录到默认的第一个容器中c-nginx中。若需要指定登录到具体的容器中,可以增加参数-c。登录之后,可以观察到在容器内部命令提示符发生变化,变为了root@container-demo(Pod名)。cat /etc/os-release命令输出可以发现c-nginx容器是基于Debian GNU/Linux 12 (bookworm)的。
4)使用kubectl exec命令,增加参数-c以登录到c-tomcat容器:
root@master1:~/tutor/workloads-pods# kubectl exec container-demo -c c-tomcat -it -- bash
root@container-demo:/usr/local/tomcat# cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.1 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
root@container-demo:/usr/local/tomcat# exit
exit
root@master1:~/tutor/workloads-pods#
- 登录之后,提示符依然为
root@container-demo(Pod名),但是其当前目录已经切为Tomcat部署的目录/usr/local/tomcat。 cat /etc/os-release命令输出可以发现c-tomcat容器是基于Ubuntu 24.05.1 LTS的。
5)清除Pod,恢复工作目录:
root@master1:~/tutor/workloads-pods# kubectl delete -f container-demo.yaml
pod "container-demo" deleted
root@master1:~/tutor/workloads-pods# kubectl get pods
No resources found in default namespace.
root@master1:~/tutor/workloads-pods# cd
root@master1:~#
使用kubectl apply应用配置文件
【实践04-使用kubectl apply根据配置文件对资源对象进行修改或创建】
使用kubectl apply根据配置文件对资源对象进行修改或创建,并观察与kubectl create命令的差异。
1)在主节点master1上的workloads-pods文件夹内创建apply-demo1.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: apply-demo
labels:
app: nginx
spec:
containers:
- name: c-nginx
image: nginx:1.26-alpine
imagePullPolicy: IfNotPresent
在配置文件中的metadata部分,增加labels字段,内容为app: nginx。
2)创建apply-demo Pod,并进行标签查看:
root@master1:~/tutor/workloads-pods# ll
total 32
-rw-r--r-- 1 root root 168 Jan 31 12:09 apply-demo1.yaml
-rw-r--r-- 1 root root 218 Jan 31 11:55 container-demo.yaml
-rw-r--r-- 1 root root 229 Jan 31 11:45 cpu-demo.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:18 memory-demo1.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:29 memory-demo2.yaml
-rw-r--r-- 1 root root 108 Jan 31 09:48 pod1.yaml
-rw-r--r-- 1 root root 190 Jan 31 09:58 pod2.yaml
-rw-r--r-- 1 root root 145 Jan 31 10:12 pod3.yaml
root@master1:~/tutor/workloads-pods# kubectl create -f apply-demo1.yaml
pod/apply-demo created
root@master1:~/tutor/workloads-pods# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
apply-demo 1/1 Running 0 9s app=nginx
apply-demo的标签现为app=nginx。
3)拷贝apply-demo1.yaml文件为apply-demo2.yaml:
root@master1:~/tutor/workloads-pods# cp apply-demo1.yaml apply-demo2.yaml
修改apply-demo2.yaml文件,将app: nginx修改为app: nginx-v2。修改后,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: apply-demo
labels:
app: nginx-v2
spec:
containers:
- name: c-nginx
image: nginx:1.26-alpine
imagePullPolicy: IfNotPresent
4)使用kubectl apply命令应用该配置文件:
root@master1:~/tutor/workloads-pods# ll
total 36
-rw-r--r-- 1 root root 168 Jan 31 12:09 apply-demo1.yaml
-rw-r--r-- 1 root root 171 Jan 31 12:15 apply-demo2.yaml
-rw-r--r-- 1 root root 218 Jan 31 11:55 container-demo.yaml
-rw-r--r-- 1 root root 229 Jan 31 11:45 cpu-demo.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:18 memory-demo1.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:29 memory-demo2.yaml
-rw-r--r-- 1 root root 108 Jan 31 09:48 pod1.yaml
-rw-r--r-- 1 root root 190 Jan 31 09:58 pod2.yaml
-rw-r--r-- 1 root root 145 Jan 31 10:12 pod3.yaml
root@master1:~/tutor/workloads-pods# kubectl create -f apply-demo2.yaml
Error from server (AlreadyExists): error when creating "apply-demo2.yaml": pods "apply-demo" already exists
root@master1:~/tutor/workloads-pods# kubectl apply -f apply-demo2.yaml
Warning: resource pods/apply-demo is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
pod/apply-demo configured
root@master1:~/tutor/workloads-pods# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
apply-demo 1/1 Running 0 49m app=nginx-v2
先使用kubectl create -f apply-demo2.yaml命令尝试创建已经存在的apply-demo Pod,但是系统提示错误,因为对应的资源(apply-demo Pod)已经存在。针对已经存在的资源,仅修改配置的情况,可以通过kubectl apply实现。使用kubectl apply命令应用配置之后,标签变更为app=nginx-v2。
5)删除apply-demo Pod。然后在没有apply-demo Pod的情况下,再次应用配置文件apply-demo2.yaml,则apply-demo Pod被创建:
root@master1:~/tutor/workloads-pods# kubectl delete -f apply-demo2.yaml
pod "apply-demo" deleted
root@master1:~/tutor/workloads-pods# kubectl get pods
No resources found in default namespace.
root@master1:~/tutor/workloads-pods# kubectl apply -f apply-demo2.yaml
pod/apply-demo created
root@master1:~/tutor/workloads-pods# kubectl get pods
NAME READY STATUS RESTARTS AGE
apply-demo 1/1 Running 0 5s
若对应的资源不存在,则kubectl apply反馈的结果便不再是pod/apply-demo configured(配置生效)信息,而是创建pod/apply-demo1 created(已创建)。
【常识与技巧】
由于kubectl apply命令在资源不存在时创建资源,而资源存在时则修改资源,因此,在日常运维过程中,更多的情况下将会使用kubectl apply命令而不是kubectl create命令。
使用kubectl edit编辑资源对象
【实践05-使用kubectl edit查看、编辑资源对象】
使用kubectl edit命令在线查看资源对象,并编辑资源对象。
1)使用kubectl apply命令,将apply-demo Pod的app=nginx-v2标签修改回app=nginx(在apply-demo1.yaml文件中设置):
root@master1:~/tutor/workloads-pods# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
apply-demo 1/1 Running 0 6s app=nginx-v2
root@master1:~/tutor/workloads-pods# kubectl apply -f apply-demo1.yaml
pod/apply-demo configured
root@master1:~/tutor/workloads-pods# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
apply-demo 1/1 Running 0 50s app=nginx
2)使用kube edit命令编辑apply-demo Pod的标签,将app=nginx修改为app=nginx-v3:
root@master1:~/tutor/workloads-pods# kubectl edit pod apply-demo
在资源编辑环境中,可以查看到注解部分metadata.annotations字段的内容:
metadata:
annotations:
cni.projectcalico.org/containerID: 39f48693772c4755f86fa414ed9915b9b043f62a32563c211277fe2993b6476b
cni.projectcalico.org/podIP: 10.244.189.77/32
cni.projectcalico.org/podIPs: 10.244.189.77/32
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"apply-demo","namespace":"default"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"IfNotPresent","name":"c-nginx"}]}}
说明之前使用kubectl apply命令时更新标签("app":"nginx")已经被记录到了注解kubectl.kubernetes.io/last-applied-configuration当中(“联动”更新)。
3)在资源编辑环境中修改标签metadata.labels字段的内容如下:
labels:
app: nginx-v3
如同在
vim编辑器中一样,输入:wq保存退出。
4)查看修改后标签情况:
root@master1:~/tutor/workloads-pods# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
apply-demo 1/1 Running 0 10m app=nginx-v3
apply-demo的标签已更新。但是再次使用kubectl edit pod apply-demo命令编辑apply-demo Pod,查看其注解部分metadata.annotations字段的内容,并没有如同之前使用kubectl apply命令一样,随着应用配置而发生“联动”更新,而仅仅是更新了标签部分,对于注解部分并不“联动”更新。而这是kubectl edit命令与kubectl apply命令不同之处,kubectl apply命令可以“联动”更新。
使用kubectl path更新资源对象
【实践06-使用kubectl path更新资源对象】
使用kubectl path命令更新资源对象。
1)可以使用kubectl patch -h命令查看帮助,其给出了部分示例:
root@master1:~/tutor/workloads-pods# kubectl patch -h
Update fields of a resource using strategic merge patch, a JSON merge patch, or
a JSON patch.
JSON and YAML formats are accepted.
Note: Strategic merge patch is not supported for custom resources.
Examples:
# Partially update a node using a strategic merge patch, specifying the patch
as JSON
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'
...
2)在使用kubect patch命令之前,可以先将待修改的Pod以json格式输出,方便后续命令行的内容编写:
root@master1:~/tutor/workloads-pods# kubectl get pod apply-demo -o json
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"annotations": {
"cni.projectcalico.org/containerID": "39f48693772c4755f86fa414ed9915b9b043f62a32563c211277fe2993b6476b",
"cni.projectcalico.org/podIP": "10.244.189.77/32",
"cni.projectcalico.org/podIPs": "10.244.189.77/32",
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"nginx\"},\"name\":\"apply-demo\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"c-nginx\"}]}}\n"
},
"creationTimestamp": "2025-01-31T05:20:36Z",
"labels": {
"app": "nginx-v3"
},
...
}
若要修改其"app": "nginx-v3"部分,将标签值修改为nginx-v4,根据json结构,该命令应为:
kubectl patch pod apply-demo -p '{"metadata": {"labels": {"app": "nginx-v4"}}}'
其中metadata、labels、app均为json格式中的各层级。
3)使用kubectl patch pod apply-demo -p '{"metadata": {"labels": {"app": "nginx-v4"}}}'命令,修改标签值为nginx-v4:
root@master1:~/tutor/workloads-pods# kubectl patch pod apply-demo -p '{"metadata": {"labels": {"app": "nginx-v4"}}}'
pod/apply-demo patched
root@master1:~/tutor/workloads-pods# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
apply-demo 1/1 Running 0 24m app=nginx-v4
apply-demo的标签已经修改为nginx-v4。但是,kubectl patch命令与kubectl edit命令一样,不会“联动”修改注解。
4)清除Pod,恢复工作目录:
root@master1:~/tutor/workloads-pods# kubectl delete pods --all
pod "apply-demo" deleted
root@master1:~/tutor/workloads-pods# kubectl get pods
No resources found in default namespace.
root@master1:~/tutor/workloads-pods# cd
root@master1:~#
Init容器创建及初始化过程
【实践07-创建含有Init容器的Pod,并观察其运行过程】
创建含有Init容器的Pod,并观察其运行过程。
1)在主节点master1上的workloads-pods文件夹内创建init-demo.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-init-ctr-demo
labels:
app: myapp
spec:
containers:
- name: c-myapp
image: busybox:stable
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo The c-myapp running! && sleep 3600']
initContainers:
- name: c-init01
image: busybox:stable
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo The c-init01 is running! && sleep 20']
- name: c-init02
image: busybox:stable
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo The c-init02 is running! && sleep 20']
其中:
busybox镜像将若干常见的Unix工具集成到一起,为替代常见GNU文件工具提供了一种选择。具体可参见:https://hub.docker.com/_/busybox;- 在配置文件中,存在两个
initContainer(Init容器),一个是c-init01,一个是c-init02。启动过程为先运行c-init01,待c-init01结束之后,运行c-init02,然后待c-init02结束之后,再运行containers中的c-myapp。
2)使用kubectl apply命令应用配置,创建并运行相应的资源:
root@master1:~/tutor/workloads-pods# ll
total 40
-rw-r--r-- 1 root root 168 Jan 31 12:09 apply-demo1.yaml
-rw-r--r-- 1 root root 171 Jan 31 12:15 apply-demo2.yaml
-rw-r--r-- 1 root root 218 Jan 31 11:55 container-demo.yaml
-rw-r--r-- 1 root root 229 Jan 31 11:45 cpu-demo.yaml
-rw-r--r-- 1 root root 463 Jan 31 13:56 init-demo.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:18 memory-demo1.yaml
-rw-r--r-- 1 root root 307 Jan 31 11:29 memory-demo2.yaml
-rw-r--r-- 1 root root 108 Jan 31 09:48 pod1.yaml
-rw-r--r-- 1 root root 190 Jan 31 09:58 pod2.yaml
-rw-r--r-- 1 root root 145 Jan 31 10:12 pod3.yaml
root@master1:~/tutor/workloads-pods# kubectl apply -f init-demo.yaml
pod/pod-init-ctr-demo created
root@master1:~/tutor/workloads-pods# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-init-ctr-demo 0/1 Init:0/2 0 3s
root@master1:~/tutor/workloads-pods# kubectl logs pod-init-ctr-demo c-init01; kubectl logs pod-init-ctr-demo c-init02; kubectl logs pod-init-ctr-demo
The c-init01 is running!
Error from server (BadRequest): container "c-init02" in pod "pod-init-ctr-demo" is waiting to start: PodInitializing
Defaulted container "c-myapp" out of: c-myapp, c-init01 (init), c-init02 (init)
Error from server (BadRequest): container "c-myapp" in pod "pod-init-ctr-demo" is waiting to start: PodInitializing
root@master1:~/tutor/workloads-pods# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-init-ctr-demo 0/1 Init:1/2 0 27s
root@master1:~/tutor/workloads-pods# kubectl logs pod-init-ctr-demo c-init01; kubectl logs pod-init-ctr-demo c-init02; kubectl logs pod-init-ctr-demo
The c-init01 is running!
The c-init02 is running!
Defaulted container "c-myapp" out of: c-myapp, c-init01 (init), c-init02 (init)
Error from server (BadRequest): container "c-myapp" in pod "pod-init-ctr-demo" is waiting to start: PodInitializing
NAME READY STATUS RESTARTS AGE
pod-init-ctr-demo 0/1 PodInitializing 0 47s
root@master1:~/tutor/workloads-pods# kubectl logs pod-init-ctr-demo c-init01; kubectl logs pod-init-ctr-demo c-init02; kubectl logs pod-init-ctr-demo
The c-init01 is running!
The c-init02 is running!
Defaulted container "c-myapp" out of: c-myapp, c-init01 (init), c-init02 (init)
The c-myapp running!
root@master1:~/tutor/workloads-pods# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE ...
pod-init-ctr-demo 1/1 Running 0 3m52s 10.244.189.79 worker2 ...
由于排版限制,略去部分列内容。pod-init-ctr-demo Pod的状态经历了由Init:0/2到Running的转变,该Pod运行于工作节点worker2之上。在pod-init-ctr-demo Pod的状态转变过程中,不断执行kubectl logs pod-init-ctr-demo init01; kubectl logs pod-init-ctr-demo init02; kubectl logs pod-init-ctr-demo命令(其含有三条子命令,分别查看Pod内的三个容器c-init01、c-init02和c-myapp的日志输出)。由于每个Init容器都必须在下一个Init容器启动之前成功完成,因此,可以看到Pod状态转变过程中日志也是逐渐输出的。
3)在工作节点worker2之上,使用nerdctl相关的命令查看涉及到的Init容器及应用容器:
root@worker2:~# nerdctl -n=k8s.io ps -a
CONTAINER ID IMAGE STATUS NAMES
...
1c7645ca813c docker.io/library/busybox:stable Up k8s://default/pod-init-ctr-demo/c-myapp
4bddc8c60677 docker.io/library/busybox:stable Created k8s://default/pod-init-ctr-demo/c-init01
9e7e3922b932 docker.io/library/busybox:stable Created k8s://default/pod-init-ctr-demo/c-init02
da66a505879a .../google_containers/pause:3.10 Up k8s://default/pod-init-ctr-demo
由于排版限制,略去部分列内容。含有c-myapp、c-init01、c-init02字符串的容器名在实际运行的节点之上由Kubernetes自动在前面附加上了k8s://<命名空间>/<Pod名>/,共同造成了实际运行容器名字符串。此外,还有一个特殊的容器,名字为Pod名的容器(pod-init-ctr-demo),镜像pause:3.10,该容器为Pasue容器。每启动一个Pod,同时也会启动一个伴随容器(Pause容器),Pause容器伴随着Pod的启动而启动,伴随着Pod的删除而删除,其作用为通过Pause容器实现Pod内的容器共享网络空间。
4)在主节点master1上清除Pod,恢复工作目录:
root@master1:~/tutor/workloads-pods# kubectl delete pod --all
pod "pod-init-ctr-demo" deleted
root@master1:~/tutor/workloads-pods# kubectl get pods
No resources found in default namespace.
root@master1:~/tutor/workloads-pods# cd
root@master1:~#
静态Pod创建与删除
【实践08-创建、查看并删除静态Pod】
创建静态Pod,并在主节点master1上查看静态Pod,最后删除静态Pod。
1)在工作节点worker2的/etc/kubernetes/manifests/目录(若不存在则创建)下创建static-pod-demo.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
containers:
- name: c-web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
static-pod-demo.yaml文件在工作节点worker2上创建好之后,可以使用ls命令查看文件的存在:
root@worker2:~# ll /etc/kubernetes/manifests/
total 4
-rw-r--r-- 1 root root 196 Jan 31 14:54 static-pod-demo.yaml
2)直接在主节点master1上无需进行任何创建操作,直接查看Pod的扩展信息:
root@master1:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE ...
static-web-worker2 1/1 Running 0 28s 10.244.189.81 worker2 ...
root@master1:~# kubectl exec static-web-worker2 -it -- bash
root@static-web-worker2:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@static-web-worker2:/# exit
exit
root@master1:~# kubectl delete pod static-web-worker2
pod "static-web-worker2" deleted
root@master1:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE ...
static-web-worker2 1/1 Running 0 7s 10.244.189.81 worker2 ...
root@master1:~# scp root@worker2:/etc/kubernetes/manifests/static-pod-demo.yaml /root/tutor/workloads-pods
The authenticity of host 'worker2 (192.168.152.202)' can't be established.
ED25519 key fingerprint is SHA256:6V5mlcHGCo2FSQ1JBdQwDnrz6dr+thCR+tMkIv1DugQ.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'worker2' (ED25519) to the list of known hosts.
root@worker2's password:
static-pod-demo.yaml 100% 196 68.6KB/s 00:00
由于排版限制,略去部分列内容。其中:
- 可以观察到工作节点
worker2上存在一个名为static-web-worker02的Pod。只要将Pod所对应的yaml或json配置文件放到节点的特定目的之下,则该节点上的kubelet守护进程将直接使用该配置文件创建相应的Pod,并可以在主节点上查看到相应的Pod状态,甚至可以通过kubectl exec命令进行登录与访问; - 在主节点上观察的到的静态
Pod名是在Pod相对应的配置文件中的Pod名基础之上附加了-<节点名>构成的。 - 尝试使用
kubectl delete pod static-web-worker2命令删除静态Pod,并不报错。仅从输出内容pod "static-web-worker2" deleted上判断,删除成功。但是接下来再次使用kubectl get pods -o wide命令查看Pod列表信息时,发现静态Pod依然存在,不受影响。 - 最后,在主节点
master1上对静态Pod配置文件static-pod-demo.yaml进行了备份。
3)删除静态Pod也相对简单,仅需要在之前部署的节点的特定目录中删除静态Pod相对应的yaml或json文件,则相应的Pod便自动删除了。在工作节点worker02上将之前创建static-pod-demo.yaml文件删除:
root@worker2:~# rm /etc/kubernetes/manifests/static-pod-demo.yaml
root@worker2:~# ls /etc/kubernetes/manifests/
root@worker2:~#
4)在主节点master1上直接查看Pod简略信息:
root@master1:~# kubectl get pods
No resources found in default namespace.
5)在主节点master1上查看集群核心组件状态及扩展信息:
root@master1:~# kubectl -n kube-system get pods -o wide
NAME READY STATUS NODE
calico-kube-controllers-5745477d4d-x4dbj 1/1 Running master1
calico-node-d9sv5 1/1 Running master1
calico-node-ntx28 1/1 Running worker2
calico-node-zpbc2 1/1 Running worker1
coredns-6766b7b6bb-hqgsd 1/1 Running master1
coredns-6766b7b6bb-w8ztk 1/1 Running master1
etcd-master1 1/1 Running master1
kube-apiserver-master1 1/1 Running master1
kube-controller-manager-master1 1/1 Running master1
kube-proxy-b8n2k 1/1 Running worker2
kube-proxy-h2v5l 1/1 Running master1
kube-proxy-jf5tm 1/1 Running worker1
kube-scheduler-master1 1/1 Running master1
metrics-server-554dcb6944-kplgb 1/1 Running worker1
其中etcd-master1、kube-apiserver-master1、kube-controller-manager-master1和kube-scheduler-master1四个集群核心组件均以-master1结尾,因此可以初步判断它们均为静态Pod类型。查看主节点master1的/etc/Kubernetes/manifests目录:
root@master1:~# ll /etc/kubernetes/manifests/
total 16
-rw------- 1 root root 2579 Jan 29 17:16 etcd.yaml
-rw------- 1 root root 3921 Jan 29 17:16 kube-apiserver.yaml
-rw------- 1 root root 3419 Jan 29 17:16 kube-controller-manager.yaml
-rw------- 1 root root 1680 Jan 29 17:16 kube-scheduler.yaml
恰好是四个核心组件的yaml配置文件。也即,当主节点master1的kubelet守护进程启动之后,后者就将/etc/Kubernetes/manifests/目录下的yaml配置文件以静态Pod形式运行起来。
收尾工作
1)使用poweroff命令,关闭三台虚拟机。
2)AI时代背景之下,运维将从传统CPU服务器切入到GPU服务器与端边设备,对于运维开发人员,技术玩家而言,也同步需要跟上新的技术栈,欢迎关注“GPU那些事儿”微信公众号与微信视频号,了解相关资讯与技术。
3)本学习内容涉及到的软件包、配置文件等资源,可以直接从百度网盘下载获取:
- 百度网盘分享文件:
Kubernetes1.32 - 链接:
https://pan.baidu.com/s/18XeGQ28BDPjHh8JKj0uZFQ?pwd=6x17提取码:6x17