372 lines
No EOL
9.6 KiB
YAML
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" |