shell脚本之Docker安装

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 可能解析为 stream8stream9

  • 但阿里云镜像只认 89,所以强制替换为 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) |
| 系统管理 | systemctlusermodgroups、文件权限 |
| 包管理 | dnfrpm -q、仓库配置(.repo 文件) |
| Docker 架构 | 引擎、CLI、containerd、镜像加速、日志驱动 |
| 安全实践 | 非 root 用户操作 Docker、日志限制、清理残留 |
| 兼容性处理 | RHEL 9 的 $releasever 问题、Podman 冲突 |

运维思想 幂等性、可重复部署、用户友好提示
相关推荐
Leinwin1 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
2401_865382502 小时前
信息化项目运维与运营的区别
运维·运营·信息化项目·政务信息化
漠北的哈士奇2 小时前
VMware Workstation导入ova文件时出现闪退但是没有报错信息
运维·vmware·虚拟机·闪退·ova
如意.7592 小时前
【Linux开发工具实战】Git、GDB与CGDB从入门到精通
linux·运维·git
运维小欣2 小时前
智能体选型实战指南
运维·人工智能
yy55272 小时前
Nginx 性能优化与监控
运维·nginx·性能优化
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 小时前
Linux 查询某进程文件所在路径 命令
linux·运维·服务器
05大叔5 小时前
网络基础知识 域名,JSON格式,AI基础
运维·服务器·网络
安当加密5 小时前
无需改 PAM!轻量级 RADIUS + ASP身份认证系统 实现 Linux 登录双因子认证
linux·运维·服务器
dashizhi20155 小时前
服务器共享禁止保存到本地磁盘、共享文件禁止另存为本地磁盘、移动硬盘等
运维·网络·stm32·安全·电脑