[Ceph] cephadm으로 디스크 없는 서버 3대에 학습용 클러스터 세우기 — file-backed OSD와 비표준 SSH 포트 함정
운영 클러스터 재구축을 앞두고 Ceph를 도입하기로 했는데, 문서만 읽어서는 감이 안 왔다. 마침 재설치 대기 중인 유휴 서버 3대가 있어서 하룻밤 학습용 PoC를 돌렸다 — 조건이 좀 짓궂었다. OSD에 줄 여유 디스크가 한 개도 없고(전부 OS 디스크 단발), SSH는 22가 아닌 비표준 포트로 통일된 환경. 이 글은 그 제약 안에서 cephadm으로 3노드 클러스터를 세우고, 복제와 장애 복구를 눈으로 확인하기까지의 기록이다.
최종 산출: Ceph 19.2.5(squid), MON 3 쿼럼 + MGR active/standby + OSD 3, HEALTH_OK. 그리고 도중에 밟은 함정들.
0. 재료
- 유휴 서버 3대 — Ubuntu 24.04, 여유 디스크 없음(루트에 115~248GB 여유 공간만), RAM 160GB
- 노드 간 방화벽 ufw active (기본 deny)
- sshd는 전 노드 비표준 포트(이 글에서는 2222라고 하자)
- 컨테이너 런타임 없음 → cephadm은 docker 위에 데몬을 컨테이너로 띄운다
1. 준비 — 패키지와 방화벽
3노드 모두:
sudo DEBIAN_FRONTEND=noninteractive apt install -y docker.io cephadm
# Ubuntu 24.04 noble 저장소의 cephadm 19.2.x — 부트스트랩 때 컨테이너 이미지는 어차피 quay.io에서 온다
ufw가 기본 deny라면 포트를 미리 뚫어두는 게 중요하다. Ceph 데몬 간 통신이 막히면 "데몬은 뜨는데 쿼럼이 안 되고 OSD가 안 붙는" 무소음 실패로 나타나서, 나중엔 원인 찾기가 훨씬 괴롭다.
# 각 노드에서, 소스는 클러스터 노드 IP로 한정
sudo ufw allow from <노드IP> to any port 3300 proto tcp # MON msgr v2
sudo ufw allow from <노드IP> to any port 6789 proto tcp # MON msgr v1
sudo ufw allow from <노드IP> to any port 6800:7300 proto tcp # OSD/MGR
2. bootstrap — 비표준 SSH 포트 함정 (이 글의 하이라이트)
cephadm 오케스트레이터는 노드 간 배치를 root SSH로 수행한다. 그리고 기본 포트는 당연히 22다. 우리처럼 sshd가 비표준 포트면 어떻게 될까? "일단 bootstrap 하고 나서 ceph cephadm set-ssh-config로 바꾸면 되겠지"라고 생각했는데 —
ERROR: Failed to add host <본인 호스트>: Failed to connect to <본인 IP> (<본인 IP>).
Connect error: [Errno 111] Connect call failed ('<본인 IP>', 22)
bootstrap의 마지막 단계가 "자기 자신을 호스트로 등록"인데, 그게 이미 포트 22로 나간다. 즉 set-ssh-config를 넣을 "bootstrap 후" 시점이 존재하지 않는다. 더 아픈 건 이 실패로 bootstrap이 롤백되면서 방금 만든 클러스터를 통째로 지운다는 것.
정답은 선주입이다:
sudo tee /root/cephadm-ssh-config <<EOF
Host *
Port 2222
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
EOF
sudo cephadm bootstrap --mon-ip <노드1 IP> \
--skip-dashboard --skip-monitoring-stack \
--ssh-config /root/cephadm-ssh-config
(--skip 두 개는 학습용 최소 구성 — 대시보드·prometheus/grafana는 필요해지면 나중에 ceph orch apply로 얹을 수 있다.)
3. 노드 편입 — root 키 배포 후 host add
bootstrap이 만든 /etc/ceph/ceph.pub을 나머지 노드의 root authorized_keys에 넣고(오케스트레이터가 root로 접속하므로 일반 계정이 아니다), 편입한다:
sudo cephadm shell -- ceph orch host add node2 <노드2 IP>
sudo cephadm shell -- ceph orch host add node3 <노드3 IP>
sudo cephadm shell -- ceph orch apply mon 3
몇 분 뒤 ceph -s에 mon: 3 daemons, quorum node1,node2,node3이 뜨면 쿼럼 완성.
4. 디스크가 없는데 OSD는 어떻게? — file-backed LV
OSD는 통째로 내줄 블록 장치를 원하는데, 우리에겐 파티션 하나 없다. 그래서 스파스 파일 → loop 장치 → LVM으로 가짜 블록 장치를 만들었다:
sudo mkdir -p /var/lib/ceph-poc
sudo truncate -s 50G /var/lib/ceph-poc/osd.img # 스파스 — 실제 공간은 쓴 만큼만
dev=$(sudo losetup -f --show /var/lib/ceph-poc/osd.img)
sudo pvcreate $dev && sudo vgcreate cephpoc $dev
sudo lvcreate -n osd0 -l 100%FREE cephpoc
loop 장치는 재부팅하면 사라지므로 재연결 systemd 유닛을 깔았다. 포인트 두 개: loop 번호를 하드코딩하지 말고 이미지 경로 기준으로 다룰 것(losetup -j), 그리고 Ceph 데몬(docker 컨테이너)보다 먼저 붙도록 순서를 잡을 것.
[Unit]
Description=Ceph PoC file-backed loop device (osd.img)
After=local-fs.target
Wants=local-fs.target
Before=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c "/usr/sbin/losetup -j /var/lib/ceph-poc/osd.img | grep -q . || /usr/sbin/losetup -f /var/lib/ceph-poc/osd.img"
ExecStart=/usr/sbin/vgchange -ay cephpoc
[Install]
WantedBy=multi-user.target
그리고 걱정했던 것과 달리 — cephadm 표준 경로가 이 LV를 군말 없이 받아준다:
sudo cephadm shell -- ceph orch daemon add osd node1:/dev/cephpoc/osd0
# → Created osd(s) 0 on host 'node1' (node2, node3 동일)
3개 편입 후 HEALTH_OK. 참고로 file-backed인데도 CRUSH device class는 hdd로 자동 분류된다.
5. 복제 3벌을 눈으로 — 4MiB가 12MiB가 되는 순간
풀을 만들고 4MiB 랜덤 파일을 넣었다 꺼내서 해시를 비교했다:
sudo cephadm shell -- bash -c '
ceph osd pool create pocpool 32 && rbd pool init pocpool
dd if=/dev/urandom of=/tmp/poc.bin bs=1M count=4
rados -p pocpool put pocobj /tmp/poc.bin
rados -p pocpool get pocobj /tmp/poc.out
md5sum /tmp/poc.bin /tmp/poc.out'
해시 일치. 그리고 ceph df:
POOL ID PGS STORED OBJECTS USED %USED MAX AVAIL
pocpool 2 32 4.0 MiB 7 12 MiB 0 47 GiB
STORED 4MiB, USED 12MiB — "쓰기 1번 = 클러스터에 3벌"이라는 복제 산수가 숫자로 그대로 보인다. (통계는 반영까지 수 초 걸린다 — put 직후 0B로 보여도 고장이 아니다.) 잔재미 하나: cephadm shell은 호출마다 새 컨테이너라, 위처럼 임시 파일을 쓰는 작업은 한 번의 bash -c 안에서 끝내야 한다.
6. 장애 실험 — OSD 하나를 죽여보자
OSD 데몬 하나를 강제로 내렸다:
sudo systemctl stop ceph-<fsid>@osd.1.service
30초 뒤:
health: HEALTH_WARN
Degraded data redundancy: 9/27 objects degraded (33.333%), 8 pgs degraded
osd: 3 osds: 2 up
27개 객체 중 9개 — 정확히 1/3이 복제 부족 상태가 됐다. 3벌 중 1벌이 사라졌으니까. 재기동 30초 뒤:
health: HEALTH_OK
osd: 3 osds: 3 up
pgs: 33 active+clean
핫스페어도, 사람 개입도 없이 원상 복구. RAID에 익숙한 눈으로 보면 "리빌드를 기다리지 않는다"는 게 가장 낯설고 가장 매력적인 부분이다.
배운 것 정리
- cephadm의 SSH 설정은 bootstrap 인자로만 선주입 가능 — 비표준 포트 환경이면
--ssh-config없이 시작하지 마라. 실패하면 클러스터가 자동 삭제되면서 처음부터다. - 여유 디스크가 없어도 PoC는 된다 — 스파스 파일→loop→LVM 경로를 orch가 표준으로 수용한다. 물론 성능은 무의미하니 개념·운영 연습 전용.
- 방화벽은 선개방 — ufw 기본 deny에서 포트 누락은 무소음 실패로 나타난다. MON(3300·6789)·OSD/MGR(6800:7300)을 노드 IP 한정으로.
- 복제 3벌·self-heal은 과장이 아니다 — USED 3배, degraded 33%→자동 복구를 직접 관찰했다. 반대로 말하면 가용 용량 = 원시 ÷ 3이라는 비용도 진짜다.
- 실전 배치에서 바뀔 것: MON은 전용 컨트롤 노드로, OSD는 실디스크로, public/cluster 망 분리 — 하지만 오늘 밟은 함정들은 그대로 다시 나온다.
댓글
아직 댓글이 없습니다. 첫 댓글을 남겨보세요.