1. 安装脚本:
#!/usr/bin/env bash
#
# Docker 安装脚本(Ubuntu)
#
# 功能:
# - 安装 docker-ce / docker-ce-cli / containerd.io
# - 安装 docker-buildx-plugin / docker-compose-plugin
# - 可选按官方方式卸载旧 Docker 相关包
# - 可选使用国内 APT 源安装 Docker
# - 可选配置 Docker registry 国内镜像加速
# - 可选配置容器日志轮转限制
# - 默认自动检测网络环境
#
# 支持:
# - Ubuntu 20.04 / 22.04 / 24.04
#
# 用法示例:
# sudo bash install_docker.sh
# sudo bash install_docker.sh -u
# sudo bash install_docker.sh -m -r -c
# sudo bash install_docker.sh -h
# sudo bash install_docker.sh --help
if [ -z "${BASH_VERSION:-}" ]; then
echo "Please run this script with bash"
exit 1
fi
set -Eeuo pipefail
#==============================
# 默认参数
#==============================
UNINSTALL=0
USE_CN_SOURCE=0
ENABLE_LOG_LIMIT=0
ENABLE_REGISTRY_MIRROR=0
AUTO_DETECT=1
NEED_RELOGIN=0
#==============================
# 日志函数
#==============================
log() { echo -e "[INFO] $*"; }
warn() { echo -e "[WARN] $*"; }
error() { echo -e "[ERROR] $*" >&2; }
ok() { echo -e "[ OK ] $*"; }
#==============================
# 帮助信息
#==============================
show_help() {
cat <<'EOF'
Docker 安装脚本(Ubuntu)
用法:
install_docker.sh [options]
选项:
-u 按官方方式卸载旧 Docker 相关包后再安装
-m 强制使用国内 Docker APT 源安装(清华源)
-r 强制配置 Docker registry 国内镜像加速
-c 配置容器日志限制
-h, --help 显示帮助信息并退出
说明:
- 本脚本默认自动检测网络环境:
国内网络:自动启用国内 APT 源和 registry 镜像加速
海外网络:默认使用 Docker 官方 APT 源,不启用镜像加速
- 指定 -m 或 -r 可手动强制开启对应配置
- -c 会配置 json-file 日志轮转:
max-size=20m
max-file=3
- 若 /etc/docker/daemon.json 已存在,会先自动备份再覆盖
- 本脚本不会自动删除 /var/lib/docker 和 /var/lib/containerd
EOF
}
#==============================
# 错误处理
#==============================
on_error() {
local exit_code=$?
error "脚本执行失败,退出码:${exit_code}"
exit "${exit_code}"
}
trap on_error ERR
#==============================
# 无参数提示
#==============================
show_default_hint_if_no_args() {
if [ "$#" -eq 0 ]; then
echo "未指定任何参数,将按默认方式安装 Docker。"
echo "默认行为:自动检测网络环境;国内网络自动使用国内源和镜像加速;海外网络使用官方源。"
echo
fi
}
#==============================
# 权限检查
#==============================
check_root() {
if [ "$(id -u)" -ne 0 ]; then
error "请使用 root 或 sudo 运行本脚本。"
exit 1
fi
}
#==============================
# 系统检查
#==============================
detect_os() {
if [ ! -f /etc/os-release ]; then
error "无法检测系统版本:缺少 /etc/os-release"
exit 1
fi
# shellcheck disable=SC1091
. /etc/os-release
if [ "${ID:-}" != "ubuntu" ]; then
error "当前系统不是 Ubuntu(检测到 ID=${ID:-unknown}),本脚本仅支持 Ubuntu。"
exit 1
fi
CODENAME="${VERSION_CODENAME:-}"
if [ -z "$CODENAME" ]; then
case "${VERSION_ID:-}" in
"24.04") CODENAME="noble" ;;
"22.04") CODENAME="jammy" ;;
"20.04") CODENAME="focal" ;;
*)
error "无法自动识别当前 Ubuntu 代号(VERSION_ID=${VERSION_ID:-unknown})"
exit 1
;;
esac
fi
ARCH="$(dpkg --print-architecture)"
ok "检测到系统:Ubuntu ${VERSION_ID:-unknown} (${CODENAME}), arch=${ARCH}"
}
#==============================
# 参数解析
#==============================
parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
-u)
UNINSTALL=1
;;
-m)
USE_CN_SOURCE=1
;;
-r)
ENABLE_REGISTRY_MIRROR=1
;;
-c)
ENABLE_LOG_LIMIT=1
;;
-h|--help)
show_help
exit 0
;;
--)
shift
break
;;
-*)
error "未知选项: $1"
echo
show_help
exit 1
;;
*)
error "不支持的位置参数: $1"
echo
show_help
exit 1
;;
esac
shift
done
}
#==============================
# 自动检测国内网络
#==============================
detect_china_network() {
local country=""
country="$(curl -fsSL --connect-timeout 3 --max-time 5 https://ipinfo.io/country 2>/dev/null | tr -d '[:space:]' || true)"
if [ "$country" = "CN" ]; then
return 0
elif [ -n "$country" ]; then
return 1
fi
country="$(curl -fsSL --connect-timeout 3 --max-time 5 https://ifconfig.co/country-iso 2>/dev/null | tr -d '[:space:]' || true)"
if [ "$country" = "CN" ]; then
return 0
elif [ -n "$country" ]; then
return 1
fi
if ! curl -fsSL --connect-timeout 3 --max-time 5 https://registry-1.docker.io/v2/ >/dev/null 2>&1; then
warn "地区判断失败,且 Docker Hub 连通性较差,按中国网络环境处理"
return 0
fi
return 1
}
apply_auto_detect() {
if [ "$AUTO_DETECT" -ne 1 ]; then
return
fi
log "开始自动检测网络环境..."
if detect_china_network; then
if [ "$USE_CN_SOURCE" -eq 0 ]; then
USE_CN_SOURCE=1
fi
if [ "$ENABLE_REGISTRY_MIRROR" -eq 0 ]; then
ENABLE_REGISTRY_MIRROR=1
fi
ok "检测结果:当前更像中国网络环境,自动启用国内 APT 源和 registry 镜像加速"
else
ok "检测结果:当前更像海外网络环境,默认使用官方 APT 源,不启用 registry 镜像加速"
fi
}
#==============================
# 安装基础依赖
#==============================
install_dependencies() {
log "安装必要依赖..."
apt update
apt install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
ok "基础依赖安装完成"
}
#==============================
# 卸载旧 Docker(官方方式)
#==============================
uninstall_old_docker() {
log "按官方方式卸载旧 Docker 相关包..."
systemctl stop docker 2>/dev/null || true
systemctl stop docker.socket 2>/dev/null || true
systemctl stop containerd 2>/dev/null || true
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
apt remove -y "$pkg" || true
done
apt autoremove -y || true
rm -f /etc/apt/sources.list.d/docker.list
rm -f /etc/apt/keyrings/docker.gpg
rm -f /etc/apt/keyrings/docker.asc
warn "未自动删除 /var/lib/docker 和 /var/lib/containerd,避免误删现有镜像/容器数据"
warn "如需彻底清理,可手动执行:rm -rf /var/lib/docker /var/lib/containerd"
ok "旧 Docker 相关包清理完成"
}
#==============================
# 配置 Docker APT 源
#==============================
configure_docker_repo() {
log "配置 Docker APT 源..."
rm -f /etc/apt/sources.list.d/docker.list
rm -f /etc/apt/keyrings/docker.gpg
if [ "$USE_CN_SOURCE" -eq 1 ]; then
log "使用清华 Docker CE APT 源"
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
cat >/etc/apt/sources.list.d/docker.list <<EOF
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu ${CODENAME} stable
EOF
else
log "使用 Docker 官方 APT 源"
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
cat >/etc/apt/sources.list.d/docker.list <<EOF
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${CODENAME} stable
EOF
fi
chmod a+r /etc/apt/keyrings/docker.gpg
apt update
ok "Docker APT 源配置完成"
}
#==============================
# 安装 Docker
#==============================
install_docker() {
log "安装 Docker Engine / CLI / containerd / Buildx / Compose..."
apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
ok "Docker 安装完成"
}
#==============================
# 备份 daemon.json
#==============================
backup_daemon_json_if_exists() {
if [ -f /etc/docker/daemon.json ]; then
local backup_file="/etc/docker/daemon.json.bak.$(date +%Y%m%d%H%M%S)"
cp -f /etc/docker/daemon.json "$backup_file"
warn "检测到已有 /etc/docker/daemon.json,已备份到:$backup_file"
fi
}
#==============================
# 写入 daemon.json
#==============================
write_daemon_json() {
mkdir -p /etc/docker
backup_daemon_json_if_exists
local need_comma=0
{
echo "{"
if [ "$ENABLE_LOG_LIMIT" -eq 1 ]; then
echo ' "log-driver": "json-file",'
echo ' "log-opts": {'
echo ' "max-size": "20m",'
echo ' "max-file": "3"'
echo -n ' }'
need_comma=1
fi
if [ "$ENABLE_REGISTRY_MIRROR" -eq 1 ]; then
if [ "$need_comma" -eq 1 ]; then
echo ","
else
echo
fi
echo ' "registry-mirrors": ['
echo ' "https://docker.m.daocloud.io",'
echo ' "https://hub-mirror.c.163.com",'
echo ' "https://mirror.baidubce.com"'
echo -n ' ]'
fi
echo
echo "}"
} >/etc/docker/daemon.json
ok "已写入 /etc/docker/daemon.json"
}
#==============================
# 启动 Docker
#==============================
enable_and_start_docker() {
log "启动 Docker 服务并设置开机自启..."
systemctl enable --now docker
ok "Docker 服务已启动"
}
#==============================
# 应用 Docker 配置
#==============================
apply_docker_config_if_needed() {
if [ "$ENABLE_LOG_LIMIT" -eq 0 ] && [ "$ENABLE_REGISTRY_MIRROR" -eq 0 ]; then
return
fi
log "写入 Docker 配置..."
write_daemon_json
log "重载并重启 Docker..."
systemctl daemon-reload
systemctl restart docker
ok "Docker 配置已生效"
}
#==============================
# 用户加入 docker 组
#==============================
add_user_to_docker_group() {
local orig_user="${SUDO_USER:-}"
if [ -n "$orig_user" ] && [ "$orig_user" != "root" ]; then
log "将用户 ${orig_user} 加入 docker 组..."
usermod -aG docker "$orig_user"
NEED_RELOGIN=1
ok "用户 ${orig_user} 已加入 docker 组"
fi
}
#==============================
# 验证安装
#==============================
verify_installation() {
echo
log "验证安装结果..."
docker --version
docker compose version
systemctl is-active docker >/dev/null
ok "Docker 当前状态:active"
echo
echo "========== 安装结果 =========="
if [ "$USE_CN_SOURCE" -eq 1 ]; then
echo "APT 源:国内清华 Docker 源"
else
echo "APT 源:Docker 官方源"
fi
if [ "$ENABLE_REGISTRY_MIRROR" -eq 1 ]; then
echo "Registry 镜像加速:已开启"
else
echo "Registry 镜像加速:未开启"
fi
if [ "$ENABLE_LOG_LIMIT" -eq 1 ]; then
echo "容器日志限制:已开启(20m x 3)"
else
echo "容器日志限制:未开启"
fi
if [ "$NEED_RELOGIN" -eq 1 ]; then
echo "提示:请退出当前终端并重新登录后,再以普通用户执行 docker 命令。"
fi
echo "测试命令:docker run hello-world"
echo "=============================="
}
#==============================
# 主流程
#==============================
main() {
show_default_hint_if_no_args "$@"
parse_args "$@"
check_root
detect_os
apply_auto_detect
echo
echo "==== Docker 安装脚本启动 ===="
echo "UNINSTALL=${UNINSTALL}"
echo "USE_CN_SOURCE=${USE_CN_SOURCE}"
echo "ENABLE_REGISTRY_MIRROR=${ENABLE_REGISTRY_MIRROR}"
echo "ENABLE_LOG_LIMIT=${ENABLE_LOG_LIMIT}"
echo "AUTO_DETECT=${AUTO_DETECT}"
echo
if [ "$UNINSTALL" -eq 1 ]; then
uninstall_old_docker
fi
install_dependencies
configure_docker_repo
install_docker
enable_and_start_docker
apply_docker_config_if_needed
add_user_to_docker_group
verify_installation
}
main "$@"2. 用户加入Docker用户组
sudo usermod -aG docker ${USER}3. 清理Docker环境
3.1. 查看 Docker 磁盘占用情况
在动手之前,建议先看看是谁在占用空间:
docker system df
这个命令会列出镜像、容器、数据卷和构建缓存(Build Cache)各自占用的空间,以及可回收(RECLAIMABLE)的比例。
3.2. 日常安全清理
这个命令非常安全,它只会清理那些没有任何容器正在使用的“悬空(dangling)”资源:
docker system prune
它会清理:
已停止的容器
没被任何容器使用的网络
悬空镜像(就是名称和标签都是
<none>的残次品镜像)悬挂的构建缓存
3.3. 深度清理
如果你想给 Docker 来一次“大扫除”,把所有没在运行的东西全删掉,可以加上 -a(all)和 --volumes(数据卷)参数:
Bash
docker system prune -a --volumes
它除了执行安全清理的内容外,还会额外清理:
所有没有被当前运行容器使用的镜像(哪怕它是有名字和标签的正常镜像,只要现在没在跑,就会被删掉。下次用需要重新拉取)。
所有没被使用的匿名和命名数据卷(⚠️ 极度危险:如果你的数据库数据卷没有挂载到运行中的容器上,里面的数据会被直接清空!)。
3.4. 分类精准清理
如果你不想一刀切,只想清理某一类特定的资源,可以使用以下命令:
只清理废弃镜像:
docker image prune -a只清理已停止的容器:
docker container prune只清理无用的数据卷(常用于释放大量空间):
docker volume prune清理构建缓存(经常 build 镜像的话,这里会攒很多垃圾):
docker builder prune
3.5 重启docker环境
sudo systemctl daemon-reload
sudo systemctl restart docker4. 将docker镜像推到公网
4.1 注册 Docker Hub 账号
如果你还没有账号,需要先去 Docker Hub 官网 注册一个账号。记住你的 Docker ID(用户名),这在后面打标签时会用到。
4.2 在终端登录Docker Hub
在你的服务器或本地电脑上打开终端,运行登录命令:
docker login
系统会提示你输入 Docker Hub 的用户名(Username)和密码(Password)。 注意:现在 Docker Hub 推荐使用 Personal Access Token (PAT) 代替密码登录,你可以在 Docker Hub 网页端的 Account Settings -> Security 中生成一个 Token 用作密码。
看到 Login Succeeded 就说明登录成功了。
4.3. 找到你本地的镜像
运行以下命令查看你本地已经构建好的镜像:
docker images
假设你本地构建出来的镜像名叫做 my-speedtest,标签是 latest。你需要记住这个名字或者它的 IMAGE ID。
4.4 为镜像重新打标签(Tag)
这是最关键的一步。Docker Hub 要求推送到服务器的镜像名称必须遵循特定的格式:你的Docker用户名/仓库名:标签。
如果你直接 docker push my-speedtest,Docker 会报错,因为它不知道该推送到哪个用户的名下。
假设你的 Docker 用户名是 zhangsan,你想把这个镜像命名为 speedtest-custom,版本号定为 v1.0。你需要执行:
# 格式:docker tag <本地镜像名> <用户名>/<仓库名>:<标签>
docker tag my-speedtest:latest zhangsan/speedtest-custom:v1.0
执行后,你再运行 docker images,就会看到多出了一个名为 zhangsan/speedtest-custom:v1.0 的镜像(它和原镜像的 ID 是一样的,只是多了一个名字)。
4.5 推送到 Docker Hub
现在可以直接推送了:
docker push zhangsan/speedtest-custom:v1.0
这时候你会看到终端开始上传各个数据层(Layer)。根据你的网速和镜像大小,可能需要等待几分钟
评论区