【归档】Kata Containers 2.0 介绍

Kata Containers 2.0

2.0.0 于 2020年10月19日 发布。
文档地址:https://github.com/kata-containers/kata-containers/tree/2.0.0/docs

Design

1. Kata Containers 架构

概览


Kata Containers 的交付物是一个 CRI 友好的 Shim(e.g. containerd-shim-kata-v2),在它的后面还有一个 CRI 友好的 API 库。


Kata Containers runtime 与 OCI 运行时规范兼容,因此,可以通过 CRI-O 和 Containerd 实现与 Kubernetes CRI 进行无缝协作。


Kata Containers 为 kubelet 创建的 Pod 创建 QEMU/KVM 虚拟机。


containerd-shim-kata-v2(以下简称为 shimv2) 是 Kata Containers 的入口点,它为 Kata 实现了 Containered Runtime V2(Shim API)。


在 shimv2 之前(如在 Kata Containers 1.x 版本中所做的那样),我们需要为每个容器和 Pod 沙箱本身创建一个 containerd-shim 和 kata-shim,以及在 VSOCK 不可用时创建一个可选的 kata-proxy。借助 shimv2,Kubernetes 可以启动 Pod 和 OCI 兼容的容器,每个 Pod 可以使用一个 Shim(shimv2)而不是 2N+1 Shim,并且即使没有 VSOCK 也不用使用独立的 kata-proxy 程序。


image.png


然后,由 kata-agent 生成容器进程,kata-agent 是在虚拟机内部作为守护程序运行的代理进程。kata-agent 使用 VIRTIO 串行或 VSOCK 接口在虚拟机中运行 ttRPC 服务器,该接口由 QEMU 生成一个 Socket 文件暴露给宿主机。shimv2 使用 ttRPC 协议与代理进程进行通信。该协议允许运行时将容器管理命令发送到代理进程。该协议还用于在容器和管理引擎(例如 CRI-O 或 Containerd)之间承载 I/O 流(stdout,stderr,stdin)。


对于任何给定的容器,该容器中的初始化过程和所有可能执行的命令以及它们相关的 I/O 流都需要通过 QEMU 导出的 VSOCK 接口。


容器工作负载(即实际的 OCI bundle rootfs)已从宿主机导出到虚拟机。在配置基于块的图形驱动程序的情况下,将使用 virtio-scsi。其他情况下,将使用 9pfs VIRTIO 挂载。kata-agent 使用此安装点作为容器进程的根文件系统。(现在默认使用 virtis-fs)

虚拟化


虚拟化文档中介绍了 Kata Containers 如何将容器概念映射到虚拟机技术,以及如何在 Kata 支持的多个虚拟机管理程序和 VMM 中实现该概念。


Kata 容器是在传统 namespace 容器提供的隔离之上创建第二层隔离。硬件虚拟化接口是此附加层的基础。Kata 将启动轻型虚拟机,并使用虚拟机的 Linux 内核创建容器工作负载,或在多容器 Pod 的情况下创建工作负载。 在 Kubernetes 和 Kata 实现中,Sandbox 是在 Pod 级别执行的。在 Kata 中,此 Sandbox 是使用虚拟机创建的。

将容器概念映射到虚拟机技术


Kata 容器的典型部署将通过容器运行时接口(CRI)实现在 Kubernetes 中进行。在每个节点上,Kubelet 将与 CRI 实现器(例如 Containerd 或 CRI-O)进行交互,后者又将与 Kata 容器(基于 OCI 的运行时)交互。


在 Kubernetes CRI-API 存储库中定义的 CRI API 涉及一些由 CRI 支持的结构体,最终在 Kata Containers 中得到支持。为了使用 CRI-implementor 支持完整的 API,Kata 必须提供以下结构体:


image.png


然后可以将这些结构体进一步映射到与虚拟机接口所需的设备:


image.png


最终,这些概念映射到特定的半虚拟化设备或虚拟化技术。


image.png


每个虚拟机管理程序或 VMM 在处理每个虚拟机管理程序或如何处理它们方面都各不相同。

Hypervisor,又称虚拟机监控器(英语:virtual machine monitor,缩写为 VMM),是用来创建与运行虚拟机的软件、固件或硬件。
被 hypervisor 用来运行一个或多个虚拟机的电脑称为宿主机(host machine),这些虚拟机则称为客户机(guest machine)。Hypervisor 提供虚拟的作业平台来运行客户操作系统(guest operating systems),负责管理其他客操作系统的运行阶段;这些客操作系统,共同分享虚拟化后的硬件资源。

—— 维基百科

Kata Container Hypervisor 和 VMM 支持


Kata Containers 旨在支持多个虚拟机监视器(VMM)和虚拟机管理程序。Kata Containers 支持:

  • ACRN hypervisor
  • Cloud Hypervisor/KVM
  • Firecracker/KVM
  • QEMU/KVM

> QEMU/KVM


具有 QEMU 的 Kata 容器与 Kubernetes 完全兼容。


使用的设备和功能:

  • virtio VSOCK or virtio serial
  • virtio block or virtio SCSI
  • virtio net
  • virtio fs or virtio 9p (recommend: virtio fs)
  • VFIO
  • hotplug
  • machine accelerators


Kata Containers 中使用 machine accelerators and hotplug 来管理资源限制,缩短启动时间并减少内存占用。 这些记录在下面。


Machine accelerators: 机器加速器是特定于体系结构的,可用于提高性能并启用机器类型的特定功能。Kata Containers 中使用以下机器加速器:

  • NVDIMM:此计算机加速器特定于 x86,仅 pc 和 q35 机器类型支持。nvdimm 用于将根文件系统作为持久性存储设备提供给虚拟机。


Hotplug devices: Kata Containers VM 以最少的资源启动,从而缩短了启动时间并减少了内存占用。随着容器启动的进行,设备会热插拔到 VM。例如,当指定的 CPU 约束后要使用更多 CPU 时,可以将其热添加。Kata Containers 支持热添加以下设备:

  • Virtio block
  • Virtio SCSI
  • VFIO
  • CPU

> Firecracker/KVM


Firecracker 基于 rust-VMM,其设备模型非常有限,占用更小的空间,攻击面更小,专注于 FaaS 的使用。因此,带有 Firecracker VMM 的 Kata Containers 支持 CRI API 的子集。Firecracker 不支持文件系统共享,因此仅支持基于块的存储驱动程序。Firecracker 不支持设备热插拔,也不支持 VFIO。因此,带有 Firecracker VMM 的 Kata Containers 在启动后不支持更新容器资源,也不支持设备直通。

VFIO 是一套用户态驱动框架,可用于编写高效用户态驱动;在虚拟化情景下,亦可用来在用户态实现 device passthrough。通过 VFIO 访问硬件并无新意,VFIO 可贵之处在于第一次向用户态开放了 IOMMU 接口,能完全在用户态配置 IOMMU,将 DMA 地址空间映射进而限制在进程虚拟地址空间之内。这对高性能用户态驱动以及在用户态实现 device passthrough 意义重大。

—— https://blog.csdn.net/nb_zsy/article/details/106602334


使用的设备:

  • virtio VSOCK
  • virtio block
  • virtio net

> Cloud Hypervisor/KVM


基于 rust-VMM 的 Cloud Hypervisor 旨在减少占用空间和攻击面。对于 Kata Containers,相对于 Firecracker,Cloud Hypervisor 配置提供了更好的兼容性,但以暴露其他设备为代价:文件系统共享和直接设备分配。从 Kata Containers 1.10 版本开始,Cloud Hypervisor 不支持设备热插拔,因此不支持在引导后或使用基于块的卷后更新容器资源。尽管 Cloud Hypervisor 确实支持 VFIO,但 Kata 仍在添加此支持。从 1.10 版本开始,Kata 不支持基于块的卷或直接设备分配。


使用的设备:

  • virtio VSOCK
  • virtio block
  • virtio net
  • virtio fs

> 总结
解决方案引入版本总结
QEMU1.0上游 QEMU,支持热插拔和文件系统共享
NEMU1.4不推荐使用,从 1.10 版本开始删除。 QEMU的精简版,带有 virtio-fs 的实验支持
Firecracker1.5上游 Firecracker,基于 rust-VMM,无 VFIO,无 FS 共享,无内存/ CPU 热插拔
QEMU-virtio-fs1.7支持 virtio-fs 的上游 QEMU。一旦 virtio-fs 进入上游 QEMU,将被删除
Cloud Hypervisor1.10基于 rust-VMM,包括通过 virtio-fs 共享 VFIO 和 FS,无热插拔

虚拟机 Guest assets


系统管理程序将启动一个虚拟机,该虚拟机包括最小的 guest kernel 和 guest image。

Guest kernel


guest kernel 被传递到管理程序,并用于引导虚拟机。Kata Containers 中提供的默认内核针对内核启动时间和最小的内存占用进行了高度优化,仅提供了容器工作负载所需的那些服务。这基于最新的上游 Linux 内核。

Guest Image


Kata Containers 支持基于 initrd 和 rootfs 的最小 guest image。

Root filesystem image


默认的根文件系统映像是基于 Clear Linux 的高度优化的容器引导系统,称为“mini O/S”。它提供了一个极小的环境,并具有高度优化的引导路径。


在 mini O/S 上下文中运行的唯一服务是 init 守护程序(systemd)和代理 Agent。 使用 libcontainer 创建用户希望运行的实际工作负载,并以与 runc 相同的方式创建容器。


例如,当运行 ctr run -ti ubuntu date 时:

  • 系统管理程序将使用 guest kernel 引导 mini-OS 映像。
  • 在 mini-OS 上下文中运行的 systemd 将在同一上下文中启动 kata-agent。
  • 代理将创建一个新的受限上下文来运行指定的命令(在此示例中为 date)。
  • 然后,代理将在此新上下文中执行命令(在此示例中为 date),首先将根文件系统设置为预期的 Ubuntu 根文件系统。

Initrd image


从 rootfs 创建的压缩 cpio(1) 归档文件,该归档文件已加载到内存中,并用作 Linux 启动过程的一部分。在启动期间,内核将其解压缩到 tmpfs 的特殊实例中,该实例成为初始的根文件系统。


在 initrd 上下文中运行的唯一服务是作为 init 守护程序的代理 Agent。 使用 libcontainer 创建用户希望运行的实际工作负载,并以与 runc 相同的方式创建容器。

Agent


kata-agent 是在虚拟机中作为管理员运行的进程,用于管理容器和在这些容器中运行的进程。


对于 2.0 发行版,kata-agent 用 Rust 编程语言进行了重写,以便我们可以最小化其内存占用量,同时保持 Kata Container 1.x 中使用的 kata-agent 原始 Go 版本的内存安全性。这种内存占用空间的减少非常令人印象深刻,从几十 MB 降至不到 100 KB,这使 Kata Containers 可以在更多用例(例如函数计算和边缘计算)中使用。


kata-agent 执行单元是 Sandbox。kata-agent Sandbox 是由一组 namespace(NS,UTS,IPC 和 PID)定义的容器 Sandbox。 shimv2 可以为每个 VM 运行多个容器,以支持需要在 Pod 内运行多个容器的容器引擎。


kata-agent 通过 ttRPC 与其他 Kata 组件进行通信。

Runtime


containerd-shim-kata-v2 是 Containerd 运行时 shimv2 的实现,负责处理运行时 v2 shim API,这类似于 OCI 运行时规范,但通过加载一次运行时并通过 RPC 调用来处理各种容器生命周期管理命令来简化体系结构。这种改进是对 OCI 规范的改进,该规范要求容器管理器多次调用运行时二进制文件,对于每个生命周期命令至少调用一次。


containerd-shim-kata-v2 大量使用了 virtcontainers 软件包,该软件包提供了通用的,运行时规范透明的,硬件虚拟化的容器库。


运行时使用 TOML 格式的配置文件,称为 configuration.toml。可以通过运行以下命令确定实际的配置文件路径:

$ kata-runtime --kata-show-default-config-paths

网络 Networking


https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/architecture.md#networking

存储 Storage


容器工作负载通过 virtio-fs 与虚拟化环境共享。


https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/architecture.md#storage

Kubernetes support


https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/architecture.md#kubernetes-support

附录

DAX


Kata Containers 利用 Linux 内核 DAX(直接访问文件系统)功能将某些主机端文件有效地映射到虚拟机 VM 空间。特别是,Kata Containers 使用 QEMU NVDIMM 功能来提供内存映射的虚拟设备,该设备可用于 DAX 将虚拟机的根文件系统映射到来宾内存地址空间。


与更传统的 VM 文件和设备映射机制相比,使用 DAX 映射文件具有许多优点:

  • 映射为直接访问设备允许虚拟机直接访问主机内存页面(例如通过就地执行(XIP)),而忽略虚拟机页面缓存。这提供了时间和空间优化。
  • 通过映射为 VM 内部的直接访问设备,可以使用页面错误从主机加载页面,而不必通过虚拟设备发出请求(导致昂贵的 VM 退出/超调用),从而提供了速度优化。
  • 通过在主机上使用 MAP_SHARED 共享内存,主机可以有效地共享页面。


Kata Containers 使用以下步骤来设置 DAX 映射:

  1. QEMU 配置了 NVDIMM 内存设备,并带有后端的内存文件以将主机端文件映射到虚拟 NVDIMM 空间。
  2. 虚拟机内核命令行在启用了 DAX 功能的情况下挂载此 NVDIMM 设备,从而允许直接页面映射和访问,从而绕过了虚拟机页面缓存。


image.png

2. Kata API 设计


https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/kata-api-design.md

3. 设计要求


https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/kata-design-requirements.md

4. Kata Containers and VSOCKs

介绍


虚拟机中的进程可以通过两种不同的方式与主机中的进程进行通信。第一个是使用串行端口,其中虚拟机中的进程可以从串行端口设备读取/写入数据,而主机中的进程可以从 Unix 套接字读取/写入数据。大多数 GNU/Linux 发行版都支持串行端口,使其成为最可移植的解决方案。但是,串行链接一次只能对一个进程进行读/写访问。


一种更新,更简单的方法是 VSOCK,它可以接受来自多个客户端的连接。下图显示了如何在 Kata Containers 中实现它。

.----------------------.
| .------------------. |
| | .-----.  .-----. | |
| | |cont1|  |cont2| | |
| | `-----'  `-----' | |
| |       |   |      | |
| |    .---------.   | |
| |    |  agent  |   | |
| |    `---------'   | |
| |       |   |      | |
| | POD .-------.    | |
| `-----| vsock |----' |
|       `-------'      |
|         |   |        |
|  .------.   .------. |
|  | shim |   | shim | |
|  `------'   `------' |
| Host                 |
`----------------------'

系统要求


主机 Linux 内核版本必须大于或等于 v4.8,并且 vhost_vsock 模块必须已加载或内置(CONFIG_VHOST_VSOCK = y)。要加载模块,请运行以下命令:

$ sudo modprobe -i vhost_vsock


Kata Containers 版本必须大于或等于 1.2.0,并且必须在运行时配置文件中将 use_vsock 设置为 true。

使用 VSOCK 的优点

高密度


使用 Proxy 多路传输 VM 和主机之间的连接时,每个 Pod 占用 4.5MB。在高密度部署中,这可能会增加可能用于承载更多 Pod 的 GB 内存。当我们谈论密度时,每千字节很重要,这可能是运行另一个 Pod 与否之间的决定性因素。例如,如果服务器中有 500 个 Pod,则将运行相同数量的 kata-proxy 进程,并消耗约 2250MB 的 RAM。 在决定不使用 VSOCK 之前,您应该问自己,用 Kata Proxy 消耗的内存 RAM 可以运行多少个容器?

可靠性


如果 kata-proxy 负责多路复用虚拟机和主机进程之间的连接,如果它死掉了,那么所有连接就都死掉了。例如,如果您有一个正在运行 10 个容器的 Pod,则如果 kata-proxy 死了,尽管它们仍在运行,则不可能联系您的容器。 由于通过 VSOCK 进行的通信是直接的,因此与容器失去通信的唯一方法是 VM 本身或 containerd-shim-kata-v2 死亡,如果发生这种情况,则会自动删除容器。

5. Kata Containers 中虚拟机的 vCPU 大小


配置文件中 default_vcpus 默认为 1,不建议修改。如果需要更多的 CPU,可以通过 docker --cpus, docker update 或 Kubernetes cpu limits 来修改。

创建具有 CPU 约束的容器时,运行时将添加该容器所需的 vCPU 数量。 同样,当容器终止时,运行时将删除这些资源。


没有 CPU 约束的容器使用配置文件中指定的默认 vCPU 数量。对于 Kubernetes Pod,不受 CPU 限制的容器使用并在它们之间共享默认数量的 vCPU。


在运行不受 CPU 限制的容器之前,请考虑您的容器不是单独运行的。 由于您的容器在虚拟机中运行,因此其他进程也会使用 vCPU(例如 systemd 和 Kata Containers Agent)。 通常,建议将 default_vcpus 设置为 1,以允许非容器进程在此 vCPU 上运行,并为每个容器指定 CPU 约束。 如果您的容器已经在运行并且需要更多 vCPU,则可以使用 docker update 添加更多。

6. 宿主机 cgroup 管理


在 Kata Containers 中,工作负载在由主机上运行的虚拟机监视器(VMM)管理的虚拟机中运行。因此,Kata Containers 在两层 cgroup 上运行。第一层在虚拟机中放置工作负载,而第二层在主机上运行 VMM和 关联线程。


根据上级协调器,放置协调器的 cgroup 由协调器管理。 对于 Kubernetes,pod-cgroup 由 Kubelet 创建,而容器 cgroup 将由运行时处理。 Kubelet 将根据容器资源要求调整 pod-cgroup 的大小。


Kata Containers 为运行 Sandbox(pod)引入了不可忽略的开销。 基于此,可能出现两种情况:

  1. 调整 pod-cgroup 的大小时,上层协调器会考虑运行 Sandbox 的开销,或者
  2. Kata Containers 没有完全约束 VMM 和关联的进程,而是将它们的子集放置在 pod-cgroup 之外。


对于如何在主机上处理 cgroup,Kata Containers 提供了两个选项。 通过 Kata Containers 配置文件中的 SandboxCgroupOnly 标志可以选择这些选项。


详细信息请查阅官方文档:https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/host-cgroups.md

7. Kata 2.0 指标设计


Kata 实现 CRI 的 API,并支持 ContainerStats 和 ListContainerStats 接口以公开容器指标。用户可以使用这些界面来获取有关容器的基本指标。


但是与 runc 不同,Kata 是基于 VM 的运行时,并且具有不同的体系结构。


Kata 1.x 有许多与可观察性相关的限制,可能会限制大规模运行 Kata Containers。


在 Kata 2.0 中,以下组件将能够提供有关系统的更多详细信息。

  • containerd shim v2 (effectively kata-runtime)
  • Hypervisor statistics
  • Agent process
  • Guest OS statistics

注意:在 Kata 1.x 中,面向用户的主要组件是运行时(kata-runtime)。从 1.5 开始,Kata 引入了 Kata containerd shim v2(containerd-shim-kata-v2),本质上是经过修改的运行时,由 containerd 加载,以简化和改进基于 VM 的容器的创建和管理方式。

对于 Kata 2.0,主要组件是 Kata containerd shim v2,尽管已弃用的 kata-runtime 二进制文件将保留一段时间。

除非另有明确说明(例如,明确地将其称为 kata-runtime 二进制文件),否则本文档中对“ Kata 运行时”的任何提及均应指代 Kata containerd shim v2。

指标架构


Kata 2.0 指标强烈依赖于 Prometheus,这是 CNCF 的毕业项目。


Kata Containers 2.0 引入了一个名为 kata-monitor 的新 Kata 组件,该组件用于监视主机上的其他 Kata 组件。这是 Kata 运行时的监控接口,我们可以执行以下操作:

  • Get metrics
  • Get events


在本文档中,我们将仅涵盖指标。 直到现在,它仅支持指标功能。


这是 Kata Containers 2.0 中的体系结构概述指标。


image.png


序列图如下所示:


image.png


详细指标介绍请查阅官方文档:https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/design/kata-2-0-metrics.md


可以参考这个来收集函数运行内存。

附图:


create:


image.png


start:


image.png

How To

日志


可使用 Fluentd 记录 Kata 的日志,包括 runtime、proxy、shim 甚至是 agent。
参考:https://github.com/kata-containers/kata-containers/blob/2.0.0/docs/how-to/how-to-import-kata-logs-with-fluentd.md


默认情况下,日志进入系统日志(system journal),但也可以配置为存储在文件中。


日志默认格式为 logfmt 结构化日志记录,但可以使用命令行选项将其切换为 JSON。


我们现在使用的是 shimev2,与之前有两个区别:

  1. Kata 日志通过 Containerd 进行定向,并将与 Containerd 日志一起捕获,例如在 Containerd 标准输出或系统日志(system journal)中。
  2. 同时,Kata shimv2 将其日志以 kata 的系统名称放置在系统日志(system journal)中。


可以使用如下命令查看 Kata 的日志:

journalctl -u containerd.service -f


如果需要开启全部组件的 debug,使用如下命令,参考

# (可选)复制配置文件到 /etc/kata-containers/configuration.toml
# mkdir -p /etc/kata-containers/
# install -o root -g root -m 0640 /usr/share/defaults/kata-containers/configuration.toml /etc/kata-containers

sed -i -e 's/^# *\(enable_debug\).*=.*$/\1 = true/g' /etc/kata-containers/configuration.toml
sed -i -e 's/^kernel_params = "\(.*\)"/kernel_params = "\1 agent.log=debug initcall_debug"/g' /etc/kata-containers/configuration.toml

Use Cases


Kata 与一些硬件集成使用的例子,可以提升性能。

### Kata Containers Root File System (RootFS) Configuration and Creation Guide #### Understanding the Role of RootFS in Kata Containers The root file system plays a critical role within Kata Containers as it provides an isolated environment where applications can run with their own set of libraries, binaries, and configurations[^1]. This isolation ensures that each container operates independently from others. #### Preparing Environment for RootFS Setup Before setting up the root file system specifically designed for use by Kata Containers, ensure all necessary dependencies are installed on your host machine. Common tools required include `debootstrap` or similar utilities depending upon distribution choice[^2]. #### Creating a Minimalistic Debian-based RootFS Using debootstrap Command Line Tool To create a minimal Debian-based root file system suitable for usage inside Kata Containers: ```bash sudo mkdir /var/lib/kata-containers/rootfs/debian-minimal sudo debootstrap --variant=minbase buster /var/lib/kata-containers/rootfs/debian-minimal http://deb.debian.org/debian/ ``` This command initializes a new directory structure at `/var/lib/kata-containers/rootfs/debian-minimal`, populating this location with essential files needed to boot into a functional Linux operating system instance when used alongside Kata Containers runtime[^3]. #### Configuring Network Interfaces Inside Chroot Jail After creating the base image, enter chroot jail using following commands which allows configuring network interfaces directly under newly created filesystem context without affecting actual hardware settings outside container scope: ```bash sudo mount -t proc none /var/lib/kata-containers/rootfs/debian-minimal/proc sudo cp /etc/resolv.conf /var/lib/kata-containers/rootfs/debian-minimal/etc/ sudo chroot /var/lib/kata-containers/rootfs/debian-minimal apt-get update && apt-get install iproute2 net-tools vim-tiny openssh-server exit umount /var/lib/kata-containers/rootfs/debian-minimal/proc ``` These operations add networking capabilities along with some basic administrative tools like SSH server enabling remote access once deployed properly within kata containers instances[^4]. #### Optimizing Image Size Through Cleanup Operations Post Installation Once installation completes successfully consider cleaning unnecessary packages reducing overall size footprint making images more portable across different environments while maintaining core functionalities intact: ```bash apt-get clean rm -rf /tmp/* ~/.bash_history find /usr/share/locale ! -name 'en' | xargs rm -r -- find /usr/share/man -type f|xargs rm - ``` Executing these cleanup steps helps minimize storage requirements ensuring efficient utilization resources during deployment phases especially important considering cloud-native workloads often operate resource-constrained conditions[^5]. --related questions-- 1. What alternatives exist besides `debootstrap` for other distributions such as CentOS? 2. How does one customize the package selection beyond what's provided initially through minbase variant option? 3. Can you explain how security features integrate with custom-built rootfs solutions? 4. Are there any best practices regarding version control over multiple iterations of customized rootfs builds?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值