最近在测试环境要搭一套缓存加关系型数据库的组合,顺手把整个流程从零到一完整走了一遍。今天就以 CentOS 7 为底,把 Docker 装好,再用 Docker 把 Redis 和 PostgreSQL 跑起来。整个过程其实就是一条命令链,但中间值得注意的坑不少,尤其是 CentOS 7 老内核和 Docker 之间的兼容问题,以及数据库容器挂数据和持久化的姿势。
这篇文章适合两类人看:一类是刚接触 Linux 运维,想在 CentOS 7 上快速搭一套环境出来跑项目的同学;另一类是已经在用 Docker,但每次部署 Redis、PostgreSQL 都要重新查参数、踩一遍老坑的朋友。我会把我实际操作中遇到的所有问题都写出来,含命令、配置和排查思路。
1. 为什么在 CentOS 7 上用 Docker 装 Redis 和 PostgreSQL
1.1 这套组合到底解决了什么问题
很多人在测试环境搭服务,第一反应是直接yum install redis和yum install postgresql-server。但 CentOS 7 默认仓库里的软件版本实在太老,Redis 可能还是 3.x,PostgreSQL 可能连 12 都不到。老版本带来的问题不是“不能用”,而是你在本地开发用的新特性它没有,或者 SQL 行为不一致,最后环境差异把问题全掩盖了。
用 Docker 直接拉镜像,版本跟生产环境对齐就很方便。比如 Redis 7.0、PostgreSQL 14,这些版本特性在测试环境验证完,生产环境用同一套镜像,行为基本一致。而且 Docker 的隔离性也让清理变得很干净,容器删掉重来,不会像 yum 安装那样留下乱七八糟的配置文件和服务。
这套方案最适合的场景是单机开发测试、个人项目、CI 环境的数据库依赖。如果是要做生产环境的高可用集群,Docker 单容器是不够的,得考虑主从、备份、监控这些额外维度,但那是另一篇文章的事。今天这篇,先把单机跑通、跑稳。
1.2 镜像版本和 Docker 版本要怎么选
这一段是很多人忽略的。CentOS 7 的内核默认是 3.10.x,非常老。新版本的 Docker 虽然名义上支持 CentOS 7,但实际踩坑率不低。
我个人的选型是:
| 组件 | 推荐版本 | 理由 |
|---|---|---|
| Docker Engine | 20.10.x 系列 | 对老内核兼容最好,稳定 |
| Redis 镜像 | redis:7.0 或 redis:6.2 | 7.0 功能新,6.2 更保守 |
| PostgreSQL 镜像 | postgres:14 或 postgres:15 | 14 是久经考验的稳定版 |
Docker 不建议一上来就装最新大版本,比如 24.x、25.x,在 CentOS 7 老内核上偶尔会出现网络和 storage driver 的兼容问题。如果你对 Docker 版本没有特殊要求,20.10.24 这个版本我在多台 CentOS 7 机器上都实测过,非常稳。
另外要提醒一点:Docker Desktop 和 Docker Engine 不是一回事。Docker Desktop 是带图形界面、面向 Windows 和 macOS 的工具,CentOS 7 服务器上我们装的是 Docker Engine,就是命令行那一套。别下错安装包,也别把 Docker Desktop 的启动报错思路套到 Linux 上来。
2. 环境准备与 Docker 安装全流程
2.1 安装前必须确认的三件事
在敲安装命令之前,我建议先花两分钟确认系统和网络状态,不然很容易在安装中段报错,然后又回头排查。
第一件事是确认系统版本和内核:
cat /etc/redhat-release uname -r看到 CentOS Linux release 7.x 和 3.10.x 内核是正常的。只要不是 CentOS 6,Docker 20.10 都能装。
第二件事是确认外网连通性。Docker 安装要拉 rpm 包,启动时要拉镜像,没有外网寸步难行。有些 CentOS 7 机器刚装完没有网,排查方法是先 ping 网关,再 ping 外网 IP,最后用nslookup或dig查 DNS。如果 ping 不通百度,多半是网络配置文件里没写 DNS 或者网关不对,修改/etc/sysconfig/network-scripts/ifcfg-eth0里的GATEWAY和DNS1,然后重启网络服务。
第三件事是防火墙和 SELinux。测试环境我推荐直接关闭 SELinux:
setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config注意setenforce 0是临时生效,重启后恢复,必须配合sed那行永久修改。生产环境你要评估是否能关,但测试环境没必要让 SELinux 给你添堵。
2.2 逐步执行 Docker 安装命令
CentOS 7 上装 Docker 的经典流程是配置阿里云 yum 源,然后用 yum 安装。为什么不用官方源?官方源在部分网络环境下访问很慢,而且经常出现 metadata 下载超时。阿里云源在国内服务器上速度优势明显。
首先卸载系统里可能残留的旧版本 Docker:
yum remove docker docker-client docker-common docker-engine然后安装依赖工具:
yum install -y yum-utils device-mapper-persistent-data lvm2配置 Docker 的阿里云 yum 源:
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装 Docker Engine:
yum install -y docker-ce docker-ce-cli containerd.io这里有个可能遇到的问题:部分 CentOS 7 机器的 yum 源会把 docker-ce 的 GPG 校验失败,报类似Public key for docker-ce-xxx.rpm is not installed的错误。解决办法是在 yum install 命令后面加--nogpgcheck:
yum install -y docker-ce docker-ce-cli containerd.io --nogpgcheck启动 Docker 并设置开机自启:
systemctl start docker systemctl enable docker验证安装结果:
docker version如果看到 Client 和 Server 两段信息,说明 Docker 已经正常工作。注意 Server 那一段必须有,只有 Client 信息说明 Docker 守护进程没起来。
最后配置镜像加速。这一步在国内几乎是必做的,否则拉 Redis、PostgreSQL 官方镜像会慢到怀疑人生。修改/etc/docker/daemon.json:
mkdir -p /etc/docker cat > /etc/docker/daemon.json <<EOF { "registry-mirrors": [ "https://docker.m.daocloud.io" ], "exec-opts": ["native.cgroupdriver=systemd"] } EOF重启 Docker 使配置生效:
systemctl daemon-reload systemctl restart dockerexec-opts那行是把 cgroup 驱动改成 systemd,这样能避免后面容器和宿主机在 cgroup 管理上出现不一致,尤其是你以后要在同机器上装 kubelet 的时候。
2.3 安装过程中三个高频报错
第一个是刚才说过的 GPG 校验失败,加--nogpgcheck解决,不啰嗦。
第二个是 Docker 启动失败,systemctl status docker或者docker info里报iptables failed。这个坑很经典,通常是因为宿主机 iptables 规则残留或者firewalld和 Docker 的 NAT 规则冲突。最简单粗暴的办法是重启宿主机,让 Docker 重新初始化 iptables。如果不想重启,可以执行:
systemctl stop firewalld systemctl disable firewalld systemctl start docker先停掉 firewalld,让 Docker 自己管理 iptables,跑通后再决定要不要开回防火墙。实际上生产环境用 Docker 时,很多人都是靠宿主机安全组或外层防火墙管控端口,宿主机内部 firewalld 管得太多反而麻烦。
第三个是容器启动时报OCI runtime create failed,常见原因是内核版本过低或者内核缺少必要模块。比如我在一台老机器上用过 Docker 24.x,拉起容器时报container_linux.go: ... kernel ... unsupported。解决办法就是切换到 Docker 20.10.x:
yum install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io指定版本重装即可。
3. 用 Docker 部署 Redis:从拉镜像到自启动
3.1 准备数据目录和工作目录
Redis 容器最核心的一个问题是:如果不挂载数据目录,容器一删,所有数据直接蒸发。我见过不少新手在测试环境跑 Redis,跑了一个月,某天清理容器,缓存的测试数据全没了,然后一脸懵。
所以哪怕只是测试环境,我也建议养成挂载数据目录和配置目录的习惯,后面的 PostgreSQL 也一样。先创建目录:
mkdir -p /data/redis/conf mkdir -p /data/redis/data配置文件放在宿主机上的/data/redis/conf/redis.conf,这样以后调整参数直接改宿主机文件,重启容器就生效,不用进容器改。
3.2 编写持久化配置并启动容器
这里给出一个适合测试环境的最小化 Redis 配置,内容不多,但把最关键的几个点都覆盖到了。
cat > /data/redis/conf/redis.conf <<EOF bind 0.0.0.0 port 6379 daemonize no protected-mode yes requirepass 123456 appendonly yes appendfsync everysec maxmemory 256mb maxmemory-policy allkeys-lru timeout 300 tcp-keepalive 60 EOF逐个解释一下这些参数的作用:
| 参数 | 作用 |
|---|---|
| bind 0.0.0.0 | 监听所有网卡,允许外部客户端连接 |
| daemonize no | 不要后台运行,因为容器主进程必须在前台 |
| protected-mode yes | 保护模式,不直接暴露公网 |
| requirepass | 设置访问密码,测试环境也要加 |
| appendonly yes | 开启 AOF 持久化,防止重启丢数据 |
| appendfsync everysec | 每秒刷盘一次,性能和安全的折中 |
| maxmemory | 限制 Redis 内存上限 |
| maxmemory-policy allkeys-lru | 内存满时按 LRU 淘汰键 |
daemonize no这个参数必须说清楚。在 Docker 里,容器的主进程必须是前台进程,如果 Redis 自己后台运行了,Docker 会认为服务启动失败,容器直接退出。所以千万不要在 redis.conf 里写daemonize yes。
接下来用 docker run 启动容器:
docker run -d \ --name redis-server \ --restart=always \ -p 6379:6379 \ -v /data/redis/conf/redis.conf:/etc/redis/redis.conf \ -v /data/redis/data:/data \ -v /etc/localtime:/etc/localtime:ro \ redis:7.0 \ redis-server /etc/redis/redis.conf这条命令的几个参数拆开看:
-d后台运行--name redis-server给容器起个名,后续管理方便--restart=always容器退出时自动重启,相当于开机自启-p 6379:6379把宿主机 6379 端口映射到容器 6379-v /data/redis/conf/redis.conf:/etc/redis/redis.conf用宿主机配置文件覆盖容器内默认配置-v /data/redis/data:/data数据目录挂载-v /etc/localtime:/etc/localtime:ro让容器和宿主机使用同一时区,避免日志时间对不上redis:7.0是镜像名- 最后那串
redis-server /etc/redis/redis.conf是容器启动时执行的命令,告诉 Redis 加载我们挂载进去的配置
启动后验证:
docker ps docker exec -it redis-server redis-cli -a 123456 ping正常会看到PONG输出,说明 Redis 已经跑起来了。
3.3 持久化和可视化工具推荐
Redis 的持久化方向有 RDB 和 AOF 两个选择,我上面的配置开启了 AOF。RDB 是定期生成快照,恢复速度快但可能丢失两次快照之间的数据;AOF 是记录每次写操作,数据安全性更高但文件会越来越大。测试环境我一般直接开 AOF,等以后做生产环境再根据业务容忍度做取舍。
如果以后 Redis 数据量涨上来,可以考虑给容器加内存限制:
docker update --memory=512m --memory-swap=512m redis-server这条命令在不重建容器的情况下动态限制内存,对于防止测试环境 Redis 把宿主机内存吃满挺有用。
可视化客户端方面,我推荐 Another Redis Desktop Manager,这个工具开源免费,跨平台,连接 Redis 时填上宿主机 IP、端口和刚才设置的密码就能连上,比命令行直观很多。图形化客户端不要太旧的版本,否则对新版 Redis 的 ACL 认证支持不好。
3.4 Redis 部署后最常见的三个问题
第一个是外部客户端连不上,Redis 日志报Protected mode is enabled。这是 Redis 的保护机制在起作用:当protected-mode yes且没有设置密码或没有设置 bind 时,Redis 默认只允许本机连接。我们配置里已经设置了密码和 bind 0.0.0.0,正常不会触发。如果还遇到,检查宿主机防火墙是否放开了 6379 端口,以及云服务器的安全组是否放行端口。
第二个是密码验证失败,NOAUTH Authentication required。这个很直白,就是客户端没带密码。用命令行时记住redis-cli -a 你的密码,或者先auth 密码再执行命令。
第三个是 AOF 文件损坏,日志里出现Bad file format reading the append only file。这个通常是因为宿主机突然断电或者磁盘写满,导致 AOF 文件尾部不完整。测试环境不想重建容器的话,可以用redis-check-aof --fix修复。但要注意,修复过程会截断文件尾部损坏的数据,所以实际上数据可能已经丢了一部分,这就是为什么持久化文件要定期备份。
4. 用 Docker 部署 PostgreSQL:数据目录与连接配置
4.1 拉镜像和启动容器的标准姿势
PostgreSQL 官方镜像做得很成熟,环境变量配置很清晰,比 Redis 那边还简单。先用 docker pull 拉镜像:
docker pull postgres:14然后创建数据目录并启动容器:
mkdir -p /data/postgres docker run -d \ --name pgsql-server \ --restart=always \ -p 5432:5432 \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres123 \ -e POSTGRES_DB=testdb \ -v /data/postgres:/var/lib/postgresql/data \ -v /etc/localtime:/etc/localtime:ro \ postgres:14这里三个环境变量需要说明:
| 环境变量 | 作用 |
|---|---|
| POSTGRES_USER | 超级用户名称,默认是 postgres |
| POSTGRES_PASSWORD | 超级用户密码,必须设置 |
| POSTGRES_DB | 初始化时自动创建的数据库名 |
有个细节比较微妙:如果你已经挂载了一个包含旧数据的/data/postgres目录,那么 POSTGRES_USER、POSTGRES_PASSWORD 这些环境变量在容器启动时不会生效,因为数据库已经初始化过了,密码还是旧的。这个很容易让人误解,以为改了环境变量密码就会变,实际上不会。只有首次初始化数据目录时,环境变量才会生效。
启动后验证:
docker ps docker exec -it pgsql-server psql -U postgres -d testdb能进入 psql 命令行就算部署成功。
关于镜像版本,我推荐 postgres:14 而不是 postgres:latest。latest 标签跟随官方最新大版本,但大版本升级可能导致数据目录不兼容,如果你从 16 downgrade 回 15,数据文件是没法直接读的,还是老版本配合 docker tag 锁定版本最省心。
4.2 PostgreSQL 的连接认证机制
PostgreSQL 的连接认证是个高频难点,报错五花八门,但核心是理解两个文件:pg_hba.conf和postgresql.conf。
postgresql.conf里的listen_addresses决定 PostgreSQL 监听哪些 IP,官方镜像默认是*,也就是监听所有网卡,不用改。
pg_hba.conf决定哪些来源可以使用什么方式认证连接。官方镜像默认的 pg_hba 内容最后几行类似:
host all all all scram-sha-256意思是所有来源、所有用户都能连,但需要密码认证。这意味着外部工具只要知道 IP、端口、用户名、密码就能连接,总体上还算安全。
但如果你自己改过 pg_hba.conf,加了一行local all all peer,本地连接就会走 peer 认证,报Peer authentication failed for user "postgres"。这行的意思是:连接本机的用户必须是系统用户,且系统用户名要和数据库用户名一致。容器里的系统用户通常不是 postgres,所以就会失败。
解决方法是把peer改成md5或scram-sha-256。另外我再强调一次,改了 pg_hba.conf 后需要重启容器或执行SELECT pg_reload_conf();才会生效。
远程连接工具方面,如果你用 DBeaver、Navicat、psql 这些客户端连接宿主机 IP 的 5432 端口,最常遇到的错误是connection refused。先检查容器状态:
docker ps -a | grep pgsql如果容器是 Exited 状态,看日志:
docker logs pgsql-server容器起来了但连接还是被拒,检查宿主机防火墙和云安全组有没有放行 5432。用ss -lntp | grep 5432确认端口有监听,然后再去查防火墙。
4.3 PostgreSQL 备份恢复思路
单机环境的 PostgreSQL,不管是不是容器化的,备份都是必须考虑的问题。这里给两个层级的备份方案,测试环境足够了。
第一层是逻辑备份,用 pg_dump。PostgreSQL 官方容器提供了 pg_dump、pg_restore 这些工具,不需要在宿主机再装客户端。比如备份 testdb 数据库:
docker exec pgsql-server pg_dump -U postgres -d testdb -F c -f /tmp/testdb.dump docker cp pgsql-server:/tmp/testdb.dump /data/postgres/backup/第一条命令在容器内生成自定义格式的备份文件,-F c是自定义压缩格式,比纯 SQL 文件更灵活,支持选择性恢复。第二条命令把容器内的备份文件拷贝到宿主机。
恢复时反向操作:
docker cp /data/postgres/backup/testdb.dump pgsql-server:/tmp/ docker exec pgsql-server pg_restore -U postgres -d testdb --clean /tmp/testdb.dump日常备份可以写个 crontab 定时执行,这里不展开。
第二层是物理备份,直接备份/data/postgres数据目录。容器停掉后拷贝数据目录,恢复时保证 PostgreSQL 大版本一致,直接替换数据目录。这种方法简单,但比较粗暴,如果数据目录正在写而你去拷贝,可能得到不一致的数据文件,所以备份前最好停容器或者用pg_basebackup。测试环境用第一种逻辑备份就够了。
4.4 PostgreSQL 部署后最常见的三个问题
第一个是之前提到的Peer authentication failed for user "postgres",原因就是 pg_hba.conf 里用了 peer 认证,修改成scram-sha-256或md5即可。
第二个是客户端提示password authentication failed for user "postgres"。这个先确认环境变量是否生效。如果容器是用现有数据目录启动的,POSTGRES_PASSWORD 不会生效,需要用docker exec pgsql-server psql -U postgres进入后执行:
ALTER USER postgres WITH PASSWORD '新密码';第三个是容器一直重启,日志里提示/var/lib/postgresql/data权限错误。这是挂载目录的权限不对。PostgreSQL 容器内部以 postgres 用户运行,UID 是 999,而宿主机创建目录的 UID 通常是 0,容器内没有权限写数据目录就会启动失败。解决方法是授权:
chown -R 999:999 /data/postgres或者直接用 Docker 的命名卷:
docker volume create pgdata docker run ... -v pgdata:/var/lib/postgresql/data命名卷由 Docker 管理权限,不容易出这个问题。
5. 运维日常:端口规划、日志排查和自启动管理
5.1 端口规划与防火墙策略
上面部署完,宿主机上至少有两个对外端口了。规整一下:
| 服务 | 容器端口 | 宿主机端口 | 用途 |
|---|---|---|---|
| Redis | 6379 | 6379 | 缓存、测试数据存储 |
| PostgreSQL | 5432 | 5432 | 业务数据库 |
测试环境如果要开放到局域网,记得用 firewalld 放行。CentOS 7 的防火墙操作如下:
firewall-cmd --zone=public --add-port=6379/tcp --permanent firewall-cmd --zone=public --add-port=5432/tcp --permanent firewall-cmd --reload--permanent是持久化规则,不加的话重启就没了。如果你前面安装 Docker 时图省事把 firewalld 停了,现在要恢复它再放行端口,或者干脆走云安全组,二选一,别两个都不管,不然外部访问会被卡死。
5.2 常用日志和状态排查命令
容器跑起来之后,日常排查主要是四个命令:
docker ps -a docker logs --tail 100 容器名 docker inspect 容器名 docker exec -it 容器名 bashdocker ps -a看容器状态。Exited 就是崩溃了,Up 就是正常。docker logs看日志,比如 Redis 或者 PostgreSQL 启动时报错都会打在 stdout 里。docker inspect查看容器的挂载、端口映射、环境变量,排查配置时很有用。docker exec进到容器里面去执行命令,类似 SSH 到服务器内部。
另外宿主机层面的排查,用:
ss -lntp systemctl status dockerss -lntp看端口监听,确认 Docker 的端口映射是否生效。如果端口没监听,往往是容器没起来或者启动后立刻退出了,这时候去查docker ps -a和docker logs。
5.3 容器开机自启动的正确姿势
我启动 Redis 和 PostgreSQL 时都加了--restart=always,这个参数的意思是:容器无论因为什么原因退出,Docker 守护进程都会尝试重新启动它。宿主机重启后,Docker 服务会按策略把带有--restart=always的容器拉起来。
如果启动容器时忘了加这个参数,不需要重建容器,用 update 补上即可:
docker update --restart=always redis-server docker update --restart=always pgsql-server还有一个细节:容器内的服务依赖关系。如果你的开始脚本里,Redis 和 PostgreSQL 要求先 Redis 后 PostgreSQL,单靠 Docker 的 restart 策略是不保证启动顺序的。测试环境可以直接在启动脚本里加个比较粗糙的等待,比如先等 5 秒再起 PostgreSQL。正式一点的做法是用 Docker Compose,用depends_on声明依赖关系。虽然今天这篇文章是全命令行的,但后面如果服务多起来,强烈建议切到 Compose。
5.4 从命令行到 Compose 的平滑迁移
当 Redis 和 PostgreSQL 都用 docker run 跑通之后,你会慢慢发现一个问题:几十个参数的启动命令,记不住,也不好维护。这时候就该引入 Docker Compose 了。CentOS 7 上装 Docker Compose v2 可以直接用插件方式,或者拉一个二进制文件,操作很简单,我就不展开了。
用 Compose 管理这套环境,docker-compose.yml核心结构大概是:
version: "3.8" services: redis: image: redis:7.0 container_name: redis-server restart: always ports: - "6379:6379" volumes: - /data/redis/conf/redis.conf:/etc/redis/redis.conf - /data/redis/data:/data command: redis-server /etc/redis/redis.conf pgsql: image: postgres:14 container_name: pgsql-server restart: always environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres123 POSTGRES_DB: testdb ports: - "5432:5432" volumes: - /data/postgres:/var/lib/postgresql/data把上面的内容存成文件,后续管理就是docker compose up -d起服务,docker compose down停服务,比手敲 docker run 命令舒服太多。而且depends_on能解决容器启动顺序问题,日志查看也更集中。
不过今天这篇的重点是先把 docker run 这条链路走通,等你理解了每个参数的含义,再切换到 Compose 就是水到渠成的事,不建议一上来就只用 Compose,那样出了问题反而不知道是哪一层配置错误。
6. 最后分享一点实操体会
这套 CentOS 7 上 Docker 加 Redis 加 PostgreSQL 的组合,我前前后后部署过不下十次。总结下来,掌握几个核心思路就能少踩坑:第一,CentOS 7 老内核决定了 Docker 版本不能盲目追新,锁住 20.10.x 最踏实;第二,所有数据目录必须挂载到宿主机,容器可以随便删,数据不能丢;第三,防火墙、SELinux、时区这类基础环境问题在部署前处理好,比事后排查高效得多。
最后一个小技巧:每次启动容器,尤其是带挂载目录的数据库容器,我强烈建议启动后立刻用docker logs看一眼日志,确认没有权限、路径之类的报错再离开终端。别等第二天来发现容器早就退出了,到时候排查成本翻倍。这套环境跑起来之后,后续可以加 Docker Compose 统一管理,也可以考虑给 PostgreSQL 做主从复制,给 Redis 配哨兵,但那是另一个阶段的事了,先把今天的部署吃透再说。