Kubernetes的一些运维巧思
一、临时容器
Ephemeral Containers
在 Kubernetes(简称 K8s)环境中,随着容器镜像趋向精简(如 Distroless 或 Scratch 镜像),传统 kubectl exec 方式往往因缺少 shell 或调试工具而失效。临时容器(Ephemeral Containers) 自 Kubernetes v1.25 起正式稳定(stable),成为生产环境故障排查的强大工具。它允许在现有 Pod 中动态注入一个临时容器,用于交互式诊断,而无需修改原镜像或重启 Pod。
临时容器本质上是附加到现有 Pod 的特殊容器,共享部分命名空间(如进程、网络),但不具备资源保证、端口配置或自动重启能力,因此仅适用于临时调试,而非构建应用程序。

1.1 kubectl debug 命令详解
kubectl debug 是操作临时容器的主要命令。它封装了底层 EphemeralContainers API,提供多种模式。
命令基本格式:
kubectl debug [POD_NAME] [FLAGS]
| 参数格式 | 参数说明 | 使用场景 / 补充说明 |
|---|---|---|
-it 或 --interactive + --tty |
启用交互模式并分配伪终端(TTY) | 需实时输入命令进行交互式调试(如进入 shell);结合使用等效于创建临时容器后立即 attach 到控制台 |
--image=IMAGE |
指定临时容器的镜像名称及版本 | 可选镜像:・busybox:1.36.1(轻量级工具集)・ubuntu:24.10(完整工具链)・nicolaka/netshoot(专业网络诊断工具)默认使用 busybox |
--target=CONTAINER_NAME |
让临时容器共享目标容器的进程命名空间(PID namespace) | 交互模式下必须使用;用于查看目标容器进程树(ps -ef)、发送信号等;适用于多容器 Pod 的针对性进程诊断 |
--share-processes |
启用进程命名空间共享(在某些模式下等同于 --target) | 查看原容器中的进程信息;常与 --copy-to 副本模式结合使用 |
--copy-to=NEW_POD_NAME |
创建一个 Pod 副本进行调试,原 Pod 保持不变 | 生产环境首选;避免直接注入运行中的 Pod;适合调试 CrashLoopBackOff、InitContainer 失败、命令错误等场景 |
--set-image=*=NEW_IMAGE |
在 Pod 副本中将所有容器(*)的镜像替换为指定镜像 | 原镜像为 Distroless 或无 shell 时,使用带完整工具链的镜像(如 ubuntu)继续运行原命令并调试 |
--container=CONTAINER_NAME |
指定要操作或注入的容器名称(仅在特定模式下有效) | 多容器 Pod 中精确指定目标容器,避免歧义 |
--profile=generallegacy |
指定调试配置文件 | Kubernetes 1.32+ 推荐显式使用 general 配置文件 |
--attach=false |
创建临时容器后不立即 attach,后台运行 | 适用于批量脚本自动化创建调试容器;或需要后续手动 kubectl attach 的场景 |
node/NODE_NAME |
以节点名为目标进行调试,创建运行在指定节点上的特权调试 Pod | 节点级故障诊断;如检查容器运行时(CRI)、Kubelet 日志、节点文件系统、磁盘压力等;主机根文件系统挂载至 /host |
总结
- 核心调试场景分为容器级(查看进程、交互式调试)和节点级(节点故障诊断),对应不同参数组合;
- 生产环境优先使用
--copy-to创建 Pod 副本调试,避免影响原运行中的 Pod; - 交互调试需结合
-it和--target,镜像选择可根据调试需求(轻量 / 完整工具链 / 网络诊断)灵活切换。
1.1.1 直接在运行 Pod 中注入临时容器

场景:生产 Pod 使用精简镜像,无 shell,无法 exec。
# 创建一个伪生产环境的测试pod
kubectl run c1 --image=myweb:v1 --restart=Never
# 调试c1的pod
kubectl debug -it c1 --image=busybox:1.36.1 --target=c1
- 在临时容器中执行 ps -ef、netstat -nltup、wget localhost,可直接诊断进程、网络和服务可用性。
- 退出后,临时容器自动删除,Pod 状态恢复原样。
1.1.2 通过 Pod 副本调试(避免影响原 Pod)

kubectl debug po-repl -it --image=ubuntu:24.10 --share-processes --copy-to=repl-debug --profile=general
- 创建新 Pod repl-debug,共享进程命名空间。
- 原 Pod 保持不变,新 Pod 可独立删除。
使用场景:生产环境高可用 Pod,避免直接注入风险。
1.1.3 调试命令错误导致的 CrashLoopBackOff

kubectl debug chage-cmd -it --copy-to=cmd-debug --container=chage-cmd -- sh
- 原 Pod 因命令不存在反复崩溃。
- 副本 Pod 中手动执行 sh,可修复命令或检查环境变量。
使用场景:InitContainer 或入口命令错误时。
1.1.4 替换镜像调试

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu:24.10
- 副本 Pod 使用新镜像运行原命令,但因新镜像有完整工具链,便于进一步 exec。
使用场景:原镜像为 Distroless,无法注入工具时。
1.1.5 节点级调试

kubectl debug node/k8s-node1 -it --image=ubuntu:24.10
- 创建节点调试 Pod,主机根文件系统挂载至 /host。
- 支持查看节点进程、日志、CRI 状态(containerd/docker)。
使用场景:Pod 调度失败、节点 NotReady 或容器运行时问题。
注意:默认非特权,若需 chroot /host 或更高权限,需手动创建特权 Pod。
1.2.建议与总结
在进行线上调试时,优先采用非侵入式方式(例如 --copy-to 在副本上验证、替换镜像以添加调试工具),并在操作前确保有回滚或恢复方案。临时容器为故障排查提供了便捷手段,但必须注意其对安全与审计的影响。
临时容器是 Kubernetes 故障排查的利器,尤其适用于精简镜像和生产环境。其灵活的副本模式、命名空间共享以及节点调试能力,大幅提升诊断效率。建议在 RBAC 中合理授权,并在日常运维中优先考虑此工具。
二、端口转发
2.1 kubectl port-forward命令详解
kubectl port-forward <pod_name> <forward_port> --namespace <namespace> --address <IP默认:127.0.0.1>
- 各参数说明:
<pod_name>:需要做端口转发的目标 Pod 的名称<forward_port>:端口转发规则,格式为本地端口:Pod内端口(比如8080:80表示将本地 8080 端口转发到 Pod 的 80 端口)--namespace <namespace>:指定目标 Pod 所在的命名空间(若 Pod 在默认命名空间,可省略该参数)--address <IP>:指定本地监听的 IP 地址,默认是127.0.0.1(仅本地可访问);若要允许其他机器访问,可指定服务器的公网 / 内网 IP(比如--address 0.0.0.0允许所有 IP 访问)
- 命令作用:将本地端口与 Kubernetes 集群中 Pod 的端口建立转发关系,方便本地直接访问 Pod 内的服务(比如调试 Pod 里的应用)
2.1.1 仅本机可访问
创建测试Pod
[root@k8s-master ~]# kubectl run c1 --image=myweb:v1
pod/c1 created
[root@k8s-master ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
c1 1/1 Running 0 3s
# 使用端口转发
[root@k8s-master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
c1 1/1 Running 0 110s 10.200.36.85 k8s-node1 <none> <none>
[root@k8s-master ~]# curl 10.200.36.85
vamos | This version is v1 | v111111
[root@k8s-master ~]# kubectl port-forward c1 30666:80
Forwarding from 127.0.0.1:30666 -> 80
Forwarding from [::1]:30666 -> 80
[root@k8s-master ~]# curl 127.0.0.1:30666
vamos | This version is v1 | v111111
# 此时端口转发还会有访问记录
[root@k8s-master ~]# kubectl port-forward c1 30666:80
Forwarding from 127.0.0.1:30666 -> 80
Forwarding from [::1]:30666 -> 80
Handling connection for 30666
创建测试SVC
[root@k8s-master ~]# kubectl expose pod c1 --port=80 --target-port=80 --name=c1-service
service/c1-service exposed
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
c1-service ClusterIP 10.107.203.67 <none> 80/TCP 4s
[root@k8s-master ~]# kubectl port-forward services/c1-service 21905:80
Forwarding from 127.0.0.1:21905 -> 80
Forwarding from [::1]:21905 -> 80
#测试访问
[root@k8s-master ~]# curl 127.0.0.1:21905
vamos | This version is v1 | v111111
2.1.2 所有主机都可访问
[root@k8s-master ~]# kubectl port-forward c1 :80 --address 0.0.0.0
Forwarding from 0.0.0.0:36065 -> 80
- :80 表示自动生成一个未被使用的端口做转发
浏览器访问

2.2 常见场景与总结
本地调试 Pod 内服务
开发 / 测试阶段,直接通过本地端口访问集群内 Pod 的服务(比如 Pod 里的 API、数据库),不用额外配置 Ingress/Service 暴露。
临时访问无暴露规则的服务
生产环境中,部分服务(如内部工具)未配置 Ingress/LoadBalancer,通过port-forward临时访问排查问题。
绕过网络策略限制
当集群网络策略限制了服务的外部访问,但需要临时连通时,用端口转发直接建立本地到 Pod 的通道。
本地工具对接集群服务
比如本地 MySQL 客户端连接集群内的 MySQL Pod,本地 Postman 调用 Pod 内的 API 服务。
Kubernetes 集群的网络是封闭的(Pod/Service 默认仅集群内可访问),而实际场景中需要 “本地→集群内部” 的临时连通能力:
- 避免为了临时调试而频繁配置 Ingress/Service(成本高、易留安全隐患);
- 解决 “开发 / 运维需要直接触达 Pod,但不想开放集群网络” 的矛盾;
- 提供轻量、临时的连通方式,兼顾便利性和安全性(仅本地发起的连接有效)。
三、安全参数
3.1 节点的名称空间共享
3.1.1 共享主机网络hostNetwork
[root@k8s-master ~/blog]# cat hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostnetwork-pod
spec:
nodeName: k8s-master
hostNetwork: true
containers:
- name: myapp-container
image: myweb:v1
ports:
- containerPort: 80
[root@k8s-master ~/blog]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
c1 1/1 Running 0 17m 10.200.36.85 k8s-node1 <none> <none>
hostnetwork-pod 1/1 Running 0 14s 10.0.0.6 k8s-master <none> <none>
[root@k8s-master ~/blog]# curl 10.0.0.6
vamos | This version is v1 | v111111
3.1.2 共享主机端口hostPort
[root@k8s-master ~/blog]# cat hostport.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-hostport
spec:
containers:
- name: myapp-container
image: myweb:v2
ports:
- containerPort: 80
hostPort: 8080
[root@k8s-master ~/blog]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
c1 1/1 Running 0 19m 10.200.36.85 k8s-node1 <none> <none>
hostnetwork-pod 1/1 Running 0 2m11s 10.0.0.6 k8s-master <none> <none>
pod-with-hostport 1/1 Running 0 3s 10.200.36.86 k8s-node1 <none> <none>
[root@k8s-master ~/blog]# curl 10.200.36.86
vamos | This version is v2 | v222222
[root@k8s-node1 ~]# curl 10.0.0.7:8080
vamos | This version is v2 | v222222
3.1.3 共享PID和IPC
[root@k8s-master ~/blog]# cat hostipcpid.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostpid-hostipc-pod
spec:
hostPID: true
hostIPC: true
containers:
- name: myapp-container
image: myweb:v1
[root@k8s-master ~/blog]# kubectl apply -f hostipcpid.yaml
pod/hostpid-hostipc-pod created
[root@k8s-master ~/blog]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
c1 1/1 Running 0 23m 10.200.36.85 k8s-node1 <none> <none>
hostnetwork-pod 1/1 Running 0 5m39s 10.0.0.6 k8s-master <none> <none>
hostpid-hostipc-pod 1/1 Running 0 4s 10.200.36.87 k8s-node1 <none> <none>
pod-with-hostport 1/1 Running 0 3m31s 10.200.36.86 k8s-node1 <none> <none>
[root@k8s-master ~/blog]# kubectl exec -it hostpid-hostipc-pod -- sh
/ # ps -ef
PID USER TIME COMMAND
1 root 0:05 {systemd} /sbin/init
2 root 0:00 [kthreadd]
3 root 0:00 [pool_workqueue_]
4 root 0:00 [kworker/R-rcu_g]
5 root 0:00 [kworker/R-sync_]
6 root 0:00 [kworker/R-kvfre]
7 root 0:00 [kworker/R-slub_]
...
3428 root 0:00 /usr/bin/csi-driver --nodeid=k8s-node1 --loglevel=warn
3461 root 0:00 /usr/bin/node-driver-registrar --v=5 --csi-address=/csi/csi.sock -- kubelet-registration-path=/var/lib/kubelet/plugins/csi.tigera.io/csi.sock
3620 root 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
3623 root 0:00 sshd-session: root [priv]
3629 root 0:00 /usr/lib/systemd/systemd --user
3.2 节点的安全上下文
- 指定容器中运行进程的用户(用户 ID)
- 阻止容器使用root用户运行(容器的默认运行用户通常在其镜像中指定,所以可能需要阻止容器以root 用户运行)
- 使用特权模式运行容器,使其对宿主节点的内核具有完全的访问权限
- 与以上相反,通过添加或禁用内核功能,配置细粒度的内核访问权限
- 设置 SELinux 选项, 加强对容器的限制
- 阻止进程写入容器的根文件系统
3.2.1 使用特定的用户运行Pod
[root@k8s-master ~/blog]# cat spUser.yaml
apiVersion: v1
kind: Pod
metadata:
name: user-id-pod
spec:
containers:
- name: alpine-container
image: alpine:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sleep", "9999"]
securityContext:
runAsUser: 405
[root@k8s-master ~/blog]# kubectl get po
NAME READY STATUS RESTARTS AGE
user-id-pod 1/1 Running 0 8s
[root@k8s-master ~/blog]# kubectl exec -it user-id-pod -- sh
/ $ id
uid=405(guest) gid=100(users) groups=100(users)
/ $ whoami
guest
/ $ exit
3.2.2 阻止容器以root身份运行
[root@k8s-master ~/blog]# cat non-root-user-pod-true.yaml
apiVersion: v1
kind: Pod
metadata:
name: non-root-user-pod-true
spec:
containers:
- name: myapp-container
image: wangyanglinux/myapp:v1.0-nonroot
securityContext:
runAsNonRoot: true
runAsUser: 102
在某些情况下,即使容器镜像中使用的是普通用户,也可能需要明确指定runAsUser。这可能是由于容器镜像中的用户与宿主机上的用户UID不匹配导致的。在这种情况下,即使容器中的用户是普通用户,Kubernetes 也可能会认为其是root 用户,因为它的 UiD 与宿主机上的 root 用户的 UiD 匹配。
因此,即使容器中的用户是普通用户,也建议明确指定runAsUser,以确保容器以正确的用户身份运行。
[root@k8s-master ~/blog]# kubectl apply -f non-root-user-pod-true.yaml
pod/non-root-user-pod-true created
[root@k8s-master ~/blog]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
non-root-user-pod-true 1/1 Running 0 3s 10.200.36.89 k8s-node1 <none> <none>
user-id-pod 1/1 Running 0 11m 10.200.36.88 k8s-node1 <none> <none>
[root@k8s-master ~/blog]# kubectl exec -it non-root-user-pod-true -- sh
$ whoami
nonroot
$ id
uid=102(nonroot) gid=102(nonroot) groups=102(nonroot)
$ exit
3.2.3 使用特权模式运行Pod
先看一下不是特权模式下的Pod
[root@k8s-master ~/blog]# kubectl run c1 --image=myweb:v1
pod/c1 created
[root@k8s-master ~/blog]# kubectl exec -it c1 -- sh
/ # ls /dev/
core full null pts shm stdin termination-log urandom
fd mqueue ptmx random stderr stdout tty zero
/ # exit
开启特权的参数配置
[root@k8s-master ~/blog]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
c1 1/1 Running 0 91s 10.200.36.90 k8s-node1 <none> <none>
non-root-user-pod-true 1/1 Running 0 3m10s 10.200.36.89 k8s-node1 <none> <none>
privileged-pod 1/1 Running 0 4s 10.200.36.91 k8s-node1 <none> <none>
user-id-pod 1/1 Running 0 14m 10.200.36.88 k8s-node1 <none> <none>
[root@k8s-master ~/blog]# kubectl exec -it privileged-pod -- sh
/ # ls /dev/
autofs loop-control random tty12 tty32 tty52 ttyS14 ttyS6 vcsa4
bsg loop0 rfkill tty13 tty33 tty53 ttyS15 ttyS7 vcsa5
btrfs-control loop1 rtc0 tty14 tty34 tty54 ttyS16 ttyS8 vcsa6
bus loop2 sda tty15 tty35 tty55 ttyS17 ttyS9 vcsu
core loop3 sda1 tty16 tty36 tty56 ttyS18 ttyprintk vcsu1
cpu loop4 sda2 tty17 tty37 tty57 ttyS19 udmabuf vcsu2
cpu_dma_latency loop5 sda3 tty18 tty38 tty58 ttyS2 uhid vcsu3
cuse loop6 sg0 tty19 tty39 tty59 ttyS20 uinput vcsu4
dm-0 loop7 sg1 tty2 tty4 tty6 ttyS21 urandom vcsu5
dma_heap mapper shm tty20 tty40 tty60 ttyS22 userfaultfd vcsu6
dmmidi mcelog snapshot tty21 tty41 tty61 ttyS23 userio vfio
dri mem snd tty22 tty42 tty62 ttyS24 vcs vga_arbiter
ecryptfs midi sr0 tty23 tty43 tty63 ttyS25 vcs1 vhci
fb0 mqueue stderr tty24 tty44 tty7 ttyS26 vcs2 vhost-net
fd net stdin tty25 tty45 tty8 ttyS27 vcs3 vhost-vsock
full null stdout tty26 tty46 tty9 ttyS28 vcs4 vmci
fuse nvram termination-log tty27 tty47 ttyS0 ttyS29 vcs5 vsock
hidraw0 port tty tty28 tty48 ttyS1 ttyS3 vcs6 zero
hpet ppp tty0 tty29 tty49 ttyS10 ttyS30 vcsa zfs
hwrng psaux tty1 tty3 tty5 ttyS11 ttyS31 vcsa1
input ptmx tty10 tty30 tty50 ttyS12 ttyS4 vcsa2
kmsg pts tty11 tty31 tty51 ttyS13 ttyS5 vcsa3
3.2.4 fsGroup 与 supplementalGroups
[root@k8s-master ~/blog]# cat groups.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-shared-volume-fsgroup
spec:
securityContext:
fsGroup: 555
supplementalGroups: [666, 777]
containers:
- name: first
image: wangyanglinux/tools:alpine
command: ["/bin/sleep", "9999"]
securityContext:
runAsUser: 1111
volumeMounts:
- name: shared-volume
mountPath: /volume
readOnly: false
- name: second
image: wangyanglinux/tools:alpine
command: ["/bin/sleep", "9999"]
securityContext:
runAsUser: 2222
volumeMounts:
- name: shared-volume
mountPath: /volume
readOnly: false
volumes:
- name: shared-volume
emptyDir: {}
[root@k8s-master ~/blog]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
c1 1/1 Running 0 38m 10.200.36.90 k8s-node1 <none> <none>
non-root-user-pod-true 1/1 Running 0 40m 10.200.36.89 k8s-node1 <none> <none>
pod-with-shared-volume-fsgroup 2/2 Running 0 4s 10.200.36.92 k8s-node1 <none> <none>
privileged-pod 1/1 Running 0 37m 10.200.36.91 k8s-node1 <none> <none>
user-id-pod 1/1 Running 0 51m 10.200.36.88 k8s-node1 <none> <none>
[root@k8s-master ~/blog]# kubectl exec -it pod-with-shared-volume-fsgroup -- sh
Defaulted container "first" out of: first, second
/ $ ls -l /volume/
total 0
/ $ ls -ld /volume/
drwxrwsrwx 2 root 555 4096 Dec 18 08:40 /volume/
/ $ exit
[root@k8s-master ~/blog]# kubectl exec -it pod-with-shared-volume-fsgroup -c first -- sh
/ $ ls -ld /volume/
drwxrwsrwx 2 root 555 4096 Dec 18 08:40 /volume/
/ $ echo 111>/volume/first.txt
/ $ ls -l /volume/
total 0
-rw-r--r-- 1 1111 555 0 Dec 18 08:43 first.txt
/ $ exit
[root@k8s-master ~/blog]# kubectl exec -it pod-with-shared-volume-fsgroup -c second -- sh
/ $ ls -ld /volume/
drwxrwsrwx 2 root 555 4096 Dec 18 08:43 /volume/
/ $ echo 222 >/volume/second.txt
/ $ ls -l /volume/
total 4
-rw-r--r-- 1 1111 555 0 Dec 18 08:43 first.txt
-rw-r--r-- 1 2222 555 4 Dec 18 08:44 second.txt
/ $ cat /volume/second.txt
222
这个 Pod 的核心作用是实现多容器间的共享存储权限管理,通过
securityContext配置统一管理共享卷的文件权限:
- 共享存储基础:通过
emptyDir类型的shared-volume,让first和second两个容器挂载同一个目录,实现数据共享。- 文件权限统一控制:
fsGroup: 555:指定共享卷的文件所属组为 555,Kubernetes 会自动将卷中文件的组权限设置为该组,确保两个容器都能访问卷内文件;supplementalGroups: [666, 777]:为 Pod 内的进程添加额外的附属组,扩展容器对其他组权限文件的访问能力。- 容器独立用户:两个容器分别以
runAsUser: 1111和runAsUser: 2222的非 root 用户运行,同时通过fsGroup确保它们对共享卷有统一的访问权限。
该配置既实现了多容器数据共享,又通过组权限控制保证了不同用户身份的容器能安全访问共享存储,是多容器 Pod 共享卷的标准权限管理方案。
3.3 PodSecurityAdmission
从 Kubernetes v1.21开始, Pod Security Policy 将被弃用,并将在 v1.25 中删除, Kubernetes 在
1.22 版本引l入了 Pod Security Admission 作为其替代者。
PodSecurityAdmission机制在易用性和灵活性上有了很大提升,从使用角度有以下四点显著不同:
- 可以在集群中默认开启,只要不设置约束条件就不会触发对pod的校验
- 只在命名空间级别生效,可以为不同命名空间通过添加标签的方式设置不同的安全限制
- 根据实践预设了三种安全等级,不需要由用户单独去设置每一项安全条件
3.3.1 podSecurity Standards
为了广泛的覆盖安全应用场景, Pod Security Standards 渐进式的定义了三种不同的 Pod 安全标准策略:
| 策略 | 描述 |
|---|---|
| Privileged | 不受限制的策略,提供最大可能范围的权限许可。此策略允许已知的特权提升 |
| Baseline | 限制性最弱的策略,禁止已知的策略提升。允许使用默认的(规定最少)Pod 配置 |
| Restricted | 限制性非常强的策略,遵循当前的保护 Pod 的最佳实践 |
| 策略等级 | 权限开放程度 | 安全限制强度 | 核心特点 | 适用场景 |
|---|---|---|---|---|
| Privileged | 完全开放 | 无限制 | 允许所有特权能力,等同于节点 root 权限 | 集群核心系统组件(如网络 / 存储插件) |
| Baseline | 部分开放 | 基础限制 | 禁止已知高危特权行为,允许 Pod 默认配置 | 大部分通用业务 Pod(Web 服务、中间件) |
| Restricted | 最小开放 | 严格限制 | 遵循安全最佳实践,强制非 root 运行、禁用额外权限、启用加固规则 | 高安全需求业务(金融、敏感数据处理) |
3.3.2 podSecurity admission
在 Kubernetes 集群中开启了 podSecurity admission 后,就可以通过给 namespace 设置 label 的方式来实施 Pod Security Standards。其中有三种设定模式可选用:
| 模式 | 描述 |
|---|---|
| enforce | 违反安全标准策略的 Pod 将被拒绝 |
| audit | 违反安全标准策略触发向审计日志中记录的事件添加审计注释,但其他行为被允许 |
| warn | 违反安全标准策略将触发面向用户的警告,但其他行为被允许 |
| 模式 | 权限开放程度 | 核心行为 | 适用场景 |
|---|---|---|---|
| enforce | 严格限制 | 违反安全策略的 Pod 直接被拒绝创建 / 运行 | 生产环境中需强制安全规范的命名空间 |
| audit | 无实际限制 | 违反策略时仅记录审计日志,不影响 Pod 运行 | 安全策略灰度落地前的审计评估 |
| warn | 无实际限制 | 违反策略时仅向用户弹出警告,不影响 Pod 运行 | 开发 / 测试环境的安全提醒场景 |
四、资源限制
4.1 资源限制概念
以下是提取的文字内容: Kubernetes 对资源的限制实际上是通过 CGROUP 来控制的,CGROUP 是容器的一组用来控制内核如果运进行程的相关属性集合。针对内存、CPU、和各种设备都有对应的 CGROUP
默认情况下,Pod 运行没有 CPU 和内存的限额。这意味着系统中任何 Pod 将能够执行该节点所有的运算资源,消耗足够多的 CPU 和内存。一般会针对某些应用的 Pod 资源进行资源限制,这个资源限制是通过 resources 的 requests 和 limits 来实现。
4.1.1 资源限制案例清单
apiVersion: v1
kind: Pod
metadata:
name: resource-limited-pod
spec:
containers:
- name: my-container
image: nginx
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "200m"
memory: "128Mi"
resources字段下requests和limits的含义
| 关键字 | 类型 | 含义 | 示例(以配置为例) | 作用场景 |
|---|---|---|---|---|
requests |
资源请求 | 容器启动时向 Kubernetes 申请的最小资源量,Kubernetes 会确保节点有足够资源才调度 Pod | - CPU:200m(0.2 核)- 内存:128Mi(128MB) |
用于 Kubernetes 的 Pod 调度(确保节点资源充足) |
limits |
资源限制 | 容器运行时能使用的最大资源量,超过后会被限制(CPU 被限流,内存超用会被终止) | - CPU:500m(0.5 核)- 内存:256Mi(256MB) |
防止单个 Pod 消耗过多节点资源,避免影响其他 Pod |
补充说明
- CPU 单位
m:1000m = 1核,示例中200m表示 0.2 核,500m表示 0.5 核; - 内存单位
Mi:1Mi = 1024Ki = 1024×1024字节,是 Kubernetes 中常用的内存单位; - 超限制的后果:
- CPU 超
limits:Pod 会被限流(CPU 使用率不会超过limits值),但不会被终止; - 内存超
limits:Pod 会因 “内存不足(OOM)” 被 Kubernetes 强制终止。
- CPU 超
4.1.2 资源限制特性
- 调度器在调度时并不关注各类资源在当前时刻的实际使用量,而只关心节点上部害的所有Pod 的资源申请量之和。
- 在容器内看到的始终是节点的内存,而不是容器本身的内存。
- 在容器内看到的始终是节点所有的 CPU核,而不是仅仅只是容器可用的。
4.1.3 资源限制之HPA
前提是K8S集群安装了metrics-server,可以正常使用kubectl top 命令。
[root@k8s-master ~/kube-prometheus]# kubectl top nodes
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
k8s-master 1523m 38% 1977Mi 46%
k8s-node1 243m 6% 2700Mi 50%
k8s-node2 485m 12% 2629Mi 49%
创建deployment控制器并加以限制
[root@k8s-master ~]# cat hpa-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: my-container
image: myweb:v1
resources:
requests:
cpu: 7m
[root@k8s-master ~]# kubectl apply -f hpa-deploy.yaml
deployment.apps/my-deployment created
[root@k8s-master ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
my-deployment-7ddcc9c4b-dsv2d 1/1 Running 0 2s
my-deployment-7ddcc9c4b-dtnz7 1/1 Running 0 2s
my-deployment-7ddcc9c4b-vc9pd 1/1 Running 0 2s
加上HPA自动扩缩容规则,如果CPU负载超过45%,那就扩容,但是最大扩容副本数为10个,当CPU低于50%就缩容,最低缩容副本数为2个。
[root@k8s-master ~]# kubectl autoscale deployment my-deployment --cpu-percent=45 --min=2 --max=10
horizontalpodautoscaler.autoscaling/my-deployment autoscaled
做一个统一入口,方便压测
[root@k8s-master ~]# kubectl get svc,hpa
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 45d
service/myweb-service ClusterIP 10.110.27.68 <none> 80/TCP 18s
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/my-deployment Deployment/my-deployment cpu: 0%/45% 2 10 3 94s
[root@k8s-master ~]# curl 10.110.27.68
vamos | This version is v1 | v111111
[root@k8s-master ~]# curl 10.110.27.68
vamos | This version is v1 | v111111
开始压测
[root@k8s-master ~]# kubectl exec -it deployments/my-deployment -- sh
/ # while true ;do curl myweb-service.default.svc.cluster.local;done
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
vamos | This version is v1 | v111111
查看cpu负载情况
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
my-deployment Deployment/my-deployment cpu: 14%/45% 2 10 6 3m59s
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
my-deployment Deployment/my-deployment cpu: 14%/45% 2 10 6 4m
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
my-deployment Deployment/my-deployment cpu: 1025%/45% 2 10 10 4m
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
my-deployment Deployment/my-deployment cpu: 1025%/45% 2 10 10 4m1s
查看副本数
[root@k8s-master ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
my-deployment-7ddcc9c4b-8bftq 1/1 Running 0 26s
my-deployment-7ddcc9c4b-94pmx 1/1 Running 0 41s
my-deployment-7ddcc9c4b-c27hn 1/1 Running 0 41s
my-deployment-7ddcc9c4b-dsv2d 1/1 Running 0 4m55s
my-deployment-7ddcc9c4b-dtnz7 1/1 Running 0 4m55s
my-deployment-7ddcc9c4b-lrck4 1/1 Running 0 26s
my-deployment-7ddcc9c4b-mkvzq 1/1 Running 0 41s
my-deployment-7ddcc9c4b-q9rjl 1/1 Running 0 26s
my-deployment-7ddcc9c4b-s9kkx 1/1 Running 0 26s
my-deployment-7ddcc9c4b-vc9pd 1/1 Running 0 4m55s
- 副本数到最大值后不再扩容:Deployment 的
replicas若配置了 HPA(Horizontal Pod Autoscaler)的maxReplicas,当达到maxReplicas(比如 10)时,即使 CPU 仍超阈值,也不会继续扩容。- 缩容不会立即执行:K8S 的 HPA 缩容是 “延迟性” 的,默认需要等待5 分钟(可通过
--horizontal-pod-autoscaler-downscale-stabilization调整) ,目的是避免因临时负载波动导致频繁缩容 / 扩容(即 “抖动”)。
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
my-deployment Deployment/my-deployment cpu: 1173%/45% 2 10 10 4m35s
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
my-deployment Deployment/my-deployment cpu: 0%/45% 2 10 10 5m51s
[root@k8s-master ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
my-deployment-7ddcc9c4b-8bftq 1/1 Running 0 2m10s
my-deployment-7ddcc9c4b-94pmx 1/1 Running 0 2m25s
my-deployment-7ddcc9c4b-c27hn 1/1 Running 0 2m25s
my-deployment-7ddcc9c4b-dsv2d 1/1 Running 0 6m39s
my-deployment-7ddcc9c4b-dtnz7 1/1 Running 0 6m39s
my-deployment-7ddcc9c4b-lrck4 1/1 Running 0 2m10s
my-deployment-7ddcc9c4b-mkvzq 1/1 Running 0 2m25s
my-deployment-7ddcc9c4b-q9rjl 1/1 Running 0 2m10s
my-deployment-7ddcc9c4b-s9kkx 1/1 Running 0 2m10s
my-deployment-7ddcc9c4b-vc9pd 1/1 Running 0 6m39s
等待一段时间
[root@k8s-master ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
my-deployment-7ddcc9c4b-94pmx 1/1 Running 0 7m57s
my-deployment-7ddcc9c4b-dtnz7 1/1 Running 0 12m
4.1.4 Pod 的 QoS 等级
| QoS 等级 | 资源配置要求 | 特点 | 资源保障 | 典型场景 |
|---|---|---|---|---|
| Guaranteed(保证级) | 容器同时设置了requests和limits,且两者数值完全相等(所有容器都需满足) |
资源被严格限制,不会被其他 Pod 抢占 | 最后被回收(仅当节点资源耗尽且无 Burstable/BestEffort Pod 时才会被驱逐) | 核心业务 Pod(如数据库、支付服务) |
| Burstable(突发级) | 满足以下任一条件:1. 仅设置requests,未设置limits;2. requests < limits(部分容器满足即可) |
可使用节点空闲资源,但超出requests的部分可能被抢占 |
回收优先级中等(在 BestEffort 之后、Guaranteed 之前被驱逐) | 非核心但需基础保障的业务(如日志采集、普通 API 服务) |
| BestEffort(尽力而为级) | 未设置任何requests和limits |
无资源保障,完全依赖节点空闲资源 | 最先被回收(节点资源不足时优先驱逐) | 临时任务、测试 Pod、非关键的后台脚本 |
QoS 回收顺序案例说明
假设某节点上同时运行以下 3 个 Pod:
- Pod-A(Guaranteed):
requests.cpu=1核,limits.cpu=1核(数据库服务) - Pod-B(Burstable):
requests.cpu=0.5核,limits.cpu=2核(普通 API 服务) - Pod-C(BestEffort):无任何资源配置(临时测试脚本)
当节点 CPU 资源耗尽时,回收顺序为:Pod-C → Pod-B → Pod-A:
- 首先驱逐
BestEffort的 Pod-C,释放其占用的资源; - 若释放后资源仍不足,驱逐
Burstable的 Pod-B(优先回收超出requests部分的资源,若仍不够则驱逐整个 Pod); - 仅当 Pod-C、Pod-B 都被驱逐后,才会考虑驱逐
Guaranteed的 Pod-A(此时节点已接近完全不可用)。
4.1.5 LimitRange
通过创建一个 LimitRange 资源来避免必须配置每个容器的资源限制。LimitRange 资源不仅允许用户(为每个命名空间)指定能给容器配置的每种资源的最小和最大限额,还支持在没有显示指定资源request时为容器设置默认值。
apiVersion: v1
kind: LimitRange
metadata:
name: example-limit-range
spec:
limits:
- type: Pod
max: # Pod所有容器资源总和的上限
cpu: "1" # 总CPU不超过1核
memory: 1024Mi # 总内存不超过1Gi(修正:大于min的512Mi)
min: # Pod所有容器资源总和的下限
cpu: "200m" # 总CPU至少200m
memory: 128Mi # 总内存至少128Mi
- type: Container
max: # 单个容器的上限
cpu: "500m" # 单容器CPU不超过500m
memory: 512Mi # 单容器内存不超过512Mi
min: # 单个容器的下限
cpu: "100m" # 单容器CPU至少100m
memory: 64Mi # 单容器内存至少64Mi
defaultRequest: # 未指定requests时的默认值
cpu: "200m"
memory: 128Mi
default: # 未指定limits时的默认值
cpu: "300m"
memory: 200Mi
maxLimitRequestRatio: # limits/requests的最大比例(防止资源过度预留)
cpu: "2" # CPU limits最多是requests的2倍
memory: "4" # 内存limits最多是requests的4倍
[root@k8s-master ~]# kubectl get -f limitrange.yaml
NAME CREATED AT
example-limit-range 2025-12-19T01:11:05Z
创建一个pod测试
apiVersion: v1
kind: Pod
metadata:
name: resource-limited-default-pod
spec:
containers:
- name: my-containe
[root@k8s-master ~]# kubectl describe po resource-limited-default-pod
Containers:
my-container:
Container ID: containerd://2c9b35ced7676909325b2d118f0fc241160a7586619a4f073f28e7f2f4687c62
Image: myweb:v2
Image ID: sha256:08e9b034c1bcb8f5dd51396c3e09b5f0563470def7d866c02e55541311a8d1cc
Port: <none>
Host Port: <none>
State: Running
Started: Fri, 19 Dec 2025 09:15:43 +0800
Ready: True
Restart Count: 0
Limits:
cpu: 300m
memory: 200Mi
Requests:
cpu: 200m
memory: 128Mi
Environment: <none>
4.1.6 ResourceQuota
LimitRange 只应用于单独的 Pod,而集群需要一种手段可以限制命名空间中的可用资源总量,这个就是ResourceQuota
ResourceQuota 限制了一个命名空间中 pod 和 PVC 存储最多可以使用的资源总量。同时也可以限制用户允许在该命名空间中创建 pod、PVC 以及其它 API 对象的数量。
[root@k8s-master ~]# cat resourcequota.yaml
# 声明该配置文件遵循的 Kubernetes API 版本(资源配额对应的稳定版本)
apiVersion: v1
# 配置对象类型:ResourceQuota(资源配额),用于限制命名空间级别的资源使用
kind: ResourceQuota
# 元数据:包含名称、命名空间等基础信息
metadata:
# 资源配额的名称,用于唯一标识该配额规则
name: test-resources-rq
# 该配额生效的命名空间,仅限制 test 命名空间内的资源
namespace: test
# 规格配置:定义具体的资源配额限制规则
spec:
# hard 表示硬性配额限制(必须遵守,超出则无法创建/更新资源)
hard:
# CPU 请求总量限制:该命名空间下所有 Pod 的 CPU requests 总和不超过 20 核
requests.cpu: "20"
# 内存请求总量限制:该命名空间下所有 Pod 的 memory requests 总和不超过 100GiB
requests.memory: 100Gi
# CPU 限制总量限制:该命名空间下所有 Pod 的 CPU limits 总和不超过 40 核
limits.cpu: "40"
# 内存限制总量限制:该命名空间下所有 Pod 的 memory limits 总和不超过 200GiB
limits.memory: 200Gi
# Pod 数量限制:该命名空间下最多只能运行 1 个 Pod(包括运行中/已调度的)
pods: "1"
# ConfigMap 数量限制:该命名空间下最多创建 10 个配置映射(存储非敏感配置)
configmaps: "10"
# PVC 数量限制:该命名空间下最多创建 4 个持久卷声明(申请持久化存储)
persistentvolumeclaims: "4"
# RC 数量限制:该命名空间下最多创建 20 个副本控制器(早期 Pod 编排控制器)
replicationcontrollers: "20"
# Secret 数量限制:该命名空间下最多创建 10 个密钥(存储敏感配置,如密码/令牌)
secrets: "10"
# Service 数量限制:该命名空间下最多创建 10 个服务(暴露 Pod 网络访问)
services: "10"
# LoadBalancer 类型 Service 限制:最多创建 2 个负载均衡类型的服务(对外暴露服务)
services.loadbalancers: "2"
测试发现只能创建一个Pod了
[root@k8s-master ~]# kubectl apply -f resourcequota.yaml
resourcequota/test-resources-rq created
[root@k8s-master ~]# kubectl get po -n test
NAME READY STATUS RESTARTS AGE
myweb 1/1 Running 0 18s
[root@k8s-master ~]# kubectl run myweb-2 -n test --image=myweb:v1
Error from server (Forbidden): pods "myweb-2" is forbidden: failed quota: test-resources-rq: must specify limits.cpu for: myweb-2; limits.memory for: myweb-2; requests.cpu for: myweb-2; requests.memory for: myweb-2
[root@k8s-master ~]# kubectl run myapp -n test --image=myweb:v1
Error from server (Forbidden): pods "myapp" is forbidden: failed quota: test-resources-rq: must specify limits.cpu for: myapp; limits.memory for: myapp; requests.cpu for: myapp; requests.memory for: myapp
五、calico报错calico/node is not ready: BIRD is not ready:
BGP not established with 10.0.0.7,10.0.0.8
5.1 问题发现
在 Kubernetes 集群中,Calico 默认会通过 IP 自动检测机制 获取节点的网卡地址。但在本环境中,Calico 识别到了 nerdctl0 或其它容器网桥接口,而非真实物理接口 ens33,导致:
-
projectcalico.org/IPv4Address与InternalIP不一致 -
BGP 邻居关系无法正常建立
-
calico-node Readiness 探针反复失败
-
BIRD 报错:
Invalid NEXT_HOP attribute
根源:自动检测到了错误的接口
5.1.1 calico-node 滚动重建后 BGP 状态异常
查看全局 Pod 运行状态:
[root@k8s-master ~/istio]# kubectl get po -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
calico-system calico-node-gms5f 1/1 Running 0 43s 10.0.0.8 k8s-node2 <none> <none>
calico-system calico-node-n9ntl 0/1 Running 0 43s 10.0.0.6 k8s-master <none> <none>
calico-system calico-node-pksq8 1/1 Running 0 43s 10.0.0.7 k8s-node1 <none> <none>
5.1.2 calico-node Readiness 失败日志
Events:
Warning Unhealthy 72s (x2 over 73s) kubelet Readiness probe failed:
calico/node is not ready: BIRD is not ready:
Error querying BIRD: unable to connect to BIRDv4 socket:
dial unix /var/run/calico/bird.ctl: connect: connection refused
进一步日志:
calico/node is not ready: BIRD is not ready:
BGP not established with 10.0.0.7,10.0.0.8
说明 本节点未与其它节点建立 BGP 邻居。
5.1.3 Calico 分配的 IPv4Address 异常(master 与 Node 不一致)
[root@k8s-master ~/istio]# kubectl get nodes -o custom-columns=NAME:.metadata.name,INTERNAL-IP:.status.addresses[0].address,CALICO-IP:.metadata.annotations."projectcalico\.org/IPv4Address"
NAME INTERNAL-IP CALICO-IP
k8s-master 10.0.0.6 10.4.0.1/24
k8s-node1 10.0.0.7 10.0.0.7/24
k8s-node2 10.0.0.8 10.0.0.8/24
节点 内网 IP Calico 探测 IP 结论
k8s-master 10.0.0.6 10.4.0.1 错误,取了容器桥 nerdctl0
k8s-node1 / node2 一致 一致 正常nerdctl0: ...
inet 10.4.0.1/24 scope global nerdctl0Calico 自动检测误判,将 nerdctl0 当成节点 IP 来源,而正确的应为 ens33。
[root@k8s-master ~/istio]# ip a
2: ens33: inet 10.0.0.6/24 brd 10.0.0.255 scope global ens33 valid_lft forever
7: nerdctl0: inet 10.4.0.1/24 brd 10.4.0.255 scope global nerdctl0 valid_lft forever preferred_lft forever inet6
5.2 解决问题
Calico 通过环境变量 IP_AUTODETECTION_METHOD 控制自动检测方法。 只要显式指定真正的物理接口,Calico 即可正确识别 IP,从而恢复 BGP 拓扑。
两种常用配置方式:
| 方式 | 说明 | 示例 |
|---|---|---|
| 指定接口名称 | 最明确且稳定的方式,推荐 | interface=^ens33$ |
| 出口可达性探测 | 若多物理网卡,可使用 | can-reach=10.0.0.1 |
5.2.1 修改步骤
编辑calico-node·daemonset
kubectl -n calico-system edit daemonset calico-node
在yaml中找到containers.env部分,添加或修改
- name: IP_AUTODETECTION_METHOD
value: "interface=^ens33$"
保存后退出,Kubernetes 会自动滚动更新 calico-node Pods。
也可以强制回滚生效
kubectl -n calico-system rollout restart daemonset calico-node
kubectl get pods -n calico-system -w
等待所有节点 calico-node 状态变为 READY 1/1
5.3 验证结果
[root@k8s-master ~/istio]# kubectl get nodes -o custom-columns=NAME:.metadata.name,CALICO-IP:.metadata.annotations."projectcalico\.org/IPv4Address"
k8s-master 10.0.0.6/24
k8s-node1 10.0.0.7/24
k8s-node2 10.0.0.8/24
5.3.1 检查BGP状态
[root@k8s-master ~/istio]# calicoctl node status
PEER ADDRESS | STATE | INFO
10.0.0.7 | up | Established
10.0.0.8 | up | Established
六、总结
综上,本文围绕K8s运维核心需求,系统梳理了临时容器、端口转发、安全配置、资源管控四大关键模块的实操方法与最佳实践,旨在为运维人员提供高效调试、灵活访问、安全合规、资源可控的全流程指南。各模块核心要点梳理如下:
|
核心模块 |
核心能力/工具 |
生产环境最佳实践 |
|---|---|---|
|
临时容器 |
kubectl debug注入临时容器,支持容器级/节点级调试、Pod副本调试、镜像替换 |
优先使用--copy-to创建副本调试,避免影响原Pod;根据需求选择busybox/netshoot等镜像 |
|
端口转发 |
kubectl port-forward实现本地-集群Pod/Service端口转发,支持本地/全主机访问 |
临时调试用,避免频繁配置Ingress/Service;需对外访问时指定--address 0.0.0.0 |
|
安全配置 |
命名空间共享、安全上下文、PodSecurityAdmission(3种策略+3种生效模式) |
禁用不必要的主机共享;业务Pod用Baseline/Restricted策略;生产环境启用enforce模式 |
|
资源管控 |
resources.requests/limits、HPA自动扩缩容、QoS等级、LimitRange、ResourceQuota |
分层配置资源限制;核心业务用Guaranteed级QoS;命名空间启用ResourceQuota实现隔离 |
更多推荐
所有评论(0)