最近在做产品授权的东西,开始宿主机为Window,程序获取机器硬件信息相对简单些,后来部署时发现各种各样的的环境问题,所有后来改用dokcer部署,docker方式获取宿主机信息时花了些时间,特此记录一下
docker 获取宿主机的信息
// dmidecode -t 4 | grep ID | tail -1 // CPUID
// 系统
// dmidecode -s system-serial-number // 查看系统序列号
// dmidecode -s system-uuid // 查看系统UUID
// dmidecode -s system-product-name //查看服务器系统型号
// dmidecode -s processor-manufacturer | tail -1 // 处理器厂家
// 主板
// dmidecode -s baseboard-product-name // 主板型号
// dmidecode -s baseboard-serial-number // 主板序列号
// dmidecode -s baseboard-manufacturer // 主板厂家
实际项目当中,我获取了CPUID、系统序列号、系统UUID、系统型号、处理器厂家,之所有获取这么多信息标识机器,是考虑到有些信息在某些系统可能为空,而且CPUID也不唯一了,所以就多获取些。
调查下来,docker 获取宿主机信息大体可以通过三种方式
- 通过环境变量由外部传入容器内
-
使用挂载宿主机目录方式
- 在容器中使用ssh连接到主机
一:通过环境变量由外部传入容器内
大体思路是docker 支持通过-e来传递参数到容器内部程序,就像安装docker-mysql那样密码可以通过参数传递一样
- 在DockeFile中增加环境变量配置节点 (此步骤主要用来设置参数默认,也可以省略,通过其它方式设置)
- 在程序启动时应用获取程序变量并应用
- 在docker run 时通过-e参数传递到容器中
二:使用挂载宿主机目录方式
确保宿主机能执行dmidecode命令(必须)
将宿主机的如下两个目录挂载到容器中
// dmidecode程序的目录,如果不挂载那么容器中识别不了dmidecode命令
/usr/sbin/dmidecode或者/sbin/dmidecode
// dmidecode调用时会使用到mem这个文件,如果不挂载会找不到文件
/dev/mem
在容器启动时增加 --privileged = true参数,让容器获得近似于宿主机root的权限
三:在容器中使用ssh连接到主机
思路:在docker容器内安装ssh,sshpass服务,通过ssh连接到宿主机执行命令,获 取宿主机信息(必须知道宿主机Ip和密码)
步骤:
- 安装服务 yum -y install openssh-server
- 修改配置 vim /etc/ssh/sshd_config PermitRootLogin的值修改为yes保存退出
- 启动ssh服务 systemctl start sshd.service
- 设置开机启动 systemctl enable sshd.service
- 安装sshpass yum -y install sshpass
Window 获取设备信息帮助类
/// <summary>
/// 注册帮助类
/// </summary>
public class RegisterHelper
{
// 机器指纹字符串
private static string m_FingerPrintString = string.Empty;
/// <summary>
/// Get a string Unique Identification code of a computer
/// </summary>
/// <returns></returns>
public static string StringValue(string mac)
{
if (string.IsNullOrEmpty(m_FingerPrintString))
{
m_FingerPrintString = "MAC >> " + mac + "\nCPU >> " + GetCpuId() + "\nBIOS >> " + GetBiosId() + "\nBASE >> " + GetBaseId()
+ "\nDISK >> " + GetDiskId() + "\nVIDEO >> " + GetVideoId();
}
return m_FingerPrintString;
}
/// <summary>
/// First enabled network card ID
/// </summary>
/// <returns></returns>
public static string GetMacId()
{
return Identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled");
}
/// <summary>
/// Get the cpuID
/// </summary>
/// <returns></returns>
private static string GetCpuId()
{
//Uses first CPU identifier available in order of preference
//Don't get all identifiers, as it is very time consuming
string retVal = Identifier("Win32_Processor", "UniqueId");
if (string.IsNullOrEmpty(retVal)) //If no UniqueID, use ProcessorID
{
retVal = Identifier("Win32_Processor", "ProcessorId");
if (string.IsNullOrEmpty(retVal)) //If no ProcessorId, use Name
{
retVal = Identifier("Win32_Processor", "Name");
if (string.IsNullOrEmpty(retVal)) //If no Name, use Manufacturer
{
retVal = Identifier("Win32_Processor", "Manufacturer");
}
//Add clock speed for extra security
retVal += Identifier("Win32_Processor", "MaxClockSpeed");
}
}
return retVal;
}
/// <summary>
/// BIOS Identifier
/// </summary>
/// <returns></returns>
private static string GetBiosId()
{
return Identifier("Win32_BIOS", "Manufacturer") + " | " + Identifier("Win32_BIOS", "SMBIOSBIOSVersion")
+ " | " + Identifier("Win32_BIOS", "IdentificationCode") + " | " + Identifier("Win32_BIOS", "SerialNumber")
+ " | " + Identifier("Win32_BIOS", "ReleaseDate") + " | " + Identifier("Win32_BIOS", "Version")
+ " | " + Identifier("Win32_BIOS", "Name");
}
/// <summary>
/// Main physical hard drive ID
/// </summary>
/// <returns></returns>
private static string GetDiskId()
{
return Identifier("Win32_DiskDrive", "Model") + " | " + Identifier("Win32_DiskDrive", "SerialNumber")
+ " | " + Identifier("Win32_DiskDrive", "Signature") + " | " + Identifier("Win32_DiskDrive", "TotalHeads");
}
/// <summary>
/// Motherboard ID
/// </summary>
/// <returns></returns>
private static string GetBaseId()
{
return Identifier("Win32_BaseBoard", "Model") + " | " + Identifier("Win32_BaseBoard", "Manufacturer")
+ " | " + Identifier("Win32_BaseBoard", "Name") + " | " + Identifier("Win32_BaseBoard", "SerialNumber")
+ " | " + Identifier("Win32_BaseBoard", "SKU") + " | " + Identifier("Win32_BaseBoard", "Product");
}
/// <summary>
/// Primary video controller ID
/// </summary>
/// <returns></returns>
private static string GetVideoId()
{
return Identifier("Win32_VideoController", "Name") + " | " + Identifier("Win32_VideoController", "AdapterRAM");
}
/// <summary>
/// Return a hardware identifier
/// </summary>
/// <param name="wmiClass"></param>
/// <param name="wmiProperty"></param>
/// <returns></returns>
private static string Identifier(string wmiClass, string wmiProperty)
{
string result = string.Empty;
System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
System.Management.ManagementObjectCollection moc = mc.GetInstances();
foreach (System.Management.ManagementObject mo in moc)
{
//Only get the first one
if (string.IsNullOrEmpty(result))
{
try
{
result = mo[wmiProperty]?.ToString();
break;
}
catch(Exception e)
{
LogSingleton.CreateInstance().Error(e, "Window获取硬件信息失败");
}
}
}
return result;
}
/// <summary>
/// Return a hardware identifier
/// </summary>
/// <param name="wmiClass"></param>
/// <param name="wmiProperty"></param>
/// <param name="wmiMustBeTrue"></param>
/// <returns></returns>
private static string Identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue)
{
string result = string.Empty;
System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
System.Management.ManagementObjectCollection moc = mc.GetInstances();
foreach (System.Management.ManagementObject mo in moc)
{
if (mo[wmiMustBeTrue].ToString() == "True")
{
//Only get the first one
if (string.IsNullOrEmpty(result))
{
try
{
result = mo[wmiProperty]?.ToString();
break;
}
catch(Exception e)
{
LogSingleton.CreateInstance().Error(e,"Window获取硬件信息失败");
}
}
}
}
return result;
}
}
Linux 获取设备信息帮助类
public class LinuxHelper
{
// sudo dmidecode -t 4 | grep ID | tail -1 // CPUID
// 系统
// sudo dmidecode -s system-serial-number // 查看系统序列号
// sudo dmidecode -s system-uuid // 查看系统UUID
// sudo dmidecode -s system-product-name // 查看服务器系统型号
// sudo dmidecode -s processor-manufacturer | tail -1 // 处理器厂家
// 主板
// sudo dmidecode -s baseboard-product-name // 主板型号
// sudo dmidecode -s baseboard-serial-number // 主板序列号
// sudo dmidecode -s baseboard-manufacturer // 主板厂家
/// <summary>
/// Get a string Unique Identification code of a computer
/// </summary>
/// <returns></returns>
public static string StringValue()
{
string cpuID = GetCpuId();
string serialNumber = GetSerialNumber();
string productName = GetProductName();
string processorManufacturer = GetProcessorManufacturer();
if (string.IsNullOrWhiteSpace(cpuID) && string.IsNullOrWhiteSpace(serialNumber) && string.IsNullOrWhiteSpace(productName) && string.IsNullOrWhiteSpace(processorManufacturer))
{
return string.Empty;
}
return "CPU >> " + cpuID + "\nSerialNumber >> " + serialNumber + "\nProductName >> " + productName + "\nProcessorManufacturer >> " + processorManufacturer;
}
/// <summary>
/// Get the cpuID
/// </summary>
/// <returns></returns>
private static string GetCpuId()
{
return ProcessShell("dmidecode -t 4 | grep ID | tail -1");
}
/// <summary>
/// SerialNumber
/// </summary>
/// <returns></returns>
private static string GetSerialNumber()
{
return ProcessShell("dmidecode -s system-serial-number");
}
/// <summary>
/// product-name
/// </summary>
/// <returns></returns>
private static string GetProductName()
{
return ProcessShell("dmidecode -s system-product-name");
}
/// <summary>
/// ProcessorManufacturer
/// </summary>
/// <returns></returns>
private static string GetProcessorManufacturer()
{
return ProcessShell("dmidecode -s processor-manufacturer | tail -1");
}
/// <summary>
/// 执行Shell命令
/// </summary>
/// <param name="shellCmd"></param>
/// <returns></returns>
private static string ProcessShell(string shellCmd)
{
string result = string.Empty;
try
{
using Process process = new Process
{
StartInfo = new ProcessStartInfo("/bin/bash", "")
};
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
process.StandardInput.WriteLine(shellCmd);
process.StandardInput.Close();
result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
}
catch (Exception e)
{
LogSingleton.CreateInstance().Error(e, "Linux获取硬件信息失败");
}
return result;
}
补充:方案二在实际测试中,发布在Ubuntu 22.04 TSL版本中获取宿主机信息失败。大致错误为找不到相关文件。
最终采取的方式为在容器中获取授权服务容器的ID,以此作为唯一标识(具体采用哪种方式看自己的实际应用环境)
关键代码
/// <summary>
/// 获取所有容器信息
/// </summary>
/// <returns></returns>
private static List<ContainerListResponse> GetContainerList()
{
string apiVersion = Environment.GetEnvironmentVariable("DockerApiVersin", EnvironmentVariableTarget.Process);
apiVersion = string.IsNullOrWhiteSpace(apiVersion) ? "1.37" : apiVersion;
string result = ProcessShell($"curl --unix-socket /var/run/docker.sock http://172.17.0.1/v{apiVersion}/containers/json");
return JsonConvert.DeserializeObject<List<ContainerListResponse>>(result);
}