1. 安装前准备工作:环境检查与依赖梳理
1.1 先确认内核版本和系统版本
CentOS 7.9到今天仍然在大量服务器上跑着,虽然官方已经停止维护,但存量设备实在太多,很多机房、内部系统、离线环境还在用。在这些机器上装Docker,第一件事不是急着敲命令,而是先看清楚系统底子。
先看系统版本:
cat /etc/redhat-release正常会输出CentOS Linux release 7.9.2009 (Core)。接着看内核:
uname -rDocker官方对CentOS 7的最低内核要求是3.10,7.9默认内核一般在3.10.0-1160.el7.x86_64左右,满足要求。但这里有个非常关键的经验:内核版本低,直接影响Docker的存储驱动和网络性能。如果你手里的机器内核还停在3.10.0-514这种老版本,我建议先执行一次系统更新,至少把内核升到1160系列,不然跑高负载容器时容易出现io卡顿、容器频繁重启的问题。
内核升级命令:
yum update -y kernel更新完后需要重启系统,重启后确认内核版本已经变化。实测中很多人在这一步偷懒跳过,结果后面跑容器时遇到kernel:unable to handle kernel paging request之类的错误,排查一圈才发现是内核太旧。这个坑我踩过一次,后来只要有新机器装Docker,内核没到1160我绝对不动手。
1.2 清理旧版本Docker与依赖
有些机器可能之前装过docker或者docker-engine这类旧包,如果不清理干净,跟docker-ce装在一起会产生冲突。先查一下:
yum list installed | grep docker如果有输出,逐个卸载:
yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine注意,这条命令会把老版本的docker、docker-engine都清理掉,但不会删除/var/lib/docker目录下的数据。如果你之前的容器数据还要用,先备份这个目录;如果确定不要了,可以顺手删掉,避免新旧数据混在一起造成诡异现象。
另外还要检查containerd和runc。Docker新版本依赖独立的containerd,旧版本可能自带一个runc,冲突时会出现runc: symbol lookup error这类问题。保守做法是装新版docker-ce时让yum自己解析依赖,不要手动装指定版本的runc。很多时候Docker启动失败,不是Docker本身的问题,而是系统里残留的老库在捣乱。
1.3 网络与yum源准备
CentOS 7.9停止维护后,默认的mirrorlist失效,直接用yum装东西经常报Could not resolve host: mirrorlist.centos.org。装Docker之前,先把基础yum源切到可用状态。
我常用的做法是换成阿里云的vault源:
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo sed -i 's/mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/CentOS-Base.repo sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://mirrors.aliyun.com|g' /etc/yum.repos.d/CentOS-Base.repo然后清理缓存:
yum clean all && yum makecache这里顺便提一句,如果你的机器处于内网环境,连外网受限,那更需要在本地准备好docker-ce的rpm包或者内网yum源,离线安装的思路我在后面的章节单独说。网络这东西,看起来是个废话问题,但实际部署时因为yum源失效而卡住的案例,我见过太多了。
2. 正式安装:从yum源配置到命令上手
2.1 配置docker-ce的yum源
Docker官方仓库在海外,直接配置后下载速度慢到怀疑人生。国内建议用阿里云或清华的docker-ce镜像源。我一般用阿里云:
yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo装完yum-utils后,yum-config-manager这个命令才会存在。注意,yum-config-manager --add-repo会自动写入repo文件,但写入的地址可能还是官方源,需要再确认一下:
cat /etc/yum.repos.d/docker-ce.repo看到baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable这段才是对的。如果不是,手动把download.docker.com替换成mirrors.aliyun.com。
这一步能省很多时间,默认官方源我在国内服务器上实测下载速度经常不到10KB/s,换阿里云后基本能跑到满速。如果你是海外服务器,那直接用官方repo就行,不用折腾。
2.2 安装docker-ce与关键组件
执行安装:
yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin这里说下这几个包各自的职责:
docker-ce:Docker服务端核心,也就是daemon进程docker-ce-cli:docker命令行工具,负责跟daemon通信containerd.io:容器运行时管理组件,新版Docker通过containerd来管理容器生命周期docker-compose-plugin:Compose V2插件,支持docker compose命令,用来编排多容器
如果你只是随手想装个Docker玩玩,只装前三个也够用。但如果你打算后面跑MySQL+Redis+Nginx这种组合,compose插件强烈建议一起装上,用起来太舒服了。
安装完查看版本:
docker --version老一点的版本输出类似Docker version 24.0.7,新一点的可能是26.x甚至更高。Docker小版本迭代很快,只要能用就行,不用追求最新。最新版有时候会对内核有额外要求,CentOS 7.9这种老系统反而适合装稍旧一点的稳定版。
2.3 启动服务与验证安装结果
安装完后,Docker服务默认是停止状态,需要手动启动:
systemctl start docker systemctl enable dockerenable的作用是设置开机自启,服务器上一定要做这一步,不然机器重启后Docker没起来,你的容器全挂了,排查起来很痛苦。
验证Docker是否健康,看两条信息:
systemctl status docker状态显示active (running)说明服务正常。再执行:
docker info这个命令会输出Docker的详细环境信息,包括宿主机内核、存储驱动、镜像数量等。如果这里报Cannot connect to the Docker daemon,说明daemon没起来,优先看服务日志:
journalctl -u docker --no-pager | tail -50日志里最常见的信息是iptables相关错误,后面我在问题排查章节细说。
3. 镜像加速与常用镜像实战
3.1 配置镜像加速器
Docker Hub的镜像拉取在多数网络环境下都不稳定,超时断流是家常便饭。解决思路是配置镜像加速器,或者叫registry mirror。
国内主流云厂商都提供Docker镜像加速服务,从你的云厂商控制台能拿到专属地址。以阿里云为例,在控制台搜“容器镜像服务”,找到镜像加速器页面,会给你一个https://xxxx.mirror.aliyuncs.com的地址。
配置方式:
mkdir -p /etc/docker cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://你的专属加速地址.mirror.aliyuncs.com"] } EOF配置完重启Docker:
systemctl daemon-reload systemctl restart docker验证加速器是否生效:
docker info | grep -A 1 "Registry Mirrors"看到你的加速地址列表就说明配置成功。
这里多说一句,如果你是在纯内网环境部署,daemon.json里还需要配置insecure-registries来允许访问内网仓库,否则拉取内网镜像时会报证书错误。经验之谈,内网环境第一件事往往不是装Docker,而是确认私有仓库地址怎么访问,不然装好了也是个摆设。
3.2 拉取CentOS、MySQL、Redis、Nginx等常用镜像
加速器配好后,拉镜像就顺畅多了。下面是我常用的几个镜像及其典型用途。
先来个小测试,拉一个最基础的镜像:
docker pull centos:7.9.2009虽然CentOS 7.9已经停止维护,但很多老业务系统还在用它当基础镜像,这个镜像拉下来做个基础环境还是很方便的。
再看MySQL,目前生产环境用得最多的版本是8.0和5.7:
docker pull mysql:8.0 docker pull mysql:5.7Redis在缓存场景和消息队列场景都有应用:
docker pull redis:7Nginx用做前端静态资源服务和反向代理:
docker pull nginx:1.24如果你要做Java应用部署,openjdk镜像也常备:
docker pull openjdk:8u342拉镜像时可以用docker images查看本地已有的镜像列表,用docker rmi 镜像名删除不需要的镜像。这块没什么难度,真正的坑都在后面启动容器的时候。
3.3 容器启动参数解读:端口映射、数据卷、时区
这里我以MySQL和Redis为重点讲,因为这两个容器启动参数最繁琐,也最容易出错。
先看MySQL:
docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=YourPassword123 \ -e TZ=Asia/Shanghai \ -v /data/mysql:/var/lib/mysql \ -v /etc/localtime:/etc/localtime:ro \ --restart=always \ mysql:8.0逐个解释参数含义:
-d:后台运行,容器不会占据当前终端--name:给容器起名字,后续管理靠这个名字-p 3306:3306:宿主机3306端口映射到容器3306端口。宿主机端口可以改,比如-p 33061:3306,容器内MySQL始终监听3306-e MYSQL_ROOT_PASSWORD:指定root密码,这是MySQL镜像特有的初始化环境变量-e TZ=Asia/Shanghai:设置容器时区,不设置的话容器内默认是UTC时间,日志时间会慢8小时-v /data/mysql:/var/lib/mysql:数据卷挂载,容器内MySQL数据实际存在宿主机/data/mysql目录下。这个参数至关重要,没有它,容器一删数据全没-v /etc/localtime:/etc/localtime:ro:把宿主机的时区文件映射进容器,保证容器内时间和宿主机一致--restart=always:Docker服务重启后容器自动启动
为什么数据卷这么重要?因为容器本身是无状态的,docker rm一执行,容器里的所有文件都会被清掉。如果你没挂数据卷,MySQL数据就跟着容器一起消失了,这个教训可以说是Docker新手必须付的学费。
再看Redis,比MySQL简单一些,但有个小坑:
docker run -d \ --name redis7 \ -p 6379:6379 \ -v /data/redis:/data \ --restart=always \ redis:7 --requirepass YourRedisPassword注意最后的--requirepass YourRedisPassword,这个看似是给redis-server传参,实际是镜像的默认启动命令(redis-server)接收的追加参数。容器启动时,镜像默认执行redis-server,后面跟的--requirepass会被拼接进去,从而设置访问密码。
如果要挂载自定义的redis.conf配置文件,可以这样:
docker run -d \ --name redis7 \ -p 6379:6379 \ -v /data/redis/redis.conf:/etc/redis/redis.conf \ -v /data/redis:/data \ redis:7 redis-server /etc/redis/redis.conf注意挂载配置文件时,宿主机上的配置文件必须是完整的redis.conf,如果文件内容为空,容器会启动失败。原因很简单,挂载相当于把宿主机文件覆盖到容器路径上,空文件覆盖掉镜像内自带的配置,redis也就不知道去哪里加载设置了。
Nginx的启动一般涉及静态文件和配置文件的挂载:
docker run -d \ --name nginx \ -p 80:80 \ -v /data/www:/usr/share/nginx/html:ro \ -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \ --restart=always \ nginx:1.24挂载目录用:ro后缀可以限制容器内对该目录只读,提高安全性。Nginx容器默认会读取/etc/nginx/conf.d/下的所有conf文件,你可以把自己的站点配置放在宿主机/data/nginx/conf.d/下,改配置不用进容器,改完宿主机文件再docker exec nginx nginx -s reload重载即可。
4. 常见问题排查与避坑实录
4.1 CentOS 7.9的存储驱动与内核兼容问题
CentOS 7.9默认的存储驱动是overlay2,但你可能会在某些机器上看到Docker自动降级成了devicemapper。这意味着容器磁盘性能会变差,尤其是在大量文件读写的场景下,差距非常明显。
检查方法:
docker info | grep "Storage Driver"如果输出是devicemapper,最好切回overlay2。在/etc/docker/daemon.json中加:
{ "storage-driver": "overlay2" }但前提是内核支持overlayfs。CentOS 7.9的内核在3.10.0-1160版本上对overlay2的支持已经比较稳定。如果你的内核低于这个版本,切到overlay2后容器可能会崩溃。这种情况我建议先升级内核,再切换存储驱动,顺序不能反。
另一个和内核相关的经典报错是:
cannot stat /var/lib/docker/overlay2/xxx: no such file or directory这种问题多半是之前系统异常重启,导致overlay目录数据不一致。解决方法不复杂但有点粗暴:
systemctl stop docker rm -rf /var/lib/docker/overlay2 systemctl start docker注意,/var/lib/docker下的overlay2目录存放的是镜像和容器层数据,删掉后所有容器和镜像都没了,但数据卷(带-v挂载的目录)不受影响。一定要确认数据卷都挂在宿主机外部路径,再执行这个操作。
4.2 Docker启动失败、无法拉取镜像的处理思路
Docker启动失败,最常见的是这两种。
第一种是iptables相关错误,报错内容里通常会出现Failed to start Docker Application Container Engine。CentOS 7自带的firewalld和Docker有历史兼容问题,Docker启动时会修改iptables规则,firewalld偶尔会拦一把。
推荐做法是停用firewalld,改用iptables服务:
systemctl stop firewalld systemctl disable firewalld生产环境如果此前依赖firewalld的防火墙规则,这一步要慎重,最好先导出现有规则再操作。如果只想放行某些端口,也可以在firewalld里添加富规则,但说实话,和Docker搭伙干活,firewalld越少介入越省心。
第二种是docker daemon的socket权限问题,现象是普通用户执行docker ps报:
permission denied while trying to connect to the Docker daemon socket原因很简单,docker.sock 只对root开放,你的普通用户没有权限访问。解决方式是把用户加入docker组:
usermod -aG docker $USER然后重新登录当前会话,再执行docker ps就正常了。这里提醒一句,加入docker组的用户其实等同于拿到了root权限,因为容器可以挂载宿主机目录,所以在多人共用服务器时要谨慎授权docker组。
拉取镜像超时的处理,除了前面说的配置加速器,还可以手动指定镜像源拉取。比如从阿里云容器镜像服务的公开仓库拉取:
docker pull registry.cn-hangzhou.aliyuncs.com/library/mysql:8.0拉下来后再打上官方tag:
docker tag registry.cn-hangzhou.aliyuncs.com/library/mysql:8.0 mysql:8.0 docker rmi registry.cn-hangzhou.aliyuncs.com/library/mysql:8.0这个方法在没有加速器可用或者加速器不够快时非常实用,相当于手动指定镜像地址。
4.3 systemd管理、防火墙放行与开机自启
服务器上装完Docker后,还需要处理好宿主机防火墙的端口放行问题。容器端口映射到宿主机后,外部流量要进得来,必须在宿主机防火墙层放行。
如果你用的是阿里云ECS、腾讯云这类云服务器,除了系统内部防火墙,云平台的安全组也记得放行对应端口。这两层缺一不可,很多人的容器明明启动了,外部就是访问不了,查了半天发现是云平台安全组没放行。
系统内部以iptables示例,放行3306端口:
iptables -I INPUT -p tcp --dport 3306 -j ACCEPT如果想要规则持久化,安装iptables-services后保存:
yum install -y iptables-services service iptables save再说回开机自启的问题。我在2.3节提到过systemctl enable docker,这是让Docker服务本身开机启动。但如果你用docker run创建的容器没加--restart=always,Docker服务重启后容器不会自动拉起。
对于已经创建的容器,可以用命令更新重启策略:
docker update --restart=always 容器名或者查看当前策略:
docker inspect 容器名 | grep -A 5 RestartPolicy--restart=always也有小坑:如果容器一直启动失败(比如配置文件写错,进程反复崩溃),Docker会一直尝试重启,导致日志爆炸。遇到这种场景,可以把策略改成--restart=unless-stopped,这个策略下容器正常退出后会被自动拉起,但如果容器处于停止状态,不会强制拉起来。实战中我习惯用unless-stopped,避免在排障时容器不断自动重启,干扰定位。
4.4 磁盘空间与容器日志增长的隐患
容器跑久了,最隐蔽的问题是日志文件无限增长。尤其是Nginx、应用服务这类日志量大的容器,/var/lib/docker/containers/<容器ID>/下的json日志文件能轻松吃掉几十GB磁盘。
解决方案有两种。一是在全局daemon.json中限制日志大小:
{ "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }二是对单个容器加参数:
docker run -d \ --log-opt max-size=100m \ --log-opt max-file=3 \ nginx配置生效后,单个容器的日志最多占300MB,超过自动滚动,不会再把磁盘写满。这类问题在初始部署时很少有人注意,通常要到磁盘报警才发现。经验就是,所有容器化环境,日志限制从第一天就要配好。
磁盘本身也需要关注,CentOS 7.9默认分区方案很多时候只给根目录几十GB,Docker默认把数据都放在/var/lib/docker下,一旦镜像多、容器多,根分区很容易爆。建议在安装Docker前,用独立的个大分区来挂载Docker数据目录,或者在/etc/docker/daemon.json里指定数据目录到空间充足的盘:
{ "data-root": "/data/docker" }修改后重启Docker,新镜像和新容器都会写到新路径。这个操作不会迁移已有数据,如果你有存量容器,需要先stop掉,迁移/var/lib/docker下的数据,再改配置。迁移前记得重新梳理数据卷路径,避免容器启动时找不到数据。
5. 从单容器到多容器:Compose编排的用武之地
很多时候我们会遇到这样的场景:一个项目依赖MySQL、Redis、Nginx三个服务,手敲三次docker run不仅费劲,命令一长还容易写错。这时候Compose就派上用场了。
用文件定义整套服务。先建一个目录:
mkdir -p /data/stack && cd /data/stack创建docker-compose.yml:
version: "3.8" services: mysql: image: mysql:8.0 container_name: mysql8 restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: YourPassword123 TZ: Asia/Shanghai ports: - "3306:3306" volumes: - /data/mysql:/var/lib/mysql - /etc/localtime:/etc/localtime:ro redis: image: redis:7 container_name: redis7 restart: unless-stopped command: ["redis-server", "--requirepass", "YourRedisPassword"] ports: - "6379:6379" volumes: - /data/redis:/data nginx: image: nginx:1.24 container_name: nginx restart: unless-stopped ports: - "80:80" volumes: - /data/www:/usr/share/nginx/html:ro - /data/nginx/conf.d:/etc/nginx/conf.d:ro启动整套服务:
docker compose up -d查看状态:
docker compose ps停止服务:
docker compose downCompose对个人项目或者小团队来说,最大的意义在于把环境配置代码化,换一台机器,把compose文件和挂载目录带过去,一条命令就能拉起整套环境。不用再对着笔记敲一条条docker run,也不会漏掉某个参数。
使用Compose后,改配置的操作也变得更安全。修改yml文件后,执行:
docker compose up -dCompose会自动识别配置差异,重建需要更新的容器,不用你手动stop、rm、run一路操作。
6. 几个容易被忽略的操作心得
第一点,docker exec进入容器后,很多容器镜像默认不带vim、netstat这些常用工具。比如CentOS镜像里要装个网络工具:
yum install -y net-tools但如果你用的是精简版镜像比如Alpine,包管理器是apk,命令完全不同。用容器时先确认基础镜像是什么系统,再决定用什么包管理器。这点看着小,实际操作中经常卡壳。
第二点,容器内改配置直接改后,容器一重启配置就丢了。容器是无状态的,任何没有挂在数据卷里的文件修改,容器重建后全部回到初始状态。所以修改容器内配置文件前,先确认这个文件有没有挂载宿主机目录,如果没有,要么挂载出来改,要么进入容器改完后docker commit保存为新镜像。后者不推荐但确实能救急。
第三点,docker inspect是排查问题最得力的命令。容器启动不了,别只在外面猜,看看容器的输出数据最直接:
docker logs 容器名容器是Running状态但业务访问异常,先看端口映射:
docker port 容器名查看容器的完整配置:
docker inspect 容器名这些都是基本功,但越基础的东西越容易被忽略。很多新手遇到问题第一反应是百度,实际上一条docker logs就能告诉你答案。
CentOS 7.9装Docker这套流程,我前前后后在各种环境重复过不知道多少次,从裸机到内网离线,从单机到集群节点,每次都能遇到新的小问题。但核心思路是固定的:准备好yum源、装对包、配好加速器、数据卷挂在宿主机、日志限制从一开始就拉满。把这几条做到位,剩下的就是在这个框架里自由发挥了。