容器内的Nodejs应用如何获取宿主机的基础信息-系统、内存、cpu、启动时间,以及一个df -h的坑

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本文介绍了如何在Docker容器内的Node.js应用中获取宿主机的基础信息,包括系统信息、内存使用情况、磁盘空间和启动时间等。核心思路是将宿主机的根目录挂载到容器,但需注意权限和安全问题。文章还提到了使用`df -P`替代`df -h`以获得一致性输出,避免解析错误。

 在现代应用部署时中,Docker容器化技术被广泛应用。Node.js应用在容器中运行时,有时需要获取宿主机的基础信息,如系统信息、内存使用情况、磁盘空间和启动时间等。本文将介绍如何在Docker容器内的Node.js应用中获取这些信息,以及可能遇到的坑*。

核心思路:将宿主机的根目录挂载到容器

避坑总结:容器内执行 df -h /xxx, 如果xxx是挂载到容器的宿主机目录,那么它的输出格式可能会异常:第二行第一列直接换行,导致常规数值解析失败

前提条件

  1. 确保已安装Docker。
  2. Node.js已在Docker容器中运行。

1. 获取宿主机的基础信息

1.1 系统信息

要获取宿主机的系统信息,可以通过读取 /etc/os-release +文件来实现。这是Linux系统提供的标准文件,包含了操作系统的名称和版本等信息。

async function getHostOSInfo(hostRoot: string) {
  try {
    // 尝试读取 /etc/os-release 文件
    const osReleasePath = path.join(hostRoot, "/etc/os-release");
    const osReleaseContent: string = await fs.readFile(osReleasePath, "utf8");
    const osInfo = {} as any;
    osReleaseContent.split("\n").forEach((line) => {
      const [key, value] = line.split("=");
      if (key && value) {
        osInfo[key] = value.replace(/"/g, "");
      }
    });
    // 尝试读取 /etc/lsb-release 文件(用于某些 Ubuntu 版本)
    let lsbReleaseContent = "";
    try {
      lsbReleaseContent = await fs.readFile(
        path.join(hostRoot, "/etc/lsb-release"),
        "utf8"
      );
      lsbReleaseContent.split("\n").forEach((line) => {
        const [key, value] = line.split("=");
        if (key && value) {
          osInfo[key] = value.replace(/"/g, "");
        }
      });
    } catch (error) {
      // 如果文件不存在,就忽略这个错误
    }
    // 尝试读取内核版本
    const kernelVersion = await fs.readFile(
      path.join(hostRoot, "proc/version"),
      "utf8"
    );
    return {
      arch: osInfo.NAME || osInfo.DISTRIB_ID || "Unknown",
      osVersion: osInfo.VERSION_ID || osInfo.DISTRIB_RELEASE || "Unknown",
      osDescription:
        osInfo.PRETTY_NAME ||
        `${osInfo.DISTRIB_ID} ${osInfo.DISTRIB_RELEASE}` ||
        "Unknown",
      kernelVersion: kernelVersion.split(" ")[2] || "Unknown",
      firewallStatus: getFirewallStatus(hostRoot),
    };
  } catch (error) {
    console.error("Error reading host OS information:", error);
    return null;
  }
}

image.gif

1.2 内存信息

内存使用情况可以通过读取 /proc/meminfo 文件来获取。该文件提供了系统内存的详细信息。

async function getHostMemInfo(hostRoot: string) {
  try {
    const filePath = path.join(hostRoot, "/proc/meminfo");
    const meminfo = await fs.readFile(filePath, "utf8");
    const memTotal = parseInt(meminfo.match(/MemTotal:\s+(\d+)/)[1]) * 1024;
    const memFree = parseInt(meminfo.match(/MemFree:\s+(\d+)/)[1]) * 1024;
    const memUsed = memTotal - memFree;
    return {
      total: memTotal,
      free: memFree,
      used: memUsed,
    };
  } catch (error) {
    console.error("Error reading memory information:", error);
    return null;
  }
}

image.gif

1.3 CPU信息

可以通过读取 /proc/stat文件来获取cpu信息,隔段时间读取两次就可以计算出使用率。

async function readHostCpuInfo(hostRoot: string) {
  const filePath = path.join(hostRoot, "/proc/cpuinfo");
  const cpuinfo = await fs.readFile(filePath, "utf8");
  const cpus = cpuinfo.match(/processor/g) || [];
  return cpus;
}
async function readHostCpuUsage(hostRoot: string) {
  const filePath = path.join(hostRoot, "/proc/stat");
  const stat = await fs.readFile(filePath, "utf8");
  const cpuLine = stat.split("\n")[0];
  const cpuTimes = cpuLine.split(/\s+/).slice(1).map(Number);
  const totalTime = cpuTimes.reduce((a: number, b: number) => a + b, 0);
  const idleTime = cpuTimes[3];
  return { totalTime, idleTime };
}
async function getHostCpuInfo(hostRoot: string) {
  try {
    const cpus = await readHostCpuInfo(hostRoot);
    const usage1 = await readHostCpuUsage(hostRoot);
    // 等待一小段时间再次读取,以计算使用率
    await new Promise((resolve) => setTimeout(resolve, 1000));
    const usage2 = await readHostCpuUsage(hostRoot);
    const totalDiff = usage2.totalTime - usage1.totalTime;
    const idleDiff = usage2.idleTime - usage1.idleTime;
    const usagePercent = (1 - idleDiff / totalDiff) * 100;
    return {
      cpus,
      cpuUsage: usagePercent.toFixed(2),
    };
  } catch (error) {
    console.error("Error reading CPU information:", error);
    return null;
  }
}

image.gif

1.4 启动时间

启动时间可以通过读取 /proc/stat 文件中的 btime 字段来获取,表示自Unix时间(1970年1月1日)以来的秒数。

async function getHostUptime(hostRoot: string) {
  try {
    const filePath = path.join(hostRoot, "/proc/uptime");
    const uptime = await fs.readFile(filePath, "utf8");
    const uptimeSeconds = parseFloat(uptime.split()[0]);
    return uptimeSeconds;
  } catch (error) {
    console.error("Error reading uptime information:", error);
    return null;
  }
}

image.gif

2. 获取磁盘使用

2.1. 使用df -h 失败

function getDiskUsage(hostRoot= "/") {
  try {
    const output = execSync(`df -h ${hostRoot}`).toString();
    const lines = output.trim().split("\n");
    const data = lines[1].split(/\s+/);
    const [filesystem, size, used, available, percentUsed, mounted] = data;
    return {
      total:size,
      free: available,
      used: used,
      percentUsed: parseInt(percentUsed),
    };
  } catch (error: any) {
    console.error("Error checking disk usage:", error.message);
    return {
      total: 0,
      free: 0,
      used: 0,
      percentUsed: 0,
      error: error.message,
    };
  }
}

image.gif

2.2 使用df -h 的诡异结果 - 外挂磁盘输出格式不一致

经过测试,前端拿到的磁盘信息全为空,百思不得其解,而且进入到容器,测试df -h /也是对的,这里犯了个错,我想当然因为 df -h **的返回格式一样

下图为我在磁盘里,随便找了个非/路径的磁盘信息,正常!我也是按照这个方式解析的

image.gif 编辑

但是还是不明白,为啥有问题呢?为了复原情况,我还是敲出了映射路径,结果如下:

image.gif 编辑

看到没有,多了一个空行,在第二行就显示了第一列,后面就换行了。

环境信息:

Docker version 18.09.6, build 481bc77

基础镜像 node:20.11.1-alpine

宿主机 centos 7 / ubuntu

磁盘映射 / -> /home_dev:ro

2.3 用df -P 替代以获得一致性输出

因为df -h使用比较多,我试了一些方法,发现df -P具有一致性

image.gif 编辑

3. 综合示例

3.1 将宿主机根目录以只读模式挂载到容器内

docker run --name voi-nodejs-app --restart unless-stopped -e DATABASE_URL=file:/home/nodejs/app/dev.db  -e HOST_MOUNT_POINT=/home_dev/  --network host -v /root/test/db/dev.db:/home/nodejs/app/dev.db -v /:/home_dev:ro  nodeapp:latest

image.gif

3.2 从环境变量读取宿主机根目录挂载位置获取相关信息

const hostRoot = process.env["HOST_MOUNT_POINT"]
memo = await getHostMemInfo(hostRoot)
uptime = await getHostOUptime(hostRoot)
cpu = await getHostCpuInfo(hostRoot)
os = await getHostOSInfo(hostRoot)

image.gif

4. 注意事项

  1. 权限问题:在某些Docker环境中,可能需要提升容器的权限(如使用 --privileged--cap-add=SYS_ADMIN)才能访问某些文件。
  2. 安全性:暴露宿主机的详细信息可能会带来安全风险,请确保在受信任的环境中使用。

总结

通过读取特定的系统文件和执行命令,您可以获取有关操作系统、内存、磁盘和启动时间的详细信息。这些信息对于监控、性能分析和故障排查都是非常有用的。希望这篇文章能为您提供帮助!

实事求是才是解决问题的真理,不能想当然,很多看似一样的输入却在某些特殊情况下会输出不一样的结果。例如我就把df -h 的结果想象成一样了。

相关文章
|
1月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
99 7
|
30天前
|
开发框架 .NET PHP
网站应用项目如何选择阿里云服务器实例规格+内存+CPU+带宽+操作系统等配置
对于使用阿里云服务器的搭建网站的用户来说,面对众多可选的实例规格和配置选项,我们应该如何做出最佳选择,以最大化业务效益并控制成本,成为大家比较关注的问题,如果实例、内存、CPU、带宽等配置选择不合适,可能会影响到自己业务在云服务器上的计算性能及后期运营状况,本文将详细解析企业在搭建网站应用项目时选购阿里云服务器应考虑的一些因素,以供参考。
|
1月前
|
存储 Prometheus 监控
Docker容器内进行应用调试与故障排除的方法与技巧,包括使用日志、进入容器检查、利用监控工具及检查配置等,旨在帮助用户有效应对应用部署中的挑战,确保应用稳定运行
本文深入探讨了在Docker容器内进行应用调试与故障排除的方法与技巧,包括使用日志、进入容器检查、利用监控工具及检查配置等,旨在帮助用户有效应对应用部署中的挑战,确保应用稳定运行。
40 5
|
1月前
|
开发框架 安全 开发者
Docker 是一种容器化技术,支持开发者将应用及其依赖打包成容器,在不同平台运行而无需修改。
Docker 是一种容器化技术,支持开发者将应用及其依赖打包成容器,在不同平台运行而无需修改。本文探讨了 Docker 在多平台应用构建与部署中的作用,包括环境一致性、依赖管理、快速构建等优势,以及部署流程和注意事项,展示了 Docker 如何简化开发与部署过程,提高效率和可移植性。
65 4
|
1月前
|
负载均衡 网络协议 算法
Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式
本文探讨了Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式,以及软件负载均衡器、云服务负载均衡、容器编排工具等实现手段,强调两者结合的重要性及面临挑战的应对措施。
75 3
|
2月前
|
运维 Kubernetes Docker
深入理解容器化技术及其在微服务架构中的应用
深入理解容器化技术及其在微服务架构中的应用
65 1
|
2月前
|
Kubernetes Linux 开发者
深入探索容器化技术——Docker 的实战应用
深入探索容器化技术——Docker 的实战应用
92 0
|
2月前
|
弹性计算 Kubernetes Perl
k8s 设置pod 的cpu 和内存
在 Kubernetes (k8s) 中,设置 Pod 的 CPU 和内存资源限制和请求是非常重要的,因为这有助于确保集群资源的合理分配和有效利用。你可以通过定义 Pod 的 `resources` 字段来设置这些限制。 以下是一个示例 YAML 文件,展示了如何为一个 Pod 设置 CPU 和内存资源请求(requests)和限制(limits): ```yaml apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image:
267 1
|
2月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
873 2
|
4月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
221 5