Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,36 @@ postgresql_user_privileges:
db: foobar # database
priv: "ALL" # privilege string format: example: INSERT,UPDATE/table:SELECT/anothertable:ALL
role_attr_flags: "CREATEDB" # role attribute flags

# Manage replication with repmgr (optional)
repmgr_target_group: "postgresql-db"
postgresql_ext_install_repmgr: yes
repmgr_user: repmgr
repmgr_database: repmgr
postgresql_wal_level: "replica"
postgresql_max_wal_senders: 10
postgresql_max_replication_slots: 10
postgresql_wal_keep_segments: 100
postgresql_hot_standby: on
postgresql_archive_mode: on
postgresql_archive_command: "test ! -f /tmp/%f && cp %p /tmp/%f"
postgresql_shared_preload_libraries:
- repmgr

postgresql_users:
- name: "{{repmgr_user}}"
pass: "password"

postgresql_databases:
- name: {{repmgr_database}}
owner: "{{repmgr_user}}"
encoding: "UTF-8"

postgresql_user_privileges:
- name: "{{repmgr_user}}"
db: {{repmgr_database}}
priv: "ALL"
role_attr_flags: "SUPERUSER,REPLICATION"
```

There's a lot more knobs and bolts to set, which you can find in the [defaults/main.yml](./defaults/main.yml)
Expand Down
1 change: 1 addition & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ postgresql_database_owner: "{{ postgresql_admin_user }}"
postgresql_ext_install_contrib: no
postgresql_ext_install_dev_headers: no
postgresql_ext_install_postgis: no
postgresql_ext_install_repmgr: no

postgresql_ext_postgis_version: "2.5" # be careful: check whether the postgresql/postgis versions work together

Expand Down
95 changes: 95 additions & 0 deletions defaults/repmgr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Required configuration items
repmgr_target_group: ""
repmgr_user: "repmgr"
repmgr_database: "repmgr"
repmgr_master: "{{ groups[repmgr_target_group][0] }}"
repmgr_conninfo: "host={{ansible_hostname}} user={{repmgr_user}} dbname={{repmgr_database}} connect_timeout=2"
repmgr_data_directory: "{{postgresql_data_directory}}"

# Optional configuration items
#

# Replication
repmgr_config_directory: "{{postgresql_conf_directory}}"
repmgr_replication_user: "{{repmgr_user}}"
repmgr_replication_type: "physical"
repmgr_location: "default"
repmgr_use_replication_slots: "no"
repmgr_witness_sync_interval: 15
repmgr_use_primary_conninfo_password: "false"
repmgr_passfile: "/var/lib/pgsql/.pgpass"
repmgr_private_key_path: "/var/lib/pgsql/.ssh/id_rsa"
repmgr_public_key_path: "/var/lib/pgsql/.ssh/id_rsa.pub"

# Logging
repmgr_log_level: "INFO"
repmgr_log_facility: "STDERR"
repmgr_log_file: ""
repmgr_log_status_interval: 300

# Notification
repmgr_event_notification_command: ""
repmgr_event_notifications: ""

# Directories
repmgr_pg_bindir: "{{postgresql_bin_directory}}"
repmgr_repmgr_bindir: "{{postgresql_bin_directory}}"

# Commands & options
repmgr_pg_ctl_options: ""
repmgr_pg_basebackup_options: ""
repmgr_rsync_options: ""
repmgr_tablespace_mapping: ""
repmgr_restore_command: ""
repmgr_archive_cleanup_command: ""
repmgr_recovery_min_apply_delay: ""
repmgr_ssh_options: "-o StrictHostKeyChecking=no"

# Intervals & timeouts
repmgr_promote_check_timeout: 60
repmgr_promote_check_interval: 1
repmgr_primary_follow_timeout: 60
repmgr_standby_follow_timeout: 15
repmgr_shutdown_check_timeout: 60
repmgr_standby_reconnect_timeout: 60
repmgr_node_rejoin_timeout: 60

# Barman
repmgr_barman_server: ""
repmgr_barman_host: ""
repmgr_barman_config: ""
repmgr_repmgrd_pid_file: ""

# Fail-over
repmgr_failover: "manual"
repmgr_priority: 100
repmgr_reconnect_attempts: 6
repmgr_reconnect_interval: 10
repmgr_promote_command: ""
repmgr_follow_command: ""
repmgr_primary_notification_timeout: 60
repmgr_repmgrd_standby_startup_timeout: 60

# Monitoring
repmgr_monitoring_history: "no"
repmgr_monitor_interval_secs: 2
repmgr_degraded_monitoring_timeout: -1
repmgr_async_query_timeout: 60
repmgr_keep_history_days: 14

# Service commands
repmgr_service_start_command : "sudo systemctl start postgresql-{{postgresql_version}}"
repmgr_service_stop_command : "sudo systemctl stop postgresql-{{postgresql_version}}"
repmgr_service_restart_command : "sudo systemctl restart postgresql-{{postgresql_version}}"
repmgr_service_reload_command : "sudo systemctl reload postgresql-{{postgresql_version}}"
repmgr_service_promote_command : ""

# Warning levels
repmgr_archive_ready_warning: 16
repmgr_archive_ready_critical: 128
repmgr_replication_lag_warning: 300
repmgr_replication_lag_critical: 600

# Bi-Directional Replication
repmgr_bdr_local_monitoring_only: "false"
repmgr_bdr_recovery_timeout: 30
2 changes: 2 additions & 0 deletions tasks/extensions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
when: postgresql_ext_install_dev_headers
- import_tasks: extensions/postgis.yml
when: postgresql_ext_install_postgis
- import_tasks: extensions/repmgr.yml
when: postgresql_ext_install_repmgr
148 changes: 148 additions & 0 deletions tasks/extensions/configure_repmgr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
- name: Repmgr | Update configuration (repmgr.conf)
template:
src: "repmgr.conf.j2"
dest: "{{postgresql_conf_directory}}/repmgr.conf"
owner: "{{ postgresql_service_user }}"
group: "{{ postgresql_service_group }}"
mode: 0640

- name: Repmgr | Ensure systemd drop-in directory exists
file:
path: "/etc/systemd/system/repmgr{{postgresql_version}}.service.d/"
state: directory
mode: 0755

- name: Repmgr | Update drop-in
template:
src: "repmgr.custom.conf.j2"
dest: "/etc/systemd/system/repmgr{{postgresql_version}}.service.d/custom.conf"

- name: Repmgr | Allow passwordless restarts with postgres user
template:
src: "sudoers.postgresql.j2"
dest: "/etc/sudoers.d/postgresql"
mode: 0640

- name: Repmgr | Update .pgpass for postgres user
template:
src: "pgpass.j2"
dest: "{{repmgr_passfile}}"
owner: "{{ postgresql_service_user }}"
group: "{{ postgresql_service_group }}"
mode: 0400
trim_blocks: no

- name: Repmgr | Generate ssh keypair for postgres user
command: "ssh-keygen -b 4096 -f {{repmgr_private_key_path}} -N ''"
args:
creates: "{{repmgr_private_key_path}}"
become: yes
become_user: "{{ postgresql_service_user }}"
when: repmgr_master == ansible_hostname

- name: Repmgr | Slurp ssh public key
slurp:
src: "{{repmgr_public_key_path}}"
register: ssh_public_key
when: repmgr_master == ansible_hostname

- name: Repmgr | Slurp ssh private key
slurp:
src: "{{repmgr_private_key_path}}"
register: ssh_private_key
when: repmgr_master == ansible_hostname

- name: Repmgr | Update authorized_keys for postgres user
authorized_key:
user: postgres
state: present
key: "{{ hostvars[repmgr_master]['ssh_public_key']['content'] | b64decode }}"

- name: Repmgr | Update public ssh key for postgres user
copy:
content: "{{hostvars[repmgr_master]['ssh_public_key']['content'] | b64decode}}"
dest: "{{repmgr_public_key_path}}"
owner: "{{ postgresql_service_user }}"
group: "{{ postgresql_service_group }}"
mode: 0644

- name: Repmgr | Update private ssh key for postgres user
copy:
content: "{{hostvars[repmgr_master]['ssh_private_key']['content'] | b64decode}}"
dest: "{{repmgr_private_key_path}}"
owner: "{{ postgresql_service_user }}"
group: "{{ postgresql_service_group }}"
mode: 0600

- name: Repmgr | Check cluster status
command: "{{postgresql_bin_directory}}/repmgr -f {{postgresql_conf_directory}}/repmgr.conf cluster show"
become: yes
become_user: "{{ postgresql_service_user }}"
changed_when: repmgr_cluster_show.rc != 0
register: repmgr_cluster_show
ignore_errors: True

- name: Repmgr | Register as primary
command: "{{postgresql_bin_directory}}/repmgr -f {{postgresql_conf_directory}}/repmgr.conf primary register"
become: yes
become_user: "{{ postgresql_service_user }}"
when: repmgr_master == ansible_hostname and not ansible_hostname in repmgr_cluster_show.stdout and not "primary" in repmgr_cluster_show.stdout

- name: Repmgr | Ensure postgresql slave is stopped before clone
service:
name: "postgresql-{{postgresql_version}}"
state: stopped
when: repmgr_master != ansible_hostname and not ansible_hostname in repmgr_cluster_show.stdout and not "standby" in repmgr_cluster_show.stdout

- name: Repmgr | Clone standby
command: "{{postgresql_bin_directory}}/repmgr -F -h {{repmgr_master}} -U repmgr -d repmgr -f {{postgresql_conf_directory}}/repmgr.conf standby clone"
become: yes
become_user: "{{ postgresql_service_user }}"
when: repmgr_master != ansible_hostname and not ansible_hostname in repmgr_cluster_show.stdout and not "standby" in repmgr_cluster_show.stdout

- name: Repmgr | Ensure postgresql slave is running after clone
service:
name: "postgresql-{{postgresql_version}}"
state: started
when: repmgr_master != ansible_hostname

- name: Repmgr | Wait for Postgres
wait_for:
timeout: 2
delegate_to: localhost

- name: Repmgr | Register standby
command: "{{postgresql_bin_directory}}/repmgr -F -h {{repmgr_master}} -U repmgr -d repmgr -f {{postgresql_conf_directory}}/repmgr.conf standby register"
become: yes
become_user: "{{ postgresql_service_user }}"
when: repmgr_master != ansible_hostname and not ansible_hostname in repmgr_cluster_show.stdout and not "standby" in repmgr_cluster_show.stdout

- name: Repmgr | Verify cluster functionality
command: "{{postgresql_bin_directory}}/repmgr -F -h {{repmgr_master}} -U repmgr -d repmgr -f {{postgresql_conf_directory}}/repmgr.conf cluster crosscheck"
become: yes
become_user: "{{ postgresql_service_user }}"

- name: Repmgr | Ensure repmgrd is running
service:
name: "repmgr{{postgresql_version}}"
state: started
enabled: yes
when: repmgr_monitoring_history == "true" or repmgr_failover == "automatic"

- name: Repmgr | Ensure crontab is installed
package:
name: crontabs
when: repmgr_monitoring_history == "true" or repmgr_failover == "automatic"

- name: Repmgr | Ensure crond is running
service:
name: "crond"
state: started
enabled: yes

- name: Repmgr | Setup cluster monitoring history cleanup
copy:
content: "{{postgresql_bin_directory}}/repmgr -f {{postgresql_conf_directory}}/repmgr.conf cluster cleanup --keep-history={{repmgr_keep_history_days}}"
dest: "/etc/cron.daily/repmgr_cleanup"
mode: 0755
when: repmgr_monitoring_history == "true" or repmgr_failover == "automatic"
28 changes: 28 additions & 0 deletions tasks/extensions/repmgr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# file: postgresql/tasks/extensions/contrib.yml

- name: PostgreSQL | Extensions | Make sure the postgres repmgr extensions are installed | apt
apt:
name: "postgresql-{{postgresql_version}}-repmgr"
state: present
update_cache: yes
cache_valid_time: "{{apt_cache_valid_time | default (3600)}}"
when: ansible_pkg_mgr == "apt"
notify:
- restart postgresql

- name: PostgreSQL | Extensions | Make sure the postgres repmgr extensions are installed | yum
yum:
name: "repmgr{{postgresql_version}}"
state: present
when: ansible_pkg_mgr == "yum"
notify:
- restart postgresql

- name: PostgreSQL | Extensions | Make sure the postgres repmgr extensions are installed | dnf
dnf:
name: "repmgr{{postgresql_version}}"
state: present
when: ansible_pkg_mgr == "dnf"
notify:
- restart postgresql

6 changes: 5 additions & 1 deletion tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,9 @@
when: monit_protection is defined and monit_protection == true
tags: [postgresql, postgresql-monit]

- import_tasks: extensions/configure_repmgr.yml
when: postgresql_ext_install_repmgr
tags: [postgresql, postgresql-extensions]

- import_tasks: check_pg_version_mismatch.yml
tags: [postgresql, postgresql-version-mismatch]
tags: [postgresql, postgresql-version-mismatch]
4 changes: 4 additions & 0 deletions templates/pgpass.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% for server in groups[repmgr_target_group] %}
{{ server }}:{{postgresql_port}}:{{repmgr_database}}:{{repmgr_user}}:{% for user in postgresql_users if user['name'] == repmgr_user %}{{user['pass']}}{% endfor %}
{{ server }}:{{postgresql_port}}:replication:{{repmgr_replication_user}}:{% for user in postgresql_users if user['name'] == repmgr_user %}{{user['pass']}}{% endfor %}
{% endfor %}
Loading