Archived
Check NixOS configurations / eval-hosts (push) Failing after 9m44s
/dev/sda and /dev/sdb are assigned by the OS based on Proxmox disk-add
order, which is not consistent across VMs. Use the SCSI controller path
instead — drive-scsi1 is always the dedicated data disk on all HA nodes
regardless of which sda/sdb it gets assigned to.
variables.nix: replace per-node haServer{1,2}DrbdDisk with a single
haServerDrbdDisk using /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi1.
cluster-config.nix: revert to shared volume block (per-node block was
needed for /dev/sd* but the by-id path is identical on both nodes).
cluster-init.sh:
- single DRBD_DISK variable (matching haServerDrbdDisk)
- robust /etc/drbd.conf patch: NixOS manages this file as a symlink to
a read-only Nix store path; cp --remove-destination breaks the symlink
before sed -i so the edit actually takes effect
- scp helper script to NODE2 rather than bash -c over SSH to avoid
quoting complexity
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
136 lines
5.0 KiB
Nix
136 lines
5.0 KiB
Nix
# Cluster-wide HA config shared by both ha-server nodes.
|
|
#
|
|
# Covers everything that is identical on both nodes and references cluster
|
|
# topology (node IPs, hostnames, DRBD resource). Per-node identity
|
|
# (hostname, static IP, stateVersion) lives in hosts/ha-server-{1,2}/host.nix.
|
|
#
|
|
# Corosync authkey:
|
|
# /etc/corosync/authkey (mode 0400) is managed by sops-nix below.
|
|
# Bootstrap: run scripts/ha/cluster-init.sh on node1 to generate the key,
|
|
# then encrypt it with: sops -e --input-type binary /etc/corosync/authkey > secrets/ha-corosync-authkey
|
|
# Both host keys must be registered via sync-host-keys.sh first so both nodes can decrypt it.
|
|
#
|
|
# DRBD fencing:
|
|
# resource-only with crm-fence-peer.sh: DRBD calls the Pacemaker-aware
|
|
# crm-fence-peer.sh handler before promoting. The handler checks the CIB
|
|
# to confirm the peer's DRBD resource is stopped and returns 7 (successfully
|
|
# fenced), allowing safe promotion without requiring power-fencing (STONITH).
|
|
# The unfence handler crm-unfence-peer.sh clears the outdate flag when the
|
|
# peer reconnects. This is the correct setting for Pacemaker+DRBD clusters
|
|
# with STONITH disabled; crm-fence-peer.sh replaces the need for a separate
|
|
# STONITH device during the testing phase. Switch to resource-and-stonith
|
|
# once the fence_pve_ssh STONITH resource is active (see
|
|
# scripts/ha/cluster-enable-stonith.sh).
|
|
{ lib, vars, ... }:
|
|
{
|
|
# Root SSH access — same key set as nixos user so all admin keys can reach root.
|
|
users.users.root.openssh.authorizedKeys.keys = [
|
|
vars.adminSshKey
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICMJhrfFayLBG+gWtO6oAvgambw5nWWgztiTFEaaaVRH debian@surface"
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGygkCljN6uKpdJbHTOQtn8ZnH+wKXDLAwrDFbLrE/65 nixos@nixos"
|
|
];
|
|
|
|
# Passwordless sudo for wheel — operator SSHes as nixos and uses sudo for
|
|
# cluster management commands (drbdadm, crm*, pcs, etc.)
|
|
security.sudo.wheelNeedsPassword = lib.mkForce false;
|
|
|
|
# DRBD lock-file directory (drbd-utils checks for it; missing → harmless but noisy warnings).
|
|
systemd.tmpfiles.rules = [ "d /var/lib/drbd 0750 root root -" ];
|
|
|
|
# Prevent drbd.service from auto-starting at boot / nixos-rebuild switch.
|
|
# Pacemaker's OCF drbd agent calls drbdadm up/down directly when managing
|
|
# the resource. If drbd.service also runs drbdadm up all while DRBD is
|
|
# already Primary under Pacemaker, apply-al fails with "device busy" (exit 20).
|
|
systemd.services.drbd.wantedBy = lib.mkForce [];
|
|
|
|
services.drbd = {
|
|
enable = true;
|
|
config = ''
|
|
global {
|
|
usage-count yes;
|
|
}
|
|
|
|
common {
|
|
net {
|
|
protocol C;
|
|
ping-int 1;
|
|
verify-alg sha256;
|
|
after-sb-0pri discard-zero-changes;
|
|
after-sb-1pri discard-secondary;
|
|
}
|
|
disk {
|
|
fencing resource-only;
|
|
}
|
|
handlers {
|
|
fence-peer "/run/current-system/sw/lib/drbd/crm-fence-peer.sh";
|
|
unfence-peer "/run/current-system/sw/lib/drbd/crm-unfence-peer.sh";
|
|
}
|
|
}
|
|
|
|
resource ha-data {
|
|
volume 0 {
|
|
device /dev/drbd0;
|
|
disk ${vars.haServerDrbdDisk};
|
|
meta-disk internal;
|
|
}
|
|
|
|
on ${vars.haServer1Host} {
|
|
address ${vars.haServer1StorageIp}:${toString vars.ports.haServerDrbd};
|
|
}
|
|
|
|
on ${vars.haServer2Host} {
|
|
address ${vars.haServer2StorageIp}:${toString vars.ports.haServerDrbd};
|
|
}
|
|
}
|
|
'';
|
|
};
|
|
|
|
# /etc/corosync/authkey — sops binary secret, identical on both nodes.
|
|
# Decryptable by both ha-server host keys (added by sync-host-keys.sh).
|
|
sops.secrets.corosync_authkey = {
|
|
sopsFile = ../../secrets/ha-corosync-authkey;
|
|
format = "binary";
|
|
path = "/etc/corosync/authkey";
|
|
mode = "0400";
|
|
restartUnits = [ "corosync.service" ];
|
|
};
|
|
|
|
# NixOS common config enables NetworkManager by default; HA cluster nodes
|
|
# need stable static IPs with predictable interface names — NM is not suitable.
|
|
networking.networkmanager.enable = lib.mkForce false;
|
|
|
|
# services.corosync.enable is set by modules/ha/pacemaker-stack.nix.
|
|
services.corosync = {
|
|
clusterName = "ha-cluster";
|
|
nodelist = [
|
|
{ nodeid = 1; name = vars.haServer1Host; ring_addrs = [ vars.haServer1StorageIp ]; }
|
|
{ nodeid = 2; name = vars.haServer2Host; ring_addrs = [ vars.haServer2StorageIp ]; }
|
|
];
|
|
};
|
|
|
|
networking.firewall = {
|
|
allowedTCPPorts = [
|
|
vars.ports.haServerIscsi
|
|
vars.ports.haServerPacemakerRemoted
|
|
vars.ports.haServerPcsd
|
|
vars.ports.haServerDrbd
|
|
vars.ports.nfsRpcbind
|
|
vars.ports.nfsd
|
|
vars.ports.nfsMountd
|
|
];
|
|
allowedUDPPorts = [
|
|
vars.ports.haServerCorosync1
|
|
vars.ports.haServerCorosync2
|
|
vars.ports.haServerCorosyncCrypto
|
|
vars.ports.nfsRpcbind
|
|
vars.ports.nfsd
|
|
vars.ports.nfsMountd
|
|
];
|
|
extraCommands = ''
|
|
iptables -A INPUT -s ${vars.haServer1Ip}/32 -j ACCEPT
|
|
iptables -A INPUT -s ${vars.haServer2Ip}/32 -j ACCEPT
|
|
iptables -A INPUT -s ${vars.haStorageCidr} -j ACCEPT
|
|
'';
|
|
};
|
|
}
|