Logo

Personal Ops Runbook

Personal runbook covering infrastructure operations for Cloud, Kubernetes, OpenStack, and Ceph environments. Includes deployment and teardown procedures, node management, cluster monitoring setup, and incident response workflows compiled from day-to-day operational work. Intended strictly for personal reference — configurations and scripts are environment-specific and not guaranteed to work as-is elsewhere.

1. Backfill = degraded backfill + misplaced backfill — ĐÚNG

Backfill (cơ chế quét toàn bộ PG rồi copy) có hai "vị" tùy trạng thái an toàn dữ liệu: - Degraded backfill — dựng lại mảnh đã mất lên target trống (undersized+degraded+backfilling). Có rủi ro dữ liệu → ưu tiên cao. - Misplaced backfill — dời mảnh còn đủ sang đúng chỗ (remapped+backfilling). Không rủi ro → ưu tiên thấp.

Đúng như bạn thấy trong ceph -s: hai dòng tách ra chính là hai vị này.

2. Recovery vs Backfill — ranh giới KHÔNG phải "loại thao tác", mà là "LOG có phủ được không"

Đây là chỗ cần sửa cách nghĩ:

  • Recovery (log-based): OSD quay lại trước khi PG log bị trim qua khoảng thiếu → Ceph nhìn log biết chính xác vài object nào lỡ → chỉ replay đúng những object đó. Nhỏ, nhanh, trúng đích.
  • Backfill (scan-based): target trống (OSD mới), hoặc OSD down quá lâu đến mức log không phủ nổi nữa → Ceph không biết cái gì thiếu → buộc quét cả PG rồi so. To, chậm.

Nên cùng một sự kiện "OSD down rồi up" có thể ra recovery (về nhanh) hoặc backfill (về trễ, log đã trim). Trên cluster ghi nặng như bạn (~5k write op/s) log trim nhanh → cửa sổ "còn recovery được" khá hẹp.

3. Recovery ít gặp hơn backfill

Recovery không hiếm — nó xảy ra mỗi lần OSD restart / host reboot / blip mạng ngắn (rất thường trong vận hành). Chỉ là nó nhỏ + nhanh nên bạn không để ý, khác backfill to-chậm-thấy-rõ. Và recovery thường là degraded recovery (khôi phục bản sao thiếu sau khi OSD vắng mặt một lúc).

Bảng tra thao tác → cơ chế → vị

Thao tác Recovery / Backfill Degraded / Misplaced
OSD/host restart ngắn (log còn phủ) Recovery Degraded (tạm)
OSD down lâu / log đã trim Backfill Degraded
Stop OSD → để quá mon_osd_down_out_interval (~10') → bị mark out Backfill Degraded
Remove/purge OSD hay node Backfill Degraded
Thêm OSD/node Backfill Misplaced
out/drain nhẹ nhàng (OSD vẫn up, còn data) Backfill Misplaced
Reweight / đổi CRUSH / PG autoscale Backfill Misplaced

Lưu ý 2 điểm: - Stop OSD không lập tức backfill — có ân hạn mon_osd_down_out_interval (~600s). Trong cửa sổ đó PG chỉ degraded chờ; về kịp → recovery, quá hạn → mark out → backfill. - Remove/purge thì out ngay, không ân hạn → backfill luôn.

Khớp với cái bạn đã thấy

Sự kiện của bạn là gỡ OSD (osd.301...) + rebalance → target trống / log không phủ → toàn bộ là backfill: degraded backfill (dựng lại mảnh mất) + misplaced backfill (cân bằng). Đó chính là lý do mọi PG trong ceph -s của bạn đều ở trạng thái backfilling, không có recovering. Nếu hôm nào bạn thấy state recovering xuất hiện → đó là một OSD vừa bounce ngắn rồi về kịp, log-based recovery đang chạy.

Misplaced backfill (EC) = copy 1:1 ✓

Mảnh vẫn còn nguyên, chỉ sai chỗ → OSD đang giữ mảnh đó đọc chunk rồi ghi thẳng sang OSD đích. Không decode, không đụng mảnh nào khác.

1 chunk (S/6) đọc    1 chunk (S/6) ghi    đối xứng 1:1

Degraded backfill (EC) = đọc 6 + tái tạo ✓

Mảnh mất (NONE) → đọc k=6 mảnh sống → decode → ghi 1 mảnh dựng lại.

6 chunk đọc (= S, rải trên 6 )    decode    1 chunk (S/6) ghi    6:1

Vài điểm tinh

  1. "6 ổ" = 6 trong 7 ổ còn sống. EC 6+2 mất 1 mảnh thì còn 7 mảnh, decode chỉ cần bất kỳ k=6 trong số đó (có thể gồm cả mảnh parity). Không cần đọc cả 7.

  2. Tái tạo KHÔNG ở target. Decode chạy ở primary (CPU/RAM); target chỉ hứng và ghi mảnh kết quả. Nên target nhẹ + hay chờ, còn 6 ổ source mới là chỗ đọc nặng — đúng phân tích trước.

  3. Một PG có thể vừa misplaced vừa degraded. Nếu acting set có cả mảnh NONE lẫn vài mảnh sai chỗ (bạn từng thấy PG có cả cột DEGRADED và MISPLACED khác 0) → trong cùng PG đó: mảnh mất thì tái tạo (đọc 6), mảnh sai chỗ thì copy 1:1. Hai hành vi song song theo từng vị trí shard.

  4. Pool replica thì luôn 1:1. Index/metadata SSD của bạn là replica → dù degraded hay misplaced cũng chỉ copy nguyên bản sao (đọc 1 replica đủ → ghi 1 replica), không có phép EC nào. Chuyện "đọc 6 tái tạo" chỉ xảy ra ở pool EC.

  cluster:
    id:     c85b0190-27aa-11ef-97a7-7f90cac4804a
    health: HEALTH_OK

  services:
    mon: 5 daemons, quorum VSTOR-CEPH-MBF9-MON-05,VSTOR-CEPH-MBF9-MDS-09,VSTOR-CEPH-MBF9-MDS-08,VSTOR-CEPH-MBF9-MON-07,VSTOR-CEPH-MBF9-MON-06 (age 3d)
    mgr: VSTOR-CEPH-MBF9-MON-05.dptncn(active, since 11d), standbys: VSTOR-CEPH-MBF9-MON-07.blpwtk, VSTOR-CEPH-MBF9-MON-06.oleywv
    mds: 2/2 daemons up, 2 hot standby
    osd: 965 osds: 965 up (since 23h), 965 in (since 23h); 2761 remapped pgs
    rgw: 27 daemons active (9 hosts, 1 zones)

  data:
    volumes: 1/1 healthy
    pools:   16 pools, 14497 pgs
    objects: 2.47G objects, 3.5 PiB
    usage:   4.8 PiB used, 3.3 PiB / 8.1 PiB avail
    pgs:     794010447/18302310391 objects misplaced (4.338%)
             10962 active+clean
             2653  active+remapped+backfill_wait
             594   active+clean+scrubbing
             180   active+clean+scrubbing+deep
             108   active+remapped+backfilling

  io:
    client:   139 MiB/s rd, 143 MiB/s wr, 35.92k op/s rd, 7.97k op/s wr
    recovery: 2.8 GiB/s, 999 objects/s

  progress:
    Global Recovery Event (24h)
      [======================......] (remaining: 5h)
root@VSTOR-CEPH-MBF9-MON-05:/# for id in $(ceph osd ls-tree mbf9-02); do
  v=$(ceph tell osd.$id dump_recovery_reservations 2>/dev/null | jq -c \
    '{max:.remote_reservations.max_allowed, run:(.remote_reservations.in_progress|length), wait:([.remote_reservations.queues[].items|length]|add // 0)}')
  echo "osd.$id $v"9-MON-05:/# for id in $(ceph osd ls-tree mbf9-02); do_pr  v=$(ceph tell osd.$id dump_recovery_reservations 2>/dev/null | jq -c \/ 0    '{max:.remote_reservations.max_allowed, run:(.remote_reservations.in_progress|length), wait:([.remote_reservations.queues[].items|length]|add // done
osd.244 {"max":3,"run":3,"wait":49}
osd.286 {"max":3,"run":2,"wait":0}
osd.301 {"max":3,"run":3,"wait":43}
osd.329 {"max":3,"run":3,"wait":43}
osd.404 {"max":3,"run":2,"wait":0}
osd.405 {"max":3,"run":3,"wait":3}
osd.406 {"max":3,"run":2,"wait":0}
osd.407 {"max":3,"run":1,"wait":0}
osd.408 {"max":3,"run":3,"wait":5}
osd.409 {"max":3,"run":3,"wait":2}
osd.410 {"max":3,"run":1,"wait":0}
osd.411 {"max":3,"run":3,"wait":1}
osd.412 {"max":3,"run":2,"wait":0}
osd.413 {"max":3,"run":2,"wait":0}
osd.414 {"max":3,"run":3,"wait":0}
osd.415 {"max":3,"run":3,"wait":4}
osd.416 {"max":3,"run":3,"wait":2}
osd.417 {"max":3,"run":2,"wait":0}
osd.418 {"max":3,"run":3,"wait":0}
osd.419 {"max":3,"run":2,"wait":0}
osd.420 {"max":3,"run":2,"wait":0}
osd.421 {"max":3,"run":3,"wait":3}
osd.422 {"max":3,"run":3,"wait":0}
osd.423 {"max":3,"run":3,"wait":1}
osd.424 {"max":3,"run":3,"wait":0}
osd.425 {"max":3,"run":3,"wait":4}
osd.426 {"max":3,"run":1,"wait":0}
osd.427 {"max":3,"run":3,"wait":1}
osd.428 {"max":3,"run":3,"wait":1}
osd.429 {"max":3,"run":1,"wait":0}
osd.430 {"max":3,"run":0,"wait":0}
osd.431 {"max":3,"run":3,"wait":0}
osd.432 {"max":3,"run":3,"wait":0}
osd.433 {"max":3,"run":3,"wait":0}
osd.434 {"max":3,"run":2,"wait":0}
osd.435 {"max":3,"run":2,"wait":0}
osd.436 {"max":3,"run":3,"wait":1}
osd.437 {"max":3,"run":0,"wait":0}
osd.438 {"max":3,"run":3,"wait":1}
osd.439 {"max":3,"run":3,"wait":1}
osd.440 {"max":3,"run":2,"wait":0}
osd.441 {"max":3,"run":3,"wait":0}
osd.442 {"max":3,"run":3,"wait":2}
osd.443 {"max":3,"run":2,"wait":0}
osd.444 {"max":3,"run":3,"wait":0}
osd.445 {"max":3,"run":3,"wait":1}
osd.446 {"max":3,"run":3,"wait":2}
osd.447 {"max":3,"run":1,"wait":0}
osd.448 {"max":3,"run":3,"wait":2}
osd.449 {"max":3,"run":2,"wait":0}
osd.450 {"max":3,"run":2,"wait":0}
osd.451 {"max":3,"run":1,"wait":0}
osd.452 {"max":3,"run":3,"wait":2}
osd.453 {"max":3,"run":3,"wait":0}
osd.454 {"max":3,"run":3,"wait":0}
osd.455 {"max":3,"run":3,"wait":0}
osd.456 {"max":3,"run":1,"wait":0}
osd.457 {"max":3,"run":1,"wait":0}
osd.458 {"max":3,"run":3,"wait":0}
osd.459 {"max":3,"run":3,"wait":0}
osd.460 {"max":3,"run":3,"wait":0}
osd.461 {"max":3,"run":3,"wait":1}
osd.462 {"max":3,"run":3,"wait":0}
osd.463 {"max":3,"run":3,"wait":0}
osd.464 {"max":3,"run":1,"wait":0}
osd.465 {"max":3,"run":3,"wait":1}
osd.466 {"max":3,"run":3,"wait":0}
osd.467 {"max":3,"run":2,"wait":0}
osd.468 {"max":3,"run":2,"wait":0}
osd.469 {"max":3,"run":3,"wait":0}
osd.470 {"max":3,"run":3,"wait":1}
osd.471 {"max":3,"run":3,"wait":3}
osd.472 {"max":3,"run":3,"wait":1}
osd.473 {"max":3,"run":3,"wait":0}
osd.474 {"max":3,"run":3,"wait":3}
osd.475 {"max":3,"run":0,"wait":0}
osd.476 {"max":3,"run":0,"wait":0}
osd.477 {"max":3,"run":3,"wait":1}
osd.478 {"max":3,"run":1,"wait":0}
osd.479 {"max":3,"run":3,"wait":0}
osd.480 {"max":3,"run":3,"wait":3}
osd.481 {"max":3,"run":3,"wait":1}
osd.482 {"max":3,"run":2,"wait":0}
osd.483 {"max":3,"run":3,"wait":1}
osd.484 {"max":3,"run":3,"wait":1}
osd.485 {"max":3,"run":2,"wait":0}
osd.486 {"max":3,"run":2,"wait":0}
osd.487 {"max":3,"run":2,"wait":0}
osd.488 {"max":3,"run":0,"wait":0}
osd.489 {"max":3,"run":0,"wait":0}
osd.490 {"max":3,"run":2,"wait":0}
osd.491 {"max":3,"run":2,"wait":0}
osd.492 {"max":3,"run":0,"wait":0}
osd.493 {"max":3,"run":1,"wait":0}
osd.494 {"max":3,"run":2,"wait":0}
osd.495 {"max":3,"run":2,"wait":0}
osd.496 {"max":3,"run":2,"wait":0}
osd.497 {"max":3,"run":3,"wait":4}
osd.498 {"max":3,"run":1,"wait":0}
osd.499 {"max":3,"run":3,"wait":0}
osd.500 {"max":3,"run":0,"wait":0}
osd.501 {"max":3,"run":3,"wait":1}
osd.502 {"max":3,"run":3,"wait":0}
osd.503 {"max":3,"run":2,"wait":0}
osd.514 {"max":3,"run":0,"wait":0}
osd.515 {"max":3,"run":3,"wait":0}
osd.516 {"max":3,"run":3,"wait":3}
osd.517 {"max":3,"run":1,"wait":0}
osd.518 {"max":3,"run":3,"wait":4}
osd.519 {"max":3,"run":2,"wait":0}
osd.520 {"max":3,"run":3,"wait":1}
osd.521 {"max":3,"run":3,"wait":0}
osd.522 {"max":3,"run":3,"wait":1}
osd.523 {"max":3,"run":1,"wait":0}
osd.524 {"max":3,"run":2,"wait":0}
osd.525 {"max":3,"run":1,"wait":0}
osd.526 {"max":3,"run":2,"wait":0}
osd.527 {"max":3,"run":3,"wait":1}
osd.528 {"max":3,"run":2,"wait":0}
osd.529 {"max":3,"run":0,"wait":0}
osd.530 {"max":3,"run":0,"wait":0}
osd.531 {"max":3,"run":2,"wait":0}
osd.532 {"max":3,"run":2,"wait":0}
osd.533 {"max":3,"run":3,"wait":1}
osd.534 {"max":3,"run":3,"wait":0}
osd.535 {"max":3,"run":1,"wait":0}
osd.536 {"max":3,"run":1,"wait":0}
osd.537 {"max":3,"run":3,"wait":0}
osd.538 {"max":3,"run":2,"wait":0}
osd.539 {"max":3,"run":2,"wait":0}
osd.540 {"max":3,"run":2,"wait":0}
osd.541 {"max":3,"run":2,"wait":0}
osd.542 {"max":3,"run":3,"wait":0}
osd.543 {"max":3,"run":3,"wait":1}
osd.544 {"max":3,"run":2,"wait":0}
osd.545 {"max":3,"run":2,"wait":0}
osd.546 {"max":3,"run":3,"wait":0}
osd.547 {"max":3,"run":2,"wait":0}
osd.548 {"max":3,"run":3,"wait":0}
osd.549 {"max":3,"run":2,"wait":0}
osd.550 {"max":3,"run":1,"wait":0}
osd.551 {"max":3,"run":1,"wait":0}
osd.552 {"max":3,"run":2,"wait":0}
osd.553 {"max":3,"run":3,"wait":0}
osd.554 {"max":3,"run":3,"wait":1}
osd.555 {"max":3,"run":3,"wait":0}
osd.556 {"max":3,"run":3,"wait":1}
osd.557 {"max":3,"run":3,"wait":0}
osd.558 {"max":3,"run":3,"wait":3}
osd.559 {"max":3,"run":2,"wait":0}
osd.560 {"max":3,"run":3,"wait":1}
osd.561 {"max":3,"run":1,"wait":0}
osd.562 {"max":3,"run":2,"wait":0}
osd.563 {"max":3,"run":0,"wait":0}
osd.564 {"max":3,"run":3,"wait":0}
osd.565 {"max":3,"run":3,"wait":0}
osd.566 {"max":3,"run":2,"wait":0}
osd.567 {"max":3,"run":0,"wait":0}
osd.568 {"max":3,"run":1,"wait":0}
osd.569 {"max":3,"run":2,"wait":0}
osd.570 {"max":3,"run":3,"wait":0}
osd.571 {"max":3,"run":0,"wait":0}
osd.572 {"max":3,"run":3,"wait":2}
osd.573 {"max":3,"run":3,"wait":1}
osd.574 {"max":3,"run":2,"wait":0}
osd.575 {"max":3,"run":2,"wait":0}
osd.576 {"max":3,"run":3,"wait":3}
osd.577 {"max":3,"run":3,"wait":1}
osd.578 {"max":3,"run":2,"wait":0}
osd.579 {"max":3,"run":3,"wait":1}
osd.580 {"max":3,"run":3,"wait":1}
osd.581 {"max":3,"run":3,"wait":0}
osd.582 {"max":3,"run":3,"wait":0}
osd.583 {"max":3,"run":3,"wait":0}
osd.584 {"max":3,"run":3,"wait":3}
osd.585 {"max":3,"run":2,"wait":0}
osd.586 {"max":3,"run":3,"wait":0}
osd.587 {"max":3,"run":3,"wait":1}
osd.588 {"max":3,"run":1,"wait":0}
osd.589 {"max":3,"run":2,"wait":0}
osd.590 {"max":3,"run":3,"wait":3}
osd.591 {"max":3,"run":3,"wait":4}
osd.592 {"max":3,"run":3,"wait":1}
osd.593 {"max":3,"run":3,"wait":5}
osd.594 {"max":3,"run":2,"wait":0}
osd.595 {"max":3,"run":3,"wait":4}
osd.596 {"max":3,"run":2,"wait":0}
osd.597 {"max":3,"run":3,"wait":3}
osd.598 {"max":3,"run":3,"wait":0}
osd.599 {"max":3,"run":3,"wait":2}
osd.600 {"max":3,"run":2,"wait":0}
osd.601 {"max":3,"run":3,"wait":4}
osd.658 {"max":3,"run":0,"wait":0}
osd.727 {"max":3,"run":2,"wait":0}
osd.728 {"max":3,"run":3,"wait":0}
osd.729 {"max":3,"run":3,"wait":4}
osd.730 {"max":3,"run":3,"wait":0}
osd.731 {"max":3,"run":2,"wait":0}
osd.732 {"max":3,"run":2,"wait":0}
osd.733 {"max":3,"run":3,"wait":0}
osd.734 {"max":3,"run":1,"wait":0}
osd.735 {"max":3,"run":3,"wait":0}
osd.736 {"max":3,"run":0,"wait":0}
osd.737 {"max":3,"run":3,"wait":2}
osd.751 {"max":3,"run":3,"wait":1}
osd.752 {"max":3,"run":3,"wait":43}
osd.753 {"max":3,"run":2,"wait":0}
osd.754 {"max":3,"run":3,"wait":35}
osd.755 {"max":3,"run":2,"wait":0}
osd.756 {"max":3,"run":2,"wait":0}
osd.757 {"max":3,"run":3,"wait":1}
osd.758 {"max":3,"run":3,"wait":0}
osd.759 {"max":3,"run":3,"wait":2}
osd.760 {"max":3,"run":0,"wait":0}
osd.761 {"max":3,"run":1,"wait":0}
osd.762 {"max":3,"run":2,"wait":0}
osd.763 {"max":3,"run":0,"wait":0}
osd.764 {"max":3,"run":3,"wait":0}
osd.765 {"max":3,"run":0,"wait":0}
osd.766 {"max":3,"run":2,"wait":0}
osd.767 {"max":3,"run":1,"wait":0}
osd.768 {"max":3,"run":1,"wait":0}
osd.769 {"max":3,"run":3,"wait":44}
osd.770 {"max":3,"run":2,"wait":0}
osd.771 {"max":3,"run":2,"wait":0}
osd.772 {"max":3,"run":3,"wait":4}
osd.773 {"max":3,"run":2,"wait":0}
osd.774 {"max":3,"run":2,"wait":0}
osd.775 {"max":3,"run":0,"wait":0}
osd.776 {"max":3,"run":1,"wait":0}
osd.777 {"max":3,"run":2,"wait":0}
osd.778 {"max":3,"run":2,"wait":0}
osd.779 {"max":3,"run":1,"wait":0}
osd.780 {"max":3,"run":1,"wait":0}
osd.781 {"max":3,"run":1,"wait":0}
osd.782 {"max":3,"run":0,"wait":0}
osd.783 {"max":3,"run":3,"wait":0}
osd.784 {"max":3,"run":3,"wait":1}
osd.785 {"max":3,"run":2,"wait":0}
osd.786 {"max":3,"run":0,"wait":0}
osd.787 {"max":3,"run":2,"wait":0}
osd.788 {"max":3,"run":2,"wait":0}
osd.789 {"max":3,"run":1,"wait":0}
osd.790 {"max":3,"run":2,"wait":0}
osd.791 {"max":3,"run":1,"wait":0}
osd.792 {"max":3,"run":0,"wait":0}
osd.793 {"max":3,"run":3,"wait":0}
osd.794 {"max":3,"run":1,"wait":0}
osd.795 {"max":3,"run":1,"wait":0}
osd.796 {"max":3,"run":3,"wait":0}
osd.797 {"max":3,"run":1,"wait":0}
osd.798 {"max":3,"run":2,"wait":0}
osd.799 {"max":3,"run":3,"wait":0}
osd.800 {"max":3,"run":1,"wait":0}
osd.801 {"max":3,"run":1,"wait":0}
osd.802 {"max":3,"run":0,"wait":0}
osd.803 {"max":3,"run":2,"wait":0}
osd.804 {"max":3,"run":1,"wait":0}
osd.805 {"max":3,"run":2,"wait":0}
osd.807 {"max":3,"run":0,"wait":0}
osd.808 {"max":3,"run":0,"wait":0}
osd.809 {"max":3,"run":2,"wait":0}
osd.810 {"max":3,"run":1,"wait":0}
osd.811 {"max":3,"run":1,"wait":0}
osd.812 {"max":3,"run":1,"wait":0}
osd.813 {"max":3,"run":1,"wait":0}
osd.814 {"max":3,"run":3,"wait":1}
osd.815 {"max":3,"run":0,"wait":0}
osd.816 {"max":3,"run":0,"wait":0}
osd.817 {"max":3,"run":0,"wait":0}
osd.818 {"max":3,"run":1,"wait":0}
osd.819 {"max":3,"run":0,"wait":0}
osd.820 {"max":3,"run":1,"wait":0}
osd.821 {"max":3,"run":1,"wait":0}
osd.865 {"max":3,"run":3,"wait":1}
osd.866 {"max":3,"run":2,"wait":0}
osd.867 {"max":3,"run":3,"wait":1}
osd.868 {"max":3,"run":2,"wait":0}
osd.869 {"max":3,"run":3,"wait":0}
osd.870 {"max":3,"run":0,"wait":0}
osd.871 {"max":3,"run":0,"wait":0}
osd.872 {"max":3,"run":2,"wait":0}
osd.873 {"max":3,"run":3,"wait":0}
osd.874 {"max":3,"run":1,"wait":0}
osd.875 {"max":3,"run":3,"wait":0}
osd.876 {"max":3,"run":2,"wait":0}
osd.877 {"max":3,"run":2,"wait":0}
osd.878 {"max":3,"run":3,"wait":0}
osd.879 {"max":3,"run":2,"wait":0}
osd.880 {"max":3,"run":3,"wait":44}
osd.881 {"max":3,"run":0,"wait":0}
osd.882 {"max":3,"run":0,"wait":0}
osd.883 {"max":3,"run":3,"wait":0}
osd.884 {"max":3,"run":1,"wait":0}
osd.885 {"max":3,"run":1,"wait":0}
osd.886 {"max":3,"run":2,"wait":0}
osd.909 {"max":3,"run":1,"wait":0}
osd.910 {"max":3,"run":3,"wait":0}
osd.911 {"max":3,"run":3,"wait":3}
osd.912 {"max":3,"run":3,"wait":0}
osd.913 {"max":3,"run":0,"wait":0}
osd.914 {"max":3,"run":3,"wait":0}
osd.915 {"max":3,"run":3,"wait":0}
osd.916 {"max":3,"run":0,"wait":0}
osd.917 {"max":3,"run":0,"wait":0}
osd.918 {"max":3,"run":1,"wait":0}
osd.919 {"max":3,"run":0,"wait":0}
osd.920 {"max":3,"run":0,"wait":0}
osd.921 {"max":3,"run":1,"wait":0}
osd.922 {"max":3,"run":1,"wait":0}
osd.923 {"max":3,"run":3,"wait":1}
osd.924 {"max":3,"run":1,"wait":0}
osd.925 {"max":3,"run":0,"wait":0}
osd.926 {"max":3,"run":3,"wait":43}
osd.927 {"max":3,"run":3,"wait":0}
osd.928 {"max":3,"run":0,"wait":0}
osd.929 {"max":3,"run":1,"wait":0}
osd.930 {"max":3,"run":0,"wait":0}
osd.931 {"max":3,"run":2,"wait":0}
osd.932 {"max":3,"run":0,"wait":0}
osd.933 {"max":3,"run":3,"wait":47}
osd.934 {"max":3,"run":3,"wait":30}
osd.935 {"max":3,"run":3,"wait":44}
osd.936 {"max":3,"run":3,"wait":51}
osd.937 {"max":3,"run":3,"wait":38}
osd.938 {"max":3,"run":3,"wait":42}
osd.939 {"max":3,"run":3,"wait":33}
osd.940 {"max":3,"run":3,"wait":39}
osd.941 {"max":3,"run":3,"wait":46}
osd.942 {"max":3,"run":3,"wait":37}
osd.943 {"max":3,"run":3,"wait":31}
osd.944 {"max":3,"run":3,"wait":34}
osd.945 {"max":3,"run":3,"wait":38}
osd.946 {"max":3,"run":3,"wait":38}
osd.947 {"max":3,"run":3,"wait":41}
osd.948 {"max":3,"run":3,"wait":43}
osd.949 {"max":3,"run":3,"wait":17}
osd.950 {"max":3,"run":3,"wait":16}
osd.951 {"max":3,"run":3,"wait":15}
osd.952 {"max":3,"run":3,"wait":20}
osd.953 {"max":3,"run":3,"wait":14}
osd.954 {"max":3,"run":3,"wait":14}
osd.955 {"max":3,"run":3,"wait":9}
osd.956 {"max":3,"run":3,"wait":6}
osd.957 {"max":3,"run":3,"wait":2}
osd.958 {"max":3,"run":3,"wait":0}
osd.959 {"max":3,"run":3,"wait":8}
osd.960 {"max":3,"run":3,"wait":10}
osd.961 {"max":3,"run":3,"wait":6}
osd.962 {"max":3,"run":3,"wait":0}
osd.963 {"max":3,"run":3,"wait":0}
osd.964 {"max":3,"run":3,"wait":3}

1. Xác nhận: max_backfills=3 đã ăn toàn fleet

Mọi OSD đều "max":3 → setting đã hiệu lực trên tất cả OSD subtree mbf9-02. Verify thành công.

2. Cap đang CẮN mạnh trên các OSD MỚI thêm

Để ý nhóm OSD ID cao (đặc biệt 933–948, và rải rác 752/754/769/880/926):

osd.936 {run:3, wait:51}
osd.933 {run:3, wait:47}
osd.941 {run:3, wait:46}
osd.880 {run:3, wait:44}   osd.769 {run:3, wait:44}   osd.926 {run:3, wait:43}
osd.752 {run:3, wait:43}   osd.948 {run:3, wait:43}
...

Đây gần như chắc chắn là 40 ổ bạn vừa thêm (925→965). Chúng trống → CRUSH muốn đổ data vào → hàng chục PG xếp hàng đòi backfill VÀO, nhưng max_backfills=3 chỉ cho 3 cái/lúc → nên queue dồn 30–51. Đây là chữ ký kinh điển của "ổ mới đang được lấp đầy". Cap đang làm đúng việc: ghì tốc độ đổ vào mỗi ổ mới ở mức 3.

osd.960 bạn soi chi tiết là một ví dụ chuẩn:

remote_running: 3    đang nhận 3 PG (bão hòa cap)
remote_waiting: 10   10 PG chờ tới lượt
local_running:  0    KHÔNG điều phối  ( mới, chỉ hứng, chưa làm primary)

→ pure target, no đến nóc, 10 cái đợi.

3. OSD cũ phần lớn wait≈0 — vì chúng là SOURCE

Đa số ổ còn lại run:1–3, wait:0, và một loạt run:0 (430, 475, 488, 530, 736, 808, 916…). Nghĩa là vai TARGET của chúng không bị tranh — vì data đang chảy RA khỏi chúng sang ổ mới, tức chúng làm SOURCE.

Nhớ lại điểm mấu chốt: dump này chỉ đếm vai target/điều phối (cái max_backfills khóa). Vai source thì không bị cap và KHÔNG hiện ở đây. Nên một ổ cũ run:0,wait:0 không có nghĩa nó rảnh — nó có thể đang bị đọc làm nguồn cho nhiều PG. Muốn thấy tải đó phải nhìn iostat phía read trên ổ cũ, không phải lệnh này.

4. Bức tranh tổng

~40 ổ mới   → TARGET, run=3 bão hòa, queue 30–50  → đây là động cơ + nút cổ chai rebalance
ổ cũ        → SOURCE, đọc ra (vô hình ở dump này), vai target nhẹ
  • Toàn bộ là misplaced (HEALTH_OK, 0 degraded) → copy 1:1, không 6:1, không rủi ro dữ liệu → để max_backfills=3 chạy cho nhanh là hợp lý.
  • Tốc độ rebalance bị ghì bởi các ổ mới (mỗi ổ hút 3 luồng). ETA ~5h chính là thời gian để các queue 30–50 kia rút cạn ở nhịp 3-một-lúc.
  • Cái gradient queue tụt dần (933≈47 → 957≈2 → 958=0) cho thấy các ổ mới đang ở các mức lấp đầy khác nhau; ổ queue sâu sẽ là ổ xong sau cùng.

Có cần lo không

Một điểm đáng liếc: các ổ mới đang run=3 trên HDD co-located WAL. 3 luồng backfill ghi + RocksDB cùng một spindle → nếu chúng đồng thời phục vụ client thì có thể nhú latency. Nhưng vì là ổ mới/đang lấp nên client trên chúng gần như chưa có → 3 hầu như an toàn. Cứ liếc ceph health detail xem có slow op nào rơi vào dải 933–964 không; nếu có thì hạ về 2, còn không thì để 3 cho xong sớm.

root@VSTOR-CEPH-MBF9-MON-05:/# ceph tell osd.914 dump_recovery_reservations | jq '{
  remote_max:     .remote_reservations.max_allowed,
  remote_running: (.remote_reservations.in_progress|length),
  remote_waiting: ([.remote_reservations.queues[].items|length]|add // 0),
  local_max:      .local_reservations.max_allowed,
  local_running:  (.local_reservations.in_progress|length)
}'
{
  "remote_max": 3,
  "remote_running": 3,
  "remote_waiting": 0,
  "local_max": 3,
  "local_running": 3
}
  • max:3 cả hai reserver → setting đúng. ✓
  • local_running:3 → osd.914 đang làm PRIMARY điều phối 3 backfill (đọc shard của nó + đẩy sang đích). Đây là điểm khác hẳn ổ mới (osd.960 có local:0): 914 là ổ CŨ, đã là primary của nhiều PG.
  • remote_running:3 → đồng thời nó cũng đang nhận backfill VÀO (vai target) cho 3 PG.
  • wait:0 cả hai → không có hàng chờ → nó chạy hết công suất nhưng không bị dồn ứ (khác ổ mới queue 30–50).

→ Tóm: osd.914 đang gánh 3 (primary/coordinator) + 3 (target nhận) = 6 vai backfill cùng lúc, cả hai núm đều chạm trần 3, lại còn đọc làm source cho các PG khác (vô hình ở dump này). Tức đây là ổ cũ đang bận cả hai đầu — vừa cho data chảy qua (primary/source) vừa hứng một ít. wait=0 nghĩa là nó theo kịp, chưa thành nút thắt; nhưng đây là loại ổ đáng iostat nếu có slow op, vì nó đang làm nhiều việc nhất trong các vai.

osdroles() {
  local N=$1
  declare -A H
  while read -r id host; do H[$id]=$host; done < <(
    ceph osd tree -f json 2>/dev/null \
    | jq -r '.nodes[] | select(.type=="host") | .name as $h
             | .children[]? | select(type=="number" and .>=0) | "\(.) \($h)"')
  echo "================ osd.$N  @ ${H[$N]:-?} ================"
  local resv; resv=$(ceph tell osd.$N dump_recovery_reservations 2>/dev/null)
  echo "--- ACTUALLY RUNNING (reservation) ---"
  echo "$resv" | jq -r '
    "  TARGET receiving (remote): \(.remote_reservations.in_progress|length) running / \([.remote_reservations.queues[].items|length]|add // 0) waiting",
    "  PRIMARY coordinating (local): \(.local_reservations.in_progress|length) running / \([.local_reservations.queues[].items|length]|add // 0) waiting"'
  echo "--- SOURCE (osd.$N being read → pushed to) ---"
  local srcs total nact
  srcs=$(ceph pg ls-by-osd osd.$N -f json 2>/dev/null | jq -r --argjson N $N '
    (if type=="object" then .pg_stats else . end)
    | .[] | select(.state|test("backfill")) | . as $p
    | ($p.acting|index($N)) as $ia
    | ($p.acting|index(2147483647)) as $none
    | (if ($p.state|test("backfilling")) then "active" else "wait" end) as $st
    | if   ($ia!=null) and ($p.up[$ia]!=$N) then "\($p.pgid) s\($ia) \($p.up[$ia]) \($st) -"
       elif ($ia!=null) and ($none!=null)   then "\($p.pgid) s\($ia) \($p.up[$none]) \($st) rebuild"
       else empty end')
  total=$(printf '%s\n' "$srcs" | grep -c .)
  nact=$(printf '%s\n' "$srcs" | awk '$4=="active"{c++} END{print c+0}')
  echo "  SOURCE: $total PG  ( $nact actively READING / $((total-nact)) waiting )"
  printf '%s\n' "$srcs" | sort -k4 | while read -r pg pos peer st tag; do
    [ -z "$pg" ] && continue
    mark="[wait]"; [ "$st" = "active" ] && mark="[READ]"
    x=""; [ "$tag" = "rebuild" ] && x=" (rebuild)"
    echo "      $mark $pg  $pos → osd.$peer (${H[$peer]:-?})$x"
  done
  echo "--- TARGET (osd.$N receiving ←, all currently running) ---"
  echo "  TARGET: $(echo "$resv" | jq '.remote_reservations.in_progress|length') PG"
  echo "$resv" | jq -r '.remote_reservations.in_progress[].item' | while read -r it; do
    pg=${it%s*}; sp=${it##*s}
    acting=$(ceph pg "$pg" query 2>/dev/null | jq -c '.acting')
    peer=$(jq -rn --argjson a "$acting" --argjson sp "$sp" '$a[$sp]' 2>/dev/null)
    if [ "$peer" = "2147483647" ]; then
      echo "      [WRITE] $pg  s$sp ← rebuilt from survivors (degraded)"
    else
      echo "      [WRITE] $pg  s$sp ← osd.$peer (${H[$peer]:-?})"
    fi
  done
}

Kết quả

root@VSTOR-CEPH-MBF9-MON-05:/# osdroles 914
================ osd.914  @ VSTOR-CEPH-MBF9-OSD-54 ================
--- ĐANG CHẠY THẬT (reservation) ---
  TARGET nhận  (remote): 3 chạy / 0 chờ
  PRIMARY đ/phối(local): 3 chạy / 6 chờ
--- SOURCE (osd.914 bị đọc  đẩy cho) ---
  SOURCE: 14 PG
      14.15f  s2  osd.580 (VSTOR-CEPH-MBF9-OSD-49)
      14.3c1  s6  osd.434 (VSTOR-CEPH-MBF9-OSD-42)
      14.599  s1  osd.918 (VSTOR-CEPH-MBF9-OSD-54)
      14.60c  s0  osd.769 (VSTOR-CEPH-MBF9-OSD-56)
      14.79d  s2  osd.792 (VSTOR-CEPH-MBF9-OSD-52)
      14.95e  s0  osd.522 (VSTOR-CEPH-MBF9-OSD-46)
      14.960  s5  osd.943 (VSTOR-CEPH-MBF9-OSD-56)
      14.9a5  s4  osd.934 (VSTOR-CEPH-MBF9-OSD-56)
      14.b09  s0  osd.938 (VSTOR-CEPH-MBF9-OSD-56)
      14.b4c  s5  osd.543 (VSTOR-CEPH-MBF9-OSD-47)
      14.bf2  s6  osd.583 (VSTOR-CEPH-MBF9-OSD-49)
      14.c78  s4  osd.933 (VSTOR-CEPH-MBF9-OSD-56)
      14.e64  s7  osd.411 (VSTOR-CEPH-MBF9-OSD-41)
      14.fc4  s7  osd.244 (VSTOR-CEPH-MBF9-OSD-56)
--- TARGET (osd.914 nhận ) ---
  TARGET: 3 PG
      14.237  s3  osd.727 (VSTOR-CEPH-MBF9-OSD-50)
      14.4e9  s5  osd.591 (VSTOR-CEPH-MBF9-OSD-49)
      14.66f  s3  osd.727 (VSTOR-CEPH-MBF9-OSD-50)
for id in $(ceph osd ls-tree mbf9-02); do
  v=$(ceph tell osd.$id dump_recovery_reservations 2>/dev/null | jq -c \
    '{max:.remote_reservations.max_allowed, run:(.remote_reservations.in_progress|length), wait:([.remote_reservations.queues[].items|length]|add // 0)}')
  echo "osd.$id $v"
done

ceph osd ls-tree mbf9-02 | xargs -P 12 -I{} sh -c '
  v=$(ceph tell osd.{} dump_recovery_reservations 2>/dev/null | jq -c "{max:.remote_reservations.max_allowed, run:(.remote_reservations.in_progress|length), wait:([.remote_reservations.queues[].items|length]|add // 0)}")
  echo "osd.{} $v"' | sort -V