infra/ansible/roles/postgresql/tasks/main.yml

372 lines
No EOL
9.6 KiB
YAML

---
# Set up storage before installing PostgreSQL
- name: Set up storage for PostgreSQL
include_tasks: storage.yml
when: postgresql_storage_setup_enabled | default(false)
- name: Download PostgreSQL GPG key
ansible.builtin.get_url:
url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
dest: /usr/share/keyrings/postgresql-archive-keyring.asc
mode: '0644'
- name: Add PostgreSQL repository
ansible.builtin.deb822_repository:
name: pgdg
types: deb
uris: http://apt.postgresql.org/pub/repos/apt
suites: "{{ ansible_distribution_release }}-pgdg"
components: main
signed_by: /usr/share/keyrings/postgresql-archive-keyring.asc
state: present
- name: Install PostgreSQL and PostGIS
apt:
name:
- postgresql-{{ postgresql_version }}
- postgresql-client-{{ postgresql_version }}
- postgresql-contrib-{{ postgresql_version }}
- postgresql-{{ postgresql_version }}-postgis-3
- postgresql-{{ postgresql_version }}-postgis-3-scripts
- postgis
- python3-psycopg2
- libpq-dev
- pgbouncer
state: present
update_cache: yes
- name: Stop PostgreSQL service if running
systemd:
name: postgresql
state: stopped
ignore_errors: yes
- name: Disable PostgreSQL from starting automatically (we'll configure it first)
systemd:
name: postgresql
enabled: no
- name: Remove any default PostgreSQL data directory
file:
path: /var/lib/postgresql/{{ postgresql_version }}/main
state: absent
- name: Install locales package
apt:
name: locales
state: present
- name: Generate en_US.UTF-8 locale
locale_gen:
name: en_US.UTF-8
state: present
- name: Create PostgreSQL config directory structure
file:
path: "{{ item }}"
state: directory
owner: postgres
group: postgres
mode: '0755'
loop:
- /etc/postgresql
- /etc/postgresql/{{ postgresql_version }}
- /etc/postgresql/{{ postgresql_version }}/main
- name: Create PostgreSQL cluster on RAID array
command: |
/usr/lib/postgresql/{{ postgresql_version }}/bin/initdb \
-D {{ postgresql_data_directory }} \
--wal-segsize=64 \
--data-checksums \
--encoding=UTF8 \
--locale=C.UTF-8 \
--lc-collate=C.UTF-8 \
--lc-ctype=C.UTF-8
args:
creates: "{{ postgresql_data_directory }}/PG_VERSION"
become_user: postgres
environment:
PGDATA: "{{ postgresql_data_directory }}"
- name: Set up SSL/TLS
include_tasks: ssl.yml
- name: Configure PostgreSQL
template:
src: postgresql.conf.j2
dest: "{{ postgresql_config_directory }}/postgresql.conf"
owner: postgres
group: postgres
mode: '0644'
notify: restart postgresql
- name: Configure PostgreSQL authentication
template:
src: pg_hba.conf.j2
dest: "{{ postgresql_config_directory }}/pg_hba.conf"
owner: postgres
group: postgres
mode: '0640'
notify: restart postgresql
- name: Create sysctl configuration for PostgreSQL
template:
src: 30-postgresql.conf.j2
dest: /etc/sysctl.d/30-postgresql.conf
mode: '0644'
notify: reload sysctl
- name: Create systemd override directory
file:
path: /etc/systemd/system/postgresql@.service.d
state: directory
mode: '0755'
- name: Configure systemd limits for PostgreSQL
template:
src: postgresql-override.conf.j2
dest: /etc/systemd/system/postgresql@.service.d/override.conf
mode: '0644'
notify:
- reload systemd
- restart postgresql
- name: Check if PostgreSQL cluster is properly initialized
stat:
path: "{{ postgresql_data_directory }}/PG_VERSION"
register: pg_initialized
- name: Reinitialize PostgreSQL if corrupted
block:
- name: Stop PostgreSQL if running
systemd:
name: postgresql@{{ postgresql_version }}-main
state: stopped
ignore_errors: yes
- name: Remove corrupted data directory
file:
path: "{{ postgresql_data_directory }}"
state: absent
- name: Create data directory
file:
path: "{{ postgresql_data_directory }}"
state: directory
owner: postgres
group: postgres
mode: '0700'
- name: Clear WAL directory for reinit
file:
path: "{{ postgresql_wal_directory }}"
state: absent
- name: Create fresh WAL directory
file:
path: "{{ postgresql_wal_directory }}"
state: directory
owner: postgres
group: postgres
mode: '0700'
- name: Initialize new PostgreSQL cluster
command: |
/usr/lib/postgresql/{{ postgresql_version }}/bin/initdb \
-D {{ postgresql_data_directory }} \
--wal-segsize=64 \
--data-checksums \
--encoding=UTF8 \
--locale=C.UTF-8 \
--lc-collate=C.UTF-8 \
--lc-ctype=C.UTF-8 \
--waldir={{ postgresql_wal_directory }}
become_user: postgres
environment:
PGDATA: "{{ postgresql_data_directory }}"
when: pg_initialized.stat.exists and force_reinit | default(false)
- name: Ensure WAL directory is properly configured
block:
- name: Check if WAL directory exists
stat:
path: "{{ postgresql_wal_directory }}"
register: wal_dir_stat
- name: Create WAL directory if missing
file:
path: "{{ postgresql_wal_directory }}"
state: directory
owner: postgres
group: postgres
mode: '0700'
when: not wal_dir_stat.stat.exists
- name: Check if pg_wal is a symlink
stat:
path: "{{ postgresql_data_directory }}/pg_wal"
register: pg_wal_stat
- name: Ensure pg_wal symlink has correct ownership
file:
path: "{{ postgresql_data_directory }}/pg_wal"
owner: postgres
group: postgres
when: pg_wal_stat.stat.exists and pg_wal_stat.stat.islnk
- name: Start and enable PostgreSQL
systemd:
name: postgresql
state: started
enabled: yes
daemon_reload: yes
- name: Wait for PostgreSQL to start
wait_for:
port: 5432
host: localhost
delay: 5
- name: Handle password management with 1Password
include_tasks: 1password.yml
when: use_1password | default(false)
- name: Create PostgreSQL users
postgresql_user:
name: "{{ item.name }}"
password: "{{ item.password }}"
encrypted: yes
state: present
become: yes
become_user: postgres
loop: "{{ postgresql_users }}"
no_log: true
- name: Create databases
postgresql_db:
name: "{{ item.name }}"
owner: "{{ item.owner }}"
encoding: UTF-8
lc_collate: en_US.UTF-8
lc_ctype: en_US.UTF-8
template: template0
state: present
become: yes
become_user: postgres
loop: "{{ postgresql_databases }}"
- name: Enable PostGIS extension
postgresql_ext:
name: postgis
db: "{{ item.name }}"
state: present
become: yes
become_user: postgres
loop: "{{ postgresql_databases }}"
- name: Enable additional extensions
postgresql_ext:
name: "{{ item[1] }}"
db: "{{ item[0].name }}"
state: present
become: yes
become_user: postgres
loop: "{{ postgresql_databases | product(['pg_stat_statements', 'btree_gist', 'btree_gin']) | list }}"
- name: Grant privileges
postgresql_privs:
database: "{{ priv_item.split(':')[0] }}"
privs: "{{ priv_item.split(':')[1] }}"
type: database
role: "{{ user.name }}"
state: present
become: yes
become_user: postgres
loop: "{{ postgresql_users | selectattr('priv', 'defined') | map(attribute='priv') | list }}"
loop_control:
loop_var: priv_item
vars:
user: "{{ postgresql_users | selectattr('priv', 'equalto', priv_item) | first }}"
- name: Retrieve SCRAM hashes for PgBouncer
postgresql_query:
db: postgres
query: "SELECT rolname, rolpassword FROM pg_authid WHERE rolname = %s"
positional_args:
- "{{ item.name }}"
become: yes
become_user: postgres
loop: "{{ postgresql_users }}"
register: scram_hashes
no_log: true
- name: Configure PgBouncer
template:
src: pgbouncer.ini.j2
dest: /etc/pgbouncer/pgbouncer.ini
owner: postgres
group: postgres
mode: '0640'
notify: restart pgbouncer
- name: Configure PgBouncer authentication
template:
src: pgbouncer-userlist.txt.j2
dest: /etc/pgbouncer/userlist.txt
owner: postgres
group: postgres
mode: '0640'
notify: restart pgbouncer
- name: Enable and start PgBouncer
systemd:
name: pgbouncer
state: started
enabled: yes
- name: Set up backup script
template:
src: backup-postgresql.sh.j2
dest: /usr/local/bin/backup-postgresql
mode: '0755'
when: postgresql_backup_enabled
- name: Create backup directory
file:
path: "{{ postgresql_backup_directory }}"
state: directory
owner: postgres
group: postgres
mode: '0755'
when: postgresql_backup_enabled
- name: Set up backup cron job
cron:
name: "PostgreSQL backup"
minute: "{{ postgresql_backup_schedule.minute }}"
hour: "{{ postgresql_backup_schedule.hour }}"
day: "{{ postgresql_backup_schedule.day }}"
month: "{{ postgresql_backup_schedule.month }}"
weekday: "{{ postgresql_backup_schedule.weekday }}"
user: postgres
job: "/usr/local/bin/backup-postgresql"
when: postgresql_backup_enabled
- name: Configure log rotation
template:
src: postgresql-logrotate.j2
dest: /etc/logrotate.d/postgresql-custom
mode: '0644'
- name: Install monitoring script
template:
src: check-postgresql.sh.j2
dest: /usr/local/bin/check-postgresql
mode: '0755'
- name: Set up monitoring cron job
cron:
name: "Check PostgreSQL health"
minute: "*/5"
job: "/usr/local/bin/check-postgresql || echo 'PostgreSQL health check failed' | logger -t postgresql-health"