From f8fe304cd9a9786786185148d3ec44c4c95e927a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:37:21 +0300 Subject: [PATCH] patroni-postgresql-cluster (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Описана инфрастуктура кластера * Добавлена роль etcd * Добавлена роль haproxy * Добавлена роль keepalived * Добавлена роль ntp_install * Добавлена роль patroni * Добавлена групповые переменные --------- Co-authored-by: Fedor Batonogov --- .ansible-lint | 1 + ansible/group_vars/all.yml | 2 + ansible/inventory.yml | 20 ++++ ansible/patroni-postgresql.yml | 53 ++++++++++ ansible/roles/etcd/handlers/main.yml | 7 ++ ansible/roles/etcd/tasks/main.yml | 3 + ansible/roles/etcd/tasks/service.yml | 21 ++++ ansible/roles/etcd/templates/etcd.service.j2 | 34 +++++++ ansible/roles/etcd/vars/main.yml | 1 + ansible/roles/haproxy/handlers/main.yml | 8 ++ ansible/roles/haproxy/tasks/config.yml | 13 +++ ansible/roles/haproxy/tasks/install.yml | 13 +++ ansible/roles/haproxy/tasks/main.yml | 7 ++ .../roles/haproxy/templates/haproxy.cfg.j2 | 36 +++++++ .../haproxy/templates/haproxy.service.j2 | 21 ++++ ansible/roles/haproxy/vars/main.yml | 3 + ansible/roles/keepalived/defaults/main.yml | 3 + ansible/roles/keepalived/handlers/main.yml | 7 ++ ansible/roles/keepalived/tasks/config.yml | 7 ++ ansible/roles/keepalived/tasks/install.yml | 7 ++ ansible/roles/keepalived/tasks/main.yml | 10 ++ ansible/roles/keepalived/tasks/start.yml | 5 + .../templates/keepalived.backup.conf.j2 | 14 +++ .../templates/keepalived.master.conf.j2 | 14 +++ ansible/roles/ntp_install/README.md | 36 +++++++ ansible/roles/ntp_install/tasks/debian.yml | 22 +++++ ansible/roles/ntp_install/tasks/main.yml | 9 ++ ansible/roles/ntp_install/tasks/ubuntu.yml | 22 +++++ .../ntp_install/templates/debian.conf.j2 | 51 ++++++++++ .../ntp_install/templates/ubuntu.conf.j2 | 60 +++++++++++ ansible/roles/patroni/handlers/main.yml | 7 ++ ansible/roles/patroni/tasks/config.yml | 17 ++++ ansible/roles/patroni/tasks/main.yml | 6 ++ ansible/roles/patroni/tasks/service.yml | 26 +++++ ansible/roles/patroni/templates/config.yml.j2 | 82 +++++++++++++++ .../patroni/templates/patroni.service.j2 | 26 +++++ ansible/roles/patroni/vars/main.yml | 4 + .../patroni-postgresql/.terraform.lock.hcl | 25 +++++ opentofu/patroni-postgresql/etcd.tf | 94 ++++++++++++++++++ .../patroni-postgresql/patroni-postgresql.tf | 99 +++++++++++++++++++ opentofu/patroni-postgresql/provider.tf | 17 ++++ .../terraform.tfvars.example | 3 + opentofu/patroni-postgresql/variables.tf | 14 +++ 43 files changed, 930 insertions(+) create mode 100644 ansible/group_vars/all.yml create mode 100644 ansible/patroni-postgresql.yml create mode 100644 ansible/roles/etcd/handlers/main.yml create mode 100644 ansible/roles/etcd/tasks/main.yml create mode 100644 ansible/roles/etcd/tasks/service.yml create mode 100644 ansible/roles/etcd/templates/etcd.service.j2 create mode 100644 ansible/roles/etcd/vars/main.yml create mode 100644 ansible/roles/haproxy/handlers/main.yml create mode 100644 ansible/roles/haproxy/tasks/config.yml create mode 100644 ansible/roles/haproxy/tasks/install.yml create mode 100644 ansible/roles/haproxy/tasks/main.yml create mode 100644 ansible/roles/haproxy/templates/haproxy.cfg.j2 create mode 100644 ansible/roles/haproxy/templates/haproxy.service.j2 create mode 100644 ansible/roles/haproxy/vars/main.yml create mode 100644 ansible/roles/keepalived/defaults/main.yml create mode 100644 ansible/roles/keepalived/handlers/main.yml create mode 100644 ansible/roles/keepalived/tasks/config.yml create mode 100644 ansible/roles/keepalived/tasks/install.yml create mode 100644 ansible/roles/keepalived/tasks/main.yml create mode 100644 ansible/roles/keepalived/tasks/start.yml create mode 100644 ansible/roles/keepalived/templates/keepalived.backup.conf.j2 create mode 100644 ansible/roles/keepalived/templates/keepalived.master.conf.j2 create mode 100644 ansible/roles/ntp_install/README.md create mode 100644 ansible/roles/ntp_install/tasks/debian.yml create mode 100644 ansible/roles/ntp_install/tasks/main.yml create mode 100644 ansible/roles/ntp_install/tasks/ubuntu.yml create mode 100644 ansible/roles/ntp_install/templates/debian.conf.j2 create mode 100644 ansible/roles/ntp_install/templates/ubuntu.conf.j2 create mode 100644 ansible/roles/patroni/handlers/main.yml create mode 100644 ansible/roles/patroni/tasks/config.yml create mode 100644 ansible/roles/patroni/tasks/main.yml create mode 100644 ansible/roles/patroni/tasks/service.yml create mode 100644 ansible/roles/patroni/templates/config.yml.j2 create mode 100644 ansible/roles/patroni/templates/patroni.service.j2 create mode 100644 ansible/roles/patroni/vars/main.yml create mode 100644 opentofu/patroni-postgresql/.terraform.lock.hcl create mode 100644 opentofu/patroni-postgresql/etcd.tf create mode 100644 opentofu/patroni-postgresql/patroni-postgresql.tf create mode 100644 opentofu/patroni-postgresql/provider.tf create mode 100644 opentofu/patroni-postgresql/terraform.tfvars.example create mode 100644 opentofu/patroni-postgresql/variables.tf diff --git a/.ansible-lint b/.ansible-lint index 254aa7d..85ecfa1 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,5 +1,6 @@ skip_list: - risky-file-permissions + - var-naming[no-role-prefix] exclude_paths: - ansible/roles/haproxy_static_pods/files/haproxy.yaml diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml new file mode 100644 index 0000000..051b714 --- /dev/null +++ b/ansible/group_vars/all.yml @@ -0,0 +1,2 @@ +patroni_user: patroni +patroni_uid: 1666 diff --git a/ansible/inventory.yml b/ansible/inventory.yml index 10e4166..b2db812 100644 --- a/ansible/inventory.yml +++ b/ansible/inventory.yml @@ -27,3 +27,23 @@ test_hosts: vars: ansible_user: infra ansible_port: 22 + +patroni_postgresql_cluster: + children: + patroni_postgresql_hosts: + hosts: + patroni-postgresql-01: + ansible_host: 10.0.75.111 + patroni-postgresql-02: + ansible_host: 10.0.75.112 + patroni-postgresql-03: + ansible_host: 10.0.75.113 + haproxy_hosts: + hosts: + haproxy-01: + ansible_host: 10.0.75.114 + haproxy-02: + ansible_host: 10.0.75.115 + vars: + ansible_user: infra + ansible_port: 22 diff --git a/ansible/patroni-postgresql.yml b/ansible/patroni-postgresql.yml new file mode 100644 index 0000000..8526449 --- /dev/null +++ b/ansible/patroni-postgresql.yml @@ -0,0 +1,53 @@ +--- +- name: Подготовка узлов + become: true + hosts: + - patroni_postgresql_hosts + roles: + - ntp_install + - docker_install + tasks: + - name: Создаю пользователя + ansible.builtin.user: + name: "{{ patroni_user }}" + uid: "{{ patroni_uid }}" + shell: /usr/sbin/nologin + groups: + - docker + +- name: Подготовка кластера + become: true + hosts: + - patroni_postgresql_hosts + roles: + - etcd + - patroni + +- name: Подготовка кластера + become: true + hosts: + - haproxy_hosts + roles: + - ntp_install + - docker_install + - haproxy + +- name: Настройка master keepalived + become: true + hosts: + - haproxy-01 + roles: + - role: keepalived + unit_file: "keepalived.master.conf.j2" + virtual_ip: "10.0.75.110/24" + virtual_router_id: 250 + +- name: Настройка backup keepalived + become: true + hosts: + - haproxy-02 + roles: + - role: keepalived + unit_file: "keepalived.backup.conf.j2" + virtual_ip: "10.0.75.110/24" + virtual_router_id: 250 diff --git a/ansible/roles/etcd/handlers/main.yml b/ansible/roles/etcd/handlers/main.yml new file mode 100644 index 0000000..675c2a8 --- /dev/null +++ b/ansible/roles/etcd/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Перезапускаю etcd.service + ansible.builtin.systemd: + name: etcd.service + state: restarted + enabled: true + daemon_reload: true diff --git a/ansible/roles/etcd/tasks/main.yml b/ansible/roles/etcd/tasks/main.yml new file mode 100644 index 0000000..00eb397 --- /dev/null +++ b/ansible/roles/etcd/tasks/main.yml @@ -0,0 +1,3 @@ +--- +- name: Наливаю юнит файл + ansible.builtin.import_tasks: service.yml diff --git a/ansible/roles/etcd/tasks/service.yml b/ansible/roles/etcd/tasks/service.yml new file mode 100644 index 0000000..9e8dcea --- /dev/null +++ b/ansible/roles/etcd/tasks/service.yml @@ -0,0 +1,21 @@ +--- +- name: Задаю права для директории + ansible.builtin.file: + path: /var/lib/etcd + state: directory + recurse: true + owner: "{{ patroni_uid }}" + group: "{{ patroni_uid }}" + +- name: Наливаю юнит файл etcd + ansible.builtin.template: + src: etcd.service.j2 + dest: /etc/systemd/system/etcd.service + notify: + - Перезапускаю etcd.service + +- name: Настраиваю etcd.service + ansible.builtin.systemd: + name: etcd.service + state: started + enabled: true diff --git a/ansible/roles/etcd/templates/etcd.service.j2 b/ansible/roles/etcd/templates/etcd.service.j2 new file mode 100644 index 0000000..a381ca4 --- /dev/null +++ b/ansible/roles/etcd/templates/etcd.service.j2 @@ -0,0 +1,34 @@ +[Unit] +Description=etcd +Requires=docker.service +After=docker.service + +[Service] +User={{ patroni_user }} +Restart=always +Environment="TOKEN={{ lookup('password', 'secrets/patroni-postgresql/etcd_cluster_token length=64') }}" +Environment="CLUSTER=patroni-postgresql-01=http://10.0.75.111:2380,patroni-postgresql-02=http://10.0.75.112:2380,patroni-postgresql-03=http://10.0.75.113:2380" +ExecStartPre=-/usr/bin/docker rm -f etcd +ExecStart=/usr/bin/docker run \ + --rm \ + --user {{ patroni_uid }}:{{ patroni_uid }} \ + --publish 2379:2379 \ + --publish 2380:2380 \ + --name etcd \ + --volume=/var/lib/etcd:/etcd-data \ + quay.io/coreos/etcd:{{ etcd_version }} \ + /usr/local/bin/etcd \ + --data-dir=/etcd-data \ + --name {{ inventory_hostname }} \ + --initial-advertise-peer-urls http://{{ ansible_host }}:2380 \ + --listen-peer-urls http://0.0.0.0:2380 \ + --advertise-client-urls http://{{ ansible_host }}:2379 \ + --listen-client-urls http://0.0.0.0:2379 \ + --initial-cluster ${CLUSTER} \ + --initial-cluster-state new \ + --initial-cluster-token ${TOKEN} \ + --enable-v2=true +ExecStop=/usr/bin/docker stop -t 10 etcd + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/etcd/vars/main.yml b/ansible/roles/etcd/vars/main.yml new file mode 100644 index 0000000..28a9d96 --- /dev/null +++ b/ansible/roles/etcd/vars/main.yml @@ -0,0 +1 @@ +etcd_version: v3.5.14 diff --git a/ansible/roles/haproxy/handlers/main.yml b/ansible/roles/haproxy/handlers/main.yml new file mode 100644 index 0000000..97085e1 --- /dev/null +++ b/ansible/roles/haproxy/handlers/main.yml @@ -0,0 +1,8 @@ +--- +# handlers file for haproxy +- name: Перезапускаю haproxy.service + ansible.builtin.systemd: + name: haproxy.service + state: restarted + enabled: true + daemon_reload: true diff --git a/ansible/roles/haproxy/tasks/config.yml b/ansible/roles/haproxy/tasks/config.yml new file mode 100644 index 0000000..8718a82 --- /dev/null +++ b/ansible/roles/haproxy/tasks/config.yml @@ -0,0 +1,13 @@ +--- +- name: Создаю директорию /usr/local/etc/haproxy/ + ansible.builtin.file: + path: /usr/local/etc/haproxy/ + state: directory + +- name: Наливаю haproxy.cfg + ansible.builtin.template: + src: haproxy.cfg.j2 + dest: "/usr/local/etc/haproxy/haproxy.cfg" + mode: "755" + notify: + - Перезапускаю haproxy.service diff --git a/ansible/roles/haproxy/tasks/install.yml b/ansible/roles/haproxy/tasks/install.yml new file mode 100644 index 0000000..05a97af --- /dev/null +++ b/ansible/roles/haproxy/tasks/install.yml @@ -0,0 +1,13 @@ +--- +- name: Наливаю юнит файл haproxy + ansible.builtin.template: + src: haproxy.service.j2 + dest: /etc/systemd/system/haproxy.service + notify: + - Перезапускаю haproxy.service + +- name: Настраиваю haproxy.service + ansible.builtin.systemd: + name: haproxy.service + state: started + enabled: true diff --git a/ansible/roles/haproxy/tasks/main.yml b/ansible/roles/haproxy/tasks/main.yml new file mode 100644 index 0000000..54ebb79 --- /dev/null +++ b/ansible/roles/haproxy/tasks/main.yml @@ -0,0 +1,7 @@ +--- +# tasks file for haproxy +- name: Копирую конфигурацию haproxy + ansible.builtin.import_tasks: config.yml + +- name: Устанавливаю haproxy + ansible.builtin.import_tasks: install.yml diff --git a/ansible/roles/haproxy/templates/haproxy.cfg.j2 b/ansible/roles/haproxy/templates/haproxy.cfg.j2 new file mode 100644 index 0000000..d429a5d --- /dev/null +++ b/ansible/roles/haproxy/templates/haproxy.cfg.j2 @@ -0,0 +1,36 @@ +global + maxconn 100 + +defaults + log global + mode tcp + retries 2 + timeout client 30m + timeout connect 4s + timeout server 30m + timeout check 5s + +listen stats + mode http + bind *:7000 + stats enable + stats uri / + +listen patroni-postgresql-primary + bind *:5000 + option httpchk OPTIONS /master + http-check expect status 200 + default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions + server 10.0.75.111 10.0.75.111:5432 maxconn 100 check port 8008 + server 10.0.75.112 10.0.75.112:5432 maxconn 100 check port 8008 + server 10.0.75.113 10.0.75.113:5432 maxconn 100 check port 8008 + +listen patroni-postgresql-standbys + balance roundrobin + bind *:5001 + option httpchk OPTIONS /replica + http-check expect status 200 + default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions + server 10.0.75.111 10.0.75.111:5432 maxconn 100 check port 8008 + server 10.0.75.112 10.0.75.112:5432 maxconn 100 check port 8008 + server 10.0.75.113 10.0.75.113:5432 maxconn 100 check port 8008 diff --git a/ansible/roles/haproxy/templates/haproxy.service.j2 b/ansible/roles/haproxy/templates/haproxy.service.j2 new file mode 100644 index 0000000..236d105 --- /dev/null +++ b/ansible/roles/haproxy/templates/haproxy.service.j2 @@ -0,0 +1,21 @@ +[Unit] +Description=haproxy +Requires=docker.service +After=docker.service + +[Service] +Restart=always +ExecStartPre=-/usr/bin/docker rm -f haproxy +ExecStart=/usr/bin/docker run \ + --rm \ + --publish 5000:5000 \ + --publish 5001:5001 \ + --publish 7000:7000 \ + --volume /usr/local/etc/haproxy/:/usr/local/etc/haproxy/:ro \ + --sysctl net.ipv4.ip_unprivileged_port_start=0 \ + --name haproxy \ + haproxy:{{ haproxy_version }} +ExecStop=/usr/bin/docker stop -t 10 haproxy + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/haproxy/vars/main.yml b/ansible/roles/haproxy/vars/main.yml new file mode 100644 index 0000000..84f163f --- /dev/null +++ b/ansible/roles/haproxy/vars/main.yml @@ -0,0 +1,3 @@ +--- +# vars file for haproxy +haproxy_version: 3.0.2-alpine diff --git a/ansible/roles/keepalived/defaults/main.yml b/ansible/roles/keepalived/defaults/main.yml new file mode 100644 index 0000000..6ce0081 --- /dev/null +++ b/ansible/roles/keepalived/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for keepalived +unit_file: keepalived.master.conf.j2 diff --git a/ansible/roles/keepalived/handlers/main.yml b/ansible/roles/keepalived/handlers/main.yml new file mode 100644 index 0000000..d3ee6b9 --- /dev/null +++ b/ansible/roles/keepalived/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Перезапускаю keepalived.service + ansible.builtin.systemd: + name: keepalived.service + state: restarted + enabled: true + daemon_reload: true diff --git a/ansible/roles/keepalived/tasks/config.yml b/ansible/roles/keepalived/tasks/config.yml new file mode 100644 index 0000000..fa346bb --- /dev/null +++ b/ansible/roles/keepalived/tasks/config.yml @@ -0,0 +1,7 @@ +- name: Наливаю конфигурацию keepalived + ansible.builtin.template: + src: "{{ unit_file }}" + dest: /etc/keepalived/keepalived.conf + mode: "644" + notify: + - Перезапускаю keepalived.service diff --git a/ansible/roles/keepalived/tasks/install.yml b/ansible/roles/keepalived/tasks/install.yml new file mode 100644 index 0000000..6915a0c --- /dev/null +++ b/ansible/roles/keepalived/tasks/install.yml @@ -0,0 +1,7 @@ +--- +- name: Устанавливаю keepalived + ansible.builtin.apt: + name: + - keepalived + state: present + update_cache: true diff --git a/ansible/roles/keepalived/tasks/main.yml b/ansible/roles/keepalived/tasks/main.yml new file mode 100644 index 0000000..9abb4ac --- /dev/null +++ b/ansible/roles/keepalived/tasks/main.yml @@ -0,0 +1,10 @@ +--- +# tasks file for keepalived +- name: Устанавливаю keepalived + ansible.builtin.import_tasks: install.yml + +- name: Наливаю конфигурацию + ansible.builtin.include_tasks: config.yml + +- name: Запускаю сервис + ansible.builtin.include_tasks: start.yml diff --git a/ansible/roles/keepalived/tasks/start.yml b/ansible/roles/keepalived/tasks/start.yml new file mode 100644 index 0000000..6c9a838 --- /dev/null +++ b/ansible/roles/keepalived/tasks/start.yml @@ -0,0 +1,5 @@ +- name: Настраиваю keepalived.service + ansible.builtin.systemd: + name: keepalived.service + state: started + enabled: true diff --git a/ansible/roles/keepalived/templates/keepalived.backup.conf.j2 b/ansible/roles/keepalived/templates/keepalived.backup.conf.j2 new file mode 100644 index 0000000..901a9c7 --- /dev/null +++ b/ansible/roles/keepalived/templates/keepalived.backup.conf.j2 @@ -0,0 +1,14 @@ +vrrp_instance VI_1 { + state BACKUP + interface eth0 + virtual_router_id {{ virtual_router_id }} + priority 100 # PAY ATTENTION ON PRIORITY!! + authentication { + auth_type PASS + auth_pass {{ lookup('password', 'secrets/keepalived/' + virtual_ip + ' length=64') }} + } + + virtual_ipaddress { + {{ virtual_ip }} dev eth0 + } +} diff --git a/ansible/roles/keepalived/templates/keepalived.master.conf.j2 b/ansible/roles/keepalived/templates/keepalived.master.conf.j2 new file mode 100644 index 0000000..f9e81fb --- /dev/null +++ b/ansible/roles/keepalived/templates/keepalived.master.conf.j2 @@ -0,0 +1,14 @@ +vrrp_instance VI_1 { + state MASTER + interface eth0 + virtual_router_id {{ virtual_router_id }} + priority 101 # PAY ATTENTION ON PRIORITY!! + authentication { + auth_type PASS + auth_pass {{ lookup('password', 'secrets/keepalived/' + virtual_ip + ' length=64') }} + } + + virtual_ipaddress { + {{ virtual_ip }} dev eth0 + } +} diff --git a/ansible/roles/ntp_install/README.md b/ansible/roles/ntp_install/README.md new file mode 100644 index 0000000..89dde97 --- /dev/null +++ b/ansible/roles/ntp_install/README.md @@ -0,0 +1,36 @@ +Настройка NTP +========= + +Данная роль настраивает часовой пояс и NTP на узлах. + +Requirements +------------ + +Тестирование приводилось на **Ubuntu 20.04 (Focal Fossa)** и **Ubuntu 22.04 (Jammy Jellyfish)**. + +Role Variables +-------------- + +Переменные не используются. + +Dependencies +------------ + +Для настройки часового пояса используется роль [community.general.timezone](https://docs.ansible.com/ansible/latest/collections/community/general/timezone_module.html), в качестве параметра принимается строка с нужным часовым поясом, например **Europe/Moscow**. + +Example Playbook +---------------- + + - hosts: servers + roles: + - ntp-install + +License +------- + +MIT + +Author Information +------------------ + +Федор Батоногов f.batonogov@yandex.ru diff --git a/ansible/roles/ntp_install/tasks/debian.yml b/ansible/roles/ntp_install/tasks/debian.yml new file mode 100644 index 0000000..f5956e1 --- /dev/null +++ b/ansible/roles/ntp_install/tasks/debian.yml @@ -0,0 +1,22 @@ +--- +# tasks file for ntp-install +- name: Настраиваю Московское время + community.general.timezone: + name: Europe/Moscow + +- name: Устанавливаю ntp + ansible.builtin.apt: + pkg: + - ntp + update_cache: true + +- name: Настраиваю сервис ntp + ansible.builtin.systemd: + name: ntp + state: started + enabled: true + +- name: Наливаю конфигурацию ntp из шаблона + ansible.builtin.template: + src: debian.conf.j2 + dest: '/etc/ntpsec/ntp.conf' diff --git a/ansible/roles/ntp_install/tasks/main.yml b/ansible/roles/ntp_install/tasks/main.yml new file mode 100644 index 0000000..539a945 --- /dev/null +++ b/ansible/roles/ntp_install/tasks/main.yml @@ -0,0 +1,9 @@ +--- +# tasks file for ntp-install +- name: Устанавливаю ntp на Ubuntu + ansible.builtin.import_tasks: ubuntu.yml + when: ansible_distribution == 'Ubuntu' + +- name: Устанавливаю ntp на Debian + ansible.builtin.import_tasks: debian.yml + when: ansible_distribution == 'Debian' diff --git a/ansible/roles/ntp_install/tasks/ubuntu.yml b/ansible/roles/ntp_install/tasks/ubuntu.yml new file mode 100644 index 0000000..4f6195e --- /dev/null +++ b/ansible/roles/ntp_install/tasks/ubuntu.yml @@ -0,0 +1,22 @@ +--- +# tasks file for ntp-install +- name: Настраиваю Московское время + community.general.timezone: + name: Europe/Moscow + +- name: Устанавливаю ntp + ansible.builtin.apt: + pkg: + - ntp + update_cache: true + +- name: Настраиваю сервис ntp + ansible.builtin.systemd: + name: ntp + state: started + enabled: true + +- name: Наливаю конфигурацию ntp из шаблона + ansible.builtin.template: + src: ubuntu.conf.j2 + dest: '/etc/ntp.conf' diff --git a/ansible/roles/ntp_install/templates/debian.conf.j2 b/ansible/roles/ntp_install/templates/debian.conf.j2 new file mode 100644 index 0000000..66d3c29 --- /dev/null +++ b/ansible/roles/ntp_install/templates/debian.conf.j2 @@ -0,0 +1,51 @@ +# /etc/ntpsec/ntp.conf, configuration for ntpd; see ntp.conf(5) for help + +driftfile /var/lib/ntpsec/ntp.drift +leapfile /usr/share/zoneinfo/leap-seconds.list + +# To enable Network Time Security support as a server, obtain a certificate +# (e.g. with Let's Encrypt), configure the paths below, and uncomment: +# nts cert CERT_FILE +# nts key KEY_FILE +# nts enable + +# You must create /var/log/ntpsec (owned by ntpsec:ntpsec) to enable logging. +#statsdir /var/log/ntpsec/ +#statistics loopstats peerstats clockstats +#filegen loopstats file loopstats type day enable +#filegen peerstats file peerstats type day enable +#filegen clockstats file clockstats type day enable + +# This should be maxclock 7, but the pool entries count towards maxclock. +tos maxclock 11 + +# Comment this out if you have a refclock and want it to be able to discipline +# the clock by itself (e.g. if the system is not connected to the network). +tos minclock 4 minsane 3 + +# Specify one or more NTP servers. + +# Public NTP servers supporting Network Time Security: +# server time.cloudflare.com nts + +# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will +# pick a different set every time it starts up. Please consider joining the +# pool: +pool 0.debian.pool.ntp.org iburst +pool 1.debian.pool.ntp.org iburst +pool 2.debian.pool.ntp.org iburst +pool 3.debian.pool.ntp.org iburst + +# Access control configuration; see /usr/share/doc/ntpsec-doc/html/accopt.html +# for details. +# +# Note that "restrict" applies to both servers and clients, so a configuration +# that might be intended to block requests from certain clients could also end +# up blocking replies from your own upstream servers. + +# By default, exchange time with everybody, but don't allow configuration. +restrict default kod nomodify nopeer noquery limited + +# Local users may interrogate the ntp server more closely. +restrict 127.0.0.1 +restrict ::1 diff --git a/ansible/roles/ntp_install/templates/ubuntu.conf.j2 b/ansible/roles/ntp_install/templates/ubuntu.conf.j2 new file mode 100644 index 0000000..7702785 --- /dev/null +++ b/ansible/roles/ntp_install/templates/ubuntu.conf.j2 @@ -0,0 +1,60 @@ +# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help + +driftfile /var/lib/ntp/ntp.drift + +# Leap seconds definition provided by tzdata +leapfile /usr/share/zoneinfo/leap-seconds.list + +# Enable this if you want statistics to be logged. +#statsdir /var/log/ntpstats/ + +statistics loopstats peerstats clockstats +filegen loopstats file loopstats type day enable +filegen peerstats file peerstats type day enable +filegen clockstats file clockstats type day enable + +# Specify one or more NTP servers. + +# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board +# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for +# more information. +pool 0.ubuntu.pool.ntp.org iburst +pool 1.ubuntu.pool.ntp.org iburst +pool 2.ubuntu.pool.ntp.org iburst +pool 3.ubuntu.pool.ntp.org iburst + +# Use Ubuntu's ntp server as a fallback. +pool ntp.ubuntu.com + +# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for +# details. The web page +# might also be helpful. +# +# Note that "restrict" applies to both servers and clients, so a configuration +# that might be intended to block requests from certain clients could also end +# up blocking replies from your own upstream servers. + +# By default, exchange time with everybody, but don't allow configuration. +restrict -4 default kod notrap nomodify nopeer noquery limited +restrict -6 default kod notrap nomodify nopeer noquery limited + +# Local users may interrogate the ntp server more closely. +restrict 127.0.0.1 +restrict ::1 + +# Needed for adding pool entries +restrict source notrap nomodify noquery + +# Clients from this (example!) subnet have unlimited access, but only if +# cryptographically authenticated. +#restrict 192.168.123.0 mask 255.255.255.0 notrust + + +# If you want to provide time to your local subnet, change the next line. +# (Again, the address is an example only.) +#broadcast 192.168.123.255 + +# If you want to listen to time broadcasts on your local subnet, de-comment the +# next lines. Please do this only if you trust everybody on the network! +#disable auth +#broadcastclient diff --git a/ansible/roles/patroni/handlers/main.yml b/ansible/roles/patroni/handlers/main.yml new file mode 100644 index 0000000..10a8f5f --- /dev/null +++ b/ansible/roles/patroni/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Перезапускаю patroni.service + ansible.builtin.systemd: + name: patroni.service + state: restarted + enabled: true + daemon_reload: true diff --git a/ansible/roles/patroni/tasks/config.yml b/ansible/roles/patroni/tasks/config.yml new file mode 100644 index 0000000..9b0b553 --- /dev/null +++ b/ansible/roles/patroni/tasks/config.yml @@ -0,0 +1,17 @@ +--- +- name: Создаю директории и задаю права + ansible.builtin.file: + path: "{{ patroni_config_dir }}" + state: directory + mode: "755" + owner: "999" + group: "999" + +- name: Наливаю конфиг patroni + ansible.builtin.template: + src: config.yml.j2 + dest: "{{ patroni_config_dir }}/config.yml" + mode: "755" + # backup: true + notify: + - Перезапускаю patroni.service diff --git a/ansible/roles/patroni/tasks/main.yml b/ansible/roles/patroni/tasks/main.yml new file mode 100644 index 0000000..40395c2 --- /dev/null +++ b/ansible/roles/patroni/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Наливаю юнит файл + ansible.builtin.import_tasks: service.yml + +- name: Наливаю конфигурацию + ansible.builtin.import_tasks: config.yml diff --git a/ansible/roles/patroni/tasks/service.yml b/ansible/roles/patroni/tasks/service.yml new file mode 100644 index 0000000..7da5a51 --- /dev/null +++ b/ansible/roles/patroni/tasks/service.yml @@ -0,0 +1,26 @@ +--- +- name: Наливаю юнит файл patroni + ansible.builtin.template: + src: patroni.service.j2 + dest: /etc/systemd/system/patroni.service + notify: + - Перезапускаю patroni.service + +- name: Создаю директории и задаю права + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0700" + owner: "999" + group: "999" + loop: + - "{{ pg_data_dir }}" + - "{{ pg_data_dir }}/data" + notify: + - Перезапускаю patroni.service + +- name: Настраиваю patroni.service + ansible.builtin.systemd: + name: patroni.service + state: started + enabled: true diff --git a/ansible/roles/patroni/templates/config.yml.j2 b/ansible/roles/patroni/templates/config.yml.j2 new file mode 100644 index 0000000..ba56461 --- /dev/null +++ b/ansible/roles/patroni/templates/config.yml.j2 @@ -0,0 +1,82 @@ +scope: patroni +name: {{ inventory_hostname }} + +restapi: + listen: 0.0.0.0:8008 + connect_address: {{ inventory_hostname }}:8008 + +etcd: + host: {{ inventory_hostname }}:2379 + +bootstrap: + # this section will be written into Etcd:///config after initializing new cluster + dcs: + ttl: 30 + loop_wait: 10 + retry_timeout: 10 + maximum_lag_on_failover: 1048576 +# master_start_timeout: 300 +# synchronous_mode: false + postgresql: + use_pg_rewind: true + use_slots: true + parameters: + wal_level: replica + hot_standby: "on" + logging_collector: 'on' + max_wal_senders: 5 + max_replication_slots: 5 + wal_log_hints: "on" + #archive_mode: "on" + #archive_timeout: 600 + #archive_command: "cp -f %p /home/postgres/archived/%f" + #recovery_conf: + #restore_command: cp /home/postgres/archived/%f %p + + # some desired options for 'initdb' + initdb: # Note: It needs to be a list (some options need values, others are switches) + - encoding: UTF8 + - data-checksums + + pg_hba: # Add following lines to pg_hba.conf after running 'initdb' + - host replication replicator 10.0.75.0/24 md5 + - host replication replicator 127.0.0.1/32 trust + - host all all 10.0.75.0/24 md5 + - host all all 0.0.0.0/0 md5 +# - hostssl all all 0.0.0.0/0 md5 + + # Additional script to be launched after initial cluster creation (will be passed the connection URL as parameter) +# post_init: /usr/local/bin/setup_cluster.sh + # Some additional users users which needs to be created after initializing new cluster + users: + admin: + password: admin + options: + - createrole + - createdb + +postgresql: + listen: 0.0.0.0:5432 + connect_address: {{ inventory_hostname }}:5432 + data_dir: "/var/lib/postgresql/patroni/main" + bin_dir: "/usr/lib/postgresql/16/bin" +# config_dir: + pgpass: /tmp/pgpass0 + authentication: + replication: + username: replicator + password: {{ lookup('password', 'secrets/patroni-postgresql/replicator-password length=64') }} + superuser: + username: postgres + password: {{ lookup('password', 'secrets/patroni-postgresql/postgres-password length=64') }} + parameters: + unix_socket_directories: '/var/run/postgresql' + +watchdog: + mode: off + +tags: + nofailover: false + noloadbalance: false + clonefrom: false + nosync: false diff --git a/ansible/roles/patroni/templates/patroni.service.j2 b/ansible/roles/patroni/templates/patroni.service.j2 new file mode 100644 index 0000000..b0e03f0 --- /dev/null +++ b/ansible/roles/patroni/templates/patroni.service.j2 @@ -0,0 +1,26 @@ +[Unit] +Description=patroni +Requires=etcd.service +After=etcd.service + +[Service] +User={{ patroni_user }} +Restart=always +ExecStartPre=-/usr/bin/docker rm -f patroni +ExecStart=/usr/bin/docker run \ + --rm \ + --name patroni \ + --hostname {{ inventory_hostname }} \ + --publish 5432:5432 \ + --publish 8008:8008 \ + --publish 8091:8091 \ + --add-host "patroni-postgresql-01:10.0.75.111" \ + --add-host "patroni-postgresql-02:10.0.75.112" \ + --add-host "patroni-postgresql-03:10.0.75.113" \ + --volume={{ patroni_config_dir }}/config.yml:{{ patroni_config_dir }}/config.yml:ro \ + --volume={{ pg_data_dir }}:{{ pg_data_dir }} \ + ghcr.io/batonogov/patroni-docker:{{ image_version }} +ExecStop=/usr/bin/docker stop -t 10 patroni + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/patroni/vars/main.yml b/ansible/roles/patroni/vars/main.yml new file mode 100644 index 0000000..363cda0 --- /dev/null +++ b/ansible/roles/patroni/vars/main.yml @@ -0,0 +1,4 @@ +--- +image_version: v3.3.0-pg16.3 +pg_data_dir: /var/lib/postgresql +patroni_config_dir: /etc/patroni diff --git a/opentofu/patroni-postgresql/.terraform.lock.hcl b/opentofu/patroni-postgresql/.terraform.lock.hcl new file mode 100644 index 0000000..fa0009f --- /dev/null +++ b/opentofu/patroni-postgresql/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/bpg/proxmox" { + version = "0.60.0" + constraints = ">= 0.53.1" + hashes = [ + "h1:ZC+6RI6NKsTB8Y0Lm0onj6N/+k8RZP//GwGqQZN/UVM=", + "zh:0d244b94bdfba501ae285db1d563ad91c393cdb97807377ecdbbc96454e345f7", + "zh:146617d6065d52f512e8e7f9d230e5f8e6e11a67e67c7d8906e212631e53c61e", + "zh:6290bed20fa3e5c070ff867b7a6f2be750c22b9bc97b976a134ab342bdc617ae", + "zh:70d69b7863d0bb5cede73bcb04b81e8ea2be124e19ba854795198302ba04762a", + "zh:7655a8326618cfce8d9e24c1f3fad4a191f80bfc3b3be9a2b7e9169f0c11c05a", + "zh:7700e2443eea1508de880484209f625015b7e8f2c74f615e2c18cc986ee1ffdf", + "zh:96805c02119733f0639084ffd1a194795b153801f91cc22ed4f1cb5487efc035", + "zh:9f8a331cecd7db82cba94e58016ec92d0835fd2f6efc7c5ec46217e4109f1914", + "zh:ae7cdd011e156dd8dadf836c8f1badbc9798c5dbf308313ca29153855bf4f17f", + "zh:b5937d52fdf294b92be3e39581e4c1ecceb89fe614756334c5384102b4551a87", + "zh:b5e11bec8bad9b1ed044d550a8d0a95ca5e94ecd0660a876260909ed42e4dcdb", + "zh:d3f390ae2788240ca3db0aac2d36a01128257649a3e90dd9e600fd8b0c9d9e33", + "zh:d947c22f05af6d81025bf92c5d99fd7db27ee2e5642f4b3a9b1e251673c80656", + "zh:dfeed9507c83d7d4b25539c010d04bd6de12396f15d180a15119b923b3b49fb5", + "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", + ] +} diff --git a/opentofu/patroni-postgresql/etcd.tf b/opentofu/patroni-postgresql/etcd.tf new file mode 100644 index 0000000..8cdeb8c --- /dev/null +++ b/opentofu/patroni-postgresql/etcd.tf @@ -0,0 +1,94 @@ +variable "etcd_vms" { + type = list(object({ + name = string + address = string + node_name = string + })) + default = [ + { + name = "etcd-01" + address = "10.0.75.114/24" + node_name = "pve-01" + }, + { + name = "etcd-02" + address = "10.0.75.115/24" + node_name = "pve-02" + } + ] +} + +# Создание виртуальных машин +resource "proxmox_virtual_environment_vm" "etcd" { + for_each = { for vm in var.etcd_vms : vm.name => vm } + + name = each.value.name + migrate = true + description = "Managed by OpenTofu" + tags = ["etcd"] + on_boot = true + node_name = each.value.node_name + + clone { + vm_id = "2404" + node_name = "pve-01" + retries = 3 + } + + agent { + enabled = true + } + + operating_system { + type = "l26" + } + + cpu { + cores = 2 + type = "host" + numa = true + } + + memory { + dedicated = 2048 + } + + vga { + memory = 4 + type = "serial0" + } + + disk { + size = "20" + interface = "virtio0" + datastore_id = "proxmox-data-01" + file_format = "raw" + } + + network_device { + bridge = "vmbr0" + model = "virtio" + } + + initialization { + datastore_id = "proxmox-data-01" + ip_config { + ipv4 { + address = each.value.address + gateway = "10.0.75.1" + } + } + dns { + servers = [ + "10.0.75.65", + "10.0.75.66" + ] + } + user_account { + username = "infra" + keys = [ + var.ssh_public_key + ] + } + } +} diff --git a/opentofu/patroni-postgresql/patroni-postgresql.tf b/opentofu/patroni-postgresql/patroni-postgresql.tf new file mode 100644 index 0000000..9dddece --- /dev/null +++ b/opentofu/patroni-postgresql/patroni-postgresql.tf @@ -0,0 +1,99 @@ +variable "patroni_vms" { + type = list(object({ + name = string + address = string + node_name = string + })) + default = [ + { + name = "patroni-postgresql-01" + address = "10.0.75.111/24" + node_name = "pve-01" + }, + { + name = "patroni-postgresql-02" + address = "10.0.75.112/24" + node_name = "pve-02" + }, + { + name = "patroni-postgresql-03" # Исправлено имя + address = "10.0.75.113/24" + node_name = "pve-02" + } + ] +} + +# Создание виртуальных машин +resource "proxmox_virtual_environment_vm" "patroni" { + for_each = { for vm in var.patroni_vms : vm.name => vm } + + name = each.value.name + migrate = true + description = "Managed by OpenTofu" + tags = ["patroni", "postgresql"] + on_boot = true + node_name = each.value.node_name + + clone { + vm_id = "2404" + node_name = "pve-01" + retries = 3 + } + + agent { + enabled = true + } + + operating_system { + type = "l26" + } + + cpu { + cores = 2 + type = "host" + numa = true + } + + memory { + dedicated = 2048 + } + + vga { + memory = 4 + type = "serial0" + } + + disk { + size = "20" + interface = "virtio0" + datastore_id = "proxmox-data-01" + file_format = "raw" + } + + network_device { + bridge = "vmbr0" + model = "virtio" + } + + initialization { + datastore_id = "proxmox-data-01" + ip_config { + ipv4 { + address = each.value.address + gateway = "10.0.75.1" + } + } + dns { + servers = [ + "10.0.75.65", + "10.0.75.66" + ] + } + user_account { + username = "infra" + keys = [ + var.ssh_public_key + ] + } + } +} diff --git a/opentofu/patroni-postgresql/provider.tf b/opentofu/patroni-postgresql/provider.tf new file mode 100644 index 0000000..ac701aa --- /dev/null +++ b/opentofu/patroni-postgresql/provider.tf @@ -0,0 +1,17 @@ +terraform { + required_providers { + proxmox = { + source = "bpg/proxmox" + version = ">= 0.60.0" + } + } +} + +provider "proxmox" { + endpoint = var.virtual_environment_endpoint + api_token = var.virtual_environment_api_token + insecure = true + ssh { + agent = false + } +} diff --git a/opentofu/patroni-postgresql/terraform.tfvars.example b/opentofu/patroni-postgresql/terraform.tfvars.example new file mode 100644 index 0000000..e046f62 --- /dev/null +++ b/opentofu/patroni-postgresql/terraform.tfvars.example @@ -0,0 +1,3 @@ +virtual_environment_api_token = "root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +virtual_environment_endpoint = "https://x.x.x.x:8006/" +ssh_public_key = "ssh-rsa ..." diff --git a/opentofu/patroni-postgresql/variables.tf b/opentofu/patroni-postgresql/variables.tf new file mode 100644 index 0000000..4d3e347 --- /dev/null +++ b/opentofu/patroni-postgresql/variables.tf @@ -0,0 +1,14 @@ +variable "virtual_environment_endpoint" { + type = string + description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" +} + +variable "virtual_environment_api_token" { + type = string + description = "The api roken the Proxmox Virtual Environment API (example: root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)" +} + +variable "ssh_public_key" { + type = string + description = "SSH Puclic key for VMs (example: ssh-rsa ...)" +}