Skip to content

PostgreSQL Ansible集成

核心概念

PostgreSQL Ansible集成是指使用Ansible自动化工具来管理和配置PostgreSQL数据库环境。Ansible是一种开源的自动化配置管理工具,通过SSH协议实现无代理部署和配置,具有简单、强大、灵活的特点。

主要功能

  • 自动化部署:快速部署PostgreSQL数据库实例
  • 配置管理:集中管理数据库配置,确保一致性
  • 状态管理:确保数据库处于预期状态
  • 批量操作:同时管理多个数据库节点
  • 版本控制:配置变更可追溯,支持回滚
  • 集成测试:部署后自动验证配置和功能

核心组件

  • Playbook:定义自动化任务的YAML文件
  • Role:模块化的任务集合
  • Module:Ansible内置的PostgreSQL管理模块
  • Inventory:定义被管理主机的配置文件
  • 变量:存储配置参数的机制

Ansible PostgreSQL模块

常用模块

Ansible提供了多个专门用于管理PostgreSQL的模块:

  • postgresql_db:管理数据库
  • postgresql_user:管理数据库用户
  • postgresql_privs:管理用户权限
  • postgresql_query:执行SQL查询
  • postgresql_schema:管理数据库模式
  • postgresql_ext:管理数据库扩展
  • postgresql_table:管理数据库表

模块安装

如果Ansible主机上缺少PostgreSQL相关模块,需要安装依赖:

bash
# 安装PostgreSQL Python依赖
pip install psycopg2-binary

基础集成示例

1. 数据库部署Playbook

以下是一个完整的PostgreSQL数据库部署Playbook,用于自动化安装、配置和启动PostgreSQL实例。该Playbook支持RedHat/CentOS系统,可通过变量调整PostgreSQL版本和配置参数。

yaml
# postgresql_deploy.yml
# 自动化部署PostgreSQL数据库实例

- name: 部署PostgreSQL数据库
  hosts: postgres_servers  # 目标主机组
  become: yes  # 使用sudo权限
  vars:  # 定义变量
    pg_version: "15"  # PostgreSQL版本
    pg_data_dir: "/var/lib/pgsql/{{ pg_version }}/data"  # 数据目录
    pg_conf_file: "{{ pg_data_dir }}/postgresql.conf"  # 主配置文件
    pg_hba_file: "{{ pg_data_dir }}/pg_hba.conf"  # 客户端认证配置
    pg_admin_user: "postgres"  # 超级用户名
    pg_admin_password: "StrongPassword123!"  # 超级用户密码
  
  tasks:  # 定义任务列表
    # 安装PostgreSQL YUM源
    - name: 安装PostgreSQL YUM源
      yum:
        name: "https://download.postgresql.org/pub/repos/yum/{{ pg_version }}/redhat/rhel-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm"
        state: present
    
    # 安装PostgreSQL软件包
    - name: 安装PostgreSQL
      yum:
        name:
          - "postgresql{{ pg_version }}-server"  # 服务器包
          - "postgresql{{ pg_version }}-contrib"  # 扩展包
          - "postgresql{{ pg_version }}-libs"  # 库文件
        state: present
    
    # 初始化数据库
    - name: 初始化数据库
      command: "/usr/pgsql-{{ pg_version }}/bin/postgresql-{{ pg_version }}-setup initdb"
      args:
        creates: "{{ pg_data_dir }}/PG_VERSION"  # 仅当文件不存在时执行
    
    # 配置PostgreSQL核心参数
    - name: 配置PostgreSQL
      lineinfile:  # 修改配置文件行
        path: "{{ pg_conf_file }}"  # 配置文件路径
        regexp: "^#{{ item.key }} ="  # 匹配要修改的行
        line: "{{ item.key }} = {{ item.value }}"  # 新内容
      with_items:  # 循环处理多个参数
        - { key: "listen_addresses", value: "'*'" }  # 监听所有地址
        - { key: "max_connections", value: "200" }  # 最大连接数
        - { key: "shared_buffers", value: "512MB" }  # 共享缓冲区
    
    # 配置客户端认证策略
    - name: 配置客户端认证
      lineinfile:
        path: "{{ pg_hba_file }}"  # 认证配置文件
        line: "host    all             all             0.0.0.0/0                scram-sha-256"  # 允许所有IP访问
        insertbefore: "^# IPv6 local connections:"  # 插入位置
    
    # 设置超级用户密码
    - name: 设置PostgreSQL密码
      postgresql_user:
        login_user: "{{ pg_admin_user }}"  # 登录用户
        login_password: ""  # 初始密码为空
        user: "{{ pg_admin_user }}"  # 要修改的用户
        password: "{{ pg_admin_password }}"  # 新密码
        encrypted: yes  # 使用加密密码
      become_user: postgres  # 切换到postgres用户执行
    
    # 启动并启用PostgreSQL服务
    - name: 启动并启用PostgreSQL服务
      service:
        name: "postgresql-{{ pg_version }}"  # 服务名称
        state: started  # 启动服务
        enabled: yes  # 设置开机自启
    
    # 验证PostgreSQL服务是否正常运行
    - name: 验证PostgreSQL服务
      wait_for:
        port: 5432  # 检查端口
        state: started  # 状态为启动
        timeout: 30  # 超时时间

2. 数据库管理Playbook

以下Playbook用于管理PostgreSQL数据库、用户、权限和扩展,适用于应用部署后的数据库初始化工作。

yaml
# postgresql_manage.yml
# 管理PostgreSQL数据库、用户和权限

- name: 管理PostgreSQL数据库
  hosts: postgres_servers  # 目标主机组
  become: yes  # 使用sudo权限
  vars:  # 定义变量
    pg_host: "localhost"  # 数据库主机
    pg_port: 5432  # 数据库端口
    pg_admin_user: "postgres"  # 超级用户名
    pg_admin_password: "StrongPassword123!"  # 超级用户密码
    app_db: "myappdb"  # 应用数据库名
    app_user: "appuser"  # 应用用户名
    app_password: "AppPassword123!"  # 应用用户密码
  
  tasks:  # 任务列表
    # 创建应用数据库
    - name: 创建应用数据库
      postgresql_db:
        login_host: "{{ pg_host }}"
        login_port: "{{ pg_port }}"
        login_user: "{{ pg_admin_user }}"
        login_password: "{{ pg_admin_password }}"
        name: "{{ app_db }}"  # 数据库名称
        state: present  # 状态为存在
      become_user: postgres  # 切换到postgres用户执行
    
    # 创建应用用户
    - name: 创建应用用户
      postgresql_user:
        login_host: "{{ pg_host }}"
        login_port: "{{ pg_port }}"
        login_user: "{{ pg_admin_user }}"
        login_password: "{{ pg_admin_password }}"
        user: "{{ app_user }}"  # 用户名
        password: "{{ app_password }}"  # 密码
        encrypted: yes  # 使用加密密码
        state: present  # 状态为存在
      become_user: postgres  # 切换到postgres用户执行
    
    # 授予应用用户权限
    - name: 授予应用用户权限
      postgresql_privs:
        login_host: "{{ pg_host }}"
        login_port: "{{ pg_port }}"
        login_user: "{{ pg_admin_user }}"
        login_password: "{{ pg_admin_password }}"
        db: "{{ app_db }}"  # 数据库名
        role: "{{ app_user }}"  # 角色名
        privs: "ALL"  # 所有权限
        type: "database"  # 权限类型
        state: present  # 状态为存在
      become_user: postgres  # 切换到postgres用户执行
    
    # 创建数据库扩展
    - name: 创建数据库扩展
      postgresql_ext:
        login_host: "{{ pg_host }}"
        login_port: "{{ pg_port }}"
        login_user: "{{ pg_admin_user }}"
        login_password: "{{ pg_admin_password }}"
        db: "{{ app_db }}"  # 数据库名
        name: "pg_stat_statements"  # 扩展名称
        state: present  # 状态为存在
      become_user: postgres  # 切换到postgres用户执行
    
    # 执行SQL查询
    - name: 执行SQL查询
      postgresql_query:
        login_host: "{{ pg_host }}"
        login_port: "{{ pg_port }}"
        login_user: "{{ pg_admin_user }}"
        login_password: "{{ pg_admin_password }}"
        db: "{{ app_db }}"  # 数据库名
        # 创建用户表
        query: "CREATE TABLE IF NOT EXISTS users (id serial primary key, name varchar(50) not null);"
      become_user: postgres  # 切换到postgres用户执行

PostgreSQL Role示例

Ansible Role是一种模块化的任务集合,可以将复杂的自动化任务组织成可重用的组件。以下是PostgreSQL管理Role的示例结构和内容。

1. Role结构

Role采用标准的目录结构,便于组织和维护:

roles/
└── postgresql/              # Role名称
    ├── defaults/            # 默认变量目录
    │   └── main.yml       # 默认变量文件
    ├── files/              # 静态文件目录
    ├── handlers/           # 处理器目录(用于响应通知)
    │   └── main.yml       # 处理器文件
    ├── meta/               # 元数据目录(包含依赖关系)
    │   └── main.yml       # 元数据文件
    ├── tasks/              # 主任务目录
    │   └── main.yml       # 主任务文件
    ├── templates/          # 模板文件目录
    │   ├── postgresql.conf.j2  # PostgreSQL配置模板
    │   └── pg_hba.conf.j2      # 客户端认证配置模板
    └── vars/               # 变量目录
        └── main.yml       # 变量文件

2. Role主任务

主任务文件定义了Role的核心功能,包括安装、配置和启动PostgreSQL服务:

yaml
# roles/postgresql/tasks/main.yml
# PostgreSQL Role主任务文件

- name: 包含变量文件
  include_vars: main.yml  # 加载vars/main.yml中的变量

# 安装PostgreSQL YUM源(仅RedHat系统)
- name: 安装PostgreSQL YUM源
  yum:
    name: "https://download.postgresql.org/pub/repos/yum/{{ pg_version }}/redhat/rhel-{{ ansible_distribution_major_version }}-x86_64/pgdg-redhat-repo-latest.noarch.rpm"
    state: present
  when: ansible_os_family == "RedHat"  # 仅在RedHat系统上执行

# 安装PostgreSQL软件包
- name: 安装PostgreSQL
  package:
    name:
      - "postgresql{{ pg_version }}-server"  # 服务器包
      - "postgresql{{ pg_version }}-contrib"  # 扩展包
      - "postgresql{{ pg_version }}-libs"  # 库文件
    state: present  # 确保包已安装

# 初始化数据库
- name: 初始化PostgreSQL数据库
  command: "/usr/pgsql-{{ pg_version }}/bin/postgresql-{{ pg_version }}-setup initdb"
  args:
    creates: "{{ pg_data_dir }}/PG_VERSION"  # 仅当数据目录不存在时执行

# 复制配置模板到目标主机
- name: 复制PostgreSQL配置模板
  template:
    src: "postgresql.conf.j2"  # 模板文件
    dest: "{{ pg_conf_file }}"  # 目标文件路径
    owner: postgres  # 文件所有者
    group: postgres  # 文件所属组
    mode: '0600'  # 文件权限
  notify: 重启PostgreSQL服务  # 配置变更后通知重启服务

# 复制客户端认证配置模板
- name: 复制客户端认证配置模板
  template:
    src: "pg_hba.conf.j2"  # 模板文件
    dest: "{{ pg_hba_file }}"  # 目标文件路径
    owner: postgres  # 文件所有者
    group: postgres  # 文件所属组
    mode: '0600'  # 文件权限
  notify: 重启PostgreSQL服务  # 配置变更后通知重启服务

# 启动并启用PostgreSQL服务
- name: 启动并启用PostgreSQL服务
  service:
    name: "postgresql-{{ pg_version }}"  # 服务名称
    state: started  # 启动服务
    enabled: yes  # 设置开机自启

# 等待PostgreSQL服务完全启动
- name: 等待PostgreSQL服务启动
  wait_for:
    port: "{{ pg_port }}"  # 检查端口
    state: started  # 状态为启动
    timeout: 30  # 超时时间

3. 配置模板

txt
# roles/postgresql/templates/postgresql.conf.j2
# PostgreSQL配置模板

# 监听配置
listen_addresses = '{{ pg_listen_addresses }}'
port = {{ pg_port }}

# 连接配置
max_connections = {{ pg_max_connections }}
shared_buffers = {{ pg_shared_buffers }}
work_mem = {{ pg_work_mem }}
maintenance_work_mem = {{ pg_maintenance_work_mem }}

# WAL配置
wal_level = {{ pg_wal_level }}
max_wal_senders = {{ pg_max_wal_senders }}
wal_keep_size = {{ pg_wal_keep_size }}

# 归档配置
archive_mode = {{ pg_archive_mode }}
archive_command = '{{ pg_archive_command }}'

# 热备配置
hot_standby = {{ pg_hot_standby }}

主从复制集成示例

yaml
# postgresql_replication.yml
# 配置PostgreSQL主从复制

- name: 配置PostgreSQL主从复制
  hosts: postgres_servers
  become: yes
  vars:
    pg_version: "15"
    pg_data_dir: "/var/lib/pgsql/{{ pg_version }}/data"
    pg_conf_file: "{{ pg_data_dir }}/postgresql.conf"
    pg_hba_file: "{{ pg_data_dir }}/pg_hba.conf"
    pg_admin_user: "postgres"
    pg_admin_password: "StrongPassword123!"
    repl_user: "replica"
    repl_password: "ReplicaPassword123!"
    master_host: "192.168.1.100"
  
  tasks:
    - name: 主节点配置
      block:
        - name: 修改主节点配置
          lineinfile:
            path: "{{ pg_conf_file }}"
            regexp: "^#{{ item.key }} ="
            line: "{{ item.key }} = {{ item.value }}"
          with_items:
            - { key: "wal_level", value: "logical" }
            - { key: "max_wal_senders", value: "10" }
            - { key: "wal_keep_size", value: "1GB" }
            - { key: "archive_mode", value: "on" }
            - { key: "archive_command", value: "'cp %p /var/lib/pgsql/wal_archive/%f'" }
        
        - name: 创建WAL归档目录
          file:
            path: /var/lib/pgsql/wal_archive
            state: directory
            owner: postgres
            group: postgres
        
        - name: 配置主节点认证
          lineinfile:
            path: "{{ pg_hba_file }}"
            line: "host    replication     {{ repl_user }}     192.168.1.0/24            scram-sha-256"
            insertbefore: "^# IPv6 local connections:"
        
        - name: 重启主节点
          service:
            name: "postgresql-{{ pg_version }}"
            state: restarted
        
        - name: 创建复制用户
          postgresql_user:
            login_user: "{{ pg_admin_user }}"
            login_password: "{{ pg_admin_password }}"
            user: "{{ repl_user }}"
            password: "{{ repl_password }}"
            encrypted: yes
            replication: yes
            state: present
          become_user: postgres
      when: inventory_hostname == "postgres-master"
    
    - name: 从节点配置
      block:
        - name: 停止从节点服务
          service:
            name: "postgresql-{{ pg_version }}"
            state: stopped
        
        - name: 清空从节点数据目录
          file:
            path: "{{ pg_data_dir }}"
            state: absent
        
        - name: 从主节点备份数据
          command: >
            pg_basebackup -h {{ master_host }} -U {{ repl_user }} -D {{ pg_data_dir }} -Fp -Xs -Pv
          become_user: postgres
          environment:
            PGPASSWORD: "{{ repl_password }}"
        
        - name: 创建standby.signal文件
          file:
            path: "{{ pg_data_dir }}/standby.signal"
            state: touch
            owner: postgres
            group: postgres
        
        - name: 配置从节点连接信息
          copy:
            content: "primary_conninfo = 'host={{ master_host }} port=5432 user={{ repl_user }} password={{ repl_password }} application_name={{ inventory_hostname }}'\n"
            dest: "{{ pg_data_dir }}/primary_conninfo"
            owner: postgres
            group: postgres
            mode: '0600'
        
        - name: 修改从节点配置
          lineinfile:
            path: "{{ pg_conf_file }}"
            regexp: "^#hot_standby ="
            line: "hot_standby = on"
        
        - name: 启动从节点服务
          service:
            name: "postgresql-{{ pg_version }}"
            state: started
        
        - name: 验证从节点状态
          postgresql_query:
            login_user: "{{ pg_admin_user }}"
            login_password: "{{ pg_admin_password }}"
            query: "SELECT * FROM pg_stat_wal_receiver;"
          register: wal_receiver_status
          become_user: postgres
        
        - name: 显示从节点状态
          debug:
            var: wal_receiver_status
      when: inventory_hostname in groups["postgres-slaves"]

最佳实践

1. 变量管理

  • 使用分层变量管理:默认变量、主机变量、组变量
  • 敏感信息使用Ansible Vault加密
  • 使用外部变量文件管理不同环境配置

2. 配置管理

  • 使用模板文件管理配置,支持动态参数
  • 配置变更后自动重启服务
  • 版本控制所有配置文件

3. 安全管理

  • 避免硬编码密码,使用Vault或环境变量
  • 最小权限原则:使用专用的数据库用户
  • 定期更新Ansible和PostgreSQL版本
  • 配置防火墙,限制数据库访问

4. 测试与验证

  • 部署前在测试环境验证Playbook
  • 部署后自动验证数据库状态
  • 集成CI/CD流水线,实现自动化测试
  • 定期运行Playbook,确保配置一致性

常见问题与解决方案

Q1: 执行PostgreSQL模块时出现连接错误

可能原因

  • PostgreSQL服务未启动
  • 防火墙阻止连接
  • 认证配置错误
  • 密码错误

解决方案

yaml
# 确保PostgreSQL服务正在运行
- name: 检查PostgreSQL服务状态
  service:
    name: "postgresql-{{ pg_version }}"
    state: started

# 临时关闭防火墙测试
- name: 临时关闭防火墙
  service:
    name: firewalld
    state: stopped

# 检查认证配置
- name: 验证pg_hba.conf配置
  lineinfile:
    path: "{{ pg_hba_file }}"
    line: "host    all             all             0.0.0.0/0                scram-sha-256"
    state: present

Q2: 执行pg_basebackup时出现权限错误

解决方案

  • 确保repl_user具有REPLICATION权限
  • 确保主节点pg_hba.conf允许从节点连接
  • 验证PGPASSWORD环境变量设置正确

Q3: 如何在不同环境中使用不同配置?

解决方案

  • 使用环境变量文件:

    bash
    # 开发环境
    ansible-playbook postgresql_deploy.yml -e @env/dev.yml
    
    # 生产环境  
    ansible-playbook postgresql_deploy.yml -e @env/prod.yml
  • 使用Ansible Tower/AWX管理环境变量

常见问题(FAQ)

Q1: Ansible支持哪些PostgreSQL版本?

A1: Ansible的PostgreSQL模块支持PostgreSQL 9.4及以上版本,不同模块可能有不同的版本要求。建议使用最新版本的Ansible和PostgreSQL以获得最佳兼容性。

Q2: 如何处理PostgreSQL版本差异?

A2: 可以使用条件判断和变量管理来处理不同版本的差异:

yaml
- name: PostgreSQL 12+ 配置
  lineinfile:
    path: "{{ pg_conf_file }}"
    regexp: "^#hot_standby ="
    line: "hot_standby = on"
  when: pg_version is version('12', '>=')

- name: PostgreSQL 11及以下配置
  lineinfile:
    path: "{{ pg_conf_file }}"
    regexp: "^#hot_standby ="
    line: "hot_standby = on"
  when: pg_version is version('11', '<=')

Q3: 如何使用Ansible管理多个PostgreSQL实例?

A3: 可以通过以下方式管理多个实例:

  • 使用不同的端口和数据目录
  • 在Playbook中使用循环创建多个实例
  • 使用不同的Role分别管理不同实例
  • 使用变量区分不同实例的配置

Q4: 如何备份和恢复PostgreSQL数据库?

A4: 使用Ansible可以自动化备份和恢复过程:

yaml
# 备份数据库
- name: 备份PostgreSQL数据库
  postgresql_db:
    login_user: "{{ pg_admin_user }}"
    login_password: "{{ pg_admin_password }}"
    name: "{{ app_db }}"
    state: dump
    target: "/backup/{{ app_db }}_{{ ansible_date_time.date }}.sql"
  become_user: postgres

# 恢复数据库
- name: 恢复PostgreSQL数据库
  postgresql_db:
    login_user: "{{ pg_admin_user }}"
    login_password: "{{ pg_admin_password }}"
    name: "{{ app_db }}"
    state: restore
    target: "/backup/{{ app_db }}_2023-01-01.sql"
  become_user: postgres

Q5: 如何监控PostgreSQL数据库?

A5: Ansible可以集成监控工具:

  • 部署Prometheus和Grafana
  • 安装pg_exporter收集指标
  • 配置告警规则
  • 生成监控仪表盘

Q6: 如何使用Ansible升级PostgreSQL?

A6: 升级步骤:

  1. 备份现有数据库
  2. 安装新版本PostgreSQL
  3. 使用pg_upgrade工具升级数据目录
  4. 验证升级结果
  5. 更新配置文件
  6. 启动新版本服务