active_postgres 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +58 -3
- data/lib/active_postgres/cli.rb +7 -0
- data/lib/active_postgres/components/core.rb +5 -5
- data/lib/active_postgres/components/monitoring.rb +165 -0
- data/lib/active_postgres/components/pgbackrest.rb +59 -9
- data/lib/active_postgres/components/pgbouncer.rb +17 -5
- data/lib/active_postgres/components/repmgr.rb +103 -7
- data/lib/active_postgres/configuration.rb +19 -2
- data/lib/active_postgres/generators/active_postgres/install_generator.rb +1 -0
- data/lib/active_postgres/generators/active_postgres/templates/postgres.yml.erb +10 -0
- data/lib/active_postgres/health_checker.rb +29 -7
- data/lib/active_postgres/installer.rb +9 -0
- data/lib/active_postgres/overview.rb +351 -0
- data/lib/active_postgres/secrets.rb +18 -0
- data/lib/active_postgres/ssh_executor.rb +9 -5
- data/lib/active_postgres/version.rb +1 -1
- data/lib/active_postgres.rb +1 -0
- data/lib/tasks/postgres.rake +67 -0
- data/templates/repmgr.conf.erb +10 -0
- data/templates/repmgr_dns_failover.sh.erb +25 -11
- metadata +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'sshkit'
|
|
2
2
|
require 'sshkit/dsl'
|
|
3
3
|
require 'securerandom'
|
|
4
|
+
require 'stringio'
|
|
4
5
|
|
|
5
6
|
module ActivePostgres
|
|
6
7
|
class SSHExecutor
|
|
@@ -294,18 +295,21 @@ module ActivePostgres
|
|
|
294
295
|
SSHKit.config.format = :pretty
|
|
295
296
|
end
|
|
296
297
|
|
|
297
|
-
return unless File.exist?(config.ssh_key)
|
|
298
|
-
|
|
299
298
|
SSHKit::Backend::Netssh.configure do |ssh|
|
|
300
|
-
|
|
301
|
-
keys: [config.ssh_key],
|
|
302
|
-
keys_only: true,
|
|
299
|
+
options = {
|
|
303
300
|
forward_agent: false,
|
|
304
301
|
auth_methods: ['publickey'],
|
|
305
302
|
verify_host_key: config.ssh_host_key_verification || :always,
|
|
306
303
|
timeout: 10,
|
|
307
304
|
number_of_password_prompts: 0
|
|
308
305
|
}
|
|
306
|
+
|
|
307
|
+
if config.ssh_key && File.exist?(config.ssh_key)
|
|
308
|
+
options[:keys] = [config.ssh_key]
|
|
309
|
+
options[:keys_only] = true
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
ssh.ssh_options = options
|
|
309
313
|
end
|
|
310
314
|
end
|
|
311
315
|
end
|
data/lib/active_postgres.rb
CHANGED
|
@@ -23,6 +23,7 @@ require_relative 'active_postgres/installer'
|
|
|
23
23
|
require_relative 'active_postgres/ssh_executor'
|
|
24
24
|
require_relative 'active_postgres/direct_executor'
|
|
25
25
|
require_relative 'active_postgres/health_checker'
|
|
26
|
+
require_relative 'active_postgres/overview'
|
|
26
27
|
require_relative 'active_postgres/failover'
|
|
27
28
|
require_relative 'active_postgres/performance_tuner'
|
|
28
29
|
require_relative 'active_postgres/connection_pooler'
|
data/lib/tasks/postgres.rake
CHANGED
|
@@ -150,6 +150,15 @@ namespace :postgres do
|
|
|
150
150
|
health_checker.show_status
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
+
desc 'Show control tower overview'
|
|
154
|
+
task overview: :environment do
|
|
155
|
+
require 'active_postgres'
|
|
156
|
+
|
|
157
|
+
config = ActivePostgres::Configuration.load
|
|
158
|
+
overview = ActivePostgres::Overview.new(config)
|
|
159
|
+
overview.show
|
|
160
|
+
end
|
|
161
|
+
|
|
153
162
|
desc 'Visualize cluster nodes and topology'
|
|
154
163
|
task nodes: :environment do
|
|
155
164
|
require 'active_postgres'
|
|
@@ -270,6 +279,50 @@ namespace :postgres do
|
|
|
270
279
|
puts "\n#{'=' * 80}\n"
|
|
271
280
|
end
|
|
272
281
|
|
|
282
|
+
desc 'Show help for PostgreSQL rake tasks'
|
|
283
|
+
task help: :environment do
|
|
284
|
+
puts "\nPostgreSQL Rake Tasks"
|
|
285
|
+
puts '=' * 70
|
|
286
|
+
puts "\nTip: set RAILS_ENV=production for production targets"
|
|
287
|
+
|
|
288
|
+
puts "\nSetup & Maintenance"
|
|
289
|
+
puts " rake postgres:setup # Deploy HA cluster (CLEAN=true for fresh install)"
|
|
290
|
+
puts " rake postgres:purge # Destroy cluster (DESTRUCTIVE)"
|
|
291
|
+
puts " rake postgres:setup:core # PostgreSQL config (postgresql.conf, pg_hba.conf)"
|
|
292
|
+
puts " rake postgres:setup:repmgr # HA + failover (repmgr)"
|
|
293
|
+
puts " rake postgres:setup:pgbouncer # PgBouncer pooling"
|
|
294
|
+
puts " rake postgres:setup:pgbackrest # Backups (pgBackRest)"
|
|
295
|
+
puts " rake postgres:setup:monitoring # postgres_exporter"
|
|
296
|
+
puts " rake postgres:setup:ssl # SSL certs"
|
|
297
|
+
|
|
298
|
+
puts "\nStatus & Health"
|
|
299
|
+
puts " rake postgres:status # Cluster status (SSH by default)"
|
|
300
|
+
puts " rake postgres:overview # Control tower overview"
|
|
301
|
+
puts " rake postgres:nodes # Topology view"
|
|
302
|
+
puts " rake postgres:verify # Comprehensive checklist"
|
|
303
|
+
puts " ACTIVE_POSTGRES_STATUS_MODE=ssh|direct rake postgres:status"
|
|
304
|
+
|
|
305
|
+
puts "\nBackups"
|
|
306
|
+
puts " rake postgres:backup:full # Full backup"
|
|
307
|
+
puts " rake postgres:backup:incremental # Incremental backup"
|
|
308
|
+
puts " rake postgres:backup:list # List backups"
|
|
309
|
+
puts " rake postgres:backup:restore[ID] # Restore backup set"
|
|
310
|
+
puts " rake postgres:backup:restore_at[\"YYYY-MM-DD HH:MM:SS\",promote] # PITR"
|
|
311
|
+
|
|
312
|
+
puts "\nMigrations"
|
|
313
|
+
puts " rake postgres:migrate # Run migrations on primary only"
|
|
314
|
+
|
|
315
|
+
puts "\nPgBouncer"
|
|
316
|
+
puts " rake postgres:pgbouncer:update_userlist[users] # Update userlist"
|
|
317
|
+
puts " rake postgres:pgbouncer:stats # Service status + stats"
|
|
318
|
+
|
|
319
|
+
puts "\nTests"
|
|
320
|
+
puts " rake postgres:test:replication[rows] # Replication stress test"
|
|
321
|
+
puts " rake postgres:test:pgbouncer[connections] # PgBouncer load test"
|
|
322
|
+
|
|
323
|
+
puts
|
|
324
|
+
end
|
|
325
|
+
|
|
273
326
|
desc 'Promote standby to primary'
|
|
274
327
|
task :promote, [:host] => :environment do |_t, args|
|
|
275
328
|
require 'active_postgres'
|
|
@@ -325,6 +378,20 @@ namespace :postgres do
|
|
|
325
378
|
installer.run_restore(args[:backup_id])
|
|
326
379
|
end
|
|
327
380
|
|
|
381
|
+
desc 'Restore to a point in time (PITR)'
|
|
382
|
+
task :restore_at, [:target_time, :target_action] => :environment do |_t, args|
|
|
383
|
+
require 'active_postgres'
|
|
384
|
+
|
|
385
|
+
unless args[:target_time]
|
|
386
|
+
puts 'Usage: rake postgres:backup:restore_at["2026-01-29 01:15:00",promote]'
|
|
387
|
+
exit 1
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
config = ActivePostgres::Configuration.load
|
|
391
|
+
installer = ActivePostgres::Installer.new(config)
|
|
392
|
+
installer.run_restore_at(args[:target_time], target_action: args[:target_action] || 'promote')
|
|
393
|
+
end
|
|
394
|
+
|
|
328
395
|
desc 'List available backups'
|
|
329
396
|
task list: :environment do
|
|
330
397
|
require 'active_postgres'
|
data/templates/repmgr.conf.erb
CHANGED
|
@@ -31,6 +31,16 @@ follow_command='repmgr standby follow -f /etc/repmgr.conf --upstream-node-id=%n'
|
|
|
31
31
|
reconnect_attempts=<%= repmgr_config[:reconnect_attempts] || 6 %>
|
|
32
32
|
reconnect_interval=<%= repmgr_config[:reconnect_interval] || 10 %>
|
|
33
33
|
|
|
34
|
+
service_start_command='sudo systemctl start postgresql@<%= config.version %>-main'
|
|
35
|
+
service_stop_command='sudo systemctl stop postgresql@<%= config.version %>-main'
|
|
36
|
+
service_restart_command='sudo systemctl restart postgresql@<%= config.version %>-main'
|
|
37
|
+
service_reload_command='sudo systemctl reload postgresql@<%= config.version %>-main'
|
|
38
|
+
|
|
39
|
+
ssh_options='-i /var/lib/postgresql/.ssh/active_postgres_dns -o StrictHostKeyChecking=no'
|
|
40
|
+
<% if repmgr_config.key?(:use_rewind) %>
|
|
41
|
+
use_rewind=<%= repmgr_config[:use_rewind] ? 'yes' : 'no' %>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
34
44
|
log_level=INFO
|
|
35
45
|
log_facility=STDERR
|
|
36
46
|
log_file='/var/log/postgresql/repmgr.log'
|
|
@@ -6,30 +6,44 @@ DNS_USER="<%= dns_user %>"
|
|
|
6
6
|
DNS_SERVERS=(<%= dns_servers.map(&:to_s).join(' ') %>)
|
|
7
7
|
DNS_SSH_KEY="<%= dns_ssh_key_path %>"
|
|
8
8
|
DNSMASQ_FILE="/etc/dnsmasq.d/active_postgres.conf"
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
PRIMARY_RECORDS=(<%= primary_records.map(&:to_s).join(' ') %>)
|
|
10
|
+
REPLICA_RECORDS=(<%= replica_records.map(&:to_s).join(' ') %>)
|
|
11
11
|
SSH_STRICT_HOST_KEY="<%= ssh_strict_host_key %>"
|
|
12
12
|
SSH_KNOWN_HOSTS="/var/lib/postgresql/.ssh/known_hosts"
|
|
13
13
|
LOG_TAG="active_postgres_dns"
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
REPMGR_DB=$(grep -oP "dbname=\K[^ ']+" "$REPMGR_CONF" | head -1)
|
|
16
|
+
REPMGR_USER=$(grep -oP "user=\K[^ ']+" "$REPMGR_CONF" 2>/dev/null | head -1)
|
|
17
|
+
REPMGR_USER="${REPMGR_USER:-repmgr}"
|
|
18
|
+
|
|
19
|
+
cluster_data=$(sudo -u postgres psql -h /var/run/postgresql -d "$REPMGR_DB" -tAF',' -c "SELECT type, conninfo FROM repmgr.nodes WHERE active = true" 2>/dev/null || true)
|
|
20
|
+
if [[ -z "$cluster_data" ]]; then
|
|
17
21
|
exit 0
|
|
18
22
|
fi
|
|
19
23
|
|
|
20
|
-
primary_host=$(printf "%s\n" "$
|
|
21
|
-
standby_hosts=$(printf "%s\n" "$
|
|
24
|
+
primary_host=$(printf "%s\n" "$cluster_data" | awk -F',' '$1 == "primary" {print $2; exit}' | sed -n 's/.*host=\([^ ]*\).*/\1/p')
|
|
25
|
+
standby_hosts=$(printf "%s\n" "$cluster_data" | awk -F',' '$1 == "standby" {print $2}' | sed -n 's/.*host=\([^ ]*\).*/\1/p' | sort -u)
|
|
22
26
|
|
|
23
27
|
if [[ -z "$primary_host" ]]; then
|
|
24
28
|
exit 0
|
|
25
29
|
fi
|
|
26
30
|
|
|
27
31
|
content=$'# Managed by active_postgres\n'
|
|
28
|
-
|
|
32
|
+
for record in "${PRIMARY_RECORDS[@]}"; do
|
|
33
|
+
if [[ -n "$record" ]]; then
|
|
34
|
+
printf -v content '%saddress=/%s/%s\n' "$content" "$record" "$primary_host"
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
29
37
|
|
|
30
38
|
if [[ -n "$standby_hosts" ]]; then
|
|
31
|
-
for
|
|
32
|
-
|
|
39
|
+
for record in "${REPLICA_RECORDS[@]}"; do
|
|
40
|
+
if [[ -z "$record" ]]; then
|
|
41
|
+
continue
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
for host in $standby_hosts; do
|
|
45
|
+
printf -v content '%saddress=/%s/%s\n' "$content" "$record" "$host"
|
|
46
|
+
done
|
|
33
47
|
done
|
|
34
48
|
fi
|
|
35
49
|
|
|
@@ -41,9 +55,9 @@ for server in "${DNS_SERVERS[@]}"; do
|
|
|
41
55
|
fi
|
|
42
56
|
|
|
43
57
|
if ! /usr/bin/ssh "${ssh_opts[@]}" "${DNS_USER}@${server}" \
|
|
44
|
-
"sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' &&
|
|
58
|
+
"sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' && sudo systemctl restart dnsmasq"; then
|
|
45
59
|
/usr/bin/logger -t "$LOG_TAG" "Failed updating dnsmasq on ${server}"
|
|
46
60
|
fi
|
|
47
61
|
done
|
|
48
62
|
|
|
49
|
-
/usr/bin/logger -t "$LOG_TAG" "Updated DNS records for ${
|
|
63
|
+
/usr/bin/logger -t "$LOG_TAG" "Updated DNS records for ${PRIMARY_RECORDS[*]} and ${REPLICA_RECORDS[*]} (primary=${primary_host})"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_postgres
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- BoringCache
|
|
@@ -179,6 +179,7 @@ files:
|
|
|
179
179
|
- lib/active_postgres/installer.rb
|
|
180
180
|
- lib/active_postgres/log_sanitizer.rb
|
|
181
181
|
- lib/active_postgres/logger.rb
|
|
182
|
+
- lib/active_postgres/overview.rb
|
|
182
183
|
- lib/active_postgres/performance_tuner.rb
|
|
183
184
|
- lib/active_postgres/rails/database_config.rb
|
|
184
185
|
- lib/active_postgres/rails/migration_guard.rb
|