dbs结项报告
1. 📽 项目实现方案研究:
实现方案给出是为了提供可复现性
配置 | 描述 |
---|---|
处理器 | AMD® Ryzen 7 4800h with radeon graphics × 16 |
内存 | 32g |
显卡 | NVIDIA Corporation TU117M [GeForce GTX 1650 Ti Mobile] / NVIDIA GeForce GTX 1650 Ti/PCIe/SSE2 |
OS | Ubuntu 22.04 LTS |
rustc版本 | rustc 1.62.1 |
1.1 🎲 参考目标: firecracker
1.1.1 构建firecracker
cargo的使用文档: https://doc.rust-lang.org/cargo/index.html
如果需要编译其他平台的crate, 可能还需要用rustup添加对应的toolchain, rustup的使用文档: https://rust-lang.github.io/rustup/index.html
- 拉取源码:
1 |
|
配置依赖
构建firecraker依赖于
Docker
, 我的个人博客上docker的安装笔记可供参考: Ubuntu上使用Docker
1 |
|
- 构建firecracker
1 |
|
1.1.2 使用firecracker
创建虚拟机
参考资料: https://github.com/firecracker-microvm/firecracker/blob/main/docs/getting-started.md
事实上, 报告的最终目的目的并不在于描述构建firecraker或者使用dbs之类的方式, 而是借助对firecraker的了解来辅助使用命令行参数从
DBS
中启动一个虚拟机, 因此文档叙述不以线性进行, 将进行跳转, 体现思考的过程.
使用firecracker创建虚拟机, 需要这些内容:
API socket
1 |
|
Linux kernel
rootfs
因此当我们开始了解DBS
时, 需要关注DBS
已经具备和不具备的要素,
1.2 🦌 更进一步
上面的步骤不存在任何困难, 但我们并不关心是否创建好虚拟机, 而关注在firecracker一开始是如何创建一个虚拟机
1.2.1 VMM的对外接口
定位到
src/firecracker/src/main.rs
, 关注在api-sock
命令行参数
firecracker对虚拟机进行配置主要分为两种形式:
- HTTP API的请求方式进行配置(也可以同时指定配置文件)
- 通过指定配置文件的方式进行配置
1 |
|
1 |
|
对于DBS而言, 项目最初目标是通过命令行参数进行启动, 而不需要kata的环境, 因此我们可以关注在使用配置文件创建虚拟机的代码逻辑部分, 因此可以关注函数
run_without_api
VmResources::from_json
1.2.2 rootfs和Linux kernel的使用方式
要了解rootfs和Linux kernel如何被使用了, 需要从
main.rs
的run_without_api
中, 深入到build_microvm_from_json
- kernel被以
VmmConfig::block_devices
的形式传入 - rootfs作为
VmmConfig::block_devices
配置的一部分进行传入 - 至于启动虚拟机等命令, 则可以HTTP API形式, 传递给
api_server/src/lib.rs
, 接受请求/actions
的参数, 并启动配置好的虚拟机
至此, 我们可以带着疑问, 边开始实现我们的项目需求, 边浏览了解firecracker是如何实现的.
2. 🔨 阶段一: 创建命令行前端接口
由于rust编程经验不多, 因此尽可能使用第三方库, 在命令行解析上, 选用clap (Dual-licensed under
Apache 2.0
orMIT
, 对于DBS而言, 正好可以用Apache 2.0
)
clap
的使用文档: https://docs.rs/clap/latest/clap/
2.1 🍨 命令行配置项
从kata-containers的
runtime-rs
分支中, 可以在dragonball::vm::VmConfigInfo
中看到DBS使用的虚拟机配置.
1 |
|
使用clap
定义命令行参数后,具体内容如下:
arguments | required | default value | description |
---|---|---|---|
rootfs | true | - | The path to rootfs image. |
kernel-path | true | - | The path of kernel image (Only uncompressed kernel is supported for Dragonball). |
log-file | false | "dbs-cli.log" | The path to log file |
log-level | false | "Info" | The logging level. |
boot-args | false | console=ttyS0 tty0 reboot=k debug panic=1 pci=off root=/dev/vda1 | The boot arguments passed to the kernel. |
is-root | false | true | Decide the device to be the root boot device or not. |
is-read-only | false | false | The driver opened in read-only or not. |
vcpu | false | 1 | The number of vcpu to start. |
max-vcpu | false | 1 | The max number of vpu can be added. |
cpu-pm | false | 0 | vpmu support level. |
threads-per-core | false | 1 | Threads per core to indicate hyper-threading is enabled or not. |
cores-per-die | false | 1 | Cores per die to guide guest cpu topology init. |
dies-per-socket | false | 1 | Dies per socket to guide guest cpu topology. |
sockets | false | 1 | The number of sockets. |
mem-type | false | shmem | Memory type that can be either hugetlbfs or shmem. |
mem-file | false | `` | Memory file path. |
initrd-path | false | None | The path of initrd. |
2.2 参数相关的注意事项:
整个命令行参数最为关键的三个参数是rootfs
, kernel-path
和 boot-args
. 由于从命令行终端进入vm的console采用的方式是将ttySx的输出重定向到当前终端的stdout, 因此需要确保kernel支持serial console并且rootfs在构建时需要配置好启动之后能够启动对应终端, 否则将无法进入vm. 对于boot-args
, console=xx
和root="dev/vda1"
是根据对应rootfs的设置来的, 因此虽然不是必须给出的参数, 但是需要自行查看是否需要有所修改。
3. 🔥 阶段二: 项目实现思路及问题解决
3.1 命令行参数:
根据firecraker和kata-container现有代码,确定启动虚拟机必备参数后,便可直接用clap
提供的一些derive
来定义命令行参数,参数项见上表,暂无问题出现。
3.2 进入vm终端:
基于现有kata-container的一些创建vm、启动vm、创建设备等API,只需进行简单的修改调用即可完成。但是最后在IO上出现问题,具体可以描述为创建vm的标准输入输出终端时,com1
未将输出重定向到stdout,导致无法与vm进行交互。
解决方法,为com1
设置输出流并重定向到stdout
:
1 |
|
3.3 基本原理: serial console
- Linux Serial Console — The Linux Kernel documentation
- Serial HOWTO: Serial Port Devices /dev/ttyS2, etc. (tldp.org)
下图为串口对应终端名以及IO地址
kata-container
在runtime-rs
分支下,com2被用来输出日志信息(同时也将dmesg也写入logger了),因此只能使用设备com1连接到对应的终端。
console=device,options
用来指定终端的输出,该参数放在kernel的启动参数中。
device
可能的值为:
1
2
3
4
5
tty0 for the foreground virtual console
ttyX for any other virtual console
ttySx for a serial port
lp0 for the first parallel port
ttyUSB0 for the first USB serial device
在kata-container
仓库runtime-rs
分支的现有代码下,已经有通过socket进入serial console的代码,但是从命令行将stdio重定向到虚拟机serial console的代码却不能达到正常预期。在通过反复浏览代码,通过3.2
中的修改,解决了无法创建stdio console的问题,再加上此前实现的命令行参数解析,整个流程就已经完成了。
4. 项目成果展示:
4.1 本地开发仓库:
仓库地址: https://github.com/QiliangFan/kata-containers
分支:runtime-rs
4.2 pull request:
4.3 运行示例与结果截图:
为了结果易于复现,使用了firecraker项目的kernel和rootfs(方便用户下载, 当然其他支持serial console的内核和rootfs也可以),如需运行此CLI,可前往firecracker/getting-started.md at main · firecracker-microvm/firecracker (github.com)下载并使用
1 |
|
4.4 可通过命令行配置的参数:
arguments | required | default value | description |
---|---|---|---|
rootfs | true | - | The path to rootfs image. |
kernel-path | true | - | The path of kernel image (Only uncompressed kernel is supported for Dragonball). |
log-file | false | "dbs-cli.log" | The path to log file |
log-level | false | "Info" | The logging level. |
boot-args | false | console=ttyS0 tty0 reboot=k debug panic=1 pci=off root=/dev/vda1 | The boot arguments passed to the kernel. |
is-root | false | true | Decide the device to be the root boot device or not. |
is-read-only | false | false | The driver opened in read-only or not. |
vcpu | false | 1 | The number of vcpu to start. |
max-vcpu | false | 1 | The max number of vpu can be added. |
cpu-pm | false | 0 | vpmu support level. |
threads-per-core | false | 1 | Threads per core to indicate hyper-threading is enabled or not. |
cores-per-die | false | 1 | Cores per die to guide guest cpu topology init. |
dies-per-socket | false | 1 | Dies per socket to guide guest cpu topology. |
sockets | false | 1 | The number of sockets. |
mem-type | false | shmem | Memory type that can be either hugetlbfs or shmem. |
mem-file | false | `` | Memory file path. |
initrd-path | false | None | The path of initrd. |
4.5 使用示例:
1 |
|
For the rootfs from firecracker:
1 |
|
For the rootfs build from kata:
1 |
|
Set the log level and log file:
The log-level argument is case-insensitive: ErrOR and InFO are valid.
1 |
|