简介:FOG开源项目的跨平台计算机管理客户端源码,基于C#开发,面向系统管理员、IT运维人员以及需要二次定制FOG客户端的开发者。该源码能让Linux、macOS和Windows机器接受远程服务器的统一管控,功能涵盖自动登录与登出、自动更新、电源管理、主机改名、AD域或Samba目录加入、管理单元包下发、任务重启、用户行为追踪以及多种打印机部署等。资源包共304个文件,大小约7.7MB,主体为121个cs主程序源码、47个resx界面资源、22个dll依赖库、17个config配置及11个csproj工程文件,并包含macOS的Xcode工程文件、Linux的systemd服务配置和Windows安装工程脚本,目录结构完整,方便逐模块阅读。当前已有168人学习浏览。对研究远程设备管理协议、客户端模块化架构或基于FOG做企业级二次开发的人来说,这份源码提供了可编译、可扩展的完整参考实现,尤其适合需要理解跨平台客户端底层交互与构建流程的读者。 不用把 FOG 想得太玄乎,我最早接触它是因为一个特别拧巴的场景:公司机房里有三百多台电脑,操作系统五花八门,Windows、Ubuntu、还有一小批 macOS。每次重装系统、改主机名、下发软件,运维同事都靠一台一台跑,赶上月底审计光资产盘点就得加一周班。fog-client 就是在这个背景下进了我的视野——它是 FOG 项目里的跨平台计算机管理客户端,负责让每台终端主动跟服务器保持联系,接收部署、采集、唤醒这类任务指令。这篇东西我不会把 FOG 整个文档给你抄一遍,而是从“客户端”这个角色切入,讲讲它到底解决了什么问题,跨平台是怎么实现的,以及真实部署时那些文档里不会写的坑。
1. fog-client 在整个管理链路里的位置
1.1 一台机器从裸机到被纳管的完整流程
很多人一上来就找 fog-client 的安装包,却不知道它只是整条链路里最末端的一块。FOG 这套体系里,核心是服务器端:它负责提供 PXE 启动服务、镜像存储、DHCP 辅助、任务调度和 Web 管理界面。而 fog-client 装在每一台需要被管理的电脑上,负责跟服务器通信,执行服务器下发的任务。
一台电脑从裸机到被纳管,大致走这样的流程:
- 机器通过 PXE 网络启动,从 FOG 服务器拉取引导镜像。
- 管理员在 Web 界面给这台机器关联一个镜像模板,执行“部署”任务。
- 部署完成后,系统里被写入 fog-client,并配置好服务器地址和密钥。
- fog-client 启动后向服务器上报主机信息,服务器端能看到这台机器“已注册”。
- 之后这台机器就进入日常轮询状态,等待任务指令。
我周围有些朋友第一次用的时候以为 fog-client 装完就完事了,结果发现服务器上始终看不到主机在线,最后排查下来是客户端配置文件里的服务器地址写错了,或者密钥不匹配。客户端不是“装上就自动被管理”,它更像一个哨兵,先要让服务器确认“你是谁”,之后才认账。
1.2 客户端能触发的核心任务类型
fog-client 并不是一个只会“挂着上报在线状态”的简单服务。以我实际使用过的版本为例,它支持的任务大致包括这些:
| 任务类型 | 作用 | 典型触发场景 |
|---|---|---|
| 镜像部署 | 从服务器拉取镜像并覆盖本机系统盘 | 新机器初始化、系统崩溃后恢复 |
| 镜像捕获 | 把当前系统盘打包上传到服务器 | 制作标准镜像模板 |
| 软件分发 | 从服务器拉取安装包并静默安装 | 批量装办公软件、安全补丁 |
| 打印机管理 | 根据服务器下发的配置添加打印机驱动和连接 | 新员工入职时自动配置打印 |
| 远程唤醒 | 让休眠或关机的机器通过 Wake-on-LAN 启动 | 夜间批量维护、补丁推送 |
| 主机名修改 | 按命名规范改写本机主机名 | 资产编号、重装后标准化 |
| 电源管理 | 定时关机、注销、重启 | 下班后统一关机节能 |
不同分支的客户端实现可能略有差异,但核心思路都是“服务器端建任务,客户端轮询时领任务,然后执行并回执结果”,这一点是统一的。
1.3 为什么“跨平台”这件事不是营销话术
FOG 最早期的客户端其实只支持 Windows。那时候 Linux 机器要接入 FOG,只能靠 PXE 部署完镜像后“裸奔”,日常管理根本摸不到。后面随着环境越来越混合,才有了跨平台客户端的诉求。
我实际对比过原版 Windows 服务和后来做的跨平台版本。真正的跨平台不是“把代码用 Java 重写一遍”那么简单。客户端要跨平台,至少要处理三件麻烦事:服务怎么托管、系统信息怎么采集、以及任务执行时怎么调用不同系统的能力。Windows 用服务控制管理器,Linux 用 systemd 或 SysV,macOS 用 launchd,这三者的启停、开机自启、日志处理逻辑完全不一样。跨平台版本能把这三套都统一成一种“服务抽象”,这块工作才是核心价值所在。
2. 客户端跨平台的技术底子:抽象与轮询
2.1 为什么客户端要主动轮询而不是服务端推送
跨平台客户端第一个要讲清楚的东西是通信模型。FOG 这套架构里,客户端和服务器之间默认是“客户端主动轮询”,不是服务器推送。原因是现实网络环境太复杂了:终端在 NAT 后面,没有公网地址;笔记本经常换网络;DHCP 重新分配 IP 之后服务器根本找不到它;再加上很多公司防火墙策略默认挡入站。这种情况下如果让服务器主动连客户端,大概率大量终端失联。
客户端主动轮询的优势就是能穿透这层不确定性。它只要保证“能访问到服务器地址”,就能定期把状态送到服务器。实际轮询间隔我一般控制在 30 到 60 秒。太短了,几百台机器同时打过来对服务器压力很大;太长了,用户在 Web 界面点“重启”之后半天没反应。
轮询请求里通常带上主机标识、MAC 地址、当前任务状态、客户端版本号、运行结果回执等字段。服务器收到后返回一条“当前有没有任务、任务是什么、参数是什么”的指令。客户端根据指令执行,执行完下次轮询再把结果交上去。
2.2 把“平台差异”关进抽象层
跨平台客户端最核心的设计是抽象层。如果代码里到处写if Windows ... else if Linux ...,后期维护会非常痛苦。我通常会让客户端按下面几个维度做抽象:
- 服务生命周期:Windows 作为服务运行,Linux 用 systemd unit,macOS 用 launchd。抽象成一个
ServiceController,统一处理启动、停止、重启。 - 系统信息采集:Windows 用 WMI 拿序列号、主板型号,Linux 读
/sys/class/dmi/id和dmidecode,macOS 用system_profiler和ioreg。上层只关心“拿到一个标准结构化的机器信息”。 - 任务执行器:镜像部署、软件分发这类操作在三个平台上的执行方式差别很大,抽象成
TaskExecutor,每个平台各自实现。 - 配置存储:Windows 用注册表,Linux 用
/etc/fog-client/config.json,macOS 用 plist。配置加载层做统一封装。
这样做的好处是,新增一个平台时,只需要实现抽象接口,不用碰业务逻辑。我见过有些团队维护跨平台客户端时,把平台判断写得到处都是,最后加一个平台支持要改十几个文件,还特别容易改出 bug。
2.3 轮询协议里的关键字段
轮询协议里最容易被忽视的是“回执”字段。很多新手以为客户端把状态发给服务器就完事了,实际上服务器能不能知道任务执行成功了,全靠回执。
一次比较完整的轮询请求一般包含这些内容:
{ "hostname": "asset-0421", "macAddress": "3c:ec:ef:12:34:56", "os": "linux", "kernelVersion": "5.15.0", "taskingEnabled": true, "powerState": "on", "completeStatus": "ready" }服务器返回的内容可能是空任务,也可能是具体指令:
{ "task": "hostname", "params": { "newHostname": "asset-0999" } }执行完之后,客户端要把结果写进下一次轮询的请求里,告诉服务器“这个任务跑成功了”或者“失败在哪一步”。如果没有这套回执,服务器界面里任务会一直挂着,管理员看到的就是一个“假死”状态。
3. 手工部署 fog-client 的完整记录
3.1 部署前要准备的信息
我每次给一批新机器装客户端,都会先把下面几项信息确认好,缺一不可:
- 服务器地址:客户端要能解析并访问到的 FOG 服务器地址。
- 通信密钥:客户端和服务器握手时用的预共享密钥,不一致会导致注册失败。
- 加密证书指纹:如果服务器启用了 HTTPS,首次通信时会校验指纹,防止中间人。
- 轮询间隔:建议根据终端总量折算,我一般先用 60 秒跑一天,观察服务器负载再决定要不要调短。
- 代理设置:如果公司网络走 HTTP 代理,需要提前确认客户端是否支持代理配置,否则轮询请求会卡在代理层。
这些信息在正式部署前最好做成一份清单发给网络和服务器团队成员确认,避免到了装机现场才发现地址不通、端口被封这种低级问题。
3.2 Windows 安装细节
Windows 平台下安装 fog-client 相对省事,因为原版生态比较成熟,安装包里可以直接配置服务器地址。但有几个细节我吃过亏:
第一,服务登录账户不要用普通用户。客户端要执行镜像部署、修改主机名这类高权限操作,服务账户至少要给到本地 SYSTEM 权限。如果用普通账户,任务执行时可能会因为权限不足直接失败。
第二,Windows 防火墙默认会拦截外联端口。很多部署失败的案例其实是防火墙把客户端的出站请求拦了。我一般在组策略里放行客户端程序的所有出站连接,或者至少放行到 FOG 服务器的端口。
第三,如果机器上有安全软件,记得把客户端的安装目录加白名单。我见过杀毒软件把客户端的核心执行文件当木马隔离的,服务器端表现就是“在线状态一切正常,但任务始终执行不了”,排查了半天才发现是隔离区里躺着客户端文件。
3.3 Linux 的 systemd 单元文件示例
Linux 上如果客户端提供了 systemd 单元文件,部署就很干净。下面这个示例是我在 Ubuntu 服务器和桌面版上常用的配置:
[Unit] Description=FOG Client Service After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/fog-client --config /etc/fog-client/config.json Restart=on-failure RestartSec=10 User=root [Install] WantedBy=multi-user.target需要注意两点:一是务必加上network-online.target依赖。我曾经跳过这一步,结果机器开机时网络还没就绪,客户端就启动去轮询了,连续失败好几次后自己退了。二是Restart=on-failure很关键。客户端长驻进程偶尔会因为网络波动退出,有了自动重启能减少很多“失联”情况。
装完之后执行两个命令:
systemctl daemon-reload systemctl enable --now fog-client然后看一眼状态是不是 active,再去服务器端确认主机有没有注册成功。我习惯用journalctl -u fog-client -f盯前几分钟日志,确认轮询请求发出去了、服务器有响应,再往下进行。
3.4 macOS 的 launchd plist 示例
macOS 上部署稍微绕一点,因为 launchd 的配置格式和 systemd 差别很大。一个比较常见的 plist 长这样:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.fog.client</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/fog-client</string> <string>--config</string> <string>/etc/fog-client/config.json</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/var/log/fog-client.log</string> <key>StandardErrorPath</key> <string>/var/log/fog-client.err.log</string> </dict> </plist>然后加载服务:
sudo launchctl load /Library/LaunchDaemons/com.fog.client.plistmacOS 上最容易出的问题是配置文件路径和权限。launchd 以 root 运行,如果配置文件放在当前用户的 home 目录下,服务根本读不到。我一般统一放在/etc/fog-client/下面,权限设成 600。
4. 上线半年后印象最深的五个坑
4.1 时间偏差导致认证失败
第一次批量部署时,我遇到过一个特别隐蔽的问题:一半客户端注册成功,另一半全部报认证失败。当时排查了很久,最后发现是这批机器的主板电池没电,系统时间慢了十几分钟,而客户端和服务器之间的密钥校验带了时间戳或者有效期概念,时间偏差一大,服务器就把请求当作重放攻击丢掉了。
解决方案分两步:先给所有客户端配上 NTP 同步,确保时间基本一致;然后如果客户端支持时间偏移容忍参数,就把窗口稍微放宽一点。这里我提醒一句,如果你打算用带时间戳的认证机制,一定要先保证全网的 NTP 是通的,不然这个“安全加固”反而会变成大面积故障源。
4.2 多网卡机器上报了错误的主网卡
机房里有不少服务器是双网卡甚至多网卡的。fog-client 默认取“当前默认路由关联的网卡”作为主网卡上报,但有些机器的网络配置比较特殊,默认路由走了管理网段,而 FOG 服务器在业务网段,结果服务器端看到的主机 MAC 地址完全是另一块网卡的,任务下发后客户端根本收不到。
这个问题的排查链路是这样的:先在服务器端看主机注册信息里的 MAC 地址,跟机器实际网卡逐一比对,再在客户端侧手动执行一次信息采集命令看它上报的是哪块网卡。解决方式是在客户端配置里显式指定要上报的网卡(按 MAC 或接口名),不要依赖自动选择。这属于“配置多写一行,排查少花两天”的典型例子。
4.3 系统服务环境里执行任务的坑
fog-client 作为系统服务运行的时候,运行环境跟用户桌面完全不是一个世界。Windows 下 SYSTEM 账户访问网络驱动器、访问用户证书存储、调用需要桌面会话的应用都有可能失败;Linux 下 root 服务虽然权限够大,但拿不到用户在桌面环境里设置的代理、环境变量和密钥环。
我踩过最实在的一个坑是软件分发任务。分发脚本里有个步骤需要访问用户级的挂载目录,服务账户跑的时候根本没权限,任务就挂在那边了。正确的做法是所有任务执行器写清楚“运行环境要求”,比如依赖用户会话的任务尽量用独立触发机制,而不是全塞进服务轮询里。服务只做全局性、系统级的任务,涉及用户态的部分要单独设计。
4.4 自签名证书造成轮询失败
测试环境里直接用 HTTP 或者自签名 HTTPS 都能跑,但在生产环境如果启用了 HTTPS,证书问题就躲不掉了。有一回 FOG 服务器换了一次证书,结果所有客户端的轮询全部失败,服务器端看到的是一片“离线”。
根本原因是客户端第一次连接时,默认把服务器的证书指纹或者根证书记在了本地配置里,服务器换了证书,客户端校验不通过,就直接放弃通信了。处理办法不是让客户端跳过证书校验(那样安全风险太大),而是建立证书更新的标准流程:先在服务器端发一个“重新校验证书”的任务,或者批量更新客户端的信任指纹,再切换新证书。这类操作最好在维护窗口里做,同时准备好一键回滚方案。
4.5 轮询频率和任务时机的权衡
轮询间隔设多少,直接影响到服务器的承接压力。我当时图省事给所有客户端统一设了 20 秒,结果终端量从三百台涨到五百台之后,服务器的应用日志里开始频繁出现连接超时,Web 界面打开都变得迟钝。
后来我把轮询间隔按终端类型分了三档:普通办公机 60 秒,需要快速响应任务的测试机 30 秒,服务器本身可以放到 90 秒。同时把服务器端的并发连接参数和数据库连接池调大了一圈。上线观察了两周,整体压力就降下来了。
5. 把 fog-client 卷进自动化体系的几个方向
5.1 批量安装客户端
手工一台台装客户端只能用在几十台的小环境。批量部署其实有很成熟的路径:Windows 走 GPO 或软件分发系统,Linux 走 Ansible 或 Puppet,macOS 走描述文件和 MDM。
我自己常用的 Ansible 角色大致长这样:
- name: Install fog-client apt: name: fog-client state: present - name: Deploy config copy: src: "{{ fog_client_config }}" dest: /etc/fog-client/config.json mode: "0600" - name: Start service systemd: name: fog-client state: started enabled: true批量部署时最容易翻车的是配置文件分发环节。很多机器在模板里的主机名是一样的,客户端注册时如果以主机名为唯一标识,就会出现互相顶替的问题。所以我一般注册标识用 MAC 地址或主板序列号,确保每台机器在服务器端的身份是唯一的。
5.2 用心跳数据做监控
fog-client 不停上报的心跳数据本身就是很有价值的监控源。我后来写了一个小脚本,定时读取服务器端的数据,检查三件事:在线率是否异常下降、客户端版本是否过旧、最近一次轮询时间是否超过阈值。
这个脚本逻辑不复杂,也就是从服务器的接口拉状态,然后做阈值判断,有问题就发告警到运维群。部署半年之后,这套机制帮我提前发现了两次网络分区故障——客户端大面积离线往往比业务系统告警更早暴露问题,因为客户端在每一台终端上,覆盖面足够大。
5.3 扩展自定义采集脚本
客户端如果支持自定义任务或外部脚本扩展,能做的就不止“部署镜像”和“改主机名”这些内置功能了。我实际用过的做法是写一个资产采集脚本,在轮询任务里触发,然后把主机的内存型号、硬盘健康状态、已安装软件列表收集起来回传给服务器。
这样做的收益很大:以前资产盘点要逐台登录去查配置,现在服务器端直接就能导出表格。关键点是脚本执行时间不能太长,我一般控制在十秒以内,避免占用轮询窗口太久。
5.4 安全加固:最小化密钥和通信
客户端密钥如果长期不改,或者所有机器共用一把密钥,一旦泄露,攻击者就能在终端上伪装指令、干扰任务下发。我这边做了几件事:一是管理端和客户端之间务必启用 HTTPS;二是密钥定期轮换,并在客户端配置里支持“多密钥”方式,让新旧密钥有一段共存期,避免一次性大面积断连;三是服务器端对客户端的访问做来源限制,比如只放行已知网段。
日志审计也要开。客户端侧记录每次轮询的结果和错误信息,服务器侧记录每次任务下发的操作人和时间。真出了问题,顺着日志往回翻要比瞎猜高效得多,这在多管理员协作的场景里尤其重要。
如果你正准备在现有环境里引入 fog-client,我个人最大的建议是先拿一个隔离网段做小规模试点,把注册、任务下发、软件分发、轮询异常这几个基础场景全部跑通,再扩大到全量。这个过程里积累下来的部署脚本和排障清单,会比任何文档都值钱。另外,多关注一下客户端和服务器的版本兼容性,跨版本升级之前最好在测试环境先验证一轮,不然等生产环境大面积报错再回头处理,压力会大很多。
本文还有配套的精品资源,点击获取