Archived
variables.nix: - HA server LAN IPs: .200/.201/.202 → .228/.227/.229 (from ip-addressing.md) - Add haServer1StorageIp (.228), haServer2StorageIp (.227) for 192.168.4.0/29 - Add haStorageCidr for firewall rules ha-server host.nix (both nodes): - Add ens19 interface on storage subnet (/29) - Fix defaultGateway: 192.168.2.1 → 192.168.2.254 - Fix nameservers: 192.168.2.1/8.8.8.8 → domainControllerIp (.253) cluster-config.nix: - DRBD replication addresses: LAN IPs → storage IPs (keep replication off LAN) - Corosync ring_addrs: LAN IPs → storage IPs - Firewall: add haStorageCidr to allowed sources docs/network-cutover.md: step-by-step cutover plan with rollback at every stage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
3.4 KiB
Nix
108 lines
3.4 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:
|
|
# Production setting is resource-only: DRBD waits for the STONITH fence
|
|
# agent to confirm the peer is dead before promoting to Primary. This
|
|
# requires a working fence_pve_ssh STONITH resource in Pacemaker
|
|
# (see scripts/ha/cluster-enable-stonith.sh). On a fresh cluster with
|
|
# no fence device yet, temporarily change to dont-care and run
|
|
# cluster-enable-stonith.sh once the fence key is deployed.
|
|
{ lib, vars, ... }:
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
resource ha-data {
|
|
volume 0 {
|
|
device /dev/drbd0;
|
|
disk /dev/sdb;
|
|
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
|
|
'';
|
|
};
|
|
}
|