patroni-postgresql-cluster (#13)

* Описана инфрастуктура кластера

* Добавлена роль etcd

* Добавлена роль haproxy

* Добавлена роль keepalived

* Добавлена роль ntp_install

* Добавлена роль patroni

* Добавлена групповые переменные

---------

Co-authored-by: Fedor Batonogov <f.batonogov@yandex.ru>
This commit is contained in:
github-actions[bot]
2024-06-18 10:37:21 +03:00
committed by GitHub
parent 5b7920bdca
commit f8fe304cd9
43 changed files with 930 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
skip_list: skip_list:
- risky-file-permissions - risky-file-permissions
- var-naming[no-role-prefix]
exclude_paths: exclude_paths:
- ansible/roles/haproxy_static_pods/files/haproxy.yaml - ansible/roles/haproxy_static_pods/files/haproxy.yaml

View File

@@ -0,0 +1,2 @@
patroni_user: patroni
patroni_uid: 1666

View File

@@ -27,3 +27,23 @@ test_hosts:
vars: vars:
ansible_user: infra ansible_user: infra
ansible_port: 22 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

View File

@@ -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

View File

@@ -0,0 +1,7 @@
---
- name: Перезапускаю etcd.service
ansible.builtin.systemd:
name: etcd.service
state: restarted
enabled: true
daemon_reload: true

View File

@@ -0,0 +1,3 @@
---
- name: Наливаю юнит файл
ansible.builtin.import_tasks: service.yml

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1 @@
etcd_version: v3.5.14

View File

@@ -0,0 +1,8 @@
---
# handlers file for haproxy
- name: Перезапускаю haproxy.service
ansible.builtin.systemd:
name: haproxy.service
state: restarted
enabled: true
daemon_reload: true

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,7 @@
---
# tasks file for haproxy
- name: Копирую конфигурацию haproxy
ansible.builtin.import_tasks: config.yml
- name: Устанавливаю haproxy
ansible.builtin.import_tasks: install.yml

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
---
# vars file for haproxy
haproxy_version: 3.0.2-alpine

View File

@@ -0,0 +1,3 @@
---
# defaults file for keepalived
unit_file: keepalived.master.conf.j2

View File

@@ -0,0 +1,7 @@
---
- name: Перезапускаю keepalived.service
ansible.builtin.systemd:
name: keepalived.service
state: restarted
enabled: true
daemon_reload: true

View File

@@ -0,0 +1,7 @@
- name: Наливаю конфигурацию keepalived
ansible.builtin.template:
src: "{{ unit_file }}"
dest: /etc/keepalived/keepalived.conf
mode: "644"
notify:
- Перезапускаю keepalived.service

View File

@@ -0,0 +1,7 @@
---
- name: Устанавливаю keepalived
ansible.builtin.apt:
name:
- keepalived
state: present
update_cache: true

View File

@@ -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

View File

@@ -0,0 +1,5 @@
- name: Настраиваю keepalived.service
ansible.builtin.systemd:
name: keepalived.service
state: started
enabled: true

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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: <https://www.pool.ntp.org/join.html>
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

View File

@@ -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 <http://support.ntp.org/bin/view/Support/AccessRestrictions>
# 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

View File

@@ -0,0 +1,7 @@
---
- name: Перезапускаю patroni.service
ansible.builtin.systemd:
name: patroni.service
state: restarted
enabled: true
daemon_reload: true

View File

@@ -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

View File

@@ -0,0 +1,6 @@
---
- name: Наливаю юнит файл
ansible.builtin.import_tasks: service.yml
- name: Наливаю конфигурацию
ansible.builtin.import_tasks: config.yml

View File

@@ -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

View File

@@ -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:/<namespace>/<scope>/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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
---
image_version: v3.3.0-pg16.3
pg_data_dir: /var/lib/postgresql
patroni_config_dir: /etc/patroni

View File

@@ -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",
]
}

View File

@@ -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
]
}
}
}

View File

@@ -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
]
}
}
}

View File

@@ -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
}
}

View File

@@ -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 ..."

View File

@@ -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 ...)"
}