Add backup node
This commit is contained in:
parent
fa003c1dc1
commit
88bc60396e
182
flake.nix
182
flake.nix
|
@ -39,7 +39,6 @@
|
|||
};
|
||||
|
||||
makeGenericK3sConfig = { hostname, enableK3s ? true, serverIp ? null, extraModules ? [ ] }:
|
||||
with sd-images.packages.x86_64-linux.modules;
|
||||
makeConfig {
|
||||
inherit hostname;
|
||||
|
||||
|
@ -454,6 +453,98 @@
|
|||
];
|
||||
};
|
||||
|
||||
makeBackupConfig = system: { hostname, selfIp, unlockMounts ? true, mountVolumes ? true, backupHosts ? [] }:
|
||||
let
|
||||
deviceLabel = "RAID";
|
||||
device = "/dev/disk/by-label/${deviceLabel}";
|
||||
mountDir = "/btrfs/raid";
|
||||
in
|
||||
makeConfig {
|
||||
inherit hostname;
|
||||
|
||||
extraModules = sd-images.packages.${system}.RockPro64v2.modules ++ [
|
||||
(networkModule { inherit selfIp; })
|
||||
(btrbkModule {
|
||||
instances = builtins.map (item: item // { localMountDir = mountDir; }) backupHosts;
|
||||
})
|
||||
({ config, lib, pkgs, ... }:
|
||||
let
|
||||
keyFile = "backupKeyFile";
|
||||
keyFilePath = config.sops.secrets."${keyFile}".path;
|
||||
dryRun = ''
|
||||
for conf in $(ls /etc/btrbk); do
|
||||
btrbk -c /etc/btrbk/$conf dryrun
|
||||
done
|
||||
'';
|
||||
prepareDrives = ''
|
||||
for drive in "$1" "$2" "$3" "$4"; do
|
||||
if [ "$drive" == "" ]; then
|
||||
echo "Must provide 4 drives"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
drive_num=1
|
||||
for drive in "$1" "$2" "$3" "$4"; do
|
||||
echo "YES" | cryptsetup luksFormat $drive -d ${keyFilePath}
|
||||
cryptsetup luksOpen $drive "cryptdrive$drive_num" -d ${keyFilePath}
|
||||
drive_num=$((drive_num+1))
|
||||
done
|
||||
|
||||
mkfs.btrfs -L ${deviceLabel} -d raid10 /dev/mapper/cryptdrive1 /dev/mapper/cryptdrive2 /dev/mapper/cryptdrive3 /dev/mapper/cryptdrive4
|
||||
|
||||
mkdir -p ${mountDir}
|
||||
|
||||
mount ${device} ${mountDir}
|
||||
|
||||
btrfs subvolume create ${mountDir}/@snapshots
|
||||
|
||||
umount ${mountDir}
|
||||
|
||||
for drive_num in {1..4}; do
|
||||
cryptsetup luksClose "cryptdrive$drive_num"
|
||||
done
|
||||
'';
|
||||
in
|
||||
{
|
||||
sops.secrets."${keyFile}" = {
|
||||
format = "binary";
|
||||
sopsFile = ./secrets/${keyFile}.bin;
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
(writeShellScriptBin "prepare-drives" prepareDrives)
|
||||
(writeShellScriptBin "btrbk-dryrun" dryRun)
|
||||
];
|
||||
|
||||
environment.etc.crypttab = {
|
||||
enable = unlockMounts;
|
||||
text = ''
|
||||
cryptdrive1 /dev/sda ${keyFilePath} luks
|
||||
cryptdrive2 /dev/sdb ${keyFilePath} luks
|
||||
cryptdrive3 /dev/sdc ${keyFilePath} luks
|
||||
cryptdrive4 /dev/sdd ${keyFilePath} luks
|
||||
'';
|
||||
};
|
||||
|
||||
fileSystems =
|
||||
let
|
||||
filesystemConfig = {
|
||||
"${mountDir}" = {
|
||||
inherit device;
|
||||
fsType = "btrfs";
|
||||
options = [ "defaults" "compress=zstd" "rw" "loop" ];
|
||||
};
|
||||
};
|
||||
in
|
||||
if unlockMounts && mountVolumes then
|
||||
filesystemConfig
|
||||
else
|
||||
{ };
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
makeGarageConfig = system: { hostname, selfIp, unlockMounts ? true, mountVolumes ? true }:
|
||||
makeConfig {
|
||||
inherit hostname;
|
||||
|
@ -800,6 +891,80 @@
|
|||
selfIp = "192.168.20.180";
|
||||
serverIp = "192.168.20.120";
|
||||
};
|
||||
|
||||
backup-rockpro1 =
|
||||
let
|
||||
k3sMain = { ip, name }: {
|
||||
inherit name;
|
||||
primaryIp = ip;
|
||||
mountDir = "/btrfs/nvme";
|
||||
subvolumes = ["@k3s-config"];
|
||||
};
|
||||
|
||||
k3sOld = { ip, name }: {
|
||||
inherit name;
|
||||
primaryIp = ip;
|
||||
mountDir = "/btrfs/nvme2";
|
||||
subvolumes = ["@exports" "@garage"];
|
||||
};
|
||||
|
||||
k3s = { ip, name }: {
|
||||
inherit name;
|
||||
primaryIp = ip;
|
||||
mountDir = "/btrfs/nvme";
|
||||
subvolumes = ["@exports" "@garage"];
|
||||
};
|
||||
|
||||
garage = { ip, name }: [
|
||||
{
|
||||
name = "${name}-meta";
|
||||
primaryIp = ip;
|
||||
mountDir = "/btrfs/meta";
|
||||
subvolumes = ["@garage-cluster-config" "@garage-cluster-meta"];
|
||||
}
|
||||
{
|
||||
name = "${name}-data";
|
||||
primaryIp = ip;
|
||||
mountDir = "/btrfs/data";
|
||||
subvolumes = ["@garage-cluster-data"];
|
||||
}
|
||||
];
|
||||
|
||||
db = { ip, name }: {
|
||||
inherit name;
|
||||
primaryIp = ip;
|
||||
mountDir = "/btrfs/ssd";
|
||||
subvolumes = ["@postgres" "@postgres-cfg"];
|
||||
};
|
||||
in
|
||||
makeBackupConfig system {
|
||||
hostname = "backup-rockpro1";
|
||||
selfIp = "192.168.20.190";
|
||||
backupHosts =
|
||||
[(k3sMain { ip = "192.168.20.120"; name = "k3s1-config"; })] ++
|
||||
(builtins.map k3sOld [
|
||||
{ ip = "192.168.20.120"; name = "k3s1"; }
|
||||
{ ip = "192.168.20.121"; name = "k3s2"; }
|
||||
{ ip = "192.168.20.122"; name = "k3s3"; }
|
||||
{ ip = "192.168.20.123"; name = "k3s4"; }
|
||||
{ ip = "192.168.20.124"; name = "k3s5"; }
|
||||
{ ip = "192.168.20.125"; name = "k3s6"; }
|
||||
]) ++
|
||||
(builtins.map k3s [
|
||||
{ ip = "192.168.20.126"; name = "k3s7"; }
|
||||
{ ip = "192.168.20.127"; name = "k3s8"; }
|
||||
{ ip = "192.168.20.128"; name = "k3s9"; }
|
||||
{ ip = "192.168.20.129"; name = "k3s10"; }
|
||||
]) ++
|
||||
(builtins.foldl' (acc: item: (acc ++ (garage item))) [] [
|
||||
{ ip = "192.168.20.40"; name = "garage1"; }
|
||||
{ ip = "192.168.20.41"; name = "garage2"; }
|
||||
]) ++
|
||||
(builtins.map db [
|
||||
{ ip = "192.168.20.11"; name = "whitestorm1"; }
|
||||
{ ip = "192.168.20.24"; name = "redtail2"; }
|
||||
]);
|
||||
};
|
||||
};
|
||||
|
||||
deploy.nodes =
|
||||
|
@ -810,6 +975,13 @@
|
|||
{ name = "garage1"; ip = "192.168.20.40"; }
|
||||
{ name = "garage2"; ip = "192.168.20.41"; }
|
||||
{ name = "build2"; ip = "192.168.20.101"; }
|
||||
{ name = "k3s-rock1"; ip = "192.168.20.110"; }
|
||||
{ name = "k3s-rock2"; ip = "192.168.20.111"; }
|
||||
{ name = "k3s-rock3"; ip = "192.168.20.112"; }
|
||||
{ name = "k3s-rock4"; ip = "192.168.20.113"; }
|
||||
{ name = "k3s-rock5"; ip = "192.168.20.114"; }
|
||||
{ name = "k3s-rock6"; ip = "192.168.20.115"; }
|
||||
{ name = "k3s-rock7"; ip = "192.168.20.116"; }
|
||||
{ name = "k3s1"; ip = "192.168.20.120"; }
|
||||
{ name = "k3s2"; ip = "192.168.20.121"; }
|
||||
{ name = "k3s3"; ip = "192.168.20.122"; }
|
||||
|
@ -820,15 +992,9 @@
|
|||
{ name = "k3s8"; ip = "192.168.20.127"; }
|
||||
{ name = "k3s9"; ip = "192.168.20.128"; }
|
||||
{ name = "k3s10"; ip = "192.168.20.129"; }
|
||||
{ name = "k3s-rock1"; ip = "192.168.20.110"; }
|
||||
{ name = "k3s-rock2"; ip = "192.168.20.111"; }
|
||||
{ name = "k3s-rock3"; ip = "192.168.20.112"; }
|
||||
{ name = "k3s-rock4"; ip = "192.168.20.113"; }
|
||||
{ name = "k3s-rock5"; ip = "192.168.20.114"; }
|
||||
{ name = "k3s-rock6"; ip = "192.168.20.115"; }
|
||||
{ name = "k3s-rock7"; ip = "192.168.20.116"; }
|
||||
{ name = "k3s-quartza1"; ip = "192.168.20.160"; }
|
||||
{ name = "k3s-rockpro1"; ip = "192.168.20.180"; }
|
||||
{ name = "backup-rockpro1"; ip = "192.168.20.190"; }
|
||||
];
|
||||
in
|
||||
builtins.foldl'
|
||||
|
|
|
@ -54,6 +54,22 @@ let
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
backup = primaryIp: remoteMountDir: localMountDir: subvolumes: {
|
||||
backend_remote = "btrfs-progs-sudo";
|
||||
ssh_identity = config.sops.secrets.private_key.path;
|
||||
ssh_user = "btrbk";
|
||||
stream_buffer = "512m";
|
||||
target_preserve = "2h 2d 10w *m";
|
||||
target_preserve_min = "24h";
|
||||
transaction_log = "/var/log/btrbk.log";
|
||||
volume = {
|
||||
"ssh://${primaryIp}${remoteMountDir}" = btrbkSecondary {
|
||||
targetDir = "${localMountDir}/@snapshots";
|
||||
inherit subvolumes;
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
sops.secrets.private_key = {
|
||||
|
@ -79,9 +95,15 @@ in
|
|||
];
|
||||
extraPackages = with pkgs; [ gzip ];
|
||||
instances = (builtins.foldl'
|
||||
(acc: { primaryIp ? null, mountDir, subvolumes, name ? "btrbk" }:
|
||||
(acc: { primaryIp ? null, mountDir, localMountDir ? null, subvolumes, name ? "btrbk" }:
|
||||
let
|
||||
selected = if primaryIp == null then (primary mountDir subvolumes) else (secondary primaryIp mountDir subvolumes);
|
||||
selected =
|
||||
if primaryIp == null && localMountDir == null then
|
||||
(primary mountDir subvolumes)
|
||||
else if localMountDir == null then
|
||||
(secondary primaryIp mountDir subvolumes)
|
||||
else
|
||||
(backup primaryIp mountDir localMountDir subvolumes);
|
||||
in
|
||||
acc //
|
||||
{
|
||||
|
|
24
secrets/backupKeyFile.bin
Normal file
24
secrets/backupKeyFile.bin
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"data": "ENC[AES256_GCM,data:1z/pASQGRm9I3cSlxlqtM+I8bcKQNWLxbNwKlRMTDctBTpHm/ayVwsVeNImRqMeu9zzaA/EJV1uP9s+Zxc5S5hk0hpKuNnPUwrxWMm+RvCbMpYcjE7kYorYwfZEsqniALlzA7uVmK0CcTMSy3dKSKCaEWHXgZyk9dK8wPCmu5KPlmIpZhtv6l761X6uWbsHhhW8AgRG9qPzJkdgdtuqFc0bzQeuQG7zJjeP2HXO4VgYkFJ5HtCrTxDJuYLEL5JpEl+CMuFk7I8RIw7wAr4h7DSCEQj28rw+lyiGgqMa+ZIOBfvo1Oq/el6Ut1jrcpdIZGYv8HpVduypEpfANsDjFl4yvauos8mH402Y6ag5MOLvUEKlQvvNz3xttd/mOnfSFhqcqihcD6KHqkUfhNkucGWmphne17zRYIlelzz0Ic+GuboaKaPuHx7Xlt36K/hNAUwcV2IcfdZfTMmgQY0tQwjtvQoX0PvJnBCUe6NgXO4kmq33Mgi6G6WGgNuHawyX9qfBJj5eXtbd2SdQmf56A2NdQiT8GeasEfLOYYxEmMX1kRp5qfNPTUVKI/Qfhktew20ZPBqAEo3LnyiVr/x8AFunqaJHJE0k7bBHfUnhy6ZOm2/NhL6SntP7h9QmzLhjlrRGufuWfxEPvnIk7pn/Fuos4bmxYxZ/qU7q2jMcPDFM=,iv:uohMMzhcL4krkBUDdiPMljBHF221ssNJYg1VerGwSIU=,tag:7Xg2qI4KGaU24cIZuobDIA==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age17yhtwnhqjssghc5qqamt0fqdu27zpqms8d8ghrc0txeevywfp3ssklfy57",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4Tzd0RDUvZEYrc0pLbVFy\nUnV2SXFZb3kwL2laTTVYT0VJMEE3TGVGZXc4CjluVHpJRWZEM01Ta0NrWmJuaTc4\nRnpteXRJOEZuemlqcm45RFdjSSsrQVkKLS0tIGZpNnVHbkZxT0RQUlJ4Z1F1dXk1\nN1k1c1crRmNyc0hRSUYvclpoK3E2RHMKkRZM2aav3IJXC/t8b3uizgiaNA4JX61P\nc4dqbZPAP/3s9Go3/853/trFNXbys498VEUI4BYl7kjEPG1PUplpcg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age145uwrexj6ffaaxy7jg3j29gtchhwy0y0nttw06zeuxkqsy8rnpds7fh7xq",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvUGxjdGZKdTIvREtyY3lB\nZWdHcmNJQ2p1V0YyVkNZR0QrbG5BTWFBQ3dnCmI0WktWeHZsNTBwY0FNOUd1Qllk\nVXlkTTJFWHMzR0FJMVNDd3lib2FtVzQKLS0tIGMzWURjTFk2aFVObWwrSDB3N0hh\nWU5MVjEvVCtSeXJQS0w5VnFzRzFlWDgKokE04o3VtUo5l6VFDqMqj3ZfhNaNXwq3\n2Ggq0LMrow8hag/ssOcbylKOnUEb559tz6pIQCamRXcaT/epAh7iiA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2023-05-06T17:49:04Z",
|
||||
"mac": "ENC[AES256_GCM,data:M0okeypT1Ot5SR4DsoOoht1kXchalmCyA62j3RxVJU9uO1rQ6kxL4LnGtVjrhgR0mk6tenZe5Ym745gq3IsMgtBmExLfC7zbY8G/zf2KUxeRPYwEbOnjxVe4IoJutsq2/fC5G1Fviqp1ih1EJTs9VHNUn5e7A6ZXoHt9cdB/J6A=,iv:R+oFRuwR2afidCn/+/whjkWd9Q+KfW0Jz7L8Hy7ZIio=,tag:M9ODjko2iSBbB60qndBQRg==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.7.3"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue