├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── ansible.cfg ├── defaults └── main.yml ├── hosts └── production ├── requirements.txt ├── role.yml ├── tasks ├── bootstrap.yml ├── configure-master.yml ├── configure-slaves.yml ├── databases.yml ├── flask.yml ├── install.yml └── users.yml └── templates ├── flaskpgsql.conf ├── pg_hba.conf.j2 └── recovery.conf.j2 /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "library/cloudstack"] 2 | path = library/cloudstack 3 | url = https://github.com/resmo/ansible-cloudstack.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Loic Lambiel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: ansible-install 2 | 3 | ansible-install: 4 | pip install -r requirements.txt 5 | git submodule sync 6 | git submodule update --init 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ansible-pgsql-demo 2 | 3 | An Ansible playbook to deploy postgresql in a master - slave replication configuration on [Exoscale](https://www.exoscale.com/) cloud, including continous wal archiving in Exoscale [S3 compatible object store](https://www.exoscale.com/object-storage/) using wal-e [wal-e](https://github.com/wal-e/wal-e). 4 | 5 | The goal of this playbook is to demonstrate how fast & easy it can be to deploy applications with high availability into the cloud using Ansible. 6 | 7 | ## Setup 8 | 9 | ### Prerequisites 10 | 11 | ``` 12 | apt-get update && apt-get install -y git python-pip python-dev build-essential 13 | git clone https://github.com/exoscale/ansible-pgsql-demo 14 | cd ansible-pgsql-demo 15 | make 16 | ``` 17 | 18 | ### API Key setup 19 | 20 | You need to setup your API keys. The Key & secret can be found within your [portal](https://portal.exoscale.com) under the account section. 21 | 22 | ``` 23 | cat $HOME/.cloudstack.ini 24 | [cloudstack] 25 | endpoint = https://api.exoscale.ch/compute 26 | key = Your API Key here 27 | secret = Your secret key here 28 | ``` 29 | 30 | ### SSH key setup 31 | 32 | Your SSH RSA public key must be present at the following location: '~/.ssh/id_rsa.pub' 33 | 34 | ### Variable setup 35 | 36 | A few variables must be setup prior executing this playbook. These variables are located in the following file: 37 | 38 | ``` 39 | defaults/main.yml 40 | ``` 41 | 42 | The following variables needs be edited: 43 | 44 | * aws_key: "mykey" 45 | * aws_secret: "mysecret" 46 | * aws_bucket: "s3://mybucket/directory/or/whatever" 47 | 48 | The bucket must be created prior running the playbook. [s3cmd](http://s3tools.org/s3cmd) or any S3 compatible tool may be used for this purpose. 49 | 50 | ## Running the playbook 51 | 52 | To run the playbook: 53 | 54 | ``` 55 | ansible-playbook -v role.yml 56 | ``` 57 | 58 | ## Notice 59 | 60 | This playbook has been tested only on Ubuntu 14.04. 61 | 62 | It is inspired from [this] (https://github.com/lesovsky/ansible-postgresql-on-el6) playbook. 63 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | forks=20 3 | remote_user = root 4 | hostfile = ./hosts/production 5 | host_key_checking = false 6 | ansible_managed = Warning: File is managed by Ansible 7 | roles_path = playbooks/roles 8 | library = ./library 9 | gathering = smart 10 | [ssh_connection] 11 | ssh_args = -o ControlMaster=auto -o ControlPersist=60s 12 | pipelining = true 13 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | # file: defaults/main.yml -- main variables 2 | 3 | # Basic settings 4 | postgresql_version: 9.3 5 | 6 | # Stop postgresql service if started and drop cluster if existing. 7 | postgresql_drop_old_cluster: yes 8 | 9 | # Enable some options for streaming replication purposes 10 | postgresql_streaming_user: 11 | name: replica 12 | pass: replica1234 13 | 14 | # Specify the main data directory 15 | postgresql_main_directory: /var/lib/postgresql/{{ postgresql_version }}/main/ 16 | 17 | # Specify a directory for the logs if the default directory does not plan to use 18 | postgresql_log_directory: /var/log/postgresql 19 | 20 | # PostgreSQL users and roles 21 | postgresql_admin_user: "postgres" 22 | postgresql_users: 23 | - { name: "johndoe", pass: "test1234", flags: "LOGIN,SUPERUSER" } 24 | 25 | # PostgreSQL databases and settings. Do not remove template, encoding, collate, ctype options from postgresql_database because it most certainly lead to Ansible role inoperability. 26 | postgresql_encoding: UTF8 27 | postgresql_locale: en_US.UTF-8 28 | postgresql_databases: 29 | - { name: "db1", owner: "postgres", template: "template0", encoding: "{{ postgresql_encoding }}", collate: "{{ postgresql_locale }}", ctype: "{{ postgresql_locale }}" } 30 | 31 | # Settings related to the pg_hba rules 32 | postgresql_default_unix_auth_method: "trust" 33 | postgresql_default_ipv4_auth_method: "md5" 34 | postgresql_default_ipv6_auth_method: "md5" 35 | 36 | # Specify default rules for pg_hba.conf. Change them only if it is really necessary. 37 | postgresql_pg_hba_default: 38 | - { type: local, database: all, role: "{{ postgresql_admin_user }}", address: "", method: "{{ postgresql_default_unix_auth_method }}", comment: '"local" is for Unix domain socket connections only' } 39 | - { type: host, database: all, role: all, address: "127.0.0.1/32", method: "{{ postgresql_default_ipv4_auth_method }}", comment: 'IPv4 local connections:' } 40 | - { type: host, database: all, role: all, address: "::1/128", method: "{{ postgresql_default_ipv6_auth_method }}", comment: 'IPv6 local connections:' } 41 | 42 | # Specify custom rules for pg_hba.conf. Specify here all necessary pg_hba rules. 43 | postgresql_pg_hba_custom: 44 | - { type: host, database: replication, role: "{{ postgresql_admin_user }}", address: "127.0.0.1/32", method: "{{ postgresql_default_ipv4_auth_method }}", comment: '' } 45 | 46 | # PostgreSQL parameters which will appears in the postgresql.conf. Be aware, some parameters from newer postgresql versions, does not supported in the elder postrgesql versions, and may lead to the case when postgresql service does not start 47 | postgresql_conf_default_guc: 48 | - { regexp: "^#?listen_addresses = .*$", guc: "listen_addresses = '*'" } 49 | - { regexp: "^#?wal_level = .*$", guc: "wal_level = {{'hot_standby'}}" } 50 | - { regexp: "^#?max_wal_senders = .*$", guc: "max_wal_senders = {{ 3 }}" } 51 | - { regexp: "^#?wal_keep_segments = .*$", guc: "wal_keep_segments = {{ 256 }}" } 52 | - { regexp: "^#?hot_standby = .*$", guc: "hot_standby = {{ 'on' }}" } 53 | 54 | # pip install extra args 55 | pip_install_args: "-q" 56 | 57 | # Wal-e S3 configuration 58 | aws_key: "mykey" 59 | aws_secret: "mysecret" 60 | aws_bucket: "s3://mybucket/directory/or/whatever" 61 | aws_endpoint: "https+path://sos.exo.io:443" 62 | 63 | # Cloudstack template to use 64 | cs_template: "Linux Ubuntu 14.04 LTS 64-bit" 65 | 66 | # Deploy test flask app 67 | deploy_flask_testapp: False 68 | -------------------------------------------------------------------------------- /hosts/production: -------------------------------------------------------------------------------- 1 | [pgsql] 2 | 3 | [pgmaster] 4 | 5 | [pgslaves] 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible==1.9.5 2 | markupsafe 3 | cs 4 | netaddr 5 | sshpubkeys 6 | -------------------------------------------------------------------------------- /role.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Install and setup PostgreSQL Streaming Replication 3 | - name: bootstrap 4 | hosts: localhost 5 | vars_files: 6 | - '../defaults/main.yml' 7 | include: 'tasks/bootstrap.yml' 8 | 9 | - name: Base setup 10 | hosts: pgsql 11 | vars_files: 12 | - '../defaults/main.yml' 13 | include: 'tasks/install.yml' 14 | 15 | - name: setup master 16 | hosts: pgmaster 17 | vars_files: 18 | - 'defaults/main.yml' 19 | tasks: 20 | - include: 'tasks/configure-master.yml' 21 | - include: 'tasks/users.yml' 22 | - include: 'tasks/databases.yml' 23 | 24 | - name: setup slaves 25 | hosts: pgslaves 26 | vars_files: 27 | - 'defaults/main.yml' 28 | tasks: 29 | - include: 'tasks/configure-slaves.yml' 30 | 31 | - name: setup test flask app 32 | hosts: pgsql 33 | vars_files: 34 | - 'defaults/main.yml' 35 | tasks: 36 | - include: 'tasks/flask.yml' 37 | when: deploy_flask_testapp 38 | 39 | -------------------------------------------------------------------------------- /tasks/bootstrap.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Bootstrap: SSH key setup" 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | tasks: 7 | 8 | - name: "Bootstrap: ensure SSH key exist" 9 | cs_sshkeypair: 10 | name: demo 11 | public_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" 12 | 13 | - name: "Bootstrap: security group setup" 14 | hosts: localhost 15 | connection: local 16 | gather_facts: no 17 | tasks: 18 | 19 | - name: "Bootstrap: ensure security groups exists" 20 | cs_securitygroup: 21 | name: '{{ item }}' 22 | with_items: 23 | - Demo 24 | 25 | - name: "Bootstrap: add inbound rules to security group Demo" 26 | cs_securitygroup_rule: 27 | security_group: Demo 28 | start_port: '{{ item }}' 29 | end_port: '{{ item }}' 30 | with_items: 31 | - 22 32 | 33 | - name: "Bootstrap: allow PGSQL traffic between instances" 34 | cs_securitygroup_rule: 35 | security_group: Demo 36 | user_security_group: Demo 37 | start_port: 5432 38 | end_port: 5432 39 | protocol: '{{ item }}' 40 | with_items: 41 | - tcp 42 | 43 | - name: "Bootstrap: ensure affinity groug exists" 44 | cs_affinitygroup: 45 | name: pgsql 46 | affinty_type: host anti-affinity 47 | 48 | - name: "Bootstrap: deploy Instances" 49 | hosts: localhost 50 | connection: local 51 | gather_facts: no 52 | tasks: 53 | 54 | - name: "Bootstrap: deploy PGDB Master" 55 | cs_instance: 56 | name: PGSQL-MASTER 57 | template: "{{ cs_template }}" 58 | service_offering: Small 59 | security_groups: 60 | - Demo 61 | affinity_groups: pgsql 62 | ssh_key: demo 63 | register: pgmaster 64 | 65 | - name: "Bootstrap: deploy PGDB Slave" 66 | cs_instance: 67 | name: PGSQL-SLAVE1 68 | template: "{{ cs_template }}" 69 | service_offering: Small 70 | security_groups: 71 | - Demo 72 | affinity_groups: pgsql 73 | ssh_key: demo 74 | register: pgslave 75 | 76 | - name: "Bootstrap: assing IP to the inventory" 77 | add_host: hostname={{ pgmaster.default_ip }} groups=pgsql,pgmaster 78 | 79 | - name: "Bootstrap: assing IP to the inventory" 80 | add_host: hostname={{ pgslave.default_ip }} groups=pgsql,pgslaves 81 | 82 | - name: "Bootstrap: waiting for SSH to come up" 83 | wait_for: port=22 host={{ pgmaster.default_ip }} 84 | 85 | - name: "Bootstrap: waiting for SSH to come up" 86 | wait_for: port=22 host={{ pgslave.default_ip }} 87 | 88 | 89 | -------------------------------------------------------------------------------- /tasks/configure-master.yml: -------------------------------------------------------------------------------- 1 | # file: tasks/configure-master.yml -- postgresql pre-start configuration of master server and startup 2 | 3 | - name: "Configure-master: stop old postgresql service" 4 | service: name="postgresql" state=stopped 5 | when: postgresql_drop_old_cluster 6 | 7 | - name: "Configure-master:: remove existing data dir" 8 | file: path="/var/lib/postgresql/{{ postgresql_version }}/main" state=absent 9 | when: postgresql_drop_old_cluster 10 | 11 | - name: "Configure-master:: initialize new postgresql cluster" 12 | sudo: yes 13 | sudo_user: "{{ postgresql_admin_user }}" 14 | command: "/usr/lib/postgresql/{{ postgresql_version }}/bin/initdb -D /var/lib/postgresql/{{ postgresql_version }}/main" 15 | when: postgresql_drop_old_cluster 16 | 17 | - name: "Configure-master: configure pg_hba.conf" 18 | template: 19 | src: pg_hba.conf.j2 20 | dest: "/etc/postgresql/{{ postgresql_version }}/main/pg_hba.conf" 21 | owner: "{{ postgresql_admin_user }}" 22 | group: "{{ postgresql_admin_user }}" 23 | mode: 0640 24 | 25 | - name: "Configure-master: configure postgresql.conf" 26 | lineinfile: 27 | dest: "/etc/postgresql/{{ postgresql_version }}/main/postgresql.conf" 28 | state: present 29 | regexp: "{{ item.regexp }}" 30 | line: "{{ item.guc }}" 31 | with_items: postgresql_conf_default_guc 32 | 33 | - name: "Configure-master: enable archive mode" 34 | lineinfile: 35 | dest: "/etc/postgresql/{{ postgresql_version }}/main/postgresql.conf" 36 | state: present 37 | regexp: "^#?archive_mode = .*$" 38 | line: "archive_mode = 'on'" 39 | 40 | - name: "Configure-master: set archive mode timeout" 41 | lineinfile: 42 | dest: "/etc/postgresql/{{ postgresql_version }}/main/postgresql.conf" 43 | state: present 44 | regexp: "^#?archive_timeout = .*$" 45 | line: "archive_timeout = 60 " 46 | 47 | - name: "Configure-master: set archive mode command" 48 | lineinfile: 49 | dest: "/etc/postgresql/{{ postgresql_version }}/main/postgresql.conf" 50 | state: present 51 | regexp: "^#?archive_command = .*$" 52 | line: "archive_command = 'envdir /etc/wal-e.d/env /usr/local/bin/wal-e wal-push {{ postgresql_main_directory }}%p'" 53 | 54 | - name: "Configure-master: create postgresql log directory" 55 | file: 56 | path: "{{ postgresql_log_directory }}" 57 | state: directory 58 | owner: "{{ postgresql_admin_user }}" 59 | group: "{{ postgresql_admin_user }}" 60 | mode: 0755 61 | when: postgresql_log_directory is defined 62 | 63 | - name: "Configure-master: start postgresql service" 64 | service: name="postgresql" state=started enabled=yes 65 | -------------------------------------------------------------------------------- /tasks/configure-slaves.yml: -------------------------------------------------------------------------------- 1 | # file: tasks/configure-slaves.yml -- postgresql pre-start configuration of the slave servers and startup 2 | 3 | - name: "Configure-slaves: stop old postgresql service" 4 | service: name="postgresql" state=stopped 5 | when: postgresql_drop_old_cluster 6 | 7 | - name: "Configure-slaves: remove existing data dir" 8 | file: path="/var/lib/postgresql/{{ postgresql_version }}/main" state=absent 9 | when: postgresql_drop_old_cluster 10 | 11 | - name: "Configure-slaves: write .pgpass for postgres user" 12 | shell: "echo '*:*:*:{{ postgresql_streaming_user.name }}:{{ postgresql_streaming_user.pass }}' > ~postgres/.pgpass" 13 | 14 | - name: "Configure-slaves: change permissions on .pgpass" 15 | file: 16 | path: ~postgres/.pgpass 17 | state: file 18 | owner: "{{ postgresql_admin_user }}" 19 | group: "{{ postgresql_admin_user }}" 20 | mode: 0600 21 | 22 | - name: "Configure-slaves: initialize new postgresql cluster" 23 | command: "pg_basebackup -c fast -X stream -h {{ hostvars[groups['pgmaster'][0]]['ansible_default_ipv4']['address'] }} -U {{ postgresql_streaming_user.name }} -D /var/lib/postgresql/{{ postgresql_version }}/main" 24 | sudo: True 25 | sudo_user: postgres 26 | when: postgresql_drop_old_cluster 27 | 28 | - name: "Configure-slaves: configure pg_hba.conf" 29 | template: 30 | src: pg_hba.conf.j2 31 | dest: "/etc/postgresql/{{ postgresql_version }}/main/pg_hba.conf" 32 | owner: "{{ postgresql_admin_user }}" 33 | group: "{{ postgresql_admin_user }}" 34 | mode: 0640 35 | 36 | - name: "Configure-slaves: configure postgresql.conf" 37 | lineinfile: 38 | dest: "/etc/postgresql/{{ postgresql_version }}/main/postgresql.conf" 39 | state: present 40 | regexp: "{{ item.regexp }}" 41 | line: "{{ item.guc }}" 42 | with_items: postgresql_conf_default_guc 43 | 44 | - name: "Configure-slaves: configure recovery.conf" 45 | template: 46 | src: recovery.conf.j2 47 | dest: "/var/lib/postgresql/{{ postgresql_version }}/main/recovery.conf" 48 | owner: "{{ postgresql_admin_user }}" 49 | group: "{{ postgresql_admin_user }}" 50 | mode: 0640 51 | 52 | - name: "Configure-slaves: create postgresql log directory" 53 | file: 54 | path: "{{ postgresql_log_directory }}" 55 | state: directory 56 | owner: "{{ postgresql_admin_user }}" 57 | group: "{{ postgresql_admin_user }}" 58 | mode: 0755 59 | when: postgresql_log_directory is defined 60 | 61 | - name: "Configure-slaves: start postgresql service" 62 | service: name="postgresql" state=started enabled=yes 63 | -------------------------------------------------------------------------------- /tasks/databases.yml: -------------------------------------------------------------------------------- 1 | # file: tasks/databases.yml -- manage databases 2 | 3 | - name: "Configure_databases: add databases" 4 | postgresql_db: 5 | state: present 6 | name: "{{ item.name }}" 7 | template: "{{ item.template }}" 8 | encoding: "{{ item.encoding }}" 9 | lc_collate: "{{ item.collate }}" 10 | lc_ctype: "{{ item.ctype }}" 11 | owner: "{{ item.owner }}" 12 | with_items: postgresql_databases 13 | when: postgresql_databases|length > 0 14 | -------------------------------------------------------------------------------- /tasks/flask.yml: -------------------------------------------------------------------------------- 1 | # file: tasks/flask.yml 2 | 3 | - name: "Flask: install flask binaries" 4 | pip: name={{item}} extra_args="{{ pip_install_args }}" 5 | with_items: 6 | - flask 7 | - Flask-SQLAlchemy 8 | 9 | - name: "Flask: clone flask test app" 10 | git: repo=https://github.com/llambiel/flask-pgsql-demo.git dest=/srv/flask-pgsql-demo 11 | 12 | - name: "Add flask test app upstart job" 13 | template: 14 | src: flaskpgsql.conf 15 | dest: "/etc/init/flaskpgsql.conf" 16 | owner: "root" 17 | group: "root" 18 | mode: 0640 19 | 20 | - name: "Flask: start our flask test app" 21 | service: name=flaskpgsql state=started 22 | 23 | -------------------------------------------------------------------------------- /tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "Install: Setup PGSQL" 4 | remote_user: root 5 | hosts: pgsql 6 | gather_facts: yes 7 | pre_tasks: 8 | - name: apt-get update 9 | apt: update_cache=yes cache_valid_time=86400 10 | 11 | tasks: 12 | 13 | - name: "Install: Build hosts file" 14 | lineinfile: dest=/etc/hosts regexp='{{ ansible_default_ipv4.address }}' line="{{ ansible_default_ipv4.address }} {{ ansible_hostname }}" state=present 15 | 16 | - name: "Install: install packages" 17 | apt: pkg={{ item }} 18 | with_items: 19 | - postgresql 20 | - postgresql-contrib 21 | - postgresql-client 22 | - git 23 | - libpq-dev 24 | - python-psycopg2 25 | - python-pip 26 | - python-dev 27 | - daemontools 28 | - libffi-dev 29 | - lzop 30 | 31 | - name: "Install: Upgrade pip" 32 | pip: name={{item}} extra_args=--upgrade 33 | with_items: 34 | - pip 35 | 36 | - name: "Install: install WAL-E" 37 | pip: name=wal-e extra_args="{{ pip_install_args }}" 38 | 39 | - name: "Install: Create wal-e env dir" 40 | file: path=/etc/wal-e.d/env state=directory mode=0750 owner=root group=postgres force=yes 41 | 42 | - name: "Install: Create wal-e AWS access key config" 43 | lineinfile: dest=/etc/wal-e.d/env/AWS_ACCESS_KEY_ID line="{{ aws_key }}" owner=root group=postgres state=present create=yes mode=0640 44 | 45 | - name: "Install: Create wal-e AWS secret key config" 46 | lineinfile: dest=/etc/wal-e.d/env/AWS_SECRET_ACCESS_KEY line="{{ aws_secret }}" owner=root group=postgres state=present create=yes mode=0640 47 | 48 | - name: "Install: Create wal-e AWS bucket config" 49 | lineinfile: dest=/etc/wal-e.d/env/WALE_S3_PREFIX line="{{ aws_bucket }}" owner=root group=postgres state=present create=yes mode=0640 50 | 51 | - name: "Install: Create wal-e AWS endpoint" 52 | lineinfile: dest=/etc/wal-e.d/env/WALE_S3_ENDPOINT line="{{ aws_endpoint }}" owner=root group=postgres state=present create=yes mode=0640 53 | -------------------------------------------------------------------------------- /tasks/users.yml: -------------------------------------------------------------------------------- 1 | # file: tasks/users.yml -- manage postgresql roles 2 | 3 | - name: "Setup users: add postgresql roles" 4 | postgresql_user: 5 | state: present 6 | name: "{{ item.name }}" 7 | encrypted: no 8 | password: "{{ item.pass }}" 9 | role_attr_flags: "{{ item.flags }}" 10 | with_items: postgresql_users 11 | when: postgresql_users|length > 0 12 | 13 | - name: "Setup users: add postgresql replication roles" 14 | postgresql_user: 15 | state: present 16 | name: "{{ postgresql_streaming_user.name }}" 17 | encrypted: no 18 | password: "{{ postgresql_streaming_user.pass }}" 19 | role_attr_flags: REPLICATION 20 | when: postgresql_streaming_user|length > 0 21 | -------------------------------------------------------------------------------- /templates/flaskpgsql.conf: -------------------------------------------------------------------------------- 1 | start on runlevel [2345] 2 | stop on runlevel [016] 3 | 4 | respawn 5 | setuid nobody 6 | setgid nogroup 7 | exec python /srv/flask-pgsql-demo/app.py 8 | -------------------------------------------------------------------------------- /templates/pg_hba.conf.j2: -------------------------------------------------------------------------------- 1 | # PostgreSQL Client Authentication Configuration File 2 | # =================================================== 3 | # 4 | # Refer to the "Client Authentication" section in the PostgreSQL 5 | # documentation for a complete description of this file. A short 6 | # synopsis follows. 7 | # 8 | # This file controls: which hosts are allowed to connect, how clients 9 | # are authenticated, which PostgreSQL user names they can use, which 10 | # databases they can access. Records take one of these forms: 11 | # 12 | # local DATABASE USER METHOD [OPTIONS] 13 | # host DATABASE USER ADDRESS METHOD [OPTIONS] 14 | # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] 15 | # hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] 16 | # 17 | # (The uppercase items must be replaced by actual values.) 18 | # 19 | # The first field is the connection type: "local" is a Unix-domain 20 | # socket, "host" is either a plain or SSL-encrypted TCP/IP socket, 21 | # "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a 22 | # plain TCP/IP socket. 23 | # 24 | # DATABASE can be "all", "sameuser", "samerole", "replication", a 25 | # database name, or a comma-separated list thereof. The "all" 26 | # keyword does not match "replication". Access to replication 27 | # must be enabled in a separate record (see example below). 28 | # 29 | # USER can be "all", a user name, a group name prefixed with "+", or a 30 | # comma-separated list thereof. In both the DATABASE and USER fields 31 | # you can also write a file name prefixed with "@" to include names 32 | # from a separate file. 33 | # 34 | # ADDRESS specifies the set of hosts the record matches. It can be a 35 | # host name, or it is made up of an IP address and a CIDR mask that is 36 | # an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that 37 | # specifies the number of significant bits in the mask. A host name 38 | # that starts with a dot (.) matches a suffix of the actual host name. 39 | # Alternatively, you can write an IP address and netmask in separate 40 | # columns to specify the set of hosts. Instead of a CIDR-address, you 41 | # can write "samehost" to match any of the server's own IP addresses, 42 | # or "samenet" to match any address in any subnet that the server is 43 | # directly connected to. 44 | # 45 | # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", 46 | # "krb5", "ident", "peer", "pam", "ldap", "radius" or "cert". Note that 47 | # "password" sends passwords in clear text; "md5" is preferred since 48 | # it sends encrypted passwords. 49 | # 50 | # OPTIONS are a set of options for the authentication in the format 51 | # NAME=VALUE. The available options depend on the different 52 | # authentication methods -- refer to the "Client Authentication" 53 | # section in the documentation for a list of which options are 54 | # available for which authentication methods. 55 | # 56 | # Database and user names containing spaces, commas, quotes and other 57 | # special characters must be quoted. Quoting one of the keywords 58 | # "all", "sameuser", "samerole" or "replication" makes the name lose 59 | # its special character, and just match a database or username with 60 | # that name. 61 | # 62 | # This file is read on server startup and when the postmaster receives 63 | # a SIGHUP signal. If you edit the file on a running system, you have 64 | # to SIGHUP the postmaster for the changes to take effect. You can 65 | # use "pg_ctl reload" to do that. 66 | 67 | # Put your actual configuration here 68 | # ---------------------------------- 69 | # 70 | # If you want to allow non-local connections, you need to add more 71 | # "host" records. In that case you will also need to make PostgreSQL 72 | # listen on a non-local interface via the listen_addresses 73 | # configuration parameter, or via the -i or -h command line switches. 74 | 75 | 76 | 77 | # TYPE DATABASE USER ADDRESS METHOD 78 | 79 | {% for connection in postgresql_pg_hba_default %} 80 | # {{ connection.comment }} 81 | {{ connection.type }} {{ connection.database }} {{ connection.role }} {{ connection.address }} {{ connection.method }} 82 | {% endfor %} 83 | 84 | {% if ansible_default_ipv4.address in groups['pgmaster'] %} 85 | {% for slave in groups['pgslaves'] %} 86 | host replication {{ postgresql_streaming_user.name }} {{ hostvars[slave]['ansible_default_ipv4']['address'] }}/32 {{ postgresql_default_ipv4_auth_method }} 87 | {% endfor %} 88 | {% endif %} 89 | 90 | {% if ansible_default_ipv4.address in groups['pgslaves'] %} 91 | host replication {{ postgresql_streaming_user.name }} {{ hostvars[groups['pgmaster'][0]]['ansible_default_ipv4']['address'] }}/32 {{ postgresql_default_ipv4_auth_method }} 92 | {% endif %} 93 | 94 | 95 | # Custom 96 | #{% for connection in postgresql_pg_hba_custom %} 97 | #{{ connection.type }} {{ connection.database }} {{ connection.role }} {{ connection.address }} {{ connection.method }} 98 | #{% endfor %} 99 | -------------------------------------------------------------------------------- /templates/recovery.conf.j2: -------------------------------------------------------------------------------- 1 | standby_mode = 'on' 2 | primary_conninfo = 'host={{ hostvars[groups['pgmaster'][0]]['ansible_default_ipv4']['address'] }} port=5432 user={{ postgresql_streaming_user.name }} password={{ postgresql_streaming_user.pass }}' 3 | trigger_file = '/var/lib/postgresql/{{ postgresql_version }}/main/failover' 4 | --------------------------------------------------------------------------------