有句话说在前面:CentOS 7虽然已经进入了生命周期后期,但真实生产环境里跑着的存量机器依然非常多,很多公司到现在还在用CentOS 7.9做服务器系统。如果你也是刚拿到一台CentOS 7的云服务器,或者公司给你分配了一台旧机器让你装Docker,那这篇教程就是给你准备的。
我尽量把每一步都拆开讲清楚,不光是“敲什么命令”,还会告诉你“为什么这么敲”、“不这么敲会踩什么坑”。整个部署过程我实测过很多遍,按着下面的顺序走,基本不会出问题。
1. 部署前准备与环境检查
1.1 先确认系统版本和内核
很多人拿到服务器就直接开始装Docker,装完报错才发现内核版本太低,或者系统本身是CentOS 6.x,这属于白忙活。所以第一步永远是检查环境。
cat /etc/centos-release正常你应该看到类似这样的输出:
CentOS Linux release 7.9.2009 (Core)如果输出的是CentOS 6.x,那下面的教程对你就没意义了,因为CentOS 6的Docker版本停留在很久以前,而且很多依赖包都拉不下来,建议直接重装系统。
接着看内核版本:
uname -rDocker要求CentOS 7的内核版本必须在3.10以上,大多数CentOS 7.9默认内核都在3.10.0-1160左右,这是能满足要求的。如果你看到的是3.10.0-327之类的,也能用,但建议后续有精力时升级一下内核,后面我会在常见问题里专门说这事。
1.2 检查关键模块是否加载
Docker依赖一些内核模块和iptables相关功能,尤其是overlay2存储驱动和bridge网络,这两个是Docker能正常工作的基础。
lsmod | grep overlay lsmod | grep br_netfilter如果上面两条命令没有输出,说明模块没加载,需要手动启用:
modprobe overlay modprobe br_netfilter然后确认net.bridge.bridge-nf-call-iptables参数是开启的,这个参数决定了容器网络能不能正常转发:
sysctl -a | grep bridge-nf-call-iptables如果是0或者没输出,就把它写进系统配置:
cat <<EOF | sudo tee /etc/sysctl.d/docker.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl -p /etc/sysctl.d/docker.conf这一步在云服务器上尤其重要,我遇到过好几台机器没开ip_forward,导致容器能启动但访问不了外网,排查了很久才找到是这个参数的问题。
1.3 yum源和网络连通性检查
安装Docker需要联网,而且yum源要能正常使用。先测一下yum能不能解析:
yum -y update如果你的服务器带宽不大,这个步骤可能要几分钟,属于正常现象。但如果你执行yum update的时候报错,比如“Could not retrieve mirrorlist”之类的,那就说明yum源有问题,建议先把源换成国内镜像源。
这里说一个经验:如果你是在国内服务器上操作,yum源最好直接用阿里云镜像站或者清华源,因为默认的CentOS官方源在国内访问速度很慢,碰到网络波动还会超时。
另外还要检查一下系统时间:
dateDocker和很多HTTPS仓库都依赖证书验证,如果系统时间和实际时间差太多,装完Docker之后拉镜像也会报证书错误,这个问题我从没见系统教程里提过,但实际踩坑概率很高。
注意:时间不准、yum源不通、内核模块没加载,这三个问题任何一个都会在中途打断你,而且报错信息特别容易误导人。与其装到一半再排查,不如一开始就花五分钟全查一遍。
2. 完整安装Docker CE
2.1 清理旧版本与安装依赖包
如果你以前装过老版本的Docker,比如通过yum装过docker或者docker-io,建议先卸载干净,否则会和docker-ce起冲突。
yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine如果你确认机器上从来没装过Docker,这一步可以跳过。
然后安装依赖工具包:
yum install -y yum-utils device-mapper-persistent-data lvm2这三个包的作用分别是:yum-utils提供yum-config-manager命令,用来添加和修改yum仓库;device-mapper-persistent-data和lvm2是Docker的devicemapper存储驱动依赖。虽然现在Docker默认用overlay2,但装这两个包属于官方文档里的标准流程,能保证兼容性。
2.2 配置Docker的yum仓库
这一步有两种做法:一种是指向Docker官方源,一种是指向国内镜像源。我直接把两个方案都给你,自己按网络情况选一个。
官方源(适合海外服务器或者网络好的情况):
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo阿里云镜像源(适合国内服务器,速度明显快很多):
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo走阿里云源的话,添加完仓库之后要顺手把repo文件里的URL替换成阿里云地址,避免它跳回官方源:
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo这里多说一句,不要觉得“反正都是源,随便配一个算了”。我实测过,国内机器用官方源拉docker-ce的时候,经常卡在metadata下载阶段,等很久才出结果,换成阿里云源之后整个安装过程一分钟内就能结束。这种体验差距会直接影响你后面所有操作的效率。
2.3 安装docker-ce及核心组件
仓库配置好之后,执行:
yum 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:官方插件,分别用来增强镜像构建和Docker Compose编排。
如果你用的yum源版本比较老,可能找不到buildx和compose插件,这时候只装前三个也能正常工作,缺的Compose可以按后面第4章的方式单独装。
安装完成后,先别急着启动,先把开机自启配好:
systemctl enable docker systemctl start docker然后看一眼运行状态:
systemctl status docker如果你看到类似这样的输出,说明Docker已经在运行了:
● docker.service - Docker Application Container Engine Loaded: loaded (/etc/systemd/system/docker.service; enabled; vendor preset: disabled) Active: active (running) since ...注意:有不少教程会让你执行service docker start,在CentOS 7上这么做不是不行,但systemd的systemctl才是正统方式。用systemctl的好处是能同时管理开机自启和守护进程状态,排查问题也更方便。
2.4 验证安装结果
输入版本命令确认一下:
docker version正常情况下你看到的Client和Server版本应该是一致的,比如都显示20.10.x或24.0.x。如果Client有输出但Server报错,说明Docker守护进程没起来,需要检查系统日志:
journalctl -u docker -n 50最后用经典的hello-world镜像跑一遍完整验证:
docker run hello-world这一步会从镜像仓库拉取一个测试镜像并运行容器,容器的任务就是打印一句“Hello from Docker!”,看到这句话说明你的整个Docker链路是通的。
有一个细节说一下:如果hello-world拉取特别慢或者直接超时,不是你服务器的问题,就是网络访问Docker Hub不通畅。这时候别硬等,直接跳到第3章配一个镜像加速器,配完再跑hello-world就能秒过。
3. Docker核心配置与镜像加速
3.1 daemon.json配置文件详解
Docker的守护进程配置都在/etc/docker/daemon.json里,这个文件默认是不存在的,需要自己创建。它影响着镜像拉取速度、容器日志大小、数据存储位置等关键行为。
我建议你在装好Docker之后,第一件事就创建这个文件,把下面这些配置写进去:
{ "registry-mirrors": ["https://你的加速器地址"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "data-root": "/var/lib/docker", "ipv6": false }简单解释一下每个配置项的目的:
- registry-mirrors:镜像加速器地址,国内服务器必须要配,否则拉镜像慢到怀疑人生。
- log-driver和log-opts:限制容器日志大小。这是很多人忽略的大坑,Docker默认不限制日志文件大小,一个容器长期不清理,日志能占用几十G甚至上百G磁盘,我见过最离谱的一次是nginx日志把200G的磁盘直接写满了。
- >systemctl daemon-reload systemctl restart docker
然后执行docker info,查看输出里的Registry Mirrors一栏,确认加速器已经生效。
3.2 修改Docker数据目录的完整步骤
如果你的系统盘只有40G,而数据盘有100G以上,那强烈建议把Docker的数据目录挪到数据盘上。具体操作如下:
# 停止Docker服务 systemctl stop docker # 创建目标目录并同步已有数据(如果没有数据就直接跳过同步) mkdir -p /data/docker cp -r /var/lib/docker/* /data/docker/ # 在daemon.json里修改data-root # 把"data-root": "/var/lib/docker"改成"data-root": "/data/docker" # 重启Docker systemctl daemon-reload systemctl start docker如果你已经跑了不少容器,特别是容器里还有数据库这类有状态应用,改data-root之前一定要确认同步完整,最好用rsync而不是cp,因为容器运行时会持续写入文件,cp的数据可能会不一致。最稳妥的办法是先停Docker再做同步,同步完先别删旧目录,等新目录运行几天确认无误后再清理。
3.3 常用命令速查
Docker的命令体系比较庞大,但对于刚部署完环境的阶段,你主要会用到下面这些:
场景 命令 查看运行中的容器 docker ps 查看所有容器 docker ps -a 查看本地镜像 docker images 拉取镜像 docker pull 镜像名:标签 启动容器 docker run -d --name 容器名 镜像名 进入容器交互 docker exec -it 容器名 bash 查看容器日志 docker logs -f 容器名 停止/启动/重启容器 docker stop/start/restart 容器名 删除容器 docker rm -f 容器名 删除镜像 docker rmi 镜像名 查看资源占用 docker stats 这些命令先用熟,再往深处慢慢研究docker network和docker volume,不用一上来就背所有参数,实际用多了自然就记住了。
提示:docker run -d --name 这个组合是我日常用得最频繁的组合。挂载目录、映射端口、设置环境变量这些参数如果记不住,用docker run --help查就行,别去死记参数含义。
4. Docker Compose的部署与使用
4.1 安装Docker Compose
Docker Compose是用来管理多个容器的编排工具,用一条命令同时启动、停止、重建一组相关联的容器。对于刚部署完Docker的人,要想跑nginx、mysql、redis这类常见组合,Compose几乎是必备的。
如果你在前面的安装步骤里用yum装上了docker-compose-plugin,那可以直接验证一下:
docker compose version如果提示没有这个命令,就用独立安装方式:
curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose国内服务器拉GitHub上的资源慢是常态,如果这个curl命令卡住,可以试着从国内镜像站下载Compose,或者用python的pip方式装。不管用哪种方式,装完之后确认版本:
docker-compose --version看到版本号输出就说明装好了。
4.2 用Compose编排一个Nginx+MySQL实例
为了让你直观感受Compose的用法,我写一个最小可用的docker-compose.yml示例。假设我们要在服务器上跑一个Nginx和一个MySQL 8.0:
version: '3.8' services: nginx: image: nginx:1.24-alpine container_name: web-nginx ports: - "80:80" - "443:443" volumes: - ./nginx/html:/usr/share/nginx/html - ./nginx/conf:/etc/nginx/conf.d - ./nginx/logs:/var/log/nginx restart: always mysql: image: mysql:8.0 container_name: web-mysql ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: "YourStrongPassword" MYSQL_DATABASE: "webapp" MYSQL_USER: "webuser" MYSQL_PASSWORD: "WebUserPassword" volumes: - ./mysql/data:/var/lib/mysql restart: always然后在这个文件所在目录执行:
docker-compose up -dCompose会按照配置自动拉取镜像、创建容器、建立网络。观察一下执行过程,你会发现它默认会创建一个以目录名命名的网络,nginx和mysql两个容器都在这个网络内,它们之间可以通过服务名直接互相访问。比如nginx容器内部可以用mysql这个主机名去访问数据库,这就是Compose最核心的价值:容器之间不需要再手写IP地址互连。
4.3 数据持久化必须写在前面
上面示例里的volumes配置是必须的,千万不能省。如果容器删了重建,而数据没有挂载到宿主机,那所有数据都会跟着容器一起消失。
以MySQL为例,官方镜像的默认数据目录在/var/lib/mysql,如果你不挂载宿主机目录,容器一删,数据库就没了。挂载了./mysql/data之后,数据实际存储在宿主机上,容器随便删随便重建,数据都还在。
这里有一个我踩过的坑:MySQL 8.0的官方镜像默认会初始化数据库目录,挂载空目录启动时会自动写入数据,但如果挂载的目录已经存在非空文件,尤其是权限不对的情况,MySQL会初始化失败。解决办法是先把宿主机的目录权限设置好,比如chmod -R 778 ./mysql/data,或者先让容器用默认配置启动一次,再停掉容器把数据拷贝出来。
nginx的日志目录同理,不挂载的话日志只在容器内部,容器一删日志全没。生产环境里日志是排障的第一手资料,丢失成本很高,建议从一开始就养成“所有有状态的数据必须挂载”的习惯。
4.4 Compose常用操作命令
- docker-compose up -d:创建并后台启动所有服务
- docker-compose ps:查看当前项目容器状态
- docker-compose logs -f:跟踪所有服务的日志输出
- docker-compose restart:重启所有服务
- docker-compose down:停止并移除所有容器和网络
- docker-compose down -v:额外删除所有数据卷,注意这会导致数据丢失
关于Compose给一个明确建议:不同项目的服务尽量分开目录部署,每个目录有自己的docker-compose.yml。比如/opt/nginx-web和/opt/mysql-cluster各自独立,不要把所有服务塞在一个Compose文件里,一方面影响独立升级,另一方面某个服务的配置错误会影响整个项目。
5. 常见问题与排查技巧实录
5.1 典型问题速查表
问题现象 根本原因 解决方法 docker run报Cannot connect to the Docker daemon Docker服务未启动或启动失败 systemctl status docker查看日志 拉取镜像超时或报EOF 网络访问Docker Hub不通 配置镜像加速器 容器能创建但访问不了外网 net.ipv4.ip_forward未开启 sysctl -w net.ipv4.ip_forward=1并持久化 容器日志占满磁盘 未配置日志切割策略 配置daemon.json的log-opts并重建容器 启动MySQL容器报Can't open file权限错误 宿主机挂载目录权限不对 chmod 755或778对应数据目录 端口被占用导致容器启动失败 宿主机已有进程占用端口 检查端口占用并调整映射 docker-compose命令找不到 未安装Compose插件或独立安装失败 重新安装并添加执行权限 Docker重启后容器没有自动恢复 容器创建时未配置restart策略 docker update --restart=always 容器名 5.2 内核模块和版本相关的翻车现场
这是比较常见的问题,尤其是老机器或者最小化安装的CentOS 7。如果你启动容器的时候报类似这样的错:
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: ... : open /dev/... no such file or directory或者:
kernel: overlayfs: unsupported and detected problem多半是overlay模块未加载,或者内核版本太老。前面第1.2节已经让你提前加载过模块了,但如果你内核本身就是3.10.0-327这种老版本,overlay2可能不稳定。这种情况下有两个选择:
- 升级内核到3.10.0-1160以上,或直接换到4.x/5.x主线内核。
- 在daemon.json里设置"storage-driver": "vfs",强制换存储驱动。但vfs性能和空间利用率都远不如overlay2,只适合临时过渡,不推荐生产使用。
还有一个藏在底下的坑:如果你用的是云服务商提供的CentOS 7自定义镜像,有些镜像会阉割一些内核模块。遇到这种情况,先试modprobe overlay,如果提示Module not found,那基本可以确认内核模块缺失,只能考虑换内核或者换系统。
5.3 iptables与防火墙的相爱相杀
Docker在创建容器时会默认写入iptables规则,而且它会直接管理nat表里的规则。如果你在宿主机上手动配置了firewalld或者iptables规则,可能会和Docker的规则互相覆盖,表现为:宿主机端口明明开着,但外面就是访问不到容器映射的端口。
排查思路:
- 先确认容器在跑:docker ps
- 确认端口映射:docker port 容器名
- 检查宿主机防火墙:firewall-cmd --list-all
- 检查iptables nat表:iptables -t nat -L -n
CentOS 7上很多安全基线加固的脚本会顺手清掉Docker自己的链路,导致重启后端口映射失效,所以如果系统加固过,重装Docker之后一定要检查iptables的DOCKER链是否存在。
我处理过的一台机器就是这样,内网其他机器访问不了Docker端口,排查到最后一查iptables发现DOCKER链整个不见了,重建Docker网络才恢复。
5.4 日志清理的正确姿势
如果你已经跑了几周容器,但之前没配日志切割,可能会出现磁盘突然告警的情况。这时候可以先查看哪个容器占的日志最多:
du -sh /var/lib/docker/containers/*/找到大文件之后,不要直接rm那个json.log文件,因为这可能导致Docker读写句柄异常。正确做法是:
cat /dev/null > /var/lib/docker/containers/容器ID/容器ID-json.log用cat清空而非rm,保留文件句柄,Docker才不会报错。别问我怎么知道的,我吃过这个亏,直接rm了log文件之后容器还活着,但日志输出断了,最后只能重启容器恢复。
5.5 Docker命令突然找不到容器
还有一类问题属于Docker数据目录出问题导致的异常。比如你执行docker ps -a,发现之前的容器不见了,但磁盘上还有数据。这多半是data-root配置被改过,或者镜像仓库ID不匹配。
第一种情况:如果你在daemon.json里改过data-root,但启动Docker时旧容器数据还在旧目录,新目录是空的,自然看不到容器。解决办法就是确认data-root路径并做数据同步。
第二种情况:docker-ce升级后有时会调整存储结构,旧版容器数据挂在/var/lib/docker/containers,新版还在用这个路径,但如果你中间用过其他容器运行时比如containerd,目录结构可能会乱。这种情况建议先备份数据,再考虑用docker pull重新拉镜像或者直接重建容器。
6. 部署完成后的第一件正事
装好Docker只是万里长征第一步。我每次给新服务器装完Docker,会立刻做三件事:
第一,创建一个普通用户并加入docker组,避免所有操作都用root完成。虽然root方便,但万一误操作删了容器目录连后悔的机会都没有。普通用户加上docker组权限,日常运维已经绰绰有余。
useradd -m deploy usermod -aG docker deploy第二,配置好daemon.json的日志切割和镜像加速,这个前面说过,不再重复,但确实值得再强调一遍:这个文件是你Docker环境稳定性的基石,优先级最高。
第三,写一个简单的服务器信息记录文档,把Docker版本、数据目录、挂载磁盘、容器端口、镜像加速器地址都记下来。以后每次排障都能省去一大半时间。
根据我个人经验,CentOS 7上部署Docker这件事本身难度不大,真正难的是部署完之后的日常维护和故障排查。这篇教程里提到的那些配置,尤其是日志切割、数据持久化、镜像加速,建议你在第一次部署时就全部配好,别等出问题再补救。毕竟容器的优势在于快速交付和一致性,环境基础打好了,后面跑任何应用都会顺手很多。