☰
CentOS部署Docker实战指南:版本选型、网络调优与问题排查
2026/9/30 11:59:19 网站建设 项目流程

CentOS上部署Docker,听起来就是个“装个软件”的事,实际操作里能把人折腾到怀疑人生的点多得是——镜像拉不下来、容器起不来、端口映射不生效、跨主机容器不通,每一个坑我都替大家踩过。这篇文章不是官方文档的复述,是我自己在多台CentOS服务器上反复部署、卸载、重装之后整理出来的完整实战记录,包含环境选型、安装配置、网络调优、典型容器部署和问题速查表。无论你是刚接触Linux的运维新人,还是准备把业务容器化的开发者,照着这套流程走,能少走不少弯路。

先说结论:CentOS部署Docker本身不难,难点集中在三个地方——系统版本选型、YUM源可用性、网络与存储配置。这篇文章把这三个点彻底讲透,再附上我实测过的MySQL、Redis主从、GitLab部署案例,最后给一份踩坑速查表,基本能覆盖你90%以上的部署需求。

1. 部署前先把系统和Docker的“账”算清楚

1.1 CentOS版本选型:7.9、8、还是Stream

很多人上来就是一顿yum install docker,装完才发现版本不匹配、内核太老、仓库失效,白白浪费一下午。版本选型必须放在第一步。CentOS这几个版本的差异,直接决定了你的Docker能不能安稳跑起来。

先说结论:CentOS 7.9和CentOS Stream 9是目前部署Docker最稳妥的两个选择。CentOS 8已经进入EOL状态,官方源全搬到了vault.centos.org,虽然能用,但每次配置都得手动指过去,Docker官方源对它的支持也不如7和Stream系列那么顺畅。CentOS Stream 10比较新,如果你追求新内核、新工具链,可以上,但生产环境建议再等等社区沉淀。Ubuntu和CentOS的选择问题,如果你的团队习惯apt生态、看重更新的软件包,Ubuntu确实顺手;但在国内服务器租赁和企业内部环境中,CentOS系依然占有很大比例,运维习惯、文档积累、云厂商镜像支持都更成熟,这也是我写这篇操作记录的原因。

版本确定之后,还要看一眼系统小版本。CentOS 7.9是7系的最终版,别再用老旧的7.4、7.6,内核太老跑不动新版容器。用下面这条命令确认:

cat /etc/centos-release uname -r

输出里如果看到CentOS Linux release 7.9.2009,内核是3.10.0-1160.el7.x86_64,这是标准状态。但注意,Docker新版对内核有要求,20.10系列在3.10内核上还凑合,24版本之后建议内核至少4.18以上。CentOS 7要是想用新版Docker,最好先走ElRepo把内核升到长期支持版,这个我在后面安装章节会展开讲。

1.2 内核、防火墙与虚拟化基础检查清单

系统版本定了,接下来按顺序过一遍基础环境。这一步做扎实了,后面能少排一堆莫名其妙的故障。

第一项,内核模块。Docker依赖overlay和br_netfilter这两个内核模块,加载方式是在/etc/modules-load.d/docker.conf里写入:

cat > /etc/modules-load.d/docker.conf <<EOF overlay br_netfilter EOF

然后执行modprobe overlay和modprobe br_netfilter立即加载。br_netfilter的作用很关键,它让桥接流量也能经过iptables过滤,容器网络不通的时候,十有八九和它没加载有关。

第二项,网络转发。容器要访问外网,需要系统开启IP转发:

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p

第三项,SELinux和firewalld。这两兄弟是容器环境的“老朋友”了。SELinux处于Enforcing状态时,容器挂载宿主机目录经常遇到权限拒绝,虽然可以通过配置context解决,但生产环境图省事的话,建议至少调整为Permissive。firewalld和Docker的iptables规则存在竞争关系,端口映射经常莫名其妙失效,我的习惯是直接停掉firewalld:

setenforce 0 systemctl disable --now firewalld

注意这样改完要重启一次机器让SELinux配置持久生效。如果你用的是虚拟机,记得装好open-vm-tools或qemu-guest-agent,否则宿主机和虚拟机之间复制文件、剪贴板共享全得靠手敲,那效率太低了,我见过有人因为没装tools,光传安装包就折腾了半小时。

2. Docker安装全流程:YUM源、核心组件与关键参数

2.1 配置Docker官方源与版本锁定的正确玩法

CentOS自带的源里没有Docker,以前装老版本走的是extras源里的docker包,那个又老又难用。现在标准的做法是用Docker官方提供的YUM源。

先装基础工具,再添加仓库:

sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

这里有个细节,CentOS 7下执行完这条命令,生成的/etc/yum.repos.d/docker-ce.repo里的$releasever会自动替换成7,不会有问题。但CentOS 8系统上,因为8已经EOL,$releasever解析会有偏差,稳妥的做法是下载repo文件后手动把$releasever改成8。

接下来安装完整组件:

sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

很多人只装docker-ce,导致后面要用Compose时发现命令不存在。现在Docker官方把docker-compose-plugin独立出来了,装上之后直接用docker compose命令,不用再单独去GitHub下载二进制的docker-compose。

如果你需要锁定版本,比如在CentOS 7上想用20.10系列的老版本,先查看可用版本:

yum list docker-ce --showduplicates | sort -r

看到版本列表后指定安装:

sudo yum install docker-ce-20.10.24 docker-ce-cli-20.10.24

为什么要锁定版本?因为数据库集群、K8s这类系统对Docker版本有强依赖,尤其是CentOS 7配老内核的环境,升到新版Docker反而容易出兼容性问题。生产环境我建议锁版本,升级走灰度验证。

2.2 启动服务、开机自启与数据目录迁移

安装完成不代表能直接用了,启动之前先把配置想清楚,否则后面改起来很痛。

启动Docker:

sudo systemctl start docker sudo systemctl enable docker

enable是为了开机自启,这步别省,我见过不少人没开自启,服务器一重启容器全趴了。

默认的Docker数据目录在/var/lib/docker,系统盘空间紧张的话必须迁移。迁移方式不复杂:

sudo systemctl stop docker sudo mkdir -p /data/docker sudo vim /etc/docker/daemon.json

在daemon.json中加入:

{ "data-root": "/data/docker" }

然后systemctl start docker。注意一个坑:迁移前如果有镜像和容器,直接改data-root后它们是看不到的,最好先记录一下已有镜像列表,迁移完按需重新拉取,或者把整个/var/lib/docker目录rsync拷贝过去再进行切换。拷贝时记得停掉Docker服务,防止数据不一致。

2.3 daemon.json优化:镜像拉取加速、日志清理与存储驱动

daemon.json是Docker引擎的核心配置文件,几乎所有运行时的全局参数都在这控制。以一个生产环境常用配置为例:

{ "registry-mirrors": [ "https://你的云厂商加速地址" ], "data-root": "/data/docker", "exec-opts": ["native.cgroupdriver=systemd"], "storage-driver": "overlay2", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "ip-forward": true, "iptables": true }

一个个解释关键项。registry-mirrors是镜像加速,公共镜像仓库的拉取速度受网络环境影响很大,建议在云厂商容器镜像服务控制台申请一个专属加速地址,一般格式是https://xxxxxxxx.mirror.xxx.com,拿到的地址填进去,systemctl daemon-reload && systemctl restart docker之后,执行docker info看Registry Mirrors字段确认生效。

exec-opts设成native.cgroupdriver=systemd,这个在K8s环境里是硬性要求,否则kubelet和Docker的cgroup驱动不一致,节点起不来。单机用Docker也建议加上,系统和容器的资源管理更统一。

storage-driver用overlay2,这是现代Docker默认且性能最好的存储驱动,不需要额外配置。但如果你的文件系统是xfs,确认创建时带了ftype=1,否则overlay2会报错,这也是个容易忽视的坑。

log-opts限制日志文件大小和数量。默认Docker日志是无限增长的,我之前遇到过容器日志把200G数据盘写满的事故,就是因为没做限制。配上这三行,单个容器日志超过100M自动切割,最多保留3个历史文件。

改完配置重启Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

验证是否正常:

docker run --rm hello-world

拉取hello-world这种小镜像能成功,基本说明拉取链路、存储驱动、容器运行全链路通了。

3. 容器网络配置与“网络不通”的完整排查路径

3.1 Docker默认网络模型与端口映射原理

Docker装好之后,宿主机上会多出一个叫docker0的虚拟网桥,IP段默认是172.17.0.0/16。容器默认通过bridge模式接入这个网桥,相当于容器主机之间连了一台虚拟交换机。理解这个模型很重要,因为后面你遇到的80%网络问题,都和这张“拓扑图”有关。

端口映射的本质是iptables DNAT规则。你执行docker run -p 8080:80 nginx,Docker会在宿主机上自动添加一条规则,把发往宿主机8080端口的流量转发到容器的80端口。查看规则:

iptables -t nat -L -n | grep 8080

能看到一条DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80的规则,这就对了。很多时候端口映射不生效,本质上是这条规则没生成,或者被firewalld清掉了。

跑一下容器验证:

docker run -d --name test-nginx -p 8080:80 nginx curl http://127.0.0.1:8080

有NGINX默认页面输出,说明端口映射链路正常。

3.2 跨主机容器通信的三种实用方案

单机部署时容器一切正常,一上多机,各种互通问题就来了。跨主机容器通信有三种常见方案,按使用场景选型即可。

方案一:host网络模式。启动时加--network host,容器直接共享宿主机网络命名空间,没有网络隔离,端口也不存在映射,容器监听什么端口,宿主机就监听什么端口。优点是零性能损耗、简单粗暴,缺点是端口管理混乱,多容器部署同一端口直接冲突。适合对网络性能要求极高、单机跑少量服务的场景。

方案二:overlay网络。Docker Swarm模式下支持docker network create -d overlay创建跨主机网络,容器在这个网络中互相通信时,流量会自动封装转发。但Swarm本身用得越来越少,如果你已经在用K8s,那K8s的CNI插件(比如Calico、Flannel)是更主流的选择。

方案三:物理网络桥接(macvlan)。给每个容器分配一个和宿主机同网段的IP,容器从外部看就像独立主机,可以直接被局域网内其他机器访问。配置方法:

docker network create -d macvlan \ --subnet=192.168.1.0/24 \ --gateway=192.168.1.1 \ -o parent=eth0 \ macnet

但这个方案依赖物理交换机开启混杂模式,而且IP管理和漂移比较麻烦,适合内网固定IP的轻量场景。

简单业务集群用host网络模式快速解决问题;等规模上去了再平滑过渡到K8s,这是我实践下来成本最低的路径。

3.3 网络故障定位的完整流程

容器网络不通,排除法按照下面顺序来,避免像无头苍蝇一样乱试。

第一步:确认容器自身网络状态。

docker exec -it 容器名 ping 172.17.0.1 docker exec -it 容器名 ping baidu.com

ping不通网关IP,说明容器和宿主机的连接断了,检查docker0网桥是否存在:ip addr show docker0。ping不通外网域名,先看DNS,进入容器执行cat /etc/resolv.conf,如果DNS指向有问题,用--dns 8.8.4.4这种参数重新启动容器。

第二步:检查宿主机内核转发。

sysctl net.ipv4.ip_forward

输出为0时,容器外网铁定不通,改成1并sysctl -p生效。

第三步:检查iptables规则链。

iptables -t nat -L -n iptables -L -n

重点看DOCKER链是否有对应规则。没有规则,重新启动一遍容器让Docker重建规则。如果之前跑过firewalld,建议先systemctl stop firewalld再重启Docker,有时候firewalld残留的规则会和Docker冲突,清掉就恢复了。

第四步:用抓包确认数据走向。

yum install -y tcpdump tcpdump -i docker0 port 80 tcpdump -i eth0 port 80

分别看docker0和物理网卡上的流量,能直观看出数据到底卡在哪一跳。我曾经遇到过外部访问不到容器服务的问题,抓包发现物理网卡上有请求进来,docker0上却没流量,最后定位是firewalld的规则把FORWARD链劫持了,停掉firewalld后恢复。这个案例说明,网络问题不能只凭感觉判断,按链路一步步排查才是最快的。

4. 经典容器场景实战:MySQL、Redis主从与GitLab

4.1 MySQL 8.0容器化:数据卷、密码与时区

MySQL容器化是最高频的需求,很多团队第一步迁移就是把数据库跑进Docker。我用docker compose的方式演示,因为现在新装Docker都带Compose插件,配置文件化管理比一长串docker run参数清晰得多。

先建目录结构:

mkdir -p /data/mysql/{conf,data}

写/data/mysql/docker-compose.yml:

services: mysql8: image: mysql:8.0 container_name: mysql8 restart: always environment: MYSQL_ROOT_PASSWORD: "Root@123456" TZ: Asia/Shanghai command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --default-time-zone=+08:00 ports: - "3306:3306" volumes: - ./conf:/etc/mysql/conf.d - ./data:/var/lib/mysql

启动:

cd /data/mysql && docker compose up -d

几个关键点说明一下。数据目录必须挂载出来,./data不挂载的话容器删掉数据就没了,这在生产环境是不可接受的。密码不要直接写在compose文件里,生产环境用--env-file加载环境变量文件,避免密码泄露到版本控制里。TZ: Asia/Shanghai和--default-time-zone=+08:00要一起配,否则服务器系统时间是对的,MySQL里的NOW()函数却返回UTC时间,业务数据时区全乱。

验证:

docker exec -it mysql8 mysql -p

输入密码后执行show variables like '%character%';确认utf8mb4生效,再执行select now();确认时区正确。

4.2 Redis主从集群:一条命令拉起读写分离

Redis主从是缓存场景的常见架构,Docker下搭建特别方便,用同一个镜像跑多个容器就行。先建一个自定义网络,保证容器间域名解析可用:

docker network create redis-net

启动主节点:

docker run -d --name redis-master \ --network redis-net \ -p 6379:6379 \ -v /data/redis/master:/data \ redis:7 redis-server --appendonly yes

启动从节点,注意新版Redis用--replicaof而不是老版本文档里的--slaveof:

docker run -d --name redis-slave1 \ --network redis-net \ -p 6380:6379 \ -v /data/redis/slave1:/data \ redis:7 redis-server --appendonly yes --replicaof redis-master 6379

验证主从状态:

docker exec -it redis-master redis-cli info replication

输出里role:master、connected_slaves:1就说明主从搭建成功。再验证数据同步:在主节点set testkey testvalue,然后在从节点get testkey能查到,同步正常。

这套模式的好处是扩容方便,再想加从节点就复制一条命令改个容器名和端口,一分钟搞定。生产环境建议给每个从节点也配数据卷,容灾恢复时不至于全从空数据启动。

4.3 GitLab社区版部署:内存评估与502排障

GitLab是很多企业自建代码托管的首选,但它对资源要求不低,Docker部署时要注意内存。社区版官方推荐至少4GB内存,512MB内存的机器跑出来页面能打开但操作卡顿严重。

部署命令:

mkdir -p /data/gitlab/{config,logs,data} docker run -d \ --name gitlab \ --hostname gitlab.example.com \ -p 8022:22 -p 8080:80 \ -v /data/gitlab/config:/etc/gitlab \ -v /data/gitlab/logs:/var/log/gitlab \ -v /data/gitlab/data:/var/opt/gitlab \ --restart always \ gitlab/gitlab-ce:latest

--hostname是重点,它决定GitLab生成的仓库克隆地址里的域名,改完之后要和实际访问域名保持一致。端口映射这里把宿主机的8080映射到容器内部的80端口,宿主机的8022映射到容器的22端口,避免和宿主机已有SSH端口冲突。

启动后第一次访问需要等几分钟,GitLab初始化比较慢,可以用docker logs -f gitlab观察进度。浏览器访问http://服务器IP:8080,首次会让你设置root密码。

如果看到502页面,先别急着重启容器,排查顺序如下:

  1. 等2-3分钟,GitLab内部组件(Puma、Sidekiq)启动慢是正常的。
  2. 看内存:free -h,物理内存不足2GB的话跑GitLab很勉强。
  3. 看日志:docker logs --tail 50 gitlab,有没有明显的报错。
  4. 改配置:在/data/gitlab/config/gitlab.rb里设external_url 'http://服务器IP:8080'然后docker exec gitlab gitlab-ctl reconfigure。

我的一份GitLab实例就是通过external_url配置修正后,502消失的,这个问题的核心在于GitLab内部生成的外部访问地址和实际地址不一致,导致页面跳转失败。

5. 高频问题速查与维护经验总结

5.1 安装与启动阶段高频报错对照表

把部署到现在遇到过的典型问题整理成一张速查表,按图索骥能省很多时间。

现象可能原因解决办法
Error: Failed to download metadata for repo docker-ceYUM源网络问题或仓库地址失效检查DNS,curl https://download.docker.com测试连通性;CentOS 8用sed -i 's/$releasever/8/g'修正repo文件
Cannot connect to the Docker daemondockerd没有启动systemctl start docker;再看journalctl -u docker日志定位启动失败原因
docker: permission denied while trying to connect to the Docker daemon socket当前用户不在docker用户组sudo usermod -aG docker $USER,退出重登
容器启动后立刻退出并返回非0状态镜像本身的问题或启动命令参数错误docker logs 容器名查日志,按日志排查
OCI runtime exec failed: exec failed: unable to start container process容器内没有指定命令对应的可执行文件确认docker exec后面的命令路径正确
failed to create task for container: failed to create shim taskcgroup驱动或内核不兼容检查daemon.json中exec-opts配置,必要时升级内核
拉镜像超时或速度极慢未配置镜像加速在daemon.json中配置registry-mirrors并重启Docker
虚拟机里装的CentOS无法和宿主机复制文件没装虚拟化工具VMvare装open-vm-tools,KVM/QEMU装qemu-guest-agent

Windows上安装Docker Desktop时如果遇到virtualization support not detected的报错,那是另一回事,需要去BIOS/UEFI里开启Intel VT-x或AMD-V。这个和服务器上直接部署Docker Engine没关系,CentOS服务器上原生跑Docker不需要桌面套件,如果执着于图形化界面管理容器,推荐装个Portainer,但本文场景就用命令行最顺手。

5.2 运行稳定后的日常维护四点建议

部署完成不代表事情结束了,容器化系统的维护比传统部署多了几个必须盯紧的环节,这几条经验都是我用故障换来的。

第一,磁盘空间要常盯。容器镜像、日志、数据卷都是磁盘黑洞。建议写个简单的脚本,每天检查/data/docker和挂载卷所在分区的使用率,超过80%报警。docker system df命令可以查看Docker占用的详细空间。

第二,日志不能放任自流。daemon.json里的log-opts限了大小也不是万事大吉,应用本身的业务日志要配合宿主机的logrotate做轮转。另外容器的时间戳默认是UTC,应用打日志时记得把TZ=Asia/Shanghai环境变量传进去,不然后期排查问题看时间轴会疯掉。

第三,镜像和容器定期清理。无人维护的旧镜像、临时容器、悬挂卷,时间长了会堆积成山。Docker自带的docker system prune可以清理所有未使用资源,高危操作建议人工确认后再执行,别写进定时任务里自动跑。

第四,改动配置文件后别忘验证。每次改daemon.json,先docker run --rm hello-world验证一下再让业务容器滚动重启。我师傅教我的规矩是:任何配置层面的变更,先小范围验证,再全部生效,这条在容器环境里尤为重要。

最后再分享一个我自己的习惯:新环境部署Docker,我会先备份关键目录,比如系统盘不大的时候用Clonezilla这类分区克隆工具做一次整盘快照,然后再动分区和数据目录。Docker本身不复杂,复杂的是你把它嵌进现有运维体系时面临的那些边界情况,每一步都稳着来,比什么花活都强。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询