前几天帮一位老同事处理服务器环境,系统清一色CentOS 7,任务很直接:把Docker装好,把现有服务容器化跑起来。按理说,CentOS 7安装docker命令就那么几条,网上教程一抓一大把,但真上手你会发现,每一步都可能藏着坑——内核版本对不对、yum源配哪个、存储驱动是不是overlay2、镜像拉不拉得下来,这些问题不搞清楚,装完也是一堆隐患。如果你手头正好也有一台CentOS 7服务器要装Docker,或者准备把老服务迁移到容器环境,这篇笔记应该能帮你省不少时间。我会把从零到能跑通容器的完整过程、每一步为什么这样做、以及实际踩过的坑一起讲清楚,照着做基本不会再翻车。
1. 安装前的系统检查与条件准备
很多教程上来就是让你敲yum install docker,这其实是个很不负责任的教法。CentOS 7的生命周期虽然已经进入维护阶段,但存量服务器非常多,版本跨度也很大——从7.2到7.9都有,内核从3.10到3.10的小版本更新,表现完全不一样。Docker官方要求的内核最低版本是3.10,CentOS 7正好卡在这个线上,但你实际的Docker功能表现跟内核补丁级别、SELinux配置、文件系统类型都有关系,不做检查直接装,后面多半要返工。
1.1 查看内核版本与系统版本
登录服务器第一件事,先确认系统底子:
cat /etc/redhat-release uname -r我用过的CentOS 7机器里,最老的是7.2的内核3.10.0-327,新一点的是7.9的内核3.10.0-1160。这里有个很现实的问题:内核3.10对overlay2存储驱动的支持不是所有版本都稳定,尤其是老内核,overlay2在某些场景下会遇到operation not supported之类的报错。如果内核版本太老,建议先做个内核小版本更新:
yum update kernel -y更新完重启机器,让新内核生效。我知道有人不爱给生产环境随便重启,但内核版本直接影响Docker的存储驱动和网络性能,这一步省不得。
1.2 检查SELinux与防火墙状态
SELinux是CentOS系列的老朋友了,Docker官方对SELinux的态度是:支持,但需要正确配置。CentOS 7上SELinux默认是Enforcing状态,如果你不管它,Docker装好后可能遇到诡异的权限问题,比如容器内读写文件报Permission denied,挂载目录时提示failed to mount。
查看当前状态:
getenforce如果你对SELinux策略不熟悉,最稳妥的做法是把它设为Permissive或Disabled,然后重启。我个人在处理容器环境时习惯直接关掉:
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config重启后确认:
setenforce 0注意,setenforce 0只是临时生效,重启后还是以配置文件为准,所以两个都要改。在生产环境要关SELinux的话,记得先跟团队报备,虽然现在很多云服务器的CentOS 7镜像默认就是Disabled,但物理机和老虚拟机往往不是。
防火墙方面,CentOS 7默认用firewalld。Docker在启动时会自己往iptables里写NAT规则,这个行为跟firewalld容易冲突。最常见的问题是:firewalld先启动,docker后启动,容器端口映射可能不正常;反过来docker先启动,firewalld reload规则又会把docker的规则冲掉。我的习惯是服务器如果只跑Docker相关服务,直接停掉firewalld:
systemctl stop firewalld systemctl disable firewalld如果你必须保留防火墙,那需要额外配置firewalld放行容器端口和网络段,复杂度和收益不成正比,家庭自用或测试环境不建议折腾。
1.3 磁盘空间与目录规划
Docker默认把所有数据放在/var/lib/docker,包括镜像层、容器读写层、卷数据。检查一下这个目录所在分区的可用空间:
df -h /var/lib/docker如果这台机器之前装过别的东西,/分区剩余空间不大,而数据盘挂在/data之类的位置,那最好在安装前就把data-root改到数据盘。方法是在后面要说的daemon.json里加一行配置,或者在安装前先做软链接,我的建议是用配置项,管理起来更清晰。等Docker装完再挪数据目录就很折腾了,来回搬运镜像层还容易出错,所以这个检查一定放在最前面。
空间建议按用途粗算:基础镜像一般几百MB,运行一堆容器之后镜像加日志,随随便便就到二三十GB。你至少给/var/lib/docker留出50GB比较宽裕,如果计划跑数据库、日志类应用,100GB才算安心。
2. yum源配置与安装命令详解
CentOS 7上安装Docker的正规途径不是系统自带的yum源——那个源里只有老掉牙的docker包,版本停留在1.13,连Docker官方都早就不维护了。你要装的是Docker CE(Community Edition),也就是现在的Docker Engine,必须从Docker官方仓库或受信任的镜像源下载。
2.1 官方源 vs 镜像源的选择
安装Docker CE需要先把Docker的yum源加到系统里。官方给的是这个:
yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoyum-utils提供了yum-config-manager工具,这是RedHat系管理源配置的瑞士军刀。--add-repo参数会自动下载repo文件并放到/etc/yum.repos.d/下,之后你可以用cat /etc/yum.repos.d/docker-ce.repo查看里面的源URL。
官方源在国内某些网络环境下速度不太理想,遇到过一天都拉不完几十MB的元数据,这种时候可以换成国内公共软件源镜像站提供的docker-ce.repo,把上面命令中的URL换成镜像站地址即可。我个人的经验是:只要能正常访问官方源,优先用官方,版本最新、验证最完整;如果官方源慢到无法忍受,再考虑镜像源——两者安装出来的Docker本质一样,只是下载路径不同,不用纠结什么"正版不正版"。
2.2 安装docker-ce、docker-ce-cli与containerd.io
源配好后,安装命令很直接:
yum install -y docker-ce docker-ce-cli containerd.io这里有个新手容易忽略的点:Docker Engine现在的包拆成了三个组件。docker-ce是核心守护进程和大部分运行时逻辑;docker-ce-cli是你在命令行里敲的docker客户端工具;containerd.io是容器运行时层,负责实际创建和运行容器。三个都得装,缺了哪个命令行都会报错。
CentOS 7的默认yum行为是装最新版,如果你不想追新,想固定一个已知稳定的版本,可以查看可用版本列表:
yum list docker-ce --showduplicates | sort -r输出里会列出20.x和25.x等一堆版本,比如想装25.0.5:
yum install -y docker-ce-25.0.5 docker-ce-cli-25.0.5 containerd.io版本号必须跟join,让docker-ce和cli保持一致,否则可能出现客户端和服务端API版本不匹配的警告。
2.3 依赖冲突排查
CentOS 7上偶尔会碰到依赖冲突的报错,最常见的是containerd.io版本与系统已有的runc或container-selinux包冲突。遇到时先看完整的错误信息,通常提示是某个包需要更高版本的依赖。处理方法一般是:
yum clean all yum makecache然后手动指定安装某个较新的containerd.io版本,例如先查:
yum list containerd.io --showduplicates | sort -r选一个最新版本装。如果还报冲突,检查一下是否装了老的docker.x86_64包:
yum list installed | grep -i docker把老的docker包卸掉再装,这个坑我真实遇到过——某台机器上有遗留的docker1.13,直接和新版docker-ce装在一起,yum直接拒绝开工。
3. 启动Docker服务与基础验证
装完之后别急着敲docker ps,先把服务拉起来。CentOS 7上用systemd管理服务,Docker装好后会自动生成docker.service单元文件。
3.1 启动并设置开机自启
systemctl start docker systemctl enable dockerenable这一步是设置开机自动启动,实测过多次,很多人在这一步会忘,重启服务器后Docker没起来,容器全都停着,还以为是数据丢了。顺手确认一下运行状态:
systemctl status docker状态里看到Active: active (running)就说明起来了。如果启动失败,别慌,先看日志:
journalctl -u docker --no-pager -n 50journalctl查看systemd服务的日志,-n 50只看最后50行,这个命令我用得很多,比dmesg定位快得多。常见的启动失败原因包括:/etc/docker/daemon.json写错了JSON语法、SELinux导致的权限拒绝、iptables规则初始化失败,这些都能在日志里找到线索。
3.2 验证客户端与服务端
服务起来后,第一件事确认客户端和服务端版本:
docker version这个命令会分两段打印,一段是Client,一段是Server。两边都正常显示版本号,说明客户端到守护进程的通信没问题;如果Server段卡住或报错,多半是守护进程异常。再看更详细的环境信息:
docker info重点关注几项:Storage Driver(应该是overlay2)、Cgroup Driver(CentOS 7上是systemd)、Kernel Version(3.10或更高)。这些字段直接决定了后面容器的运行方式。
3.3 用hello-world容器做全链路验证
跑一个最基本的容器验证整体链路:
docker run hello-world这个命令会先从Docker Hub拉取hello-world镜像,然后创建并运行一个最小容器,打印一段说明文字后退出。如果这条命令能顺利输出,说明你已经完成了pull镜像、创建容器、启动容器、退出清理这一整套流程,Docker安装宣告成功。
如果这一步卡住,最常见的原因是网络问题拉不到镜像,这个我们会放到后面镜像加速的部分详细处理。
4. 镜像加速与存储驱动选型
Docker装好、能跑hello-world,只是完成了30%。真正用起来的问题有两个:镜像下载速度,以及长期运行后磁盘管理的稳定性,这两件事需要在安装阶段就做好配置。
4.1 配置镜像加速器
Docker默认从Docker Hub拉取公共镜像,国内网络环境下,直接拉镜像经常遇到超时,或龟速下载几十MB的镜像能等半天。解决办法是给Docker配置镜像加速器,原理是使用一台位于国内、缓存了Docker Hub镜像内容的公共仓库节点,拉取时直接走这个节点。
配置位置在/etc/docker/daemon.json,这个文件是Docker守护进程的核心配置文件,在启动时读取。格式如下:
{ "registry-mirrors": ["https://你的加速器地址"] }加速器地址从哪里来?现在国内主流的云服务商基本都提供公共或专属的镜像加速地址,一般在你使用的云服务商的容器服务文档里能找到。如果你使用的是某些技术服务商的服务器,也可以去它的镜像服务页面申请一个专属加速地址,速度快而且不限制并发。整个过程不涉及修改系统文件,只改daemon.json里的这一个数组字段。
配置完记得重启Docker:
systemctl daemon-reload systemctl restart docker然后验证是否生效:
docker info | grep -A 2 "Registry Mirrors"能看到你配置的地址就说明生效了。我在这件事上踩过的一个坑是:改了daemon.json但忘了重启服务,然后pull镜像依然慢,排查了半天才发现配置根本没加载。
4.2 为什么把存储驱动切成overlay2
CentOS 7上Docker历史上有几个存储驱动可选,早期版本默认用devicemapper,这货在CentOS 7上默认跑的是loopback模式——就是把文件系统塞到一个稀疏文件里,性能和空间利用率都很差,最经典的问题是磁盘空间明明还有,却报no space left on device。后来Docker官方默认推荐overlay2,性能更好,空间管理更直接。
如果你docker info看到Storage Driver不是overlay2,需要检查内核模块。overlay2需要内核支持,CentOS 7默认是没问题的,但个别最小化安装的系统可能缺模块。确认方法:
lsmod | grep overlay没有输出就手动加载:
modprobe overlay然后写入开机自动加载:
echo "overlay" > /etc/modules-load.d/overlay.conf接着在daemon.json里明确指定存储驱动,避免Docker自作主张:
{ "storage-driver": "overlay2", "registry-mirrors": ["https://你的加速器地址"] }再重启Docker,docker info里的Storage Driver就会变成overlay2。
4.3 日志大小和数据目录的前置限制
容器跑久了,最占磁盘的不一定是镜像,而是容器日志。json-file日志驱动默认不限制文件大小,一个容器有大量日志输出时,/var/lib/docker/containers/目录可以膨胀到吓人。我见过一台测试机被一个打印调试日志的容器写满50GB磁盘,当时人都麻了。
比较好的习惯是在daemon.json里直接加日志轮转限制:
{ "storage-driver": "overlay2", "registry-mirrors": ["https://你的加速器地址"], "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" } }这样单个容器日志超过50MB就轮转,最多保留5个文件,总占用上限250MB左右,基本不用担心日志把磁盘打爆。
另外,刚提到的data-root配置如果你需要改位置,也是在同一个文件里加:
{ "data-root": "/data/docker" }这个字段一定要在Docker运行并产生大量数据之前设好,装完用很久之后再改,等于要迁移整个镜像层数据,操作难度不是一个量级。
5. 装完就能用?把常见坑提前说清楚
讲完安装配置,最后说一说那些"装好了却怎么都用不爽"的问题。这些坑不算曹,但在CentOS 7这个组合下特别容易出现,提前知道能省很多春秋。
5.1 镜像拉取超时或失败
表现是docker pull卡在等待,或提示EOF、i/o timeout。原因很直接,就是网络到Docker Hub的链路不稳定。解决思路分三级:
第一级,确认你配置了镜像加速器,并且验证了生效。很多朋友配了但没重启,等于白配。
第二级,如果加速器配置了依然慢,可以试试直接更换一个加速器地址,不同服务商在不同地区的节点速度差异很大。第三级,如果是内网环境无法访问外网,就只能走离线导入的路线——在一台能联网的机器上docker pull并docker save成tar包,再传输到目标机器docker load。这个方案我帮人处理过不止一次,效果很稳定。
5.2 firewalld重启导致容器网络断开
前面说过firewalld和Docker的iptables规则会打架。实际场景是这样的:Docker正常运行,容器端口映射都正常,某天有人执行了systemctl restart firewalld,然后容器网络全断,外部访问不通。原因在于firewalld重启时会刷新iptables规则,顺手把Docker写入的NAT规则清了。
解决办法有两个方向:其一是彻底停用firewalld,前面已经给了命令;其二是每次firewalld重启后,手动重启Docker服务让规则重新加载。后者治标不治本,建议直接选择前者。
5.3 容器内时间与宿主机不匹配
这个坑跟安装本身无关,但属于装好后几乎必然遇到的问题。Docker容器默认使用UTC时区,宿主机如果是北京时间,容器里的时间会差8个小时。日志排查时经常被误导。
解决方式很简单,在daemon.json里挂载宿主机的/etc/localtime,或者在运行时加-v /etc/localtime:/etc/localtime:ro,我更推荐在daemon.json里设置默认挂载:
{ "default-runtime": "runc" }不对,这个挂载方式在daemon.json里没有直接参数,还是用运行时的-v参数或者docker-compose的volumes配置更稳。或者更省事的方式:创建容器时加-e TZ=Asia/Shanghai,很多基础镜像支持直接在环境变量里设置时区,这个做法比挂载文件更干净。
5.4 yum update误升级Docker
CentOS 7环境里,如果你跑了yum update,而docker-ce的repo还开着,yum会顺手把Docker也升级到最新版。这可能导致运行中的容器突然被重启,或者版本不兼容导致第三方面板工具失效。规避办法是使用yum的版本锁定插件:
yum install -y yum-plugin-versionlock yum versionlock docker-ce docker-ce-cli containerd.io这样后续yum update时会自动跳过这三个包。等真要升级Docker时,再yum versionlock delete对应的包名,手动升级。这个习惯我用到现在,再也没有出现过"莫名其妙Docker变了个版本"的情况。
5.5 清理磁盘时别乱删/var/lib/docker
一旦容器多了,/var/lib/docker占空间变大,新手很容易手动删里面的目录来瘦身,这是最危险的操作——镜像层和容器层之间有关联引用,手动删会直接把整个Docker搞坏。正确的清理姿势是:
docker system prune这个命令会清理停止的容器、未使用的网络、悬空镜像和构建缓存,加-a参数还会把未被容器引用的所有镜像一起清掉。执行前仔细看它列出的确认信息,特别是-a,可能会把你想留着的镜像也删了。
我在实际操作中的体会是,Docker在CentOS 7上的安装流程并不复杂,复杂的是装完以后跟系统的相处之道。留好内核余量、关掉跟你抢规则的系统组件、在daemon.json层面做好空间和日志的约束,后面几乎不会出大的幺蛾子。最后再分享一个小技巧:安装完成后,把配置好的daemon.json备份一份放在/etc/docker/daemon.json.bak,遇到升级或者误改配置时,一条命令就能还原。这套流程我已经在好几台CentOS 7机器上验证过,只要前置检查和daemon.json这两块做扎实,剩余的部分真的就是复制粘贴那几条命令的事。