707 lines
28 KiB
Markdown
707 lines
28 KiB
Markdown
---
|
|
title: recovering a rook-ceph cluster
|
|
author: "0x3bb"
|
|
date: M08-10-2024
|
|
---
|
|
|
|
I share a _rook-ceph_ cluster between a friend of mine approximately 2000km
|
|
away. I had reservations whether this would be a good idea at first because of
|
|
the latency and the fact consumer grade WAN might bring anything
|
|
that breathes to its knees. However, I'm pleased with how robust it has been
|
|
under those circumstances.
|
|
|
|
Getting started with _rook-ceph_ is really simple because it orchestrates
|
|
everything for you. That's convenient, although, if and when you have problems,
|
|
you can suddenly become an operator of a distributed system that you may know
|
|
very little about due to the abstraction.
|
|
|
|
During this incident, I found myself in exactly that position, relying heavily on the
|
|
(great) documentation for both _rook_ and _ceph_ itself.
|
|
|
|
# the beginning
|
|
In the process of moving nodes, **I accidentally zapped 2 OSDs**.
|
|
|
|
Given that the _"ceph cluster"_ is basically two bedrooms peered together, we
|
|
were down to the final OSD.
|
|
|
|
This sounds bad, but it was fine: mons were still up, just a matter of removing
|
|
the old OSDs from the tree and letting replication work its magic.
|
|
|
|
# the real mistake
|
|
I noticed although the block pool was replicated, we had lost all our RADOS
|
|
object storage.
|
|
|
|
![](/b/images/ec-2-1.png)
|
|
|
|
The erasure-coding profile was `k=2, m=1`. That meant we could only lose 2
|
|
OSDs, which had already happened.
|
|
|
|
Object storage (which our applications interfaced with via Ceph S3 Gateway) was
|
|
lost. Nothing critical -- we just needed our CSI volume mounts back online for
|
|
databases -- packages and other artifacts could easily be restored from
|
|
backups.
|
|
|
|
Moving on from this, the first thing I did was _"fix"_ the EC configuration to
|
|
`k=3, m=2`. This would spread the data over 5 OSDs.
|
|
|
|
```
|
|
- dataChunks: 2
|
|
- codingChunks: 1
|
|
+ dataChunks: 3
|
|
+ codingChunks: 2
|
|
```
|
|
|
|
Happy with that, I restored a backup of the objects. Everything was working.
|
|
|
|
A few days later, _renovatebot_ arrived with a new PR to bump _rook-ceph_ `1.14.9`.
|
|
|
|
![](/b/images/rook-ceph-pr.png)
|
|
|
|
Of course I want that -- the number is bigger than the previous number so
|
|
everything is going to be better than before.
|
|
|
|
# downtime
|
|
Following the merge, all services on the cluster went down at once. I checked
|
|
the OSDs which were in `CrashLoopBackOff`. Inspecting the logs, I saw a bunch
|
|
of gibberish and decided to check out the GitHub issues. Since nothing is ever
|
|
my fault, I decided to see who or what was to blame.
|
|
|
|
With no clues, I had still hoped this would be relatively simple to fix.
|
|
|
|
Resigning to actually reading the OSD logs in the `rook-ceph-crashcollector`
|
|
pods, I saw (but did not understand) the problem:
|
|
|
|
`osd/ECUtil.h: 34: FAILED ceph_assert(stripe_width % stripe_size == 0)`
|
|
|
|
![](/b/images/ec-3-2.png)
|
|
|
|
With the _"fixed"_ configuration, what I had actually done is split the object
|
|
store pool across _5_ OSDs. We had _3_.
|
|
|
|
Due to the `rook-ceph-operator` being rescheduled from the version bump, the
|
|
OSD daemons had been reloaded as part of the update procedure and now demanded
|
|
an answer for the data and coding chunks that simply did not exist. Sure enough,
|
|
`ceph -s` also reported undersized placement groups.
|
|
|
|
This makes sense as there weren't enough OSDs to split the data.
|
|
|
|
# from bad to suicide
|
|
|
|
## reverting erasure-coding profile
|
|
The first attempt I made was to revert the EC profile back to `k=2, m=1`. The
|
|
OSDs were still in the same state complaining about the erasure-coding
|
|
profile.
|
|
|
|
## causing even more damage
|
|
The second attempt (and in hindsight, a very poor choice) was to zap the other
|
|
two underlying OSD disks:
|
|
|
|
[ `osd-1`, `osd-2` ].
|
|
|
|
`zap.sh`
|
|
|
|
```
|
|
DISK="/dev/vdb"
|
|
|
|
# Zap the disk to a fresh, usable state (zap-all is important, b/c MBR has to be clean)
|
|
sgdisk --zap-all $DISK
|
|
|
|
# Wipe a large portion of the beginning of the disk to remove more LVM metadata that may be present
|
|
dd if=/dev/zero of="$DISK" bs=1M count=100 oflag=direct,dsync
|
|
|
|
# SSDs may be better cleaned with blkdiscard instead of dd
|
|
blkdiscard $DISK
|
|
|
|
# Inform the OS of partition table changes
|
|
partprobe $DISK
|
|
```
|
|
|
|
Perhaps having two other OSDs online would allow me to replicate the healthy
|
|
pgs without the offending RADOS objects.
|
|
|
|
Sure enough, the 2 new OSDs started.
|
|
|
|
Since the `osd-0` with the actual data still wouldn't start, the cluster was
|
|
still in a broken state.
|
|
|
|
Now down to the last OSD, at this point I knew that I was going to make many,
|
|
many more mistakes. If I was going to continue I needed to backup the logical
|
|
volume used by the `osd-0` node before continuing, which I did.
|
|
|
|
## clutching at mons
|
|
I switched my focus to a new narrative: _something was wrong with the mons_.
|
|
|
|
They were in quorum but I still couldn't figure out why the now last-surviving OSD
|
|
was having issues starting.
|
|
|
|
The `mon_host` configuration was correct in `secret/rook-ceph-config`:
|
|
|
|
`mon_host: '[v2:10.50.1.10:3300,v1:10.50.1.10:6789],[v2:10.50.1.11:3300,v1:10.50.1.11:6789],[v2:10.55.1.10:3300,v1:10.55.1.10:6789]'`
|
|
|
|
Nothing had changed with the underlying data on those mons. Maybe there was
|
|
corruption in the monitor store? The monitors maintain a map of the cluster
|
|
state: the `osdmap`, the `crushmap`, etc.
|
|
|
|
My theory was: if the cluster map did not have the correct placement groups and
|
|
other OSD metadata then perhaps replacing it would help.
|
|
|
|
I replaced the data from another mon to the one used for the failing OSD
|
|
deployment (`store.db`) and scaled up the deployment:
|
|
|
|
`2024-08-04T00:49:47.698+0000 7f12fc78f700 0 mon.s@2(probing) e30 removed
|
|
from monmap, suicide.`
|
|
|
|
With all data potentially lost and it being almost 1AM, that message was not
|
|
very reassuring. I did manually change the monmap and inject it back in, but
|
|
ended up back in the same position.
|
|
|
|
I figured I had done enough experimenting at this point and had to look deeper
|
|
outside of the deployment. The only meaningful change we had made was the
|
|
erasure-coding profile.
|
|
|
|
# initial analysis
|
|
First, I looked back to the OSD logs. They are monstrous, so I focused on the erasure-coding errors:
|
|
|
|
```
|
|
2024-08-03T18:58:49.845+0000 7f2a7d6da700 1 osd.0 pg_epoch: 8916
|
|
pg[12.11s2( v 8915'287 (0'0,8915'287] local-lis/les=8893/8894 n=23 ec=5063/5059
|
|
lis/c=8893/8413 les/c/f=8894/8414/0 sis=8916 pruub=9.256847382s)
|
|
[1,2,NONE]p1(0) r=-1 lpr=8916 pi=[8413,8916)/1 crt=8915'287 mlcod 0'0 unknown
|
|
NOTIFY pruub 20723.630859375s@ mbc={}] state<Start>: transitioning to Stray
|
|
2024-08-03T18:58:49.849+0000 7f2a7ced9700 -1G
|
|
/home/jenkins-build/build/workspace/ceph-build/ARCH/x86_64/AVAILABLE_ARCH/x86_64/AVAILABLE_DIST/centos8/DIST/centos8/MACHINE_SIZE/gigantic/release/18.2.2/rpm/el8/BUILD/ceph-18.2.2/src/osd/ECUtil.h:
|
|
In function 'ECUtil::stripe_info_t::stripe_info_t(uint64_t, uint64_t)' thread
|
|
7f2a7e6dc700 time 2024-08-03T18:58:49.853351+0000
|
|
/home/jenkins-build/build/workspace/ceph-build/ARCH/x86_64/AVAILABLE_ARCH/x86_64/AVAILABLE_DIST/centos8/DIST/centos8/MACHINE_SIZE/gigantic/release/18.2.2/rpm/el8/BUILD/ceph-18.2.2/src/osd/ECUtil.h:
|
|
34: FAILED ceph_assert(stripe_width % stripe_size == 0)
|
|
```
|
|
|
|
```
|
|
-2> 2024-08-03T18:59:00.086+0000 7ffa48b48640 5 osd.2 pg_epoch: 8894 pg[12.9(unlocked)] enter Initial
|
|
...
|
|
/src/osd/ECUtil.h: 34: FAILED ceph_assert(stripe_width % stripe_size == 0)
|
|
|
|
```
|
|
|
|
I noticed a pattern: all the failing pg IDs were prefixed with `12`.
|
|
|
|
Seeing this, I had concluded:
|
|
- mons were ok and in quorum;
|
|
- the `osd-0` daemon fails to start;
|
|
- other fresh OSDs (`osd-1` and `osd-2`) start fine, this was a data
|
|
integrity issue confined to `osd-0` (and the previous OSDs had I not nuked
|
|
them);
|
|
- the cause was a change in the erasure-coding profile, which happened on
|
|
only one pool where the chunk distribution was modified;
|
|
|
|
Accepting the loss of the miniscule data on the object storage pool in favor of
|
|
saving the block storage, I could correct the misconfiguration.
|
|
|
|
# preparation
|
|
To avoid troubleshoting issues caused from my failed attempts, I decided I
|
|
would do a clear out of the existing CRDs and just focus first on getting the
|
|
OSD with the data back online. If I ever got the data back, then I'd probably
|
|
be conscious of prior misconfiguration and have to do so regardless.
|
|
|
|
- backup the important shit;
|
|
- clear out the `rook-ceph` namespace;
|
|
|
|
## backups
|
|
- the logical volume for `osd-0`, so I can re-attach it and afford mistakes;
|
|
- `/var/lib/rook` on all nodes, containing mon data;
|
|
|
|
## removal
|
|
|
|
### deployments/daemonsets
|
|
These were the first to go, as I didn't want the `rook-operator` persistently
|
|
creating Kubernetes objects when I was actively trying to kill them.
|
|
|
|
### crds
|
|
Removal of the all `rook-ceph` resources, and their finalizers to protect them from being removed:
|
|
|
|
- `cephblockpoolradosnamespaces`
|
|
- `cephblockpools`
|
|
- `cephbucketnotifications`
|
|
- `cephclients`
|
|
- `cephclusters`
|
|
- `cephcosidrivers`
|
|
- `cephfilesystemmirrors`
|
|
- `cephfilesystems`
|
|
- `cephfilesystemsubvolumegroups`
|
|
- `cephnfses`
|
|
- `cephobjectrealms`
|
|
- `cephobjectstores`
|
|
- `cephobjectstoreusers`
|
|
- `cephobjectzonegroups`
|
|
- `cephobjectzones`
|
|
- `cephrbdmirrors`
|
|
|
|
### /var/lib/rook
|
|
I had these backed up for later, but I didn't want them there when the cluster came online.
|
|
|
|
### osd disks
|
|
I did not wipe any devices.
|
|
|
|
First, I obviously didn't want to wipe the disk with the data on it. As for the
|
|
other, now useless OSDs that I had mistakenly created over the old ones; I knew
|
|
spawning the `rook-operator` would create new OSDs if they didn't belong to an
|
|
old ceph cluster.
|
|
|
|
This would make troubleshooting `osd-0` more difficult, as I'd now have to consider
|
|
analysing the status reported from `osd-1` and `osd-2`.
|
|
|
|
|
|
## provisioning
|
|
Since at this point I only cared about `osd-0` and it was beneficial to have
|
|
fewer moving parts to work with, I changed the `rook-ceph-cluster` mon count to `1`
|
|
within the helm `values.yaml`.
|
|
|
|
Following this, I simply reconciled the chart.
|
|
|
|
I noticed the `rook-ceph-operator`, `rook-ceph-mon-a`, `rook-ceph-mgr-a` came online as expected.
|
|
|
|
Because the OSDs were part of an old cluster, I now had a ceph-cluster with no
|
|
OSDs, as shown in the `rook-ceph-osd-prepare-*` jobs for each node.
|
|
|
|
```
|
|
osd.0: "cd427c63-b43f-40cb-99a4-7f58af25d624" belonging to a different ceph cluster "47f25963-57c0-4b3b-9b35-bbf68c09eec6"
|
|
osd.1: "cd427c63-b43f-40cb-99a4-7f58af25d624" belonging to a different ceph cluster "47f25963-57c0-4b3b-9b35-bbf68c09eec6"
|
|
osd.2: "cd427c63-b43f-40cb-99a4-7f58af25d624" belonging to a different ceph cluster "47f25963-57c0-4b3b-9b35-bbf68c09eec6"
|
|
```
|
|
|
|
# surgery
|
|
With less noise and a clean slate, it was time to attempt to fix this mess.
|
|
|
|
- adopt `osd-0` to the new cluster;
|
|
- remove the corrupted pgs from `osd-0`;
|
|
- bring up two new OSDs for replication;
|
|
|
|
## osd-0
|
|
I started trying to determine how I would _safely_ remove the offending
|
|
objects. If that happened, then the OSD would have no issues with the
|
|
erasure-coding profile since the pgs wouldn't exist, and the OSD daemon should
|
|
start.
|
|
|
|
- If the placement groups contained only objects created from the
|
|
_RADOS Object Gateway_, then I can simply remove the pgs.
|
|
|
|
- If, however, the pgs contain both the former _and_ block device objects
|
|
then it would require careful removal of all non-rdb (block storage)
|
|
objects as there would be valuable data loss by purging the entire
|
|
placement groups.
|
|
|
|
Since OSD pools have a `1:N` relationship with pgs, the second scenario seemed
|
|
unlikely, perhaps impossible.
|
|
|
|
Next, I needed to inspect the OSD somehow, because the existing deployment would continously crash.
|
|
|
|
`kubectl rook-ceph debug start rook-ceph-osd-0`
|
|
|
|
Running this command allowed me to observe the OSD without it actually joining
|
|
the cluster. The "real" OSD deployment need only be scheduled, but crashing
|
|
continously was ok.
|
|
|
|
Once you execute that command, it will scale the OSD daemon down and create a
|
|
new deployment that mirrors the configuration but _without_ the daemon running
|
|
in order to perform maintenance.
|
|
|
|
Now in a shell of the debug OSD container, I confirmed these belonged to the object storage pool.
|
|
|
|
```
|
|
[root@rook-ceph-osd-0-maintenance-686bbf69cc-5bcmj ceph]# ceph pg ls-by-pool ceph-objectstore.rgw.buckets.data
|
|
PG OBJECTS DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG LOG_DUPS STATE SINCE VERS
|
|
12.0 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.1 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.2 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.3 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.4 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.5 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.6 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.7 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.8 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.9 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.a 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.b 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.c 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.d 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.e 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.f 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.10 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.11 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.12 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.13 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.14 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.15 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.16 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.17 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.18 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.19 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.1a 0 0 0 0 0 0 0 0 unknown 8h
|
|
12.1b 0 0 0 0 0 0 0 0 unknown 8h
|
|
```
|
|
|
|
Seeing this, I first checked to see how many placement groups prefixed with `12` existed using the actual path to the OSD.
|
|
|
|
```
|
|
[root@rook-ceph-osd-0-maintenance-686bbf69cc-5bcmj ceph]# ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-0 --op list-pgs | grep ^12
|
|
12.bs1
|
|
12.6s1
|
|
12.1fs0
|
|
12.1ds1
|
|
12.15s0
|
|
12.16s0
|
|
12.11s0
|
|
12.12s2
|
|
12.0s0
|
|
12.17s2
|
|
12.4s1
|
|
12.9s0
|
|
12.19s0
|
|
12.cs2
|
|
12.13s0
|
|
12.14s2
|
|
12.3s2
|
|
12.1as0
|
|
12.1bs2
|
|
12.as1
|
|
12.1es1
|
|
12.1cs2
|
|
12.2s2
|
|
12.8s1
|
|
12.7s2
|
|
12.ds0
|
|
12.es0
|
|
12.fs0
|
|
12.18s0
|
|
12.1s0
|
|
12.5s1
|
|
12.10s2
|
|
```
|
|
|
|
I still needed to be convinced I wasn't removing any valuable data.
|
|
I inspected a few of them to be sure.
|
|
|
|
```
|
|
[root@rook-ceph-osd-0-maintenance-686bbf69cc-5bcmj ceph]# ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-0 --pgid 12.10s0 --op list
|
|
"12.10s0", {"oid" :"7d92708-bd9b-4d4b-bfc1-d331eb216e68.21763481.3__shadow_packages/07/19/071984080b32e2867 f 1ac6ec2b7d2b8724bc5d75e2850b5e7 f20040ee52F55d1.2~e7rYg3S
|
|
"hash" :1340195137, "max":0, "pool":12, "namespace":"","shard_id":2, "max":0}
|
|
|
|
"12.10s0", {"oid" :"7d92708-bd9b-4d4b-bfc1-d331eb216e68.21763481.3__shadow_packages/9a/82/9a82a64c3a8439c75d8e584181427b073712afd1454747bec3dcb84bcbe19ac5. 2~urbG4nd
|
|
"hash" :4175566657, "max":0, "pool":12, "namespace":"","shard_id":2, "max":0}
|
|
|
|
"12.10s0", {"oid" :"7d927F08-bd9b-4d4b-bfc1-d331eb216e68.22197937.1__shadow Windows Security Internals.pdf.2~g9stQ9inkWvsTq33S9z5xNEHEgST2H4.1_1","key":"", "snapid":-
|
|
"shard id":2,"max":0}]
|
|
...
|
|
|
|
```
|
|
|
|
With this information, I now knew:
|
|
|
|
- the log exceptions matched the pgs that were impacted from the change in
|
|
the erasure-coding configuration;
|
|
- `ceph-objectstore.rgw.buckets.data` had a relationship with those pgs where
|
|
the configuration was changed;
|
|
- the objects were familiar with the objects in the buckets, e.g. books;
|
|
|
|
Since I _did_ modify the erasure-coding profile this is all starting to make sense.
|
|
|
|
Carefully, the next operation was to remove the offending placement groups.
|
|
Simply removing the pool wouldn't work, as the OSD daemon not starting meant it
|
|
would know nothing about this change, and still not have enough chunks to come alive.
|
|
|
|
```
|
|
[root@rook-ceph-osd-0-maintenance-686bbf69cc-5bcmj ceph]# ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-0 --op remove --type bluestore --force --pgid 12.17s2
|
|
marking collection for removal
|
|
setting '_remove' omap key
|
|
finish_remove_pgs 12.17s2_head removing 12.17s2
|
|
Remove successful
|
|
|
|
[root@rook-ceph-osd-0-maintenance-686bbf69cc-5bcmj ceph]# ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-0 --op remove --type bluestore --force --pgid 12.0s0
|
|
marking collection for removal
|
|
setting '_remove' omap key
|
|
finish_remove_pgs 12.0s0_head removing 12.0s0
|
|
Remove successful
|
|
```
|
|
|
|
I did this for every PG listed above. Once I scaled down the maintenance
|
|
deployment, I then scaled back `deployment/rook-ceph-osd-0` to start the daemon
|
|
with (hopefully) agreeable placement groups and thankfully, it had come alive.
|
|
|
|
```
|
|
k get pods -n rook-ceph
|
|
|
|
rook-ceph-osd-0-6f57466c78-bj96p 2/2 Running
|
|
```
|
|
|
|
An eager run of `ceph -s` produced both relief and disappointment. The OSD was up, but the pgs were in an `unknown` state.
|
|
|
|
```
|
|
ceph -s
|
|
...
|
|
data:
|
|
pools: 12 pools, 169 pgs
|
|
objects: 0 objects, 0 B
|
|
usage: 0 B used, 0 B / 0 B avail
|
|
pgs: 100.000% pgs unknown
|
|
169 unknown
|
|
```
|
|
|
|
At this point, I had mentioned to my friend (helping by playing GuildWars 2)
|
|
that we might be saved. It seemed promising as we at least had `osd-0` running
|
|
again now that the troublesome pgs were removed.
|
|
|
|
He agreed, and contemplated changing his character's hair colour instead of
|
|
saving our data.
|
|
|
|
## mons
|
|
|
|
### restoring
|
|
I had `/var/lib/rook` backups from each node with the old mon data. At this
|
|
point, with the correct number of placement groups and seeing 100% of them
|
|
remaining in an `unknown` state, it seemed the next step was to restore the
|
|
mons.
|
|
|
|
I knew from reading the `rook-ceph` docs that if you want to restore the data
|
|
of a monitor to a new cluster, you have to inject the `monmap` into the old
|
|
mons `store.db`.
|
|
|
|
Before doing this, I scaled `deployment/rook-ceph-mon-a` down to `0` first.
|
|
|
|
Then, navigating to a directory on my local machine with the backups I ran a
|
|
container to modify the `monmap` on my local fs.
|
|
|
|
`docker run -it --rm --entrypoint bash -v .:/var/lib/rook rook/ceph:v1.14.9`
|
|
|
|
```
|
|
touch /etc/ceph/ceph.conf
|
|
cd /var/lib/rook
|
|
ceph-mon --extract-monmap monmap --mon-data ./mon-q/data
|
|
monmaptool --rm q
|
|
monmaptool --rm s
|
|
monmaptool --rm t
|
|
```
|
|
|
|
Now the old mons `q`, `s` and `t` were removed from the map, I had to add the
|
|
new cluster mon `rook-ceph-mon-a` created following the new
|
|
ceph-cluster.
|
|
|
|
```
|
|
monmaptool --addv a '[v2:10.50.1.10:3300,v1:10.50.1.10:6789]`
|
|
ceph-mon --inject-monmap monmap --mon-data ./mon-q/data
|
|
exit
|
|
```
|
|
|
|
Shoving it back up to the node `rook-ceph-mon-a` lives on:
|
|
|
|
`scp -r ./mon-q/data/* 3bb@10.50.1.10:/var/lib/rook/mon-a/data/`
|
|
|
|
Rescheduling the deployment and although the mon log output isn't giving me
|
|
suggestions of suicide, all our pgs still remain in an `unknown` state.
|
|
|
|
## recovering the mon store
|
|
It turns out that you can actually recover the mon store. It's not a huge deal
|
|
so long as your OSDs have data integrity.
|
|
|
|
Scaling the useless `mon-a` down, I copied the existing `mon-a` data onto the
|
|
`rook-ceph-osd-0` daemon container.
|
|
|
|
Another `osd-0` debug container... `k rook-ceph debug start rook-ceph-osd-0`
|
|
|
|
I rebuilt the mon data, using the existing RocksDB kv store.
|
|
|
|
This would have worked without the backup, but I was interested to see the
|
|
`osdmaps` trimmed due to the other 2 removed OSDs.
|
|
|
|
```
|
|
[root@he-prod-k3s-controlplane-ch-a-1 ceph]# ceph-objectstore-tool --type bluestore --data-path /var/lib/ceph/osd/ceph-0/ --op update-mon-db --mon-store-path /tmp/mon-a/data/
|
|
osd.0 : 3099 osdmaps trimmed, 635 osdmaps added.
|
|
```
|
|
|
|
```
|
|
[root@he-prod-k3s-controlplane-ch-a-1 ceph]# ceph-authtool /tmp/mon-a/keyring -n mon. --cap mon 'allow *' --gen-key
|
|
|
|
[root@he-prod-k3s-controlplane-ch-a-1 ceph]# ceph-monstore-tool /tmp/mon-a/data rebuild -- --keyring /tmp/mon-a/keyring
|
|
4 rocksdb: [db/flush_job.cc:967] [default] [JOB 3] Level-0 flush table #3433: 62997231 bytes OK
|
|
4 rocksdb: EVENT_LOG_v1 {"time_micros": 1722831731454649, "job": 3, "event": "flush_finished", "output_compression": "NoCompression", "lsm_state": [2, 0, 0, 0, 0, 0, 2], "immutable_memtables": 1}
|
|
4 rocksdb: [file/delete_scheduler.cc:74] Deleted file /tmp/mon-a/data/store.db/003433.sst immediately, rate_bytes_per_sec 0, total_trash_size 0 max_trash_db_ratio 0.250000
|
|
4 rocksdb: EVENT_LOG_v1 {"time_micros": 1723067397472153, "job": 4, "event": "table_file_deletion", "file_number": 3433}
|
|
4 rocksdb: [db/db_impl/db_impl.cc:704] Shutdown complete
|
|
```
|
|
|
|
After copying the now _rebuilt_ `mon-a` store back, and bringing everything up
|
|
again, the cluster was finally resurrecting.
|
|
|
|
It took some time for the rebalancing and replication to occur, but hours
|
|
later, `ceph -s` reported a healthy cluster and services resumed being entirely
|
|
unaware of the chaos that had ensued over the previous few days:
|
|
|
|
```
|
|
cluster:
|
|
id: 47f25963-57c0-4b3b-9b35-bbf68c09eec6
|
|
health: HEALTH_OK
|
|
|
|
services:
|
|
mon: 3 daemons, quorum a,b,c (age 3h)
|
|
mgr: b(active, since 8h), standbys: a
|
|
mds: 1/1 daemons up, 1 hot standby
|
|
osd: 3 osds: 3 up (since 3h), 3 in (since 3h)
|
|
rgw: 2 daemons active (2 hosts, 1 zones)
|
|
|
|
data:
|
|
volumes: 1/1 healthy
|
|
pools: 12 pools, 169 pgs
|
|
objects: 4.88k objects, 16 GiB
|
|
usage: 47 GiB used, 1.1 TiB / 1.2 TiB avail
|
|
pgs: 169 active+clean
|
|
|
|
io:
|
|
client: 639 B/s rd, 9.0 KiB/s wr, 1 op/s rd, 1 op/s wr
|
|
recovery: 834 KiB/s, 6 objects/s
|
|
```
|
|
|
|
It seemed like a miracle, but it is entirely credited to how resilient ceph is
|
|
built to tolerate that level of abuse.
|
|
|
|
# why
|
|
_Data appears to be lost_
|
|
|
|
- ceph OSD daemons fail to start;
|
|
- the OSDs could not reconstruct the data from chunks;
|
|
- the `osdmap` referenced a faulty erasure-coding profile;
|
|
- the monstore `osdmap` still had reference to the above erasure-coding profile;
|
|
- the erasure-coding profile was changed to a topology impossible to satisfy under the current architecture;
|
|
- 2 disks were zapped, hitting the ceiling of the failure domain for `ceph-objectstore.rgw.buckets.data_ecprofile`;
|
|
|
|
|
|
The monitor `osdmap` still contained the bad EC profile.
|
|
|
|
`ceph-monstore-tool /tmp/mon-bak get osdmap > osdmap.bad`
|
|
|
|
osdmaptool --dump json osdmap.bad | grep -i profile
|
|
|
|
```
|
|
"erasure_code_profiles":{
|
|
"ceph-objectstore.rgw.buckets.data_ecprofile":{
|
|
"crush-device-class":"",
|
|
"crush-failure-domain":"host",
|
|
"crush-root":"default",
|
|
"jerasure-per-chunk-alignment":"false",
|
|
"k":"3",
|
|
"m":"2"
|
|
}
|
|
}
|
|
```
|
|
|
|
After rebuilding the monstore...
|
|
|
|
`ceph-monstore-tool /tmp/mon-a get osdmap > osdmap.good`
|
|
|
|
```
|
|
"erasure_code_profiles":{
|
|
"ceph-objectstore.rgw.buckets.data_ecprofile":{
|
|
"crush-device-class":"",
|
|
"crush-failure-domain":"host",
|
|
"crush-root":"default",
|
|
"jerasure-per-chunk-alignment":"false",
|
|
"k":"3",
|
|
"m":"1"
|
|
}
|
|
}
|
|
```
|
|
|
|
Therefore, it seems as if I could have attempted to rebuild the monstore first,
|
|
possibly circumventing the `ECAssert` errors. The placement groups on `osd-0` were
|
|
still mapping to 3 OSDs, not 5.
|
|
|
|
```
|
|
[root@ad9e4c6e7343 rook]# osdmaptool --test-map-pgs-dump --pool 12 osdmap
|
|
osdmaptool: osdmap file 'osdmap'
|
|
pool 12 pg_num 32
|
|
12.0 [2147483647,2147483647,2147483647] -1
|
|
12.1 [2147483647,2147483647,2147483647] -1
|
|
12.2 [2147483647,2147483647,2147483647] -1
|
|
12.3 [2147483647,2147483647,2147483647] -1
|
|
12.4 [2147483647,2147483647,2147483647] -1
|
|
12.5 [2147483647,2147483647,2147483647] -1
|
|
12.6 [2147483647,2147483647,2147483647] -1
|
|
12.7 [2147483647,2147483647,2147483647] -1
|
|
12.8 [2147483647,2147483647,2147483647] -1
|
|
12.9 [2147483647,2147483647,2147483647] -1
|
|
12.a [2147483647,2147483647,2147483647] -1
|
|
12.b [2147483647,2147483647,2147483647] -1
|
|
12.c [2147483647,2147483647,2147483647] -1
|
|
12.d [2147483647,2147483647,2147483647] -1
|
|
12.e [2147483647,2147483647,2147483647] -1
|
|
12.f [2147483647,2147483647,2147483647] -1
|
|
12.10 [2147483647,2147483647,2147483647] -1
|
|
12.11 [2147483647,2147483647,2147483647] -1
|
|
12.12 [2147483647,2147483647,2147483647] -1
|
|
12.13 [2147483647,2147483647,2147483647] -1
|
|
12.14 [2147483647,2147483647,2147483647] -1
|
|
12.15 [2147483647,2147483647,2147483647] -1
|
|
12.16 [2147483647,2147483647,2147483647] -1
|
|
12.17 [2147483647,2147483647,2147483647] -1
|
|
12.18 [2147483647,2147483647,2147483647] -1
|
|
12.19 [2147483647,2147483647,2147483647] -1
|
|
12.1a [2147483647,2147483647,2147483647] -1
|
|
12.1b [2147483647,2147483647,2147483647] -1
|
|
12.1c [2147483647,2147483647,2147483647] -1
|
|
12.1d [2147483647,2147483647,2147483647] -1
|
|
12.1e [2147483647,2147483647,2147483647] -1
|
|
12.1f [2147483647,2147483647,2147483647] -1
|
|
#osd count first primary c wt wt
|
|
osd.0 0 0 0 0.488297 1
|
|
osd.1 0 0 0 0.488297 1
|
|
osd.2 0 0 0 0.195297 1
|
|
in 3
|
|
avg 0 stddev 0 (-nanx) (expected 0 -nanx))
|
|
size 3 32
|
|
```
|
|
|
|
Since the cluster did not have enough OSDs (wanted 5 with `k=3,m=2`), the rule
|
|
can be tested against the old crush map, with `--num-rep` representing the
|
|
required OSDs, i.e. `k+m`:
|
|
|
|
With the original erasure-coding profile (`k+m=3`), everything looks good -- no
|
|
bad mappings.
|
|
|
|
```
|
|
[root@ad9e4c6e7343 rook]# crushtool -i crush --test --num-rep 3 --show-bad-mappings
|
|
|
|
// healthy
|
|
```
|
|
|
|
With `k+m=5`, though -- or anything great than `3` OSDs...
|
|
|
|
```
|
|
[root@ad9e4c6e7343 rook]# crushtool -i crush --test --num-rep 5 --show-bad-mappings
|
|
...
|
|
bad mapping rule 20 x 1002 num_rep 5 result [0,2147483647,1,2,2147483647]
|
|
bad mapping rule 20 x 1003 num_rep 5 result [0,2147483647,2,1,2147483647]
|
|
bad mapping rule 20 x 1004 num_rep 5 result [1,0,2147483647,2,2147483647]
|
|
bad mapping rule 20 x 1005 num_rep 5 result [0,1,2147483647,2,2147483647]
|
|
bad mapping rule 20 x 1006 num_rep 5 result [0,1,2147483647,2,2147483647]
|
|
bad mapping rule 20 x 1007 num_rep 5 result [0,1,2147483647,2147483647,2]
|
|
bad mapping rule 20 x 1008 num_rep 5 result [1,2,0,2147483647,2147483647]
|
|
bad mapping rule 20 x 1009 num_rep 5 result [2,1,0,2147483647,2147483647]
|
|
bad mapping rule 20 x 1010 num_rep 5 result [0,1,2,2147483647,2147483647]
|
|
bad mapping rule 20 x 1011 num_rep 5 result [0,2147483647,2,1,2147483647]
|
|
bad mapping rule 20 x 1012 num_rep 5 result [0,1,2147483647,2,2147483647]
|
|
bad mapping rule 20 x 1013 num_rep 5 result [0,2,2147483647,1,2147483647]
|
|
bad mapping rule 20 x 1014 num_rep 5 result [1,0,2147483647,2,2147483647]
|
|
bad mapping rule 20 x 1015 num_rep 5 result [2,0,2147483647,1,2147483647]
|
|
bad mapping rule 20 x 1016 num_rep 5 result [1,0,2,2147483647,2147483647]
|
|
bad mapping rule 20 x 1017 num_rep 5 result [2,0,1,2147483647,2147483647]
|
|
bad mapping rule 20 x 1018 num_rep 5 result [1,0,2147483647,2147483647,2]
|
|
bad mapping rule 20 x 1019 num_rep 5 result [0,1,2,2147483647,2147483647]
|
|
bad mapping rule 20 x 1020 num_rep 5 result [0,2147483647,1,2147483647,2]
|
|
bad mapping rule 20 x 1021 num_rep 5 result [2,1,0,2147483647,2147483647]
|
|
bad mapping rule 20 x 1022 num_rep 5 result [1,0,2,2147483647,2147483647]
|
|
bad mapping rule 20 x 1023 num_rep 5 result [0,2,1,2147483647,2147483647]
|
|
```
|
|
|
|
Mappings were found on _3_ OSDs, but missing the 4th and 5th reference as
|
|
indicated by the largest 32-bit int (i.e. missing). The object storage data
|
|
would still have been lost, but it could have made the recovery of the cluster
|
|
significantly less painful.
|