K8s DaemonSet部署的Golang程序如何执行宿主机命令获取节点状态
问题根因
你目前的方案存在两个核心错误,导致无法获取宿主机服务状态:
- 启动脚本逻辑错误:
nsenter -t 1 -m -u -i -n /bin/sh只会启动一个运行在宿主机命名空间的交互式shell进程,只有手动退出这个shell后,后续的/monitor启动命令才会执行,监控程序本身始终运行在容器的隔离命名空间内,根本没有进入宿主机环境。 - 容器挂载配置缺失:即使开启了特权模式、hostPID/hostNetwork/hostIPC,只要没有挂载宿主机systemd依赖的系统目录,
systemctl就无法和宿主机的systemd进程建立通信,自然拿不到服务状态。
解决方案
你可以根据需求选择以下任意一种方案,都可以稳定获取宿主机systemd服务状态。
方案1:修正启动逻辑,让监控程序直接运行在宿主机命名空间
这个方案改动最小,只需要补全DaemonSet的挂载配置、修正entrypoint脚本即可。
第一步:补全DaemonSet配置
需要把宿主机上systemd依赖的核心目录挂载进容器,修正后的DaemonSet yaml如下:
apiVersion: apps/v1 kind: DaemonSet metadata: name: cluster-monitor namespace: cluster-monitor spec: selector: matchLabels: app: cluster-monitor template: metadata: labels: app: cluster-monitor spec: containers: - name: cluster-monitor image: monitor:v6 imagePullPolicy: IfNotPresent securityContext: runAsUser: 0 privileged: true volumeMounts: # 挂载宿主机systemd运行时目录 - name: host-run mountPath: /run # 挂载cgroup目录,systemctl依赖cgroup信息判断服务状态 - name: host-cgroup mountPath: /sys/fs/cgroup # 挂载宿主机procfs,方便读取进程信息 - name: host-proc mountPath: /proc hostIPC: true hostNetwork: true hostPID: true volumes: - name: host-run hostPath: path: /run - name: host-cgroup hostPath: path: /sys/fs/cgroup - name: host-proc hostPath: path: /proc
第二步:修正entrypoint.sh脚本
不要通过nsenter启动交互式shell,直接用nsenter启动你的监控程序,让监控进程本身运行在宿主机命名空间内:
#!/bin/sh # 进入宿主机的mount/uts/ipc/net/pid命名空间,直接启动监控程序 # 加-p参数进入宿主机PID命名空间,exec让监控进程成为容器1号进程,避免多余shell进程残留 exec nsenter -t 1 -m -u -i -n -p -- /monitor -conf /config.toml
方案2:仅执行命令时切换命名空间,不修改程序启动逻辑
如果你不想让整个监控程序都运行在宿主机命名空间,只需要在执行systemctl相关命令时临时切换到宿主机环境,可以直接修改Go代码的命令执行逻辑,不需要调整entrypoint。
前提:容器镜像内需要包含
nsenter工具(基础镜像如果没有的话,安装util-linux包即可),同时需要保留方案1中DaemonSet的hostPID配置和目录挂载。
修改后的Go代码如下:
package main import ( "fmt" "os/exec" "strings" ) func main() { res,_:=ExecCommand("systemctl","status","kubelet") fmt.Println(res) } func ExecCommand(command string, args ...string) (string, error) { // 拼接nsenter参数,先进入宿主机命名空间再执行目标命令 cmdArgs := []string{"-t", "1", "-m", "-u", "-i", "-n", "-p", "--", command} cmdArgs = append(cmdArgs, args...) cmd := exec.Command("nsenter", cmdArgs...) out, err := cmd.Output() if err != nil { return "", err } return strings.TrimSuffix(string(out), "\n"), nil }
验证方法
部署完成后进入容器,执行nsenter -t 1 -m -u -i -n -p -- systemctl status kubelet,如果返回结果和你直接在宿主机上执行命令的结果一致,说明配置生效。
注意:如果你的集群节点使用cgroup v2,不需要给/sys/fs/cgroup挂载加读写权限,查询状态只读挂载即可;如果后续需要在容器内执行服务启停、重启操作,再调整为读写挂载。
内容的提问来源于stack exchange,提问作者Worlder_Mo
相关产品推荐
相关产品推荐

