blob: 145fcac7510c690ceb4e82b6487afd970a198d1f (
plain) (
tree)
|
|
{ pkgs, config, lib, ... }:
let
cfg = config.myServices.databasesReplication.postgresql;
in
{
options.myServices.databasesReplication.postgresql = {
enable = lib.mkEnableOption "Enable postgresql replication";
base = lib.mkOption {
type = lib.types.path;
description = ''
Base path to put the replications
'';
};
hosts = lib.mkOption {
default = {};
description = ''
Hosts to backup
'';
type = lib.types.attrsOf (lib.types.submodule {
options = {
package = lib.mkOption {
type = lib.types.package;
default = pkgs.postgresql;
description = ''
Postgresql package for this host
'';
};
slot = lib.mkOption {
type = lib.types.str;
description = ''
Slot to use for replication
'';
};
connection = lib.mkOption {
type = lib.types.str;
description = ''
Connection string to access the psql master
'';
};
};
});
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [ (self: super: {
postgresql = self.postgresql_11_custom;
}) ];
users.users.postgres = {
name = "postgres";
uid = config.ids.uids.postgres;
group = "postgres";
description = "PostgreSQL server user";
home = "/var/lib/postgresql";
useDefaultShell = true;
extraGroups = [ "keys" ];
};
users.groups.postgres.gid = config.ids.gids.postgres;
environment.systemPackages = [ pkgs.postgresql ];
secrets.keys = lib.flatten (lib.mapAttrsToList (name: hcfg: [
{
dest = "postgresql_replication/${name}/recovery.conf";
user = "postgres";
group = "postgres";
permissions = "0400";
text = ''
standby_mode = on
primary_conninfo = '${hcfg.connection}?sslmode=require'
primary_slot_name = '${hcfg.slot}'
'';
}
{
dest = "postgresql_replication/${name}/connection_string";
user = "postgres";
group = "postgres";
permissions = "0400";
text = hcfg.connection;
}
{
dest = "postgresql_replication/${name}/postgresql.conf";
user = "postgres";
group = "postgres";
permissions = "0400";
text = let
dataDir = "${cfg.base}/${name}/postgresql";
in ''
listen_addresses = '''
unix_socket_directories = '${dataDir}'
data_directory = '${dataDir}'
wal_level = logical
'';
}
]) cfg.hosts);
services.cron = {
enable = true;
systemCronJobs = lib.flatten (lib.mapAttrsToList (name: hcfg:
let
dataDir = "${cfg.base}/${name}/postgresql";
backupDir = "${cfg.base}/${name}/postgresql_backup";
backup_script = pkgs.writeScript "backup_psql_${name}" ''
#!${pkgs.stdenv.shell}
set -euo pipefail
resume_replication() {
${hcfg.package}/bin/psql -h ${dataDir} -c "SELECT pg_wal_replay_resume();" >/dev/null || echo "impossible to resume replication"
}
trap resume_replication EXIT
${hcfg.package}/bin/psql -h ${dataDir} -c "SELECT pg_wal_replay_pause();" >/dev/null || (echo "impossible to pause replication" && false)
${hcfg.package}/bin/pg_dumpall -h ${dataDir} -f ${backupDir}/$(${pkgs.coreutils}/bin/date -Iseconds).sql
'';
in [
"0 22,4,10,16 * * * postgres ${backup_script}"
"0 3 * * * postgres ${pkgs.coreutils}/bin/rm -f $(${pkgs.coreutils}/bin/ls -1 ${backupDir}/*.sql | ${pkgs.coreutils}/bin/sort -r | ${pkgs.gnused}/bin/sed -e '1,12d')"
]) cfg.hosts);
};
system.activationScripts = lib.attrsets.mapAttrs' (name: hcfg:
lib.attrsets.nameValuePair "psql_replication_${name}" {
deps = [ "users" ];
text = ''
install -m 0700 -o postgres -g postgres -d ${cfg.base}/${name}/postgresql
install -m 0700 -o postgres -g postgres -d ${cfg.base}/${name}/postgresql_backup
'';
}) cfg.hosts;
systemd.services = lib.attrsets.mapAttrs' (name: hcfg:
let
dataDir = "${cfg.base}/${name}/postgresql";
in
lib.attrsets.nameValuePair "postgresql_backup_${name}" {
description = "Postgresql replication for ${name}";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
environment.PGDATA = dataDir;
path = [ hcfg.package ];
preStart = ''
if ! test -e ${dataDir}/PG_VERSION; then
mkdir -m 0700 -p ${dataDir}
chown -R postgres:postgres ${dataDir}
fi
'';
script = let
fp = n: config.secrets.fullPaths."postgresql_replication/${name}/${n}";
in ''
if ! test -e ${dataDir}/PG_VERSION; then
pg_basebackup -d $(cat ${fp "connection_string"}) -D ${dataDir} -S ${hcfg.slot}
fi
ln -sfn ${fp "recovery.conf"} ${dataDir}/recovery.conf
ln -sfn ${fp "postgresql.conf"} ${dataDir}/postgresql.conf
exec postgres
'';
serviceConfig = {
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
User = "postgres";
Group = "postgres";
PermissionsStartOnly = true;
RuntimeDirectory = "postgresql";
Type = "notify";
KillSignal = "SIGINT";
KillMode = "mixed";
# basebackup can take a long time
TimeoutStartSec="infinity";
TimeoutStopSec = 120;
};
unitConfig.RequiresMountsFor = dataDir;
}) cfg.hosts;
};
}
|