最近帮某项目组在一台Linux服务器上部署PostgreSQL 12.0,从环境准备到正式接入业务花了大半天。整个过程不算复杂,但因为细节比较多,坑也不少,所以整理一版详细步骤出来。如果你是第一次在Linux上部署PostgreSQL,照着这篇走完,基本能避掉大部分常见问题。文章会覆盖环境选型、yum源安装与源码编译两个方案、数据目录初始化、远程认证配置、基础验证以及我在实际部署中踩到的几个典型坑。安装版本以12.0为例,方法对后续12.x小版本同样适用;系统方面以CentOS 7为主,Ubuntu等发行版我会在差异处单独说明。
1. 选环境与版本:先想清楚再动手
1.1 为什么选12.0而不是追新版本?
很多生产环境跑的还是12或13,原因很简单:经过几年打磨,稳定性和周边生态都验证过了。对于大多数业务系统,12.0的并行查询、分区表增强、索引改进已经完全够用。选版本时我通常看两条:一是业务依赖的第三方扩展是否兼容,二是团队过往运维经验集中在哪个版本。如果没人用过某个太新的版本,上线后遇到问题只能现学,风险反而高。
这篇文章拿12.0当例子,不是说非要装12.0不可。如果你们团队已经在用12.x的小版本,比如12.9、12.15,部署步骤和本文完全一样。版本号后面几位通常只是补丁修复,不会改变initdb、systemd、pg_hba.conf这些核心操作。所以看到“12.0”不要紧张,按后面步骤来就好。
1.2 确认操作系统与基础资源
这次用的是CentOS 7.9,这也是目前比较常见的生产系统。如果你用的是Ubuntu 20.04,部署步骤大同小异,只是包管理工具和init脚本略有差别。动手之前先确认几件事:
- 系统版本:
cat /etc/redhat-release或uname -a,确认是64位系统。 - 磁盘空间:
df -h,建议数据目录所在分区剩余空间至少在10GB以上,具体按业务量预留。 - 内存情况:
free -m,PostgreSQL对内存比较敏感,4GB起步比较舒服;小内存也能跑,但后面参数需要认真调。 - 当前用户权限:是否有root权限或sudo权限。PostgreSQL的数据目录、系统服务管理都需要权限,别把时间浪费在权限不够的报错上。
另外推荐顺手确认一下是否有外网访问权限。yum源安装需要从官方仓库拉包,如果服务器处于内网隔离环境,要走离线RPM包方案,那准备的东西就完全不一样了。先把这个前提搞清楚,后面才不会白忙。
1.3 确认安装方式:yum源还是源码
Linux下安装PostgreSQL主要有两条路径:官方yum源安装RPM包,以及源码编译安装。两条路径我都走通过,差异整理如下:
| 对比项 | yum源安装 | 源码编译 |
|---|---|---|
| 安装速度 | 快,依赖自动解决 | 慢,需要编译时间 |
| 灵活性 | 安装路径固定 | 可自定义prefix |
| systemd管理 | 自带服务脚本 | 需要自己写unit文件 |
| 适合场景 | 绝大多数生产环境 | 定制化要求高的场景 |
| 后续维护 | 升级打补丁方便 | 升级麻烦,要重新编译 |
我的建议是:没有任何特殊要求的情况下,直接用yum源装RPM包最省心。源码编译虽然听起来“可控”,但后续维护成本高,尤其是版本升级时,每次重新编译都要重新踩一遍坑。所以本文主线先讲yum源安装,源码编译只把关键命令列出来备用。
2. 使用yum源快速安装PostgreSQL 12.0
2.1 配置官方YUM源
以CentOS 7为例,PostgreSQL官方提供了一个统一的repo安装包,一行命令就能把源配置好。执行前确认当前是root或者有sudo权限。
yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm这条命令执行后,系统里会多出几个以pgdg开头的repo文件,里面包含了PostgreSQL各个版本、各个组件的软件包来源。如果你用的是CentOS 8或RHEL 8,还要额外执行一条命令,把系统自带的PostgreSQL模块禁用掉,否则安装时容易出现包冲突:
yum -q module disable postgresql然后刷新缓存:
yum makecache这里有个经验之谈:repo包安装后,尽量先看一眼/etc/yum.repos.d/pgdg-redhat-all.repo,确认里面的enabled状态符合预期。默认通常没问题,但有些安全加固的服务器会调整repo文件全局配置,导致后面的包搜不到。排查时先从这里开始。
2.2 安装服务端组件与验证
源配好之后,安装命令很简单:
yum install -y postgresql12-server postgresql12-contribpostgresql12-server是数据库服务端,必装;postgresql12-contrib是扩展组件集,里面包含了pg_stat_statements、postgres_fdw等常用工具,建议一起装上。如果后续要用PostgreSQL地理信息扩展PostGIS,还可以再装postgresql12-postgis,但本文先不展开。
安装完成后,可执行文件被放在/usr/pgsql-12/bin目录下,数据目录默认是/var/lib/pgsql/12/data。注意:RPM安装过程会自动创建系统用户postgres,并且数据目录的属主也会被设置成postgres:postgres。这一刻起,默认就不要用root身份去操作数据库了。
确认版本:
/usr/pgsql-12/bin/postgres --version正常情况下输出postgres (PostgreSQL) 12.x。如果提示找不到命令,检查一下是否安装了postgresql12-server,或者看看/usr/pgsql-12/bin是否在PATH里。这台机器可能同时装了其他PostgreSQL版本,一定要带上路径执行,避免用错二进制。
2.3 如果必须走源码编译:关键命令与区别
少数场景下必须从源码编译,比如需要指定安装路径、静态编译、或者定制某些编译参数。基本流程如下:
# 下载源码包并解压 tar -xjf postgresql-12.0.tar.bz2 cd postgresql-12.0 # 配置、编译、安装 ./configure --prefix=/opt/pgsql12 --with-openssl make -j4 make install--prefix指定安装目录,--with-openssl是为了支持SSL连接。编译完成后,还需要手动创建postgres操作系统用户,把/opt/pgsql12/bin加入PATH,再执行initdb初始化数据目录。如果想让systemd管理,还得自己写一个postgresql-12.service文件。
我在刚开始接触PostgreSQL时也试过源码编译,当时的感受是:不是说这条路多难,而是编译出来的版本没有官方RPM那种“开箱即用”的稳定性,很多默认配置和目录结构都要自己维护,出问题后网上查到的资料大多基于RPM安装,对不上路径,排查效率很低。所以文章后面都以yum源安装的路径为准。
3. 初始化数据目录:从环境变量到initdb参数
3.1 使用官方初始化脚本
RPM安装完成后,数据目录还是空的,必须先初始化。官方提供了一个简化脚本:
/usr/pgsql-12/bin/postgresql-12-setup initdb这个脚本本质上是以postgres用户身份执行initdb -D /var/lib/pgsql/12/data,同时把数据目录权限和属主都设置好。执行完会看到类似这样的输出:
Initializing database ... OK如果看到OK,说明数据目录已经生成。此时/var/lib/pgsql/12/data下面会出现base、global、pg_wal、postgresql.conf、pg_hba.conf等目录和文件。pg_wal就是WAL日志目录,在PostgreSQL 12里已经改了这个名字,对应旧版本的pg_xlog,做目录规划时要记清楚。
如果你不喜欢官方脚本,也可以手动初始化:
sudo -u postgres /usr/pgsql-12/bin/initdb -D /var/lib/pgsql/12/data手动执行时建议先导出环境变量,避免后续操作时路径混乱:
export PGDATA=/var/lib/pgsql/12/data export PATH=/usr/pgsql-12/bin:$PATH3.2 initdb常用参数怎么选
手动初始化时,我会加上这几个参数:
sudo -u postgres /usr/pgsql-12/bin/initdb \ -D /var/lib/pgsql/12/data \ -E UTF8 \ --locale=en_US.UTF-8 \ -U postgres \ -W各参数作用如下:
| 参数 | 作用 | 说明 |
|---|---|---|
-D | 指定数据目录 | 必须和启动服务时保持一致 |
-E | 默认编码 | 生产环境建议UTF8,避免中文乱码 |
--locale | 排序规则 | 需要确认系统已安装对应locale |
-U | 超级用户名 | 默认postgres,不建议改名 |
-W | 提示设置密码 | 初始化时顺手把超级用户密码设好 |
如果服务器系统语言不是英文环境,比如是zh_CN.UTF-8,--locale也可以写成对应值。需要提前用locale -a查看系统已支持的locale列表。有一个常见问题:系统本身没安装en_US.UTF-8locale,强行指定会在initdb阶段直接报错,这时候要么换一个已存在的locale,要么先安装对应的语言包。
另外,命令最后还可以追加--auth-local=peer --auth-host=scram-sha-256,这样初始化出来的认证配置更安全。不过我们后面还会集中改认证规则,这里先不纠结。
3.3 启动服务并设置开机自启
初始化完成后,用systemd启动:
systemctl start postgresql-12 systemctl enable postgresql-12 systemctl status postgresql-12正常状态会显示active (running)。如果启动失败,第一件事不是反复尝试,而是看日志:
journalctl -u postgresql-12 -n 50日志通常会直接告诉你原因:数据目录没初始化、端口被占用、权限不对、PGDATA路径不对等等。我在实际部署中遇到过几次启动失败,基本都是数据目录权限被之前的解压或复制操作搞乱了,重新用chown -R postgres:postgres /var/lib/pgsql/12修正后就能起来。
3.4 数据目录权限问题小记
PostgreSQL对数据目录权限很敏感,目录属主必须是非root用户,通常是postgres。如果目录权限变成777,或者属主是root,服务会直接拒绝启动。很多人喜欢用root解压包、复制数据目录,这一步最容易埋雷。
正确做法是,数据目录创建和初始化全程都通过sudo -u postgres来执行,不要直接切到root下去初始化。如果已经出了问题,修复命令也很简单:
chown -R postgres:postgres /var/lib/pgsql/12 chmod 700 /var/lib/pgsql/12/data然后再启动服务。这个细节看着不起眼,但生产环境处理过不止一次,值得单独记一笔。
4. 认证配置与远程访问:pg_hba.conf和postgresql.conf是重头戏
4.1 先备份配置文件再动手
PostgreSQL的两个核心配置文件都在数据目录下:
/var/lib/pgsql/12/data/postgresql.conf/var/lib/pgsql/12/data/pg_hba.conf
改任何配置之前,先备份是一个好习惯。哪怕只是加一行,也要让你能随时回滚:
sudo cp /var/lib/pgsql/12/data/postgresql.conf /var/lib/pgsql/12/data/postgresql.conf.bak sudo cp /var/lib/pgsql/12/data/pg_hba.conf /var/lib/pgsql/12/data/pg_hba.conf.bak这个习惯在服务器交接、多人协作时特别重要。我见过不少环境,配置被人改乱了,只知道某个时间点之前还是好的,但手里没有备份,只能靠记忆和文档逐步恢复,非常被动。
4.2 修改postgresql.conf的监听与端口
数据库默认只监听localhost,外部无法访问。要允许远程连接,需要修改postgresql.conf中的监听地址。用文本编辑器打开配置文件,找到这一行:
#listen_addresses = 'localhost'改成:
listen_addresses = '*' port = 5432*表示监听所有网络接口,最简单。如果只想监听特定内网IP,也可以写逗号分隔的地址列表,比如:
listen_addresses = '127.0.0.1,192.168.1.100'改完postgresql.conf后需要重启数据库才能生效:
systemctl restart postgresql-12重启前建议先执行postgres -C listen_addresses或者用pg_ctl检查配置是否有语法错误。PostgreSQL对配置文件的语法要求不算严格,但写错了可能会导致服务起不来,重启前检查能省一轮折腾。
4.3 用pg_hba.conf控制谁能连
pg_hba.conf是访问控制的核心文件,它决定哪个IP、哪个用户、用哪种认证方式可以连哪个数据库。规则是“从上到下匹配,匹配后立即停止”。每一行格式如下:
连接类型 数据库 用户名 客户端地址 认证方法常用规则举例:
# 本地socket连接,用peer认证 local all postgres peer # 允许内网192.168.1.0/24网段用密码认证 host all all 192.168.1.0/24 scram-sha-256 # 允许指定IP连接指定库 host appdb app_login 10.0.0.5/32 scram-sha-256peer认证是指数据库用户和Linux系统用户同名时,直接以系统用户身份连接,不需要密码。scram-sha-256是PostgreSQL 12默认推荐的密码加密方式,比md5更安全。虽然连接规则里也可以写md5,但我建议新部署一律用scram-sha-256。
规则顺序很重要。前面写了拒绝规则,后面写允许规则也不会生效。所以需要新增网段授权时,尽量把允许规则放在文件靠前位置,或者明确放在其他规则之前。改完pg_hba.conf不用重启数据库,执行reload就行:
systemctl reload postgresql-124.4 给超级用户设置密码并创建业务账号
本地连接时,postgres用户可以靠peer认证直接登录,不需要密码。但远程连接用scram-sha-256认证时,必须有密码。所以初始化完一定要设置密码:
sudo -u postgres psql进入psql后执行:
ALTER USER postgres PASSWORD '这里填强密码';强烈建议不要直接用超级用户跑业务。创建专用账号和数据库更合理:
CREATE ROLE app_login LOGIN PASSWORD 'app_password'; CREATE DATABASE appdb OWNER app_login;这样应用连接时只用app_login连接appdb,权限控制更清晰。以后即使应用密码泄露,影响范围也被限制在单个库里。如果同一台机器上要跑多个应用,就建多个角色、多个库,互不干扰。
5. 部署完成后的验证:从命令行到几个常用检查
5.1 本地连接与版本确认
服务启动后,第一步是从本地连进去,确认基本功能正常:
sudo -u postgres psql如果看到postgres=#提示符,就说明服务正常、socket连接没问题。执行几个基础命令确认环境信息:
SELECT version(); SHOW server_version; SHOW port; SHOW max_connections;max_connections默认通常是100,小应用够用;高并发场景要结合内存调大,但不能无限调,后面会提到内存估算。SHOW port能看到当前监听端口,如果这里显示的跟postgresql.conf里设置的不一致,就要检查是否有多个配置来源。
5.2 建表读写验证
为了确认数据文件、WAL写入、查询器都能正常工作,快速建一张测试表做闭环验证:
CREATE TABLE test( id serial primary key, name text, created_at timestamp default now() ); INSERT INTO test(name) VALUES ('hello'); SELECT * FROM test;能正常插入和查询,基本说明这套部署是可用的。如果建表时提示权限不足,多半是当前角色不是数据库owner,或者没有schema权限。用app_login连接时,默认在publicschema下建表通常没问题;但生产库建议在初始化数据库后把public权限收一收,只给业务账号必要的权限。
5.3 查询当前连接和锁状态
部署完成后,还要确认服务端拿到的连接是不是预期来源。执行这条SQL:
SELECT datname, usename, client_addr, state FROM pg_stat_activity WHERE datname = 'appdb';可以看到当前哪些客户端IP在连接,连接状态是active还是idle。如果出现大量idle in transaction连接,后面就要关注应用是否有长事务。这个命令在运维排查时很常用,建议记下来,不要每次都翻文档。
5.4 快速保存一份基线配置
第一次把配置调稳之后,建议导出一份配置基线存到/etc或者运维平台上:
sudo cp /var/lib/pgsql/12/data/postgresql.conf /etc/postgresql-12.conf.bak sudo cp /var/lib/pgsql/12/data/pg_hba.conf /etc/pg_hba-12.conf.bak这样做的好处是,以后只要发现线上配置被人改乱,可以直接用这份基线对比,快速定位哪些参数偏离了默认值。我在多个环境里都遇到过配置漂移:管理员调过一两行参数,后来没人记得,半年后排查性能问题才发现某个参数设置得不合理。有基线存档,这个坑就能轻松绕开。
6. 部署过程中最容易踩的坑与排查思路
6.1 systemctl start postgresql-12一直失败
这个坑出现频率最高。现象是:
Job for postgresql-12.service failed because the control process exited with error code.原因通常有三个。第一,数据目录没有初始化,解决办法是回到第3章执行postgresql-12-setup initdb。第二,数据目录权限不对,解决办法是chown -R postgres:postgres /var/lib/pgsql/12,再chmod 700数据目录。第三,端口5432被占用,用ss -lntp | grep 5432看一下。
排查这类问题,最高效的手段是日志:
journalctl -u postgresql-12 -n 50日志会直接给出根因,不需要猜。有些朋友遇到服务起不来,第一反应是反复systemctl restart,其实每次重启只会打印同样的错误,问题根本没有解决。先看日志,再动手,是运维最基本的工作习惯。
6.2 远程连接失败的四种原因与排查链路
远程连PostgreSQL失败,常见表现和原因可以按顺序排查:
| 现象 | 可能原因 | 检查位置 |
|---|---|---|
| 连接被拒绝 | 服务未监听或者防火墙拦截 | ss -lntp、postgresql.conf |
| 连接超时 | 网络不通或云安全组未放行 | ping、telnet、安全组规则 |
| 要求密码但密码正确也不对 | 认证方式或密码不一致 | pg_hba.conf、password_encryption |
| 提示no pg_hba.conf entry | 缺少匹配规则或顺序不对 | pg_hba.conf |
如果是Connection refused,优先看服务监听:
ss -lntp | grep 5432再看防火墙。CentOS 7默认用firewalld:
firewall-cmd --list-ports firewall-cmd --permanent --add-port=5432/tcp firewall-cmd --reload如果服务器在云平台,还需要去控制台确认安全组是否放行了5432入方向。很多远程连不上的问题,不是PostgreSQL本身配置错了,而是安全组或者防火墙把端口挡了。按“客户端到服务端链路”一层层查,比盲目改配置文件快得多。
6.3 别用trust,认证方式要和网络环境匹配
PostgreSQL支持trust认证,意思是“只要网络能到这个端口,不需要密码就能连”。这个选项在单机本地的开发环境可能觉得很方便,但一旦暴露到网络,风险非常大。不要因为测试环境赶时间就用trust,后面忘了改,线上被扫描到就麻烦了。
我这里遇到过一个印象很深的案例:某开发环境把pg_hba.conf里一行改成了trust,结果当天就被内网扫描工具连上,把整张表都清空了。虽然数据有备份,但恢复过程折腾了大半天。所以在任何环境里,认证方式都建议至少用scram-sha-256,密码也要设强一点。
6.4 内存参数随手调:从默认值到可运行
部署完如果发现查询慢,不一定先加硬件。先看几个常用内存参数:
| 参数 | 默认值 | 建议值 |
|---|---|---|
shared_buffers | 128MB | 物理内存的1/4,不超过8GB |
effective_cache_size | 4GB | 接近操作系统可用缓存 |
work_mem | 4MB | 排序运算可适当调高,考虑并发 |
maintenance_work_mem | 64MB | 建索引、vacuum时使用 |
shared_buffers不是越大越好,太大反而增加维护成本;work_mem乘以最大并发数可能爆内存,要整体估算。我曾在2G内存的云主机上把shared_buffers调到512MB,结果触发OOM,后来调回256MB才稳定。调参一定要结合free -m看总内存,特别是小内存机器,先保守再调高。
6.5 备份策略至少要有一个
部署完成到最后一步,备份策略不能落下。最简单的是逻辑备份:
/usr/pgsql-12/bin/pg_dump -h 127.0.0.1 -U app_login -d appdb -F c -f appdb.dump恢复时用:
/usr/pgsql-12/bin/pg_restore -d appdb appdb.dumppg_dump适合中小库,备份和恢复都比较直观。pg_basebackup适合做物理备份和流复制,可以做时间点恢复(PITR),但配置相对复杂,这里不展开。无论如何,数据无价,不要把部署完成的瞬间当成结束。