容器内的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 的结果想象成一样了。

相关文章
|
23天前
|
缓存 监控 Linux
|
5天前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
|
9天前
|
存储 缓存 JavaScript
如何优化Node.js应用的内存使用以提高性能?
通过以上多种方法的综合运用,可以有效地优化 Node.js 应用的内存使用,提高性能,提升用户体验。同时,不断关注内存管理的最新技术和最佳实践,持续改进应用的性能表现。
|
19天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
29天前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
246 2
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
17天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
59 2
|
7天前
|
Kubernetes Linux 开发者
深入探索容器化技术——Docker 的实战应用
深入探索容器化技术——Docker 的实战应用
34 5
|
11天前
|
运维 Cloud Native 云计算
云原生之旅:Docker容器化实战
本文将带你走进云原生的世界,深入理解Docker技术如何改变应用部署与运维。我们将通过实际案例,展示如何利用Docker简化开发流程,提升应用的可移植性和伸缩性。文章不仅介绍基础概念,还提供操作指南和最佳实践,帮助你快速上手Docker,开启云原生的第一步。
|
14天前
|
机器学习/深度学习 数据采集 Docker
Docker容器化实战:构建并部署一个简单的Web应用
Docker容器化实战:构建并部署一个简单的Web应用