最近在测试环境要搭一套缓存加关系型数据库的组合顺手把整个流程从零到一完整走了一遍。今天就以 CentOS 7 为底把 Docker 装好再用 Docker 把 Redis 和 PostgreSQL 跑起来。整个过程其实就是一条命令链但中间值得注意的坑不少尤其是 CentOS 7 老内核和 Docker 之间的兼容问题以及数据库容器挂数据和持久化的姿势。这篇文章适合两类人看一类是刚接触 Linux 运维想在 CentOS 7 上快速搭一套环境出来跑项目的同学另一类是已经在用 Docker但每次部署 Redis、PostgreSQL 都要重新查参数、踩一遍老坑的朋友。我会把我实际操作中遇到的所有问题都写出来含命令、配置和排查思路。1. 为什么在 CentOS 7 上用 Docker 装 Redis 和 PostgreSQL1.1 这套组合到底解决了什么问题很多人在测试环境搭服务第一反应是直接yum install redis和yum install postgresql-server。但 CentOS 7 默认仓库里的软件版本实在太老Redis 可能还是 3.xPostgreSQL 可能连 12 都不到。老版本带来的问题不是“不能用”而是你在本地开发用的新特性它没有或者 SQL 行为不一致最后环境差异把问题全掩盖了。用 Docker 直接拉镜像版本跟生产环境对齐就很方便。比如 Redis 7.0、PostgreSQL 14这些版本特性在测试环境验证完生产环境用同一套镜像行为基本一致。而且 Docker 的隔离性也让清理变得很干净容器删掉重来不会像 yum 安装那样留下乱七八糟的配置文件和服务。这套方案最适合的场景是单机开发测试、个人项目、CI 环境的数据库依赖。如果是要做生产环境的高可用集群Docker 单容器是不够的得考虑主从、备份、监控这些额外维度但那是另一篇文章的事。今天这篇先把单机跑通、跑稳。1.2 镜像版本和 Docker 版本要怎么选这一段是很多人忽略的。CentOS 7 的内核默认是 3.10.x非常老。新版本的 Docker 虽然名义上支持 CentOS 7但实际踩坑率不低。我个人的选型是组件推荐版本理由Docker Engine20.10.x 系列对老内核兼容最好稳定Redis 镜像redis:7.0 或 redis:6.27.0 功能新6.2 更保守PostgreSQL 镜像postgres:14 或 postgres:1514 是久经考验的稳定版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 6Docker 20.10 都能装。第二件事是确认外网连通性。Docker 安装要拉 rpm 包启动时要拉镜像没有外网寸步难行。有些 CentOS 7 机器刚装完没有网排查方法是先 ping 网关再 ping 外网 IP最后用nslookup或dig查 DNS。如果 ping 不通百度多半是网络配置文件里没写 DNS 或者网关不对修改/etc/sysconfig/network-scripts/ifcfg-eth0里的GATEWAY和DNS1然后重启网络服务。第三件事是防火墙和 SELinux。测试环境我推荐直接关闭 SELinuxsetenforce 0 sed -i s/^SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config注意setenforce 0是临时生效重启后恢复必须配合sed那行永久修改。生产环境你要评估是否能关但测试环境没必要让 SELinux 给你添堵。2.2 逐步执行 Docker 安装命令CentOS 7 上装 Docker 的经典流程是配置阿里云 yum 源然后用 yum 安装。为什么不用官方源官方源在部分网络环境下访问很慢而且经常出现 metadata 下载超时。阿里云源在国内服务器上速度优势明显。首先卸载系统里可能残留的旧版本 Dockeryum 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 Engineyum 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 命令后面加--nogpgcheckyum 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.jsonmkdir -p /etc/docker cat /etc/docker/daemon.json EOF { registry-mirrors: [ https://docker.m.daocloud.io ], exec-opts: [native.cgroupdriversystemd] } 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.xyum 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 \ --restartalways \ -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给容器起个名后续管理方便--restartalways容器退出时自动重启相当于开机自启-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 --memory512m --memory-swap512m 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 \ --restartalways \ -p 5432:5432 \ -e POSTGRES_USERpostgres \ -e POSTGRES_PASSWORDpostgres123 \ -e POSTGRES_DBtestdb \ -v /data/postgres:/var/lib/postgresql/data \ -v /etc/localtime:/etc/localtime:ro \ postgres:14这里三个环境变量需要说明环境变量作用POSTGRES_USER超级用户名称默认是 postgresPOSTGRES_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 端口规划与防火墙策略上面部署完宿主机上至少有两个对外端口了。规整一下服务容器端口宿主机端口用途Redis63796379缓存、测试数据存储PostgreSQL54325432业务数据库测试环境如果要开放到局域网记得用 firewalld 放行。CentOS 7 的防火墙操作如下firewall-cmd --zonepublic --add-port6379/tcp --permanent firewall-cmd --zonepublic --add-port5432/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 时都加了--restartalways这个参数的意思是容器无论因为什么原因退出Docker 守护进程都会尝试重新启动它。宿主机重启后Docker 服务会按策略把带有--restartalways的容器拉起来。如果启动容器时忘了加这个参数不需要重建容器用 update 补上即可docker update --restartalways redis-server docker update --restartalways 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 配哨兵但那是另一个阶段的事了先把今天的部署吃透再说。
阅读完成 · 觉得有帮助?