这篇文章记录 Ubuntu 26.04 云服务器上的 Docker 安装过程,以及装好后需要做的一些基础配置。
写在前面
在 Ubuntu 云服务器上安装 Docker,常见的做法是直接运行官方安装脚本。需要使用阿里云软件源时,可以加上 --mirror Aliyun:
curl -fsSL https://get.docker.com | bash -s -- --mirror Aliyun
这条命令很方便。不过,安装时可能遇到脚本下载超时、APT 找不到软件包,或者装好后拉不下来容器镜像。这些问题发生在不同环节,需要分别排查。
下面先介绍如何配置阿里云或腾讯云的 APT 软件源,安装 Docker Engine、Buildx 和 Compose,再处理日志轮转、操作权限和端口配置。后续部署各种应用时,也能沿用这些配置。
对于长期运行的服务器,我更倾向于直接配置 APT 源,方便检查软件包来源和管理后续升级。便捷脚本可以用于开发和测试环境,Docker 官方也不建议依赖它部署生产系统。
本文以运行 Ubuntu 26.04 的阿里云 ECS 和腾讯云 CVM 为例,有相应系统权限的轻量服务器也可以参考。使用 Alibaba Cloud Linux、TencentOS,或已经部署 Kubernetes 的节点时,需要按实际环境调整安装步骤。
安装源和容器镜像源分开看
安装 Docker 时,容易把安装脚本、软件包源和容器镜像源混在一起。它们分别负责不同的事情:
| 下载内容 | 用途 | 对应的配置 |
|---|---|---|
| 安装脚本 | 自动配置软件源、安装软件包 | get.docker.com、官方源码仓库,或本文 fork / 脚本附录 |
| Docker CE 软件包 | 安装 Engine、CLI、containerd、Buildx、Compose | APT 源、脚本的 --mirror 或 DOWNLOAD_URL |
| 容器镜像 | 运行应用,例如 Traefik、Gitea | 镜像仓库地址、Docker daemon 代理,或适用的 Registry mirror |
有一个细节是安装脚本支持的--mirror Aliyun 只会把 Docker 软件包源切换到阿里云。安装脚本仍从 get.docker.com 下载,docker pull 也不会因此获得加速。
遇到报错,我们需要先看失败的是哪一步。如果 Docker 已经安装成功,但拉取容器镜像超时,就继续检查容器仓库、网络和 daemon 代理,通常无需重装 Docker。
安装前的准备
确认系统和架构
登录服务器后,查看系统版本、软件包架构和内核版本:
cat /etc/os-release
dpkg --print-architecture
uname -r
Ubuntu 26.04 的发行版代号是 resolute。后面的命令会自动读取代号和架构,使用 amd64 或 arm64 时都不需要手动修改。Ubuntu 24.04 和 22.04 也在 Docker 官方支持范围内,配置时保留各自的发行版代号。
如果镜像站返回 404,先检查地址和同步情况。不要把 Ubuntu 26.04 的代号换成 noble,也不要修改系统版本信息来绕过检查。
AMD64 和 ARM64 使用同一个 Docker CE 仓库地址,APT 根据架构配置下载对应的软件包。Ubuntu 系统源与 Docker 软件包源分别配置,下面的步骤无需修改 ubuntu.sources 或全局替换域名。
接下来,我们使用的命令按 Bash 编写,默认当前用户具有 sudo 权限。使用 Zsh 时,先运行 bash 切换 Shell;以 root 登录时,可以去掉命令中的 sudo。
检查现有安装和软件源
查看机器上是否已经安装相关组件:
dpkg-query -W -f='${binary:Package}\t${Status}\t${Version}\n' \
docker-ce docker-ce-cli containerd.io \
docker.io docker-compose docker-compose-v2 docker-doc \
docker-buildx podman-docker containerd runc 2>/dev/null || true
输出中,状态为 install ok installed 的软件包才是已安装的。
再检查是否已有 Docker 软件源配置:
sudo grep -RnsE 'docker-ce|download\.docker\.com' \
/etc/apt/sources.list /etc/apt/sources.list.d \
2>/dev/null || true
新服务器检查完后,可以继续安装。如果机器已经在运行容器,先备份业务数据和配置,再安排维护时间。下面按初始化新环境编写,直接用于现有环境可能影响正在运行的服务。
如果之前安装的是 Ubuntu 自带的 Docker 包,切换到 Docker CE 前需要检查冲突。下面按照 Docker 官方列出的冲突包,收集机器上实际安装的包,再由 APT 显示卸载计划,等待确认:
(
set -eu
packages=()
for pkg in docker.io docker-compose docker-compose-v2 docker-doc \
docker-buildx podman-docker containerd runc; do
status="$(dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null || true)"
if [ "$status" = "install ok installed" ]; then
packages+=("$pkg")
fi
done
if [ "${#packages[@]}" -gt 0 ]; then
sudo apt-get remove "${packages[@]}"
fi
)
确认卸载前,仔细看 APT 列出的软件包和关联删除项。containerd、runc 也可能被其他容器平台使用,无法确认用途时,先取消操作。
已有 Docker 软件源的机器,先检查对应文件,备份后再停用重复配置。同一仓库使用不同的 Signed-By 可能导致 APT 报错,同时配置多家镜像站也会让软件包来源难以判断。只修改确认过的 Docker 源文件。
安装基础工具
sudo apt-get -o APT::Update::Error-Mode=any update && \
sudo apt-get install -y ca-certificates curl gnupg
这一步使用 Ubuntu 系统源。如果失败,先检查系统源、DNS 和出站网络。Docker 软件源还没有配置,调整 --mirror 参数帮不上忙。
准备完成后,从下面的阿里云和腾讯云方案中选择一套。公网源可以按实际访问情况选择,云内地址则在对应的云环境中使用。
配置 Docker 软件源
根据你的实际环境,阿里云和腾讯云的配置选一套即可。两套命令都会读取系统代号和架构,检查仓库元数据,再保存公钥并写入 APT 配置。这一步完成后,还需要继续安装 Docker 软件包。
使用阿里云软件源
阿里云 Docker CE 镜像站提供的公网地址是:
https://mirrors.aliyun.com/docker-ce
阿里云 ECS 安装文档还提供了 VPC 环境中的云内地址:
http://mirrors.cloud.aliyuncs.com/docker-ce
下面统一使用公网 HTTPS 地址,下载仓库公钥时也走 HTTPS。非阿里云服务器使用公网地址;只有内网访问条件的 ECS,需要另行确认镜像站的访问路径,并通过可信渠道准备公钥。
完成前面的安装准备后,执行:
(
set -eu
. /etc/os-release
[ "$ID" = "ubuntu" ] || {
echo "这段配置仅适用于 Ubuntu。" >&2
exit 1
}
MIRROR="https://mirrors.aliyun.com/docker-ce"
CODENAME="${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}"
ARCH="$(dpkg --print-architecture)"
[ -n "$CODENAME" ] || {
echo "无法确定 Ubuntu 代号。" >&2
exit 1
}
# 已有配置时先检查,避免直接覆盖。
if [ -e /etc/apt/sources.list.d/docker.sources ]; then
echo "docker.sources 已存在,请先检查现有配置。" >&2
exit 1
fi
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
# 检查当前发行版的仓库元数据和架构记录。
curl -fsSL --connect-timeout 10 --max-time 60 \
"$MIRROR/linux/ubuntu/dists/$CODENAME/Release" \
-o "$WORK/Release"
grep -Fq "stable/binary-$ARCH/Packages" "$WORK/Release" || {
echo "仓库元数据未列出当前架构,请检查镜像同步状态。" >&2
exit 1
}
# 公钥先下载到临时目录,成功后再保存到系统目录。
curl -fsSL --connect-timeout 10 --max-time 60 \
"$MIRROR/linux/ubuntu/gpg" \
-o "$WORK/docker.asc"
gpg --batch --show-keys "$WORK/docker.asc"
sudo install -m 0755 -d /etc/apt/keyrings
sudo install -m 0644 "$WORK/docker.asc" /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: $MIRROR/linux/ubuntu
Suites: $CODENAME
Components: stable
Architectures: $ARCH
Signed-By: /etc/apt/keyrings/docker.asc
EOF
)
阿里云源配置完成后,直接进入「安装 Docker 和插件」,无需再执行腾讯云配置。
使用腾讯云软件源
这里使用腾讯云 Docker CE 镜像站的统一地址:
https://mirrors.cloud.tencent.com/docker-ce
腾讯云官方安装示例和 Tars 文档都使用了这个镜像站。下面保留镜像地址,改用独立公钥文件和 Signed-By 配置仓库。
腾讯云文档中还出现过 mirrors.tencentyun.com 这个云内域名。按照腾讯云软件源说明,mirrors.cloud.tencent.com 是公网和内网统一域名,腾讯云 VPC 使用默认 DNS 时,会优先走内网。本文的腾讯云安装验证也在 VPC 内完成。
选择腾讯云源时,执行:
(
set -eu
. /etc/os-release
[ "$ID" = "ubuntu" ] || {
echo "这段配置仅适用于 Ubuntu。" >&2
exit 1
}
MIRROR="https://mirrors.cloud.tencent.com/docker-ce"
CODENAME="${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}"
ARCH="$(dpkg --print-architecture)"
[ -n "$CODENAME" ] || {
echo "无法确定 Ubuntu 代号。" >&2
exit 1
}
# 已有配置时先检查,避免直接覆盖。
if [ -e /etc/apt/sources.list.d/docker.sources ]; then
echo "docker.sources 已存在,请先检查现有配置。" >&2
exit 1
fi
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
# 检查当前发行版的仓库元数据和架构记录。
curl -fsSL --connect-timeout 10 --max-time 60 \
"$MIRROR/linux/ubuntu/dists/$CODENAME/Release" \
-o "$WORK/Release"
grep -Fq "stable/binary-$ARCH/Packages" "$WORK/Release" || {
echo "仓库元数据未列出当前架构,请检查镜像同步状态。" >&2
exit 1
}
# 公钥先下载到临时目录,成功后再保存到系统目录。
curl -fsSL --connect-timeout 10 --max-time 60 \
"$MIRROR/linux/ubuntu/gpg" \
-o "$WORK/docker.asc"
gpg --batch --show-keys "$WORK/docker.asc"
sudo install -m 0755 -d /etc/apt/keyrings
sudo install -m 0644 "$WORK/docker.asc" /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: $MIRROR/linux/ubuntu
Suites: $CODENAME
Components: stable
Architectures: $ARCH
Signed-By: /etc/apt/keyrings/docker.asc
EOF
)
公钥和仓库检查
两套配置都通过 Signed-By 将公钥绑定到对应仓库,不再使用 apt-key add。阿里云的镜像配置说明也采用了这种方式。
gpg --show-keys 用来查看公钥信息,确认下载的文件能够解析。要确认公钥身份,还需要通过 Docker 官方渠道独立核对指纹。
Release 文件能够下载,并且包含当前架构的记录,只说明可以继续更新 APT 索引。仓库签名是否有效、软件包是否完整同步,还要看后面的 APT 更新和安装结果。
如果镜像站对应目录暂时不可用,可以检查 Docker 官方源或其他镜像站。换源时保留当前发行版代号和架构,Ubuntu 26.04 继续使用 resolute。
安装 Docker 和插件
软件源配置完成后,阿里云和腾讯云使用相同的安装步骤。
更新索引并安装软件包
先更新 APT 索引,查看待安装软件包的候选版本:
sudo apt-get -o APT::Update::Error-Mode=any update && \
apt-cache policy \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
检查输出中的 Candidate 和软件包来源,确认候选版本来自刚刚配置的仓库。如果出现 (none)、签名错误或索引下载失败,先处理报错,再继续安装。
确认后,安装 Docker 和插件:
sudo apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
这组软件包包括 Docker Engine、客户端、containerd,以及 Buildx 和 Compose 插件。安装方式也可以参考阿里云的 Docker 安装文档。
Compose 插件使用 docker compose 命令,无需额外安装旧版 docker-compose。
检查服务和插件
启动 Docker,设置开机自启,再查看服务状态和组件版本:
sudo systemctl enable --now docker
sudo systemctl is-active docker
sudo docker version
sudo docker compose version
sudo docker buildx version
systemctl is-active docker 应输出 active,docker version 应同时显示 Client 和 Server 信息,Compose 和 Buildx 也应能输出版本号。
如果只有 Client 信息,或提示无法连接 Docker daemon,继续检查服务状态和访问权限。开机自启的相关说明见 Docker 安装后配置文档。
服务启动失败时,查看状态和最近的日志:
sudo systemctl status docker --no-pager
sudo journalctl -u docker -n 100 --no-pager
检查镜像拉取和容器运行
服务正常后,你可以选择使用 Docker 官方安装文档中的 hello-world 镜像做一次测试。有时候会无法直接下载镜像,所以这里我们也可以试试我的另外一个开源小项目,更活泼的 Docker Hello World:
sudo docker pull ghcr.io/soulteary/hello:latest
sudo docker run --rm --network none ghcr.io/soulteary/hello:latest
如果你使用的是 Docker 官方文档中的 hello-world,那么看到 Hello from Docker!,说明测试容器运行成功;如果你选择使用上面的命令,那么你将看到一个活泼的鹦鹉(按 CTRL+C 中止运行)。上面的命令中的 --rm 会在容器退出后删除它,--network none 则让容器在不接入外部网络的情况下运行。
这里把拉取和运行拆成两步,更方便定位问题:pull 超时,先检查镜像仓库访问;镜像已经下载却无法运行,再查看报错和 Docker 日志。测试使用的 latest 标签,仅用于这次安装验证。
APT 软件源和 Docker Hub 是两个不同的下载入口。Docker 已经安装成功,但 docker pull 失败时,继续检查容器仓库、网络或 daemon 代理即可,无需因此重新安装 Docker。
也可以使用安装脚本
如果已经通过 APT 装好 Docker,可以跳过这一节。下面介绍用脚本初始化新服务器的方法,官方脚本和我维护的 fork 选一种即可。
使用 Docker 官方脚本
安装脚本需要管理员权限。运行前,先下载到本地,查看内容,再预演安装步骤:
curl -fsSL --connect-timeout 10 --max-time 120 \
https://get.docker.com -o get-docker.sh.download && \
mv get-docker.sh.download get-docker.sh
下载成功后,查看脚本:
less get-docker.sh
官方安装脚本提供了 --dry-run 参数,用来打印准备执行的命令。可以据此检查软件源、发行版代号和包名,但网络是否可达、软件包是否齐全,还需要实际安装时确认。
当我们选择阿里云源时,可以先预演:
sudo sh get-docker.sh --mirror Aliyun --dry-run
检查输出后,执行安装:
sudo sh get-docker.sh --mirror Aliyun
当我们选择使用腾讯云源时,官方脚本通过 DOWNLOAD_URL 指定腾讯云软件源:
sudo env DOWNLOAD_URL=https://mirrors.cloud.tencent.com/docker-ce \
sh get-docker.sh --dry-run
检查输出后,执行安装:
sudo env DOWNLOAD_URL=https://mirrors.cloud.tencent.com/docker-ce \
sh get-docker.sh
指定腾讯云地址时,不要同时传入 --mirror Aliyun,否则脚本会优先使用阿里云源。这里通过 sudo env 设置变量,避免它被 sudo 的环境过滤规则丢弃。
官方脚本的下载也会出现下载失败。有的时候,当 get.docker.com 无法访问时,可以尝试从 Docker 官方源码仓库下载:
curl -fsSL --connect-timeout 10 --max-time 120 \
https://raw.githubusercontent.com/docker/docker-install/master/install.sh \
-o get-docker.sh.download && \
mv get-docker.sh.download get-docker.sh
不过因为这是 GitHub Raw 地址,访问也可能失败。仓库的 master 分支会持续更新,下载到的内容可能与 get.docker.com 发布的版本不同,使用前同样需要查看和预演。
两个入口都无法访问,可以回到前面的 APT 安装方案。
使用我维护的 fork
我在 soulteary/docker-install 中补上了 --mirror TencentCloud 参数支持,这样阿里云和腾讯云都可以用 --mirror 选择软件源。
这个 fork 沿用上游安装流程,Docker 软件包仍从选定的镜像站下载。相关修复已经合并到 fork,使用时不再需要照搬旧版本的参数规避写法。
使用的第一步是,下载并查看脚本内容。
先在自己的工作目录中下载:
(
set -eu
umask 077
WORK="$(mktemp -d ./docker-install.XXXXXXXX)"
trap 'rm -rf "$WORK"' EXIT
curl -q -fsSL --proto '=https' --proto-redir '=https' \
--connect-timeout 10 --max-time 120 \
https://raw.githubusercontent.com/soulteary/docker-install/refs/heads/main/install.sh \
-o "$WORK/install.sh"
test -s "$WORK/install.sh"
sh -n "$WORK/install.sh"
ln "$WORK/install.sh" ./get-docker-fork.sh
printf '%s\n' '脚本已保存为 get-docker-fork.sh;尚未执行安装。'
)
这段命令只保存脚本,不安装 Docker。文件下载到临时目录,通过非空和语法检查后,再保存为 get-docker-fork.sh。
如果同名文件已经存在,命令会停止并保留旧文件。先检查旧文件,再决定备份或换名;下载或检查过程中出现错误时,也先停下来处理。
curl -q 用来忽略默认的 curl 配置,其他参数限制下载和重定向使用 HTTPS,并设置连接与传输超时。sh -n 只检查 Shell 语法,运行前还需要查看脚本内容:
less get-docker-fork.sh
这里下载的是 main 分支,内容会随仓库更新。需要保留安装记录时,保存脚本文件和来源提交。GitHub Raw 无法访问时,也可以使用文末附录中的完整脚本。
和上面一样,当我们选择使用阿里云源,先预演:
sh get-docker-fork.sh --mirror Aliyun --dry-run
检查软件源、发行版代号和待安装的包名,再执行:
sudo sh get-docker-fork.sh --mirror Aliyun
使用腾讯云源时,在腾讯云 VPC 环境中,先预演:
sh get-docker-fork.sh --mirror TencentCloud --dry-run
检查输出后,执行:
sudo sh get-docker-fork.sh --mirror TencentCloud
fork 的参数名称是 TencentCloud,不要写成 Tencent 或 Tencent Cloud。使用官方脚本时,则按前面的示例通过 DOWNLOAD_URL 指定地址。
安装完成后,回到「安装 Docker 和插件」,检查服务、插件版本和容器运行,再继续做基础配置。
在腾讯云实例上检查连通性
按照腾讯云软件源说明,mirrors.cloud.tencent.com 是公网和内网统一域名,腾讯云 VPC 使用默认 DNS 时,会优先走内网。检查时保留默认 DNS,并确认外部代理没有改变访问路径。
可以先查看域名解析结果:
getent ahostsv4 mirrors.cloud.tencent.com
如果是内网环境,通常类似:
169.254.0.3 STREAM mirrors.cloud.tencent.com
169.254.0.3 DGRAM
169.254.0.3 RAW
解析结果可以帮助排查网络,但不能单凭它判断软件包是否可用。--mirror TencentCloud 只负责选择源,主机的 DNS、路由和代理仍需要分别检查。
预演输出中看到 resolute stable 和腾讯云地址,说明生成的配置符合预期。此时还没有下载公钥、更新 APT 索引或安装软件,实际结果仍要看后续安装。
如果已经克隆了 fork 仓库,还可以在仓库根目录运行连通性检查脚本。只下载了 install.sh 的读者,可以跳过这项辅助检查。
确认处于腾讯云 VPC 内网后,手动开启检查:
TENCENT_CLOUD_INTRANET=1 \
sh scripts/test-tencent-cloud-mirror.sh ubuntu resolute
检查默认不发送网络请求,只有设置上述变量后才会执行。它下载公钥和 Release 元数据,不安装软件。软件包下载、签名验证和 Docker 服务启动,仍由安装步骤确认;公共 CI 中的离线参数测试也不能替代云内安装验证。
从文章内保存安装脚本
如果始终无法从 GitHub Raw 获取程序代码,也可以直接复制文末的完整代码块,在普通用户的工作目录中执行。它会保存 get-docker-fork.sh,不会立即安装 Docker。
保存命令使用带引号的 heredoc,避免脚本中的变量和命令在粘贴时提前展开。保存过程中还会检查 SHA-256 和 Shell 语法;如果同名文件已经存在,命令会停止,保留旧文件。
复制时使用完整代码块,不要只截取其中的函数,也不要将脚本正文直接粘贴到 root Shell。保存成功后,查看内容,再按前面的步骤预演,选择 Aliyun 或 TencentCloud 安装。
虽然本文文末的代码省去了脚本下载这一步。不过,实际安装仍需要访问 Ubuntu 系统源和 Docker 软件包源,下载软件包及依赖。
装好后补上基础配置
限制日志增长
容器长期运行,日志也会不断积累。独立云服务器上的常规容器,可以使用 Docker 官方推荐的 local 日志驱动,自带轮转和压缩。
新主机可以先用下面这份配置:
{
"log-driver": "local",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
按照 local 驱动的配置说明,上面的配置将在日志文件达到 20m 时轮转,最多保留 5 个文件。这些限制按容器生效,不过系统中整个 Docker 数据目录的用量仍需后续继承监控。(注意,log-opts 中的值使用字符串)
下面的命令适用于还没有配置过 /etc/docker/daemon.json 的主机。如果文件已经存在,命令会退出,不会造成损害。如果已经配置过,推荐先备份现有文件,再手动合并日志配置,保留原来的代理、仓库等选项。
(
set -eu
if sudo test -e /etc/docker/daemon.json; then
echo "daemon.json 已存在,请先备份并手动合并日志配置。" >&2
exit 1
fi
CONFIG="$(mktemp)"
trap 'rm -f "$CONFIG"' EXIT
cat > "$CONFIG" <<'EOF'
{
"log-driver": "local",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
EOF
sudo dockerd --validate --config-file="$CONFIG"
sudo install -m 0755 -d /etc/docker
sudo install -m 0644 "$CONFIG" /etc/docker/daemon.json
sudo systemctl restart docker
)
dockerd --validate 检查配置后就会退出,不启动服务。当我们执行脚本,写入文件并重启后,还要确认 Docker 正常运行,尤其要检查已有的 systemd 启动参数是否与文件配置冲突。机器已经承载业务时,建议在维护窗口修改和重启。
检查服务状态和默认日志驱动:
sudo systemctl is-active docker
sudo docker info --format '{{.LoggingDriver}}'
输出应分别为 active 和 local。
新的默认日志配置只用于后续新建的容器。已有容器需要重建才能采用新选项,重启 Docker 或容器都不会自动改变其日志配置。已经接入日志采集器的环境,先确认采集方式支持 local。
控制 Docker 操作权限
Docker 官方文档明确说明,加入 docker 组的账号可以获得相当于 root 的权限。所以这里继续使用 sudo docker,不为了省去 sudo,就把 CI、网站运行账号或其他用户全部加入该组。
通过 sudo 调用也需要控制授权范围。能够执行任意 Docker 命令的账号,依然具有很高的权限。
Docker API 应有访问保护,也不要随手把 Docker socket 挂进业务容器。即使挂载时加上 :ro,容器仍然可以通过 socket 调用 API,无法据此限制为只读操作。相关说明见 Docker Engine 安全文档。
公网只开放需要的端口
后面如果使用 Traefik 统一提供 Web 入口,可以在云安全组中按下面的方式配置入站访问:
| 入口 | 建议策略 |
|---|---|
| SSH,例如 TCP 22 | 只允许管理 IP、VPN 或堡垒机来源 |
| TCP 80、443 | 部署 Web 入口时按实际需求开放 |
| 业务管理后台、数据库端口 | 不直接向公网开放 |
| Docker API 2375、2376 | 本方案不开放 |
仅安装 Docker,无需开放 80、443 的入站访问。下载软件包需要的是服务器能够向外访问相应软件源。
修改 SSH 或安全组规则前,保留当前连接,并确认另一个管理连接能够正常登录,避免把自己锁在服务器外面。
容器端口发布后,也要检查监听地址。8080:80 这样的写法默认会发布到宿主机的所有地址。只供本机访问的服务,可以在 Compose 中明确绑定回环地址:
ports:
- "127.0.0.1:8080:80"
这是服务中的 ports 配置片段,适用于常规 bridge/NAT 网络,没有额外启用容器直连路由。
Docker 发布端口还可能绕过 UFW 的常规规则。配置主机防火墙时,先确认 Docker 使用的后端:iptables 和原生 nftables 的规则挂接方式不同,原生 nftables 后端没有 DOCKER-USER 链,不能直接照搬旧教程。
也不要通过关闭 Docker 的防火墙规则管理来处理端口问题,这可能破坏容器网络。应检查实际发布的端口、监听地址和对应防火墙规则。
拉取容器镜像时再检查加速配置
Docker 装好后,拉取容器镜像还需要单独检查网络和仓库配置。前面的安装方案只配置软件包源,不会自动设置 registry-mirrors。
按照阿里云目前的镜像加速说明,ACR 镜像加速已经停止同步最新镜像,服务面向个人开发场景,不允许再次封装或用于商业用途。以前保存的加速地址,使用前需要重新确认所需镜像版本是否存在,以及服务限制是否符合使用场景。
这项变化针对容器镜像加速,Docker CE 的 APT 镜像站是另一项服务。
腾讯云环境中,旧教程里的公共加速地址也需要重新检查。先确认服务是否仍在提供、是否限制访问来源,再尝试拉取实际需要的镜像版本。
对于需要长期运行的应用和服务,我更倾向于提前将所需镜像保存到自己可控的仓库,记录验证过的版本和 digest。部署时直接使用这些镜像,避免临时寻找公共代理。
registry-mirrors 用于 Docker Hub 镜像代理,其他 Registry 不会因此自动走代理。普通私有仓库也不能直接当作镜像代理填写;使用其中的镜像时,按包含仓库域名和路径的完整名称拉取即可。
如果通过代理访问镜像仓库,还要区分 Shell 和 Docker daemon 的配置。Shell 中设置的代理不会自动传给 systemd 管理的 Docker 服务。curl 已经能访问外网,Docker 仍拉不下来镜像时,继续检查 Docker daemon 的代理设置。
遇到问题和后续升级
按失败环节排查
遇到报错,先分清问题出在脚本下载、软件包安装,还是容器运行。不同环节使用的地址和配置不同,可以按下面的线索继续检查。
| 现象 | 优先检查 |
|---|---|
get.docker.com 超时 |
脚本下载链路;改用官方源码入口、本文 fork 或完整脚本附录,也可以绕过脚本配置 APT |
unknown mirror 'Tencent' |
fork 参数应为 TencentCloud;前面的官方脚本方案仍使用 DOWNLOAD_URL |
官方脚本提示 unknown mirror 'TencentCloud' |
可能拿错脚本;使用本文 fork,或回到官方 DOWNLOAD_URL 用法 |
raw.githubusercontent.com 不可达 |
只是脚本下载入口失败;使用本文完整脚本附录,并单独检查软件包源 |
| 附录保存时校验失败 | 停止执行;检查复制是否完整、Tab 是否被转换,以及是否引用了不同提交 |
| 保存脚本时提示文件已存在 | 不会覆盖旧文件;先审阅、备份或更换目标文件名,不继续执行不明版本 |
Release 下载失败、返回 404 |
网络、路径和该发行版的镜像同步状态;不要借用其他系统代号 |
软件包候选版本是 (none) |
APT 索引是否成功更新,Suites、架构和镜像同步状态 |
NO_PUBKEY、签名验证失败 |
公钥来源、文件可读性、Signed-By 配置;不要关闭签名验证 |
Conflicting values set for option Signed-By |
同一个 Docker 仓库是否被重复配置,且绑定了不同公钥 |
docker version 只有 Client |
当前连接的 daemon、服务状态、上下文及权限 |
docker pull 超时 |
容器 Registry 访问、认证或 daemon 代理,不是 APT 软件源 |
软件包安装问题可以对照 Docker Ubuntu 安装文档检查;需要通过代理拉取镜像时,参考 Docker daemon 代理配置,再结合实际日志定位。
后续升级使用 APT
安装完成后,保存当前的软件包版本:
dpkg-query -W -f='${binary:Package}\t${Version}\n' \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin \
| tee docker-package-versions.txt
准备升级时,更新索引并查看 Engine 和 CLI 的可用版本:
sudo apt-get -o APT::Update::Error-Mode=any update && \
apt-cache madison docker-ce docker-ce-cli
先在测试环境验证新版本,备份业务数据和配置,再于维护窗口执行升级:
sudo apt-get install --only-upgrade \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
这条命令可能升级到仓库中的新主版本,执行前检查 APT 给出的升级计划。需要固定版本时,显式指定验证过的软件包版本,并继续安排后续安全更新。
已有安装通过 APT 升级即可。Docker 安装脚本说明也提醒,便捷脚本不适合作为已有环境的常规升级工具。
调整存储前,先检查实际配置
旧教程中的存储配置,不一定适用于当前版本。设置 storage-driver: overlay2 或修改 data-root 前,先确认 Docker 实际使用的存储方式。
按照 Docker containerd image store 文档,Engine 29.0 及以后版本的新安装默认使用 containerd image store。镜像内容和容器快照可能位于 /var/lib/containerd,其他 Docker 数据仍在 /var/lib/docker。
这种情况下,修改 Docker 的 data-root不会同时迁移 containerd 的存储目录,需要分别规划。
先查看当前配置和磁盘用量:
sudo docker info
sudo docker system df
df -h
df -ih
各种服务还有各自的数据目录和数据卷,需要单独安排备份与恢复。遇到安装或启动问题时,先保留这些数据,查清原因后再处理,避免把重装软件变成数据丢失。
其他
完整的 fork 安装脚本快照
下面的脚本来自 soulteary/docker-install,固定提交为 f6ea53e5f8447c97d1b0d9eca8f2d5bab9637de2,与前面的固定版本下载示例一致。
install.sh 大小为 24,847 字节,Git blob 为 a759809fae73e15e5c6ce743acb048949d041f78。脚本正文保留原始内容,采用 Apache License 2.0,并保留原始版权声明和 NOTICE。
复制下面的完整代码块,从开头的 ( 到结尾的 ),不要包含 Markdown 代码围栏。在普通用户的工作目录中执行,会保存 get-docker-fork.sh。这一步不需要 sudo,也不会安装 Docker。
保存命令会检查 SHA-256 和 Shell 语法。如果同名文件已经存在,命令会停止并保留旧文件。保存成功后,回到前面的脚本章节,查看内容、预演,再执行安装。
这个版本已经包含 --setup-repo 参数解析修复,可以与 --dry-run 一起使用,无需采用旧版的参数顺序规避写法。
复制时保留脚本中的 Tab、换行和结束标记。如果校验失败,先检查复制内容和对应版本,不要跳过校验继续安装。
(
set -eu
umask 077
WORK="$(mktemp -d ./docker-install.XXXXXXXX)"
trap 'rm -rf "$WORK"' EXIT
cat > "$WORK/install.sh" <<'DOCKER_INSTALL_SNAPSHOT_EOF'
#!/bin/sh
set -e
# Docker Engine for Linux installation script.
#
# This script is intended as a convenient way to configure docker's package
# repositories and to install Docker Engine, This script is not recommended
# for production environments. Before running this script, make yourself familiar
# with potential risks and limitations, and refer to the installation manual
# at https://docs.docker.com/engine/install/ for alternative installation methods.
#
# The script:
#
# - Requires `root` or `sudo` privileges to run.
# - Attempts to detect your Linux distribution and version and configure your
# package management system for you.
# - Doesn't allow you to customize most installation parameters.
# - Installs dependencies and recommendations without asking for confirmation.
# - Installs the latest stable release (by default) of Docker CLI, Docker Engine,
# Docker Buildx, Docker Compose, containerd, and runc. When using this script
# to provision a machine, this may result in unexpected major version upgrades
# of these packages. Always test upgrades in a test environment before
# deploying to your production systems.
# - Isn't designed to upgrade an existing Docker installation. When using the
# script to update an existing installation, dependencies may not be updated
# to the expected version, resulting in outdated versions.
#
# Source code is available at https://github.com/docker/docker-install/
#
# Usage
# ==============================================================================
#
# To install the latest stable versions of Docker CLI, Docker Engine, and their
# dependencies:
#
# 1. download the script
#
# $ curl -fsSL https://get.docker.com -o install-docker.sh
#
# 2. verify the script's content
#
# $ cat install-docker.sh
#
# 3. run the script with --dry-run to verify the steps it executes
#
# $ sh install-docker.sh --dry-run
#
# 4. run the script either as root, or using sudo to perform the installation.
#
# $ sudo sh install-docker.sh
#
# Command-line options
# ==============================================================================
#
# --version <VERSION>
# Use the --version option to install a specific version, for example:
#
# $ sudo sh install-docker.sh --version 23.0
#
# --channel <stable|test>
#
# Use the --channel option to install from an alternative installation channel.
# The following example installs the latest versions from the "test" channel,
# which includes pre-releases (alpha, beta, rc):
#
# $ sudo sh install-docker.sh --channel test
#
# Alternatively, use the script at https://test.docker.com, which uses the test
# channel as default.
#
# --mirror <Aliyun|AzureChinaCloud|TencentCloud>
#
# Use the --mirror option to install from a mirror supported by this script.
# Available mirrors are "Aliyun" (https://mirrors.aliyun.com/docker-ce),
# "AzureChinaCloud" (https://mirror.azure.cn/docker-ce), and
# "TencentCloud" (https://mirrors.cloud.tencent.com/docker-ce), for example:
#
# $ sudo sh install-docker.sh --mirror AzureChinaCloud
# $ sudo sh install-docker.sh --mirror TencentCloud
#
# --setup-repo
#
# Use the --setup-repo option to configure Docker's package repositories without
# installing Docker packages. This is useful when you want to add the repository
# but install packages separately:
#
# $ sudo sh install-docker.sh --setup-repo
#
# Automatic Service Start
#
# By default, this script automatically starts the Docker daemon and enables the docker
# service after installation if systemd is used as init.
#
# If you prefer to start the service manually, use the --no-autostart option:
#
# $ sudo sh install-docker.sh --no-autostart
#
# Note: Starting the service requires appropriate privileges to manage system services.
#
# Installing docker-sbx
#
# Set the SBX environment variable to "1" to also install the docker-sbx
# package alongside the regular Docker Engine packages:
#
# $ curl -fsSL https://get.docker.com | sudo SBX=1 sh
#
# ==============================================================================
# Git commit from https://github.com/docker/docker-install when
# the script was uploaded (Should only be modified by upload job):
SCRIPT_COMMIT_SHA="${LOAD_SCRIPT_COMMIT_SHA}"
# strip "v" prefix if present
VERSION="${VERSION#v}"
# The channel to install from:
# * stable
# * test
DEFAULT_CHANNEL_VALUE="stable"
if [ -z "$CHANNEL" ]; then
CHANNEL=$DEFAULT_CHANNEL_VALUE
fi
DEFAULT_DOWNLOAD_URL="https://download.docker.com"
if [ -z "$DOWNLOAD_URL" ]; then
DOWNLOAD_URL=$DEFAULT_DOWNLOAD_URL
fi
DEFAULT_REPO_FILE="docker-ce.repo"
if [ -z "$REPO_FILE" ]; then
REPO_FILE="$DEFAULT_REPO_FILE"
# Automatically default to a staging repo fora
# a staging download url (download-stage.docker.com)
case "$DOWNLOAD_URL" in
*-stage*) REPO_FILE="docker-ce-staging.repo";;
esac
fi
mirror=''
DRY_RUN=${DRY_RUN:-}
REPO_ONLY=${REPO_ONLY:-0}
NO_AUTOSTART=${NO_AUTOSTART:-0}
SBX=${SBX:-0}
# Provide a helpful usage statement when --help or any invalid argument is passed
# to the script. Exit code deliberately not included here as error depends on
# argument provided.
usage() {
echo
echo "USAGE: "
echo " ${0} [--channel <stable|test>] [--mirror <Aliyun|AzureChinaCloud|TencentCloud>] [--version <VERSION>] [--setup-repo] [--no-autostart] [--dry-run] [--help]"
echo
}
while [ $# -gt 0 ]; do
case "$1" in
--channel)
CHANNEL="$2"
shift
;;
--dry-run)
DRY_RUN=1
;;
--mirror)
mirror="$2"
shift
;;
--version)
VERSION="${2#v}"
shift
;;
--setup-repo)
REPO_ONLY=1
shift
;;
--no-autostart)
NO_AUTOSTART=1
;;
--help)
usage
exit 0
;;
--*)
echo "Illegal option $1"
usage
exit 1
;;
esac
shift $(( $# > 0 ? 1 : 0 ))
done
case "$mirror" in
Aliyun)
DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce"
;;
AzureChinaCloud)
DOWNLOAD_URL="https://mirror.azure.cn/docker-ce"
;;
TencentCloud)
DOWNLOAD_URL="https://mirrors.cloud.tencent.com/docker-ce"
;;
"")
;;
*)
>&2 echo "unknown mirror '$mirror': use 'Aliyun', 'AzureChinaCloud', or 'TencentCloud'."
exit 1
;;
esac
case "$CHANNEL" in
stable|test)
;;
*)
>&2 echo "unknown CHANNEL '$CHANNEL': use either stable or test."
exit 1
;;
esac
command_exists() {
command -v "$@" > /dev/null 2>&1
}
# Quote one argument for commands executed through the POSIX shell in $sh_c.
# A closing quote preserves trailing newlines when the result is captured.
shell_quote() (
value=$1
printf "'"
while :; do
case "$value" in
*"'"*)
printf '%s' "${value%%\'*}" "'\''"
value=${value#*\'}
;;
*)
printf "%s'" "$value"
break
;;
esac
done
)
# version_gte checks if the version specified in $VERSION is at least the given
# SemVer (Maj.Minor[.Patch]), or CalVer (YY.MM) version.It returns 0 (success)
# if $VERSION is either unset (=latest) or newer or equal than the specified
# version, or returns 1 (fail) otherwise.
#
# examples:
#
# VERSION=23.0
# version_gte 23.0 // 0 (success)
# version_gte 20.10 // 0 (success)
# version_gte 19.03 // 0 (success)
# version_gte 26.1 // 1 (fail)
version_gte() {
if [ -z "$VERSION" ]; then
return 0
fi
version_compare "$VERSION" "$1"
}
# version_compare compares two version strings (either SemVer (Major.Minor.Path),
# or CalVer (YY.MM) version strings. It returns 0 (success) if version A is newer
# or equal than version B, or 1 (fail) otherwise. Patch releases and pre-release
# (-alpha/-beta) are not taken into account
#
# examples:
#
# version_compare 23.0.0 20.10 // 0 (success)
# version_compare 23.0 20.10 // 0 (success)
# version_compare 20.10 19.03 // 0 (success)
# version_compare 20.10 20.10 // 0 (success)
# version_compare 19.03 20.10 // 1 (fail)
version_compare() (
set +x
yy_a="$(echo "$1" | cut -d'.' -f1)"
yy_b="$(echo "$2" | cut -d'.' -f1)"
if [ "$yy_a" -lt "$yy_b" ]; then
return 1
fi
if [ "$yy_a" -gt "$yy_b" ]; then
return 0
fi
mm_a="$(echo "$1" | cut -d'.' -f2)"
mm_b="$(echo "$2" | cut -d'.' -f2)"
# trim leading zeros to accommodate CalVer
mm_a="${mm_a#0}"
mm_b="${mm_b#0}"
if [ "${mm_a:-0}" -lt "${mm_b:-0}" ]; then
return 1
fi
return 0
)
# Select the first APT package version matching a literal Docker version prefix.
# Restrict matching to madison's version field, ignoring the epoch and distro
# suffix. Require a component boundary so 24.0.1 cannot select 24.0.10.
# Debian packages use "~" for Docker's "-ce" and pre-release separators.
# Repository ordering is preserved, including the existing test-channel behavior.
select_apt_package_version() (
# An embedded newline would give grep multiple expressions and could turn a
# later fragment into an unanchored match. Invalid pins must select nothing.
case "$1" in
*'
'*) return ;;
esac
pkg_pattern="$(printf '%s\n' "$1" | sed 's/-/~/g; s/[][\\.^$*+?(){}|]/\\&/g')"
awk -F '|' '{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}' |
grep -E "^([0-9]+:)?$pkg_pattern([.~+-]|$)" | head -1
)
is_dry_run() {
if [ -z "$DRY_RUN" ]; then
return 1
else
return 0
fi
}
is_wsl() {
case "$(uname -r)" in
*microsoft* ) true ;; # WSL 2
*Microsoft* ) true ;; # WSL 1
* ) false;;
esac
}
is_darwin() {
case "$(uname -s)" in
*darwin* ) true ;;
*Darwin* ) true ;;
* ) false;;
esac
}
deprecation_notice() {
distro=$1
distro_version=$2
echo
printf "\033[91;1mDEPRECATION WARNING\033[0m\n"
printf " This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version"
echo " No updates or security fixes will be released for this distribution, and users are recommended"
echo " to upgrade to a currently maintained version of $distro."
echo
printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue."
echo
sleep 10
}
get_distribution() {
lsb_dist=""
# Every system that we officially support has /etc/os-release
if [ -r /etc/os-release ]; then
lsb_dist="$(. /etc/os-release && echo "$ID")"
fi
# Normalize Fedora Asahi Remix to fedora
if [ "$lsb_dist" = "fedora-asahi-remix" ]; then
lsb_dist="fedora"
fi
# Returning an empty string here should be alright since the
# case statements don't act unless you provide an actual value
echo "$lsb_dist"
}
start_docker_daemon() {
# Use systemctl if available (for systemd-based systems)
if command_exists systemctl; then
is_dry_run || >&2 echo "Using systemd to manage Docker service"
if (
is_dry_run || set -x
$sh_c "systemctl enable --now docker.service 2>/dev/null"
); then
is_dry_run || echo "INFO: Docker daemon enabled and started" >&2
else
is_dry_run || echo "WARNING: unable to enable the docker service" >&2
fi
else
# No service management available (container environment)
if ! is_dry_run; then
>&2 echo "Note: Running in a container environment without service management"
>&2 echo "Docker daemon cannot be started automatically in this environment"
>&2 echo "The Docker packages have been installed successfully"
fi
fi
>&2 echo
}
echo_docker_as_nonroot() {
if is_dry_run; then
return
fi
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
$sh_c 'docker version'
) || true
fi
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
echo
echo "================================================================================"
echo
if version_gte "20.10"; then
echo "To run Docker as a non-privileged user, consider setting up the"
echo "Docker daemon in rootless mode for your user:"
echo
echo " dockerd-rootless-setuptool.sh install"
echo
echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode."
echo
fi
echo
echo "To run the Docker daemon as a fully privileged service, but granting non-root"
echo "users access, refer to https://docs.docker.com/go/daemon-access/"
echo
echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent"
echo " to root access on the host. Refer to the 'Docker daemon attack surface'"
echo " documentation for details: https://docs.docker.com/go/attack-surface/"
echo
echo "================================================================================"
echo
}
# Check if this is a forked Linux distro
check_forked() {
# Check for lsb_release command existence, it usually exists in forked distros
if command_exists lsb_release; then
# A successful upstream probe means we're in a forked distro.
if lsb_release -a -u > /dev/null 2>&1; then
# Print info about current distro
cat <<-EOF
You're using '$lsb_dist' version '$dist_version'.
EOF
# Get the upstream release info
lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]')
dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]')
# Print info about upstream distro
cat <<-EOF
Upstream release is '$lsb_dist' version '$dist_version'.
EOF
else
if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then
if [ "$lsb_dist" = "osmc" ]; then
# OSMC runs Raspbian
lsb_dist=raspbian
else
# We're Debian and don't even know it!
lsb_dist=debian
fi
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
case "$dist_version" in
13|14|forky)
dist_version="trixie"
;;
12)
dist_version="bookworm"
;;
11)
dist_version="bullseye"
;;
10)
dist_version="buster"
;;
9)
dist_version="stretch"
;;
8)
dist_version="jessie"
;;
esac
fi
fi
fi
}
do_install() {
echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA"
if [ "$REPO_ONLY" != "1" ] && command_exists docker; then
cat >&2 <<-'EOF'
Warning: the "docker" command appears to already exist on this system.
If you already have Docker installed, this script can cause trouble, which is
why we're displaying this warning and provide the opportunity to cancel the
installation.
If you installed the current Docker package using this script and are using it
again to update Docker, you can ignore this message, but be aware that the
script resets any custom changes in the deb and rpm repo configuration
files to match the parameters passed to the script.
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
user="$(id -un 2>/dev/null || true)"
sh_c='sh -c'
if [ "$user" != 'root' ]; then
if command_exists sudo; then
sh_c='sudo -E sh -c'
elif command_exists su; then
sh_c='su -c'
else
cat >&2 <<-'EOF'
Error: this installer needs the ability to run commands as root.
We are unable to find either "sudo" or "su" available to make this happen.
EOF
exit 1
fi
fi
if is_dry_run; then
sh_c="echo"
fi
# perform some very rudimentary platform detection
lsb_dist=$( get_distribution )
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
if is_wsl; then
echo
echo "WSL DETECTED: We recommend using Docker Desktop for Windows."
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop/"
echo
cat >&2 <<-'EOF'
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
case "$lsb_dist" in
ubuntu)
if command_exists lsb_release; then
dist_version="$(lsb_release --codename | cut -f2)"
fi
if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
fi
;;
debian|raspbian)
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
case "$dist_version" in
13)
dist_version="trixie"
;;
12)
dist_version="bookworm"
;;
11)
dist_version="bullseye"
;;
10)
dist_version="buster"
;;
9)
dist_version="stretch"
;;
8)
dist_version="jessie"
;;
esac
;;
centos|rhel|rocky)
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
;;
*)
if command_exists lsb_release; then
dist_version="$(lsb_release --release | cut -f2)"
fi
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
;;
esac
# Check if this is a forked Linux distro
check_forked
# Print deprecation warnings for distro versions that recently reached EOL,
# but may still be commonly used (especially LTS versions).
case "$lsb_dist.$dist_version" in
centos.8|centos.7|rhel.7)
deprecation_notice "$lsb_dist" "$dist_version"
;;
debian.bullseye|debian.buster|debian.stretch|debian.jessie)
deprecation_notice "$lsb_dist" "$dist_version"
;;
raspbian.buster|raspbian.stretch|raspbian.jessie)
deprecation_notice "$lsb_dist" "$dist_version"
;;
ubuntu.focal|ubuntu.bionic|ubuntu.xenial|ubuntu.trusty)
deprecation_notice "$lsb_dist" "$dist_version"
;;
ubuntu.questing|ubuntu.oracular|ubuntu.mantic|ubuntu.lunar|ubuntu.kinetic|ubuntu.impish|ubuntu.hirsute|ubuntu.groovy|ubuntu.eoan|ubuntu.disco|ubuntu.cosmic)
deprecation_notice "$lsb_dist" "$dist_version"
;;
fedora.*)
if [ "$dist_version" -lt 43 ]; then
deprecation_notice "$lsb_dist" "$dist_version"
fi
;;
esac
# Run setup for each distro accordingly
case "$lsb_dist" in
ubuntu|debian|raspbian)
pre_reqs="ca-certificates curl"
apt_repo_lsb_dist="$lsb_dist"
# Docker does not publish a Raspbian Trixie repo; use Debian Trixie instead.
if [ "$lsb_dist" = "raspbian" ] && [ "$dist_version" = "trixie" ]; then
apt_repo_lsb_dist="debian"
fi
apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] $DOWNLOAD_URL/linux/$apt_repo_lsb_dist $dist_version $CHANNEL"
(
if ! is_dry_run; then
set -x
fi
$sh_c 'apt-get -qq update >/dev/null'
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=60 -y -qq install $pre_reqs >/dev/null"
$sh_c 'install -m 0755 -d /etc/apt/keyrings'
$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$apt_repo_lsb_dist/gpg\" -o /etc/apt/keyrings/docker.asc"
$sh_c "chmod a+r /etc/apt/keyrings/docker.asc"
$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list"
$sh_c 'apt-get -qq update >/dev/null'
)
if [ "$REPO_ONLY" = "1" ]; then
exit 0
fi
pkg_version=""
cli_pkg_version=""
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
search_command="apt-cache madison docker-ce"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
pkg_version="$($sh_c "$search_command" | select_apt_package_version "$VERSION")"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst apt-cache madison results"
echo
exit 1
fi
pkg_version="=$pkg_version"
if version_gte "18.09"; then
search_command="apt-cache madison docker-ce-cli"
echo "INFO: $search_command"
cli_pkg_version="$($sh_c "$search_command" | select_apt_package_version "$VERSION")"
if [ -n "$cli_pkg_version" ]; then
cli_pkg_version="=$cli_pkg_version"
fi
fi
fi
fi
(
pkgs="docker-ce${pkg_version}"
if version_gte "18.09"; then
# older versions didn't ship the cli and containerd as separate packages
pkgs="$pkgs docker-ce-cli${cli_pkg_version} containerd.io"
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin"
fi
if version_gte "28.2"; then
pkgs="$pkgs docker-model-plugin"
fi
if [ "$SBX" = "1" ]; then
pkgs="$pkgs docker-sbx"
fi
if ! is_dry_run; then
set -x
fi
apt_flags="-y -qq"
if [ -n "$pkg_version" ]; then
apt_flags="$apt_flags --allow-downgrades"
fi
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=60 $apt_flags install $pkgs >/dev/null"
)
if [ "$NO_AUTOSTART" != "1" ]; then
start_docker_daemon
fi
echo_docker_as_nonroot
exit 0
;;
centos|fedora|rhel|rocky)
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
repo_file_url_quoted=$(shell_quote "$repo_file_url")
(
if ! is_dry_run; then
set -x
fi
if command_exists dnf5; then
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
$sh_c "dnf5 config-manager addrepo --overwrite --save-filename=docker-ce.repo --from-repofile=$repo_file_url_quoted"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "dnf5 config-manager setopt \"docker-ce-*.enabled=0\""
$sh_c "dnf5 config-manager setopt \"docker-ce-$CHANNEL.enabled=1\""
fi
$sh_c "dnf makecache"
elif command_exists dnf; then
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
$sh_c "dnf config-manager --add-repo $repo_file_url_quoted"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "dnf config-manager --set-disabled \"docker-ce-*\""
$sh_c "dnf config-manager --set-enabled \"docker-ce-$CHANNEL\""
fi
$sh_c "dnf makecache"
else
$sh_c "yum -y -q install yum-utils"
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
$sh_c "yum-config-manager --add-repo $repo_file_url_quoted"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "yum-config-manager --disable \"docker-ce-*\""
$sh_c "yum-config-manager --enable \"docker-ce-$CHANNEL\""
fi
$sh_c "yum makecache"
fi
)
if [ "$REPO_ONLY" = "1" ]; then
exit 0
fi
pkg_version=""
cli_pkg_version=""
if command_exists dnf; then
pkg_manager="dnf"
pkg_manager_flags="-y -q --best"
else
pkg_manager="yum"
pkg_manager_flags="-y -q"
fi
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
if [ "$lsb_dist" = "fedora" ]; then
pkg_suffix="fc$dist_version"
else
pkg_suffix="el"
fi
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g').*$pkg_suffix"
search_command="$pkg_manager list --showduplicates docker-ce | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
pkg_version="$($sh_c "$search_command")"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst $pkg_manager list results"
echo
exit 1
fi
# Cut out the epoch and prefix with a '-'
pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)"
if version_gte "18.09"; then
# older versions don't support a cli package
search_command="$pkg_manager list --showduplicates docker-ce-cli | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)"
fi
fi
fi
(
pkgs="docker-ce$pkg_version"
if version_gte "18.09"; then
# older versions didn't ship the cli and containerd as separate packages
if [ -n "$cli_pkg_version" ]; then
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
else
pkgs="$pkgs docker-ce-cli containerd.io"
fi
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin docker-model-plugin"
fi
if [ "$SBX" = "1" ]; then
pkgs="$pkgs docker-sbx"
fi
if ! is_dry_run; then
set -x
fi
$sh_c "$pkg_manager $pkg_manager_flags install $pkgs"
)
if [ "$NO_AUTOSTART" != "1" ]; then
start_docker_daemon
fi
echo_docker_as_nonroot
exit 0
;;
sles)
echo "Effective v27.5, please consult SLES distro statement for s390x support."
exit 1
;;
*)
if [ -z "$lsb_dist" ]; then
if is_darwin; then
echo
echo "ERROR: Unsupported operating system 'macOS'"
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
echo
exit 1
fi
fi
echo
echo "ERROR: Unsupported distribution '$lsb_dist'"
echo
exit 1
;;
esac
exit 1
}
# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
do_install
DOCKER_INSTALL_SNAPSHOT_EOF
printf '%s %s\n' \
'fdae16d31d2a87d0056fec49454a2e1475230abe64c52ad900efb65e425399ef' \
"$WORK/install.sh" | sha256sum -c -
sh -n "$WORK/install.sh"
ln "$WORK/install.sh" ./get-docker-fork.sh
printf '%s\n' '脚本已保存为 get-docker-fork.sh;尚未执行安装。'
)
保存完成后,回到「也可以使用安装脚本」,审阅、预演,再选一套安装命令执行。
选择 main 或固定版本?
如果希望获取后续维护的脚本,可以使用 main 分支。需要与本文附录保持一致,则使用下面这个包含参数解析修复的固定提交:
f6ea53e5f8447c97d1b0d9eca8f2d5bab9637de2
对应 install.sh 的 SHA-256 为:
2b9273b4c126ee83f2cf4eea558f415ab0e16c8c946ca7898bd426ac0d819ab2
下面的命令会下载固定版本,检查校验和与语法,再保存脚本。它与前面的 main 下载方式二选一,同样不会覆盖已有文件:
(
set -eu
umask 077
WORK="$(mktemp -d ./docker-install.XXXXXXXX)"
trap 'rm -rf "$WORK"' EXIT
curl -q -fsSL --proto '=https' --proto-redir '=https' \
--connect-timeout 10 --max-time 120 \
https://raw.githubusercontent.com/soulteary/docker-install/f6ea53e5f8447c97d1b0d9eca8f2d5bab9637de2/install.sh \
-o "$WORK/install.sh"
test -s "$WORK/install.sh"
printf '%s %s\n' \
'2b9273b4c126ee83f2cf4eea558f415ab0e16c8c946ca7898bd426ac0d819ab2' \
"$WORK/install.sh" | sha256sum -c -
sh -n "$WORK/install.sh"
ln "$WORK/install.sh" ./get-docker-fork.sh
printf '%s\n' '脚本已保存为 get-docker-fork.sh;尚未执行安装。'
)
这个校验值只对应上述固定提交。main 更新后,文件内容和校验值都可能变化,不能继续拿这里的值校验新文件。
SHA-256 用于检查下载或复制的内容是否一致,预期校验值也需要来自可信渠道。它不能替代作者签名或脚本审阅。
固定脚本版本后,最终安装的软件包仍可能变化。未指定版本时,脚本会从软件源选择软件包,--version 也不会锁定全部依赖和插件。需要复现安装环境时,把实际软件包版本、架构、软件源和验证结果一起记录下来。
fork 中的其他调整
除了腾讯云参数和参数解析修复,这个版本还调整了 dpkg 锁等待、APT 版本匹配、RPM 仓库 URL 参数引用,以及衍生发行版的上游探测。具体改动和测试范围可以查看仓库说明。
APT 安装命令加入了 DPkg::Lock::Timeout=60,用于等待 dpkg 锁,最多等待 60 秒。这个设置不限制整个安装过程,也不覆盖 apt-get update 使用的索引锁。遇到锁占用,先确认系统初始化或自动更新是否仍在运行,等它完成,不要直接删除锁文件。
脚本默认安装的组件可能比前面的手动 APT 方案多,包括 rootless extras、Model 插件等。某个附加包找不到时,先对照预演输出检查镜像站同步情况。自行删掉报错包再继续,会改变原本的安装内容。
下载 install.sh 后可以直接使用,无需先执行 make build。未设置构建变量 LOAD_SCRIPT_COMMIT_SHA 时,脚本开头的 commit: 可能为空,不影响软件源选择。这个值也能通过环境变量设置,追溯版本时,仍以保存的来源提交和文件校验值为准。
最后
当我们将脚本下载、软件包安装和容器镜像拉取分开检查,遇到网络问题时,会少走一些弯路。
Docker 的配置和安装聊到这里,基础准备就差不多了。后面再继续折腾具体应用的时候,把应用权限、持久化和备份再一起配好。
这篇文章就先写到这里吧。
—EOF