ARTICLE DETAIL

资讯详情

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

cri-containerd 1.7.23 离线安装与 Kubernetes 对接实战指南

cri-containerd 1.7.23 离线安装与 Kubernetes 对接实战指南 简介面向Linux AMD64平台的containerd 1.7.23 CRI运行时安装包适合服务器环境部署与离线安装可为Kubernetes节点或独立容器环境快速配置运行时。包内共19个文件含yaml配置模板、systemd服务单元、containerd主程序以及ctr、crictl、runc、containerd-shim等可执行文件和sh脚本整体约101.22MB目录按etc/usr/opt标准布局组织。附带cri-containerd.DEPRECATED.txt提示该CRI版本可能已被新方案取代升级前需参考官方迁移指南。已有261人学习下载通过这份资源能直观了解containerd组件构成借助真实配置模板与可执行文件完成安装部署有效缩短容器运行时的排查和调优时间。1. 这个 tar 包装完你的 Linux 节点就从能跑容器变成能被 K8s 调度cri-containerd-1.7.23-linux-amd64.tar 是 containerd 面向 Kubernetes 发布的 CRI 运行时 tar 包里面打包的是 containerd 主程序、runc shim、ctr 和 crictl 客户端以及配套的 systemd 启动文件。对做交付和搞运维的人来说它解决一个很具体的落地问题在一台全新的 Linuxx86_64 架构机器上不装 Docker、不碰 docker.service就把节点运行时准备到能被 kubelet 直接使用的状态而且离线内网也能拷进去装。这个包适合离线集群交付、私有化部署以及想把节点运行时统一收敛到 containerd 的存量 K8s 用户。2. 为什么是 containerd 而不是 DockerCRI 架构与 1.7.23 的选型理由2.1 CRI 插件和 cri-containerd 包从 dockershim 移除说起Kubernetes 1.24 之后把 dockershim 从 kubelet 里抽掉了kubelet 不再内置把 Docker 翻译成 CRI的那一层。现在 kubelet 只认 CRI 协议它通过 gRPC 跟一个 runtime socket 通信。containerd 是目前最主流、也几乎是默认选择的 CRI runtime它内部实现了一个 cri 插件这个插件把 kubelet 的 CRI 请求翻译成 containerd 自己命名空间下的镜像、容器和任务操作。这也是面试题里反复在问的CRI 与 OCI 分层的含义CRI 是 kubelet 与运行时之间的接口OCI 是运行时与容器进程之间的标准containerd 同时踩在这两层上。那 cri-containerd 这个包名是哪来的早期 containerd 1.0/1.1 时代CRI 还住在独立的 cri-containerd 进程里官方把 containerd 和 cri-containerd 一起打包发布所以留下了 cri-containerd- -linux-amd64.tar 这样的命名习惯。到了 1.7 时代CRI 插件早就是 containerd 主进程的内置插件包名延续了下来内容变成内置 CRI 的 containerd shim 客户端工具 systemd 配置的合集。所以 cri-containerd-1.7.23-linux-amd64.tar 不是一个独立软件而是 containerd 1.7.23 在 linux/amd64 架构下的 CRI 发行包。很多人现在还在搜 linux 安装 docker然后照着老教程一步步装 Docker、配 docker.service最后再把 docker.sock 指给 kubelet。新项目我不推荐这条路。且不说 dockershim 已经移除从资源占用看containerd 没有 docker daemon 那套 API 网关和镜像构建链路内存占用和 PID 数量都小一截在边缘节点和嵌入式 Linux 场景里差距尤其明显。用这一套 cri-containerd 包直接落地省掉中间翻译层kubelet 看到的运行时就是原生的 CRI 语义排查链路也短。2.2 一张表看懂 containerd、ctr、crictl 的分工解压之后 bin 目录下躺着好几个命令初看容易混。我习惯让接手的人先记住一句话containerd 是服务ctr 是给 containerd 服务的原生客户端crictl 是给CRI 插件的客户端。ctr 走的接口和 crictl 走的接口不在同一层前者面向容器运行时本身后者面向 kubelet 视角下的 Pod、sandbox 和容器。| 命令 | 接口层 | 常用场景 | | containerd | 守护进程本体 | 常驻后台负责镜像、容器、快照、事件 | | ctr | containerd 原生 API | 拉镜像、导入导出 tar、调试快照与任务 | | crictl | CRI gRPC 接口 | 查 Pod、拉 pause 镜像、与 kubelet 同一视角 |最典型的分工差异在 sandbox 上ctr run 跑的是一个普通 OCI 容器kubelet 视角里先有 sandbox 再有业务容器这件事 ctr 完全不知道而 crictl 的 runp、create、start 才是按 CRI 的 Pod 生命周期一步步来。调试 K8s 节点问题时不要拿 ctr 去创建 Pod它不具备 CRI 语义创建出来的东西 kubelet 也看不到。反过来镜像导入导出这类底层操作crictl 又没有 ctr 灵活所以这两个工具是配合关系不是替代关系。2.3 版本怎么选1.7 系列为什么比 2.x 更稳1.7.23 是 containerd 1.7 长期维护分支里的补丁版本。这个分支维护得非常克制release 里几乎都是安全和稳定性修复不塞新功能不折腾配置结构。对于生产集群尤其是给客户交付、之后要长期维护的私有化环境我一般会锁死 1.7.x不轻易追 2.x。2.x 引入了新的 NRI、新的快照器体系插件配置路径也有变化在存量 K8s 环境里未必兼容得干净。如果你手头是 K8s 1.25 到 1.31 这一批containerd 1.7 是很稳的搭档。真正决定配套的不是最新而是 kubelet 声明的 CRI 版本与 containerd cri 插件的版本要对得上。containerd 1.7 的 cri 插件实现的是 Kubernetes CRI v1 语义向后兼容做得比较宽。不要因为搜到某篇教程写的是 1.6 就直接装 1.6先看你的集群版本再定。老集群升级、新集群初始化、边缘小节点三种场景选版本策略本来就不一样老集群偏保守新集群看 kubelet 版本下限边缘节点更看重资源占用和离线可维护性。这里也不去争哪个发行版生态最好只说事实containerd 在主流发行版里都有官方维护渠道但私有化交付现场经常没有外网你手里这个 cri-containerd tar 包反而是最可靠的来源。离线场景下版本一旦装上就很难频繁换所以宁可花十分钟确认配套关系也不要急着解压安装。2.4 解压前先做三个校验file、sha256、tar -tf 看目录结构拿到包先别抢着解压三秒钟的检查能省掉后面很多麻烦。第一file 看它到底是纯 tar 还是 gzip 压缩第二sha256sum 对一下发布页给的校验值第三tar -tf 看包内目录结构这决定了你后面往哪解压。file cri-containerd-1.7.23-linux-amd64.tar sha256sum cri-containerd-1.7.23-linux-amd64.tar tar -tf cri-containerd-1.7.23-linux-amd64.tar | head -20file 输出如果写着 gzip compressed data说明真正格式是 tar.gz解压要带 z 参数如果写着 POSIX tar archive就用 tar -xvf不要多按一个 z。tar -tf 是只列内容不解压重点看路径前缀是 usr/ 还是 etc/ 直接开头这一步直接决定后面的落位方式。sha256sum 在离线交付场景尤其重要U 盘拷贝、内网网盘传输过程中文件被截断是常事核一遍校验值能让很多玄学问题在源头消失。不需要去翻 Linux 常用命令大全真正高频的其实就是 tar -tf、tar -xvf 和 cp 这三条。3. 解压落位与第一次启动Linux 下 cri-containerd 的最小安装命令3.1 包内目录结构usr/ 和 etc/ 决定了你往哪解压这个 tar 包的内部布局本质上就是 Linux 文件与目录规划的一个缩影。通常 cri-containerd 包里会有 usr/local/bin 下的几个二进制以及 systemd 的 unit 文件和 containerd 的默认配置模板。正因为包内既有 usr/ 又有 etc/解压位置就有讲究如果你直接往根目录 / 解压二进制会落进 /usr/local/bin配置文件会落进 /etc/containerd 和 /etc/systemd/system如果你解压到一个自定义目录那所有路径都得手动再处理一遍。mkdir -p /opt/containerd tar -tf cri-containerd-1.7.23-linux-amd64.tar我见过不少人在这一步翻车没看包内路径随手 tar -xvf 到 /home/user结果 containerd 命令确实出来了但 systemd 找不到 unit 文件containerd 启动时报cant open /etc/containerd/config.toml然后开始怀疑是不是包坏了。先执行上面两条命令把包内顶层目录搞清楚再做落位整个过程最多多花三十秒。3.2 方案 A直接解压到根目录让 service 文件一次落位最省事的做法是解压到 /让包内 usr/ 和 etc/ 按照约定位置落位。这个方案适合全新的节点尤其是你确认 /etc/containerd 下没有需要保留的旧配置时。sudo tar -xvf cri-containerd-1.7.23-linux-amd64.tar -C / sudo systemctl daemon-reload sudo systemctl enable --now containerd命令里 -C / 是切换目标目录的意思让 tar 把包内的 usr/ 和 etc/ 直接写到系统对应位置。后面两条是让 systemd 重新扫描 unit 文件然后注册并启动 containerd 服务。这里有一个必须养成的意识解压前先备份 /etc/containerd 目录如果有旧配置的话。因为包内的 config.toml 模板会覆盖同名文件一旦覆盖原集群的镜像加速、私有 registry 配置全部丢失这是没有后悔药的操作。启动完成后立刻看状态systemctl status containerd --no-pager ls -l /run/containerd/containerd.sock看到 sock 文件存在就说明 containerd 守护进程已经起来并且监听了 CRI 的默认 endpoint。如果这一步没有 sock 文件后面 crictl 和 kubelet 全都连不上问题会越积越多。3.3 方案 B解压到 /opt/containerd保留可回滚目录如果这是存量节点或者你不想让包内文件散落到系统目录里可以解压到 /opt/containerd 做一个可回滚的部署目录。这个方式在交付场景里更常见因为客户环境往往已经有了各种旧版本工具直接覆盖 /usr/local/bin 容易被同事骂。sudo tar -xvf cri-containerd-1.7.23-linux-amd64.tar -C /opt/containerd sudo cp /opt/containerd/usr/local/bin/* /usr/local/bin/ sudo mkdir -p /etc/containerd sudo cp /opt/containerd/etc/containerd/config.toml /etc/containerd/config.toml先把包解压到 /opt/containerd再用 cp 把二进制拷到 PATH 里的 /usr/local/bin配置文件单独拷到 /etc/containerd。systemd unit 文件如果包内放在 usr/local/lib/systemd/system 或 etc/systemd/system 下需要按实际路径拷到 /etc/systemd/system/containerd.service然后再 daemon-reload。这个方案的好处是你知道每一份文件从哪来、放到哪去卸载时删掉对应路径就能还原坏处是手动步骤多漏一步就启动失败。我一般还会顺手把 /opt/containerd/usr/local/bin 加进 PATH而不是只依赖拷贝到 /usr/local/bin 的那一份。这样如果后续想切换回别的版本直接把 PATH 顺序调一下比覆盖系统目录更安全。不过要注意systemd 启动 containerd 用的是它自己解析的 PATH所以服务文件里最好写绝对路径或者保证 /usr/local/bin/containerd 存在。3.4 启动、自检和开机自启crictl 连不上时先看这句话装完不是万事大吉我要求在交付单上必须记录三条自检命令的输出containerd --version、crictl version、crictl ps。这三条命令能覆盖二进制在不在、CRI 接口通不通、运行时有没有活性三层问题。containerd --version crictl version crictl ps如果 crictl ps 报 connection refused九成是 crictl 的 endpoint 还没指过去。crictl 默认读 /etc/crictl.yaml没有这个文件就按默认值连默认值和 containerd 的实际 socket 经常对不上。手动指一下不改文件也能验证sudo crictl config --set runtime-endpointunix:///run/containerd/containerd.sock sudo crictl config --set image-endpointunix:///run/containerd/containerd.sock sudo crictl pscrictl config 会把配置写进 /etc/crictl.yaml之后所有 crictl 命令都走这个 endpoint。注意写法必须是 unix:// 开头很多人只写 /run/containerd/containerd.sockcrictl 会把它当成不认识的 scheme 然后报错。这一步搞定后containerd 侧就算完全就绪可以进入 K8s 对接阶段。4. 与 K8s 对接把 containerd 设成 kubelet 的容器运行时4.1 生成默认 config.toml并打开 SystemdCgroupcontainerd 自己不带开箱即用的 CRI 配置尤其是离线环境默认配置里有个要命的字段SystemdCgroup 默认是 false。如果你的 kubelet 用的 cgroup 驱动是 systemdkubeadm 1.22 之后的默认值而 containerd 还在用 cgroupfs两者会直接顶牛表现是 Pod 启动后各种 cgroup 写入失败或者节点状态反复 NotReady。先导出默认配置再改关键字段sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml然后编辑 /etc/containerd/config.toml找到 cri 配置段里的 runc options把 SystemdCgroup 打开[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup trueSystemdCgroup 这个参数的含义是让 runc 通过 systemd cgroup manager 创建 cgroup 目录而不是自己直接写 /sys/fs/cgroup。在 systemd 生态里如果两个管理器混用cgroup 的 ownership 会乱kubelet 的 stats 也会拿不到数据。改完之后重启 containerd再确认镜像里那双鞋真的穿对了sudo systemctl restart containerd cat /etc/containerd/config.toml | grep -A2 SystemdCgroup4.2 离线环境最关键的三个参数sandbox_image、registry 镜像、config_path离线集群里 Pod 一直拉不起来十有八九是 pause 镜像没着落。kubelet 拉起每个 Pod 之前都要先启动一个 sandboxsandbox 的镜像默认指向一个公网地址。内网环境根本拉不到所以一定要在 config.toml 里替换成你自己镜像仓库里的 pause 版本。[plugins.io.containerd.grpc.v1.cri] sandbox_image harbor.internal/base/pause:3.9版本号要以你镜像仓库里实际存在的为准不要照抄 3.9。很多交付现场就是先 docker pull 或者 ctr images pull 把 pause 打进去再回来改这个字段。改完重启 containerd然后 crictl images 看一眼 pause 是否真的被识别到。另外两个参数是给 Linux 镜像加速和私有 registry 用的。containerd 1.7 支持用 config_path 指向一个目录目录里按域名建 hosts.toml这样不同 registry 可以走不同配置比老式的 registry.mirrors 写法灵活得多config_path /etc/containerd/certs.d在这个目录下建 harbor.internal 子目录写 hosts.tomlserver https://harbor.internal [host.https://harbor.internal] capabilities [pull, resolve] skip_verify trueserver 表示这个配置对应哪个 registryhost 里可以写多个上游地址skip_verify 是跳过 TLS 证书校验内网自签名证书环境常用。如果有多个上游按顺序写containerd 会依次尝试。离线交付时这套配置几乎是必做的否则镜像拉取会一直卡在 TLS handshake 超时上。4.3 kubelet 侧参数--container-runtimeremote 和 endpoint 怎么传kubelet 要通过 CRI 连 containerd必须在启动参数里指定运行方式与 endpoint。用 kubeadm 初始化集群时这些参数写在 kubeadm 配置里或者是初始化完成后手工改 /var/lib/kubelet/kubeadm-flags.env 再重启 kubelet。--container-runtimeremote --container-runtime-endpointunix:///run/containerd/containerd.sock如果是 kubeadm 初始化通常在 InitConfiguration 的 nodeRegistration.kubeletExtraArgs 里追加kubeletExtraArgs: container-runtime: remote container-runtime-endpoint: unix:///run/containerd/containerd.sock注意新一点的 kubelet 里 container-runtime 可能默认已经是 remote但老版本的默认值还是 docker所以这个参数不能省。endpoint 必须和 crictl 用同一个 socket也就是含 unix:// 前缀且 containerd.sock 文件的权限要允许 kubelet 进程访问。很多交付现场用的是 root 跑的 kubelet一般没问题如果是普通用户跑就要确认 /run/containerd 目录的权限。参数改完kubelet 会重启并把节点状态通过 runtime 上报。验证对接是否成功看节点状态是最直接的kubectl get nodes kubectl describe node node-name | grep -i container看到 Container Runtime Version 显示 containerd://1.7.23说明 kubelet 和 cri 插件已经握手成功。4.4 存量节点切换前drain、驱逐和镜像迁移如果是要把已经跑了 Docker 的存量节点切到 containerd别直接改参数就完事。Docker 和 containerd 的数据目录不互通/var/lib/docker 里的容器和镜像containerd 一概不认。切换前先把节点 cordon 并 drain把业务 Pod 赶到别的节点上去。kubectl drain node-name --ignore-daemonsets --delete-emptydir-datadrain 之后停掉 docker 和 kubelet再做清理。清理 /var/lib/containerd 要格外慎重那是新运行时自己的数据目录第一次启动时不存在反而干净。老节点上如果跑过旧版本 containerd里面可能有 schema 版本不兼容的元数据备份后清掉再启动比较省事。具体怎么处理后面避坑章节会细说。切换完记得把节点重新标为可调度kubectl uncordon node-name这套流程看着繁琐但比直接在热节点上换运行时安全得多。集群里节点多的时候建议先把一台切完、观察几天再批量滚动别贪快。5. 避坑与排查Linux 上装 cri-containerd 踩过的 5 个典型坑5.1 报 This does not look like a tar archive.tar 与 .tar.gz 的扩展名陷阱现象执行 tar -zxvf cri-containerd-1.7.23-linux-amd64.tar 后终端直接报 gzip: stdin: not in gzip format或者 tar: This does not look like a tar archive。原因包的真实格式是 gzip 压缩的 tar.gz但文件名被人改成 .tar或者反过来文件名是 .tar 实际内容却是 gzip。很多运维翻 Linux 常用命令大全时看到 tar 包就习惯带 z这个惯性在这里正好踩坑。解决先 file 看清楚格式再决定参数。纯 tar 用 tar -xvfgzip 压缩用 tar -zxvf。如果文件名是 .tar 但 file 显示 gzip直接用 tar -zxvf 也能解开名字只是表面现象。偶尔也会遇到包下载一半导致文件截断file 显示 POSIX tar archive 但解压到一半报 unexpected EOF这种情况重新传一遍文件不用怀疑命令写错。file cri-containerd-1.7.23-linux-amd64.tar tar -xvf cri-containerd-1.7.23-linux-amd64.tar -C /opt/containerd5.2 containerd 起不来systemd unit 没重载或老 docker 占用现象systemctl start containerd 失败journalctl 里看到 Failed to start containerd container runtime或者 containerd: runc did not terminate successfully 一类的错误。原因最常见的是 cp 完 unit 文件之后没有执行 daemon-reloadsystemd 还在按旧状态找服务其次是节点上残留的 docker 进程已经占用了相关 namespace 或 cgroup 路径containerd 初始化时撞车。解决先把 unit 文件放到位再 daemon-reload一步都不能省。如果还起不来停掉 docker 和相关 socket清理 /var/lib/containerd 里的旧数据。注意这条命令是破坏性的执行前确认这是可以接受丢失的节点——正常交付现场应该是已经 drain 过的空节点。sudo systemctl daemon-reload sudo systemctl stop docker docker.socket 2/dev/null sudo systemctl start containerd journalctl -u containerd --no-pager -n 50看日志里有 schema version 或者 failed to load state 的句子基本就是旧元数据兼容问题备份后清空 /var/lib/containerd 再启动。这一步做完containerd 起不来的问题能消掉八成。5.3 Pod 一直 ContainerCreatingsandbox_image 拉不到现象节点状态 Ready但业务 Pod 卡在 ContainerCreatingkubectl describe 看到的事件是 Failed to pull image registry.k8s.io/pause:3.x: failed to resolve reference。原因containerd 默认 sandbox_image 指向公网地址内网或离线环境没有路由pause 镜像拉不下来所有 Pod 的 sandbox 都建不起来。这不是业务镜像的问题是基础设施镜像断了。解决把 pause 镜像先导到内网仓库再改 config.toml 里的 sandbox_image 指向内网地址重启 containerd最后用 crictl images 确认。改完之后可以手动拉一个 pause 镜像验证sudo crictl pull harbor.internal/base/pause:3.9 sudo crictl images | grep pause如果 crictl pull 也卡住先查 hosts.toml 是不是写错了 registry 域名再查节点到 harbor 的网络。离线环境最常见是 hosts.toml 没生效containerd 还在走默认配置。确认 /etc/containerd/certs.d 路径和 config.toml 里 config_path 一致这个参数写错私有 registry 配置就静默失效。5.4 crictl 连不上endpoint 写法和 socket 权限现象crictl ps 报 connection refused或者 failed to connect, make sure you are running as root and the runtime has been started。原因crictl 默认读 /etc/crictl.yaml文件不存在时按默认值连接而默认的 endpoint 不一定是 unix:///run/containerd/containerd.sock。另一个原因是 socket 文件权限不对普通用户没有访问权限。解决显式把 endpoint 写进 /etc/crictl.yaml路径前必须带 unix:// 协议前缀。同时检查 socket 文件归属sudo crictl config --set runtime-endpointunix:///run/containerd/containerd.sock ls -l /run/containerd/containerd.sock如果权限不足调整 /run/containerd 目录权限或者让运维人员用 sudo 执行 crictl。还有一种少见情况containerd 压根没起来socket 文件不存在这时候 crictl 报的也是同一个错误。先 ls 一下 socket 是否存在再决定去修 crictl 还是去修 containerd这个排查顺序能省很多时间。5.5 国产系统上没有 tar 命令openEuler 最小化安装的补装姿势现象在 openEuler 或麒麟这类国产 Linux 最小化安装的机器上执行 tar -xvf 直接报 -bash: tar: command not found。原因最小化安装只带基础工具链tar 不一定在默认列表里。这一点在国产系统交付现场非常常见因为现场机器往往不带 yum 源离线拿过来一个 cri-containerd tar 包结果连解压工具都没有。解决先用系统自带的包管理器装 tar。openEuler 默认是 dnf/yum有离线 repo 就 yum install -y tar没有就先挂本地 ISO 或拷贝 rpm 包进去。装完再按正常流程解压。另外提前确认基础工具还能补几个tar、gzip、sha256sum、systemctl这几个缺哪个后面都会卡住不如一次性补好。yum install -y tar gzip mkdir -p /etc/containerd tar -xvf cri-containerd-1.7.23-linux-amd64.tar -C /opt/containerddocker 时代很多人习惯装 docker 之后一路下一步切到 cri-containerd 这种手动落位的包就要把缺工具、缺目录、缺配置这些问题都当正常流程来对待。国产系统上不默认带 tar不丢人备份好包、缺啥补啥比硬装一个老版本 docker 强得多。6. 进阶验证从 ctr 到 crictl 走一条完整的容器拉起链路6.1 用 ctr 手动拉镜像并启动容器K8s 对接前的最后一关我习惯走一遍最原始的链路确认 containerd 本身能干活。先 ctr 拉一个小镜像再跑一个一次性容器sudo ctr images pull docker.io/library/nginx:stable-alpine sudo ctr run --rm docker.io/library/nginx:stable-alpine nginx-test echo okctr run 的最后一个参数可以带一个命令和参数覆盖镜像的默认 entrypoint。--rm 表示容器退出后立即清理适合做冒烟测试。看到输出 ok说明镜像拉取、快照创建、runc shim 拉起进程这一整条 OCI 链路是通的。6.2 用 crictl 走一遍真实的 Pod 生命周期ctr 通不代表 CRI 通最后还是要用 crictl 按 kubelet 的方式走一遍。先写一个 sandbox 配置再 runpcat /tmp/sandbox.json EOF { metadata: {name: sbx-demo, namespace: default, uid: 1}, log_directory: /tmp/sandbox-log, linux: {} } EOF sudo crictl runp /tmp/sandbox.jsonrunp 返回一个 sandbox id之后可以在这个 sandbox 里创建并启动业务容器也可以直接看 sandbox 状态。这一条命令能验证的是CRI 插件收到请求、pause 镜像被解析、sandbox 容器被创建整个链路和 kubelet 拉 Pod 时走的是同一条。6.3 最后一个习惯验证 SystemdCgroup 真的生效每次装完我都会顺手做一个检查看容器进程的 cgroup 路径里有没有 .slice 字样。在刚才的 sandbox 容器里执行cat /proc/self/cgroup | head输出里如果出现 system.slice/containerd.service说明 SystemdCgroup 生效如果直接是 docker 或 kubelet 的 cgroupfs 路径说明配置还没对。我现在的习惯是装完 containerd 先跑这三步crictl ps、crictl runp、cat /proc/self/cgroup。三步全过再交给 kubelet 编排基本不会再出运行时层面的问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表