You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

K8s DaemonSet部署的Golang程序如何执行宿主机命令获取节点状态

问题根因

你目前的方案存在两个核心错误,导致无法获取宿主机服务状态:

  1. 启动脚本逻辑错误:nsenter -t 1 -m -u -i -n /bin/sh只会启动一个运行在宿主机命名空间的交互式shell进程,只有手动退出这个shell后,后续的/monitor启动命令才会执行,监控程序本身始终运行在容器的隔离命名空间内,根本没有进入宿主机环境。
  2. 容器挂载配置缺失:即使开启了特权模式、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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 02:54:39