客户机代理与工作负载模型
说明客户机代理作为客户机长期存活的生命周期权威所承担的职责、工作负载的启动方式、包含语义以及会话重置的处理。
译自microsoft/nvx · doc/design/4.2-guest-agents-and-workload-model.md。技术细节以英文原文为准。
如图 4.2 所示,客户机代理是客户机长期存活的生命周期权威。它们的职责是:
- 初始化运行时文件系统与 cgroup v2。
- 发现并校验固定的虚拟设备。
- 组装工作负载文件系统。
- 应用工作负载身份、隔离与资源策略。
- 在一道放置屏障之后启动工作负载。
- 监管工作负载并回收其后代进程。
- 当宿主发布客户机的监听器时予以通告。
- 参与捕获与恢复后的修复。
- 卸载资源并报告最终的工作负载状态。
图 4.2. 客户机代理与工作负载之间的关系。
图中节点(按自上而下的顺序):
- Linux 内核(Linux kernel)
- 客户机代理(Guest agents):位于分层根中,处于工作负载的命名空间之外
- 组装完成的工作负载根(Assembled workload root)
- 身份、能力与 cgroup,以及命名空间(Identity, capabilities, and cgroups, plus namespaces):位于分层根中
- 工作负载进程树(Workload process tree)
节点之间的关系(→ 表示单向依赖,箭头旁的文字说明该依赖所承载的行为):
| 源节点 | 关系 | 目标节点 |
|---|---|---|
| Linux 内核 | → | 客户机代理 |
| 客户机代理 | → | 组装完成的工作负载根 |
| 客户机代理 | → | 身份、能力与 cgroup,以及命名空间 |
| 组装完成的工作负载根 | → | 工作负载进程树 |
| 身份、能力与 cgroup,以及命名空间 | → | 工作负载进程树 |
| 客户机代理 | →(监管并回收子进程,supervise and reap) | 工作负载进程树 |
在一次性生命周期中,客户机代理启动一个由非机密内核命令行令牌配置的工作负载,并在它退出时关闭 VM。在托管生命周期中,客户机代理保持驻留,按请求通过经认证的控制通道依次运行工作负载,每次一个。两种模式都保持同样的分离:工作负载始终是某个客户机代理的子进程。客户机代理绝不把自己替换为工作负载,因为监管、孤儿回收和控制服务都依赖它们继续存活。
4.2.1 执行启动
对于每次托管执行,客户机代理把该次执行的参数通过一个密封的匿名文件传递给一个启动辅助程序,该辅助程序只在工作负载身份被应用之后才运行。该辅助程序进入所请求的工作目录,或 /,在请求没有替换环境时设置 PWD,应用该环境,并执行工作负载。工作目录只被打开一次并保持打开,因此即使其路径在此期间被重命名、替换或重新指向,工作负载也会在被检查过的那个目录中启动。如果工作负载无法进入该目录,本次执行将失败,而不是在其他位置运行。
4.2.2 收敛
在无块托管根中,每次执行都运行在自己的 cgroup 里。当工作负载的第一个进程退出时,客户机代理会杀死该 cgroup 中残留的每个进程,并作为客户机的 init 进程回收它所继承的孤儿。只有在该 cgroup 的进程数归零之后,它才报告结果。 进程控制器会一直对一个进程计费,直到它被回收,因此没有任何工作负载进程——即使是僵尸进程——会比它的执行活得更久。杀死失败、计数不可读或收敛超时都会被报告为收敛失败,而此后每次执行都会先回收先前工作负载留下的内容,并核实该 cgroup 为空,因此会话重置无法绕过这项检查。
一个指明正在运行执行的取消请求会杀死工作负载,而针对已经完成的执行的取消请求则被忽略。会话重置意味着能够观察该工作负载的宿主会话已经消失:客户机代理杀死工作负载,不再为它发送任何内容,并确认新的纪元,因此下一个会话不会去等待一个被遗弃的工作负载。