在时间紧迫的生产环境中使用敏捷方法处理数据库可能是一种矛盾的体验。正如本文所示,您可以将许多步骤操作化,并为各种服务准备 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.yml 将 postgres.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.conf 和 server.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.conf、server.conf 和 pg_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 参考
- 入门
- 安装
- Ad-hoc 命令简介
- 关于模块
- 所有模块列表
- Ansible-doc Ansible 模块文档
- Ansible 手册页
4 条评论