一、临时容器

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

总结

  1. 核心调试场景分为容器级(查看进程、交互式调试)和节点级(节点故障诊断),对应不同参数组合;
  2. 生产环境优先使用 --copy-to 创建 Pod 副本调试,避免影响原运行中的 Pod;
  3. 交互调试需结合 -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配置统一管理共享卷的文件权限:

  1. 共享存储基础:通过emptyDir类型的shared-volume,让firstsecond两个容器挂载同一个目录,实现数据共享。
  2. 文件权限统一控制
    • fsGroup: 555:指定共享卷的文件所属组为 555,Kubernetes 会自动将卷中文件的组权限设置为该组,确保两个容器都能访问卷内文件;
    • supplementalGroups: [666, 777]:为 Pod 内的进程添加额外的附属组,扩展容器对其他组权限文件的访问能力。
  3. 容器独立用户:两个容器分别以runAsUser: 1111runAsUser: 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字段下requestslimits的含义

关键字 类型 含义 示例(以配置为例) 作用场景
requests 资源请求 容器启动时向 Kubernetes 申请的最小资源量,Kubernetes 会确保节点有足够资源才调度 Pod - CPU:200m(0.2 核)- 内存:128Mi(128MB) 用于 Kubernetes 的 Pod 调度(确保节点资源充足)
limits 资源限制 容器运行时能使用的最大资源量,超过后会被限制(CPU 被限流,内存超用会被终止) - CPU:500m(0.5 核)- 内存:256Mi(256MB) 防止单个 Pod 消耗过多节点资源,避免影响其他 Pod

补充说明

  • CPU 单位m1000m = 1核,示例中200m表示 0.2 核,500m表示 0.5 核;
  • 内存单位Mi1Mi = 1024Ki = 1024×1024字节,是 Kubernetes 中常用的内存单位;
  • 超限制的后果
    • CPU 超limits:Pod 会被限流(CPU 使用率不会超过limits值),但不会被终止;
    • 内存超limits:Pod 会因 “内存不足(OOM)” 被 Kubernetes 强制终止。

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
  1. 副本数到最大值后不再扩容:Deployment 的replicas若配置了 HPA(Horizontal Pod Autoscaler)的maxReplicas,当达到maxReplicas(比如 10)时,即使 CPU 仍超阈值,也不会继续扩容。
  2. 缩容不会立即执行: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(保证级) 容器同时设置了requestslimits,且两者数值完全相等(所有容器都需满足) 资源被严格限制,不会被其他 Pod 抢占 最后被回收(仅当节点资源耗尽且无 Burstable/BestEffort Pod 时才会被驱逐) 核心业务 Pod(如数据库、支付服务)
Burstable(突发级) 满足以下任一条件:1. 仅设置requests,未设置limits;2. requests < limits(部分容器满足即可) 可使用节点空闲资源,但超出requests的部分可能被抢占 回收优先级中等(在 BestEffort 之后、Guaranteed 之前被驱逐) 非核心但需基础保障的业务(如日志采集、普通 API 服务)
BestEffort(尽力而为级) 未设置任何requestslimits 无资源保障,完全依赖节点空闲资源 最先被回收(节点资源不足时优先驱逐) 临时任务、测试 Pod、非关键的后台脚本

QoS 回收顺序案例说明

假设某节点上同时运行以下 3 个 Pod:

  1. Pod-A(Guaranteed)requests.cpu=1核limits.cpu=1核(数据库服务)
  2. Pod-B(Burstable)requests.cpu=0.5核limits.cpu=2核(普通 API 服务)
  3. Pod-C(BestEffort):无任何资源配置(临时测试脚本)

当节点 CPU 资源耗尽时,回收顺序为:Pod-C → Pod-B → Pod-A

  1. 首先驱逐BestEffort的 Pod-C,释放其占用的资源;
  2. 若释放后资源仍不足,驱逐Burstable的 Pod-B(优先回收超出requests部分的资源,若仍不够则驱逐整个 Pod);
  3. 仅当 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/IPv4AddressInternalIP 不一致

  • 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 nerdctl0

Calico 自动检测误判,将 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实现隔离

更多推荐