Ceph OSD 削除 — safe-to-destroy が通らないときに何を実測するか

Ceph OSD 削除では、ceph osd safe-to-destroy の OK を待つ手順が紹介される。だが pool が size=3 で OSD を1基外す構成では、この前提は原理的に満たされない。判断の根拠を PG(Placement Group)の acting セット実測へ移し、purge まで到達できた。

本記事では、その判定の構造と、実際に何を数えたのかを残す。

目次

Ceph の OSD 削除とは何か — out と purge は別の操作である

Ceph の OSD(Object Storage Daemon)は、データを実際に保持するプロセスである。ノードを更改するときは、旧ノードの OSD をクラスタから外す。外し方には段階がある。

ceph osd out は「この OSD には新しいデータを置くな」という指示である。既存のデータはそのまま残るが、新規の書き込み先からは外れる。続いてデータの再配置が行われ、最後に ceph osd purge で定義そのものを消す。

purge が消すのは、OSD の定義、CRUSH map 上の位置、認証キー、osdmap 上のエントリである。Ceph の公式ドキュメントでも、OSD の追加と削除は ceph osd out → データ移動の完了確認 → ceph osd purge という順序で説明されている(出典: Ceph Documentation「Adding/Removing OSDs」、2026年10月時点)。

問題は「データ移動の完了確認」を何で判定するかにある。ここが曖昧なまま運用されると、いつまでも待つか、逆に根拠なく purge するかのどちらかになる。

safe-to-destroy が通らない理由 — 判定条件の構造

ceph osd safe-to-destroy <id> は「この OSD を壊しても安全か」を判定するコマンドである。これが OK を返すまで待つ運用は広く紹介されている。

しかし、このコマンドは「すべての PG が active+clean であること」を前提に判定する。pool の size が 3 で、OSD を1基 out にした状態では、PG は active+undersized や active+undersized+degraded になる。冗長度が設計値に戻っていない以上、全 PG が active+clean になることはない。つまり新しい OSD が参加して3基に戻るまで、safe-to-destroy は原理的に OK を返さない。

ここで「では新 OSD の参加まで待つ」と考えると、ノード更改の順序が詰む。旧ノードを止める前に新ノードを用意できるならよいが、同じ名前・同じ IP で入れ替える方式では、旧ノードを先に外す必要がある。物理的な制約が、コマンドの前提条件と噛み合わない。

コマンドが OK を返すのは、あくまで「冗長度が設計値に戻った状態」である。逆に言えば、冗長度が戻る前でも「失うデータが無い」ことは別の手段で確認できる。両者を混同しないことが出発点になる。

size と min_size の関係 — 何コピーまで耐えられるか

pool の size は保持するコピー数、min_size は書き込みを継続できる最小コピー数である。size=3、min_size=2 の場合、2コピーあれば読み書きは継続できるが、1コピーになると I/O が止まる。OSD を1基外した状態は「2コピーで運用中」であり、停止耐性は1基分しかない。

この前提があるからこそ、「この状態をいつまで許容するか」を先に決める意味が出てくる。作業手順より先に、時間の設計を決めるべき理由である。

ceph -s の Degraded と misplaced はデータ欠損ではない

OSD を out にした直後、ceph -s は Degraded data redundancy を表示し続ける。私の環境では objects misplaced 273/819 (33.333%) も表示された。33.333% という数字を見ると、3分の1のデータが壊れたように見える。

だが misplaced は「データが失われた量」ではなく「あるべき位置と現在の位置がずれているオブジェクトの量」である。pool が size=3 のまま OSD が1基 out で残っているため、Ceph は「3つ目のコピーをどこかに置きたい」と考え続ける。しかし置き場所が無いので、ずれたまま止まる。objects 819 のうち 273 がずれているというのは、3コピーのうち1コピー分が予定地に無い、という意味にすぎない。データの実体は残った2コピーで保護されている。

したがって、概要表示の悪化は「危険の証拠」ではなく「設計値に戻せていない状態の表示」である。ここを取り違えると、待つべきか進むべきかの判断を誤る。

何を実測するか — PG の up と acting を機械的に数える

判断材料として必要なのは「その OSD が実際にデータを持っているか」である。Ceph では PG の配置が up セットと acting セットで表現される。acting は実際にデータを保持している OSD の集合であり、ここに当該 OSD が含まれていなければ、その OSD を消しても失うデータは無い。

ceph pg dump の出力を JSON で取得し、PG ごとに up と acting を機械的に数える。手で目視するのではなく、必ず集計する。41 PG 程度なら目視でもできそうに見えるが、見落としが判断の誤りに直結する領域なので機械的に数えるべきである。

# 全 PG の up / acting を JSON で取得する
ceph pg dump -f json > /var/tmp/pg_dump.json

# acting に指定 OSD を含む PG の件数を数える
jq '[.pg_map.pg_stats[] | select((.acting // []) | index(1))] | length' /var/tmp/pg_dump.json

# up に含む PG の件数
jq '[.pg_map.pg_stats[] | select((.up // []) | index(1))] | length' /var/tmp/pg_dump.json

index(1) の 1 は OSD の ID である。外そうとしている OSD の ID に置き換える。jq のバージョンによっては index() の引数型が異なるため、配列を一度文字列へ寄せてから比較すると安全である。

なぜ acting で判定するのか — up と acting の違い

ceph pg dump には up と acting という2つの配置情報が現れる。混同しやすいので整理しておく。

up は CRUSH が計算した「本来この PG を置くべき OSD の集合」である。acting は「実際に I/O を処理している OSD の集合」である。平常時は両者が一致するが、OSD が停止しているときや再配置の途中ではずれる。

データの実体があるのは acting 側である。したがって「この OSD を消しても失うデータが無いか」を判断するには、acting に含まれないことを確認するのが正しい。up だけを見て「まだ含まれている」と判断すると、再配置が終わった後も待ち続けることになる。

逆に、up から消えていても acting に残っている場合は、まだその OSD が I/O を処理している。この状態で purge すると、稼働中の OSD の定義を消すことになり危険である。両方を数え、acting が 0 件であることを確認する。

実測結果の読み方 — 41 PG の内訳

私の環境での実測は次のとおりだった。対象は全 41 PG。

total_pgs = 41
up に osd.1 を含む PG:     0 件
acting に osd.1 を含む PG: 0 件
up セット分布: [2, 0]=30, [0, 2]=11
states: active+undersized+degraded 40 / active+undersized 1

up セットの分布は [2, 0] が 30 PG、[0, 2] が 11 PG である。数値は OSD の ID であり、osd.0 と osd.2 の2コピーだけで全 PG が構成されていることを意味する。osd.1 は up にも acting にも現れない。

ここが判断の分岐点である。 ceph -s の概要だけを見ると 33.333% degraded と表示されて不安になるが、PG 単位で数えると osd.1 は既にデータを持っていない。概要表示と実配置は別物であり、判断には実配置の実測が要る。

なお states が active+undersized+degraded であることは、冗長度が設計値より低いという意味であって、データを失ったという意味ではない。この区別が、待つべきか進むべきかの判断を分ける。

判断の順序 — out → 収束確認 → PG 実測 → purge

実際に実施した順序は次のとおりである。作業前に /etc/pve 全体、CRUSH map、osd dump、認証一覧、pvecm status をまとめて退避した。撤回手段を先に確保してから触る、というのが順序の前提にある。

  1. ceph osd out <id> を実行する
  2. 再配置が動き始めるのを待ち、ceph -s で misplaced の推移を確認する
  3. misplaced が動かなくなったら、PG 単位の up/acting を厳密に実測する
  4. acting に当該 OSD が含まれないことを確認したうえで、そのノードの Ceph デーモン(ceph-osd@<id> / ceph-mgr / ceph-crash)を停止する
  5. ceph osd purge <id> を実行する
  6. ceph auth list に当該 OSD が 0 件であること、osd tree から消えていることを確認する

purge 後の ceph -s は 2 osds: 2 up, 2 in となり、OSD count 2 < osd_pool_default_size 3 の警告が残る。これは想定どおりで、新しい OSD が参加するまで解消しない。警告が消えないことを「失敗」と読み替えないことが重要である。

purge しても残るもの — MDS・MGR・CRUSH・認証

OSD を purge しても、そのノードに紐づく定義は他にも残る。実際に確認したのは4種類である。

  • MDS … ceph-mds@<host> を停止し、pveceph mds destroy <host> で認証と ceph.conf の [mds.<host>] を削除する。CephFS の active が別ノードにあれば無停止で進められる
  • MGR … ceph auth del mgr.<host>
  • CRUSH バケット … crush tree から当該 host が消えていることを確認する。既に存在しない場合は ceph osd crush remove が「does not appear」を返す(rc=0 なので異常ではない)
  • 認証 … ceph auth list に当該ノード関連が 0 件であることを確認する

このうち見落としやすいのが MDS である。OSD を外しただけで満足すると、standby MDS の定義が残り、後で同じ名前のノードを参加させたときに衝突する可能性がある。OSD だけを見ていると、この残骸に気づかない。

冗長度が落ちる時間をどう限定するか

OSD が 2 基、pool が size=3、min_size=2。この状態では全 PG が undersized であり、データは2コピーで読めるが余裕はゼロである。この状態で残る2基のうち1基が落ちれば、I/O は止まる。

これはリスクである。だから「この状態をいつまで許容するか」を先に決めてから作業に入る。私の場合は1晩と決め、次の3点を同時に固定した。

  • Proxmox のレプリケーションは全ジョブを停止する(次回同期を走らせない)。定義は消さず、ノード再参加後に戻せる状態にしておく
  • 新ノードの到着を前提に、旧ノードの停止・新規構築・再参加の日程を固定する
  • 新 OSD の参加をもって3基へ戻すことを完了条件にする

冗長度が落ちた状態を「しばらく様子を見る」で延ばさないことが重要である。延ばすほど、2基目の障害で失うものが大きくなる。作業そのものより、この時間の設計のほうが効いてくる。なお件数や次回同期時刻のような値は、事前資料と実測がずれることがある。手順書に書かれた数字ではなく、その時点の実測を正とする。

まとめ — 「失うデータが無い」ことを実測してから外す

今回の作業から得た判断基準は次の3点である。

  • safe-to-destroy は size=3 のまま1基外す構成では原理的に通らない。通るまで待つ設計は成立しない
  • 判断根拠は PG の acting セットに当該 OSD が含まれないこと に置く。ceph -s の概要表示では判定できない
  • OSD 以外の残骸(MDS・MGR・CRUSH・認証)と、レプリケーションの停止を同時に管理する

Ceph のような分散ストレージでは、概要表示が「悪くなっているように見える」場面が多い。そこで止まるのではなく、内訳を実測して「失うものが無い」ことを確認する。この順序を守る限り、OSD の入れ替えは怖くない。小規模クラスタほど、この実測の習慣が効いてくる。

ストレージ構成そのものの考え方については、HCI を組む際の設計論も参考になる(関連: 脱・高コストSAN!ProxmoxとInfiniBandで実現する、次世代HCIという選択肢)。バックアップ側の設計は vzdump と sanoid の分離で整理した(関連: Proxmox の vzdump が監視を誤爆させていた — ZFS スナップショットと sanoid でバックアップを分ける)。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

金重総合研究所の主席研究員。
子供の頃から研究者を目指し、ライフワークとして日々様々な研究をしています。
経営・マネジメント・金融・DXあたりが本職です。
私を採用したい人、私と一緒に働きたい人、一緒に知識を肥やしていきたい人はぜひお声がけ下さい。

目次