
RBD 이미지 -> PG -> OSD 매핑
Ceph 클러스터를 운영하다 보면 장애 분석이나 성능·용량 이슈가 생겼을 때,“지금 보고 있는 RBD 이미지가 실제로 어떤 PG에 올라가 있고, 그 PG가 어떤 OSD 들에 퍼져 있는지”를 알고 싶은 순간이 생겼습니다.
예를 들어,
1. 특정 OSD 장애가 발생했을 때, 그 OSD에 어떤 이미지가 얼마나 몰려 있는지 보고 싶거나
2. 리밸런싱 이후 데이터가 의도대로 고르게 분산됐는지 확인하고 싶은경우
"Ceph 내부에서는 객체 이름과 풀 ID로 해시해서 PG를 계산하고 이 PG를 다시 CRUSH로 여러 OSD에 배치를 하게 됩니다."
그래서 저는 go-cpeh(librados를 go 언어로 래핑한 오픈소스)를 사용하여 RBD 이미지 -> PG -> OSD 매핑 API 개발을 진행했습니다.
이후 해당 관련 내용은 다음 포스팅에서 다루도록 하겠습니다!
Ceph MGR Module vs Custom Container
Ceph MGR Module
MGR Module: https://docs.ceph.com/en/latest/mgr/modules/
개요
- Ceph-Mgr 데몬 내부에 동작하는 Python 기반의 플러그인 모듈
- Python으로 MgrModule 클래스를 상속 받아서 구현해야 하며, serve(), notify(), handler_command()등의 메소드를 오버라이드 구현이 필요
장점
- Ceph 클러스터 내부 데이터(모니터, OSD, PG 등) 밀접하게 연동하여 기능 확장이 가능
- ceph mgr module enable <my_module> 로 활성화 가능
Custom Container
Custom Conatainer: https://docs.ceph.com/en/reef/cephadm/services/custom-container/
[Ceph MON / MGR / OSD] [Custom Service (go-ceph API)]
(Core Ceph) (독립 컨테이너)
│ │
└────── Ceph Protocols ──────┬──────┘
(librados, REST 등)
- Ceph 클러스터 내부에서 배포되지만, Ceph의 핵심 데몬 (OSD, MON, MGR등)에 직접 속하지 않는 독립 실행형 컨테이너를 의미
- Cephadm이 이서비스를 container 단위로 배포하고, 운영 관리를 도와주는 형태이지 Ceph 데몬처럼 클러스터의 핵심 구성요소는 아님
개요
- Ceph 크러스터 외부에 독립 실행형 컨테이너 서비스를 배포할 수 있는 기능
- ceph orch apply 명령으로 배포하고 Ceph-orchestrator가 lifecycle을 관리
주요 특징
- Ceph core 역할은 아니지만 Ceph 프로토콜(librados, REST)로 클러스터와 느슨하게 연결 (loose coupling)
- 배포는 원하는 노드에 자유롭게, cephadm orchestrator가 관리
Ceph-MGR Module vs Custom Container Servcie
| 항목 | MgrModule | Custom Container Service |
| 실행 위치 | Ceph‑Mgr 데몬 내부 | 독립 실행 컨테이너 |
| 언어 제약 | Python only | 언어 제한 없음 (Go, Java, Python 등) |
| 클러스터와 연동도 | 매우 밀접 (내부 기능) | 느슨한 결합 (REST, librados 등 사용) |
| 배포 대상 | 모든 mgr 노드 | 지정한 노드에 선택 배포 |
| 관리 주체 | Ceph‑Mgr 데몬 | cephadm orchestrator |
| 라이프사이클 관리 | ceph mgr module enable/disable | ceph orch apply/update/remove |
선택 배경 및 결정 이유
이번에 개발한 API는 go-ceph 기반으로 구현하였으며, Ceph 클러스터 내부의 상태 정보를 활용하는 구조입니다. 이 기능을 실 서비스 환경에 통합하기 위해 처음에는 Ceph-MGR Module을 고려했습니다. MGR Module은 Ceph 클러스터 내부 기능과 밀접하게 연동할 수 있다는 점에서 매력적이었지만, 몇 가지 현실적인 제약이 있었습니다.
첫 번째로, MGR Module은 Python 기반으로만 개발할 수 있으며, ceph-mgr 데몬 내부에서 동작하기 때문에 실제 Ceph 환경이 갖춰진 상태에서만 테스트가 가능합니다. 이로 인해 개발 및 디버깅 속도가 느려지고, 개발 환경 구축이 까다롭습니다.
두 번째로, 저희는 cephadm 기반으로 클러스터를 운영하고 있는데, 이 환경에서는 mgr 데몬이 컨테이너로 실행되므로, 직접 소스 파일을 수정하거나 테스트하는 데 제약이 따릅니다. 예를 들어, 새로운 mgr 모듈을 반영하려면 컨테이너에 볼륨 마운트 설정을 추가하거나 재시작해야 하고, ceph mgr module enable/disable 과정을 반복해야 합니다.
이러한 이유들로 인해 결국 Custom Container Service를 선택하게 되었습니다. Custom Container는 Ceph 클러스터 외부에서 독립적으로 동작하는 서비스이기 때문에, 언어나 프레임워크에 제한이 없고, 개발 및 테스트도 로컬에서 쉽게 수행할 수 있습니다. 배포 또한 ceph orch apply 명령어 한 줄로 가능하며, 전체 라이프사이클을 Ceph의 orchestrator가 관리해주기 때문에 운영 측면에서도 효율적입니다.
결과적으로 여러 요소를 고려했을 때, Custom Container Service가 우리 팀에게 더 적합한 방향이라고 판단되었고, 이에 따라 실제 구현과 배포를 진행하게 되었습니다.
과정
우선 제 목표는 클러스터에 컨테이너가 잘 올라가는지 테스트를 하는게 목적이었기 때문에, go로 간단하게 API를 하나 만들었습니다.
Dockerfile
# 빌드 단계
FROM --platform=linux/amd64 golang:1.24-bookworm AS builder
WORKDIR /app
LABEL ceph="true"
RUN apt-get update && apt-get install -y \
build-essential \
librados-dev \
librbd-dev \
ceph-common \
&& rm -rf /var/lib/apt/lists/*
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags no_encryption -o ceph-core-api ./cmd/api
# 실행 단계
FROM --platform=linux/amd64 debian:bookworm-slim
WORKDIR /app
RUN apt-get update && apt-get install -y librados2 ceph-common && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/ceph-core-api .
EXPOSE 9080
CMD ["./ceph-core-api"]
Ceph 클러스터와 통신할 수 있는 Go 기반 API를 효율적으로 빌드하고 실행하기 위해 멀티스테이지 빌드 방식을 사용했습니다. 이를 통해 불필요한 의존성을 제거한 경량 컨테이너 이미지를 만들 수 있습니다.
빌드
FROM --platform=linux/amd64 golang:1.24-bookworm AS builder
- golang 이미지를 기반으로 Go 애플리케이션을 컴파일합니다.
- --platform=linux/amd64는 호스트 아키텍처와 관계없이 명확한 타깃 플랫폼을 지정해 M1/M2 Mac 등에서도 호환 문제를 방지
RUN apt-get update && apt-get install -y \
build-essential \
librados-dev \
librbd-dev \
ceph-common \
- Ceph 클러스터와 연동하기 위해 필요한 개발 헤더 및 라이브러리를 설치합니다.
- Go의 CGO 기능을 활용해 librados를 사용하기 위한 필수 패키지입니다.
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags no_encryption -o ceph-core-api ./cmd/api
- CGO를 활성화하여 Ceph C 라이브러리와 연동 가능하게 빌드합니다.
- -tags no_encryption은 필요한 경우 빌드 태그를 조정할 수 있습니다. (해당 태그가 없는 경우 빌드 에러)
실행
FROM --platform=linux/amd64 debian:bookworm-slim
- 실행 이미지는 debian-slim 기반으로, 빌드 도구 없이 필요한 라이브러리만 포함해 이미지 크기를 최소화합니다.
- Ceph 공식 컨테이너 이미지나 대부분의 Ceph 관련 컴포넌트는 linux/amd64를 기본으로 빌드됨
- 호스트와 상관없이 항상 Ceph 클러스터와 호환되는 아키텍처로 이미지를 고정 (플랫폼을 맞춰주지 않는 경우 컨테이너 에러)
RUN apt-get update && apt-get install -y librados2 ceph-common
- 런타임에서 librados를 활용하기 위해 필요한 패키지를 설치합니다.
COPY --from=builder /app/ceph-core-api .
EXPOSE 9080
CMD ["./ceph-core-api"]
- 빌드된 바이너리만 복사해 실행 환경을 구성하고, 9080 포트로 API를 제공하도록 설정합니다.
Dockerfile로 이미지를 빌드하고 컨테이너 허브에 push
docker buildx build --platform linux/amd64 -t harbor.tareun.kr/share/djlee/ceph-core-api:v1.0.3 --push .
현재 저는 맥북(arm64)을 사용하기 때문에 --platform 옵션 없이 빌드하면 arm64 아키텍처 이미지로 생성됩니다.
arm64 아키텍처로 Ceph 클러스터에 배포하면 컨테이너 실행이 실패합니다.
그럼 이제 실제 Cluster에서 Push한 컨테이너 이미지를 사용해서 배포를 진행하면 됩니다.
Spec 예시 및 옵션 (ceph-core-api.yml)
service_type: container
service_id: ceph-core-api
placement:
hosts:
- ceph152
- ceph153
spec:
image: harbor.xxx.kr/share/djlee/ceph-core-api:v1.0.4
args:
- "--net=host"
ports:
- 9080
envs:
- GIN_MODE=release
bind_mounts:
- ['type=bind', 'source=/etc/ceph', 'destination=/etc/ceph', 'ro=true']
배포
ceph orch apply -i ceph-core-api.yml
서비스 목록과 배포 상태 확인

결과
curl http://121.141.64.152:9080/api/cluster/fsid
{
"status": "success",
"data": {
"fsid": "df5355e6-f66f-45c6-83b0-4680e086eff5"
}
}

Prometheus 연동
- Cephadm으로 관리되는 Prometheus에서 기본 Ceph exporter 외에 사용자 정의 메트릭 엔드포인트 수집하기 위함
- 기존 Ceph 모듈(ceph, ceph-exporter, node-exporter)의 scrape 설정 유지, 새로운 job 추가
1. 템플릿 파일 작성 (prometheus.yml.j2)
기존 job 설정을 모두 포함하지 않으면 기존 수집 항목이 사라질 수 있으니, 반드시 기존 job도 포함해야 함.
: 템플릿을 작성하기 전에, 현재 Cephadm에서 Prometheus가 수집 중인 모듈(job)확인 필수!
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'ceph'
static_configs:
- targets: ['ceph155:9283']
- job_name: 'ceph-exporter'
static_configs:
- targets:
- 'xxx.xxx.xx.xxx:9926'
- 'xxx.xxx.xx.xxx:9926'
- 'xxx.xxx.xx.xxx:9926'
- 'xxx.xxx.xx.xxx:9926'
- job_name: 'node'
static_configs:
- targets:
- 'xxx.xxx.xx.xxx:9100'
- 'xxx.xxx.xx.xxx:9100'
- 'xxx.xxx.xx.xxx:9100'
- 'xxx.xxx.xx.xxx:9100'
- job_name: 'ceph-core-api'
static_configs:
- targets:
- 'localhost:9080'
2. config-key로 등록
ceph config-key set mgr/cephadm/services/prometheus/prometheus.yml -i prometheus.yml.j2
3. Prometheus 서비스 재배포
ceph orch redeploy promoetheus
4. 결과

