在信创或者纯内网隔离项目里,你大概率会遇到这个场景:刚交付一台 openEuler 22.03 LTS (aarch64) 服务器,机器本身干干净净,安全策略要求不能连接外网,但业务方又点名要求 Docker 跑在 26.1.3 这个版本上。系统自带 yum 源配了等于没配,yum install docker想都不用想。这时候很多人第一反应是“那就下载个 tar 包解压呗”,真正动手才发现,离线安装 Docker 的难点根本不是 Docker 本身,而是依赖解析、架构匹配、镜像导入这一整套链路。
这篇文章我会把我在 openEuler 22.03 LTS aarch64 上离线安装 Docker 26.1.3 的完整过程写出来,从在有网机器上准备安装包、处理 RPM 依赖,到配置 daemon.json、启动服务,再到离线镜像的导入和管理,整个过程都附上我的操作命令和踩坑记录。如果你接下来要面对的是信创环境、党政内网、生产隔离区这类场景,这篇文章可以直接当操作手册来用。
1. 为什么离线装 Docker 会卡在这三件事上
1.1 没有可用 yum 源,一切依赖都得自己背
很多人以为离线安装就是把 rpm 包拷过去rpm -ivh docker-ce.rpm就完事,实际上 Docker 的 RPM 包有一堆依赖:container-selinux、containerd.io、docker-ce-cli、docker-compose-plugin、docker-buildx-plugin,有些环境还牵扯到libcgroup、slirp4netns、fuse-overlayfs。在联网环境下这些依赖会被 yum 自动拉取并安装,但你一旦离线,yum 源是空的,缺一个依赖就装不上,而且 rpm 报错信息非常直白——Requires: container-selinux >= 2.74,缺什么就给你列什么。
所以离线安装的第一步不是下载 Docker 本身,而是把所有依赖看成一个整体,一揽子打包带走。
1.2 aarch64 架构带来的包下载陷阱
这一条最容易翻车。很多人在 x86_64 的电脑上把 Docker 的 rpm 包下载好了,传到 aarch64 的 openEuler 机器上,结果 rpm 安装直接报“wrong architecture"。Docker 官方仓库对所有软件包都有x86_64和aarch64两套编译产物,下载时认准文件名里的aarch64字样,千万别想当然。
另外,openEuler 22.03 LTS 的 aarch64 版本和 CentOS 7/8 的 aarch64 版本在 RPM 上是兼容的,Docker 官方仓库中 CentOS 目录下的aarch64包可以在 openEuler 上直接使用,这是社区验证过的路线,也是我们这次离线安装的基础。
1.3 openEuler 22.03 与 Docker RPM 包的兼容关系
openEuler 22.03 LTS 是基于 RHEL 8 的兼容性设计,官方文档也说明了这一点。而 Docker 官方仓库的centos目录下同时提供el7和el8两套包。实测下来,在 openEuler 22.03 LTS (aarch64) 上选择el8架构的 Docker 26.1.3 包,配合 openEuler 自带的依赖环境,兼容性最好。
这里我明确一下版本组合,这是我在生产环境反复验证过的:
| 软件包 | 版本 | 说明 |
|---|---|---|
| docker-ce | 26.1.3 | 核心守护进程 |
| docker-ce-cli | 26.1.3 | 命令行工具,必须与 docker-ce 同版本 |
| containerd.io | 1.6.24 或 1.7.x | 容器运行时,建议用 1.7.x 稳定版 |
| docker-compose-plugin | 2.24.0+ | compose 插件,如果不用 compose 可不装 |
| docker-buildx-plugin | 0.13.0+ | buildx 插件,如果不用自定义构建可不装 |
| docker-ce-rootless-extras | 26.1.3 | 无 root 运行模式支持,按需安装 |
| container-selinux | >= 2.74 | 关键依赖,openEuler 源里一般有 |
依赖版本不对,安装过程就可能卡在container-selinux上。后面我会专门讲怎么处理这个坑。
2. 在有网机器上准备好一套完整的离线安装包
2.1 用一台同架构机器做“搬运工”
离线包必须在有外网环境下载,而且要尽量用一台同样是 openEuler 22.03 LTS aarch64 的机器来下,因为这样才能用 yum 的依赖解析能力把依赖关系完整算出来。
如果手头只有 x86_64 的联网机器,也能下载 rpm 包,但依赖解析会不准,因为你本地装的依赖和 openEuler aarch64 环境可能不一样。我建议优先找一台同架构的机器,云上开一台 ARM 机器也行,用完就释放,成本不高但能省很多事。
这台机器上需要确保能和目标机同样是 openEuler 22.03 LTS 或者至少是同类 aarch64 RPM 系统。
2.2 配置 docker-ce 源并锁定 26.1.3 版本
在这台联网机器上执行以下命令,先把 Docker 官方仓库接入:
# 安装 yum 工具集 yum install -y yum-utils dnf-plugins-core # 添加 docker-ce 官方仓库 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo添加完成后,docker-ce.repo文件里有多个不同版本的仓库,比如docker-ce-stable、docker-ce-test、docker-ce-nightly,默认启用的是docker-ce-stable。为了让后续下载时版本精确锁定到 26.1.3,可以临时禁用其他仓库:
# 查看仓库列表 yum repolist # 只保留 stable 仓库 yum-config-manager --disable docker-ce-test yum-config-manager --disable docker-ce-nightly锁定版本直接用--downloadonly是不行的,还需要把版本号写全。先查一下可用的具体版本:
yum list docker-ce --showduplicates | grep "26.1.3"输出结果里会看到类似这样的一行:
docker-ce.aarch64 26.1.3-1.el8 docker-ce-stable记下这个完整版本号26.1.3-1.el8,下一步下载时用完整版本号指定。
2.3 用 repotrack 把依赖一次拉全
普通情况下很多人会用yumdownloader docker-ce --downloadonly,但yumdownloader只下载指定包本身,不会自动拉依赖。要一次性把 Docker 及所有依赖都拉下来,最好用repotrack命令(来自yum-utils包)或者dnf download --resolve。
我的做法是这样的:
# 创建存放 rpm 包的目录 mkdir -p /root/docker-offline cd /root/docker-offline # 使用 dnf --resolve 下载 docker 及所有依赖 dnf download --resolve docker-ce-26.1.3-1.el8 docker-ce-cli-26.1.3-1.el8 containerd.io docker-compose-plugin docker-buildx-plugin docker-ce-rootless-extras--resolve参数会像正常安装一样解析所有依赖并下载,这样得到的 rpm 包集合是最完整的。如果这台机器的源里恰好缺某个依赖,比如缺了container-selinux,那么dnf download会直接报错提示缺哪个包,这时候你需要先配置好 EPEL 源或者 OpenEuler 对应版本的epol源,再重试。
下载完成后,当前目录里会有至少七八个甚至十几个 rpm 文件,这些就是要带到离线环境去的全部家当。
2.4 下载后必须做的包列表核对
这一步很多人会跳过,但恰恰是最值得做的。打包前先在联网机器上把 rpm 文件清单列出来,比对一下目标机上是否已经存在这些包,避免重复或者漏带。
我通常会执行:
rpm -qpR docker-ce-26.1.3-1.el8.aarch64.rpm | grep -E "container-selinux|containerd.io|docker-ce-cli"-qpR可以查看这个 rpm 包安装时需要的依赖,配合目标机上的rpm -qa对比,就能提前知道目标机缺哪些。
这里还有一个容易踩的坑:dnf download --resolve会把这台联网机器上已经安装过的某些包也下载下来,但这些包不一定需要在目标机重新安装。比如libcgroup如果目标机已经存在,就没必要再重复安装。为了稳妥起见,我建议把下载好的 rpm 全部带到目标机后,先用rpm -ivh *.rpm --test做一次 dry-run 检查,让 rpm 自己判断哪些需要装、哪些已经存在。
3. 离线安装执行:rpm 安装顺序和依赖冲突处理
3.1 上传离线包到目标机的正确姿势
把打包好的 rpm 文件传到目标机有几种方式:内网 scp、U盘拷贝、内部文件服务器拉取都行。我个人的习惯是先打包再传,因为文件一多容易丢:
# 在联网机器上打包 tar czf docker-offline-rpm.tar.gz /root/docker-offline/然后传到目标机的/root/docker-offline/目录下解压:
mkdir -p /root/docker-offline tar xzf docker-offline-rpm.tar.gz -C /root/docker-offline/注意解压后检查一下 rpm 文件的架构,确认每一个都是aarch64:
rpm -qip /root/docker-offline/docker-ce-26.1.3-1.el8.aarch64.rpm | grep Architecture预期输出是aarch64,如果这里是x86_64,说明下载错了,赶紧回上一步重来。
3.2 依赖缺失时怎么定位和补齐
进入目标机后,先不要急着rpm -ivh,先做一次预安装检查:
cd /root/docker-offline rpm -ivh *.rpm --test--test只做依赖检查,不实际安装。如果输出里出现error: Failed dependencies:,后面会列出一堆xxx is needed by yyy。这时候别慌,按下面的顺序排查:
第一步,先看缺失依赖是不是已经存在于目标机系统中,只是版本不达标:
rpm -qa | grep container-selinux rpm -qa | grep containerd.io第二步,如果是版本不达标,看看我们下载的 rpm 包里有没有更高版本:
ls *.rpm | grep container-selinux第三步,如果下载目录里也没有,那就在联网机器上把缺的依赖专门下载下来,单独拷贝过来:
dnf download container-selinux这里最容易卡住的就是container-selinux >= 2.74。openEuler 22.03 LTS 的基础源里很可能没有这个包,需要在联网机器上先安装 EPEL 源或者 openEuler EPOL 源,再下载container-selinux。另外,如果 Docker 26.1.3 要求的container-selinux版本太高,而你下载到的只有旧版本,可以考虑装一个低版本的 Docker 26.1.3 不行就换 26.1.2?我的建议是先尝试升级container-selinux,尽量不要为了绕过依赖去加--nodeps,那是给自己埋雷。
3.3 旧版本 Docker 如何平滑替换
如果目标机上已经存在旧版本 Docker(比如 19.03.8 或者 20.10.x),建议先卸载干净再装新版。直接rpm -ivh会因为版本冲突报错。
卸载旧版本的顺序有讲究,先停服务再卸载:
# 停止 docker 服务 systemctl stop docker systemctl stop containerd # 删除旧版本包 rpm -qa | grep docker | xargs rpm -e --nodeps rpm -qa | grep containerd | xargs rpm -e --nodeps # 清理残留目录 rm -rf /var/lib/docker rm -rf /var/lib/containerd这里提醒一句:rm -rf /var/lib/docker会把所有本地镜像和容器数据全部清掉。如果旧环境里还有需要保留的镜像,一定要先docker save备份,别急着删。
3.4 安装结果的快速验证
预检查没问题后就可以正式安装了。安装顺序建议让 rpm 自动解析,直接一次性安装:
cd /root/docker-offline rpm -ihv *.rpm --replacepkgs如果依赖完整,这一步应该能顺利跑完。安装完可以用rpm -qa | grep docker看看装上了哪些包,预期看到docker-ce-26.1.3-1.el8.aarch64、docker-ce-cli-26.1.3-1.el8.aarch64、containerd.io-1.7.x。
注意,此时 docker 还没有启动,docker version命令能正常输出版本号但不代表 docker 服务可用。要看服务状态,先往下走配置这一步。
4. 启动 Docker 前的配置:daemon.json、cgroup 与防火墙
4.1 daemon.json 里哪些配置是离线环境真正需要的
Docker 安装完成后不要急着systemctl start docker,先想清楚这个离线环境的实际需求。很多网上的教程会一股脑往里塞一堆镜像加速器地址,但在纯内网环境里,这些地址不仅没用,反而会因为无法访问导致 docker 启动时去反复探测,拖慢启动速度。
我针对纯离线环境推荐的/etc/docker/daemon.json是这样的:
{ "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "storage-driver": "overlay2", "data-root": "/var/lib/docker", "iptables": true, "ip-forward": true, "live-restore": true }各项配置的解释:
exec-opts:指定 cgroup 驱动为systemd。在 systemd 作为 init 进程的系统上,这能避免容器内进程和系统资源统计出现偏差,尤其是后续要接 Kubernetes 的话,这个必须和 kubelet 保持一致。log-driver和log-opts:限制单个容器日志文件大小到 100MB,保留 3 个文件。日志爆盘是生产环境最常见的故障之一,离线环境没人频繁清理,提前限制很重要。storage-driver:overlay2是当前内核上的最佳性能选择,openEuler 22.03 的默认内核完全支持。iptables:允许 Docker 自动管理 iptables 规则,实现容器网络端口映射。如果内网有特殊策略不建议关闭这个选项,关掉以后端口映射会大面积失效。live-restore:这个选项允许在 Docker 守护进程升级或重启时,容器保持运行不停机。对于生产环境非常实用。
如果你在内网有一个 harbor 镜像仓库,比如registry.internal:5000,那么还需要加上insecure-registries配置(后面讲镜像导入时会展开)。
4.2 cgroup 驱动选 systemd 还是 cgroupfs
这一节单独拿出来说,是因为systemd和cgroupfs的选错会带来非常隐蔽的问题。
系统在用 systemd 作为 init 进程时,也接管了 cgroup 的挂载和管理。Docker 默认使用cgroupfs驱动,这样就会有两个 cgroup 管理器同时存在。平时跑几个容器没感觉,但只要系统的 CPU、内存压力上来,两个管理器同时修改 cgroup 树,就可能出现资源统计错乱,或者容器莫名其妙被 OOM Kill。
用native.cgroupdriver=systemd让 Docker 走 systemd 的接口来管理 cgroup,就能规避这个问题。配置好后可以用下面的命令验证:
docker info | grep "Cgroup Driver"预期输出:
Cgroup Driver: systemd这一步不仅对单独跑 Docker 有意义,也是后面接 K8s 的基础,建议不管是否接 K8s,都按systemd配。
4.3 firewalld 默认策略对容器端口映射的影响
openEuler 22.03 默认会启用 firewalld,而它的默认FORWARD策略是DROP。Docker 的端口映射依赖 iptables 的FORWARD链来转发流量,如果FORWARD默认是DROP,就会出现一个经典问题:容器起来了,端口也映射了,但从宿主机外部访问不到容器端口。
这个问题在 x86 环境一样存在,但在 openEuler 系列的有些版本里尤其明显。处理方式有两种:
第一种,直接关掉 firewalld(内网环境建议,前提是安全策略允许):
systemctl stop firewalld systemctl disable firewalld第二种,保留 firewalld,但调整转发策略。可以修改/etc/firewalld/direct.xml,添加 Docker 所需的转发规则,但配置比较繁琐,不如直接关闭省心。
如果安全策略要求 firewalld 必须开着,那就单独针对docker0网桥放通 FORWARD 链。我在生产环境更倾向于用第一种,因为容器场景里 Docker 管理的 iptables 规则已经很多了,再加一层 firewalld 反而容易互相干扰。
还有一个容易被忽略的点:/etc/sysctl.conf里需要确认net.ipv4.ip_forward是 1。虽然 Docker 启动时会自动打开转发,但 firewalld 重启或者系统重启后,这个值可能被重置,导致容器无法访问外网。建议显式配置:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p4.4 用 systemctl 完成启动与开机自启
配置好 daemon.json 之后,开始真正的启动:
# 重新加载 systemd 配置 systemctl daemon-reload # 设置开机自启并立即启动 systemctl enable --now docker # 查看服务状态 systemctl status dockerdocker.service正常的话,状态应该是active (running)。再用这些命令做二次确认:
# 查看 docker 服务端和客户端版本 docker version # 查看 docker 全局信息 docker infodocker version里如果Server部分有信息,说明 daemon 已经正常启动了。如果只显示Client而没有Server,说明客户端能执行,但 daemon 没起来,需要用journalctl -u docker --no-pager -n 50查看日志。
5. Docker 装好只是开始:离线镜像怎么进去
5.1 最直接的 docker save/load 传包法
Docker 装好之后,最常见的任务是:我需要在离线环境里跑一个 MySQL 8.0,或者跑一个 Nginx,但完全没有外网可以docker pull。这时候需要在一台有外网的机器上先把镜像拉下来,打包成 tar 文件,带到离线环境再导入。
在有网机器上:
# 拉取镜像并指定版本 docker pull mysql:8.0 docker pull nginx:1.25-alpine # 将多个镜像打包到一个 tar 文件 docker save mysql:8.0 nginx:1.25-alpine -o images.tar # 也可以单个镜像打一个包 docker save mysql:8.0 -o mysql-8.0.tar传给离线目标机后:
docker load -i images.tar加载完成后用docker images确认镜像列表。注意三点:
第一,docker save和docker export不是一回事。save保存的是镜像分层和元数据,load加载后可以基于它docker run创建容器;export导出的是容器文件系统快照,导入后没有镜像的历史分层。别用混了。
第二,镜像的架构要和宿主机一致。在 aarch64 的机器上只能运行 aarch64 的镜像,如果你只有 x86_64 的镜像,在 ARM 机器上docker load能成功,但docker run会报exec format error。在有网机器上下载镜像时,可以用docker pull --platform linux/arm64指定架构。
第三,一次性docker save多个镜像到一个 tar 之前,先确认这些镜像的 tag 在列表里的 REPOSITORY 和 TAG 列存在。如果本地有一些<none>的悬空镜像,打包时不会把 tag 打进去。
5.2 多节点环境用 registry 私有仓库更省事
如果你的离线环境有多台机器,每台都docker load一遍比较低效。更专业的做法是在内网起一个私有的 Docker Registry。
先在有网环境把registry:2镜像拉下来并打包带进去:
docker pull registry:2 docker save registry:2 -o registry2.tar在离线环境其中一台机器上导入并启动:
docker load -i registry2.tar docker run -d --name registry \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ --restart=always \ registry:2为了让其他机器能通过 HTTP 访问这个私有仓库,需要在每台客户机的/etc/docker/daemon.json里加上:
{ "insecure-registries": ["registry.internal:5000"] }然后把 daemon.json 的地址换成实际内网地址,重启 docker:
systemctl restart docker接下来就可以把从外网导入的镜像重新打 tag 并推送到私有仓库:
docker tag mysql:8.0 registry.internal:5000/mysql:8.0 docker push registry.internal:5000/mysql:8.0其他机器只需要docker pull registry.internal:5000/mysql:8.0就能拉取。这种方案的优点是镜像只需要在一个节点维护,适合稍具规模的集群环境。如果公司有现成的 Harbor,那就更好了,直接配置到registry-mirrors或者insecure-registries都行。
5.3 离线镜像的版本锁定和瘦身建议
离线环境最怕的是镜像版本漂移。今天在外面下载mysql:8.0得到的镜像,和三个月后再下载mysql:8.0得到的镜像,很可能完全不一样,因为8.0这个 tag 是活的。所以离线环境下做镜像管理,必须把镜像固定到不可变版本。
方法有两种:
第一种是用docker image inspect拿到镜像的 Digest:
docker inspect --format='{{index .RepoDigests 0}}' mysql:8.0输出类似:
mysql@sha256:xxxxx记录下这个 SHA256 值,后续在别的机器验证镜像时,用它比对确认一致性。
第二种更简单粗暴:不用8.0这种短 tag,而是用8.0.36这种具体版本号去拉取,packages 和镜像都锁定到具体版本号。
镜像瘦身方面,离线环境下仓库空间有限,建议拉镜像时优先选择 alpine 版本的镜像,比如nginx:1.25-alpine、redis:7-alpine,体积通常是完整版的一半甚至更小。另外,用docker image prune定期清理悬空镜像。
6. 实测中的坑:从 RPM 报错到容器网络不通
6.1 container-selinux 版本冲突的完整排查过程
这是我安装 Docker 26.1.3 时遇到的第一道坎。在目标机上执行rpm -ivh *.rpm --test,输出里赫然一行:
error: Failed dependencies: container-selinux >= 2.74 is needed by docker-ce-26.1.3-1.el8.aarch64当时我联网机器的dnf download --resolve并没有把container-selinux拉下来,因为那台联网机器上已经装了高版本的container-selinux,所以依赖解析认为这个条件已满足,就不重复下载了。但目标机是全新的,并没有这个包。
这个坑的本质是:dnf download --resolve的依赖结论是基于“当前这台机器”的包环境算出来的,不代表目标机的环境。
解决办法是:回到联网机器,显式下载container-selinux,同时把它的依赖也拉下来:
dnf download --resolve container-selinux然后把下载得到的container-selinuxrpm 也一并拷到目标机,重新执行rpm -ihv *.rpm --test。这次依赖检查就能通过了。
这里给一个建议:离线安装的完整 rpm 包集合,一定要在彻底干净的全新环境里验证过一遍,不要只在自己有网机器上验证。推荐的做法是用一台全新安装的 openEuler 22.03 LTS aarch64 虚拟机做一次完整的离线安装演练,把所有缺的依赖都补齐,形成最终版本的包列表。
6.2 启动失败且 journalctl 无有效日志时怎么办
有一次我在目标机执行systemctl start docker,等了半天,状态还是inactive (dead)。journalctl -u docker --no-pager -n 50里只有几行加载配置的日志,没有报错信息。这种“无声失败”最容易让人没头绪。
后来排查发现,问题出在/etc/docker/daemon.json的 JSON 格式上。我在配置exec-opts时把引号写成了中文全角引号,JSON 解析直接失败,Docker 启动时发现配置无效,就默默地退出了。
检查方法很简单:
docker daemon --validate或者:
dockerd --config-file /etc/docker/daemon.json前台运行 dockerd 会打印详细的启动日志,发现问题后可以直接Ctrl+C退出修改。
从那以后我的习惯是:改完 daemon.json 先用python3 -m json.tool /etc/docker/daemon.json验证一下 JSON 语法,再重启 docker。
6.3 容器内 DNS 解析失败的根因
Docker 装好、镜像也导入了,跑一个nginx容器进去以后,发现容器内curl http://some-service.internal报could not resolve host。在纯内网环境,这个问题非常高频。
定位思路是这样的:先确认容器内/etc/resolv.conf的内容。Docker 默认会把宿主机的 DNS 配置复制到容器里,如果宿主机/etc/resolv.conf指向的是内网 DNS10.0.0.2,容器应该也能用。但有时候系统用了 systemd-resolved,/etc/resolv.conf里指向的是127.0.0.53,这个地址在容器网络命名空间里并不存在。
解决方法是在 daemon.json 里显式指定 DNS:
{ "dns": ["10.0.0.2", "10.0.0.3"], "dns-search": ["internal.domain"] }然后:
systemctl restart docker这里要提醒一下:修改 daemon.json 的 DNS 后,重启 docker 会重启所有容器,如果没有live-restore: true或者不是用--restart=always运行的容器,重启后可能不会自动拉起。生产环境操作前务必先确认容器的重启策略。
6.4 端口映射不通:FORWARD 链和防火墙的联合排查
容器跑起来了,docker ps显示端口映射正常,宿主机 curl 127.0.0.1:8080也能通,但其他机器访问宿主机IP:8080就是不通。这类问题的排查链路比较固定,按顺序走一遍基本能定位。
第一步,确认 Docker 端口映射本身没问题:
iptables -t nat -L -n | grep 8080如果能看到DNAT规则,说明 Docker 自己生成的 iptables 规则是存在的。
第二步,检查FORWARD链默认策略:
iptables -L FORWARD -n | head -20如果看到默认策略是DROP,且没有针对docker0的 ACCEPT 规则,那问题就出现在这里。解决办法前面已经说过了,systemctl stop firewalld && systemctl disable firewalld是最直接的。
第三步,检查端口是否被其他进程占用,以及目标机的防火墙安全组是否放行了这个端口。在纯内网环境,有时候是网络设备或者安全组策略挡了流量,不是主机层面的问题。
排查完这三个层面,99% 的端口映射问题都能找到答案。剩下的 1% 往往是网络设备上的策略路由或 VLAN 隔离,那已经不是 Docker 层面能解决的问题了。
最后说一点我的真实体会
这套流程我在好几个内网项目里完整跑过,每次最大的心得就是:离线安装不值钱,值钱的是把依赖搞明白、把版本锁住、把镜像管理好。哪怕你这次只需要在一台机器上装 Docker,也建议把下载好的 rpm 包、镜像 tar、daemon.json、安装脚本整体归档到一个目录里,内部组个仓库管理起来。以后再有新机器交付,直接一键装上,十分钟内就能看到docker info输出完整的系统信息。
如果在实际安装过程中遇到什么 RPM 依赖或者容器网络的问题,欢迎在评论区留下你的报错信息,我看到了都会逐一回复。别的不敢保证,这类坑我踩得是真不少。