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 冲突 |

运维思想 幂等性、可重复部署、用户友好提示
相关推荐
可爱又迷人的反派角色“yang”2 小时前
elk架构
linux·运维·elk·架构
zfj3212 小时前
深入理解 Linux Namespace:隔离技术的基石
linux·运维·网络
txzz88882 小时前
CentOS-Stream-10 系统安装之SELINUX关闭
linux·运维·centos·selinux
..Move...3 小时前
云原生运维企业级实战项目:CentOS Stream 8 下 Nginx 高可用集群部署
运维·云原生·centos
徐徐图之!3 小时前
四、【阶段一运维基础 之 走进 Liunx:心理铺垫篇】
linux·运维
LingRannn3 小时前
Ubuntu 24.04 安装 Docker Engine
linux·ubuntu·docker
lhyzws3 小时前
CENTOS上的网络安全工具(三十三) Portainer Kafka-Clickhouse部署(2)
linux·运维·centos
凯子坚持 c3 小时前
Docker存储卷深度解析:机制、管理与数据持久化实战
运维·docker·容器
测试人社区-小明3 小时前
医疗AI测试:构建安全可靠的合规体系
运维·人工智能·opencv·数据挖掘·机器人·自动化·github