Skip to content

Commit 6ba445e

Browse files
authored
Merge pull request #106 from Obmondo/node-disk-fix-guide
chore: Added guide for fixing disks in a kubernetes nodes
2 parents 333cb34 + 6635c5b commit 6ba445e

1 file changed

Lines changed: 194 additions & 0 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Procedure to fix a faulty disk in a kubernetes node
2+
3+
This guide is written while handling a corrupted NVMe disks in one of our managed kubernetes bare-metal node.
4+
5+
ssh into the node and check:
6+
7+
```shell
8+
root@<server-name> ~ # zpool status
9+
pool: primary
10+
state: DEGRADED
11+
status: One or more devices has been removed by the administrator.
12+
Sufficient replicas exist for the pool to continue functioning in a
13+
degraded state.
14+
action: Online the device using zpool online' or replace the device with
15+
'zpool replace'.
16+
scan: scrub repaired 0B in 00:05:33 with 0 errors on Sun Jun 14 00:29:34 2026
17+
config:
18+
19+
NAME STATE READ WRITE CKSUM
20+
primary DEGRADED 0 0 0
21+
mirror-0 DEGRADED 0 0 0
22+
nvme-SAMSUNG_MZVL2512HCJQ-00B00_SERIAL-NUMBER ONLINE 0 0 0
23+
nvme-SAMSUNG_MZVL2512HCJQ-00B00_SERIAL-NUMBER REMOVED 0 0 0
24+
```
25+
26+
Procedure:
27+
28+
1. Cordon and drain the node, check etcd health, and manage rook ceph if it's running on your node
29+
2. Fix/Request cloud provider for disk change
30+
3. After fixes/changes, replace the disk in zpool with newer one or fixed one and unset ceph settings and recheck etcd health
31+
32+
## Phase 1: Before taking the node down
33+
34+
### 1. Check etcd health (all members)
35+
36+
```sh
37+
kubectl -n kube-system get pods -l component=etcd -o wide
38+
39+
kubectl -n kube-system exec etcd-<healthy-node> -- etcdctl \
40+
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
41+
--cert=/etc/kubernetes/pki/etcd/server.crt \
42+
--key=/etc/kubernetes/pki/etcd/server.key \
43+
endpoint health --cluster
44+
```
45+
46+
Please note: With one control plane node down in a 3 control plane node cluster, etcd runs 2/3, quorum holds but
47+
there is zero fault tolerance, so all members must be healthy before you start.
48+
49+
### 2. Check Ceph health and OSD layout (If installed & running on the node)
50+
51+
```sh
52+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph status
53+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph health detail
54+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph osd tree
55+
```
56+
57+
You need HEALTH_OK (or an understood warning), and you need to know which OSDs / mons / MDS live on the node,
58+
pools must be able to tolerate losing that host for some time
59+
60+
### 3. Set the Ceph noout flag (If installed & running on the node)
61+
62+
Ref - <https://docs.ceph.com/en/latest/rados/troubleshooting/troubleshooting-osd/#stopping-without-rebalancing>
63+
64+
```sh
65+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph osd set noout
66+
```
67+
68+
Ceph usually tries to rebalance OSDs if it detects some OSDs are down and since this entire work is intentional,
69+
we should disable rebalancing temporarily
70+
71+
### 4. Confirm rook won't auto-remove the OSDs (If installed & running on the node)
72+
73+
This step is a safety measure, so ceph do not go rogue while the node is down
74+
75+
```sh
76+
kubectl -n rook-ceph get cephcluster -o jsonpath='{.items[0].spec.removeOSDsIfOutAndSafeToRemove}'
77+
# must be false or empty
78+
```
79+
80+
### 5. dry-run drain for verification
81+
82+
```sh
83+
kubectl drain node-name \
84+
--ignore-daemonsets \
85+
--delete-emptydir-data \
86+
--grace-period=120 \
87+
--timeout=10m \
88+
--dry-run=client
89+
```
90+
91+
Please also take care of any ```PodDisruptionBudget``` related blocks.
92+
93+
### 6. Cordon and Drain after verification
94+
95+
```sh
96+
kubectl drain node-name \
97+
--ignore-daemonsets \
98+
--delete-emptydir-data \
99+
--grace-period=120 \
100+
--timeout=10m
101+
```
102+
103+
It evicts all regular workloads so they reschedule elsewhere; DaemonSets and static pods (etcd, apiserver) remain,
104+
which is expected.
105+
106+
### 7. Confirm the planned degraded state
107+
108+
```sh
109+
kubectl get nodes # NODE = NotReady,SchedulingDisabled
110+
# etcd: 2/3 healthy (step 1 command)
111+
# ceph: 2 osds down, noout set, PGs degraded but all active
112+
```
113+
114+
This is the expected state for the whole window, so alerts are expected to come up.
115+
116+
### Rules during the maintenance window
117+
118+
- Do NOT touch the other control plane nodes (etcd has zero fault tolerance).
119+
- Do NOT restart other OSDs or change Ceph config (PGs have no spare copies).
120+
- Silence Ceph/node alerts for the window.
121+
122+
---
123+
124+
## Phase 2: Get the disk changed via cloud provider
125+
126+
### 1. Identify the disks by serial, never by device name
127+
128+
```sh
129+
lsblk -o NAME,MODEL,SERIAL
130+
```
131+
132+
`/dev/nvme0n1` vs `nvme1n1` can swap between boots, serials never lie. The surviving disk has ZFS partitions;
133+
the new one is blank.
134+
135+
### 2. Wipe the new disk to make sure no residue are present
136+
137+
```sh
138+
wipefs -a /dev/<new-disk>
139+
blkdiscard -f /dev/<new-disk>
140+
```
141+
142+
It clears any leftover signatures from burn-in/refurb and TRIMs the SSD to a factory-clean state, double-check
143+
the serial before running.
144+
145+
### 3. Reboot into the normal OS and Replace the dead ZFS mirror member (on the node)
146+
147+
```sh
148+
zpool status <pool>
149+
ls -l /dev/disk/by-id/ | grep <new-serial>
150+
151+
zpool replace <pool> \
152+
<old-disk-by-id-name> \
153+
/dev/disk/by-id/<new-disk-by-id-name>
154+
155+
zpool status -v <pool> # use this command to keep monitoring the progress
156+
```
157+
158+
### 2. Verify etcd is back to 3/3
159+
160+
```sh
161+
# same endpoint health --cluster command as Phase 1 step 1
162+
```
163+
164+
### 3. Uncordon the node
165+
166+
```sh
167+
kubectl uncordon $NODE
168+
```
169+
170+
### 4. Watch Ceph recover (if ceph installed on the node)
171+
172+
```sh
173+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph status
174+
# wait for: 8/8 osds up, all PGs active+clean, 0% degraded
175+
```
176+
177+
### 5. Unset noout, only after all PGs are active+clean (if ceph installed on the node)
178+
179+
Please make sure ```ceph status``` goes back to 0% degradation. it may take 3 to 4 hours sometimes so you need to wait
180+
181+
```sh
182+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph osd unset noout
183+
```
184+
185+
### 6. Final verification
186+
187+
Confirm every layer. nodes, Ceph, ZFS, workloads is fully back before you close the ticket.
188+
189+
```sh
190+
kubectl get nodes # all Ready, none SchedulingDisabled
191+
kubectl -n rook-ceph exec deploy/rook-ceph-tools -- ceph status # HEALTH_OK
192+
zpool status <pool> # ONLINE, 0 errors
193+
kubectl get pods -A | grep -vE 'Running|Completed' # nothing stuck
194+
```

0 commit comments

Comments
 (0)