1、写shell脚本
先创建一个脚本文件:vim install-docker.sh
接下来在脚本文件中写一下的shell脚本
适用于:RHEL 9.0~9.6、CentOS Stream 9、Rocky Linux 9
不适用于:CentOS 7 / Ubuntu(需不同脚本)
bash
#!/bin/bash
# install-docker.sh
# 在 RHEL 9 / CentOS Stream / Rocky Linux 9 上自动安装 Docker(支持重复运行)
set -e
echo "开始安装/重装 Docker..."
# === 函数:卸载旧版 Docker 和冲突组件 ===
uninstall_old_docker() {
echo "检测并卸载旧版 Docker / Podman..."
# 停止并禁用服务
for svc in docker docker.socket containerd podman; do
if systemctl is-active --quiet "$svc"; then
sudo systemctl stop "$svc" 2>/dev/null || true
sudo systemctl disable "$svc" 2>/dev/null || true
fi
done
# 卸载相关包
packages=(
docker docker-client docker-client-latest docker-common docker-latest
docker-latest-logrotate docker-logrotate docker-engine
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
podman podman-docker
)
installed_pkgs=()
for pkg in "${packages[@]}"; do
if rpm -q "$pkg" &>/dev/null; then
installed_pkgs+=("$pkg")
fi
done
if [ ${#installed_pkgs[@]} -gt 0 ]; then
echo "卸载已安装的包: ${installed_pkgs[*]}"
sudo dnf remove -y "${installed_pkgs[@]}" || true
else
echo "无旧版 Docker/Podman 需要卸载"
fi
# 清理残留目录(谨慎!会删除所有容器和镜像)
echo "清理 Docker 数据目录(/var/lib/docker)..."
sudo rm -rf /var/lib/docker /etc/docker
}
# === 主流程 ===
# 1. 可选:是否强制重装?这里默认每次运行都清理(适合测试环境)
# 如果你希望保留数据,请注释掉下一行
uninstall_old_docker
# 2. 安装必要依赖
echo "安装依赖工具..."
sudo dnf install -y yum-utils device-mapper-persistent-data lvm2
# 3. 添加 Docker 仓库(如果不存在)
REPO_FILE="/etc/yum.repos.d/docker-ce.repo"
if [ ! -f "$REPO_FILE" ]; then
echo "添加 Docker 阿里云 YUM 源..."
sudo dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
else
echo "Docker 仓库已存在,跳过添加"
fi
# 4. 修复 $releasever(RHEL 9 兼容)
echo "🔧 修复仓库中的 \$releasever 为 9..."
sudo sed -i 's/\$releasever/9/g' "$REPO_FILE"
# 5. 安装 Docker 引擎
echo "安装 Docker CE..."
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 6. 启动并启用服务
echo "启动 Docker 服务..."
sudo systemctl enable --now docker
# 7. 配置国内镜像加速器
echo "配置国内镜像加速器..."
sudo mkdir -p /etc/docker
cat > /tmp/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://ccr.ccs.tencentyun.com",
"https://docker.1ms.run"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
sudo mv /tmp/daemon.json /etc/docker/daemon.json
sudo systemctl restart docker
# 8. 将当前用户加入 docker 组(幂等)
if ! groups "$USER" | grep -qw docker; then
echo "将当前用户加入 docker 组..."
sudo usermod -aG docker "$USER"
else
echo "当前用户已在 docker 组中"
fi
# 9. 验证安装
echo "验证 Docker 安装..."
docker --version
docker run --rm hello-world # 此时应无需 sudo
echo ""
echo "Docker 已成功安装!"
echo "请重新登录终端或运行 'newgrp docker' 以应用用户组变更。"
echo "测试命令:docker run -d -p 8080:80 nginx"
如果以上的第三方docker镜像加速源还是不能成功的话,可以使用以下这些第三方docker镜像源
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://ccr.ccs.tencentyun.com", "https://docker.1ms.run", "https://hub.xdark.top", "https://dhub.kubesre.xyz", "https://docker.kejilion.pro", "https://docker.xuanyuan.me", "https://docker.hlmirror.com", "https://run-docker.cn", "https://docker.sunzishaokao.com", "https://image.cloudlayer.icu" ] } EOF
2、该脚本所涉及的知识点
一、整体结构与设计思想
1. 幂等性(Idempotency)
-
目标:无论脚本运行多少次,结果都一致。
-
实现方式:
-
检查是否已安装 → 避免重复操作
-
卸载旧版本 → 保证干净环境
-
条件判断(
if [ ! -f ... ])→ 防止重复添加仓库
-
这是自动化运维(如 Ansible、Terraform)的核心原则。
二、逐行详解 + 知识点拆解
行 1--3:Shebang 与注释
bash
#!/bin/bash
# install-docker.sh
# 在 RHEL 9 / CentOS Stream / Rocky Linux 9 上自动安装 Docker(支持重复运行)
-
#!/bin/bash:指定解释器为 Bash(不是/bin/sh),确保使用 Bash 特性(如数组)。 -
注释说明用途和兼容系统。
行 5:set -e
bash
set -e
-
作用:脚本中任何命令返回非 0 状态码(失败),立即退出。
-
目的:防止错误累积,提高可靠性。
-
注意:
|| true可绕过此行为(见后文)。
行 7--8:输出提示
bash
echo "开始安装/重装 Docker..."
- 使用
echo向用户反馈进度,提升可读性。
三、函数:uninstall_old_docker
行 11:定义函数
bash
uninstall_old_docker() {
- Bash 支持函数定义,用于封装逻辑复用。
行 15--21:停止并禁用服务
bash
for svc in docker docker.socket containerd podman; do
if systemctl is-active --quiet "$svc"; then
sudo systemctl stop "$svc" 2>/dev/null || true
sudo systemctl disable "$svc" 2>/dev/null || true
fi
done
知识点:
| 内容 | 说明 |
|---|---|
systemctl is-active --quiet |
检查服务是否正在运行(--quiet 不输出,只返回状态码) |
2>/dev/null |
丢弃标准错误输出(避免报错干扰) |
| ` | |
docker.socket |
Docker 的 socket 激活服务(systemd 特性) |
podman |
RHEL 默认容器工具,与 Docker 冲突(尤其 podman-docker 提供 docker 命令别名) |
为什么停 Podman? 因为
podman-docker包会提供一个docker命令(其实是podman的软链接),导致你误以为 Docker 已安装,实则不然。
行 24--35:卸载相关 RPM 包
bash
packages=( ... )
installed_pkgs=()
for pkg in "${packages[@]}"; do
if rpm -q "$pkg" &>/dev/null; then
installed_pkgs+=("$pkg")
fi
done
知识点:
| 内容 | 说明 |
|---|---|
packages=( ... ) |
Bash 数组定义 |
"${packages[@]}" |
展开所有数组元素(带引号防空格问题) |
rpm -q pkg |
查询 RPM 包是否安装(-q = query) |
&>/dev/null |
同时丢弃 stdout 和 stderr |
installed_pkgs+=("$pkg") |
数组追加元素 |
这种"先检测再卸载"比直接
dnf remove更安全,避免报错。
行 37--43:执行卸载
bash
if [ ${#installed_pkgs[@]} -gt 0 ]; then
echo "卸载已安装的包: ${installed_pkgs[*]}"
sudo dnf remove -y "${installed_pkgs[@]}" || true
else
echo "无旧版 Docker/Podman 需要卸载"
fi
-
${#array[@]}:获取数组长度 -
${array[*]}:以空格分隔输出所有元素(用于显示) -
dnf remove -y:自动确认卸载(Y/N)
行 46--47:清理数据目录
bash
sudo rm -rf /var/lib/docker /etc/docker
关键路径说明:
| 路径 | 作用 |
|---|---|
/var/lib/docker |
存放镜像、容器、卷、网络等所有数据(删除即清空所有容器!) |
/etc/docker |
Docker 配置文件目录(如 daemon.json) |
生产环境慎用! 此操作不可逆。
四、主流程详解
行 54:调用卸载函数
bash
uninstall_old_docker
- 默认每次运行都清理(适合测试环境)。如需保留数据,可注释此行。
行 57--58:安装依赖
bash
sudo dnf install -y yum-utils device-mapper-persistent-data lvm2
依赖包作用:
| 包名 | 用途 |
|---|---|
yum-utils |
提供 dnf config-manager 命令 |
device-mapper-persistent-data |
Docker 存储驱动(devicemapper)依赖 |
lvm2 |
逻辑卷管理工具(部分存储驱动需要) |
RHEL 9 使用
dnf(不是yum),但命令兼容。
行 61--67:添加 Docker 仓库(幂等)
bash
REPO_FILE="/etc/yum.repos.d/docker-ce.repo"
if [ ! -f "$REPO_FILE" ]; then
sudo dnf config-manager --add-repo ...
else
echo "Docker 仓库已存在,跳过添加"
fi
-
/etc/yum.repos.d/:YUM/DNF 仓库配置目录 -
dnf config-manager --add-repo URL:自动下载.repo文件到该目录
避免重复添加,防止文件冲突。
行 70:修复 $releasever
bash
sudo sed -i 's/\$releasever/9/g' "$REPO_FILE"
为什么需要?
-
Docker 官方仓库路径包含
$releasever(如.../centos/$releasever/...) -
在 RHEL/CentOS Stream 中,
$releasever可能解析为stream8、stream9等 -
但阿里云镜像只认
8或9,所以强制替换为9
这是 RHEL 9 兼容的关键一步!
行 73--74:安装 Docker 组件
bash
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
组件说明:
| 包名 | 作用 |
|---|---|
docker-ce |
Docker 引擎(核心) |
docker-ce-cli |
docker 命令行工具 |
containerd.io |
容器运行时(Docker 底层依赖) |
docker-buildx-plugin |
多平台构建插件 |
docker-compose-plugin |
Compose v2(作为 docker compose 子命令) |
现代 Docker 推荐使用插件形式,而非独立
docker-compose二进制。
行 77--78:启用服务
bash
sudo systemctl enable --now docker
-
enable:开机自启 -
--now:同时启动服务(等价于start+enable)
行 81--96:配置镜像加速器
bash
cat > /tmp/daemon.json << 'EOF'
...
EOF
sudo mv /tmp/daemon.json /etc/docker/daemon.json
为什么用 /tmp 中转?
-
避免权限问题(
sudo tee也可,但mv更简洁) -
确保写入原子性
daemon.json 配置项:
| 配置 | 作用 |
|---|---|
registry-mirrors |
镜像加速源列表(按顺序尝试) |
log-driver |
日志驱动(默认 json-file) |
log-opts.max-size |
单个日志文件最大 100MB |
log-opts.max-file |
最多保留 3 个日志文件 |
防止日志占满磁盘!
行 99--104:用户组管理(幂等)
bash
if ! groups "$USER" | grep -qw docker; then
sudo usermod -aG docker "$USER"
else
echo "当前用户已在 docker 组中"
fi
关键点:
-
groups $USER:列出用户所属组 -
grep -qw:-
-q:静默模式(不输出) -
-w:全词匹配(避免匹配到adocker这类)
-
-
usermod -aG:-
-a:append(追加,不覆盖原有组) -
-G:指定附加组
-
修改组后需重新登录或
newgrp docker才生效。
行 107--108:验证安装
bash
docker --version
docker run --rm hello-world
-
不再使用
sudo,验证用户组权限是否生效 -
--rm:容器退出后自动删除,不留残留
行 110--113:最终提示
bash
echo "请重新登录终端或运行 'newgrp docker' 以应用用户组变更。"
- 明确告知用户下一步操作,提升体验
五、高级知识点总结
| 类别 | 知识点 |
| Shell 编程 | 函数、数组、条件判断、重定向、Here Document (<< EOF) |
| 系统管理 | systemctl、usermod、groups、文件权限 |
| 包管理 | dnf、rpm -q、仓库配置(.repo 文件) |
| Docker 架构 | 引擎、CLI、containerd、镜像加速、日志驱动 |
| 安全实践 | 非 root 用户操作 Docker、日志限制、清理残留 |
| 兼容性处理 | RHEL 9 的 $releasever 问题、Podman 冲突 |
| 运维思想 | 幂等性、可重复部署、用户友好提示 |
|---|