ARTICLE DETAIL

资讯详情

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

gVisor Checkpoint Gofer 源码解析:将容器检查点文件直接保存到 GCS 的完整机制

gVisor Checkpoint Gofer 源码解析:将容器检查点文件直接保存到 GCS 的完整机制 gVisor Checkpoint Gofer 源码解析将容器检查点文件直接保存到 GCS 的完整机制【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisorCheckpoint gofer 是 gVisor 中负责把容器检查点checkpoint文件直接读写到 Google Cloud StorageGCS的独立侧车二进制。它由 runsc 在 save/restore 时按需 fork 出来通过 Unix 域套接字向 sentry 暴露一个 URPCAsyncFileServer使容器状态不再必须先落盘到本地文件系统。读完本文你将理解它为什么必须是一个独立二进制、gcs_opts.json各配置项如何生效、五类检查点文件如何映射为 GCS 对象以及并行复合上传parallel composite upload的递归组合树实现。1. 为什么 checkpoint gofer 是一个独立二进制官方说明见 runsc/checkpointgofer/README.md其原文给出了两条关键设计动机该目录实现了一个 checkpoint gofer提供将检查点文件保存到 GCS、以及从 GCS 恢复检查点文件的能力它被构建为独立二进制目的是避免把net/http拉进主 runsc 二进制——否则会导致 fsgofer 中的 netpoll 因找不到/etc/hosts而失败。这是一个很典型的“为了解耦依赖而拆分进程”的工程决策GCS 客户端栈依赖 HTTP 网络库而 gVisor 的 fsgofer文件系统 gofer负责把宿主文件系统安全地暴露给 sandbox运行在受限环境中不希望其进程镜像携带完整的 net/http 运行时。因此 GCS 访问逻辑被整体移出主二进制单独编译。从构建系统看runsc/checkpointgofer/BUILD 中checkpointgofer_binary使用了pure True属性并通过embedded_binary_go_library打包成内嵌二进制checkpointgofertarget最终由 runsc/gvisorbinaries/gvisorbinaries.go 声明为CheckpointGofer侧车二进制与普通安装中的runsc-metric-server、runsc-sentry等并列。2. 整体架构sandbox 拉 gofersentry 拿套接字从源码结构看一次经由 checkpoint gofer 的 save/restore 的数据面是runsc (sandbox) checkpoint gofer 进程 sentry fork socketpair → urpc.Server gcs.FileServer 客户端 FD 通过 FilePayload ──────────────────────────────→ Restore/Save关键入口在 runsc/sandbox/sandbox.go 的maybeStartCheckpointGoferAndGetSocket在检查点目录imagePath即注解中指定的 checkpoint path中查找名为gcs_opts.json的文件常量checkpointGCSOptsFileName定义于 sandbox.go。该文件是否存在就是是否启用 checkpoint gofer 的开关不存在则静默回退到本地文件路径创建一个socketpairUnix 域、SOCK_STREAM客户端一端fd 3留给 sentry服务端一端fd 4传给 gofer将标准输入/输出/错误重定向到/dev/null避免 gofer 的日志污染 runsc 日志日志可通过-log-fd/--debug-log-fd另行落盘通过cgroup.RunInCgroup在沙箱的 cgroup 中执行gvisorbinaries.CheckpointGofer.ForkExec并以Setsid: true脱离会话防止子进程被重新挂起父进程后收到 SIGHUP/SIGCONT进程参数固定为checkpointgofer -sock-fd3 -gcs-opts-fd4加一个权限标志且显式剔除环境中的GOMAXPROCS避免 containerd-shim 传入的GOMAXPROCS2等值被错误继承。四个场景各自传入不同的权限标志见 sandbox.go场景函数权限标志从检查点恢复内核状态setRestoreOpts#L606-allow-checkpoint-reads保存内核检查点setCheckpointOptsFiles#L1704-allow-checkpoint-writes恢复文件系统检查点openFSRestoreFiles#L1769-allow-fscheckpoint-reads保存文件系统检查点setFSSaveArgs#L1824-allow-fscheckpoint-writes如果 gofer 启动成功客户端套接字 FD 被追加进FilePayload.Files且是唯一一个文件同时置UseCheckpointGofer truesentry 侧收到该标志后就从套接字而不是本地文件读取检查点内容。3. gofer 进程命令行与 GCSOptions 配置3.1 命令行参数gofer 是一个subcommands风格的 CLI子命令名为checkpointgofer参数定义见 runsc/checkpointgofer/main.go[-allow-checkpoint-reads|-allow-checkpoint-writes| -allow-fscheckpoint-reads|-allow-fscheckpoint-writes] -sock-fdsocket fd -gcs-opts-fdoptions fd参数含义-allow-checkpoint-reads允许读取内核检查点文件checkpoint.img、pages.img、pages_meta.img-allow-checkpoint-writes允许写入内核检查点文件-allow-fscheckpoint-reads允许读取文件系统检查点文件fscheckpoint.pb、multitar.img-allow-fscheckpoint-writes允许写入文件系统检查点文件-sock-fd已连接到 sentry 的 Unix 域套接字 FD-gcs-opts-fd以 JSON 编码的GCSOptions文件 FD即gcs_opts.jsonExecute要求至少一个权限标志和两个 FD 都为有效值否则打印用法并退出。启动后main从-gcs-opts-fd解码GCSOptions构造gcs.FileServer再包一层stateipc.NewAsyncFileServer注册到urpc.Server上Handle(sock)服务请求。3.2 gcs_opts.json 字段GCSOptions结构体main.go就是gcs_opts.json的 JSON 模式{ token: null, bucket: my-checkpoint-bucket, object_prefix: sandbox-abc/, parallel_composite_upload: safe }字段JSON key说明TokentokenOAuth2 token。非空时 gofer 用该 token 认证否则回退到应用默认凭据ADC。注意omitzero标签——零值可省略Bucketbucket存放检查点文件的 GCS bucket必填runGCS与gcs.NewFileServer都会校验非空ObjectPrefixobject_prefix以纯字符串前缀不是路径拼接加到每个检查点文件名之前构成 GCS 对象名若前缀不含尾随/实现不会替你补/需要自己写对ParallelCompositeUploadparallel_composite_uploadsafe缺省安全时启用、disable无条件禁用、force无条件启用其他取值会直接报错退出4. 检查点文件模型五个文件名与权限门控gVisor 一个完整检查点由若干固定命名的文件组成定义在 pkg/sentry/state/checkpointfiles/checkpointfiles.go常量文件名内容读权限写权限StateFileNamecheckpoint.img内核状态allowCheckpointReadsallowCheckpointWritesPagesMetadataFileNamepages_meta.img页元数据内核或 FS 读权限任一内核或 FS 写权限任一PagesFileNamepages.img内存页数据最大文件内核或 FS 读权限任一内核或 FS 写权限任一FSCheckpointManifestFileNamefscheckpoint.pb文件系统检查点 manifestallowFSCheckpointReadsallowFSCheckpointWritesFSCheckpointMultiTarFileNamemultitar.img文件系统检查点 tar 数据allowFSCheckpointReadsallowFSCheckpointWritesgcs.FileServer.OpenRead/OpenWritegcs/gcs.go是一个switch path的白名单不在上表中的路径一律拒绝并记 warning 日志。被拒绝的打开会返回fs.ErrPermission并打一条 “attempted to open ... with allowXxx disabled” 的日志便于排查权限标志漏配的问题。5. GCS 客户端认证、传输与 HTTP 细节NewFileServergcs.go在构建客户端时有几处值得注意的实现细节认证。未提供token时走credentials.DetectDefault应用默认凭据并按是否允许写选择 OAuth scope只读场景请求devstorage.read_only只要开启任一写权限就升级为devstorage.read_write。凭据还会查询UniverseDomain并传给客户端保证与凭据所在宇宙域一致。为什么用 HTTP 客户端而不是 gRPC。代码注释gcs.go#L199-L215说明gRPC 客户端不支持storage.Writer.ChunkSize 0会强制最小 256 KiB chunk这对ParallelWriter是致命的其重试与内存控制依赖禁用 chunk 缓冲。因此必须使用 HTTP(JSON) 客户端并显式设置MaxIdleConnsPerHost为读/写两侧计算出的最大并行总数通过空TLSNextProtomap禁用 HTTP/2与 gcsfuse 的性能取舍一致调用storage.WithJSONReads()这也是storage.NewClient文档推荐做法。并行度预算。读写的MaxIdleConnsPerHost由一组调优常量推导常量gcs.go#L37-L59值用途manifestFileMaxReadBytes/manifestFileMaxReadParallel1 MiB / 2读fscheckpoint.pbmiscFileMaxReadBytes/miscFileMaxReadParallel1 MiB / 8读checkpoint.img、multitar.img、pages_meta.imgnumMiscFiles 2pagesFileMaxReadBytes/pagesFileMaxReadParallel16 MiB / 160读pages.imgmanifestFileMaxWriteBytes/manifestFileMaxWriteParallel2 MiB / 2写 manifestmiscFileMaxWriteBytes/miscFileMaxWriteParallel2 MiB / 4写其他小文件pagesFileMaxWriteBytes/pagesFileMaxWriteParallel32 MiB / 4写pages.img非 PCUpagesFileMaxPCUWriteParallel160写pages.imgPCU 模式读pages.img时的maxRanges取pagesFileMaxReadBytes / os.Getpagesize()即“每个页一个 range”这是pgalloc.MemoryFile恢复时可能需要的上限注释同时指出 gofer 侧的Reader不使用 readv因此不受内核UIO_MAXIOV限制。6. 读取路径range reader 池化复用reader.go 的Reader实现stateio.AsyncReader构造函数启动maxParallel个workerMaingoroutine通过subs通道接收读请求AddRead/AddReadv提交、Wait收割Completion每个 worker 用obj.NewRangeReader(ctx, off, total)发起单范围HTTP GETGCS 客户端不支持一次请求多个 range读满后通过rr.ReadHandle()复用底层读句柄给下一次读减少重复建立读取状态错误映射很讲究RangeNotSatisfiable归一为io.EOFHTTP 401/403 归一为unix.EACCES保证 sentry 侧看到的 errno 与本地文件语义一致错误码解析在 gcs/errors.go。7. 写入路径普通 Writer 与并行复合上传PCU写pages.img时FileServer.OpenWrite先查询getParallelCompositeUploadEnabled()PCU 可用则创建ParallelWriter否则或创建失败时回退到普通Writer并打一条 warning。7.1 Safe 模式的安全检查ParallelCompositeUploadSafe缺省会拉取 bucket 属性逐项检查gcs.go#L379-L426任何一项不满足就禁用PCU 并记录原因bucket 默认存储类必须为STANDARD否则临时对象会产生 early deletion 费用或落入非默认存储类bucket 不能有 retention policy不能启用默认 event-based hold不能启用 soft deleteSoftDeletePolicy.RetentionDuration ! 0——代码注释特别指出这一条比gcloud storage cp的兼容性检查更严格因为 gVisor 的写入是流式的可能需要递归组合临时存储开销随文件大小超线性增长。检查只在首次需要时执行一次sync.Once且FileServer构造时若开启了写权限会在后台 goroutine 里预取该结果。所有临时对象与最终对象都被强制StorageClass STANDARD和ContentType application/octet-stream避免存储类不一致与 Content-Type 探测开销。7.2 ParallelWriter递归组合树parallelwriter.go 是这部分最有深度的实现。GCS 官方描述的并行复合上传假设“写之前知道全部数据、固定 32 chunk”而检查点写入是流式的页数据边产出边写无法预先切块。ParallelWriter的解法每次AddWrite都生成一个 level 0 的临时对象对象名格式为目标对象名_随机hex_part_随机hex_level_partIndex——前缀与目标对象一致是为了兼容基于对象名前缀的 IAM/managed folder 授权当某一层连续攒满 32 个composeMax 32GCS 单次 compose 的源对象上限就绪对象启动 composer goroutine 将其组合成上一层对象composer 会递归向上组合直到不再凑满 32每组合成功一个中间对象立即把被消费的 32 个源对象交给 32 个 deleter goroutine 异步删除把临时对象数量维持在 O(log) 量级Finalize()时丢弃不满 32 的残缺分支重新构建一棵最小化组合次数的最终组合树让下标最小体积最大的对象尽量推迟到最终 compose用恰好一棵“次满”sub-maximal分支吸收(n-1) % 31的余数。若只剩 1 个对象优先用ObjectHandle.Move原子重命名完成落名失败则回退为 copy delete。写入重试方面writerMain把storage.Writer.ChunkSize设为 0 关闭库内缓冲否则默认 16 MiB chunk × 160 并行会吃掉大量内存改用自实现的指数退避初始 50 ms、倍率 2、上限 15 s、总超时 32 s权限类错误直接映射为EACCES。临时对象写入成功后会记录其 generation保证即使 bucket 开了版本化也能精确删除对应版本。Close()异常/取消路径则取消写与组合、递归删除所有未落名的临时对象并等待删除完成不留垃圾。parallelwriter_test.go对这套组合逻辑有专门的单元测试覆盖。8. sentry 侧对接URPC 异步文件接口与 boot 参数gofer 服务的是 pkg/sentry/state/stateipc 定义的AsyncFileServerURPC 接口AsyncFileServer.Open/AsyncFileServer.Close等方法sentry 端拿到套接字后构造AsyncFileClient把checkpoint.img等逻辑文件名的每次读异步代理到 gofer。契约在多处一致声明例如 runsc/boot/controller.go#L569-L574“若UseCheckpointGofer为 trueFilePayload的第一个也是唯一一个文件是连接到stateipc.AsyncFileServerURPC 服务器的 Unix 域套接字”此时RestoreOpts.HavePagesFile未知需由容器管理器在 restore 时确定。对应的内核态开关经 sentry boot 命令行传入runsc/cmd/sentry/sentrycmd/boot.go#L293-L298参数含义-save-fds保存检查点使用的 FD 有序列表本地模式kernel state、page metadata、page file-save-checkpoint-gofer为 true 时-save-fds只有一个 FD即连到 checkpoint gofer 的套接字-fs-save-fds/-fs-save-checkpoint-gofer文件系统检查点保存的对偶参数-fs-restore-fds/-fs-restore-checkpoint-gofer文件系统检查点恢复的对偶参数runsc/boot中各路径按此分流getRestoreReaders→getRestoreReadersForCheckpointGoferdup 客户端 FD 后建 URPC 客户端见 controller.go#L700-L705FSSave走setKernelFSSaveOptsFilesForCheckpointGoferfscheckpoint.go#L161-L166restore 的UseCheckpointGofer还会在压缩级别为 None 时置HavePagesFile truerestore.go#L202-L206。9. 使用方式小结与适用前提综合上述源码启用 GCS 检查点的操作面是在 runsc 指定的检查点目录checkpoint path中放置gcs_opts.json至少包含buckettoken可省略回退 ADCobject_prefix决定对象名前缀parallel_composite_upload三选一按普通流程触发 save/restore内核检查点经由 spec 注解给出的 checkpoint path文件系统检查点经由对应注解/命令。gcs_opts.json存在即自动走 gofer 路径日志中会出现 “Saving to GCS via checkpoint gofer” / “Restoring from GCS via checkpoint gofer”删除该文件则回退到本地目录文件权限按需最小化只做 restore 就只给-allow-checkpoint-reads由 sandbox 按场景自动传入使用者无需手工指定。适用前提与限制gofer 仅实现 GCS 一种远端后端runGCS是唯一的run*分支GCS 认证依赖 OAuth2/ADC 体系因此实际部署环境需要可达的 GCS 与合规凭据object_prefix是字符串前缀而非路径拼接漏写/会导致对象名粘连PCU 在safe模式下可能被 bucket 属性静默禁用以Disabling parallel composite upload: ...日志为准此时大页文件退化为 4 路并行的普通写入。相关源码入口汇总入口 runsc/checkpointgofer/main.go、GCS 文件系统服务 runsc/checkpointgofer/gcs/gcs.go、读取器 runsc/checkpointgofer/gcs/reader.go、并行复合写入器 runsc/checkpointgofer/gcs/parallelwriter.go、fork 与启动逻辑 runsc/sandbox/sandbox.go。【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表