$kernelink route --hydrate --safe

页面加载 /
跳到正文
auth://account/session

登录工作区

使用 Emlog 账户继续访问你的内容与互动记录。

忘记密码?

打开 Emlog 原生登录页

7.md
workspace / posts
~/posts/7.md 阅读中

Docker 部署:在线装、离线装和镜像源配置

装 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

有三处容易错:

  1. 公钥和 repo 要来自同一个镜像站。混着用一般也能装上,但镜像站换签名的时候,先坏的往往是这种组合。
  2. signed-by 是现在推荐的写法,apt-key add 已经废弃了。还在用旧写法的话会看到 Key is stored in legacy trusted.gpg keyring 的警告。
  3. 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

两点说明:

  1. 探测地址写死成 /linux/ubuntu/gpg 了,跟实际发行版无关,就是看看 download.docker.com 这个域能不能连上,后面不会拿它当下载地址。
  2. 通不等于快。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 里试,全失败了才回源。所以要同时满足两个条件:

  1. 请求得指向 docker.io。不写域名的短名(nginx、redis:7)会补全成 docker.io/library/nginx:latest,走 mirror;写了别的仓库地址的(harbor.example.com/prod/app:v1)不走 mirror,直接连那个仓库。
  2. 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

打包脚本干了这几件事:

  1. 看镜像站上最新的静态包版本。解析目录页 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
  1. 下载静态包,在打包机上就解开。这一步是故意的:目标机可能没有 tar,解压放打包机做,目标机只管拷文件。
tar -xzf "$tgz_path" -C "$unpack"
cp -a "$unpack/docker/." "$PAYLOAD_DIR/bin/"
  1. 下载 docker compose 和 docker buildx 两个插件。这俩只在 GitHub Releases 提供,脚本按官方、mirror.ghproxy.com、ghfast.top 的顺序试,都失败就跳过,安装时会告警,不影响 Docker 本体。

  2. 生成三种形态的产物:

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,直接退出,提示"请把完整离线目录拷到服务器后再执行"。

安装过程:

  1. 先看 iptables 或 nft 在不在。都没有只告警,静态包里不带这两个工具,容器网络会用不了,得另外离线装。
  2. 加载 overlay、br_netfilter 模块,打开 net.ipv4.ip_forward。overlay 是 overlay2 存储驱动的前提,br_netfilter 让桥接流量走 netfilter,缺哪个都不行,轻则容器上不了网,重则 dockerd 起不来。
  3. 按包类型装:有 rpm 用 yum localinstall,有 deb 用 dpkg -i,都没有就装静态二进制到 /usr/bin。
  4. 装 CLI 插件到 /usr/libexec/docker/cli-plugins/。
  5. 写 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
  1. daemon-reload、enable、restart,然后校验状态,输出 docker version、docker compose version、docker buildx version。
  2. 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 得单独准备。
🤞 分享