ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

KubeVirt 中基于 Go 的 Linux VM Sockets(AF_VSOCK)编程指南:深入 mdlayher/vsock 库

KubeVirt 中基于 Go 的 Linux VM Sockets(AF_VSOCK)编程指南:深入 mdlayher/vsock 库 云原生【免费下载链接】kubevirtKubernetes Virtualization API and runtime in order to define and manage virtual machines.项目地址https://gitcode.com/gh_mirrors/ku/kubevirt点击查看免费下载本指南以 KubeVirt 仓库中 vendored 的github.com/mdlayher/vsock库为主体系统讲解如何用 Go 编写基于 Linux VM socketsAF_VSOCK的宿主机hypervisor与虚拟机之间的通信程序。读完本文你将掌握vsock.Dial、vsock.Listen、vsock.ContextID等核心 API 的用法、地址模型CID Port、底层系统调用实现并了解 KubeVirt 如何在 virt-handler 中通过该库承载 gRPC over vsock 服务。一、什么是 VM Sockets宿主机与虚拟机之间的原生通信通道vsock库vendor/github.com/mdlayher/vsock为 Go 提供对 Linux VM socketsAF_VSOCK的访问能力用于宿主机与其虚拟机之间的通信采用 MIT 许可证。VM sockets 是 Linux 内核提供的一类地址族其通信端点通过CIDContext ID Port二元组寻址而不是传统的 IP 地址 端口。与走虚拟网络的 TCP/UDP 相比vsock 直连 virtio-vsock 设备路径更短、语义更贴近宿主机 ↔ 虚拟机的一对一关系。在 KubeVirt 中该库是 virt-handler 与虚拟机内 agent 建立安全通信通道的基础设施之一virt-handler 通过vsock在宿主机命名空间内监听固定端口虚拟机内进程则以固定的本地 CID 主动拨号连接进而承载 gRPC/TLS 流量。从源码结构看vsock.go库当前预留了Config结构体作为未来扩展点目前为空、传nil或空结构体均可// Config contains options for a Conn or Listener. type Config struct{}二、核心 API 全景与 net 包完全对齐的接口设计vsock包的设计理念是复用 Go 标准库net包的全部接口约定见 doc.go*Addr实现net.Addr*Conn实现net.Conn*Listener实现net.Listener这意味着任何接受net.Conn/net.Listener的 Go 代码如 gRPC 的server.Serve(listener)都可以直接使用 vsock 连接KubeVirt 正是这样把vsock.Listener直接喂给了grpc.Server。2.1 服务端Listen 系列// Listen 监听一个面向连接的 VM sockets 端口 func Listen(port uint32, cfg *Config) (*Listener, error) // ListenContextID 显式指定 context ID 监听高级用法 func ListenContextID(contextID, port uint32, cfg *Config) (*Listener, error) // FileListener 从已有 os.File如 systemd socket 激活创建 Listener func FileListener(f *os.File) (*Listener, error)要点依据 vsock.goListen(port, cfg)会自动探测本机 context ID内部调用ContextID()后转调ListenContextID需要绑定Local等特殊 context ID 的高级场景请直接用ListenContextID。传入port 0时由内核自动分配端口随后通过Listener.Addr()获取实际地址。FileListener面向 systemd socket 激活等外部机制注入的文件描述符在 Linux 实现中listener_linux.gonewListener会通过Getsockname校验地址族确实是SockaddrVM避免误用 TCP 等其他 socket 类型。不再使用时必须调用Close()释放资源Close()不会关闭已经Accept出来的连接。2.2 客户端Dial// Dial 向指定 context ID 的端口发起面向连接的拨号 func Dial(contextID, port uint32, cfg *Config) (*Conn, error)拨号方向语义见 vsock.go宿主机 → 虚拟机指定目标 VM 的 context ID虚拟机 → 宿主机进程使用Host0x2与宿主机上普通进程通信虚拟机 → 宿主机 hypervisor 进程使用Hypervisor0x0注意它只指向 hypervisor 进程本身绝大多数场景应使用Host。2.3 能力探测ContextIDfunc ContextID() (uint32, error)ContextID返回本机在 vsock 网络中的 context ID。Linux 实现fd_linux.go打开/dev/vsock设备后通过ioctl(IOCTL_VM_SOCKETS_GET_LOCAL_CID)取得本机 CIDfunc contextID() (uint32, error) { f, err : os.Open(devVsock) if err ! nil { return 0, err } defer f.Close() return unix.IoctlGetUint32(int(f.Fd()), unix.IOCTL_VM_SOCKETS_GET_LOCAL_CID) }如果内核模块不可用、无访问权限或系统不支持 VM sockets该函数会返回错误——因此它也是当前系统能否使用 vsock的直接探测手段。三、地址模型与内置常量Addr是 vsock 端点地址vsock.gotype Addr struct { ContextID, Port uint32 }Addr.String()会根据 CID 语义输出可读地址hypervisor(0):1、local(1):1、host(2):1其余 CID 一律显示为vm(cid):port。Network()固定返回vsock。包内预定义的关键 CID 常量vsock.go常量值含义vsock.Hypervisor0x0指向宿主机上的 hypervisor 进程注意并非宿主机上的任意进程vsock.Local0x1同一台机器上的对端 socket可替代 UNIX socket常用于测试vsock.Host0x2宿主机上的普通进程是虚拟机内拨号的首选目标此外/dev/vsock是宿主机与虚拟机两侧都会暴露的设备节点devVsock /dev/vsock它是 VM sockets 操作如 ioctl 获取 CID的入口。四、Linux 底层实现从 API 到系统调用的完整链路vsock的 Linux 实现位于 conn_linux.go 与 listener_linux.go均有//go:build linux约束底层依赖github.com/mdlayher/socket与golang.org/x/sys/unix。4.1 拨号链路Dialc, err : socket.Socket(unix.AF_VSOCK, unix.SOCK_STREAM, 0, vsock, nil) sa : unix.SockaddrVM{CID: cid, Port: port} rsa, err : c.Connect(context.Background(), sa)关键点以SOCK_STREAM创建面向连接的流式 socket地址族为AF_VSOCK对端地址使用内核结构unix.SockaddrVM{CID, Port}连接成功后通过Getsockname/getpeername还原本地与对端Addr组装成Conn由于github.com/mdlayher/socket处理了非阻塞connect(2)的SO_ERROR检查见 CHANGELOG v1.0.1拨号错误能被可靠识别。4.2 监听链路Listenif port 0 { port unix.VMADDR_PORT_ANY } if err : c.Bind(unix.SockaddrVM{CID: cid, Port: port}); err ! nil { ... } if err : c.Listen(unix.SOMAXCONN); err ! nil { ... }关键点传0时映射为内核常量VMADDR_PORT_ANY由内核分配空闲端口监听队列深度直接使用unix.SOMAXCONN任何系统调用失败都会先关闭 socket 再返回避免资源泄漏源码中的defer式关闭模式if err : ...; err ! nil { _ c.Close(); return nil, err }。4.3 错误处理约定Conn/Listener的全部操作错误都会被包装为net.OpError带Op、Net: vsock、Source/Addr字段并统一语义io.EOF与ENOTCONN归一为io.EOFEBADF与use of closed归一为use of closed network connectionvsock.go。特别地对/dev/vsock的权限错误不做解包保留路径上下文以便定位。因此上层代码可以像处理 TCP 错误一样处理 vsock 错误。4.4 非 Linux 平台的行为非 Linux 构建使用 vsock_others.go//go:build !linux所有函数返回统一的errUnimplementedvsock: not implemented on GOOS保证程序在 Windows 等平台仍可编译、只是运行时会得到明确的不支持错误而非编译失败。五、稳定性与版本策略可直接放心依赖的 v1 API按原 README 的 Stability 章节README.md该包拥有稳定的 v1 API任何未来的破坏性变更都会以新的主版本号发布特性与 bug 修复会持续发生在 v1.x.x 系列中只支持 Go 最近的两个大版本与 Go 官方发布策略对齐更老的 Go 版本可能缺少该包正常工作所需的关键特性与修复版本间变更详见 vendor/github.com/mdlayher/vsock/CHANGELOG.md其中几个关键里程碑v1.0.0首次稳定发布Dial/Listen引入可选的*Config参数当时无选项传nil即可新增ListenContextIDv1.1.0新增FileListener支持 systemd socket 激活等外部机制注入监听v1.1.1修复 Windows 等非 UNIX 平台的构建问题v1.2.0要求 Go 1.18更早版本请用 v1.1.1v1.2.1依赖更新并以 Go 1.20 测试。仓库内对应的 Bazel 依赖声明位于 vendor/github.com/mdlayher/vsock/BUILD.bazel。六、KubeVirt 中的实战gRPC over vsock 的安全通道该库在 KubeVirt 中承担宿主机 ↔ 虚拟机控制通道的角色以下实现细节均可在仓库源码中核对。6.1 宿主机侧在 Host CID 上监听 gRPC 服务pkg/virt-handler/vsock/servers.go 展示了标准服务端用法在每个 POD 的独立网络命名空间内以vsock.Host为 CID、固定端口1创建监听器并直接交给 gRPCconst vsockPort 1 listener, lisErr : vsock.ListenContextID(vsock.Host, vsockPort, vsock.Config{}) if lisErr ! nil { return nil, nil, fmt.Errorf(failed to listen on VSOCK CID %d port %d ..., vsock.Host, vsockPort, lisErr) } server : grpc.NewServer() systemv1.RegisterSystemServer(server, system.NewSystemService(s.caManager)) go func() { serveErr : server.Serve(listener) ... }()这正是vsock.Listener 可直接充当 net.Listener设计价值的直接体现。同构的实现还出现在 pkg/vsock/server/server.go。6.2 客户端侧拨号 TLS 网络命名空间切换pkg/virt-handler/vsock/dial.go 封装了Dialer把vsock.Dial作为底层拨号函数注入vsockDialFunc目标 CID 取自vmi.Status.VSOCKCID端口由调用方指定拨号在目标 POD 的网络命名空间内执行netns.New(pid).Do(fn)确保 VSOCK 路由落在正确的命名空间在local namespaced 模式下改用硬编码 CID3pkg/vsock/constants.go 中的LocalCID以避免与其它 VM 的 CID 冲突并可在命名空间内临时拉起对端 gRPC CA 服务连接建立后叠加TLS 13tls.Client 客户端证书回调InsecureSkipVerify配合证书双向认证机制握手过程刻意放到netns.Do之外执行避免阻塞 OS 线程源码注释明确说明了这一取舍。6.3 调用入口Dialer.Dial 是完整链路入口被 pkg/virt-handler/rest/console.go 等控制面组件使用用于向虚拟机内进程如 guest agent发起 gRPC over vsock 请求。七、快速上手一份可运行的端到端示例结合前文 API以下示例展示宿主机监听 vsock 端口 → 客户端另一侧拨号的最小闭环。服务端与客户端各取一段代码在支持AF_VSOCK的 Linux 上运行需要/dev/vsock存在且内核加载vsock/virtio_vsock模块。服务端宿主机侧监听 Host CID 的 1234 端口package main import ( fmt io net github.com/mdlayher/vsock ) func main() { // 监听 vsock.Host (2):1234传 0 可让内核自动分配端口 l, err : vsock.ListenContextID(vsock.Host, 1234, vsock.Config{}) if err ! nil { panic(fmt.Sprintf(listen vsock: %v, err)) } fmt.Printf(listening on %s\n, l.Addr()) // host(2):1234 defer l.Close() for { conn, err : l.Accept() if err ! nil { panic(fmt.Sprintf(accept: %v, err)) } go handle(conn) } } func handle(c net.Conn) { defer c.Close() _, _ io.Copy(c, c) // 回显 }客户端虚拟机侧拨号到宿主机 Host CID 的 1234 端口package main import ( fmt io github.com/mdlayher/vsock ) func main() { // 从虚拟机内拨号到宿主机普通进程 conn, err : vsock.Dial(vsock.Host, 1234, vsock.Config{}) if err ! nil { panic(fmt.Sprintf(dial vsock: %v, err)) } defer conn.Close() fmt.Printf(connected %s - %s\n, conn.LocalAddr(), conn.RemoteAddr()) _, _ conn.Write([]byte(hello from VM)) _, _ io.Copy(io.Discard, conn) }值得注意的工程细节LocalAddr()/RemoteAddr()返回的是共享指针不要修改其字段vsock.go连接与监听器用完必须显式Close()若内核未加载 vsock 模块Listen/Dial/ContextID会返回与/dev/vsock或 ioctl 相关的错误请先用vsock.ContextID()探测环境是否可用。八、适用前提与限制小结平台完整功能仅限 LinuxAF_VSOCK内核支持 /dev/vsock设备其他平台编译可通过但运行时全部返回vsock: not implemented on GOOSGo 版本当前 vendored 版本要求 Go 1.18CHANGELOG v1.2.0 起并随 Go 官方策略仅支持最近两个大版本API 稳定性v1 语义稳定Config为未来扩展预留目前为空结构体传nil与vsock.Config{}等效KubeVirt 集成前提VSOCK 通道需要 VMI 开启并上报vmi.Status.VSOCKCIDpkg/virt-handler/vsock/dial.go且拨号在 POD 网络命名空间内进行。综上mdlayher/vsock是一份标准库风格、实现克制、错误语义统一的 Linux VM sockets Go 客户端/服务端库既适合独立项目直接依赖也已被 KubeVirt 用于承载其虚拟机控制面的 gRPC/TLS 通信——理解它的 API 与底层行为是理解 KubeVirt vsock 控制通道的最佳切入点。赞分享云原生【免费下载链接】kubevirtKubernetes Virtualization API and runtime in order to define and manage virtual machines.项目地址https://gitcode.com/gh_mirrors/ku/kubevirt点击查看免费下载相关推荐Podman 中的 Linux VM SocketAF_VSOCK编程mdlayher/vsock 库解析与实战Podman 中的 Linux VM SocketAF_VSOCK编程mdlayher/vsock 库解析与实战 导读 VM socket AF_VSO容器运行时云原生CLI5分钟部署OpenShorts开源AI短视频生成器Docker自托管完整教程附GPU可选配置5分钟部署OpenShorts开源AI短视频生成器Docker自托管完整教程附GPU可选配置 OpenShorts 是一款开源的 AI 短视频生成器能把Moby 仓库中的 Go vsock 库解析用 Linux VM SocketAF_VSOCK打通 hypervisor 与虚拟机Moby 仓库中的 Go vsock 库解析用 Linux VM SocketAF_VSOCK打通 hypervisor 与虚拟机 导读 本篇文章围绕 M云原生容器运行时虚拟化容器编排上一篇Godot 资源解包工具 3 分钟上手从 .pck 包里提取纹理与音频的完整教程下一篇5分钟搭好 Vue 图片预览vue-preview 完整安装与上手指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表