9.10 — Diễn tập "node chết, đưa node dự phòng vào thay" (04 thế chỗ 05)
Kịch bản production thật: oss-nvme-05 chết vì journal (node Unattached, record SSD Failed,
không có đường mềm) → đưa node dự phòng oss-nvme-04 (đã làm sạch) vào cụm → thử lệnh
chính quy replace node → giải thể hoàn toàn 05.
Trạng thái xuất phát: - Cluster: 01/02/03 khỏe + server 35b83b77 (05) còn trong cụm, node 8861a8fc Unattached - etcd: 4 member started (01/02/03/05 — etcd của 05 vẫn sống, độc lập với tầng LightOS) - 04: đã ép sạch, chờ vào cụm
3 terminal: [ADMIN] vm-admin-01 · [NEW04] oss-nvme-04 · [REF] oss-nvme-01 · (+[OLD05] khi giải thể)
PHA A — Đưa 04 vào cụm (theo runbook v3 đã nghiệm thu, giá trị điền sẵn)
A0 — Định vị + kiểm xuất phát điểm
### [NEW04] — node sach? ky vong 0/8/0/0/0
echo -n "pkg: "; rpm -qa | grep -icE "lightos|duros|etcd"
echo -n "disks: "; lsblk | grep -cE "nvme[0-7]n1"
echo -n "repos: "; ls /etc/yum.repos.d/*.repo 2>/dev/null | wc -l
echo -n "raid0: "; grep -E "^md[0-9]+ :" /proc/mdstat | grep -c raid0
echo -n "leftover: "; ls -d /var/lib/etcd /etc/lightos* /etc/node-manager /etc/cluster-manager /etc/duroslight /var/cache/nm_service /usr/lib/systemd/system/duroslight-*.service 2>/dev/null | wc -l
# Chua dat -> chay khoi EP SACH 5 nhip (Buoc 0 runbook v3-PerNode)
### [ADMIN] — cluster: 4 server (05 con xac), khong server00
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list servers
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list nodes # 3 Active + 05 Unattached
### [REF] — member 4 started; registry DON RAC truoc khi them nguoi moi
etcdctl member list
etcdctl get --prefix /registry/discovery-service --keys-only
# Ky vong key: 88940976(03) a0d5bf2a(01) a17b6c72(02) 35b83b77(05-con-song-tam)
# NEU con a173b57d (server 04 DOI CU, da delete hom 02/08) -> xoa:
etcdctl del /registry/discovery-service/a173b57d-648b-4e52-9c14-a6c04dfd6c8b
A1 — Cài package + disable toàn bộ
### [ADMIN]
NEW=10.237.94.104
ssh root@$NEW 'rm -rf /tmp/rpms && mkdir -p /tmp/rpms'
scp ~/lightos-offline/target_rpms/*.rpm ~/lightos-offline/deps/*.rpm root@$NEW:/tmp/rpms/
### [NEW04]
yum install -y --nogpgcheck /tmp/rpms/createrepo_c-libs*.rpm /tmp/rpms/createrepo_c-[0-9]*.rpm /tmp/rpms/drpm*.rpm 2>/dev/null
createrepo /tmp/rpms
printf "[lightos]\nname=lightos\nbaseurl=file:///tmp/rpms\ngpgcheck=0\nenabled=1\n" > /etc/yum.repos.d/lightos.repo
dnf clean all
yum install -y --nogpgcheck etcd
yum install -y --nogpgcheck lightos --allowerasing
yum install -y --nogpgcheck tar net-tools yum-utils
for s in etcd node-manager cluster-manager api-service discovery-service upgrade-manager lightbox-exporter profile-generator irqbalance; do systemctl disable --now $s 2>/dev/null; done
reboot
### [NEW04] — sau reboot ~90s, CHECKPOINT 1.5
systemctl is-active etcd node-manager discovery-service cluster-manager # toan inactive/failed
ps -C etcd,node-manager -o args= # TRONG
### [ADMIN] — khong moc them server00 (van 4 server)
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list servers | tail -5
### [ADMIN] — 1.6 verify package
diff <(ssh root@10.237.94.101 'rpm -qa --qf "%{NAME}\n" | sort') \
<(ssh root@$NEW 'rpm -qa --qf "%{NAME}\n" | sort') | grep '^<' # khong lightos/duros/etcd
ssh root@$NEW 'uname -r' # = REF
A2 — Cert mới (CA cụm + clusterId + danh tính 04)
### [ADMIN]
cd ~/lightos-certificates-clusterNew
NEWHOST=oss-nvme-04; NEW_DATA_IP=10.237.95.104
CLUSTER_ID=$(ssh root@10.237.94.101 "grep clusterId /etc/cluster-manager/cluster-manager.yaml | awk '{print \$2}'")
echo "CLUSTER_ID=$CLUSTER_ID" # f49c7acd-...
mkdir -p old-$NEWHOST && mv $NEWHOST-cert-etcd-peer*.pem old-$NEWHOST/ 2>/dev/null
cat > /tmp/$NEWHOST.cnf <<EOF
[req]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
countryName = IL
stateOrProvinceName = N/A
localityName = KS
organizationName = Lightbits
commonName = $NEWHOST
serialNumber = $CLUSTER_ID
[req_ext]
subjectAltName = @alt_names
[v3_req]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth,serverAuth
[alt_names]
IP.1=$NEW_DATA_IP
IP.2=127.0.0.1
DNS.1=$NEWHOST
DNS.2=localhost
EOF
openssl req -nodes -newkey rsa:2048 -config /tmp/$NEWHOST.cnf \
-keyout $NEWHOST-cert-etcd-peer-key.pem -out $NEWHOST-cert-etcd-peer-csr.pem
openssl x509 -req -days 3650 -set_serial 0x$(openssl rand -hex 20) \
-in $NEWHOST-cert-etcd-peer-csr.pem -out $NEWHOST-cert-etcd-peer.pem \
-extensions v3_req -extfile /tmp/$NEWHOST.cnf -CA etcd-ca.pem -CAkey etcd-ca-key.pem
chmod 600 $NEWHOST-cert-etcd-peer*
openssl x509 -in $NEWHOST-cert-etcd-peer.pem -noout -text | grep -A2 'Subject Alternative Name'
openssl verify -CAfile etcd-ca.pem $NEWHOST-cert-etcd-peer.pem
diff <(openssl x509 -in $NEWHOST-cert-etcd-peer.pem -noout -pubkey) \
<(openssl pkey -in $NEWHOST-cert-etcd-peer-key.pem -pubout) && echo "KEY-CERT MATCH"
# 3/3 xanh moi upload:
ssh root@$NEW 'mkdir -p /etc/etcd/ssl && chmod 700 /etc/etcd/ssl'
scp etcd-ca.pem etcd-ca-key.pem root@$NEW:/etc/etcd/ssl/
scp $NEWHOST-cert-etcd-peer.pem root@$NEW:/etc/etcd/ssl/cert-etcd-peer.pem
scp $NEWHOST-cert-etcd-peer-key.pem root@$NEW:/etc/etcd/ssl/cert-etcd-peer-key.pem
### [NEW04]
chmod 600 /etc/etcd/ssl/*; ls -la /etc/etcd/ssl/
A3 — etcd join
### [NEW04]
set +H
NEWHOST=$(hostname); NEW_DATA_IP=10.237.95.104
REF_HOST=oss-nvme-01; REF_DATA_IP=10.237.95.101
mkdir -p /var/lib/etcd && chmod 700 /var/lib/etcd
grep -q ETCDCTL_API ~/.bashrc || sed -i "1i export ETCDCTL_API=3" ~/.bashrc
### [ADMIN] — trung chuyen unit tu REF sang NEW04
scp root@10.237.94.101:/etc/systemd/system/etcd.service /tmp/etcd.service.orig
scp /tmp/etcd.service.orig root@$NEW:/tmp/etcd.service.orig
### [REF] — chuoi initial-cluster tu member list that (4 member ke ca 05 — van hop le!)
etcdctl member list | grep -v unstarted | awk -F', ' '{printf "%s%s=%s",sep,$3,$4; sep=","}'; echo
### [NEW04] — sed + kiem 4 dieu
INITIAL_CLUSTER='<chuoi tu REF>,'"$NEWHOST=https://$NEW_DATA_IP:2380"
echo "$INITIAL_CLUSTER" # 5 entry
for v in NEWHOST NEW_DATA_IP REF_HOST REF_DATA_IP INITIAL_CLUSTER; do [ -z "${!v}" ] && echo "!! $v RONG"; done
sed -e "s/--name=$REF_HOST/--name=$NEWHOST/" \
-e "s/${REF_DATA_IP//./\\.}/$NEW_DATA_IP/g" \
-e "s/--initial-cluster-state=new/--initial-cluster-state=existing/" \
-e "s#--initial-cluster=[^ ]*#--initial-cluster=$INITIAL_CLUSTER#" \
/tmp/etcd.service.orig > /etc/systemd/system/etcd.service
grep -E 'name=|initial-cluster|urls' /etc/systemd/system/etcd.service # name=04 / .104 / existing / 5 entry
systemctl is-active firewalld >/dev/null 2>&1 && firewall-cmd --add-port=2379/tcp --add-port=2380/tcp --permanent && firewall-cmd --reload
### [REF]
etcdctl member add oss-nvme-04 --peer-urls=https://10.237.95.104:2380
etcdctl member list # 04 unstarted
### [NEW04] — quet ma + start
ps -C etcd -o args= # co gi tran trui -> con ma
systemctl stop etcd 2>/dev/null; rm -rf /var/lib/etcd/*
systemctl daemon-reload && systemctl enable --now etcd
sleep 8; systemctl is-active etcd
### [REF]
etcdctl member list # 5 member, 04 started
A4 — Config
### [ADMIN] — trung chuyen 8 thu muc REF -> NEW04
rm -rf /tmp/cfg && mkdir -p /tmp/cfg && cd /tmp/cfg
for d in node-manager cluster-manager api-service upgrade-manager discovery-service lbcli lightbox-exporter profile-generator; do
scp -q -r root@10.237.94.101:/etc/$d ./ && echo "got $d"
done
ssh root@$NEW 'rm -rf /tmp/cfg && mkdir -p /tmp/cfg'
scp -q -r /tmp/cfg/* root@$NEW:/tmp/cfg/ && echo "pushed"
### [NEW04] — OLD_* dong, UUID moi, sed, kiem
cd /tmp/cfg; set +H
OLD_SUUID=$(grep '^serverUUID:' node-manager/node-manager.yaml | awk '{print $2}')
OLD_NUUID=$(grep -m1 '^- uuid:' node-manager/node-manager.yaml | awk '{print $3}')
OLD_CMID=$(grep '^id:' cluster-manager/cluster-manager.yaml | awk '{print $2}' | tr -d '"')
SUUID=$(uuidgen); NUUID=$(uuidgen)
echo "OLD: $OLD_SUUID / $OLD_NUUID / $OLD_CMID"; echo "NEW: SUUID=$SUUID NUUID=$NUUID" # LUU LAI
for v in NEWHOST NEW_DATA_IP REF_HOST REF_DATA_IP SUUID NUUID OLD_SUUID OLD_NUUID OLD_CMID; do [ -z "${!v}" ] && echo "!! $v RONG"; done
grep -rl "$REF_HOST" . | xargs -r sed -i "s/$REF_HOST/$NEWHOST/g"
grep -rl "$REF_DATA_IP" . | xargs -r sed -i "s/${REF_DATA_IP//./\\.}/$NEW_DATA_IP/g"
grep -rl "$OLD_SUUID" . | xargs -r sed -i "s/$OLD_SUUID/$SUUID/g"
grep -rl "$OLD_NUUID" . | xargs -r sed -i "s/$OLD_NUUID/$NUUID/g"
sed -i "s/^id: $OLD_CMID/id: \"\"/" cluster-manager/cluster-manager.yaml
printf 'profileGeneratorVersion: 0\n' > profile-generator/system-profile.yaml
rm -f node-manager/gftl.db node-manager/node-manager.yaml.rpmsave
grep -rn "$REF_HOST\|${REF_DATA_IP//./\\.}\|$OLD_SUUID\|$OLD_NUUID" . # residue TRONG
grep -E '^name:|serverUUID:' node-manager/node-manager.yaml
grep -E 'serverName:|^id:|clusterId:' cluster-manager/cluster-manager.yaml
grep -iE 'serverUUID' discovery-service/*.yaml # = SUUID moi
# DON DICH truoc, copy sau
for d in node-manager cluster-manager api-service upgrade-manager discovery-service lbcli lightbox-exporter profile-generator; do rm -rf /etc/$d; done
cp -r /tmp/cfg/* /etc/
ls /etc/node-manager/gftl.db 2>/dev/null && echo "!! CON GFTL.DB" || echo OK
A5 — Start đúng thứ tự
### [NEW04]
systemctl daemon-reload
systemctl enable --now discovery-service; sleep 5; systemctl is-active discovery-service
etcdctl get --prefix /registry/discovery-service --keys-only
# CHECKPOINT: phai co key $SUUID moi (5 key: 01/02/03/05 + 04moi). Khong co -> DUNG.
for s in profile-generator node-manager cluster-manager upgrade-manager api-service; do
systemctl enable --now $s; sleep 3; echo -n "$s: "; systemctl is-active $s
done
journalctl -fu node-manager # format 8 o -> journal -> md -> duroslight -> Active
A6 — Verify
### [ADMIN]
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list servers # 5 server: 01/02/03/04moi/05chet
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list nodes # 04 Active 61TiB; 05 van Unattached
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list nvme-devices | grep $SUUID | awk '{print $NF}' | sort | uniq -c # 6 Data + 2 Journal
### [NEW04]
grep -E "^md" /proc/mdstat; systemctl is-active duroslight-0
PHA B — Thí nghiệm replace node (giáo dục, 0 volume nên vô hại)
Prereq theo docs: source Inactive, target Unattached. Hiện trạng ta có NGƯỢC vai: 05 (nguồn muốn bỏ) = Unattached, 04 (mới) = Active → dự đoán bị từ chối. Chạy để lấy thông điệp lỗi làm tư liệu máy trạng thái:
### [ADMIN]
NODE04=<node uuid moi cua 04, tu list nodes>
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" replace node --src-node-uuid=8861a8fc-b72b-4129-b9e9-1fe77a5a6e82 --target-node-uuid=$NODE04
# Ghi lai nguyen van loi tra ve (ky vong: tu choi vi src khong Inactive / target khong Unattached)
Kết luận ghi sổ: replace node dành cho kịch bản "node NGUỒN vừa Inactive còn dữ liệu cần
chuyển + node ĐÍCH dự phòng đang Unattached chờ sẵn" — không phải công cụ dọn node chết
đã Unattached. Unattached có vai trò kép: ga cuối trước delete VÀ ghế chờ của node dự phòng.
PHA C — Giải thể hoàn toàn oss-nvme-05
C1 — Tắt nguồn phát trước (bài học server hồi sinh)
### [OLD05]
for s in node-manager cluster-manager api-service discovery-service upgrade-manager lightbox-exporter; do
systemctl stop $s 2>/dev/null
done
systemctl is-active node-manager discovery-service # failed/inactive
# etcd TREN 05 TAM GIU — go member sau khi delete server
C2 — Gỡ server khỏi cụm (node đã Unattached → không timer)
### [ADMIN]
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" disable server --uuid 35b83b77-a97d-43a5-8325-39f84ac67605
sleep 10
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" delete server --uuid 35b83b77-a97d-43a5-8325-39f84ac67605
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list servers # 4: 01/02/03/04
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list nvme-devices | grep -c 35b83b77 # 0 — record Failed chet theo server
C3 — Gỡ etcd member của 05
### [REF]
etcdctl member list # delete server co the DA tu go member 05 (kiem truoc)
# Neu member 05 (ID hien tai, vd 35049cb5...) VAN CON:
### [OLD05]
systemctl disable --now etcd
### [REF]
etcdctl member remove <ID-cua-05>
etcdctl member list # 4 member: 01/02/03/04
# Don key registry mo coi cua 05 (discovery da stop, key co the li):
etcdctl get --prefix /registry/discovery-service --keys-only
etcdctl del /registry/discovery-service/35b83b77-a97d-43a5-8325-39f84ac67605 # neu con
C4 — Ép sạch máy 05 (5 nhịp)
### [OLD05]
for s in duroslight-0 node-manager cluster-manager api-service discovery-service upgrade-manager lightbox-exporter profile-generator etcd; do
systemctl disable --now $s 2>/dev/null
done
PKGS=$(rpm -qa | grep -iE "lightos|duros|node-manager|cluster-manager|api-service|discovery|upgrade-manager|profile-generator|lightbox|lbcli|etcd")
[ -n "$PKGS" ] && dnf remove -y $PKGS 2>&1 | tail -2
cd /etc/yum.repos.d && for f in *.repo; do [ -e "$f" ] && mv "$f" "$f.disabled"; done
for m in /dev/md*; do [ -e "$m" ] && mdadm --detail "$m" 2>/dev/null | grep -q raid0 && mdadm --stop "$m"; done
rm -rf /var/lib/etcd /etc/lightos* /etc/node-manager /etc/cluster-manager /etc/api-service \
/etc/discovery-service /etc/upgrade-manager /etc/profile-generator /etc/duroslight \
/etc/lbcli /etc/lightbox-exporter /var/cache/nm_service /usr/lib/systemd/system/duroslight-*.service
for d in 0 1 2 3 4 5 6 7; do wipefs -a /dev/nvme${d}n1 >/dev/null 2>&1; done
# Kiem: 0/8/0/0/0
echo -n "pkg: "; rpm -qa | grep -icE "lightos|duros|etcd"
echo -n "disks: "; lsblk | grep -cE "nvme[0-7]n1"
echo -n "repos: "; ls /etc/yum.repos.d/*.repo 2>/dev/null | wc -l
echo -n "raid0: "; grep -E "^md[0-9]+ :" /proc/mdstat | grep -c raid0
echo -n "leftover: "; ls -d /var/lib/etcd /etc/lightos* /etc/node-manager /etc/cluster-manager /etc/duroslight /var/cache/nm_service /usr/lib/systemd/system/duroslight-*.service 2>/dev/null | wc -l
C5 — Verify cụm cuối cùng
### [ADMIN]
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list servers # 4, NoRisk het
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list nodes # 4 Active 61TiB
lbcli --endpoint $ENDPOINT -J "$LIGHTOS_JWT" list events | head -5
### [REF]
etcdctl member list # 4 started
etcdctl get --prefix /registry/discovery-service --keys-only # 4 key khop 4 server
C6 — Hậu kỳ
- Cập nhật inventory ansible: 04 vào, 05 ra (3 section + host_vars)
- Timer về mặc định nếu có hạ:
update cluster-config --parameter=DurationToTurnIntoPermanentFailure --value=4h - 05 giờ là node dự phòng sạch — sẵn sàng cho vòng diễn tập kế tiếp
Bài học kịch bản này bổ sung cho sổ tay
- Node chết vì journal + record SSD Failed = không có đường mềm; đường production thật: đưa node dự phòng vào (add server thường) + giải thể node chết (delete server) — chính là tổ hợp hai runbook đã thuộc, không cần công cụ mới.
replace nodelà công cụ CHUYỂN DỮ LIỆU src-Inactive → target-Unattached (node dự phòng chờ sẵn) — không phải công cụ dọn node đã Unattached.- Record SSD Failed chết theo server khi delete server — cơ chế "gỡ record" duy nhất.
- etcd member của node chết vẫn started bình thường cho tới khi chủ động gỡ — hai tầng (etcd membership / LightOS server) sống chết độc lập nhau.