- New: smart-home/home-assistant-dashboard-conventions (Mushroom cards, view tabs, no Bubble Cards) - Updated: rke2, ceph, galera, proxmox, brainstorming, compound-learning, 1password-cli, smart-home-automation skills - New references: ceph-cluster-administration, docker-volume-forensics, ceph-crush-weight, ceph-ec-mixed-size
547 lines
22 KiB
Markdown
547 lines
22 KiB
Markdown
---
|
||
name: ceph-cluster-administration
|
||
description: "Class-level skill for Ceph cluster administration on Proxmox VE. Covers OSD lifecycle (create/destroy/WAL-DB), CRUSH topology (device classes, roots, rules, weights), EC pools (profiles, mixed disk sizes, migration), recovery & rebalancing (tuning, ratios, monitoring), RBD management (orphan cleanup, migration, inspection), pool management (PG count, full recovery, autoscale), and health diagnosis."
|
||
version: 1.0.0
|
||
author: Hermes Agent
|
||
tags: [ceph, proxmox, bluestore, crush, erasure-coding, rbd, recovery, osd, storage]
|
||
metadata:
|
||
hermes:
|
||
tags: [ceph, proxmox, bluestore, crush, erasure-coding, rbd, recovery, osd, storage]
|
||
related_skills: [proxmox-ve-administration, rke2-cluster-administration]
|
||
---
|
||
|
||
# Ceph Cluster Administration
|
||
|
||
## Scope
|
||
|
||
**This skill covers the Homelab (self-hosted) Ceph cluster.** Not noris infrastructure. At noris only remote PBS + S3 Storage exist — neither involves Ceph. Do not confuse the two environments.
|
||
|
||
## Environment
|
||
|
||
| Component | Detail |
|
||
|-----------|--------|
|
||
| Environment | **Homelab (self-hosted)** — NOT noris |
|
||
| PVE Master | 10.0.20.10 (SSH: `root@10.0.20.10`, key `~/.ssh/id_ed25519_proxmox`) |
|
||
| PVE Nodes (SSH) | All PVE hosts reachable via `10.0.20.x` (storage VLAN), NOT `10.0.30.x` (management VLAN — SSH key rejected). px1=.10, px2=.20, px3=.30, px4=.40, px5=.50, px6=.60, px7(n5pro)=.70 or .91 |
|
||
| OSDs | 11 total (6 HDD + 5 SSD) |
|
||
| HDD mix | 4× ~3 TB (osd.1, 6, 8, 11) + 2× ~1 TB (osd.7, 10) |
|
||
| EC Profile | `ec41-hdd` (k=4, m=1) → migrating to `ec31-hdd-large` (k=3, m=1) |
|
||
| Pools | `media_ec` (EC), `hdd_disk` (Repl 3), `rbd` (Repl 3), `tm_disks` (Repl 2), `media_meta` (Repl 3), `vm_disks` (Repl 3) |
|
||
| Ratios | nearfull=0.94, backfillfull=0.96, full=0.97 (failsafe hardcoded 0.97) |
|
||
| Monitoring | Grafana CT141 (DS UID=PBFA97CFB590B2093), Prometheus, Alertmanager→Telegram |
|
||
| Ceph Mon Network | `10.0.20.x` (storage VLAN) — NOT `10.0.30.x` (management) |
|
||
|
||
All Ceph commands run from PVE master (**10.0.20.10** — storage network) unless noted. Non-Ceph nodes (e.g. n5pro) need `ceph.conf` + admin keyring copied and `rbd`/`ceph` commands run from a monitor node.
|
||
|
||
### SSH Access: 10.0.20.x Works, 10.0.30.x Does NOT
|
||
|
||
PVE hosts have **two networks**: storage (10.0.20.x) and management (10.0.30.x). SSH with `~/.ssh/id_ed25519_proxmox` works on **10.0.20.x** but is rejected on **10.0.30.x** (key not in authorized_keys). Always use 10.0.20.x IPs for PVE host SSH:
|
||
|
||
```bash
|
||
# CORRECT — storage network
|
||
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.10 "ceph -s"
|
||
|
||
# WRONG — management network (key rejected)
|
||
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.10 "ceph -s" # Permission denied
|
||
```
|
||
|
||
PVE host IP mapping (storage network): px1=.10, px2=.20, px3=.30, px4=.40, px5=.50, px6=.60, px7=.70, n5pro=.91.
|
||
|
||
### Fallback: Ceph CLI via K8s ceph-csi Pods
|
||
|
||
When PVE SSH is unavailable, `ceph` commands can be run from inside K8s `ceph-csi-rbd-nodeplugin` pods (the `csi-rbdplugin` container has `/usr/bin/ceph`). Requires a ceph.conf with monitor addresses and the `csi-rbd-secret` credentials:
|
||
|
||
```bash
|
||
# On mgmt-runner (10.0.30.124):
|
||
POD=$(kubectl get pods -n kube-system --no-headers | grep ceph-csi-rbd-nodeplugin | head -1 | awk '{print $1}')
|
||
|
||
# Get cephx key from K8s secret
|
||
USER_KEY=$(kubectl get secret csi-rbd-secret -n kube-system -o jsonpath='{.data.userKey}' | base64 -d)
|
||
|
||
# Write ceph.conf into the pod and query
|
||
kubectl exec -n kube-system $POD -c csi-rbdplugin -- bash -c "
|
||
cat > /tmp/ceph.conf << 'EOF'
|
||
[global]
|
||
auth_cluster_required = cephx
|
||
auth_service_required = cephx
|
||
auth_client_required = cephx
|
||
mon host = 10.0.20.50:6789,10.0.20.70:6789,10.0.20.40:6789
|
||
[client.kubernetes]
|
||
key = $USER_KEY
|
||
EOF
|
||
ceph -c /tmp/ceph.conf --name client.kubernetes status
|
||
"
|
||
```
|
||
|
||
Monitor addresses are in the `ceph-csi-config` ConfigMap: `kubectl get cm ceph-csi-config -n kube-system -o jsonpath='{.data.config\.json}'`.
|
||
|
||
**Pitfall:** Not all monitors are always reachable. Check connectivity first (`bash -c "echo > /dev/tcp/IP/6789"` from a Galera VM or K8s node). Only use reachable monitors in the `mon host` list.
|
||
|
||
**Dual-network pitfall:** PVE hosts have SSH on `10.0.20.x` (storage
|
||
network, port 22 open) but NOT on `10.0.30.x` (management network,
|
||
SSH key rejected). Always use `10.0.20.10` for `ssh root@10.0.20.10`.
|
||
The `10.0.30.x` IPs are for VM bridges, not PVE host management.
|
||
|
||
**Fallback when PVE SSH is unavailable:** Run `ceph` commands from
|
||
inside a `ceph-csi-rbd-nodeplugin` pod in K8s (see Monitoring section
|
||
below for the full procedure).
|
||
|
||
⚠️ **Pitfall: PVE hosts are on 10.0.20.x (storage net), NOT 10.0.30.x (management net).** The 10.0.30.x addresses are for VM bridges, not PVE host SSH. Always use `root@10.0.20.10` for Ceph commands. If SSH to 10.0.30.x PVE hosts fails with "Permission denied", you're on the wrong network — check this skill first, not the SSH key.
|
||
|
||
### Fallback: Ceph CLI via K8s ceph-csi Pods
|
||
|
||
When PVE host SSH is unavailable (key rotation, host down, network change), `ceph` CLI is available inside the `csi-rbdplugin` container of `ceph-csi-rbd-nodeplugin` pods in K8s. Steps:
|
||
|
||
1. Find monitor addresses from the `ceph-csi-config` ConfigMap:
|
||
```bash
|
||
kubectl get cm ceph-csi-config -n kube-system -o jsonpath='{.data.config\.json}'
|
||
# Parse JSON for "monitors" array (e.g. ["10.0.20.10:6789","10.0.20.20:6789",...])
|
||
```
|
||
2. Get cephx credentials from the `csi-rbd-secret`:
|
||
```bash
|
||
kubectl get secret csi-rbd-secret -n kube-system -o jsonpath='{.data.userKey}' | base64 -d
|
||
```
|
||
3. Write a temporary ceph.conf into the pod and query:
|
||
```bash
|
||
POD=$(kubectl get pods -n kube-system --no-headers | grep ceph-csi-rbd-nodeplugin | head -1 | awk '{print $1}')
|
||
# Create /tmp/ceph.conf with [global] mon host = <reachable monitors> and [client.kubernetes] key = <userKey>
|
||
# Then: kubectl exec -n kube-system $POD -c csi-rbdplugin -- ceph -c /tmp/ceph.conf --name client.kubernetes status
|
||
```
|
||
|
||
⚠️ Not all monitors may be reachable from K8s nodes — test connectivity first. Only use monitors that respond on port 6789.
|
||
|
||
## Decision Trees
|
||
|
||
### OSD Creation
|
||
|
||
```
|
||
Is target a standard PVE node?
|
||
├─ YES → pveceph osd create /dev/sdX [--wal_dev /dev/nvme0n1pN]
|
||
│ ⚠️ pveceph CANNOT use LVM device-mapper paths (/dev/dm-*) as WAL/DB
|
||
│ If WAL/DB is on an LV: use ceph-volume directly (see below)
|
||
└─ NO (non-PVE node like n5pro)
|
||
├─ Has /etc/ceph/ceph.conf symlink to /etc/pve/ceph.conf?
|
||
│ ├─ NO → rm /etc/ceph/ceph.conf && ln -s /etc/pve/ceph.conf /etc/ceph/ceph.conf
|
||
│ └─ YES → proceed
|
||
├─ WAL/DB on raw partition? → pveceph osd create /dev/sdX --wal_dev /dev/nvme0n1pN
|
||
└─ WAL/DB on LVM LV? → ceph-volume lvm prepare --bluestore --data /dev/sdX --block.db /dev/pve/wal-db-osd-X
|
||
(WAL auto-collocates on DB — do NOT specify --block.wal separately)
|
||
```
|
||
|
||
### Recovery / Rebalance
|
||
|
||
```
|
||
ceph -s shows degraded/misplaced/backfill?
|
||
├─ Any OSD > full_ratio (0.97)?
|
||
│ └─ EMERGENCY: ceph osd set-full-ratio 0.99 → reweight overfull OSD → fix root cause
|
||
├─ backfill_toofull PGs > 0?
|
||
│ └─ Target OSDs above backfillfull_ratio → raise ratio (keep ordering!) or reweight
|
||
├─ Recovery speed too slow?
|
||
│ └─ Tune: osd_max_backfills, osd_recovery_op_priority, osd_recovery_sleep
|
||
│ ⚠️ ceph config set osd X does NOT propagate to running OSDs
|
||
│ Use: ceph tell osd.* injectargs "--osd_max_backfills 3" + verify with ceph daemon osd.N config get
|
||
├─ Recovery stuck at 0 MiB/s with remapped PGs?
|
||
│ └─ Check for sentinel 2147483647 in up-sets → EC with OSD count == k+m → reset all weights to 1.0
|
||
└─ upmap balancer not helping?
|
||
└─ Balancer skips when >5% misplaced → wait for recovery, then balancer resumes
|
||
```
|
||
|
||
### EC Pool with Mixed Disk Sizes
|
||
|
||
```
|
||
EC pool has OSDs of different capacities?
|
||
├─ Small OSDs filling disproportionately?
|
||
│ └─ ROOT CAUSE: EC stores equal-sized chunks regardless of OSD capacity
|
||
│ ceph osd reweight has marginal effect when k+m ≈ total OSD count
|
||
│ SOLUTION:
|
||
│ 1. Define device classes: hdd-large, hdd-small
|
||
│ 2. Create separate CRUSH root (hdd-root) for all HDDs
|
||
│ 3. EC rule: take hdd-root~hdd-large (only large OSDs)
|
||
│ 4. Replicated rule: take hdd-root (all HDDs, no class filter)
|
||
│ 5. New EC profile with k adjusted to large-OSD count
|
||
│ 6. Migrate RBD images to new pool (online via rbd migration)
|
||
└─ Artificially low CRUSH weights on small OSDs?
|
||
└─ ANTI-PATTERN: causes vicious cycle → reset to true device size after separating classes
|
||
```
|
||
|
||
## Core Operations
|
||
|
||
### 1. OSD Lifecycle
|
||
|
||
#### Create OSD with WAL/DB on NVMe (LVM-based)
|
||
|
||
```bash
|
||
# 1. Create WAL/DB LV on NVMe
|
||
lvcreate -L 30G -n wal-db-osd-X pve
|
||
|
||
# 2. Prepare OSD — ONLY --block.db, WAL auto-collocates
|
||
ceph-volume lvm prepare --bluestore \
|
||
--data /dev/sdX \
|
||
--block.db /dev/pve/wal-db-osd-X
|
||
|
||
# 3. Activate (use osd_id + fsid from prepare output)
|
||
ceph-volume lvm activate <osd_id> <osd_uuid>
|
||
|
||
# 4. Start service
|
||
systemctl enable --now ceph-osd@<osd_id>
|
||
|
||
# 5. Set CRUSH host + device class
|
||
ceph osd crush set osd.<id> <weight> root=default host=<hostname>
|
||
ceph osd crush set-device-class hdd osd.<id>
|
||
```
|
||
|
||
⚠️ **NEVER** specify `--block.wal` AND `--block.db` on the same LV — BlueStore opens the device exclusively → "Device or resource busy".
|
||
|
||
#### Destroy and Rebuild OSD
|
||
|
||
```bash
|
||
systemctl stop ceph-osd@<id>
|
||
ceph osd down <id>
|
||
ceph osd destroy <id> --yes-i-really-mean-it
|
||
ceph-volume lvm zap /dev/sdX --destroy
|
||
# Then recreate as above
|
||
```
|
||
|
||
#### Hot-Plug SATA Drive Detection
|
||
|
||
```bash
|
||
# JMB58x controller doesn't auto-scan
|
||
for h in /sys/class/scsi_host/host{0,1,2,3,4}; do
|
||
echo "- - -" > ${h}/scan
|
||
done
|
||
sleep 2
|
||
lsblk -d -o NAME,MODEL,SERIAL,SIZE,TRAN,ROTA,STATE | grep -v rbd | grep -v loop
|
||
```
|
||
|
||
#### Expand BlueFS DB (Online LV Extend + Offline BlueStore Resize)
|
||
|
||
When BlueFS spillover occurs (RocksDB grows beyond DB LV onto HDD), expand the DB LV:
|
||
|
||
```bash
|
||
# 1. Extend the LV (online, no OSD stop needed for LVM)
|
||
lvextend -L +20G pve/ceph-db
|
||
|
||
# 2. Stop the OSD — bluefs-bdev-expand CANNOT run while OSD is active
|
||
# Error if attempted online: "Device or resource busy" + assert crash
|
||
systemctl stop ceph-osd@<id>
|
||
|
||
# 3. Expand BlueFS awareness of the new DB size
|
||
ceph-bluestore-tool bluefs-bdev-expand --path /var/lib/ceph/osd/ceph-<id>
|
||
# Output: "1 : Expanding to 0x... (... GiB)" + "size updated"
|
||
|
||
# 4. Restart OSD
|
||
systemctl start ceph-osd@<id>
|
||
|
||
# 5. Verify BlueFS sees the new size
|
||
ceph daemon osd.<id> bluefs stats | head -3
|
||
# Should show: "1 : device size 0x...(XX GiB)"
|
||
|
||
# 6. Verify OSD is back up in cluster
|
||
ceph osd tree | grep "osd.<id>"
|
||
```
|
||
|
||
⚠️ **`bluefs-bdev-expand` MUST be run with OSD stopped.** Running it online crashes with `FAILED ceph_assert(r == 0)` — the block device is locked by the running OSD. The `ceph daemon osd.N` admin socket does NOT have an expand command (unlike config get/set). The only path is: stop OSD → bluestore-tool → start OSD.
|
||
|
||
⚠️ **Spillover warning clears asynchronously.** After expansion, `BLUEFS_SPILLOVER` health warning persists until BlueFS compacts RocksDB back onto the expanded DB device. This happens in the background over minutes to hours.
|
||
|
||
#### When NOT to Expand (Cosmetic Spillover)
|
||
|
||
**Spillover < 1 MiB with >50% DB free → cosmetic, do NOT expand.** Tiny spillovers (e.g. 128 KiB) are RocksDB metadata overflow that self-resolves on next compaction. Stopping an OSD for `bluefs-bdev-expand` triggers PG degradation and recovery I/O — worse than the warning itself.
|
||
|
||
**Expand only when:** spillover > 1 GiB OR DB device >80% full. This indicates sustained RocksDB growth that won't self-resolve.
|
||
|
||
### 2. CRUSH Topology
|
||
|
||
#### Device Class Separation
|
||
|
||
```bash
|
||
# Assign device classes
|
||
ceph osd crush set-device-class hdd-large osd.1 osd.6 osd.8 osd.11
|
||
ceph osd crush set-device-class hdd-small osd.7 osd.10
|
||
|
||
# Create separate root
|
||
ceph osd crush add-bucket hdd-root root
|
||
|
||
# Move HDD hosts to new root (⚠️ TRIGGERS REMAPPING!)
|
||
ceph osd crush move <hostname> root=hdd-root
|
||
```
|
||
|
||
#### CRUSH Rules
|
||
|
||
```bash
|
||
# EC rule — only large HDDs
|
||
ceph osd erasure-code-profile set ec31-hdd-large \
|
||
k=3 m=1 crush-root=hdd-root crush-device-class=hdd-large \
|
||
crush-failure-domain=osd plugin=jerasure technique=reed_sol_van
|
||
ceph osd crush rule create-erasure media_ec_large ec31-hdd-large
|
||
|
||
# Replicated rule — ALL HDDs (no class filter)
|
||
ceph osd crush rule create-replicated replicated_hdd_all hdd-root host
|
||
```
|
||
|
||
#### Weight Management
|
||
|
||
| Command | Scope | Persistence | Use Case |
|
||
|---------|-------|-------------|----------|
|
||
| `ceph osd reweight N X` | Runtime reweight | Lost on restart | Emergency drain of overfull OSD |
|
||
| `ceph osd crush reweight osd.N X` | CRUSH weight | Permanent | Match weight to device size |
|
||
|
||
**Correct CRUSH weights by device size:**
|
||
|
||
| Device | Weight |
|
||
|--------|--------|
|
||
| 1 TB | ~0.96 |
|
||
| 2 TB | ~1.82 |
|
||
| 3 TB | ~2.73 |
|
||
| 3.6 TiB | ~3.64 |
|
||
|
||
⚠️ **Anti-pattern**: Lowering CRUSH weight to "protect" a full OSD. This prevents new PGs but doesn't move existing data → vicious cycle.
|
||
|
||
### 3. EC Pool Migration
|
||
|
||
EC pools cannot change k/m in-place. Must create new pool and migrate.
|
||
|
||
```bash
|
||
# 1. Create new EC pool
|
||
ceph osd pool create media_ec_new 128 128 erasure ec31-hdd-large
|
||
ceph osd pool set media_ec_new ec_overwrites
|
||
ceph osd pool application enable media_ec_new rbd
|
||
|
||
# 2. Create new metadata pool
|
||
ceph osd pool create media_meta_new 32
|
||
|
||
# 3. Online RBD migration (VMs keep running!)
|
||
for img in $(rbd ls media_meta); do
|
||
rbd migration prepare media_meta/${img} media_meta_new/${img} --data-pool media_ec_new
|
||
rbd migration execute media_meta_new/${img}
|
||
rbd migration commit media_meta_new/${img}
|
||
done
|
||
|
||
# 4. Cleanup old pool
|
||
ceph osd pool delete media_ec --yes-i-really-really-mean-it
|
||
ceph osd pool rename media_ec_new media_ec
|
||
```
|
||
|
||
Timeline: `prepare` (instant) → `execute` (30-60 min per 500 GiB) → `commit` (atomic switch).
|
||
|
||
### 4. Recovery Tuning
|
||
|
||
#### Full Recovery Recipe
|
||
|
||
```bash
|
||
# 1. Unblock backfill (keep ratio ordering: nearfull < backfillfull < full ≤ 0.97)
|
||
ceph osd set-nearfull-ratio 0.94
|
||
ceph osd set-backfillfull-ratio 0.96
|
||
ceph osd set-full-ratio 0.97
|
||
|
||
# 2. Reweight overfull OSD (emergency, temporary)
|
||
ceph osd reweight <osd_id> 0.8
|
||
|
||
# 3. Set recovery params (persistent for future OSDs)
|
||
ceph config set osd osd_max_backfills 3
|
||
ceph config set osd osd_recovery_op_priority 10
|
||
ceph config set osd osd_recovery_sleep 0
|
||
|
||
# 4. Push to running OSDs (config set alone does NOT propagate!)
|
||
ceph tell osd.* injectargs "--osd_max_backfills 3"
|
||
|
||
# 5. Verify EACH OSD picked up the config
|
||
ceph daemon osd.<N> config get osd_max_backfills
|
||
# Must return "3" — if "1", config not applied
|
||
```
|
||
|
||
#### Post-Recovery Cleanup
|
||
|
||
```bash
|
||
# Restore defaults
|
||
ceph config set osd osd_max_backfills 1
|
||
ceph tell osd.* injectargs "--osd_max_backfills 1"
|
||
ceph osd reweight <osd_id> 1.0
|
||
# Ratios: keep at 0.94/0.96/0.97 (safe for mixed-size cluster)
|
||
```
|
||
|
||
### 5. RBD Orphan Cleanup
|
||
|
||
```bash
|
||
# 1. Get cluster-wide VM inventory (NOT per-node qm config!)
|
||
pvesh get /cluster/resources --type vm --output-format json | python3 -c "
|
||
import sys, json
|
||
for g in json.load(sys.stdin): print(g['vmid'])
|
||
" | sort -n | tr '\n' ' '
|
||
|
||
# 2. Fast orphan check for specific image (O(1))
|
||
grep -rl "vm-NNN" /etc/pve/qemu-server/ /etc/pve/lxc/ 2>/dev/null
|
||
# Exit 0 = referenced. Exit 1 = orphan.
|
||
|
||
# 3. Delete with protected snapshot
|
||
rbd snap unprotect "hdd_disk/base-NNN-disk-0"@__base__
|
||
rbd snap purge "hdd_disk/base-NNN-disk-0"
|
||
rbd rm "hdd_disk/base-NNN-disk-0"
|
||
```
|
||
|
||
⚠️ **Always use `pvesh get /cluster/resources --type vm`** for VM existence — per-node `qm config` only sees local VMs → false positives.
|
||
|
||
⚠️ **Non-Ceph nodes cannot run `rbd` commands** — run from a monitor node (e.g. proxmox7 = 10.0.20.70).
|
||
|
||
### 6. PG Management
|
||
|
||
```bash
|
||
# Reduce PG count (must disable autoscale first!)
|
||
ceph osd pool set <pool> pg_autoscale_mode off
|
||
ceph osd pool set <pool> pgp_num 32
|
||
ceph osd pool set <pool> pg_num 32
|
||
# Merge happens AFTER all PGs reach active+clean
|
||
|
||
# Raise mon_max_pg_per_osd (temporary relief)
|
||
ceph config set mon mon_max_pg_per_osd 400
|
||
ceph tell mon.* injectargs --mon_max_pg_per_osd=400
|
||
```
|
||
|
||
## Pitfalls
|
||
|
||
### Ratio Ordering (HEALTH_ERR)
|
||
|
||
`nearfull < backfillfull < full ≤ osd_failsafe_full_ratio (0.97 hardcoded)`. Violation → `HEALTH_ERR: OSD_OUT_OF_ORDER_FULL`. Always set all three ratios together.
|
||
|
||
### injectargs Reliability
|
||
|
||
`ceph tell osd.* injectargs` returns empty `{}` which looks like failure but may have succeeded. **Always verify** with `ceph daemon osd.N config get <param>`. Most reliable: `ceph config set` (persistent) + `injectargs` (runtime) + verify.
|
||
|
||
### EC Reweight with Minimal OSDs
|
||
|
||
**Never reweight OSDs in EC pools when OSD count == k+m.** CRUSH places sentinel `2147483647` ("no OSD available") in up-sets → remapped PGs can never recover. Fix: reset all weights to 1.0. General rule: only reweight when OSD count > k+m.
|
||
|
||
### pveceph LVM WAL/DB Limitation
|
||
|
||
`pveceph osd create --wal_dev /dev/pve/wal-db-osd-X` fails with "unable to get device info for '/dev/dm-2'". pveceph cannot resolve LVM device-mapper symlinks. Use `ceph-volume lvm prepare` directly with `--block.db`.
|
||
|
||
### BlueStore WAL/DB Same Device
|
||
|
||
Specifying `--block.wal` AND `--block.db` on the same LV → "Device or resource busy". BlueStore opens WAL exclusively. Solution: specify ONLY `--block.db` — WAL collocates automatically.
|
||
|
||
### ceph config set Doesn't Propagate to Running OSDs
|
||
|
||
`ceph config set osd osd_max_backfills 3` updates MON config store but running OSDs keep old value. Must use `ceph tell osd.* injectargs` for runtime change, or restart OSDs. Always verify with `ceph daemon osd.N config get`.
|
||
|
||
### backfillfull Is Derived, Not Settable
|
||
|
||
Pool-level `backfillfull` flag is derived from OSD conditions, not a settable pool flag. `ceph osd pool unset <pool> backfillfull` → EINVAL. Fix: raise `backfillfull_ratio` or reweight overfull OSDs.
|
||
|
||
### Ghost RBD Images
|
||
|
||
Images appearing in `rbd ls` but `rbd info` returns empty → header gone, data objects remain. Need `rados rm` purge, not `rbd rm`.
|
||
|
||
### pvesm list Fails When Pool Backfillfull
|
||
|
||
`pvesm list hdd_disk` returns empty when Ceph pool is in backfillfull state. Fall back to `rbd ls <pool>` for image listing.
|
||
|
||
## Monitoring
|
||
|
||
```bash
|
||
# Quick health
|
||
ceph -s
|
||
|
||
# Per-OSD utilization
|
||
ceph osd df
|
||
|
||
# PG state summary
|
||
ceph pg dump pgs_brief | awk '{print $2}' | sort | uniq -c | sort -rn
|
||
|
||
# Recovery progress
|
||
ceph -s | grep -E "recovery:|misplaced"
|
||
|
||
# Balancer status
|
||
ceph balancer status
|
||
|
||
# Slow ops
|
||
ceph health detail | grep -A5 "slow"
|
||
```
|
||
|
||
### Ceph Status via ceph-csi Pod (Fallback When PVE SSH Unavailable)
|
||
|
||
When PVE hosts are unreachable via SSH (key rotation, network issue,
|
||
host down), but K8s nodes can reach the Ceph monitors, run `ceph`
|
||
commands from inside a `ceph-csi-rbd-nodeplugin` pod:
|
||
|
||
```bash
|
||
# 1. Find the ceph-csi pod (on mgmt-runner or any K8s node with kubectl)
|
||
POD=$(kubectl get pods -n kube-system --no-headers | \
|
||
grep ceph-csi-rbd-nodeplugin | head -1 | awk '{print $1}')
|
||
|
||
# 2. Get monitor addresses from the ceph-csi-config ConfigMap
|
||
MONS=$(kubectl get cm ceph-csi-config -n kube-system \
|
||
-o jsonpath='{.data.config\.json}' | \
|
||
python3 -c "import sys,json; print(','.join(json.load(sys.stdin)[0]['monitors']))")
|
||
|
||
# 3. Get the cephx key from the csi-rbd-secret
|
||
KEY=$(kubectl get secret csi-rbd-secret -n kube-system \
|
||
-o jsonpath='{.data.userKey}' | base64 -d)
|
||
|
||
# 4. Write ceph.conf + keyring into the pod, then run ceph commands
|
||
kubectl exec -n kube-system $POD -c csi-rbdplugin -- bash -c "
|
||
cat > /tmp/ceph.conf << EOF
|
||
[global]
|
||
auth_cluster_required = cephx
|
||
auth_service_required = cephx
|
||
auth_client_required = cephx
|
||
mon host = $MONS
|
||
[client.kubernetes]
|
||
key = $KEY
|
||
"
|
||
ceph -c /tmp/ceph.conf --name client.kubernetes status
|
||
ceph -c /tmp/ceph.conf --name client.kubernetes osd df
|
||
"
|
||
```
|
||
|
||
**Key details:**
|
||
- The `csi-rbd-plugin` container has `/usr/bin/ceph` installed
|
||
- Monitor addresses are in the `ceph-csi-config` ConfigMap (may be on
|
||
a different subnet like `10.0.20.x` — the storage VLAN)
|
||
- The cephx key is in the `csi-rbd-secret` Secret (field `userKey`)
|
||
- Not all monitors need to be up — 3 of 7 was sufficient in practice
|
||
- `client.kubernetes` has limited permissions (reads status, OSD df,
|
||
pool df) but NOT admin operations (no `ceph osd set-full-ratio` etc.)
|
||
|
||
**Pitfall:** Monitor addresses in `ceph-csi-config` may be on a
|
||
different subnet (e.g. `10.0.20.x` storage VLAN) than the management
|
||
network (`10.0.30.x`). K8s pods can usually reach both, but Galera VMs
|
||
may only see `10.0.30.x`.
|
||
|
||
**Pitfall:** The `ceph -c /tmp/ceph.conf` approach requires writing
|
||
the config file into the pod. If `kubectl cp` is blocked, use
|
||
`kubectl exec -- bash -c "cat > /tmp/ceph.conf << EOF ..."` via a
|
||
heredoc through SSH to the mgmt-runner.
|
||
|
||
## Recovery Monitoring Cron Pattern
|
||
|
||
```
|
||
Schedule: every 30 minutes
|
||
Report: PGs remapped/backfilling, % misplaced, OSD utilization, recovery speed
|
||
Alert: any OSD down or health ERR
|
||
Announce "COMPLETE" when 0 remapped PGs
|
||
```
|
||
|
||
## Cross-References
|
||
|
||
Detailed session logs and edge cases in `proxmox-ve-administration` skill references:
|
||
- `references/ceph-osd-create-hotplug-2026-07.md` — Full OSD hot-plug walkthrough
|
||
- `references/ceph-ec-mixed-size-optimization-2026-07.md` — Complete EC migration plan
|
||
- `references/ceph-recovery-acceleration-2026-07.md` — Advanced recovery tuning
|
||
- `references/ceph-crush-weight-optimization-2026-07.md` — Weight alignment for mixed sizes
|
||
- `references/ceph-pg-management-2026-07.md` — PG count reduction + EC reweight pitfall
|
||
- `references/ceph-pool-full-recovery-2026-07.md` — Pool-full diagnosis + OSD preparation
|
||
- `references/ceph-orphaned-rbd-cleanup-2026-07.md` — Full orphan audit methodology
|
||
- `references/ceph-recovery-ec-reweight-2026-07.md` — EC reweight stagnation fix
|
||
- `references/ceph-pg-osd-management-2026-07.md` — PG/OSD rebalancing
|
||
- `references/ceph-ec-pool-and-service-migration-2026-07.md` — EC pool + service migration
|
||
- `references/ceph-pg-reduction-and-osd-rebalance-2026-07.md` — PG reduction + rebalance
|
||
- `references/rbd-pool-migration-2026-07.md` — Offline RBD pool migration
|
||
- `references/hdd-selection-for-osd-2026-07.md` — Drive selection criteria
|
||
- `references/bluefs-db-expansion-2026-07.md` — BlueFS DB LV expansion + cosmetic spillover assessment
|
||
|
||
Compound-learning solution docs in `~/docs/solutions/`:
|
||
- `bug-fixes/2026-07-13-pveceph-wal-lvm-device-mapper-limitation.md`
|
||
- `bug-fixes/2026-07-13-ceph-ratio-ordering-constraint.md`
|
||
- `architecture/2026-07-13-ec-mixed-disk-sizes-optimization.md`
|
||
- `tooling/2026-07-13-injectargs-vs-config-set-reliability.md`
|