news 2026/9/30 3:30:58

麒麟V11离线部署K8s 1.32.11与KubeSphere完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
麒麟V11离线部署K8s 1.32.11与KubeSphere完整指南

在没外网、没有可用Yum源、只有刚拆箱的麒麟V11服务器、还要求把K8s和KubeSphere全离线装起来的机房场景里,焦虑感是实打实的。标题里这个“信创-k8s”项目,说白了就是国产化服务器+开源容器平台+内网隔离环境的一次硬核落地。这篇文章把整条链路拆开讲清楚:从麒麟V11系统初始化、containerd 2.1.5离线部署、k8s 1.32.11集群初始化,到KubeSphere可视化平台接入,每一步都给出可直接抄作业的命令、参数和坑位。无论你是刚接触信创项目,还是被临时拉去搞离线交付的运维,照着这篇走能少熬好几个通宵。

1. 项目背景与场景还原

1.1 这到底是个什么场景

所谓“全离线安装”,和普通内网环境还不一样。普通内网至少有些基础软件源,而项目现场通常是一个完全隔离的专用网络,连系统补丁都带不进去。我这次面对的是三台银河麒麟服务器版V11,内存64G、磁盘1.2T SSD,系统刚装好,没有配置任何外部软件源。要求是在这个环境里交付Kubernetes 1.32.11集群,并挂上KubeSphere作为可视化集群管理工具。

麒麟V11不是简单换个Ubuntu内核的发行版,它的系统目录结构、服务管理方式、默认防火墙策略都有自己的脾气。最直观的感受是:基于glibc 2.38以上的用户态环境,对containerd 2.x这类新版本容器运行时兼容性很好,但一些老经验里的RHEL系操作路径并不完全通用。比如firewalld和nftables并存的问题、SELinux的默认状态、systemd服务单元的配置方式,都会在部署过程中变成不同形态的坑。

另一个关键点是“信创”这个背景。它意味着软硬件选型必须考虑供应链安全与国产化适配,但落到技术层面,本质上还是Linux生态一脉相承的部署逻辑。k8s和containerd都是CNCF开源项目,在国产系统上跑没有license问题,只是需要确认版本兼容矩阵和二进制运行库依赖。

1.2 这个方案解决的问题

一套完整的离线交付方案,要回答三个问题:软件包里怎么进来、镜像怎么分发、节点间怎么通信。后面所有操作都是围绕这三个问题展开的。

软件包怎么进来:我提前在有网络的机器上,把k8s三个核心二进制、containerd、runc、CNI插件全部下载好,带进内网。这一步没有捷径,只能依赖官方release页和镜像仓库的导出功能。

镜像怎么分发:两种路线可选。节点少时可以直接在每个节点上解压镜像tar包;节点多时在物理服务器上起一个私有registry,用内网HTTP分发。我这次用的registry方案,因为后面还要灌KubeSphere几十个镜像,统一走私有仓库最省事。

节点间怎么通信:k8s集群需要Pod网络、Service网络、节点网络三层打通。物理层没问题,Pod网络我选了Flannel的VXLAN模式,镜像小、配置简单、与containerd调度兼容性好,适合离线快速验证。

排除掉Docker这个中间层,直接使用containerd 2.1.5作为容器运行时,是从k8s 1.24之后社区的主流选择。麒麟系统不需要装Docker,也就少了一大堆兼容性问题。

2. 方案选型与离线物料准备

2.1 版本适配思路与兼容性判断

版本选择是离线部署里最容易踩雷的环节。我最终敲定的版本组合是:麒麟V11 + containerd 2.1.5 + k8s 1.32.11 + KubeSphere(以官方兼容矩阵为准的近期release)。

这里先说适配逻辑。containerd 2.x在v2配置格式下,默认的cri插件路径、runc运行时参数和1.x有较大差异。k8s 1.32.x的kubeadm对CRI支持很成熟,只要containerd的SystemdCgroup打开,socket路径正确,init基本不卡壳。选择1.32.11这个具体patch版本,是因为它是1.32系列的稳定维护版本,修掉了一批已知的kubelet和etcd兼容问题。

KubeSphere这边要单独说。新版KubeSphere对上游Kubernetes版本的兼容矩阵通常滞后一到两个小版本,而k8s 1.32.11属于比较新的大版本主线。我在现场的做法是:先在k8s集群里装好ks-installer,然后立刻查看kubectl describe clusterconfiguration的内部状态,确认KubeSphere各核心组件能正常创建Pod。如果KubeSphere版本过旧导致控制器无法识别1.32的API资源,我建议要么升级KubeSphere版本,要么把集群降到它官方支持的相邻版本。但既然项目标题锁定了1.32.11,我就按“用新版KubeSphere”的逻辑推进。

2.2 离线物料清单

离线安装最怕临场发现少了包,所以在有网环境准备物料时要按清单核对三遍。以下是我这次实际准备的内容:

类型物料版本 / 路径
容器运行时containerd-2.1.5-linux-amd64.tar.gz官方release下载
容器运行时辅助runc.amd642.1.x配套版本
CNI插件cni-plugins-linux-amd64-v1.6.2.tgz官方release下载
k8s二进制kubeadm、kubelet、kubectlv1.32.11 对应rpm或二进制
集群镜像kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、pause、etcd、coredns通过kubeadm config images list获取精确版本
Pod网络flannel v0.25.x镜像 + manifestGitHub release或镜像导出
KubeSphereks-installer、ks-chart、全部组件镜像KubeSphere release包或镜像导出
私有仓库registry:2镜像docker.io导出或离线包

这里面必须多带两样东西:nerdctl和crictl。nerdctl是containerd的命令行工具,离线导入镜像时语法和docker CLI类似;crictl是k8s标准的CRI调试工具,后面所有“容器起没起来”的问题都得靠它查。

2.3 网络拓扑与镜像分发路线设计

三台机器的规划是这样的:

角色IP职责
master1192.168.10.11控制平面、etcd、KubeSphere核心组件
worker1192.168.10.12应用负载
worker2192.168.10.13应用负载

同时我把master1当作内网软件分发节点,私有registry容器就跑在这台机器上,监听5000端口。所有镜像提前打好tag推入registry,其余节点通过containerd的registry hosts配置连接。这个设计直接把“镜像同步到每台机器”的问题消灭了,节点join时只需拉取,不需要拷贝十几GB的tar包。

3. 麒麟V11系统侧初始化

3.1 安装分区与基础配置

麒麟V11的安装程序比较友好,但默认分区不会给容器大量留空间,我装系统时手动把/var/lib/containerd和/var/lib/kubelet所在分区开大,至少预留300G以上。磁盘满了是k8s集群最常见的隐性故障,容器镜像长期堆积能把SSD写爆,所以“磁盘空间就是生命线”这句话在容器环境里不是夸张。

装完系统第一件事是配置静态IP和主机名。三台分别改成master1、worker1、worker2,并把主机名解析写到/etc/hosts。有些部署教程默认让kubeadm自己解析,但在离线环境里DNS往往不完整,手动写hosts是最稳的。

3.2 关闭不需要的服务与安全策略

麒麟V11默认开启了firewalld和SELinux(视发行版定制而定)。我实践中的做法是:

systemctl stop firewalld && systemctl disable firewalld setenforce 0 sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

SELinux和containerd的cgroup挂载、CNI插件端口绑定存在摩擦,日常排障成本远大于安全收益。信创安全合规通常要求最低权限,但这个要求交给后续的安全基线扫描工具去做,部署阶段先保证系统能跑起来。

然后确认swap关闭,kubelet在1.32版本默认要求swap为0:

swapoff -a sed -i 's/.*swap.*/#&/' /etc/fstab

3.3 内核参数与模块加载

容器网络依赖的关键内核模块必须提前加载。我敲了下面这组配置,主要是开启overlay、br_netfilter以及四层转发所需的nf_conntrack:

cat > /etc/modules-load.d/containerd.conf <<EOF overlay br_netfilter EOF modprobe overlay modprobe br_netfilter cat > /etc/sysctl.d/99-k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1 vm.swappiness = 0 vm.overcommit_memory = 1 EOF sysctl --system

这里解释一个原理:k8s的Service是通过iptables/ipvs规则实现的,Pod网桥上的数据包必须经过宿主机的netfilter框架才能做DNAT和SNAT,所以不开启bridge-nf-call-iptables,集群内部Service访问就会出现间歇性不通。这类问题最坑,因为网络基础看起来正常,curl到Service IP却卡死。

4. containerd 2.1.5全离线部署与配置

4.1 为什么绕开Docker直接上containerd

k8s从1.24版本开始彻底移除dockershim,意味着即使装了Docker,kubelet也无法直接操作这些容器。要在生产环境用Docker作为运行时,就得额外装cri-dockerd这个垫片,多一层转换就多一个不稳定点。而containerd本身实现了CRI插件,kubelet可以直接通过gRPC调用,链路最短。

从信创适配角度看,containerd是CNCF毕业项目,没有历史包袱,二进制依赖面小,非常契合国产化系统的离线交付。麒麟V11的用户态库对containerd的支持很好,我在部署中几乎没有遇到动态链接库缺失的情况。

4.2 二进制安装与目录布局

离线装containerd其实就是解压和配置:

tar Cxzvf /opt/containerd-2.1.5-linux-amd64.tar.gz -C /opt cp /opt/containerd/bin/* /usr/local/bin/ install -m 755 runc.amd64 /usr/local/bin/runc mkdir -p /opt/cni/bin tar Cxzvf cni-plugins-linux-amd64-v1.6.2.tgz -C /opt/cni/bin

生成并修改配置:

mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml

这个默认配置是v2格式的,和我们以前见到的v1格式完全不同,关键要改三处:

第一处,SystemdCgroup必须设成true。如果不设置,容器内的cgroup路径由runc自己管理,和kubelet的systemd cgroup驱动冲突,Pod会反复CrashLoopBackOff,并且报错里会出现“failed to run Kubelet: cgroup-driver=systemd”之类的话。

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true

第二处,pause镜像地址改成私有仓库。k8s每个Pod都会先启动一个pause容器来持有网络命名空间,这个镜像默认指向registry.k8s.io/pause:3.10,离线环境拉不到,必须替换:

[plugins."io.containerd.grpc.v1.cri"] sandbox_image = "192.168.10.11:5000/k8s.io/pause:3.10"

第三处,配置registry hosts,让containerd能从内网私有仓库解析镜像。在/etc/containerd/certs.d/192.168.10.11:5000/hosts.toml写入:

server = "http://192.168.10.11:5000" [host."http://192.168.10.11:5000"] capabilities = ["pull", "resolve"] skip_verify = true

skip_verify = true这段很重要。私有仓库如果用的是自签HTTPS证书,containerd默认会拒绝连接,内网环境直接走HTTP省去证书管理,但会失去传输加密,只适合隔离网络。

4.3 systemd管理与故障自检

用下面这个unit文件让containerd开机自启:

[Unit] Description=containerd container runtime After=network-online.target local-fs.target Wants=network-online.target [Service] ExecStart=/usr/local/bin/containerd Restart=always RestartSec=5 Delegate=yes KillMode=process LimitNOFILE=1048576 [Install] WantedBy=multi-user.target

启动后验证:

systemctl daemon-reload && systemctl enable --now containerd crictl info

如果crictl info提示连接失败,先检查socket路径。containerd默认监听unix:///run/containerd/containerd.sock,crictl工具也默认走这个socket,一般不冲突。真正的常见问题是系统里残留了一个老版本的containerd在占用进程或目录,排查时用ps -ef | grep containerd看一下进程特征。

5. k8s 1.32.11集群初始化与网络插件

5.1 kubeadm、kubelet、kubectl的离线安装

麒麟V11如果用rpm方式安装kubelet,依赖关系里可能会默认拉出docker相关的包,所以我这次直接采用官方二进制方式。从k8s release页下载kubernetes-server-linux-amd64.tar.gz,解压后把kubeadm、kubelet、kubectl放到/usr/local/bin,再写kubelet的systemd配置。

kubelet的启动参数不多,核心是指向containerd:

[Unit] Description=kubelet: The Kubernetes Node Agent Wants=network-online.target [Service] ExecStart=/usr/local/bin/kubelet \ --container-runtime-endpoint=unix:///run/containerd/containerd.sock \ --kubeconfig=/etc/kubernetes/kubelet.conf \ --config=/etc/kubernetes/kubelet-config.yaml Restart=always RestartSec=5 KillMode=process [Install] WantedBy=multi-user.target

启动前把kubelet登记为开机启动,但先不要启动它,等kubeadm init后有了配置文件再统一拉起。

5.2 集群镜像离线导入与私有仓库准备

这一步是整个离线流程的核心。先在master1上启动registry:

ctr -n k8s.io images import registry-2.tar ctr -n k8s.io run --rm registry:2 registry

更稳妥的方式是写一个registry容器配置,挂载持久化目录并监听5000端口,这样才叫真正的内网镜像源。

镜像导入到私有仓库有两条路。如果只是少量镜像,直接用ctr或nerdctl:

nerdctl load -i k8s-images-1.32.11.tar nerdctl tag k8s.gcr.io/kube-apiserver:v1.32.11 192.168.10.11:5000/k8s.io/kube-apiserver:v1.32.11 nerdctl push 192.168.10.11:5000/k8s.io/kube-apiserver:v1.32.11

但几十个镜像一个个tag太慢,我推荐用循环脚本。核心思路是先把镜像load进containerd,然后用ctr images list拿到完整列表,再用shell批量打tag并push。等这批操作完成,用curl http://192.168.10.11:5000/v2/_catalog能看到仓库里全部镜像,说明内网源已经具备服务能力。

5.3 kubeadm init参数解析

准备工作做完后,在master1上执行:

kubeadm init \ --kubernetes-version=1.32.11 \ --apiserver-advertise-address=192.168.10.11 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --cri-socket=unix:///run/containerd/containerd.sock \ --image-repository=192.168.10.11:5000/k8s.io \ --upload-certs

几个参数的取舍逻辑值得说清楚。pod-network-cidr用10.244.0.0/16是为了和Flannel默认清单保持一致,避免篡改网络插件配置。service-cidr保持k8s默认的10.96.0.0/12,这样集群内DNS地址固定为10.96.0.10,后续KubeSphere和业务应用都依赖这个地址。

--upload-certs是给高可用场景用的,即使你暂时只规划单master,加上它也能保留控制平面证书的上传能力,未来扩容控制平面节点会方便很多。

执行前先在master1上跑一遍kubeadm config images list --kubernetes-version=1.32.11,核对映像列表是否和私有仓库里的tag完全一致。这一步能提前发现大小写、版本号差异问题,我遇到过最离谱的情况是kubeadm内置的pause版本比containerd的默认sandbox版本落后一个Patch,导致初始化时反复尝试拉取旧版本镜像。

5.4 工作节点join与Flannel离线安装

master1初始化成功后,工作节点join是重复劳动。将kubeadm init输出的join命令复制到worker1、worker2上执行,注意join命令里包含token和CA hash,如果执行超时或报错,重新用kubeadm token create --print-join-command生成新的。

join完成后,给所有节点打上标签:

kubectl get nodes # 确认所有节点处于 NotReady 状态,这是正常的,因为还没有安装网络插件

Flannel离线安装需要两个东西:镜像和manifest。先通过ctr images import flannel.tar,把flannel镜像导入master1的containerd,然后修改flannel的yaml文件里的image前缀为私有仓库地址,再kubectl apply -f kube-flannel.yml。

Flannel Pod启动后,查看节点状态:

kubectl get nodes -o wide

如果节点有IP但Ready状态为NotReady,最快诊断方式是查看kubelet日志:journalctl -u kubelet -f,大多数情况下都是镜像拉取失败或CNI配置文件未生成。

6. KubeSphere离线接入

6.1 版本兼容与安装方式选择

KubeSphere接入k8s集群有两条路线:一个是KubeKey一键创建集群,另一个是在已有集群上通过ks-installer安装。因为集群已经手工搭好了,这里只能走第二条路线,把KubeSphere的核心组件作为工作负载部署到k8s集群中。

离线环境下,KubeSphere的部署包数量非常多,核心组件包括ks-apiserver、ks-console、ks-controller-manager、ks-installer,同时会拉起redis、mysql、minio等支撑中间件。我在有网环境提前把这些镜像全部导出,打成一个导入tar包,进内网后逐个load进containerd,再重新打tag推到私有仓库。

6.2 镜像改造与私有仓库地址替换

KubeSphere默认镜像地址前缀是docker.io/kubesphere或registry.cn-beijing.aliyuncs.com/kubesphere,离线环境必须全部替换。这个替换工作量大,我用sed批量处理yaml文件。

关键修改点有两个:

第一,所有ks-installer相关的YAML文件里的镜像地址换成私有仓库前缀。比如:

sed -i 's|docker.io/kubesphere|192.168.10.11:5000/kubesphere|g' *.yaml

第二,在ClusterConfiguration中配置私有仓库参数。KubeSphere的安装包文档里一般会提到spec.local_registry这个字段,它专门用于离线部署,写入私有仓库地址后,所有组件镜像都会从这个地址拉取:

spec: local_registry: 192.168.10.11:5000

这步配错会导致ks-installer组件全部停留在ImagePullBackOff。排查时可以直接看Pod的events,例如kubectl describe pod ks-installer -n kubesphere-system,从Events里的Failed to pull image报错能一眼看到镜像前缀对不对。

6.3 部署ks-installer与验证组件状态

部署命令严格按官方文档执行:

kubectl apply -f kubesphere-installer.yaml kubectl apply -f cluster-configuration.yaml

然后观察namespace内的Pod情况:

kubectl get pods -n kubesphere-system kubectl get pods -n kubesphere-monitoring-system

等待时间会比较长,因为KubeSphere的监控组件要部署Prometheus、Grafana、Alertmanager,这些组件在离线环境下启动时还会做一长串的配置注入。用一个循环等待就够:

kubectl -n kubesphere-system get pod -l app=ks-installer --watch

最后通过kubectl exec -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-installer -o jsonpath='{.items[0].metadata.name}') -- ls -l确认组件全部Ready。控制台访问地址是任一节点的NodePort,端口通常在30880,初始用户名和密码可以通过ks-installer日志找到。

6.4 存储与中间件的离线配套

KubeSphere默认需要StorageClass,纯离线环境没有云盘,我这次直接用local-path-provisioner把所有中间件数据落到本机磁盘。虽然这个是单点存储,但对于验证场景已经足够,后续上生产再换信创存储阵列。

local-path-provisioner的镜像也是提前导入的。安装后把它设置为默认StorageClass:

kubectl patch sc local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

这样KubeSphere里创建的PVC会自动通过local-path分配目录,不会卡在Pending状态。

7. 故障排查与信创落地心得

7.1 常见故障速查表

这里把我在现场反复遇到的几类问题整理成表,信息密度比长篇日志高得多:

症状根因解决办法
kubeadm init时报container runtime is not runningcontainerd未启动或socket路径错误systemctl status containerd,确认/run/containerd/containerd.sock存在
Pod一直Creating,crictl ps无输出kubelet无法通过CRI连接containerd检查kubelet的--container-runtime-endpoint参数
kubelet日志报Failed to create pod sandbox: open /run/netns: permission deniedSELinux拦截CNI创建网络命名空间关闭SELinux后重启containerd和kubelet
Pod拉取镜像超时registry地址不通或skip_verify未配置用crictl pull 192.168.10.11:5000/k8s.io/pause:3.10单独测试
Flannel一直CrashLoopBackOffCNI插件目录路径不匹配确认/opt/cni/bin下cni-plugins解压完整
KubeSphere ks-installer不响应ClusterConfiguration未配置local_registry查看ks-installer日志,确认image地址前缀
节点失联报PLEG is not healthy因swap或Cgroup配置变化导致kubelet假死彻底重启机器,保持系统参数一致

7.2 离线版本锁定与升级的额外建议

在信创项目里,“锁定版本”是个非常重要的运维习惯。因为没有外网,任何一次升级都意味着重新准备物料、重做测试。我建议所有镜像在导入私有仓库后,立即通过ctr images list导出一次仓库内的全量清单,这份清单要留档,后续审计和版本追溯都靠它。

还有一个容易被忽略的点:内网节点的时间同步。麒麟V11默认会启用chronyd,但如果内网没有NTP服务器,所有节点时间偏差会越来越大,而KubeSphere的监控组件对数据时间戳非常敏感。我建议至少在一台机器上部署一个基础NTP服务,或者在离线包里带好chrony配置并手动纠正一次时间,否则监控曲线全是锯齿状,排障时完全没法看。

7.3 个人实操体会

整套流程踩下来的最大感受是,离线部署的难点不在于技术本身,而在于耐心。containerd 2.x和k8s 1.32的版本组合比老一代docker+dockershim方案清爽太多,但前提是把私有仓库、镜像tag、CNI配置等基础设计想清楚。只要提前把物料清单准备好、每个节点的系统配置保持一致,整个集群从空系统到KubeSphere控制台可视化,在三台机器上完全可以在一天内完成。最后再分享一个小技巧:所有离线安装的压缩包、镜像列表、YAML清单,不要只放在一个U盘里,最好在项目现场用一台笔记本再架一个临时HTTP服务,配合一台内部NTP服务器,这样后面加节点、扩环境时会发现所有操作都轻松很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 3:30:39

迈普交换机CLI运维实战:高频命令与避坑指南

简介&#xff1a;本资源是一份面向网络运维工程师、IT管理员及通信类专业学习者的迈普交换机实操配置指南&#xff0c;聚焦命令行操作体系与日常维护场景&#xff0c;解决设备管理入门难、命令记忆混乱、模式切换易出错等实际问题。文档为单个229KB的Word文件&#xff08;.docx…

作者头像 李华
网站建设 2026/9/30 3:30:12

ENOVIA集成与二次开发实战:对象模型、REST/MQL与JPO开发避坑指南

简介&#xff1a;这份资源是一份面向工业软件领域从业者与PLM系统实施人员的Dassault Systmes ENOVIA系统集成与二次开发教程文档&#xff0c;重点讲解ENOVIA在3DEXPERIENCE平台中的系统架构、产品生命周期管理中的核心作用&#xff0c;以及如何通过API与COM接口实现与CATIA、S…

作者头像 李华
网站建设 2026/9/30 3:29:36

快速上手陌生项目:从环境搭建到安全改动的48小时路径

快速上手一个自己不太熟悉的新项目&#xff0c;拼的不是谁看得快&#xff0c;而是谁能在最短时间里把"我不确定"变成"我知道去哪儿查、找谁问、改哪里"。我带过几个从零接手的项目&#xff0c;也当过被临时拉进陌生代码库的救火队员&#xff0c;回过头看&a…

作者头像 李华
网站建设 2026/9/30 3:29:34

UE5.3 C++第一人称射击框架实战教程

简介&#xff1a;本资源是一份面向UE5初学者与中级开发者的实战型FPS游戏开发教程&#xff0c;聚焦第一人称射击游戏从零构建的核心能力训练&#xff0c;帮助开发者系统掌握UE5中玩家控制、交互逻辑、射击机制、敌人AI行为建模及HUD界面实现等关键技能。资源为单文件PDF文档&am…

作者头像 李华
网站建设 2026/9/30 3:29:34

Retrofit实战指南:从原理、注解到拦截器与高频坑解析

接手一个老项目的第一周&#xff0c;我在代码里翻到一个 AsyncTask&#xff0c;类名叫 LoadDataTask&#xff0c;里面用 HttpURLConnection 写了两百多行网络请求。connection 手动开关、InputStream 手动读、JSON 手动解析、错误手动分类&#xff0c;测一次要吐一次。我当时就…

作者头像 李华