This commit is contained in:
2026-01-29 12:55:01 +01:00
commit 65edf17dc7
15 changed files with 435 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
secrets.yml
inventory.ini

160
INSTALL.md Normal file
View File

@@ -0,0 +1,160 @@
# PostgreSQL Production Deployment Guide via Ansible
## 1. Instalacja serwerów
Dwa serwery Debian 12:
| Serwer | Rola | IP |
| ------ | ------- | -------- |
| pg-1 | Primary | 10.0.0.1 |
| pg-2 | Replica | 10.0.0.2 |
Na każdym serwerze wykonaj:
```bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y sudo vim curl gnupg rsync unzip
```
Opcjonalnie utwórz konto do Ansible:
```bash
sudo adduser ansible
sudo usermod -aG sudo ansible
```
---
## 2. Konfiguracja IP i DNS
Edytuj `/etc/hosts` na obu serwerach:
**pg-1 (/etc/hosts)**
```
10.0.0.1 pg1
10.0.0.2 pg2
```
**pg-2 (/etc/hosts)**
```
10.0.0.1 pg1
10.0.0.2 pg2
```
Upewnij się, że hostnames `pg1` i `pg2` są rozpoznawane w sieci.
---
## 3. Przygotowanie repozytorium Ansible na pg-1
Skopiuj paczkę Ansible na pg-1:
```bash
scp postgres_ansible_prod_full.zip user@pg-1:/home/user/
ssh pg-1
unzip postgres_ansible_prod_full.zip -d ~/postgres_ansible_prod
cd ~/postgres_ansible_prod
```
Edytuj `inventory.ini`, jeśli IP lub hostnames są inne:
```ini
[primary]
pg1 ansible_host=10.0.0.1
[replica]
pg2 ansible_host=10.0.0.2
[all:vars]
ansible_user=root
pg_version=16
pg_cluster=main
pg_data=/var/lib/postgresql/16/main
```
Ustaw hasła w `group_vars/all/secrets.yml`:
```yaml
replicator_password: "SILNE_HASLO_REPL"
mailuser_password: "SILNE_HASLO_DB"
```
Opcjonalnie zaszyfruj je Ansible Vault:
```bash
ansible-vault encrypt group_vars/all/secrets.yml
```
---
## 4. Uruchomienie playbooka
Zainstaluj Ansible na pg-1:
```bash
sudo apt install -y ansible
ansible --version
```
Test połączenia z pg-2:
```bash
ansible -i inventory.ini all -m ping
```
Uruchomienie wdrożenia:
```bash
ansible-playbook -i inventory.ini site.yml
```
Playbook automatycznie:
- Instaluje PostgreSQL 16 na obu serwerach
- Konfiguruje primary i replica
- Tworzy użytkownika `mailuser` i bazę `mailarchiver`
- Ustawia pgBackRest i cron backupów (full i incremental)
---
## 5. Test działania
Na pg-1:
```bash
sudo -u postgres psql -c "\l"
sudo -u postgres psql -c "\du"
```
Sprawdź replikację:
```bash
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
```
Test backupów (pgBackRest):
```bash
sudo -u postgres pgbackrest --stanza=main info
```
---
## 6. Przywracanie w razie awarii
Jeśli pg-1 padnie, przywracasz z pg-2:
```bash
ansible-playbook -i inventory.ini restore_primary.yml
```
Ręczny failover (promowanie pg-2 na primary):
```bash
sudo -u postgres pg_ctl promote
```
Następnie zmień DNS lub connection string w aplikacji, aby wskazywał nowy primary.

59
README.md Normal file
View File

@@ -0,0 +1,59 @@
# README.md
# PostgreSQL Production Setup via Ansible
Kompletne wdrożenie PostgreSQL 16 z repliką, backupami, PITR i bazą MailArchiver.
## Struktura repo
```
ansible/
├── inventory.ini
├── site.yml
├── group_vars/all/secrets.yml
└── roles/
├── common/
│ └── tasks/main.yml
├── postgres/
│ ├── tasks/main.yml
│ └── templates/
│ ├── postgresql.conf.j2
│ └── pg_hba.conf.j2
├── replication/
│ └── tasks/main.yml
├── pgbackrest/
│ ├── tasks/main.yml
│ └── templates/pgbackrest.conf.j2
└── mailarchiver_db/
└── tasks/main.yml
```
## Instalacja i konfiguracja
[INSTALL.md](INSTALL.md)
## Backup
- Full backup: niedziela 2:00
- Incremental backup: codziennie 2:00
- WAL archiving dla PITR
## Failover ręczny
Na pg2:
```bash
sudo -u postgres pg_ctl promote
```
Następnie zmień aplikację, aby łączyła się z nowym primary.
---
## Uwagi produkcyjne
- Aplikacja musi łączyć się tylko z primary
- Repozytorium backupu na pg2
- Monitoruj `/pgbackrest` i WAL
- Wersja z Ansible Vault chroni hasła
- Test restore co tydzień zalecany

View File

@@ -0,0 +1,2 @@
replicator_password: "SILNE_HASLO_REPL"
mailuser_password: "SILNE_HASLO_DB"

11
inventory.ini.example Normal file
View File

@@ -0,0 +1,11 @@
[primary]
pg1 ansible_host=10.0.0.1
[replica]
pg2 ansible_host=10.0.0.2
[all:vars]
ansible_user=root
pg_version=16
pg_cluster=main
pg_data=/var/lib/postgresql/16/main

View File

@@ -0,0 +1,8 @@
- name: Basic packages
apt:
name:
- vim
- curl
- gnupg
- rsync
update_cache: yes

View File

@@ -0,0 +1,20 @@
- name: Create db user
become_user: postgres
postgresql_user:
name: mailuser
password: "{{ mailuser_password }}"
- name: Create database
become_user: postgres
postgresql_db:
name: mailarchiver
owner: mailuser
- name: Grant schema rights
become_user: postgres
postgresql_query:
db: mailarchiver
query: |
GRANT ALL ON SCHEMA public TO mailuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON TABLES TO mailuser;

View File

@@ -0,0 +1,37 @@
- name: Install pgBackRest
apt:
name: pgbackrest
state: present
- name: Repo dir
file:
path: /pgbackrest
state: directory
owner: postgres
mode: "750"
when: inventory_hostname in groups['replica']
- name: Config
template:
src: pgbackrest.conf.j2
dest: /etc/pgbackrest.conf
- name: Create stanza
become_user: postgres
command: pgbackrest --stanza=main stanza-create
when: inventory_hostname in groups['primary']
- name: Full backup weekly
cron:
name: "pgbackrest full"
weekday: 0
hour: 2
minute: 0
job: "pgbackrest --stanza=main backup --type=full"
- name: Incremental backup daily
cron:
name: "pgbackrest incr"
hour: 2
minute: 0
job: "pgbackrest --stanza=main backup --type=incr"

View File

@@ -0,0 +1,11 @@
[main]
pg1-path=/var/lib/postgresql/16/main
[global]
{% if inventory_hostname in groups['primary'] %}
repo1-host=pg2
{% endif %}
repo1-path=/pgbackrest
repo1-retention-full=2
repo1-retention-diff=7
start-fast=y

View File

@@ -0,0 +1,47 @@
- name: Install PostgreSQL
apt:
name:
- postgresql-{{ pg_version }}
- postgresql-contrib
update_cache: yes
- name: Stop postgres before config
service:
name: postgresql
state: stopped
- name: postgresql.conf
template:
src: postgresql.conf.j2
dest: /etc/postgresql/{{ pg_version }}/main/postgresql.conf
- name: pg_hba.conf
template:
src: pg_hba.conf.j2
dest: /etc/postgresql/{{ pg_version }}/main/pg_hba.conf
- name: TLS cert dir
file:
path: /etc/postgresql/ssl
state: directory
owner: postgres
mode: "700"
- name: Copy TLS cert
copy:
src: pg.crt
dest: /etc/postgresql/ssl/pg.crt
owner: postgres
mode: "600"
- name: Copy TLS key
copy:
src: pg.key
dest: /etc/postgresql/ssl/pg.key
owner: postgres
mode: "600"
- name: Start postgres
service:
name: postgresql
state: started

View File

@@ -0,0 +1,3 @@
local all postgres peer
host all all 10.0.0.0/24 md5
host replication replicator 10.0.0.2/32 md5

View File

@@ -0,0 +1,13 @@
listen_addresses = '*'
shared_buffers = 2GB
effective_cache_size = 6GB
work_mem = 64MB
maintenance_work_mem = 1GB
wal_compression = on
checkpoint_timeout = 15min
max_wal_size = 64GB
archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'

View File

@@ -0,0 +1,28 @@
- name: Create replicator user (on primary)
delegate_to: pg1
become_user: postgres
postgresql_user:
name: replicator
password: "{{ replicator_password }}"
role_attr_flags: REPLICATION,LOGIN
- name: Stop postgres
service:
name: postgresql
state: stopped
- name: Remove old data
file:
path: "{{ pg_data }}"
state: absent
- name: Base backup
become_user: postgres
command: >
pg_basebackup -h pg1 -D {{ pg_data }}
-U replicator -Fp -Xs -P -R
- name: Start postgres
service:
name: postgresql
state: started

View File

@@ -0,0 +1,19 @@
- name: Stop postgres on primary
service:
name: postgresql
state: stopped
- name: Remove old data on primary
file:
path: "{{ pg_data }}"
state: absent
- name: Restore base backup from replica
become_user: postgres
command: >
pg_basebackup -h pg2 -D {{ pg_data }} -U replicator -Fp -Xs -P -R
- name: Start postgres on primary
service:
name: postgresql
state: started

15
site.yml Normal file
View File

@@ -0,0 +1,15 @@
- hosts: all
roles:
- common
- hosts: primary
roles:
- postgres
- mailarchiver_db
- pgbackrest
- hosts: replica
roles:
- postgres
- replication
- pgbackrest