如何使用 Ansible 管理 PostgreSQL

Ansible,一个开源自动化工具,可以简化 Postgres 中复杂的配置和管理任务。
506 位读者喜欢这篇文章。
Crowdfunding

Opensource.com

在时间紧迫的生产环境中使用敏捷方法处理数据库可能是一种矛盾的体验。正如本文所示,您可以将许多步骤操作化,并为各种服务准备 Postgres。关键是 Ansible,一个用于软件配置、配置管理和应用程序部署的开源自动化引擎。

就本文而言,我们假设读者对 Ansible 和 PostgreSQL 以及 Linux 都有一定的了解。我在这里只介绍最基本的功能;要深入了解,请查看本文末尾的参考文献。

如何在开发人员工作站上管理数据库服务器集群

以 root 用户身份,我创建我的模板容器

  lxc-create -t download -n template_centos6 -- --dist centos --release 6 --arch amd64

让我们启动容器,添加以下软件包

   lxc-start -n template_centos6

    lxc-attach -n template_centos6 -- <<_eof_
        yum update -y
        yum install openssh-server screen mlocate man vim python-psycopg2 sudo -y
        /usr/sbin/makewhatis
        /usr/bin/updatedb
        useradd ansible
        echo ansible | passwd --stdin ansible
        echo -e '\n\n# ANSIBLE GLOBAL PERMISSIONS FOR DEMO PURPOSES ONLY\nansible ALL=(ALL) PASSWD:ALL' >> /etc/sudoers
_eof_

现在我们准备好创建我们的实际容器

    lxc-stop -n template_centos6

    for u in ansible pg1 pg2 pg3
    do
        lxc-copy -n template_centos6 -N $u
    done

让我们准备容器 Ansible

    lxc-start -n ansible

    lxc-attach -n ansible <<_eof_
        rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
        yum install ansible -y
        /usr/sbin/makewhatis
        /usr/bin/updatedb
        su -c "ssh-keygen -t rsa -N '' -f /home/ansible/.ssh/id_rsa" ansible
_eof_

让我们打开所有内容

    for u in $(seq 3)
    do
        lxc-start -n pg$u
    done

看看我们的环境是什么样子

    root@wolven:~# lxc-ls --fancy
    NAME             STATE   AUTOSTART GROUPS IPV4       IPV6
    ansible          RUNNING 0         -      10.0.3.7   -
    pg1              RUNNING 0         -      10.0.3.6   -
    pg2              RUNNING 0         -      10.0.3.168 -
    pg3              RUNNING 0         -      10.0.3.17  -
    template_centos6 RUNNING 0         -      10.0.3.173 -

恭喜,您的 Linux 机器上有一个工作网络!

配置我们的 Ansible 容器

现在让我们开始工作,在 guest 主机 Ansible 上创建我们的 playbook。Ansible 使用一个特殊的配置文件,用于定义我们想要管理的所有主机

    lxc-attach -n ansible
    su - ansible

    mkdir -p $HOME/playbooks

    echo "
    pg1 ansible_ssh_pass=ansible ansible_sudo_pass=ansible
    pg2 ansible_ssh_pass=ansible ansible_sudo_pass=ansible
    pg3 ansible_ssh_pass=ansible ansible_sudo_pass=ansible
    " > $HOME/playbooks/host.cfg

Ping pg 服务器

登录到 Ansible 主机后,我们现在将使用 adhoq 命令 ping 我们的 Postgres (pg) 主机;有关命令行上使用的各种开关的更多信息,请参阅手册页

    ansible -i $HOME/playbooks/hosts.cfg all -m ping

这是输出

    pg3 | SUCCESS => {
        "changed": false,
        "ping": "pong"
    }
    pg2 | SUCCESS => {
        "changed": false,
        "ping": "pong"
    }
    pg1 | SUCCESS => {
        "changed": false,
        "ping": "pong"
    }

添加仓库:PostgreSQL 版本 9.4 和 9.6

    (01.install_repo.yml)

Playbook 01.install_repo.ymlpostgres.org 仓库安装到每个 guest 主机上。请注意,我们将安装两个版本的 Postgres——版本 9.4 和 9.6——这使得执行在线升级成为可能

    ansible-playbook -i hosts.cfg 01.install_repo.yml
---
- hosts: dbservers
  remote_user: ansible
  become: yes

  tasks:
    - name: install repo for PostgreSQL 9.4
      yum:
        name: https://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-6-x86_64/pgdg-centos94-9.4-3.noarch.rpm
        state: present

    - name: install repo for PostgreSQL 9.6
      yum:
        name: https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-6-x86_64/pgdg-centos96-9.6-3.noarch.rpm
        state: present
...

安装 Postgres 软件包,9.4

(02.install_94.yml)

配置好仓库后,我们现在可以安装 rpms。请注意在我们的 playbook 调用中使用了变量和循环 with_items

    ansible-playbook -i hosts.cfg 02.install_94.yml --extra-vars "host=dbservers"
---
- hosts: "{{ host }}"
  remote_user: ansible
  become: yes

  tasks:
    - name: install PostgreSQL version 9.4
      yum:
        name: "{{ item }}"
        state: latest
      with_items:
        - postgresql94-server
        - postgresql94-contrib
        - pg_repack94
...

注意:虽然没有讨论,但 pg_repack94 可以消除数据库膨胀,因此我建议您花时间阅读相关内容。

在所有数据库服务器上为 Unix 帐户 Ansible 添加公钥

(03.install_key.yml)

添加公钥是我多年前采用的一种做法。这是一种安全且简便的访问机器的方式,更不用说在紧急情况下非常有用,当您惊慌失措并因密码输入错误而将自己锁定在服务器之外时。

这是我们的 playbook,install_key.yml

---
- hosts: dbservers
  remote_user: ansible
  become: yes

  tasks:
    - name: install repo for PostgreSQL 9.4
      yum:
        name: https://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-6-x86_64/pgdg-centos94-9.4-3.noarch.rpm
        state: present

    - name: install repo for PostgreSQL 9.6
      yum:
        name: https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-6-x86_64/pgdg-centos96-9.6-3.noarch.rpm
        state: present
...

这是我们的调用。请注意使用额外的变量来标识 Unix 帐户 Ansible

    ansible-playbook -i hosts.cfg install_key.yml --extra-vars "user=ansible"

将每个主机配置为独立服务

    (04.configure_standalone_94.yml)

现在所有部分都已就位,是时候创建我们的 Postgres 服务器了!我喜欢逐步构建我的网络;这样,如果出现问题,我可以节省调试的痛苦。

除了初始化数据集群外,我们还在 playbook 中包含以下步骤

  • 启动 Postgres 服务
  • 创建具有密码的 role replicant
  • 更新 pg_hba.conf 以允许远程访问
  • 更新 postgresql.conf 以用于主/从服务

现在我要稍微花哨一点:为了使更改尽可能清晰明了,我将单行附加到配置文件 postgresql.conf,描述一个 include 文件,我们将在其中将所有更改放在一个单独且不同的文件中,从而提高清晰度。Ansible 关键字 blockinfile 很酷,因为它会在文件中添加文本,并在一个漂亮的、带有标签的大块中标识自己。

主服务器和从服务器之间的连接权限在 pg_hba.conf 中更新。请记住,真正安全的环境始终在复制服务器之间使用 SSL 加密,而我们在这里没有这样做。

这是我们的调用。请注意,我们将复制密码作为参数输入;这不仅安全,而且现在我们为脚本增加了灵活性

    ansible-playbook -i hosts.cfg 04.configure_standalone_94.yml --extra-vars "host=dbservers passwd=mypassword"
---
- hosts: "{{ host }}"
  remote_user: ansible
  become: yes

  tasks:
    - name: create data cluster
      command: service postgresql-9.4 initdb

    - service:
        name: postgresql-9.4
        state: started

- hosts: "{{ host }}"
  remote_user: postgres

  tasks:
    - name: create ROLE replicant
      postgresql_user:
        db: postgres
        login_unix_socket: /tmp
        name: replicant
        password: "{{ passwd }}"
        role_attr_flags: LOGIN,REPLICATION

    - name: add new configuration to "postgresql.conf"
      blockinfile:
        dest: /var/lib/pgsql/9.4/data/postgresql.conf
        block: |
          include 'server.conf'

    - name: add new configuration to "server.conf"
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/9.4/data/server.conf
        block: |
          listen_addresses = '*'
          wal_level = hot_standby
          checkpoint_segments = 10
          max_wal_senders = 6
          wal_keep_segments = 10
          hot_standby = on

    - name: add new configuration to "pg_hba.conf"
      blockinfile:
        dest: /var/lib/pgsql/9.4/data/pg_hba.conf
        block: |
          host    all             all             0.0.0.0/0                md5
          host    replication     replicant       0.0.0.0/0                md5

    - name: update environment variables in UNIX account postgres
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/.pgsql_profile
        block: |
          export PGHOST=/tmp PAGER=less PGDATA=/var/lib/pgsql/9.2/data

- hosts: "{{ host }}"
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.4
        state: restarted

    - name: configure init for startup on bootup
      shell: chkconfig --level 2345 postgresql-9.4 on
...

配置 Postgres 从库

    (05.configure_slave_94.yml)

先前为独立模式准备服务器的脚本实际上是一石二鸟,通过预配置/启用使用配置文件 postgresql.confserver.conf 的从主机,这些文件使用 pg_basebackup 命令复制到从库。此示例中特别有趣的是我们如何创建级联复制从库 pg3,它从从库 pg2 获取数据。请注意脚本的第二部分;我们正在利用我们在之前的脚本中安装的 Ansible 公钥,通过直接登录到 Postgres 而不是 Sudo

    ansible-playbook -i hosts.cfg 05.configure_slave_94.yml --extra-vars "master=pg1 slave=pg2 passwd=mypassword"
    ansible-playbook -i hosts.cfg 05.configure_slave_94.yml --extra-vars "master=pg2 slave=pg3 passwd=mypassword"
---
- hosts: "{{ slave }}"
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.4
        state: stopped

    - file:
       path: /var/lib/pgsql/9.4/data/
       state: absent

    - file:
       path:  /var/lib/pgsql/9.4/data/
       owner: postgres
       group: postgres
       mode:  0700
       state: directory

- hosts: "{{ slave }}"
  remote_user: postgres

  tasks:
    - name: execute base backup
      shell: export PGPASSWORD="{{ passwd }}" && /usr/pgsql-9.4/bin/pg_basebackup -h pg1 -U replicant -D /var/lib/pgsql/9.4/data -P -v --xlog-method=stream 2>&1

    - name: add new configuration "recovery.conf"
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/9.4/data/recovery.conf
        block: |
          standby_mode = 'on'
          primary_conninfo = 'user=replicant password={{ passwd }} host={{ master }} port=5432 sslmode=prefer'
          recovery_target_timeline = 'latest'

- hosts: "{{ slave }}"
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.4
        state: started
...

调用故障转移

    (06.failover_94.yml)

危险,威尔·鲁滨逊,危险!!

实际上我们只是关闭了它。

故障转移和提升非常容易;只需对 pg2 执行一个命令即可完成。由于 pg3 配置为级联从库,它将自动从新提升的主库复制。秘诀在于 recovery.conf 文件,我们在其中将其配置为始终读取最新的时间线

    ansible-playbook -i hosts.cfg 06.failover_94.yml
---
- hosts: pg1
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.4
        state: stopped

- hosts: pg2
  remote_user: postgres

  tasks:
    - name: promote data cluster pg4
      command:  /usr/pgsql-9.4/bin/pg_ctl -D /var/lib/pgsql/9.4/data/ promote
...

将服务器从 9.4 升级到 9.6

    (hosts.cfg 07.pg1_upgrade_94-96.yml)

在最佳情况下,从一个版本的 Postgres 升级到另一个版本可能很棘手。但是,正确配置的 playbook 甚至可以使这成为一个简单的命题

    ansible-playbook -i hosts.cfg 07.pg1_upgrade_94-96.yml

在此示例中,我们升级 pg1,它在我们的故障转移示例中被关闭,通过执行以下步骤

  • 将 Postgres 版本 9.6 的二进制文件安装到我们的三台主机上
  • 关闭 9.4 服务并禁用二进制文件在服务器重启时启动
  • 启用 9.6 二进制文件,以防机器需要重启
  • 为升级版本的 Postgres 创建一个空数据集群;可选地删除任何先前存在的数据集群
  • 使用 pg_upgrade 实用程序执行升级过程;请注意使用两个不同的端口号
  • 使用更新的配置集(即 postgresql.confserver.confpg_hba.conf)更新版本 9.6 数据集群
  • 在登录到 Unix Postgres 帐户时更新运行时环境变量,以便更轻松地进行管理
  • 启动我们新的 Postgres 版本 9.6 服务

注意:正如条条大路通罗马,编写此 playbook 的方法也有很多种;这取决于您。

---
- hosts: all
  remote_user: ansible
  become: yes

  tasks:
    - name: install repo for PostgreSQL 9.6
      yum:
        name: https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-6-x86_64/pgdg-centos96-9.6-3.noarch.rpm
        state: present

    - name: install PostgreSQL version 9.6
      yum:
        name: "{{ item }}"
        state: latest
      with_items:
        - postgresql96-server
        - postgresql96-contrib
        - pg_repack96

    - name: disable init for 9.4
      shell: chkconfig --level 2345 postgresql-9.4 off

    - name: enable init for 9.6
      shell: chkconfig --level 2345 postgresql-9.6 on

- hosts: pg1
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.6
        state: stopped

    - file:
        path: /var/lib/pgsql/9.6/data/
        state: absent

    - name: create data cluster
      command: service postgresql-9.6 initdb

- hosts: pg1
  remote_user: postgres

  tasks:
    - name: execute the upgrade from 9.4 to 9.6
      shell: |
        /usr/pgsql-9.6/bin/pg_upgrade \
          -d /var/lib/pgsql/9.4/data \
          -D /var/lib/pgsql/9.6/data \
          -b /usr/pgsql-9.4/bin \
          -B /usr/pgsql-9.6/bin \
          -p 10094 \
          -P 5432

        exit 0

    - name: add new configuration to "postgresql.conf"
      blockinfile:
        dest: /var/lib/pgsql/9.6/data/postgresql.conf
        block: |
          include 'server.conf'

    - name: add new configuration to "server.conf"
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/9.6/data/server.conf
        block: |
          listen_addresses = '*'
          wal_level = hot_standby
          max_wal_senders = 6
          wal_keep_segments = 10
          hot_standby = on

    - name: add new configuration to "pg_hba.conf"
      blockinfile:
        dest: /var/lib/pgsql/9.6/data/pg_hba.conf
        block: |
          host    all             all             0.0.0.0/0                md5
          host    replication     replicant       0.0.0.0/0                md5

    - name: update environment variables in UNIX account postgres
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/.pgsql_profile
        block: |
          export PGHOST=/tmp PAGER=less PGDATA=/var/lib/pgsql/9.6/data

- hosts: pg1
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.6
        state: started
...

调用故障转移

    (08.configure_slave_96.yml)

此脚本实际上与脚本 05.configure_slave_94.yml 相同。您甚至可能想要编辑它,从而从您的集合中删除一个脚本

    ansible-playbook -i hosts.cfg 08.configure_slave_96.yml --extra-vars "master=pg1 slave=pg2 passwd=mypassword"
    ansible-playbook -i hosts.cfg 08.configure_slave_96.yml --extra-vars "master=pg2 slave=pg3 passwd=mypassword"
---
- hosts: "{{ slave }}"
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.4
        state: stopped

    - service:
        name: postgresql-9.6
        state: stopped

    - file:
       path: /var/lib/pgsql/9.6/data/
       state: absent

    - file:
       path:  /var/lib/pgsql/9.6/data/
       owner: postgres
       group: postgres
       mode:  0700
       state: directory

- hosts: "{{ slave }}"
  remote_user: postgres

  tasks:
    - name: execute base backup
      shell: |
        export PGPASSWORD="{{ passwd }}" && \
        /usr/pgsql-9.6/bin/pg_basebackup \
            -h pg1 \
            -U replicant \
            -D /var/lib/pgsql/9.6/data \
            -P -v --xlog-method=stream 2>&1

        exit 0

    - name: add new configuration "recovery.conf"
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/9.6/data/recovery.conf
        block: |
          standby_mode = 'on'
          primary_conninfo = 'user=replicant password={{ passwd }} host={{ master }} port=5432 sslmode=prefer'
          recovery_target_timeline = 'latest'

    - name: update environment variables in UNIX account postgres
      blockinfile:
        create: yes
        dest: /var/lib/pgsql/.pgsql_profile
        block: |
          export PGHOST=/tmp PAGER=less PGDATA=/var/lib/pgsql/9.6/data

- hosts: "{{ slave }}"
  remote_user: ansible
  become: yes

  tasks:
    - service:
        name: postgresql-9.6
        state: started
...

结论

使用关系数据库管理系统成功编码通常被认为是任何合格的开发人员都应具备的一项平庸的能力。然而,正如我回顾多年来的所有对话、站立会议和会议时,它常常被证明是最关键、最耗时和最神奇的活动。整个开发团队将花费数小时讨论数据应如何在前端和后端之间移动。摇滚明星开发人员将在成功设计和实施用户界面与其持久存储之间的数据移动后赢得他们的荣誉。从来没有如此多的东西依赖于像 RDBMS 这样平凡的东西。在 PostgreSQL 漫长而传奇的历史中,从未像今天这样,在大数据和分析时代,看到如此多的采用和对基础设施的关键影响。

不要止步于此!务必跟进优秀的在线文档并创建您自己的 playbook。一点建议,通过避免尝试使用 Ansible Galaxy 中提供的众多 Postgres 模块之一进行学习,您将走得更远、更快。

Ansible 参考

Postgres 参考

User profile image.
Robert 最早是在一台玩 William Tell Overture 的迷你电脑上接触到 Hangman 游戏,他是一位老派黑客。最初接受穿孔卡片培训,并在经历了多年克服创伤所需的大量治疗后,Robert 发现了 Linux,并成为利用互联网的热潮中的一员。最终,他将他的开源活动重点放在 PostgreSQL 上。

4 条评论

好文章。您可以发布一篇关于 MySQL 数据库的类似文章吗?

感谢这篇精彩的文章!

一个小小的错误
" > $ HOME/playbooks/host.cfg ----------> hosts.cfg

似乎列表不完整
03.install_key.yml

如果源代码能在 github 上就好了。

再次感谢!

感谢您完成这项工作。它并不比仅仅用 bash/ssh 编写脚本好多少。如果不可能会严重破坏我的运行环境,我将无法再次运行此 playbook。

shell/command 模块虽然是必要的,但却是良好期望状态配置系统的祸根。

更常见的是使用幂等角色而不是 playbook 来实现这一点。使用健康检查来触发故障转移,并使用变量建模主/从。

© . All rights reserved.