Guest Agent:沙箱里面那个 agent 在干什么
cube-agent 是运行在沙箱 VM 内部的守护进程,负责在 guest 侧完成环境准备、容器管理和命令执行。Shim 通过 tRPC over vsock 下发指令,agent 在 guest 内执行真正的操作。
Agent 的身份
agent 在 VM 内有双重身份:
- PID 1(init 模式):如果 agent 以 PID 1 启动,它会先完成系统初始化(挂载 cgroups、设置 hostname),然后再启动 tRPC 服务
- 普通进程模式:如果已有其他 init 进程,agent 直接启动 tRPC 服务
启动流程
main() 入口
agent 的入口在 agent/src/main.rs:
rust
fn main() -> Result<()> {
let args = AgentOpts::parse();
if let Some(SubCommand::Init {}) = args.subcmd {
rustjail::container::init_child(); // 容器 init 子进程
exit(0);
}
if let Some(SubCommand::Exec {}) = args.subcmd {
cube::rootfs::do_exec_mount(); // exec 挂载处理
exit(0);
}
let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
rt.block_on(real_main())
}real_main() 流程
start_sandbox()
rust
async fn start_sandbox(logger, config, init_mode, tasks, shutdown) {
// 1. 创建 Sandbox 结构体
let s = Sandbox::new(logger)?;
// 2. 启动信号处理(SIGCHLD 回收子进程)
let signal_handler_task = tokio::spawn(setup_signal_handler(logger, sandbox, shutdown));
// 3. 启动 uevent 监听(设备热插拔)
let uevents_handler_task = tokio::spawn(watch_uevents(sandbox, shutdown));
// 4. 启动 tRPC 服务器(监听 vsock)
let mut server = rpc::start(sandbox, config.server_addr)?;
server.start().await?;
// 5. 等待 shutdown → 关机
rx.await?;
unsafe { libc::reboot(libc::LINUX_REBOOT_CMD_POWER_OFF) };
}Sandbox 初始化
Sandbox 结构体(agent/src/sandbox.rs)是 agent 的核心数据结构,管理所有容器和共享资源:
rust
pub struct Sandbox {
pub id: String,
pub hostname: String,
pub containers: HashMap<String, LinuxContainer>,
pub network: Network,
pub mounts: Vec<String>,
pub storages: HashMap<String, u32>, // storage 引用计数
pub shared_utsns: Namespace, // 共享 UTS 命名空间
pub shared_ipcns: Namespace, // 共享 IPC 命名空间
pub sandbox_pidns: Option<Namespace>, // 共享 PID 命名空间
pub rtnl: Handle, // netlink 句柄
pub pcimap: HashMap<pci::Address, pci::Address>, // PCI 地址映射
// ...
}当 Shim 调用 CreateSandbox RPC 时,agent 执行以下初始化:
rust
// rpc.rs — create_sandbox
async fn create_sandbox(&self, req: CreateSandboxRequest) {
// 1. 配置网络接口
for i in interfaces { sandbox.rtnl.update_interface(&i) }
sandbox.rtnl.update_routes(routes);
sandbox.rtnl.add_arp_neighbors(arps);
// 2. 设置 hostname
sandbox.hostname = req.hostname;
sandbox.running = true;
// 3. 创建共享命名空间
sandbox.setup_shared_namespaces(); // IPC + UTS
// 4. 挂载存储
add_storages(req.storages); // virtiofs、pmem、disk 等
// 5. 配置 DNS
setup_guest_dns(req.dns);
}gRPC 服务实现
AgentService 实现了 protocols::agent_ttrpc::AgentService trait(agent/src/rpc.rs),提供完整的容器生命周期管理。
AgentService 核心方法
| 方法 | 功能 |
|---|---|
create_sandbox | 初始化沙箱环境(网络、存储、DNS) |
destroy_sandbox | 销毁沙箱,触发关机 |
create_container | 创建容器(设备、存储、OCI spec → LinuxContainer) |
start_container | 启动容器(exec + OOM 监控) |
remove_container | 销毁容器,清理资源 |
exec_process | 在容器中执行新进程 |
signal_process | 向进程发送信号 |
wait_process | 等待进程退出,返回 exit code |
write_stdin / read_stdout / read_stderr | 流式 I/O |
update_interface / update_routes | 网络热更新 |
online_cpu_mem | 热插拔 CPU/内存 |
CreateContainer 流程详解
这是 agent 侧最复杂的操作:
关键代码片段
rust
// rpc.rs — do_create_container
async fn do_create_container(&self, req: CreateContainerRequest) -> Result<()> {
// 1. 添加设备
add_devices(&req.devices, &mut oci, &self.sandbox).await?;
// 2. 挂载存储
let m = add_storages(logger, req.storages, self.sandbox, Some(cid)).await?;
sandbox.container_mounts.insert(cid, m);
// 3. 更新命名空间
update_container_namespaces(&s, &mut oci, use_sandbox_pidns)?;
// 4. 设置 bundle
let olddir = setup_bundle(&cid, &mut oci, req.custom_files)?;
// 5. 创建 LinuxContainer
let opts = CreateOpts { spec: Some(oci), no_pivot_root: s.no_pivot_root, ... };
let mut ctr = LinuxContainer::new(&cid, CONTAINER_BASE, opts, &logger)?;
// 6. 创建并启动进程
let mut p = Process::new(&logger, &oci.process, &cid, true, pipe_size)?;
p.open_io(&logger, None)?;
ctr.start(p).await?;
// 7. 注册到 sandbox
s.add_container(ctr);
Ok(())
}ExecuteProcess 实现
exec_process 允许在已运行的容器中执行新命令(如 kubectl exec):
rust
// rpc.rs — do_exec_process
async fn do_exec_process(&self, req: ExecProcessRequest) -> Result<()> {
let sandbox = self.sandbox.lock().await;
// 1. 获取目标容器
let ctr = sandbox.get_container(&cid)?;
// 2. 创建新进程
let mut p = Process::new(&logger, &ocip, &exec_id, false, pipe_size)?;
// 3. 打开 I/O(pipe 或 unix socket)
if req.runtime_unix_addr.is_empty() {
p.open_io(&logger, None)?; // pipe 模式
} else {
p.open_io(&logger, Some(&req.runtime_unix_addr))?; // socket 模式
}
// 4. 运行进程
ctr.run(p).await?;
Ok(())
}信号处理
agent 作为 PID 1(或 subreaper),负责回收所有子进程(agent/src/signal.rs):
rust
pub async fn setup_signal_handler(logger, sandbox, mut shutdown) {
set_subreaper(true)?; // 注册为 subreaper
let mut sigchild_stream = signal(SignalKind::child())?;
loop {
select! {
_ = shutdown.changed() => break,
_ = sigchild_stream.recv() => {
// 遍历所有已退出的子进程
let result = wait::waitpid(Some(Pid::from_raw(-1)),
Some(WaitPidFlag::WNOHANG | WaitPidFlag::__WALL));
// 找到对应 Process,设置 exit_code
let p = sandbox.find_process(raw_pid);
p.exit_code = ret;
let _ = p.exit_tx.take(); // 通知 WaitProcess RPC
p.notify_term_close();
}
}
}
}Console 管理
agent 提供 debug console 功能(agent/src/console.rs),允许通过 vsock 连接到 guest 内的 shell:
rust
pub async fn debug_console_handler(logger, port, mut shutdown) {
let shell = find_shell(); // /bin/bash 或 /bin/sh
if port > 0 {
// vsock 模式:监听 vsock 端口
let listenfd = socket::socket(AddressFamily::Vsock, ...);
let addr = SockAddr::new_vsock(VMADDR_CID_ANY, port);
socket::bind(listenfd, &addr)?;
// 接受连接,fork shell 进程
loop {
select! {
conn = incoming.next() => {
tokio::spawn(run_debug_console_vsock(logger, shell, stream));
}
}
}
} else {
// 串口模式:直接连接 /dev/console
run_debug_console_serial(shell, fd).await;
}
}shell 进程通过 PTY 与客户端连接,实现了完整的终端交互。
网络配置
agent 的网络模块(agent/src/network.rs)负责 DNS 配置:
rust
pub fn setup_guest_dns(logger, dns_list: Vec<String>) -> Result<()> {
// 1. 将 DNS 写入临时文件
let content = dns_list.join("\n");
fs::write("/run/cube-containers/sandbox/resolv.conf", &content)?;
// 2. bind mount 到 /etc/resolv.conf
mount::mount(
Some("/run/cube-containers/sandbox/resolv.conf"),
"/etc/resolv.conf",
Some("bind"), MsFlags::MS_BIND, None
)?;
Ok(())
}网络接口和路由的管理则通过 netlink(rtnl)在 create_sandbox RPC 中完成。
小结
agent 在沙箱中的角色可以概括为:
| 职责 | 说明 |
|---|---|
| 系统初始化 | PID 1 模式下挂载文件系统、设置 cgroups |
| 沙箱管理 | 创建共享命名空间、配置网络/存储/DNS |
| 容器管理 | 通过 rustjail 创建和管理 OCI 容器 |
| 进程执行 | 支持 exec 新进程、信号发送、流式 I/O |
| 资源监控 | OOM 监控、CPU/内存热插拔 |
| 调试支持 | vsock debug console |