Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # Sample: rebuild a four-node CX7 ring and the NFS model shares.
- # All site-specific values are placeholders; fill in the CONFIG block and the
- # interface names before running.
- #
- # Addresses persist via netplan. NFS persists via fstab automounts and nfs-server.
- # Cross-ring forwarding persists via sysctl and cx7-ring-forward.service.
- #
- # Ring, walked from node1:
- # node1 right 10.100.128.2 -- node2 left 10.100.128.1 twin .129
- # node2 right 10.100.134.1 -- node3 left 10.100.134.2 twin .135
- # node3 right 10.100.136.1 -- node4 left 10.100.136.2 twin .137
- # node4 right 10.100.132.2 -- node1 left 10.100.132.1 twin .133
- #
- # The 10.100.128.0/20 plan and the enp*/enP* interface names are examples.
- # Take the real names from `ip -br link`.
- #
- # Run from node1, in a terminal so sudo can ask for a password on each machine:
- # sudo bash ./cx7-ring-apply.sh --all
- #
- # --all configures node2 and node4, reaches node3 over its management IP,
- # configures node3, then configures node1. If node3 was not reachable, fix
- # that and rerun with --finish to configure node3 and reload node1's mounts.
- set -euo pipefail
- # ---- edit for your cluster ----
- USER_NAME="<username>" # login name on every node
- USER_HOME="/home/$USER_NAME"
- MODELS_DIR="<models folder/hf cache>"
- # Host short names are assumed to be node1..node4; change the case labels
- # below if yours differ. node3 is reached once over its management/Wi-Fi IP,
- # because its ring address does not exist until this script has run there.
- NODE3_WIFI="192.0.2.170" # RFC 5737 TEST-NET-1 placeholder
- # One public key per node. Replace the key material and the comments.
- CLUSTER_KEYS=(
- "ssh-ed25519 <NODE1_PUBKEY> $USER_NAME@node1"
- "ssh-ed25519 <NODE2_PUBKEY> $USER_NAME@node2"
- "ssh-ed25519 <NODE4_PUBKEY> $USER_NAME@node4"
- )
- # -------------------------------
- MARK_BEGIN="# BEGIN cx7-models"
- MARK_END="# END cx7-models"
- die() { echo "error: $*" >&2; exit 1; }
- host_short() { hostname -s; }
- write_netplan() {
- local body="$1"
- local dest=/etc/netplan/99-cx7-ring.yaml
- if [[ -f /etc/netplan/99-nvidia-sync-cluster.yaml && ! -f /etc/netplan/99-nvidia-sync-cluster.yaml.bak-ring ]]; then
- cp -a /etc/netplan/99-nvidia-sync-cluster.yaml /etc/netplan/99-nvidia-sync-cluster.yaml.bak-ring
- fi
- # The old file still holds the triangle addresses. Removing it stops boot from putting them back.
- rm -f /etc/netplan/99-nvidia-sync-cluster.yaml
- printf '%s\n' "$body" > "$dest"
- chmod 600 "$dest"
- if ! netplan generate; then
- rm -f "$dest"
- if [[ -f /etc/netplan/99-nvidia-sync-cluster.yaml.bak-ring ]]; then
- cp -a /etc/netplan/99-nvidia-sync-cluster.yaml.bak-ring /etc/netplan/99-nvidia-sync-cluster.yaml
- fi
- die "netplan generate failed, old cluster file restored"
- fi
- netplan apply
- }
- del_addr() {
- local iface="$1" cidr="$2"
- if ip -4 addr show dev "$iface" | grep -q " ${cidr} "; then
- ip addr del "$cidr" dev "$iface" || true
- fi
- }
- write_exports() {
- local line="$1"
- local tmp
- tmp=$(mktemp)
- if [[ -f /etc/exports ]]; then
- awk -v dir="$MODELS_DIR" '$1 != dir' /etc/exports > "$tmp" || true
- fi
- printf '%s\n' "$line" >> "$tmp"
- cat "$tmp" > /etc/exports
- rm -f "$tmp"
- exportfs -ra
- }
- write_fstab() {
- local block="$1"
- local tmp
- tmp=$(mktemp)
- awk -v b="$MARK_BEGIN" -v e="$MARK_END" -v home="$USER_HOME" '
- $0 == b {skip=1; next}
- $0 == e {skip=0; next}
- skip {next}
- index($2, home "/models-") == 1 {next}
- {print}
- ' /etc/fstab > "$tmp"
- printf '\n%s\n%s\n%s\n' "$MARK_BEGIN" "$block" "$MARK_END" >> "$tmp"
- cat "$tmp" > /etc/fstab
- rm -f "$tmp"
- }
- ensure_mountpoint() {
- local dir="$1"
- mkdir -p "$dir"
- chown "$USER_NAME:$USER_NAME" "$dir"
- }
- start_automounts() {
- local name unit
- systemctl daemon-reload
- for name in "$@"; do
- ensure_mountpoint "$USER_HOME/$name"
- unit=$(systemd-escape --path "$USER_HOME/$name")
- systemctl enable "$unit.automount" >/dev/null 2>&1 || true
- systemctl restart "$unit.automount" || echo "warning: $name automount did not start"
- done
- }
- install_forward_unit() {
- cat > /etc/systemd/system/cx7-ring-forward.service << 'EOF'
- [Unit]
- Description=Accept CX7 ring forwards between 10.100.128.0/20
- After=docker.service network-online.target
- Wants=network-online.target
- [Service]
- Type=oneshot
- RemainAfterExit=yes
- ExecStart=/bin/bash -c 'iptables -C FORWARD -s 10.100.128.0/20 -d 10.100.128.0/20 -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -s 10.100.128.0/20 -d 10.100.128.0/20 -j ACCEPT'
- [Install]
- WantedBy=multi-user.target
- EOF
- systemctl daemon-reload
- systemctl enable --now cx7-ring-forward.service
- }
- install_sysctl() {
- cat > /etc/sysctl.d/99-cx7-ring.conf << 'EOF'
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.rp_filter=2
- net.ipv4.conf.default.rp_filter=2
- EOF
- sysctl --system >/dev/null
- }
- allow_ufw() {
- if command -v ufw >/dev/null && ufw status | grep -q 'Status: active'; then
- ufw allow from 10.100.128.0/20 to any port 2049 proto tcp >/dev/null || true
- ufw route allow from 10.100.128.0/20 to 10.100.128.0/20 >/dev/null || true
- fi
- }
- install_cluster_keys() {
- local home="$USER_HOME"
- local auth="$home/.ssh/authorized_keys"
- mkdir -p "$home/.ssh"
- touch "$auth"
- chown "$USER_NAME:$USER_NAME" "$home/.ssh" "$auth"
- chmod 700 "$home/.ssh"
- chmod 600 "$auth"
- local pub
- for pub in "${CLUSTER_KEYS[@]}"; do
- grep -qxF "$pub" "$auth" || echo "$pub" >> "$auth"
- done
- if [[ ! -f "$home/.ssh/id_ed25519_cluster" ]]; then
- sudo -u "$USER_NAME" ssh-keygen -t ed25519 -N "" -q -f "$home/.ssh/id_ed25519_cluster" -C "$USER_NAME@$(host_short)"
- fi
- chown "$USER_NAME:$USER_NAME" "$home/.ssh/id_ed25519_cluster" "$home/.ssh/id_ed25519_cluster.pub"
- chmod 600 "$home/.ssh/id_ed25519_cluster"
- }
- apply_local() {
- [[ $(id -u) -eq 0 ]] || die "run as root: sudo bash $0 --local"
- local name
- name=$(host_short)
- install_sysctl
- install_forward_unit
- allow_ufw
- if ! systemctl cat nfs-server.service >/dev/null 2>&1; then
- apt-get update -qq
- DEBIAN_FRONTEND=noninteractive apt-get install -y nfs-kernel-server
- fi
- systemctl enable --now nfs-server
- install_cluster_keys
- mkdir -p "$MODELS_DIR"
- chown "$USER_NAME:$USER_NAME" "$MODELS_DIR"
- case "$name" in
- node1)
- write_netplan "$(cat << 'EOF'
- network:
- version: 2
- ethernets:
- enp1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.128.2/24]
- routes:
- - to: 10.100.134.0/24
- via: 10.100.128.1
- enP2p1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.129.2/24]
- routes:
- - to: 10.100.135.0/24
- via: 10.100.129.1
- enp1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.132.1/24]
- routes:
- - to: 10.100.136.0/24
- via: 10.100.132.2
- enP2p1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.133.1/24]
- routes:
- - to: 10.100.137.0/24
- via: 10.100.133.2
- EOF
- )"
- write_exports "$MODELS_DIR 10.100.128.1(ro,async,no_subtree_check,no_root_squash) 10.100.132.2(ro,async,no_subtree_check,no_root_squash) 10.100.134.2(ro,async,no_subtree_check,no_root_squash)"
- write_fstab "$(cat << EOF
- 10.100.128.1:$MODELS_DIR $USER_HOME/models-node2 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- 10.100.132.2:$MODELS_DIR $USER_HOME/models-node4 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- 10.100.134.2:$MODELS_DIR $USER_HOME/models-node3 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- EOF
- )"
- start_automounts models-node2 models-node4 models-node3
- ;;
- node2)
- del_addr enp1s0f1np1 10.100.130.1/24
- del_addr enP2p1s0f1np1 10.100.131.1/24
- write_netplan "$(cat << 'EOF'
- network:
- version: 2
- ethernets:
- enp1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.128.1/24]
- routes:
- - to: 10.100.132.0/24
- via: 10.100.128.2
- enP2p1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.129.1/24]
- routes:
- - to: 10.100.133.0/24
- via: 10.100.129.2
- enp1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.134.1/24]
- routes:
- - to: 10.100.136.0/24
- via: 10.100.134.2
- enP2p1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.135.1/24]
- routes:
- - to: 10.100.137.0/24
- via: 10.100.135.2
- EOF
- )"
- write_exports "$MODELS_DIR 10.100.128.2(rw,async,no_subtree_check,no_root_squash) 10.100.134.2(rw,async,no_subtree_check,no_root_squash) 10.100.132.2(rw,async,no_subtree_check,no_root_squash)"
- write_fstab "$(cat << EOF
- 10.100.128.2:$MODELS_DIR $USER_HOME/models-node1 nfs4 nofail,x-systemd.automount,_netdev,noatime,ro 0 0
- 10.100.134.2:$MODELS_DIR $USER_HOME/models-node3 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- 10.100.132.2:$MODELS_DIR $USER_HOME/models-node4 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- EOF
- )"
- start_automounts models-node1 models-node3 models-node4
- ;;
- node4)
- del_addr enp1s0f0np0 10.100.130.2/24
- del_addr enP2p1s0f0np0 10.100.131.2/24
- write_netplan "$(cat << 'EOF'
- network:
- version: 2
- ethernets:
- enp1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.132.2/24]
- routes:
- - to: 10.100.128.0/24
- via: 10.100.132.1
- enP2p1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.133.2/24]
- routes:
- - to: 10.100.129.0/24
- via: 10.100.133.1
- enp1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.136.2/24]
- routes:
- - to: 10.100.134.0/24
- via: 10.100.136.1
- enP2p1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.137.2/24]
- routes:
- - to: 10.100.135.0/24
- via: 10.100.137.1
- EOF
- )"
- write_exports "$MODELS_DIR 10.100.132.1(rw,async,no_subtree_check,no_root_squash) 10.100.136.1(rw,async,no_subtree_check,no_root_squash) 10.100.128.1(rw,async,no_subtree_check,no_root_squash)"
- write_fstab "$(cat << EOF
- 10.100.132.1:$MODELS_DIR $USER_HOME/models-node1 nfs4 nofail,x-systemd.automount,_netdev,noatime,ro 0 0
- 10.100.136.1:$MODELS_DIR $USER_HOME/models-node3 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- 10.100.128.1:$MODELS_DIR $USER_HOME/models-node2 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- EOF
- )"
- start_automounts models-node1 models-node3 models-node2
- ;;
- node3)
- write_netplan "$(cat << 'EOF'
- network:
- version: 2
- ethernets:
- enp1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.134.2/24]
- routes:
- - to: 10.100.128.0/24
- via: 10.100.134.1
- enP2p1s0f0np0:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.135.2/24]
- routes:
- - to: 10.100.129.0/24
- via: 10.100.135.1
- enp1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.136.1/24]
- routes:
- - to: 10.100.132.0/24
- via: 10.100.136.2
- enP2p1s0f1np1:
- dhcp4: false
- dhcp6: false
- optional: true
- mtu: 9000
- addresses: [10.100.137.1/24]
- routes:
- - to: 10.100.133.0/24
- via: 10.100.137.2
- EOF
- )"
- write_exports "$MODELS_DIR 10.100.134.1(rw,async,no_subtree_check,no_root_squash) 10.100.136.2(rw,async,no_subtree_check,no_root_squash) 10.100.128.2(rw,async,no_subtree_check,no_root_squash)"
- write_fstab "$(cat << EOF
- 10.100.134.1:$MODELS_DIR $USER_HOME/models-node2 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- 10.100.136.2:$MODELS_DIR $USER_HOME/models-node4 nfs4 nofail,x-systemd.automount,_netdev,noatime 0 0
- 10.100.128.2:$MODELS_DIR $USER_HOME/models-node1 nfs4 nofail,x-systemd.automount,_netdev,noatime,ro 0 0
- EOF
- )"
- start_automounts models-node2 models-node4 models-node1
- ;;
- *)
- die "this host is '$name', expected node1, node2, node3, or node4"
- ;;
- esac
- echo "applied on $name"
- ip -4 -br addr show | awk '/enp1s0|enP2p1s0/'
- }
- # ssh options that ignore ~/.ssh/config. The host aliases point at Wi-Fi, and
- # netplan apply drops that route for a few seconds.
- cluster_ssh() {
- sudo -u "$USER_NAME" ssh -F /dev/null \
- -o IdentitiesOnly=yes \
- -o IdentityFile="$USER_HOME/.ssh/id_ed25519_cluster" \
- -o StrictHostKeyChecking=accept-new \
- -o User="$USER_NAME" \
- -o ConnectTimeout=8 \
- "$@"
- }
- wait_ping() {
- local ip="$1" i
- for i in $(seq 1 20); do
- ping -c 1 -W 1 "$ip" >/dev/null 2>&1 && return 0
- sleep 1
- done
- return 1
- }
- run_remote() {
- local name="$1" dest="$2" err
- echo "---- $name ($dest) ----"
- if ! err=$(cluster_ssh -o BatchMode=yes "$dest" true 2>&1); then
- echo "cannot ssh to $name at $dest: $err" >&2
- if [[ "$name" == node3 && "$err" == *Permission\ denied* ]]; then
- echo
- echo "node3 rejected the cluster key. On node3, as $USER_NAME, run:"
- echo
- echo " mkdir -p ~/.ssh && chmod 700 ~/.ssh"
- echo " touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
- echo " cat >> ~/.ssh/authorized_keys << 'KEYS'"
- printf '%s\n' "${CLUSTER_KEYS[@]}"
- echo " KEYS"
- fi
- return 1
- fi
- sudo -u "$USER_NAME" scp -F /dev/null \
- -o IdentitiesOnly=yes \
- -o IdentityFile="$USER_HOME/.ssh/id_ed25519_cluster" \
- -o StrictHostKeyChecking=accept-new \
- "$0" "$USER_NAME@${dest}:/tmp/cx7-ring-apply.sh"
- cluster_ssh -t "$dest" "sudo bash /tmp/cx7-ring-apply.sh --local"
- }
- apply_all() {
- [[ $(id -u) -eq 0 ]] || die "run as root: sudo bash $0 --all"
- [[ $(host_short) == node1 ]] || die "--all is run from node1"
- # netplan apply on node1 drops Wi-Fi and kills an SSH session. Configure the
- # other three first, and ignore SIGHUP so the last step still finishes.
- trap '' HUP
- local failed=0
- run_remote node2 10.100.128.1 || failed=1
- run_remote node4 10.100.132.2 || failed=1
- echo "waiting for the management route to node3"
- if wait_ping "$NODE3_WIFI"; then
- run_remote node3 "$NODE3_WIFI" || failed=1
- else
- echo "node3 did not answer at $NODE3_WIFI" >&2
- failed=1
- fi
- [[ "$failed" -eq 0 ]] || exit 1
- apply_local
- }
- finish_ring() {
- [[ $(id -u) -eq 0 ]] || die "run as root: sudo bash $0 --finish"
- [[ $(host_short) == node1 ]] || die "--finish is run from node1"
- run_remote node3 "$NODE3_WIFI"
- # node1's fstab is already correct. Reload the automounts now that node3 exists.
- systemctl daemon-reload
- # systemd escapes dashes in unit names; this assumes USER_NAME has none.
- systemctl reset-failed "home-${USER_NAME}-models*" || true
- start_automounts models-node2 models-node4 models-node3
- }
- case "${1:---local}" in
- --local) apply_local ;;
- --all) apply_all ;;
- --finish) finish_ring ;;
- *) die "use --local, --all, or --finish" ;;
- esac
Advertisement
Add Comment
Please, Sign In to add comment