
1. 写这篇的来龙去脉为什么还在折腾 CentOS 7 装 Docker前阵子帮朋友收拾一批跑了好几年的老服务器清一色 CentOS 7上面跑着 PHP、MySQL、Redis还有一堆手工部署的脚本服务。说实话刚接手时我是有点抗拒的CentOS 7 官方维护已经进入尾声内核还停留在 3.10怎么看都跟 Docker 这种“新东西”不太搭。但真把环境梳理完之后我意识到现实里还活跃着大量 CentOS 7 机器它们承担着生产任务不能随便重装系统只能用 Docker 在存量环境里做统一部署、隔离和迁移。这篇教程就是给同样处境的人写的。无论你是刚接手旧服务器还是想在虚拟机里搭一套和线上一致的 Docker 环境又或者纯粹想学一下 CentOS 7 上 Docker 的安装细节这篇内容都能直接用。我会把从零到能跑容器、再到初始化调优的完整过程拆开讲每一步该敲什么命令、为什么要敲这个命令、踩过的坑长什么样全都会写出来。需要先说清楚一个观点CentOS 7 上装 Docker 并不推荐无脑装最新版。内核老、glibc 老、yum 源也老任何一个环节没对齐都会在后续使用中冒出一堆奇怪问题。所以这篇文章的主线是“在不准重装系统的前提下把 Docker 稳定装好、调好、用好”顺带把兼容性选择、初始化配置、常见故障处理这些真正花时间的点讲透。2. 动手之前先把环境摸清楚省得后面反复返工2.1 确认系统版本和内核版本很多人在 CentOS 7 上装 Docker 失败第一步就输在了没确认环境。请先登录服务器把下面几行命令挨个跑一遍cat /etc/redhat-release uname -r uname -m第一行看发行版版本第二行看内核版本第三行看 CPU 架构。CentOS 7 的标准内核是 3.10.0-xxx比如我手头一台老机器显示的是3.10.0-1160.el7.x86_64。这个内核版本能用 Docker但有一个隐藏的前提你需要给 Docker 选一个和 3.10 内核相处融洽的版本具体怎么选我放到后面兼容性小节细说。另外提醒一下如果你是在 VMware 或 VirtualBox 虚拟机里装的 CentOS 7安装前一定把 CPU 虚拟化VT-x/AMD-V打开否则 Docker 跑一些需要嵌套虚拟化的场景时会比较尴尬。装完系统后建议顺手执行ls /dev/ | grep kvm看一眼有没有 kvm 设备没有也不影响普通容器的运行但提前知道环境状况总比后面排查时一头雾水强。2.2 确认网络和 yum 源状态Docker 安装本质上是从 yum 仓库拉包所以网络和仓库配置直接决定安装顺利与否。安装前先检查一下能不能访问外网curl -I https://download.docker.com如果卡住或者超时说明网络到 Docker 官方源不通需要换国内镜像仓库。CentOS 7 本身的 base/extras 源也经常因为官方源调整出现各种 404好在阿里云提供了完整的 CentOS 7 镜像源替换方法不复杂就是把/etc/yum.repos.d/CentOS-Base.repo里的源地址换成阿里云的地址然后清理缓存重新生成。这一步看起来很基础但真的建议认真做。我见过太多人装到一半报Could not resolve host或者 404 错误然后一脸懵。先把yum repolist跑通、能看到仓库列表再继续往下走整个安装会顺畅很多。2.3 CentOS 7 与 Docker 版本之间的兼容性选择题这是整篇教程里我认为最值得讲清楚的一点。CentOS 7 的内核是 3.10它太老了以至于 Docker 官方后来在做新版本时对老内核的支持越来越“佛系”。我自己实测下来不同 Docker 版本在 CentOS 7 上的表现差异很大Docker 版本CentOS 7 上的表现我的建议19.03.x稳定老机器经典搭配除非特殊兼容需求否则不推荐用太老的版本20.10.x很稳功能也够用CentOS 7 存量环境首选23.0.x整体可用但需要关注 iptables 兼容可以选建议配好内核参数24.0.x 及以上在老内核上偶发 nf_tables/iptables 冲突不推荐除非你已经升级过内核这里很多人会有疑问为什么不能用最新版我举个实际例子Docker 24.x 在 CentOS 7 默认 3.10 内核上容易出现容器网络规则写入失败的问题表现为容器启动后没有网络、端口映射不生效日志里能看到和 iptables/nf_tables 相关的报错。虽然可以通过把 iptables 切到 legacy 模式缓解但对存量机器来说引入这种额外的复杂度没必要。如果你确实需要新特性更合理的做法是先升级内核到 5.x再上新的 Docker 版本。所以最终我的选择是纯 CentOS 7 环境优先安装 Docker 20.10.x如果仓库里默认是最新版我通常会手动指定版本。后面会给出具体的指定版本安装命令。3. CentOS 7 安装 Docker 完整实操步骤3.1 第一步清理旧版残留很多服务器不是从零开始之前可能用 yum 装过老版本 docker、docker-common、docker-selinux 之类的包也有可能用过其他方式安装过实验版本。如果不清理干净后面装 docker-ce 经常会出现包冲突报错信息又臭又长。先执行清理yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine docker-engine-selinux docker-selinux如果提示没有这些包说明是干净环境跳过就行。这里要特别提醒如果之前已经用 Docker 跑过业务千万别顺手删/var/lib/docker那是镜像、容器、卷和数据所在的地方。第一条命令只是卸载程序本身数据目录独立存在。清理残留的意思是清理程序包不是清理数据。确认数据不需要了才建议手动删除rm -rf /var/lib/docker3.2 第二步安装必要依赖工具接下来安装几个工具包分别是 yum-utils、device-mapper-persistent-data 和 lvm2yum install -y yum-utils device-mapper-persistent-data lvm2逐个解释一下这些包是干嘛的。yum-utils 提供yum-config-manager命令这是后面添加 Docker 仓库的关键工具没有它就得手动去写 repo 文件。device-mapper-persistent-data 是给 Docker 的存储驱动做底层支持的虽然现在普遍用 overlay2但系统层面有这个库能避免一些兼容问题。lvm2 是逻辑卷管理工具Docker 在部分场景下会用到它来管理存储池。这一步基本不会出问题唯一需要注意的是确认这几个包能正常拉取并安装如果网络不通先解决上一节说的 yum 源问题再继续。3.3 第三步添加 Docker 官方仓库添加仓库我推荐用 yum-config-manager 一行搞定yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo这个命令会从 Docker 官方站点下载仓库配置文件并存放到/etc/yum.repos.d/docker-ce.repo。如果你在服务器上访问 download.docker.com 很慢或者超时可以换成国内镜像仓库地址比如阿里云提供的 docker-ce 仓库镜像yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo这里有个细节换镜像源跑完命令后最好检查一下仓库文件里的 baseurl 是否真的指向了镜像地址有些镜像站需要你再手动改一下。执行完用cat /etc/yum.repos.d/docker-ce.repo确认再运行yum repolist看看 docker-ce-stable 是否出现。3.4 第四步安装 docker-ce仓库配好后先看仓库里有哪些可用的 Docker 版本避免直接装到不兼容的最新版yum list docker-ce --showduplicates | sort -r输出结果会展示所有可用版本比如docker-ce-3:20.10.23-3.el7.x86_64。如果决定装 20.10.23就执行yum install -y docker-ce-20.10.23 docker-ce-cli-20.10.23 containerd.io这里我特意把 docker-ce-cli 的版本也一并指定了因为 Docker 的客户端和服务端最好保持同版本混用不同版本在部分情况下会出现 API 版本不匹配的怪异问题。containerd.io 是容器运行时依赖docker-ce 包本身会自动把它作为依赖拉取但如果你发现仓库默认的 containerd 版本和 docker 不太匹配手动指定也是可以的。如果你不想纠结版本直接yum install -y docker-ce docker-ce-cli containerd.io装最新然后祈祷内核配合。但我个人的建议还是上面这种方法花一分钟挑版本后面省一小时排查。安装过程会自动拉取一批依赖包网速正常的情况下几分钟搞定。装完先别急着跑服务先验证一下安装结果docker version这个时候很可能提示Cannot connect to the Docker daemon这是正常的因为服务还没启动只要能看到 Client 版本信息就说明安装成功。3.5 第五步启动 Docker 服务并设置开机自启启动服务并设置开机自启systemctl start docker systemctl enable docker然后查看服务状态systemctl status docker只要看到Active: active (running)基本就成功了。注意同一个命令在不同状态下显示的信息差异很大如果启动失败状态会变成failed这种时候不要慌一般是内核兼容或者存储驱动的问题详细排查方法我在后面专门开一节讲。最后用最小的镜像验证一下通路docker run hello-world这个命令会从 Docker Hub 拉取一个测试镜像并短暂运行一个容器执行成功后终端会打印一段说明文字。如果卡在拉镜像这一步说明网络不通或者镜像加速没配先跳到 4.2 节把加速配好再回来跑。4. 装好只是开始初始化配置直接决定后期体验4.1 daemon.json 里的基础配置项很多教程装完 Docker 就跑路了然后用了几天发现问题一大堆日志文件把磁盘塞满、容器数据目录挤在系统盘、cgroup 驱动和 Kubernetes 对不上。这些问题其实都可以在安装后的一小时里提前规避。Docker 守护进程的配置集中在/etc/docker/daemon.json没有这个文件就手动创建。我常用的初始配置长这样{ exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, storage-driver: overlay2, data-root: /data/docker }逐项说exec-opts里的native.cgroupdriversystemd让 Docker 使用 systemd 作为 cgroup 驱动这能保证容器和 systemd 的资源管理一致。尤其在跑 Kubernetes 或 Kubernetes 相关工具链时kubelet 默认要求 Docker 的 cgroup driver 是 systemd如果不统一Node 组件会报错。log-driver和log-opts限制单个容器日志文件最大 100MB最多保留 3 个文件。这是防止日志爆盘的经典手段我见过太多服务器磁盘被几十 GB 的容器日志填满加了限制之后世界清净了。storage-driver选overlay2这是目前 CentOS 7 上性能和稳定性的最优解。要注意 overlay2 对文件系统有要求xfs 必须是 d_type 支持开启的后面排查章节会有详细说明。>systemctl daemon-reload systemctl restart docker执行docker info可以确认配置是否生效里面能看到 Storage Driver、Cgroup Driver、Docker Root Dir 等字段。4.2 镜像加速器的配置方法与注意点CentOS 7 服务器拉取官方镜像时默认走 Docker Hub绕了大半个地球速度随缘。我在国内云服务器上实测有时候一个几百 MB 的基础镜像能拉半小时纯属浪费时间。解决方案是配置 registry mirror。在daemon.json里加一段{ registry-mirrors: [https://你的加速器地址] }加速器地址从哪来最简单的是用各大云厂商容器镜像服务提供的专属加速地址在控制台里能找到通常是https://xxx.mirror.xxx.com这样的格式。填进数组后重启 Docker再执行docker info在输出里能看到 Registry Mirrors 一栏已经列出来地址说明生效了。这里有个操作教训配完加速器后尽量跑一次真实镜像拉取测试比如拉一个 nginx:alpine别只看配置觉得“应该生效了”。实际测试遇到的情况可能比你想象的更丰富比如某些加速地址只支持 Docker Hub 镜像拉取自建仓库或者其他第三方仓库的镜像还是慢这是正常现象不表示加速器配置有问题。4.3 为什么存储驱动选 overlay2 而不是 devicemapper这个点我在实践里被问过好多次。CentOS 7 早期 Docker 版本默认存储驱动是 devicemapper配置起来麻烦且性能一般特别是 loop-lvm 模式下性能损耗明显还容易遇到空间利用率的问题。overlay2 是 overlayfs 的改进版对镜像分层、容器写时复制的支持更高效启动和运行性能都更好。CentOS 7 的内核 3.10 已经支持 overlay2只要文件系统满足条件就能用。但注意如果你用了 xfs 文件系统挂载时必须带ftype1参数否则 overlay2 会报d_type not supported。检查方法xfs_info /var/lib/docker 的挂载点 | grep ftype输出ftype1就说明支持ftype0则需要重新格式化磁盘无法通过参数调整。如果你是在虚拟机上用默认的 ext4基本不用操心这个问题。5. 常用命令速查让你尽快把 Docker 用起来5.1 镜像管理最常用的几条命令安装配置都结束后进入日常使用阶段。镜像相关的命令是使用频率最高的这里列几条我实际操作中最常用的docker search 镜像名 # 搜索镜像 docker pull 镜像名:标签 # 拉取镜像 docker images # 查看本地镜像列表 docker image rm 镜像ID或名字 # 删除镜像 docker image prune # 清理无用悬空镜像有一个实用的习惯拉取镜像时尽量指定标签不要默认 latest。latest 是个移动标签今天拉的和明天拉的可能不是一个版本生产环境里这是大忌。我习惯把版本号固定写死比如docker pull mysql:8.0.36这样环境里跑的是什么版本一目了然升级也完全可控。5.2 容器启动的几个关键参数启动容器时参数选择直接决定业务的可用性。我把最高频用到的几个参数列出来docker run -d \ --name 容器名 \ -p 宿主机端口:容器端口 \ -v 宿主机目录:容器内目录 \ -e 环境变量值 \ --restartalways \ 镜像名:标签逐项解释-d后台运行容器。--name给容器起名字后续管理、查看日志都靠它。-p端口映射。比如-p 3306:3306表示宿主机 3306 端口转发到容器内 3306 端口。-v数据卷挂载。容器本身是无状态的重启后数据可能丢失所以数据库这类服务的数据目录必须挂载到宿主机。-e设置容器内环境变量MySQL 的 root 密码、时区设置等都会用到。--restartalways容器退出后自动重启。服务器重启后 Docker 会自动拉起这个容器对生产环境几乎是必选项。有一个坑必须提醒--restartalways和手动docker stop并不冲突手动停止的容器不会因为 restart 策略立刻被拉起来。但如果容器是因为异常崩溃退出的它会不断重启。排查问题时先用docker logs看日志别让一个不断崩溃的容器反复刷屏。5.3 实操示例一条命令跑起 MySQL 8.0不空谈命令来点实际的。CentOS 7 上想用 Docker 跑 MySQL 8.0并且持久化数据我的启动命令长这样mkdir -p /data/mysql/conf /data/mysql/data docker run -d \ --name mysql8 \ -p 3306:3306 \ -v /data/mysql/conf:/etc/mysql/conf.d \ -v /data/mysql/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDYourPassword \ --restartalways \ mysql:8.0.36这里把配置目录和数据目录全部挂载到宿主机容器再怎么折腾数据不会丢。然后登录验证docker exec -it mysql8 mysql -uroot -pdocker exec是进入正在运行的容器执行命令的常用手段-it表示交互式终端。如果这条命令能进入 MySQL 命令行说明容器运行正常。之后再用docker logs mysql8看启动日志确认没报错基本就齐活了。这个例子虽然简单但它把端口映射、数据卷、环境变量、自启动策略这几个核心概念串在了一起算是 Docker 日常使用的最小完整模式。后面要跑 Redis、PostgreSQL、GitLab 等应用套路完全一样只是镜像名和环境变量不同。6. 踩坑实录与排查思路这些才是真正省时间的部分6.1 安装时报错Could not resolve host 或 yum 源 404这是新手最容易碰到的错误之一。执行 yum 安装时报Could not resolve host: download.docker.com说明服务器无法解析或访问 Docker 官方仓库域名。这时候有人会反复重试其实更高效的做法是直接换源。我建议的操作流程先确认网络通不通ping baidu.com看基础网络能通就换掉 docker-ce 的仓库地址用阿里云镜像源重来如果连 yum 基础源也很慢就把 CentOS Base 源也一并换成阿里云的。换完之后一定执行yum clean all yum makecache否则可能出现缓存没更新导致 404 的情况。这个坑的根源就是网络链路问题和 Docker 本身无关。排查方向对了十分钟能解决。6.2 启动失败Failed to start Docker Application Container Engine安装完成后systemctl start docker报 failed跟着journalctl -u docker查看日志是最直接的定位方式。常见原因有几种存储驱动初始化失败日志里提到devicemapper或overlay2相关错误需要检查文件系统支持情况。已有旧配置文件残留/etc/docker/daemon.json里写入了不支持的参数导致守护进程启动时解析失败。端口被占用Docker 默认监听本地 socket 和 iptables但某些安全软件会抢占相关资源。遇到启动失败不要一条命令反复试先看日志再动手。日志输出的错误信息通常已经指明了方向顺着排查比瞎猜效率高得多。6.3 容器启动后网络不通、端口映射失效这是我踩过最深的坑。容器能起来但从外部访问不到映射的端口或者在容器内部ping外网不通。排查思路docker ps docker port 容器名 iptables -L -n | grep 容器端口如果发现宿主机 iptables 的规则里有 Docker 相关链路被截断大概率是 CentOS 7 自带的 firewalld 和 Docker 的 iptables 规则相互干扰。经典解决方式有两种一是关掉 firewalld改成使用 iptables 服务二是保留 firewalld但确认 Docker 的网桥流量能正常通过。另外新版 Docker 在较老内核上可能和 nf_tables 有兼容问题日志里如果出现nf_tables字样可以尝试将系统默认的 iptables 切回 legacy 模式update-alternatives --set iptables /usr/sbin/iptables-legacy update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy systemctl restart docker这招在我一台老机器上成功救回过网络建议遇到兼容问题时第一个试。6.4 磁盘爆满迁移 /var/lib/docker 的正确姿势Docker 用久了/var/lib/docker目录会越来越大镜像、容器层、日志全堆积在系统盘。如果系统盘快满最快的解决方式是把这个目录整体迁移到数据盘。步骤不复杂systemctl stop docker rsync -avz /var/lib/docker/ /data/docker/然后修改daemon.json里的>systemctl stop docker yum remove docker-ce docker-ce-cli containerd.io rm -rf /var/lib/docker rm -rf /etc/docker其中前两条是常规操作第三条删除数据目录要万分谨慎。如果确认所有容器和数据都不需要了则可以删如果只是重装软件、数据还要保留就不要执行第三条。第四条删除配置文件重装时能确保从干净状态开始。另外有些版本的 Docker 会留下 systemd 服务软链接残留重装前可以检查一下ls -l /etc/systemd/system/multi-user.target.wants/docker.service如果存在且指向旧路径卸载前先删掉避免重装后 systemd 报错。7.2 卸载前的数据备份思路如果你不是因为“毁尸灭迹”而卸载 Docker而是因为要迁移环境那备份思路就很重要了。先说结论Docker 容器的持久化数据应该全部放在挂载的数据卷里备份只需要备份宿主机上对应的目录而不是用docker commit去打包整个容器。我见过不少人用docker commit做备份这会生成一个包含容器运行状态的新镜像看似方便实际上既臃肿又不干净容器里的临时文件、缓存全被打包进去。正确的做法是数据库类用 mysqldump 或 pg_dump 导出数据文件然后在宿主机上备份对应的数据目录。配置文件直接备份挂载出来的 conf 目录。镜像本身如果需要迁移到另一台机器用docker save导出镜像 tar 包到新机器docker load导入。docker save -o mysql8.tar mysql:8.0.36 docker load -i mysql8.tar这套流程我走了很多次稳定可靠比 commit 干净得多。8. 最后再分享一点我的个人体会这套教程里的命令和配置是我在十几台 CentOS 7 服务器上反复验证过的。写了这么多我最想强调的还是那句话CentOS 7 上装 Docker关键不在安装命令本身而在“兼容性选择”和“初始化配置”。版本没选对后面各种网络异常、内核兼容告警会接踵而至初始化配置没做好日志爆盘、数据迁移、cgroup 驱动不统一这些问题会在你忙得不可开交的时候突然爆发。我个人在实际操作中最受益的一个习惯是每次装完 Docker 后不急着跑业务容器而是花十分钟把 daemon.json 按自己那套标准配好再用 hello-world 和 nginx 各验证一遍。这十分钟看着是“浪费”实际上帮我省下了后面无数个排查的深夜。你也可以把这套教程当成一份自己的检查清单装的时候照着敲装完对照着配置遇到问题再翻回来看排查节基本上能覆盖 90% 以上的常见场景。