Kubernetes 环境下的 PostgreSQL 部署

Kubernetes 环境下的 PostgreSQL 部署

基于官方 postgres 镜像的生产级部署与备份实践


前言

PostgreSQL 作为最强大的开源关系型数据库之一,在企业级应用中扮演着重要角色。将 PostgreSQL 部署在 Kubernetes 环境中,不仅能享受容器化带来的便利,还能利用 Kubernetes 的弹性伸缩和自愈能力。

今天,我将分享一套完整的 PostgreSQL K8s 部署方案,包括 StatefulSet 部署、用户权限管理、以及自动备份策略。


为什么选择 Kubernetes 部署 PostgreSQL?

优势 说明
高可用 Kubernetes 自动重启故障 Pod,保证服务持续运行
弹性伸缩 根据负载动态调整资源配额
版本管理 通过镜像版本轻松实现数据库升级
资源隔离 独立的 Namespace 和资源配额
存储管理 HostPath 或持久卷灵活配置

整体架构一览

先来看一张部署架构图:

PostgreSQL 部署架构图

核心组件解析

1. StatefulSet 控制器

采用 StatefulSet 而非 Deployment,这是 PostgreSQL 作为有状态应用的必然选择:

  • 稳定的网络标识:Pod 名称固定为 postgres-master-0
  • 有序的部署/扩缩容:确保数据完整性
  • 独立的持久存储:每个 Pod 绑定独立的 HostPath

2. hostNetwork 网络模式

spec:
  hostNetwork: true
  hostAliases:
    - ip: "<INTERNAL_IP>"
      hostnames:
        - "postgres-master"

使用 hostNetwork: true 让 PostgreSQL 直接使用宿主机网络,避免端口映射带来的性能损耗。

3. NodePort 外部访问

spec:
  type: NodePort
  ports:
    - port: 5432
      targetPort: 5432
      nodePort: 30032

通过 NodePort 30032 暴露服务,外部客户端可通过 host:<NODE_IP>:30032 访问数据库。

4. 双目录 HostPath 挂载

volumeMounts:
  - name: postgres-lib
    mountPath: /var/lib/postgresql/
  - name: postgres-data
    mountPath: /var/lib/postgresql/data/

分离 lib 目录和数据目录,便于分别管理配置文件和数据文件。


部署配置文件详解

完整的 YAML 编排

apiVersion: v1
kind: Namespace
metadata:
  name: csjg-postgres

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: postgres-master-out
  name: postgres-master-out
  namespace: csjg-postgres
spec:
  type: NodePort
  ports:
    - port: 5432
      targetPort: 5432
      nodePort: 30032
  selector:
    app: postgres-master

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: postgres-master
  name: postgres-master
  namespace: csjg-postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres-master
  template:
    metadata:
      labels:
        app: postgres-master
    spec:
      hostNetwork: true
      hostAliases:
        - ip: "<INTERNAL_IP>"
          hostnames:
            - "postgres-master"
      containers:
        - name: postgres-master
          image: postgres:10.20-alpine
          imagePullPolicy: IfNotPresent
          env:
            - name: POSTGRES_PASSWORD
              value: "********"
            - name: ALLOW_IP_RANGE
              value: "0.0.0.0/0"
            - name: TZ
              value: "Asia/Shanghai"
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-lib
              mountPath: /var/lib/postgresql/
            - name: postgres-data
              mountPath: /var/lib/postgresql/data/
          resources:
            limits:
              cpu: 3
              memory: 30Gi
            requests:
              cpu: 100m
              memory: 2Gi
      volumes:
        - name: postgres-lib
          hostPath:
            path: /home/postgres/data/
        - name: postgres-data
          hostPath:
            path: /home/postgres/data/data/
      nodeSelector:
        postgres-server: master

关键配置说明

配置项 说明
postgres:10.20-alpine Alpine 精简镜像,体积小、安全性高
POSTGRES_PASSWORD 数据库超级用户密码
ALLOW_IP_RANGE 允许访问的 IP 段
resources CPU/内存限制,防止资源滥用
nodeSelector 调度到专用数据库节点

部署实战

部署流程图

PostgreSQL 部署流程

详细部署步骤

步骤一:节点准备

# 查看当前节点及其标签
kubectl get node --show-labels=true

# 给专用节点添加标签
kubectl label nodes <NODE_NAME> postgres-server=master

步骤二:创建数据目录

# 在目标节点上创建数据目录
mkdir -p /home/postgres/data/data

# 确保权限正确
chmod -R 777 /home/postgres

步骤三:部署资源

# 一键部署所有资源
kubectl apply -f postgres.yaml

# 查看 Pod 状态
kubectl get pod -n csjg-postgres -o wide

# 查看日志
kubectl logs -f postgres-master-0 -n csjg-postgres

用户权限管理

数据库安全至关重要,我们需要创建不同角色的用户:

创建只读用户

-- 创建只读账号
CREATE USER readonly_user WITH PASSWORD '********';

-- 设置只读事务模式
ALTER USER readonly_user SET default_transaction_read_only = on;

-- 授予连接权限
GRANT CONNECT ON DATABASE <DATABASE_NAME> TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;

创建读写用户

-- 创建读写账号
CREATE USER readwrite_user WITH PASSWORD '********';

-- 授予数据库级权限
GRANT ALL PRIVILEGES ON DATABASE <DATABASE_NAME> TO readwrite_user;

-- 授予 Schema 权限
GRANT ALL PRIVILEGES ON SCHEMA public TO readwrite_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO readwrite_user;

-- 如需超级权限(谨慎使用)
ALTER USER readwrite_user WITH SUPERUSER;

密码重置

-- 重置用户密码
ALTER USER <USER_NAME> WITH PASSWORD '********';

数据备份与恢复

数据备份是数据库运维的生命线!下面介绍完整的备份方案:

备份策略架构图

PostgreSQL 备份策略

备份脚本

#!/bin/bash

# 备份存放位置
BACKUP_DIR=/var/lib/backup/

# 创建备份目录
if [ ! -d "$BACKUP_DIR" ]; then
    mkdir -p "$BACKUP_DIR"
fi
cd $BACKUP_DIR

# 删除30天之前的备份
find $BACKUP_DIR -type d -mtime +30 -exec rm -rf {} \;

# 每天凌晨执行,备份数据文件夹名为 yyyy-MM-dd 格式
dateDIR=`date +%Y-%m-%d`
mkdir -p $dateDIR

# 备份指定的数据库
backupDB=(bac_doc bac_delivery)
backupDBArr=$(echo ${backupDB[@]})

# 使用压缩转储每一个数据库
for i in $backupDBArr; do
    pg_dump -h <POSTGRES_HOST> -p 30032 -U postgres $i | gzip > $BACKUP_DIR$dateDIR/${i}_${dateDIR}.gz
done

定时任务配置

# crontab 配置
01 00 * * * root docker exec postgres-db-1 /backup.sh 2>&1

每天凌晨 00:01 自动执行备份任务。

备份文件格式

/var/lib/backup/
├── 2026-04-15/
│   ├── bac_doc_2026-04-15.gz
│   └── bac_delivery_2026-04-15.gz
├── 2026-04-16/
│   ├── bac_doc_2026-04-16.gz
│   └── bac_delivery_2026-04-16.gz
└── 2026-04-17/
    ├── bac_doc_2026-04-17.gz
    └── bac_delivery_2026-04-17.gz

恢复方法

方法一:pg_restore(推荐)

# 解压并恢复
gunzip -c backup.gz | pg_restore -h <HOST> -p 5432 -U postgres -d <DB_NAME>

方法二:psql(文本格式)

# 全量恢复
gunzip -c backup.sql.gz | psql -h <HOST> -p 5432 -U postgres -d <DB_NAME>

方法三:物理备份

适用于需要快速恢复的场景,但需要停机或配合 PITR(Point-In-Time Recovery)。


连接验证

命令行连接

# 连接 PostgreSQL
psql -h <NODE_IP> -p 30032 -U postgres

# 查看数据库列表
SELECT datname FROM pg_database;

# 查看版本
SELECT version();

连接字符串

postgresql://postgres:<PASSWORD>@<NODE_IP>:30032/<DATABASE_NAME>

常用管理工具

工具 平台 说明
pgAdmin Web 官方推荐的图形化管理工具
DBeaver 跨平台 开源通用数据库客户端
Navicat 跨平台 商业级数据库管理工具

运维最佳实践

日常检查清单

# 1. Pod 状态检查
kubectl get pod -n csjg-postgres

# 2. 存储使用情况
kubectl exec postgres-master-0 -n csjg-postgres -- df -h /var/lib/postgresql/data/

# 3. 数据库连接数
kubectl exec postgres-master-0 -n csjg-postgres -- psql -U postgres -c "SELECT count(*) FROM pg_stat_activity;"

# 4. 慢查询检查
kubectl exec postgres-master-0 -n csjg-postgres -- psql -U postgres -c "SELECT * FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;"

故障排查

症状 可能原因 解决方案
Pod 处于 Pending 节点标签不存在 添加 kubectl label nodes <node> postgres-server=master
连接被拒绝 端口未开放 检查防火墙和 NodePort 配置
数据目录权限错误 HostPath 权限不足 chmod -R 777 /home/postgres
密码错误 环境变量配置错误 检查 POSTGRES_PASSWORD 设置

阅读更多

Ghost 邮箱订阅功能在中国大陆的困境:Mailgun 注册受阻实录

Ghost 邮箱订阅功能在中国大陆的困境:Mailgun 注册受阻实录

本文记录了 Ghost 博客邮箱订阅功能因 Mailgun 在中国大陆无法注册而被迫关闭的完整过程,包括注册尝试、工单沟通及最终应对方案。 一、背景:Ghost 与 Mailgun Ghost 博客平台内置了邮箱订阅(Newsletter)功能,读者可以通过输入邮箱地址订阅博客更新,Ghost 会自动将新文章以邮件形式推送给订阅者。这一功能的邮件发送能力依赖于第三方邮件服务——Mailgun。 Ghost 官方推荐使用 Mailgun 作为邮件传输层,配置方式如下: 配置项 说明 示例值 mail.from 发件人地址 newsletter@yourdomain.com mail.transport 传输协议 SMTP mail.options.host SMTP 主机 smtp.mailgun.org mail.options.port

By 菱角
Kubernetes 全栈监控体系:kube-prometheus-stack + 7 大 Exporter 生产实践

Kubernetes 全栈监控体系:kube-prometheus-stack + 7 大 Exporter 生产实践

本文基于 kube-prometheus-stack Helm Chart 在 Kubernetes 上构建完整监控体系的实战,覆盖 Prometheus、Grafana、Alertmanager 及 7 个专用 Exporter 的部署与配置。 一、监控体系全景架构 整套监控以 kube-prometheus-stack 为核心,通过 ServiceMonitor CRD 自动发现并采集 7 大数据源,配合 Alertmanager 分级告警与 Grafana 可视化: 组件 版本 作用 Prometheus v2.45.0 指标采集与存储,10 天数据保留 Alertmanager v0.25.0 告警路由与邮件推送,2 副本 HA

By 菱角
基于 Docker Compose 部署 Apache APISIX:全栈 API 网关实践

基于 Docker Compose 部署 Apache APISIX:全栈 API 网关实践

本文基于 APISIX 3.9.1 + Dashboard 3.0.1 + etcd 3.5.15 + Prometheus + Grafana 的生产级 Docker Compose 部署实践。 一、整体架构 本次部署采用 Docker Compose 编排五大组件,形成从配置管理到流量监控的完整闭环: 组件 版本 作用 端口 APISIX 3.9.1-debian API 网关核心,路由转发、插件执行 9080/9443 Dashboard 3.0.1-alpine 可视化配置管理界面 9000 etcd 3.5.15

By 菱角
在 Kubernetes 上部署 Apache Flink:生产实战指南

在 Kubernetes 上部署 Apache Flink:生产实战指南

本文基于 Flink 1.15.2 + Kubernetes 的生产环境部署实践。 一、架构概览 Flink 在 Kubernetes 上采用经典的 JobManager + TaskManager 主从架构,JobManager 负责作业调度与协调,TaskManager 承载实际的计算任务。 资源分配一览 组件 作用 副本数 Task Slots 内存配置 JobManager 作业调度、Checkpoint 管理、协调 1 — 8G TaskManager 执行计算任务 3 80/Pod 48G 总计 — 4 240 — 二、Namespace 隔离 为 Flink 创建独立 Namespace,

By 菱角