装 Docker 本身不复杂,麻烦的是环境不一样:有的机器能直接上外网,有的完全断网,还有的能上网但拉镜像慢得受不了。下面把在线安装、离线安装和镜像源配置放在一起讲一遍。脚本和配置都是平时在用的那套,openEuler 20/22/24、CentOS 7、RHEL、Ubuntu 18.04、Debian 10 都跑过。
一、先把三个"源"分开
装 Docker 遇到的慢和失败,大部分是把三个不同的东西混成一件事了。
| 名称 | 配置位置 | 作用 | 生效时机 |
|---|---|---|---|
| 软件源(repo) | /etc/yum.repos.d/docker-ce.repo、/etc/apt/sources.list.d/docker.list |
下载 docker-ce、containerd.io 等安装包 |
安装与升级 |
| 镜像加速器(registry mirror) | /etc/docker/daemon.json 的 registry-mirrors |
加速 docker pull 拉取 Docker Hub 镜像 |
拉取镜像 |
| 私有仓库 / 内网仓库 | insecure-registries 或 /etc/docker/certs.d/ |
访问自建 Harbor、Registry | 拉取私有镜像 |
这三个不能互相替代。把软件源换成清华,docker pull 该慢还是慢;配了镜像加速器,yum install docker-ce 也不会快一点。离线环境里三个概念同时存在,更容易搞混。
二、在线安装
在线安装就一个变量:包从哪下载。Docker 官方源在国内时好时坏,几个国内镜像站都有完整镜像,把 repo 地址和 GPG 公钥一起换掉就行。
2.1 Ubuntu / Debian
# 1. 依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 2. GPG 公钥(注意 keyring 目录和 signed-by 写法)
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 3. 软件源
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] \
https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 4. 安装
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
有三处容易错:
- 公钥和 repo 要来自同一个镜像站。混着用一般也能装上,但镜像站换签名的时候,先坏的往往是这种组合。
signed-by是现在推荐的写法,apt-key add已经废弃了。还在用旧写法的话会看到Key is stored in legacy trusted.gpg keyring的警告。VERSION_CODENAME不能是空的。有些国产衍生系统在/etc/os-release里没这个字段,照抄会生成一行缺代号的源,apt-get update直接报Malformed entry。这种情况手动填jammy、focal、bookworm这种代号。
2.2 RHEL / CentOS / openEuler
# 1. 依赖与仓库管理插件
sudo yum install -y ca-certificates curl gnupg2 yum-utils
# 2. 拉取 repo 文件
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo \
-o /etc/yum.repos.d/docker-ce.repo
# 3. 镜像站的 repo 文件可能还指向官方地址,统一替换一次(已经是镜像地址就是空操作)
sudo sed -i 's|https://download.docker.com|https://mirrors.aliyun.com/docker-ce|g' \
/etc/yum.repos.d/docker-ce.repo
# 4. 安装
sudo yum install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
docker-ce.repo 里是用 $releasever 拼路径的,比如 .../linux/centos/7/x86_64/stable。这个变量 yum 会按系统版本算,CentOS 7/8 算出来正好是 7/8,对得上仓库目录。系统版本比仓库覆盖范围新的时候,问题就来了。
2.3 openEuler 上的 $releasever
openEuler 22.03 算出来是 22,24.03 是 24。而 Docker 仓库只有 centos7、centos8、centos9 三个目录,没有 centos22、centos24,yum makecache 直接 404。
脚本里是按主版本号映射的:
map_docker_releasever() {
local major
major="$(printf '%s' "$OS_VERSION" | tr -d '\r')"
major="${major%%.*}"
major="${major%%-*}"
if [[ "$major" =~ ^[0-9]+$ ]] && (( major >= 24 )); then
DOCKER_YUM_RELEASEVER=9
else
DOCKER_YUM_RELEASEVER=8
fi
}
# 之后在 repo 文件里做定点替换
sed -i "s|\$releasever|${DOCKER_YUM_RELEASEVER}|g" "$repo_file"
24 及以上走 centos9,其余走 centos8。这么做的理由不是版本号刚好对得上,而是 ABI 能兼容:centos8、centos9 的包分别按 RHEL 8、RHEL 9 的 glibc 编,openEuler 22.03 / 24.03 自带的 glibc 比这两个基线新,低版本编出来的二进制在新环境里能跑。映射只是让仓库路径对得上,装完还是 openEuler 系统。麒麟、UOS、OpenCloudOS、Anolis 这些 RHEL 系衍生版同理,脚本里列了名单:
case "$OS" in
opencloudos|anolis|openanolis|alinux|tencentos|kylin|uos|uniontech)
PKG_FAMILY="rhel"
map_docker_releasever
;;
centos|rhel|fedora|rocky|almalinux|ol|amzn)
PKG_FAMILY="rhel"
;;
esac
第一个分支做映射,第二个分支保持 $releasever 原样,因为 CentOS/RHEL/Rocky 的版本号本来就对得上仓库目录。
2.4 脚本怎么选源
脚本内置了三个源,GPG 地址和包地址成对存:
declare -A MIRRORS=(
[official]="https://download.docker.com|https://download.docker.com"
[tsinghua]="https://mirrors.tuna.tsinghua.edu.cn/docker-ce|https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
[aliyun]="https://mirrors.aliyun.com/docker-ce|https://mirrors.aliyun.com/docker-ce"
)
auto 模式只判断官方域通不通,不测速:
if curl -fsSL --connect-timeout 5 --max-time 10 \
"https://download.docker.com/linux/ubuntu/gpg" -o /dev/null 2>/dev/null; then
MIRROR_NAME="official"
else
MIRROR_NAME="aliyun"
fi
两点说明:
- 探测地址写死成
/linux/ubuntu/gpg了,跟实际发行版无关,就是看看download.docker.com这个域能不能连上,后面不会拿它当下载地址。 - 通不等于快。5 秒连接超时、10 秒总超时只能筛掉完全连不上的。官方域返回 200 但只有几十 KB/s 的时候,脚本还是选 official。对速度有要求的直接
--mirror aliyun或--mirror tsinghua,跳过探测。
用法:
sudo bash install-docker.sh # 自动选源
sudo bash install-docker.sh --mirror tsinghua # 指定源
sudo bash install-docker.sh --mirror official # 走官方源
脚本跑下来是这么个流程:查 root,看有没有装过 Docker 并提示覆盖,读 /etc/os-release 认发行版和包管理器,选源,卸旧包,装依赖,写 repo 和 GPG,装五个包,systemctl enable --now docker,最后校验一遍。
2.5 装完检查
docker version
docker compose version
systemctl status docker
docker run --rm hello-world
docker compose version 挂了但 docker version 正常,就是 compose 插件没装上。Compose V2 是 CLI 插件,docker compose 这个子命令只认两个目录:
/usr/libexec/docker/cli-plugins/(系统级)~/.docker/cli-plugins/(用户级)
把二进制放到 /usr/bin/docker-compose,只是多了一个 docker-compose 命令,docker compose 照样报 docker: 'compose' is not a docker command。脚本装的是 docker-compose-plugin 包,插件会进系统插件目录。
非 root 用户要用的话,加进 docker 组再重新登录:
sudo usermod -aG docker <用户名>
# 组变更只对新的登录会话生效,当前 shell 不会变
groups
三、镜像源配置
3.1 registry-mirrors 只管 Docker Hub
镜像加速器的原理不复杂:dockerd 收到 docker pull,如果镜像是 docker.io 的,就按顺序去 mirrors 里试,全失败了才回源。所以要同时满足两个条件:
- 请求得指向
docker.io。不写域名的短名(nginx、redis:7)会补全成docker.io/library/nginx:latest,走 mirror;写了别的仓库地址的(harbor.example.com/prod/app:v1)不走 mirror,直接连那个仓库。 - mirror 本身得能通。2024 年年中开始,一批公共镜像加速服务陆续停了,有的云厂商改成账号绑定专属地址。网上那些"可用加速器列表"时效性都很差,用之前自己测一下。
怎么测:/v2/ 是 Registry V2 的探测端点,返回 200 或 401 都说明服务在线。
for u in https://mirror-a.example.com https://mirror-b.example.com; do
printf '%-40s %s\n' "$u" "$(curl -s -o /dev/null -w '%{http_code} %{time_total}s' --max-time 5 "$u/v2/")"
done
3.2 写 daemon.json
配置文件在 /etc/docker/daemon.json,没有就新建。只配加速器的最小版本:
{
"registry-mirrors": ["https://<加速器地址>"]
}
实际环境一般一次把日志、数据目录、cgroup 驱动一起定下来,省得以后反复重启:
{
"registry-mirrors": ["https://<加速器地址>"],
"insecure-registries": ["harbor.example.com:80"],
"data-root": "/data/docker",
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"live-restore": true
}
| 字段 | 作用 | 常见取值 |
|---|---|---|
registry-mirrors |
Docker Hub 拉取加速,可多个,按顺序尝试 | 镜像站地址,必须带 https:// |
insecure-registries |
允许以 HTTP 或自签证书访问的仓库 | IP:端口 或 域名:端口 |
data-root |
镜像、容器、卷的存放根目录 | 独立数据盘,如 /data/docker |
exec-opts |
运行时参数,cgroup 驱动要和 k8s 保持一致 | native.cgroupdriver=systemd |
log-driver / log-opts |
容器日志驱动与轮转 | json-file + max-size + max-file |
live-restore |
dockerd 重启时保持容器运行 | true |
log-opts 的值必须是字符串。写成数字 100,dockerd 直接起不来,报错是 JSON 反序列化:
json: cannot unmarshal number into Go struct field ...log-opts of type string
journalctl -u docker -n 50 能看到完整字段名,按提示改成 "100m" 就行。
默认的 json-file 不限制大小,服务容器跑久了能把分区写满。离线安装脚本在 daemon.json 不存在时,写的就是带轮转的一份:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
单容器最多 300 MB(100m × 3),一般 Web 服务够用了;日志量大的在 docker run 或 compose 里单独覆盖 logging 段。
3.3 生效和检查
sudo systemctl daemon-reload # 只改单元文件时才需要;只改 daemon.json 可以跳过
sudo systemctl restart docker
docker info | grep -A5 "Registry Mirrors"
docker info | grep -E "Docker Root Dir|Cgroup Driver|Storage Driver"
docker info 里看不到 Registry Mirrors,要么 daemon.json 没读到,要么文件位置不对。语法错的话 dockerd 会直接起不来,不会悄悄忽略。systemctl cat docker.service 能看出来 ExecStart 有没有带 --config-file 这种非默认路径。
3.4 私有仓库:HTTP 和自签证书
insecure-registries 管两种情况:仓库是 HTTP 的,仓库用自签证书的。配了之后拉取走明文或者不校验证书,只能在内网用。
HTTPS + 私有 CA 的话,规范做法是把 CA 放到按域名命名的目录:
sudo mkdir -p /etc/docker/certs.d/harbor.example.com
sudo cp internal-ca.crt /etc/docker/certs.d/harbor.example.com/ca.crt
sudo systemctl restart docker
目录名要和 docker pull 里的主机名一字不差,带端口就一起带上,否则证书加载不上,报 x509: certificate signed by unknown authority。
3.5 镜像源相关报错
| 报错 | 原因 | 处理 |
|---|---|---|
TLS handshake timeout |
mirror 不可达,dockerd 逐个尝试后回源 | 从列表里移除失效地址,或直接指定可用地址 |
Get https://...: dial tcp: i/o timeout |
没外网,也没配内网仓库 | 按第四节离线方案搬镜像 |
http: server gave HTTP response to HTTPS client |
仓库是 HTTP,没进 insecure-registries |
加进该仓库地址后重启 |
x509: certificate signed by unknown authority |
自签证书没放进 certs.d |
按域名建目录放 ca.crt |
pull access denied / unauthorized |
私有仓库没登录 | docker login harbor.example.com |
manifest unknown |
标签不存在,或者架构不匹配 | 确认 tag;跨架构要加 --platform |
四、离线安装
4.1 两条路
| 路线 | 产物 | 适用 | 限制 |
|---|---|---|---|
| 包管理器包(rpm/deb) | docker-ce 等一组本地包 |
和打包机同发行版、同大版本、同架构 | 跨大版本或跨发行版容易缺依赖 |
| 静态二进制 | 解压后的 dockerd、docker、containerd 等 |
任意 x86_64/aarch64 Linux,不依赖发行版包管理 | 要手工提供 systemd 单元;仍受 glibc 最低版本约束 |
静态包用得最多,因为目标机经常和打包机不是一个系统,静态包只依赖内核、glibc 和少量系统库。脚本两种都支持,默认走静态。
4.2 打包机:做离线包
找一台有外网、有 DNS 的 Linux 执行:
bash pack-docker-offline.sh --arch x86_64 --mirror aliyun
可选参数:
bash pack-docker-offline.sh --mode rpm # 在同类 RHEL 系机器上打本地包
bash pack-docker-offline.sh --mode deb # 在同类 Debian 系机器上打本地包
bash pack-docker-offline.sh --with-images nginx:1.27,redis:7 # 顺带导出镜像
bash pack-docker-offline.sh --docker-ver 27.3.1 --out /data/pkg
打包脚本干了这几件事:
- 看镜像站上最新的静态包版本。解析目录页 HTML,把
docker-x.y.z.tgz挑出来按版本排序取最大;探测失败就用脚本里的固定版本。
html="$(mktemp)"
if curl -fsSL --connect-timeout 10 --max-time 30 "$STATIC_BASE/" -o "$html" 2>/dev/null; then
latest="$(grep -oE 'docker-[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?\.tgz' "$html" \
| grep -v rootless | sort -V | tail -1 || true)"
fi
- 下载静态包,在打包机上就解开。这一步是故意的:目标机可能没有
tar,解压放打包机做,目标机只管拷文件。
tar -xzf "$tgz_path" -C "$unpack"
cp -a "$unpack/docker/." "$PAYLOAD_DIR/bin/"
-
下载
docker compose和docker buildx两个插件。这俩只在 GitHub Releases 提供,脚本按官方、mirror.ghproxy.com、ghfast.top的顺序试,都失败就跳过,安装时会告警,不影响 Docker 本体。 -
生成三种形态的产物:
docker-offline-x86_64-20261029/
├── install-docker-offline.sh # 目标机执行
├── VERSION.txt # 架构、模式、源、版本、打包时间
├── payload/
│ ├── bin/ # 已解压的 docker/containerd/runc 等
│ ├── docker-compose
│ ├── docker-buildx
│ └── docker.service
├── packages/ # rpm/deb 模式下的本地包,静态模式为空
└── images/ # --with-images 导出的镜像 tar
同时产出同名 .tar.gz 和 .zip。打包机没有 tar 就只出 zip;zip 优先用系统 zip 命令,没有就用 Python 的 zipfile,保证产物总能出来。
4.3 目标机:安装
整目录拷,别只拷脚本:
scp -r docker-offline-x86_64-20261029 root@目标机:/root/
在目标机上执行:
cd /root/docker-offline-x86_64-20261029
sudo bash install-docker-offline.sh
脚本必须和 payload 在同一层,它按固定顺序找 dockerd,命中第一个就返回:
for d in "$PAYLOAD_DIR/bin" "$BUNDLE_DIR/bin" "$BUNDLE_DIR/docker" "$PAYLOAD_DIR/docker" "$BUNDLE_DIR"; do
if [[ -x "$d/dockerd" ]]; then
echo "$d"
return 0
fi
done
都没找到,才去搜 docker-*.tgz(只看两层深,排除 rootless 包),找到就在目标机现场解。
把脚本单独拷到 /root 再执行的话,$BUNDLE_DIR 就成了 /root,找不到 payload,直接退出,提示"请把完整离线目录拷到服务器后再执行"。
安装过程:
- 先看
iptables或nft在不在。都没有只告警,静态包里不带这两个工具,容器网络会用不了,得另外离线装。 - 加载
overlay、br_netfilter模块,打开net.ipv4.ip_forward。overlay是 overlay2 存储驱动的前提,br_netfilter让桥接流量走 netfilter,缺哪个都不行,轻则容器上不了网,重则 dockerd 起不来。 - 按包类型装:有 rpm 用
yum localinstall,有 deb 用dpkg -i,都没有就装静态二进制到/usr/bin。 - 装 CLI 插件到
/usr/libexec/docker/cli-plugins/。 - 写 systemd 单元。离线包里自带一份(
payload/docker.service),没有就用脚本内置的:
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd --host=unix:///var/run/docker.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
OOMScoreAdjust=-500
[Install]
WantedBy=multi-user.target
Type=notify 是让 dockerd 通过 sd_notify 报就绪,静态包里的 dockerd 支持这套协议;LimitNOFILE=infinity 和 TasksMax=infinity 在容器多的时候是必需的,默认 1024 的文件描述符上限,表现出来就是容器随机启动失败。ExecStart 里已经有 --host,所以 daemon.json 里不能再写 hosts,写了就报重复配置:
unable to configure the Docker daemon with file /etc/docker/daemon.json:
the following directives are specified both as a flag and in the configuration file: hosts
daemon-reload、enable、restart,然后校验状态,输出docker version、docker compose version、docker buildx version。images/下有 tar 就逐个docker load。
4.4 目标机没有 tar
三种办法,按可靠程度排:
A. 拷已解压目录(最稳)。打包机上目录本来就是展开状态,scp -r 整个目录过去,目标机不需要任何解压工具。
B. 拷 zip,用 Python 解压。Python 通常随系统预装:
python3 -c "import zipfile; zipfile.ZipFile('docker-offline-x86_64-20261029.zip').extractall()"
cd docker-offline-x86_64-20261029
sudo bash install-docker-offline.sh
C. 拷了 tar.gz,目标机也有 tar:
tar -xzf docker-offline-x86_64-20261029.tar.gz
脚本内部还有一层解压兜底:tar → busybox tar → python3/python 的 tarfile,给包里还带 .tgz 的情况用。三个都没有就停下来,提示去打包机解压后整目录拷,别在目标机上硬解。
4.5 镜像一起搬
装好 Docker 只是解决了运行时,镜像是另一回事。打包这边:
bash pack-docker-offline.sh --with-images nginx:1.27,redis:7
脚本对每个镜像 docker pull 完再 docker save,文件名把 / 和 : 换成 _:
docker save -o "$IMAGES_DIR/${safe}.tar" "$name"
安装那边自动导入:
for img in "$IMAGES_DIR"/*.tar; do
docker load -i "$img"
done
docker load 会把标签一起恢复,不用重新打 tag。镜像大的话注意 images/ 的体积,docker save 出来是不压缩的层数据,几个 GB 很常见。
离线环境还有条硬规矩:别执行 docker run hello-world。本地没有这个镜像就会去 Docker Hub 拉,没 DNS 或者禁了外网必然失败,报 lookup registry-1.docker.io: no such host,这跟装没装成没关系,很容易看错。验证就用:
docker version
docker compose version
systemctl status docker
docker images
4.6 离线环境的"镜像源"
完全断网又没有内网仓库的时候,镜像只能靠 docker save / docker load 搬,registry-mirrors 没意义,配了只会让失败来得更慢:dockerd 还是会先试镜像站,超时才报错。
接了内网 Harbor 之后,Harbor 就顶了"镜像源"的位置:
- Harbor 是 HTTP:加
insecure-registries。 - Harbor 是 HTTPS + 自签证书:按 3.4 放
ca.crt。 - 要把上游镜像同步进 Harbor:建远程仓库(proxy cache)或者复制规则,客户端改成
docker pull harbor.example.com/library/nginx:1.27,不再碰 Docker Hub。
registry-mirrors 和 Harbor 是两套机制,前者只能加速 docker.io,没法把 harbor.example.com/... 的拉取转到别处。
五、装完后的统一基线
在线离线都一样,装完建议把下面几项一次配好,后面少折腾。
| 项 | 做法 | 判断标准 |
|---|---|---|
| 数据目录 | data-root 指向独立数据盘 |
docker info 的 Docker Root Dir |
| cgroup 驱动 | native.cgroupdriver=systemd |
docker info 的 Cgroup Driver |
| 日志轮转 | json-file + 100m × 3 |
单容器日志不无限增长 |
| 镜像加速 | registry-mirrors |
docker info 的 Registry Mirrors 有输出 |
| 容器自启 | docker run --restart=always 或 compose 的 restart |
主机重启后容器自动恢复 |
| dockerd 重启不杀容器 | live-restore: true |
重启 Docker 服务时容器不中断 |
迁 data-root 的时候,新目录必须是空的,先停服务再搬:
sudo systemctl stop docker
sudo mkdir -p /data/docker
sudo rsync -aHAX --numeric-ids /var/lib/docker/ /data/docker/
# 确认 daemon.json 里已有 "data-root": "/data/docker"
sudo systemctl start docker
docker images # 原有镜像和容器应该都还在
rsync 的 -aHAX 保留权限、ACL 和扩展属性,overlay2 对这些比较敏感,参数少了可能镜像层校验不过。
六、排障对照表
| 现象 | 原因 | 处理 |
|---|---|---|
Cannot connect to the Docker daemon at unix:///var/run/docker.sock |
服务没起,或者当前 shell 的 DOCKER_HOST 指向别处 |
systemctl status docker;echo $DOCKER_HOST |
permission denied while trying to connect to the Docker daemon socket |
用户不在 docker 组,或者组变更没生效 |
usermod -aG docker <用户> 后重新登录,groups 确认 |
Job for docker.service failed because the control process exited with error code |
daemon.json 语法错、字段类型错、hosts 冲突 |
journalctl -u docker -n 50 --no-pager |
failed to start daemon: Error initializing network controller ... iptables failed |
缺 iptables/nft,或者规则链被防火墙清了 |
装 iptables;检查 firewalld 和 DOCKER-USER 链 |
write /var/lib/docker/...: no space left on device |
数据目录所在分区满了 | docker system prune 清理,或者迁 data-root |
yum makecache 报 404,路径里带 centos22 |
$releasever 和仓库目录对不上 |
用 sed 固定成 8 或 9(见 2.3) |
apt-get update 报 Malformed entry |
源行缺发行版代号 | VERSION_CODENAME 为空,手动填代号 |
docker: 'compose' is not a docker command |
插件没放到 CLI 插件目录 | 装 docker-compose-plugin,或者手工放进 /usr/libexec/docker/cli-plugins/ |
docker pull 卡一会儿然后超时,docker info 里有失效 mirror |
加速器已经停服 | 从 registry-mirrors 里删掉 |
lookup registry-1.docker.io: no such host |
离线机执行了需要联网拉镜像的命令 | 用 docker load 导入本地 tar |
unable to configure the Docker daemon ... hosts 重复 |
ExecStart 和 daemon.json 同时指定了 hosts |
删掉 daemon.json 里的 hosts |
七、几个限制
- 公共镜像加速器能不能用是变化的,本文不写具体地址,用 3.1 的
/v2/脚本自己测。 - 静态二进制绕过了包管理,Docker 升级要重新打一遍包;rpm/deb 那条路才能用包管理器升级。
docker save不压缩,搬运前可以自己gzip,导入时先解压再docker load -i。- 离线包里的
docker compose、docker buildx来自 GitHub Releases,网络受限时可能只拿到 Docker 本体,compose 得单独准备。