最近在做产品授权的东西,开始宿主机为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 获取设备信息帮助类
1 /// <summary>
2 /// 注册帮助类
3 /// </summary>
4 public class RegisterHelper
5 {
6 // 机器指纹字符串
7 private static string m_FingerPrintString = string.Empty;
8
9 /// <summary>
10 /// Get a string Unique Identification code of a computer
11 /// </summary>
12 /// <returns></returns>
13 public static string StringValue(string mac)
14 {
15 if (string.IsNullOrEmpty(m_FingerPrintString))
16 {
17 m_FingerPrintString = "MAC >> " + mac + "\nCPU >> " + GetCpuId() + "\nBIOS >> " + GetBiosId() + "\nBASE >> " + GetBaseId()
18 + "\nDISK >> " + GetDiskId() + "\nVIDEO >> " + GetVideoId();
19 }
20 return m_FingerPrintString;
21 }
22
23 /// <summary>
24 /// First enabled network card ID
25 /// </summary>
26 /// <returns></returns>
27 public static string GetMacId()
28 {
29 return Identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled");
30 }
31
32 /// <summary>
33 /// Get the cpuID
34 /// </summary>
35 /// <returns></returns>
36 private static string GetCpuId()
37 {
38 //Uses first CPU identifier available in order of preference
39 //Don't get all identifiers, as it is very time consuming
40 string retVal = Identifier("Win32_Processor", "UniqueId");
41 if (string.IsNullOrEmpty(retVal)) //If no UniqueID, use ProcessorID
42 {
43 retVal = Identifier("Win32_Processor", "ProcessorId");
44 if (string.IsNullOrEmpty(retVal)) //If no ProcessorId, use Name
45 {
46 retVal = Identifier("Win32_Processor", "Name");
47 if (string.IsNullOrEmpty(retVal)) //If no Name, use Manufacturer
48 {
49 retVal = Identifier("Win32_Processor", "Manufacturer");
50 }
51 //Add clock speed for extra security
52 retVal += Identifier("Win32_Processor", "MaxClockSpeed");
53 }
54 }
55 return retVal;
56 }
57
58 /// <summary>
59 /// BIOS Identifier
60 /// </summary>
61 /// <returns></returns>
62 private static string GetBiosId()
63 {
64 return Identifier("Win32_BIOS", "Manufacturer") + " | " + Identifier("Win32_BIOS", "SMBIOSBIOSVersion")
65 + " | " + Identifier("Win32_BIOS", "IdentificationCode") + " | " + Identifier("Win32_BIOS", "SerialNumber")
66 + " | " + Identifier("Win32_BIOS", "ReleaseDate") + " | " + Identifier("Win32_BIOS", "Version")
67 + " | " + Identifier("Win32_BIOS", "Name");
68 }
69
70 /// <summary>
71 /// Main physical hard drive ID
72 /// </summary>
73 /// <returns></returns>
74 private static string GetDiskId()
75 {
76 return Identifier("Win32_DiskDrive", "Model") + " | " + Identifier("Win32_DiskDrive", "SerialNumber")
77 + " | " + Identifier("Win32_DiskDrive", "Signature") + " | " + Identifier("Win32_DiskDrive", "TotalHeads");
78 }
79
80 /// <summary>
81 /// Motherboard ID
82 /// </summary>
83 /// <returns></returns>
84 private static string GetBaseId()
85 {
86 return Identifier("Win32_BaseBoard", "Model") + " | " + Identifier("Win32_BaseBoard", "Manufacturer")
87 + " | " + Identifier("Win32_BaseBoard", "Name") + " | " + Identifier("Win32_BaseBoard", "SerialNumber")
88 + " | " + Identifier("Win32_BaseBoard", "SKU") + " | " + Identifier("Win32_BaseBoard", "Product");
89 }
90
91 /// <summary>
92 /// Primary video controller ID
93 /// </summary>
94 /// <returns></returns>
95 private static string GetVideoId()
96 {
97 return Identifier("Win32_VideoController", "Name") + " | " + Identifier("Win32_VideoController", "AdapterRAM");
98 }
99
100 /// <summary>
101 /// Return a hardware identifier
102 /// </summary>
103 /// <param name="wmiClass"></param>
104 /// <param name="wmiProperty"></param>
105 /// <returns></returns>
106 private static string Identifier(string wmiClass, string wmiProperty)
107 {
108 string result = string.Empty;
109 System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
110 System.Management.ManagementObjectCollection moc = mc.GetInstances();
111 foreach (System.Management.ManagementObject mo in moc)
112 {
113 //Only get the first one
114 if (string.IsNullOrEmpty(result))
115 {
116 try
117 {
118 result = mo[wmiProperty]?.ToString();
119 break;
120 }
121 catch(Exception e)
122 {
123 LogSingleton.CreateInstance().Error(e, "Window获取硬件信息失败");
124 }
125 }
126 }
127 return result;
128 }
129
130 /// <summary>
131 /// Return a hardware identifier
132 /// </summary>
133 /// <param name="wmiClass"></param>
134 /// <param name="wmiProperty"></param>
135 /// <param name="wmiMustBeTrue"></param>
136 /// <returns></returns>
137 private static string Identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue)
138 {
139 string result = string.Empty;
140 System.Management.ManagementClass mc = new System.Management.ManagementClass(wmiClass);
141 System.Management.ManagementObjectCollection moc = mc.GetInstances();
142 foreach (System.Management.ManagementObject mo in moc)
143 {
144 if (mo[wmiMustBeTrue].ToString() == "True")
145 {
146 //Only get the first one
147 if (string.IsNullOrEmpty(result))
148 {
149 try
150 {
151 result = mo[wmiProperty]?.ToString();
152 break;
153 }
154 catch(Exception e)
155 {
156 LogSingleton.CreateInstance().Error(e,"Window获取硬件信息失败");
157 }
158 }
159 }
160 }
161 return result;
162 }
163 }
Linux 获取设备信息帮助类
1 public class LinuxHelper
2 {
3
4 // sudo dmidecode -t 4 | grep ID | tail -1 // CPUID
5
6 // 系统
7 // sudo dmidecode -s system-serial-number // 查看系统序列号
8 // sudo dmidecode -s system-uuid // 查看系统UUID
9 // sudo dmidecode -s system-product-name // 查看服务器系统型号
10 // sudo dmidecode -s processor-manufacturer | tail -1 // 处理器厂家
11
12 // 主板
13 // sudo dmidecode -s baseboard-product-name // 主板型号
14 // sudo dmidecode -s baseboard-serial-number // 主板序列号
15 // sudo dmidecode -s baseboard-manufacturer // 主板厂家
16
17 /// <summary>
18 /// Get a string Unique Identification code of a computer
19 /// </summary>
20 /// <returns></returns>
21 public static string StringValue()
22 {
23 string cpuID = GetCpuId();
24 string serialNumber = GetSerialNumber();
25 string productName = GetProductName();
26 string processorManufacturer = GetProcessorManufacturer();
27 if (string.IsNullOrWhiteSpace(cpuID) && string.IsNullOrWhiteSpace(serialNumber) && string.IsNullOrWhiteSpace(productName) && string.IsNullOrWhiteSpace(processorManufacturer))
28 {
29 return string.Empty;
30 }
31 return "CPU >> " + cpuID + "\nSerialNumber >> " + serialNumber + "\nProductName >> " + productName + "\nProcessorManufacturer >> " + processorManufacturer;
32 }
33
34
35 /// <summary>
36 /// Get the cpuID
37 /// </summary>
38 /// <returns></returns>
39 private static string GetCpuId()
40 {
41 return ProcessShell("dmidecode -t 4 | grep ID | tail -1");
42 }
43
44 /// <summary>
45 /// SerialNumber
46 /// </summary>
47 /// <returns></returns>
48 private static string GetSerialNumber()
49 {
50 return ProcessShell("dmidecode -s system-serial-number");
51 }
52
53 /// <summary>
54 /// product-name
55 /// </summary>
56 /// <returns></returns>
57 private static string GetProductName()
58 {
59 return ProcessShell("dmidecode -s system-product-name");
60 }
61
62 /// <summary>
63 /// ProcessorManufacturer
64 /// </summary>
65 /// <returns></returns>
66 private static string GetProcessorManufacturer()
67 {
68 return ProcessShell("dmidecode -s processor-manufacturer | tail -1");
69 }
70
71 /// <summary>
72 /// 执行Shell命令
73 /// </summary>
74 /// <param name="shellCmd"></param>
75 /// <returns></returns>
76 private static string ProcessShell(string shellCmd)
77 {
78 string result = string.Empty;
79 try
80 {
81 using Process process = new Process
82 {
83 StartInfo = new ProcessStartInfo("/bin/bash", "")
84 };
85 process.StartInfo.RedirectStandardInput = true;
86 process.StartInfo.RedirectStandardOutput = true;
87 process.StartInfo.UseShellExecute = false;
88 process.Start();
89 process.StandardInput.WriteLine(shellCmd);
90 process.StandardInput.Close();
91 result = process.StandardOutput.ReadToEnd();
92 process.WaitForExit();
93 }
94 catch (Exception e)
95 {
96 LogSingleton.CreateInstance().Error(e, "Linux获取硬件信息失败");
97 }
98 return result;
99 }
补充:方案二在实际测试中,发布在Ubuntu 22.04 TSL版本中获取宿主机信息失败。大致错误为找不到相关文件。
最终采取的方式为在容器中获取授权服务容器的ID,以此作为唯一标识(具体采用哪种方式看自己的实际应用环境)
关键代码
1 /// <summary>
2 /// 获取所有容器信息
3 /// </summary>
4 /// <returns></returns>
5 private static List<ContainerListResponse> GetContainerList()
6 {
7 string apiVersion = Environment.GetEnvironmentVariable("DockerApiVersin", EnvironmentVariableTarget.Process);
8 apiVersion = string.IsNullOrWhiteSpace(apiVersion) ? "1.37" : apiVersion;
9 string result = ProcessShell($"curl --unix-socket /var/run/docker.sock http://172.17.0.1/v{apiVersion}/containers/json");
10 return JsonConvert.DeserializeObject<List<ContainerListResponse>>(result);
11 }
1 IList<ContainerListResponse> containers = GetContainerList();
2 ContainerListResponse containerListResponse = containers.FirstOrDefault(c => c.Names.Any(n => n.Contains("tuguan-server-license")));
3 return containerListResponse?.ID;
// 目录挂载
-v /var/run/docker.sock:/var/run/docker.sock