mirror of
https://github.com/famedly/ansible-collection-base
synced 2024-11-10 06:24:17 +00:00
feat: new postgresql role
This commit is contained in:
parent
01d9d8532f
commit
34ff7cefd7
7 changed files with 366 additions and 0 deletions
64
roles/postgresql/README.md
Normal file
64
roles/postgresql/README.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# `famedly.base.postgresql` ansible role for deploying PostgreSQL inside a docker container
|
||||
This role supports both PostgreSQL listening on a
|
||||
- UNIX socket on the host (mountable into other containers)
|
||||
- a TCP socket on a configurable port on the host
|
||||
at the same time.
|
||||
|
||||
It does not support changing the listening TCP port inside the container to anything other than the standard `5432`.
|
||||
|
||||
If both UNIX and TCP sockets are activated, the configuration of global options is done via UNIX socket.
|
||||
|
||||
Since the container sets the permissions of the UNIX socket to `0777`, we do not allow `trust` as auth method for local connections in `pg_hba.conf`.
|
||||
|
||||
## Requirements
|
||||
- psycopg2
|
||||
|
||||
## Role Variables
|
||||
See `defaults/main.yml`.
|
||||
|
||||
The `postgresql_superuser_password` variable is required and sets the password for the default user `postgres`.
|
||||
|
||||
In `postgresql_global_config_options` you can specify global config options in the form of `{ option: "listen_addresses", value: "*" }.
|
||||
|
||||
In the optional `postgresql_host_port` variable you can provide an unused port on the host which will be forwarded to the container port 5432.
|
||||
|
||||
By default `postgresql_connect_socket` is set to `true`, which will mount `postgresql_socket_path` into the container and thus provide the PostgreSQL UNIX socket on the host.
|
||||
You can disable this by setting the variable to `false`, PostgreSQL will still listen on the UNIX socket inside the container.
|
||||
|
||||
### Alternative docker image
|
||||
You can specify an alternative container image to use, for example if you want PostGIS support.
|
||||
If this image is based on the default `docker.io/postgres` image, this role should work as expected.
|
||||
However, this is not guaranteed.
|
||||
|
||||
This is an example for specifying the official PostGIS image:
|
||||
```yaml
|
||||
postgresql_version: "13"
|
||||
postgis_version: "3.1"
|
||||
postgresql_container_image_name: "docker.io/postgis/postgis"
|
||||
postgresql_container_image_tag: "{{ postgresql_version }}-{{ postgis_version }}-{{ postgresql_container_distro }}"
|
||||
```
|
||||
|
||||
You could also override the entire `postgresql_container_image` variable.
|
||||
|
||||
## Dependencies
|
||||
Docker needs to be installed and configured.
|
||||
|
||||
## Example Playbook
|
||||
```yaml
|
||||
---
|
||||
- name: Install PostgreSQL in a docker container
|
||||
hosts: [ all ]
|
||||
become: true
|
||||
roles:
|
||||
- famedly.base.postgresql
|
||||
vars:
|
||||
postgresql_host_port: "2345"
|
||||
postgresql_superuser_password: "{{ vault_postgresql_superuser_password }}"
|
||||
postgresql_connect_socket: "false"
|
||||
```
|
||||
|
||||
## License
|
||||
GNU Affero General Public License v3
|
||||
|
||||
## Author Information
|
||||
Famedly GmbH, famedly.de
|
31
roles/postgresql/defaults/main.yml
Normal file
31
roles/postgresql/defaults/main.yml
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
postgresql_user: postgresql
|
||||
postgresql_base_path: /opt/postgresql
|
||||
postgresql_data_path: "{{ postgresql_base_path }}/data"
|
||||
postgresql_socket_path: "{{ postgresql_base_path }}/sockets"
|
||||
postgresql_config_path: "{{ postgresql_base_path }}/config"
|
||||
postgresql_connect_socket: true
|
||||
|
||||
postgresql_container_image_name: "docker.io/postgres"
|
||||
postgresql_container_version: "13.4"
|
||||
postgresql_container_distro: alpine
|
||||
postgresql_container_image_tag: "{{ postgresql_container_version }}{{ '-' + postgresql_container_distro if postgresql_container_distro else '' }}"
|
||||
postgresql_container_image: "{{ postgresql_container_image_name }}:{{ postgresql_container_image_tag }}"
|
||||
postgresql_container_name: postgresql
|
||||
postgresql_container_labels: {}
|
||||
postgresql_container_ports: >-
|
||||
{{
|
||||
[] if postgresql_host_port is undefined else
|
||||
[ postgresql_host_port | string + ':5432' ]
|
||||
}}
|
||||
postgresql_container_networks: []
|
||||
postgresql_container_etc_hosts: {}
|
||||
postgresql_container_pull: true
|
||||
postgresql_container_recreate: false
|
||||
|
||||
postgresql_container_fd_soft_limit: "8192"
|
||||
postgresql_container_fd_hard_limit: "8192"
|
||||
postgresql_container_ulimits: ["nofile:{{ postgresql_container_fd_soft_limit }}:{{ postgresql_container_fd_hard_limit }}"]
|
||||
postgresql_container_memory_reservation: "256M"
|
||||
postgresql_container_memory: "512M"
|
||||
postgresql_container_shm_size: "128M"
|
8
roles/postgresql/handlers/main.yml
Normal file
8
roles/postgresql/handlers/main.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
|
||||
- name: Restart PostgreSQL docker container
|
||||
community.docker.docker_container:
|
||||
name: postgresql
|
||||
state: started
|
||||
restart: true
|
||||
listen: postgresql_container_restart
|
51
roles/postgresql/tasks/initialize.yml
Normal file
51
roles/postgresql/tasks/initialize.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
|
||||
- name: Create directories for PostgreSQL
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ postgresql_user_res.uid }}"
|
||||
group: "{{ postgresql_user_res.group }}"
|
||||
mode: 0755
|
||||
loop:
|
||||
- "{{ postgresql_base_path }}"
|
||||
- "{{ postgresql_data_path }}"
|
||||
- "{{ postgresql_socket_path }}"
|
||||
- "{{ postgresql_config_path }}"
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Template fake /etc/passwd for postgres
|
||||
ansible.builtin.template:
|
||||
src: postgresql-passwd.j2
|
||||
dest: "{{ postgresql_config_path }}/postgresql-passwd"
|
||||
owner: "{{ postgresql_user_res.uid }}"
|
||||
group: "{{ postgresql_user_res.group }}"
|
||||
mode: 0640
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Initialize PostgreSQL container
|
||||
community.docker.docker_container:
|
||||
name: "{{ postgresql_container_name }}"
|
||||
image: "{{ postgresql_container_image }}"
|
||||
ports: "{{ postgresql_container_ports }}"
|
||||
volumes: "{{ postgresql_container_init_volumes }}"
|
||||
labels: "{{ postgresql_container_labels_complete }}"
|
||||
networks: "{{ postgresql_container_networks }}"
|
||||
etc_hosts: "{{ postgresql_container_etc_hosts }}"
|
||||
user: "{{ postgresql_user_res.uid }}:{{ postgresql_user_res.group }}"
|
||||
state: started
|
||||
env:
|
||||
POSTGRES_PASSWORD: "{{ postgresql_superuser_password }}"
|
||||
register: postgresql_container
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Wait for container to be initialized
|
||||
wait_for:
|
||||
path: "{{ postgresql_socket_path }}/.s.PGSQL.5432"
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Stop initialized container
|
||||
community.docker.docker_container:
|
||||
name: "{{ postgresql_container_name }}"
|
||||
state: absent
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
146
roles/postgresql/tasks/main.yml
Normal file
146
roles/postgresql/tasks/main.yml
Normal file
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
|
||||
- name: Create system user to run postgresql as
|
||||
user:
|
||||
name: "{{ postgresql_user }}"
|
||||
state: present
|
||||
system: yes
|
||||
register: postgresql_user_res
|
||||
tags: ['prepare', 'prepare-postgresql',
|
||||
'deploy', 'deploy-postgresql']
|
||||
|
||||
- name: Ensure PostgreSQL container image is pulled
|
||||
community.docker.docker_image:
|
||||
name: "{{ postgresql_container_image }}"
|
||||
force_source: "{{ postgresql_container_pull }}"
|
||||
source: pull
|
||||
state: present
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Check if postgresql_data_path exists
|
||||
stat:
|
||||
path: "{{ postgresql_data_path }}"
|
||||
register: stat_postgresql_data_path
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Check if postgresql_data_path is empty
|
||||
find:
|
||||
paths: "{{ postgresql_data_path }}"
|
||||
file_type: any
|
||||
when: stat_postgresql_data_path.stat.exists
|
||||
register: find_postgresql_data_path
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Initialize
|
||||
include_tasks: initialize.yml
|
||||
when: (not stat_postgresql_data_path.stat.exists) or (find_postgresql_data_path is defined and find_postgresql_data_path.examined == 0)
|
||||
|
||||
- name: Template fake /etc/passwd for postgres
|
||||
ansible.builtin.template:
|
||||
src: postgresql-passwd.j2
|
||||
dest: "{{ postgresql_config_path }}/postgresql-passwd"
|
||||
owner: "{{ postgresql_user_res.uid }}"
|
||||
group: "{{ postgresql_user_res.group }}"
|
||||
mode: 0640
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Insert pg_hba.conf header
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ postgresql_config_path }}/pg_hba.conf"
|
||||
insertbefore: BOF
|
||||
line: "# Ansible managed"
|
||||
create: true
|
||||
mode: 0640
|
||||
owner: "{{ postgresql_user_res.uid }}"
|
||||
group: "{{ postgresql_user_res.group }}"
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Allow peer login for postgres user on socket
|
||||
community.postgresql.postgresql_pg_hba:
|
||||
dest: "{{ postgresql_config_path }}/pg_hba.conf"
|
||||
contype: local
|
||||
users: postgres
|
||||
method: peer
|
||||
options: map=root_postgres
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Insert pg_ident.conf header
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ postgresql_config_path }}/pg_ident.conf"
|
||||
insertbefore: BOF
|
||||
line: "# Ansible managed"
|
||||
create: true
|
||||
mode: 0640
|
||||
owner: "{{ postgresql_user_res.uid }}"
|
||||
group: "{{ postgresql_user_res.group }}"
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Insert pg_ident.conf root_postgres user map
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ postgresql_config_path }}/pg_ident.conf"
|
||||
insertafter: "# Ansible managed"
|
||||
line: "root_postgres\troot\tpostgres" # noqa no-tabs
|
||||
tags: [ 'prepare', 'prepare-postgresql' ]
|
||||
|
||||
- name: Ensure PostgreSQL container is started
|
||||
community.docker.docker_container:
|
||||
name: "{{ postgresql_container_name }}"
|
||||
image: "{{ postgresql_container_image }}"
|
||||
ports: "{{ postgresql_container_ports }}"
|
||||
volumes: "{{ postgresql_container_volumes_complete }}"
|
||||
labels: "{{ postgresql_container_labels_complete }}"
|
||||
networks: "{{ postgresql_container_networks }}"
|
||||
etc_hosts: "{{ postgresql_container_etc_hosts }}"
|
||||
ulimits: "{{ postgresql_container_ulimits }}"
|
||||
memory_reservation: "{{ postgresql_container_memory_reservation }}"
|
||||
memory: "{{ postgresql_container_memory }}"
|
||||
shm_size: "{{ postgresql_container_shm_size }}"
|
||||
recreate: "{{ postgresql_container_recreate }}"
|
||||
user: "{{ postgresql_user_res.uid }}:{{ postgresql_user_res.group }}"
|
||||
restart_policy: unless-stopped
|
||||
state: started
|
||||
register: postgresql_container
|
||||
tags: [ 'deploy', 'deploy-postgresql' ]
|
||||
|
||||
- name: Wait for container startup
|
||||
wait_for:
|
||||
path: "{{ postgresql_socket_path }}/.s.PGSQL.5432"
|
||||
when: postgresql_connect_socket
|
||||
tags: [ 'deploy', 'deploy-postgresql' ]
|
||||
|
||||
- name: Wait for container startup
|
||||
wait_for:
|
||||
port: "{{ postgresql_host_port }}"
|
||||
when: not postgresql_connect_socket
|
||||
tags: [ 'deploy', 'deploy-postgresql' ]
|
||||
|
||||
- name: Set superuser password
|
||||
community.postgresql.postgresql_user:
|
||||
name: postgres
|
||||
password: "{{ postgresql_superuser_password }}"
|
||||
login_host: "{{ postgresql_connection.login_host }}"
|
||||
tags: [ 'deploy', 'deploy-postgresql' ]
|
||||
|
||||
- name: Set global configuration options
|
||||
community.postgresql.postgresql_set:
|
||||
name: "{{ item.option }}"
|
||||
value: "{{ item.value }}"
|
||||
login_host: "{{ postgresql_connection.login_host }}"
|
||||
login_port: "{{ postgresql_connection.login_port }}"
|
||||
login_password: "{{ postgresql_connection.login_password }}"
|
||||
register: postgresql_set_result
|
||||
loop: "{{ postgresql_global_config_options }}"
|
||||
tags: [ 'deploy', 'deploy-postgresql' ]
|
||||
|
||||
# The above task sets global options, but only some of them require a restart
|
||||
# The below task notifies the restart handler only in these cases, preventing unnecessary downtime
|
||||
- name: Check and notify handler if restart is required
|
||||
debug:
|
||||
msg: "{{ item.item.option }} changed. Restart required: {{ item.restart_required }}"
|
||||
when: item.changed # noqa no-handler
|
||||
changed_when: item.restart_required
|
||||
notify: postgresql_container_restart
|
||||
loop: "{{ postgresql_set_result.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.option }}"
|
||||
tags: [ 'deploy', 'deploy-postgresql' ]
|
29
roles/postgresql/templates/postgresql-passwd.j2
Normal file
29
roles/postgresql/templates/postgresql-passwd.j2
Normal file
|
@ -0,0 +1,29 @@
|
|||
root:x:0:0:root:/root:/bin/ash
|
||||
bin:x:1:1:bin:/bin:/sbin/nologin
|
||||
daemon:x:2:2:daemon:/sbin:/sbin/nologin
|
||||
adm:x:3:4:adm:/var/adm:/sbin/nologin
|
||||
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
|
||||
sync:x:5:0:sync:/sbin:/bin/sync
|
||||
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
|
||||
halt:x:7:0:halt:/sbin:/sbin/halt
|
||||
mail:x:8:12:mail:/var/mail:/sbin/nologin
|
||||
news:x:9:13:news:/usr/lib/news:/sbin/nologin
|
||||
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
|
||||
operator:x:11:0:operator:/root:/sbin/nologin
|
||||
man:x:13:15:man:/usr/man:/sbin/nologin
|
||||
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
|
||||
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
|
||||
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
|
||||
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
|
||||
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
|
||||
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
|
||||
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
|
||||
games:x:35:35:games:/usr/games:/sbin/nologin
|
||||
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
|
||||
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
|
||||
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
|
||||
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
|
||||
guest:x:405:100:guest:/dev/null:/sbin/nologin
|
||||
nobody:x:65534:65534:nobody:/:/sbin/nologin
|
||||
{{ postgresql_user_res.name }}:x:{{ postgresql_user_res.uid }}:{{ postgresql_user_res.group }}::{{ postgresql_user_res.home }}:{{ postgresql_user_res.shell }}
|
||||
utmp:x:100:406:utmp:/home/utmp:/bin/false
|
37
roles/postgresql/vars/main.yml
Normal file
37
roles/postgresql/vars/main.yml
Normal file
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
postgresql_connection:
|
||||
login_host: >-
|
||||
{{
|
||||
postgresql_socket_path if postgresql_connect_socket
|
||||
else (postgresql_container.container.NetworkSettings.IPAddress if postgresql_host_port is undefined
|
||||
else "127.0.0.1")
|
||||
}}
|
||||
login_port: >-
|
||||
{{
|
||||
postgresql_host_port if postgresql_host_port is defined and not postgresql_connect_socket
|
||||
else "5432"
|
||||
}}
|
||||
login_password: "{{ postgresql_superuser_password }}"
|
||||
|
||||
postgresql_container_data_dir: /var/lib/postgresql/data
|
||||
postgresql_container_socket_dir: /var/run/postgresql
|
||||
|
||||
postgresql_container_volumes_base:
|
||||
- "{{ postgresql_data_path }}:{{ postgresql_container_data_dir }}:z"
|
||||
- "{{ postgresql_config_path }}/pg_hba.conf:{{ postgresql_container_data_dir }}/pg_hba.conf:ro"
|
||||
- "{{ postgresql_config_path }}/pg_ident.conf:{{ postgresql_container_data_dir }}/pg_ident.conf:ro"
|
||||
- "{{ postgresql_config_path }}/postgresql-passwd:/etc/passwd:ro"
|
||||
postgresql_container_volumes_complete: >-
|
||||
{{ postgresql_container_volumes_base
|
||||
+ ([postgresql_socket_path + ':' + postgresql_container_socket_dir + ':z']
|
||||
if postgresql_connect_socket else [])
|
||||
}}
|
||||
|
||||
postgresql_container_init_volumes:
|
||||
- "{{ postgresql_data_path }}:{{ postgresql_container_data_dir }}:z"
|
||||
- "{{ postgresql_socket_path }}:{{ postgresql_container_socket_dir }}:z"
|
||||
- "{{ postgresql_config_path }}/postgresql-passwd:/etc/passwd:ro"
|
||||
|
||||
postgresql_container_labels_base:
|
||||
version: "{{ postgresql_container_version }}"
|
||||
postgresql_container_labels_complete: "{{ postgresql_container_labels_base | combine(postgresql_container_labels) }}"
|
Loading…
Reference in a new issue