PostgreSQL 14 源码编译安装实战:RHEL 8 离线环境从零到可用

面向需要在生产服务器上自行编译部署 PostgreSQL 的场景,包含离线环境依赖处理、编译参数含义、初始化细节和十个高频报错的处置方式。全部命令在 RHEL 8.10 x86_64 上实测通过。

一、为什么选择源码编译

PostgreSQL 在 Linux 上常见三种安装方式,先看清楚差异再决定。

方式 来源 版本可控 安装路径 适用场景
官方仓库 rpm yum install postgresql-server 跟随仓库版本 固定,分散在多处 有外网、不挑版本
第三方仓库(PGDG) yum install postgresql14-server 可选大版本 固定 有外网、需要指定大版本
源码编译 下载 tar.gz 自行编译 精确到小版本 完全自定义 离线环境、需要指定次版本、需要追加编译参数

源码编译的核心价值在于版本与路径完全自主:项目要求部署 14.23 就必须是 14.23,不能是仓库里的 14.22 或 14.24;程序装在哪个目录、端口默认值是多少,也都能在编译时固定下来。代价是需要自己处理依赖、自己准备服务脚本、升级时要重新编译。

如果环境有外网且不挑次版本,直接用仓库安装更省事。本文的场景是企业内网离线环境,必须从源码构建。

二、环境准备

2.1 系统检查

复制代码
cat /etc/redhat-release
uname -m
nproc
free -h
df -h /data /usr/local

预期输出示例:

复制代码
Red Hat Enterprise Linux release 8.10 (Ootpa)
x86_64
16
              total        used        free      shared  buff/cache   available
Mem:           31Gi       2.1Gi        27Gi       0.0Gi       1.6Gi        28Gi

要点:

  • 架构必须是 x86_64,源码包按平台区分,arm 需要另外下载对应版本
  • nproc 返回的核数后面编译时要用,16 核编译 PostgreSQL 约 3 到 5 分钟,单核要 20 分钟以上
  • 磁盘空间:编译过程产生大量中间文件,数据目录与安装目录所在分区各留 2G 以上

2.2 离线环境处理依赖

离线环境最大的障碍是装不了编译依赖。解决办法是用系统安装 ISO 做本地 yum 源,这是内网部署的标准做法。

先确认 ISO 是否挂在光驱上:

复制代码
lsblk
blkid /dev/sr0

blkid 能输出文件系统类型和卷标,说明光驱有介质。如果提示找不到媒体,需要在虚拟化平台把 ISO 挂载并勾选「已连接」,光驱里的介质识别不到时 lsblk 的 SIZE 列可能显示为 0,不可靠,以 blkid 为准。

挂载并配置本地源:

复制代码
mkdir -p /mnt/iso
mount /dev/sr0 /mnt/iso
ls /mnt/iso

能列出 BaseOS 和 AppStream 两个目录即挂载成功。挂载时出现「写保护,将以只读方式挂载」的提示属正常,光盘本身就是只读的。

复制代码
printf '[Local-BaseOS]\nname=Local-BaseOS\nbaseurl=file:///mnt/iso/BaseOS\nenabled=1\ngpgcheck=0\n[Local-AppStream]\nname=Local-AppStream\nbaseurl=file:///mnt/iso/AppStream\nenabled=1\ngpgcheck=0\n' > /etc/yum.repos.d/local-iso.repo

RHEL 8 的安装介质把软件包拆在 BaseOS 和 AppStream 两个仓库里,两个都要配。写成 .repo 文件而不是直接改 /etc/yum.repos.d/redhat.repo,是为了不覆盖系统原有配置。

三、安装编译工具链

3.1 四个必需依赖

复制代码
dnf install -y gcc make readline-devel zlib-devel
包 作用 缺了会怎样
gcc C 编译器 configure 报 no acceptable C compiler found in $PATH,直接中断
make 构建工具 configure 通过但编译无法开始
readline-devel 命令行编辑库 能编译,但 psql 里上下键翻不了历史命令、左右键不能编辑,且没有自动补全
zlib-devel 压缩库 能编译,但 pg_dump 无法输出压缩格式,备份体积翻几倍

后两个是「缺了不报错、但装出来不好用」的类型,configure 遇到可选依赖缺失会自动降级,不会中断。这一点下面还会专门讲。

RHEL 未注册订阅时,执行 dnf 会提示订阅相关警告,本地源照样可用,可以忽略。如果 dnf 仍然尝试联网,强制指定仓库:

复制代码
dnf --disablerepo=* --enablerepo=Local-BaseOS --enablerepo=Local-AppStream install -y gcc make readline-devel zlib-devel

3.2 验证

复制代码
gcc --version | head -1
make --version | head -1
rpm -q readline-devel zlib-devel

预期:

复制代码
gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21)
GNU Make 4.2.1
readline-devel-8.0-4.el8.x86_64
zlib-devel-1.2.11-25.el8.x86_64

四、获取并校验源码包

PostgreSQL 源码在官方 ftp 站点发布:

复制代码
https://ftp.postgresql.org/pub/source/v14.24/postgresql-14.24.tar.gz

把版本号换成需要的版本即可。该目录下同时有 .md5 文件和 .sha256 文件,一并下载用于校验。

上传到服务器后先算校验值:

复制代码
cd /root
md5sum postgresql-14.24.tar.gz

与官方 md5 文件逐位比对,完全一致才继续。这一步不能省:安装包在跨网传输、U 盘拷贝过程中损坏并不罕见,损坏的包可能编译中途报出难以理解的错误,也可能编译成功但运行时行为异常,排查成本远高于十秒的校验。

复制代码
cat postgresql-14.24.tar.gz.md5

五、configure 参数详解

这一步是整个安装过程中最容易出错的环节,参数写错会导致程序装到错误位置,或者运行时找不到配置文件。

5.1 三个核心参数

复制代码
tar -xzf postgresql-14.24.tar.gz -C /opt
cd /opt/postgresql-14.24
./configure --prefix=/usr/local/pgsql --with-pgport=5432 --datadir=/usr/local/pgsql/share
参数 含义 影响
--prefix 安装根目录 程序装到 $prefix/bin,库到 $prefix/lib,头文件到 $prefix/include
--with-pgport 默认端口 未在 postgresql.conf 中显式指定端口时使用此值
--datadir 只读数据目录 时区文件、系统视图定义、全文检索词典、postgres.bki 等不随数据变化的文件

5.2 关于 datadir 的一个常见误解

很多人以为 --datadir 指定的是数据库数据目录(存放 base、global、pg_wal 的那个目录),其实不是。

翻 configure 的 --help 可以确认:

复制代码
./configure --help | grep -A1 "datadir"

  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]

注意 read-only 这个词。它指的是只读数据 ,也就是 share 目录下的内容,不含任何业务数据。

数据库数据目录(PGDATA)由初始化命令 initdb -D 指定,与编译参数无关。这一点在实际部署中会造成一个很隐蔽的问题:如果把 --datadir 指向了数据目录的路径,那么 make install 会以 root 身份把时区文件写进数据目录,而这些文件属主变成 root,数据库进程以 postgres 身份运行读不了,启动时直接报错退出。

这个坑在生产环境上出过一次,排查花了不少时间。后面升级那篇文章会详细讲怎么处理。

默认不指定 --datadir 时,它等于 $prefix/share,多数场景用默认值即可。

5.3 如何反查已有环境的编译参数

接手别人的环境、或者要对齐现有配置时,不能靠猜。两种查法:

复制代码
# 方法一:看已有安装的 pg_config
/usr/local/pgsql/bin/pg_config --configure

# 方法二:如果是编译源码树还在,看生成的 Makefile
grep -E "^(prefix|datadir|DEF_PGPORT) " src/Makefile.global

第一条会直接打印出当初 configure 时的完整参数列表,最可靠。如果旧环境已经没有源码树,这是唯一的途径。

5.4 判断 configure 是否成功

configure 的输出有几百行,不需要逐行看。只看结尾:

复制代码
config.status: creating GNUmakefile
config.status: creating src/Makefile.global
config.status: creating src/include/pg_config.h
config.status: linking src/makefiles/Makefile.linux to src/Makefile.port

出现这几行且正常回到提示符,就是成功了。全文出现 configure: error: 才是失败。

输出里的 no 大多不用管:

复制代码
checking for bison... no
checking for flex... no
checking for g++... no
checking for ICU... no
checking for LDAP... no
checking for PAM... no

bison、flex 在 bison 缺失时 configure 会打一行 WARNING,原文就写了官方发布包已预生成相关文件,不需要这两个工具。g++ 只影响 C++ 接口,不影响 C 核心。ICU、LDAP、PAM 是可选功能,没装就没有对应能力,不影响基本运行。

5.5 两个层次的判断

这里需要区分两个概念,很多人会混淆:

  • 退出码回答「能不能往下走」。致命项(缺 gcc、参数写错、库冲突)会让 configure 中断并返回非 0。
  • pg_config.h 里的宏回答「装出来好不好用」。缺 readline、缺 zlib 时 configure 照样返回 0,只是对应功能没编进去。

所以配置完成后,除了看退出码,还应检查关键宏:

复制代码
grep -E "HAVE_LIBREADLINE|HAVE_LIBZ|USE_OPENSSL" src/include/pg_config.h

预期:

复制代码
#define HAVE_LIBREADLINE 1
#define HAVE_LIBZ 1
/* #undef USE_OPENSSL */

前两个是 1 说明 readline 和 zlib 已编入;USE_OPENSSL 被注释掉说明没编 SSL 支持,如果需要加密连接,要先装 openssl-devel 再带 --with-openssl 重新配置。

记法:退出码管能不能走,宏文件管好不好用。

六、编译与安装

6.1 编译

复制代码
make -j$(nproc); echo "退出码=$?"

-j$(nproc) 按 CPU 核数并发编译。16 核约 3 到 5 分钟,单核 20 分钟以上,差距明显。

输出量很大,几万行滚动是正常的。不要试图在输出里找成功提示 ,PostgreSQL 14 的 Makefile 并不会打印类似 All of PostgreSQL successfully made 这样的总结语(这句话在部分文档和旧版本教程里出现,实际 14.x 没有)。唯一可靠的判断是退出码:

  • 退出码=0 表示成功
  • 其他数字表示失败

编译失败时定位报错:

复制代码
make -j$(nproc) 2>&1 | grep -n "error:" | head -20

6.2 确认编译产物

复制代码
src/backend/postgres --version

postgres (PostgreSQL) 14.24

注意这条查的是源码目录里刚编出来的程序,还没有安装到系统。显示正确版本只说明编译成功,不代表系统已经升级或装好了。

6.3 安装

复制代码
make install; echo "退出码=$?"

退出码为 0 即成功。安装过程把程序写入 --prefix 指定目录,把只读数据写入 --datadir 指定目录,同名旧文件被覆盖。

安装完成后确认系统目录里的程序版本:

复制代码
/usr/local/pgsql/bin/postgres --version

6.4 登记共享库

复制代码
echo /usr/local/pgsql/lib > /etc/ld.so.conf.d/pgsql.conf
ldconfig

作用是让系统动态链接器能找到 libpq.so 等库文件。省略这一步,自行编译并链接 PostgreSQL 库的程序在运行时会报找不到 libpq.so。两条命令都无输出,安静返回即为成功。

验证:

复制代码
ldconfig -p | grep libpq

能列出 libpq.so.5 及其路径,说明登记生效。

七、初始化数据库实例

7.1 创建运行账号

PostgreSQL 拒绝以 root 身份运行,必须有一个普通账号承载数据库进程:

复制代码
groupadd postgres
useradd -g postgres postgres
id postgres

预期:

复制代码
uid=1001(postgres) gid=1001(postgres) 组=1001(postgres)

7.2 创建数据目录并赋权

复制代码
mkdir -p /data/pgdata
chown postgres:postgres /data/pgdata
chmod 700 /data/pgdata
ls -ld /data/pgdata

预期:

复制代码
drwx------. 2 postgres postgres 6 10月 21 10:12 /data/pgdata

权限必须是 700。PostgreSQL 初始化时会检查数据目录权限,权限过宽会直接拒绝执行,这是安全设计:数据目录里存放的是全部业务数据,不能允许其他用户读取。

如果父目录 /data 是 root 创建的,postgres 无法在下面新建子目录。此时由 root 先建好 pgdata,再把属主交给 postgres,不要试图给 /data 放宽权限。

7.3 初始化

复制代码
su postgres -c "/usr/local/pgsql/bin/initdb -D /data/pgdata"

su postgres 后面不带横杠,这一点有讲究:

  • su postgres:切换身份但保留当前环境变量,PATH 里含 $prefix/bin,可直接调 initdb
  • su - postgres:加载 postgres 的登录环境,PATH 重置为默认值,不含 $prefix/bin,需要写全路径

两种写法都能用,但混着用容易出错。本文统一用带横杠的 su - postgres -c "..." 或 su postgres -c "..." 加全路径,避免依赖环境变量。

成功输出结尾:

复制代码
Success. You can now start the database server using:

    /usr/local/pgsql/bin/pg_ctl -D /data/pgdata -l logfile start

初始化过程中的一条 WARNING 值得注意:

复制代码
could not find suitable text search configuration for locale "zh_CN.UTF-8"

这是 WARNING 不是错误 ,初始化会继续并成功。原因是中文 locale 下没有匹配的全文检索词典配置,PostgreSQL 自动降级为 simple 配置。影响仅限于中文全文检索(to_tsvector),普通查询、索引、事务都不受影响。如果不需要全文检索功能,可以忽略。

要彻底避免,可以在 initdb 时指定 locale:

复制代码
su postgres -c "/usr/local/pgsql/bin/initdb -D /data/pgdata -E UTF8 --locale=C"

C locale 下全文检索配置为 simple,行为一致但没有告警。注意 locale 一旦确定,后续修改需要重新初始化或重建数据库,初始化前想清楚。

7.4 配置环境变量

不要在 /etc/profile 里直接追加,用独立文件:

复制代码
echo 'export PATH=/usr/local/pgsql/bin:$PATH' > /etc/profile.d/pgsql.sh
chmod 644 /etc/profile.d/pgsql.sh
source /etc/profile

/etc/profile 末尾有循环自动加载 /etc/profile.d/*.sh,等效但更干净:系统文件保持原样,要改要删都只动自己这一文件。

这里有一个高频错误:把 PATH 写成源码目录。

复制代码
export PATH=/opt/pgsql/bin:$PATH     # 错误:/opt/pgsql 是解压目录,下面没有 bin
export PATH=/usr/local/pgsql/bin:$PATH   # 正确:$prefix/bin

源码解压目录(如 /opt/postgresql-14.24)里确实有一个 src/bin,但没有 bin。写成解压目录的 PATH 会出现「psql 找不到命令,但文件确实装好了」的怪现象。

另一个错误是漏了 :$PATH:

复制代码
export PATH=/usr/local/pgsql/bin       # 错误:整个 PATH 被覆盖,ls、vi 都无法执行

漏掉 $PATH 后连基本命令都调不到,只能重新登录或临时补回默认 PATH 才能修复。

验证:

复制代码
which psql pg_ctl initdb
psql --version

预期三条都指向 /usr/local/pgsql/bin/,且不带全路径能执行出版本号。

八、启动与验证

复制代码
su postgres -c "/usr/local/pgsql/bin/pg_ctl -D /data/pgdata -l /data/pgdata/logfile start"

预期:

复制代码
waiting for server to start.... done
server started

验证三连:

复制代码
su postgres -c "/usr/local/pgsql/bin/pg_ctl -D /data/pgdata status"
su postgres -c "/usr/local/pgsql/bin/psql -c 'select version();'"
ss -lntp | grep 5432

预期:

复制代码
pg_ctl: server is running (PID: 12345)
PostgreSQL 14.24 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514, 64-bit
LISTEN 0 244 127.0.0.1:5432 0.0.0.0:*

三条都对,说明实例已可用。

停止:

复制代码
su postgres -c "/usr/local/pgsql/bin/pg_ctl -D /data/pgdata stop"

预期 waiting for server to shut down.... done 与 server stopped。

8.1 关于开机自启

源码编译装出来的实例没有开机自启 ,也没建 systemd 单元。服务器重启后数据库是停的,需要手动 pg_ctl start。

要在生产环境上做成服务,新建单元文件:

复制代码
cat > /etc/systemd/system/postgresql.service <<'EOF'
[Unit]
Description=PostgreSQL database server
After=network.target

[Service]
Type=forking
User=postgres
Group=postgres
Environment=PGDATA=/data/pgdata
ExecStart=/usr/local/pgsql/bin/pg_ctl -D /data/pgdata -l /data/pgdata/logfile start
ExecStop=/usr/local/pgsql/bin/pg_ctl -D /data/pgdata stop
ExecReload=/usr/local/pgsql/bin/pg_ctl -D /data/pgdata reload

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now postgresql.service
systemctl status postgresql.service

注意 Type=forking:pg_ctl start 是 fork 出后台进程后自己退出,如果写成 Type=simple,systemd 会以为服务已退出而反复重启。

如果实例已用 pg_ctl 启动过,先 pg_ctl stop 再 systemctl start,否则 systemd 找不到它派生的进程,状态显示 inactive 但端口实际在监听,容易误判。

不要混用两种启停方式。用 systemctl 起的就用 systemctl 停,用 pg_ctl 起的就用 pg_ctl 停。混用会导致 systemd 记录的状态与实际不符。

九、十个常见问题

9.1 configure 报 no acceptable C compiler found in $PATH

缺 gcc。RHEL 8 最小化安装不含编译器。

复制代码
dnf install -y gcc

离线环境先按第 2.2 节挂 ISO 配本地源。装完不需要清理,./configure 失败不留残件,直接重跑即可。

9.2 mount /dev/sr0 报「找不到媒体」

虚拟化平台的光驱没有连接介质。在 vSphere 或类似平台上打开虚拟机设置,选择 ISO 文件后勾选「已连接」(Connected),只挂载不连接是识别不到的。

lsblk 显示 /dev/sr0 的 SIZE 为 0 或 1024 都不代表没介质,用 blkid /dev/sr0 确认:

复制代码
blkid /dev/sr0
# 有介质时输出类似:/dev/sr0: UUID="2023-11-08-..." LABEL="RHEL-8-10-0-BaseOS-x86_64" TYPE="iso9660"

9.3 装完 psql 提示 command not found

两种可能:PATH 没配,或 PATH 配错路径。

复制代码
echo $PATH
which psql
ls /usr/local/pgsql/bin/psql

如果 ls 能看到文件而 which 找不到,就是 PATH 问题。检查 /etc/profile.d/pgsql.sh 内容,改完记得 source /etc/profile。

已经开着的终端不会自动加载新配置,改完必须 source 或重新登录,否则会误判成配置没生效。

9.4 报错 FATAL: role "root" does not exist

用 root 身份连库时出现。PostgreSQL 本地 socket 连接默认把当前系统用户名当作数据库角色名,root 不是数据库里的角色。

两个解法:

复制代码
# 解法一:显式指定角色
psql -U postgres -c "select version();"

# 解法二:切到 postgres 用户再执行
su postgres
psql -c "select version();"

所有 psql、pg_dump、pg_dumpall 命令都建议带 -U postgres,不受当前登录身份影响。

9.5 报错 FATAL: could not create shared memory segment

共享内存不足,多为 shared_buffers 设置过大,或容器环境限制了 shm 大小。

复制代码
ipcs -lm | head -5
sysctl kernel.shmmax kernel.shmall
df -h /dev/shm

容器环境中 --shm-size 默认 64M,需要启动时调大:--shm-size=1g。物理机或虚拟机上调 kernel.shmmax 即可。

9.6 启动报错 could not open file ".../timezone/Asia/Shanghai"

时区文件属主不是 postgres。典型成因是以 root 身份执行了 make install,文件属主变成 root。

复制代码
find / -name "timezone" -type d 2>/dev/null
chown -R postgres:postgres /usr/local/pgsql/share/timezone
chown -R postgres:postgres /usr/local/pgsql/share/timezonesets

用 -R 递归修正,只改目录本身不够,里面按洲和城市分层的文件仍是 root 属主。

9.7 initdb 报 permission denied

数据目录属主不对或权限过宽。

复制代码
ls -ld /data/pgdata
chown postgres:postgres /data/pgdata
chmod 700 /data/pgdata

权限必须是 700,755 或 775 都会被拒绝。

9.8 psql 里上下键出现 ^[[A

readline 没编进去。检查宏:

复制代码
grep HAVE_LIBREADLINE /usr/local/pgsql/include/pg_config.h 2>/dev/null || \
  grep HAVE_LIBREADLINE src/include/pg_config.h

如果是 /* #undef HAVE_LIBREADLINE */,说明编译时缺 readline-devel。装上后必须 make distclean 清除再重新 configure,直接重跑 configure 可能沿用之前的缓存结果。

9.9 服务器重启后数据库没起来

源码编译的实例没有开机自启。配置 systemd 单元,见 8.1 节。临时先手动起:

复制代码
su postgres -c "/usr/local/pgsql/bin/pg_ctl -D /data/pgdata -l /data/pgdata/logfile start"

养成习惯:每次登录服务器先查状态。

复制代码
su postgres -c "/usr/local/pgsql/bin/pg_ctl -D /data/pgdata status"

9.10 编译退出码非 0,报错看不懂

先筛出报错行:

复制代码
make -j$(nproc) 2>&1 | grep -n "error:" | head -20

高频原因:

报错关键字 原因 处理
cannot find -lreadline 缺 readline-devel 装上后 make distclean 重来
cannot find -lz 缺 zlib-devel 同上
No space left on device 磁盘满 清理后重来
fatal error: openssl/ssl.h 缺 openssl-devel 但带了 --with-openssl 装依赖或去掉该参数
Killed 内存不足被 OOM 去掉 -j,改单线程编译

十、验证清单

安装完成后逐项确认,全部通过才算可用。

复制代码
# 1. 程序版本
/usr/local/pgsql/bin/postgres --version

# 2. 已编入的关键功能
grep -E "HAVE_LIBREADLINE|HAVE_LIBZ" /usr/local/pgsql/include/pg_config.h

# 3. 共享库已登记
ldconfig -p | grep libpq

# 4. 实例运行中
su postgres -c "/usr/local/pgsql/bin/pg_ctl -D /data/pgdata status"

# 5. 服务端版本
su postgres -c "/usr/local/pgsql/bin/psql -c 'select version();'"

# 6. 端口监听
ss -lntp | grep 5432

# 7. 数据目录权限
ls -ld /data/pgdata

# 8. 时区文件属主
ls -ld /usr/local/pgsql/share/timezone

八项全过,环境可用。

写在最后

源码编译安装 PostgreSQL 本身的命令不多,出问题基本集中在四处:离线环境的依赖、configure 参数含义、PATH 配置、文件属主。

四个坑各记一句话:

  • 离线装不了包,先挂 ISO 做本地源
  • --datadir 是只读数据目录,不是 PGDATA
  • PATH 指向 $prefix/bin,不是源码解压目录,且不能漏 :$PATH
  • 属于 postgres 的文件,属主就得是 postgres

装完之后如果要升级到新版本,可以看另一篇《PostgreSQL 小版本升级实战》,讲的是同大版本内替换二进制的完整流程和几个更隐蔽的坑。

相关推荐
ss2731 小时前
AI时代首先数据库-PostgreSQL实战笔记 | 23 PostgreSQL数据库文件系统与数据页结构
数据库·笔记·postgresql
LMT流氓兔1 小时前
第五单元——第10课:用自动化测试验证工具逻辑
数据库·ai·oracle·langchain
小小AK1 小时前
生产入库单同步实战:MySQL 到金蝶云星空的带料委外场景拆解
数据库·mysql
凤凰院凶涛QAQ1 小时前
《MySQL内核拆解:从SQL到InnoDB》(一) : 数据库基础&库操作&数据类型
数据库·sql·mysql
360智汇云1 小时前
隔离VPC标准路径:出公网链路的设计与实现
android·服务器·数据库
紫金桥软件1 小时前
紫金桥国产组态软件:安全稳定护航工业现场
数据库·自动化·跨平台·scada·组态软件
qq_436962181 小时前
金蝶云星空数据分析怎么做?
java·数据库·数据分析
坐吃山猪2 小时前
ChromaDB09-数据库操作
数据库·chromadb
IntoTech英途汇骏2 小时前
数据虚拟化平台选型分析:主流厂商解决方案与技术能力对比
数据库