---
title: "HAProxy를 활용한 Ceph 클러스터의 통합 리버스 프록시 설정 및 Grafana 연동 가이드"
description: "HAProxy를 이용해 Ceph 클러스터의 Dashboard, Grafana, RADOS Gateway 등 여러 웹 서비스를 도메인 기반 SNI 라우팅과 SSL 종료로 단일 진입점(443)에서 제공하고, 각 서비스별 backend를 라운드로빈 로드밸런싱으로 구성한다. 또한 Ceph Dashboard와 Grafana 연동을 위해 Grafana의 iframe 허용 및 익명 인증 설정, API URL 및 프론트엔드 URL 지정, 필요 시 SSL 검증 비활성화 등을 수행한다. 설정 파일 검증·HAProxy 재시작·서비스 접근 테스트·트러블슈팅 절차도 포함한다."
date: "2026-02-12"
last_modified: "2026-05-15T08:36:00.000Z"
type: "Post"
tags:
  - "haproxy"
  - "ceph"
  - "ssl"
  - "load balancing"
  - "networking"
categories:
  - "🤖 Computer Science"
series:
  - "ceph"
canonical_url: "https://blog.pieroot.xyz/haproxy-ceph-grafana"
markdown_url: "https://blog.pieroot.xyz/haproxy-ceph-grafana.md"
---

# HAProxy를 활용한 Ceph 클러스터의 통합 리버스 프록시 설정 및 Grafana 연동 가이드

HAProxy를 이용해 Ceph 클러스터의 Dashboard, Grafana, RADOS Gateway 등 여러 웹 서비스를 도메인 기반 SNI 라우팅과 SSL 종료로 단일 진입점(443)에서 제공하고, 각 서비스별 backend를 라운드로빈 로드밸런싱으로 구성한다. 또한 Ceph Dashboard와 Grafana 연동을 위해 Grafana의 iframe 허용 및 익명 인증 설정, API URL 및 프론트엔드 URL 지정, 필요 시 SSL 검증 비활성화 등을 수행한다. 설정 파일 검증·HAProxy 재시작·서비스 접근 테스트·트러블슈팅 절차도 포함한다.

Ceph 클러스터를 운영하다 보면 Dashboard, Grafana, RADOS Gateway 등 다양한 웹 서비스들이 각자의 포트에서 돌아가게 됩니다.

문제는 이 서비스들을 외부에 깔끔하게 노출하려면 **단일 진입점**이 필요하다는 거죠.

매번 [`https://172.30.2.101:8443`](https://172.30.2.101:8443/)이나 [`https://172.30.2.101:3000`](https://172.30.2.101:3000/) 같은 IP+포트 조합을 기억하는 건 솔직히 고문에 가깝습니다.

그래서 HAProxy를 사용하여 **도메인 기반 reverse proxy**를 구성하고, Ceph Dashboard에서 Grafana 연동까지 한 번에 정리해보겠습니다.

## 🤔 왜 HAProxy인가?

> HAProxy(High Availability Proxy)는 TCP/HTTP 기반의 오픈소스 로드 밸런서이자 프록시 서버입니다. 높은 성능과 안정성으로 인해 GitHub, Reddit, Twitter 등 대규모 서비스에서도 사용되고 있습니다.

우리 환경에서 HAProxy를 선택한 이유는 다음과 같습니다:

- **SNI 기반 라우팅**: 하나의 IP에서 도메인 이름만으로 여러 backend 서비스를 분리할 수 있음

- **SSL 종료(SSL Termination)**: HAProxy에서 SSL을 한 번에 처리하여 backend 서버의 부담을 줄임

- **로드 밸런싱**: Ceph 노드 3대에 대한 트래픽 분산 및 헬스 체크 지원

- **설정이 직관적**: Nginx에 비해 프록시 전용 설정이 깔끔하게 정리됨

> Nginx도 reverse proxy로 많이 사용되지만, HAProxy는 **프록시와 로드 밸런싱에 특화**되어 있어 복잡한 라우팅 규칙을 더 명확하게 표현할 수 있습니다. 특히 SNI 기반 라우팅이나 TCP 레벨 프록시가 필요한 경우 HAProxy가 더 적합합니다.

## 🏗️ 아키텍처 개요

전체 구성을 간단히 정리하면 이렇습니다:

모든 트래픽은 HAProxy가 **443 포트(HTTPS)**에서 수신하고, SNI(Server Name Indication)를 기반으로 적절한 backend로 라우팅합니다. HTTP(80)로 들어오는 요청은 자동으로 HTTPS로 리다이렉트됩니다.

![HAProxy SNI 라우팅과 SSL Termination 흐름](https://blog.pieroot.xyz/api/image-proxy?id=9c5042cf-1720-4155-b035-bb5fcd20f903&kind=s3&pageId=305067c0-15d0-80d0-bc6a-e861f0992d0a&source=block&blockId=e59a5fb7-cc38-459e-83fe-bdd3ac487465)

> **요청 흐름을 한 번에 보면**
> 
> 1. 사용자는 `https://ceph.dmslab.re.kr`처럼 도메인으로 접속한다
> 
> 1. TLS 핸드셰이크 단계에서 클라이언트가 SNI 값으로 접속 도메인을 HAProxy에 전달한다
> 
> 1. HAProxy는 `/etc/haproxy/ssl/`의 인증서를 선택하고 SSL Termination을 수행한다
> 
> 1. 복호화된 HTTP 요청을 SNI 조건에 맞는 backend로 전달한다
> 
> 1. backend 응답은 다시 HAProxy를 거쳐 사용자에게 HTTPS로 반환된다

## ⚙️ HAProxy 설정

### Frontend 설정

HAProxy의 frontend는 모든 들어오는(incoming) 트래픽을 받아서 적절한 backend로 보내주는 관문 역할을 합니다.

```bash
frontend dmslab_frontend
    mode http
    bind *:80
    bind *:443 ssl crt /etc/haproxy/ssl/
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Forwarded-Ssl on if { ssl_fc }
    option forwardfor
    
    timeout client 30m
    redirect scheme https code 301 if !{ ssl_fc }
    
    # Ceph 관련 backend 라우팅
    use_backend ceph_backend if { ssl_fc_sni ceph.dmslab.re.kr }
    use_backend ceph_dashboard_backend if { ssl_fc_sni gf.ceph.dmslab.re.kr }
    use_backend grafana_backend if { ssl_fc_sni gf.dmslab.re.kr }
    use_backend rgw_backend if { ssl_fc_sni -i -m reg ^(([a-z0-9-]+\.)?s3\.dmslab\.re\.kr)$ }
    
    # 기타 서비스들...
    default_backend gitlab_backend
```

여기서 핵심 설정을 하나씩 살펴보겠습니다:

#### `bind *:443 ssl crt /etc/haproxy/ssl/`

HAProxy에서 **SSL 종료(SSL Termination)**를 수행합니다. `/etc/haproxy/ssl/` 디렉토리에 있는 인증서 파일(`.pem`)을 자동으로 로드하여 각 도메인에 맞는 인증서를 매칭합니다.

> HAProxy의 SSL 인증서는 **PEM 형식**이어야 하며, 하나의 `.pem` 파일에 인증서 체인 + 개인키가 모두 포함되어야 합니다. Let's Encrypt를 사용하는 경우 `fullchain.pem`과 `privkey.pem`을 합쳐서 사용하면 됩니다:
> 
> `cat fullchain.pem privkey.pem > /etc/haproxy/ssl/`[`dmslab.re.kr`](http://dmslab.re.kr/)`.pem`

#### X-Forwarded 헤더 설정

```bash
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-For %[src]
http-request set-header X-Forwarded-Ssl on if { ssl_fc }
```

reverse proxy를 거치면 backend 서버 입장에서는 **원래 클라이언트의 정보를 알 수 없게** 됩니다. 이 헤더들은 원본 요청의 정보(클라이언트 IP, 프로토콜, 포트 등)를 backend에 전달해주는 역할을 합니다.

이게 없으면 Ceph Dashboard 로그에 모든 접속이 HAProxy 서버 IP에서 온 것으로 기록되어 디버깅이 상당히 곤란해집니다. ~~(누가 접속한 건지 알 수가 없어요 😭)~~

#### SNI 기반 라우팅

```bash
use_backend ceph_backend if { ssl_fc_sni ceph.dmslab.re.kr }
use_backend rgw_backend if { ssl_fc_sni -i -m reg ^(([a-z0-9-]+\.)?s3\.dmslab\.re\.kr)$ }
```

`ssl_fc_sni`는 TLS 핸드셰이크 과정에서 클라이언트가 전달하는 **SNI(Server Name Indication)** 값을 기반으로 라우팅합니다. 즉, 같은 IP:443으로 들어와도 **도메인 이름이 다르면 다른 backend로** 보낼 수 있습니다.

특히 RADOS Gateway의 경우, [`s3.dmslab.re.kr`](http://s3.dmslab.re.kr/)뿐만 아니라 [`bucket-name.s3.dmslab.re.kr`](http://bucket-name.s3.dmslab.re.kr/) 형태의 서브도메인도 처리해야 하므로 **정규식 매칭**(`-m reg`)을 사용했습니다.

---

### Backend 설정

이제 각 서비스별 backend 설정을 살펴보겠습니다.

> **Backend에서 특히 중요한 설정**
> 
> - `balance roundrobin`: 정상 상태의 backend 서버로 요청을 순차 분산한다
> 
> - `check`: HAProxy가 backend 상태를 주기적으로 확인한다
> 
> - `ssl verify none`: backend가 self-signed 인증서를 사용할 때 TLS 검증 실패를 피한다
> 
> - `option httpchk GET /`: HTTP 헬스 체크 경로를 지정해 장애 노드를 자동으로 제외한다

#### Ceph Dashboard Backend (포트 8443)

```bash
backend ceph_backend
    mode http
    option httpchk GET /
    balance roundrobin
    server ceph1 172.30.2.101:8443 ssl verify none check
    server ceph2 172.30.2.102:8443 ssl verify none check
    server ceph3 172.30.2.103:8443 ssl verify none check
```

Ceph Dashboard는 기본적으로 **8443 포트에서 HTTPS**로 동작합니다. backend 서버에 `ssl verify none`을 지정하여 self-signed 인증서를 사용하는 Ceph Manager에 연결할 수 있도록 합니다.

> **주의:** Ceph Dashboard는 **활성 Manager(active mgr)**에서만 제대로 응답합니다. standby Manager에 접속하면 기본적으로 active Manager로 HTTP 303 리다이렉트가 발생합니다. HAProxy 뒤에 있을 때는 이 리다이렉트가 내부 IP를 반환하여 문제가 될 수 있으므로, standby 동작을 에러로 변경하는 것을 권장합니다:
> 
> `ceph config set mgr mgr/dashboard/standby_behaviour "error"`
> 
> 이렇게 하면 HAProxy의 헬스 체크가 standby 노드를 자동으로 제외하게 됩니다.

#### Grafana Backend (포트 3000)

```bash
backend ceph_dashboard_backend
    mode http
    option httpchk GET /
    balance roundrobin
    server ceph1 172.30.2.101:3000 ssl verify none check
    server ceph2 172.30.2.102:3000 ssl verify none check
    server ceph3 172.30.2.103:3000 ssl verify none check
```

Ceph에서 cephadm으로 배포된 Grafana는 각 Manager 노드의 **3000번 포트**에서 실행됩니다. Ceph Dashboard Backend와 동일하게 3개 노드에 대해 roundrobin 로드 밸런싱을 적용합니다.

#### RADOS Gateway Backend (포트 80)

```bash
backend rgw_backend
    mode http
    balance roundrobin
    server ceph1 172.30.2.101:80 check
    server ceph2 172.30.2.102:80 check
    server ceph3 172.30.2.103:80 check
```

RADOS Gateway는 S3 호환 오브젝트 스토리지 서비스입니다. 기본적으로 HTTP(80)로 동작하며, SSL은 HAProxy에서 종료합니다.

---

### HAProxy 적용

#### 1. 설정 파일 편집

```bash
sudo vi /etc/haproxy/haproxy.cfg
```

위의 frontend와 backend 설정을 추가합니다.

#### 2. 설정 검증

설정 파일에 문법 오류가 없는지 반드시 검증합니다:

```bash
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
```

> `Configuration file is valid` 메시지가 나오면 정상입니다. 이 단계를 건너뛰고 바로 재시작했다가 설정 오류로 HAProxy가 죽으면... 모든 서비스가 한 번에 접근 불가능해집니다. 꼭 검증하세요! 🙏

#### 3. HAProxy 재시작

```bash
sudo systemctl restart haproxy
sudo systemctl status haproxy
```

## 🔗 Ceph Dashboard에 Grafana 연동

HAProxy까지 세팅했으면 이제 핵심인 **Ceph Dashboard ↔ Grafana 연동**을 진행합니다.

Ceph Dashboard는 Grafana 차트를 **iframe**으로 임베딩하여 클러스터 모니터링 정보를 시각적으로 표시합니다. 이 연동이 정상적으로 작동하려면 **두 가지 연결**이 모두 필요합니다:

1. **Backend 연결** (`grafana-api-url`): Ceph Manager가 Grafana 대시보드의 존재 여부를 확인하는 용도

1. **Frontend 연결** (`grafana-frontend-api-url`): 사용자의 브라우저가 실제로 Grafana에 접근하는 용도

> 왜 URL이 두 개나 필요할까요? 🤔
> 
> Ceph Manager(backend)는 내부 네트워크에서 Grafana에 직접 접근할 수 있지만, **사용자의 브라우저(frontend)는 외부 네트워크**에 있을 수 있습니다. HAProxy를 통해 접근하는 환경에서는 두 URL이 다를 수 있기 때문에, Ceph는 이를 분리하여 설정할 수 있도록 제공합니다.

```mermaid
flowchart LR
    Browser["User Browser"] --> Dashboard["Ceph Dashboard<br>ceph.dmslab.re.kr"]
    Dashboard --> Iframe["Grafana iframe"]
    Iframe -->|grafana-frontend-api-url| HAProxy["HAProxy<br>:443"]
    HAProxy --> Grafana["Grafana<br>gf.ceph.dmslab.re.kr:3000"]

    CephMgr["Ceph Manager<br>backend"] -->|grafana-api-url| HAProxy
    HAProxy --> Grafana
```

> **Grafana 연동에서 헷갈리기 쉬운 지점**
> 
> - `grafana-api-url`: Ceph Manager가 Grafana API를 확인하는 **서버 측 경로**
> 
> - `grafana-frontend-api-url`: 사용자의 브라우저가 iframe으로 Grafana를 불러오는 **클라이언트 측 경로**
> 
> - HAProxy 뒤에 있을 때는 두 경로가 같은 도메인을 바라볼 수도 있지만, 내부망/외부망이 분리된 환경에서는 다르게 설정될 수 있다

### Grafana 사전 설정

Ceph Dashboard에서 Grafana를 iframe으로 임베딩하려면, Grafana 쪽에서도 몇 가지 설정이 필요합니다.

#### allow\_embedding 활성화

Grafana 6.2.0 이상에서는 보안상의 이유로 **iframe 임베딩이 기본적으로 차단**되어 있습니다. Ceph Dashboard에서 Grafana 차트를 표시하려면 이 설정을 반드시 활성화해야 합니다.

Grafana 설정 파일(`/etc/grafana/grafana.ini` 또는 cephadm 환경에서는 커스텀 설정)에서:

```bash
[security]
allow_embedding = true
```

#### 익명 접근 허용

Ceph Dashboard의 iframe에서 Grafana에 접근할 때 별도의 로그인 없이 차트를 볼 수 있도록 익명 접근을 설정합니다:

```bash
[auth.anonymous]
enabled = true
org_name = Main Org.
org_role = Viewer
```

~~(이 설정을 안 하면 Grafana 로그인 창이 iframe 안에서 뜨는 아름다운 광경을 볼 수 있습니다)~~

### Grafana URL 설정

#### 현재 설정 확인

먼저 현재 Grafana 설정 상태를 확인합니다:

```bash
ceph dashboard get-grafana-api-url
ceph dashboard get-grafana-frontend-api-url
```

#### Backend API URL 설정

Ceph Manager가 Grafana의 존재를 확인할 때 사용하는 URL입니다. 내부 네트워크에서 접근 가능한 주소를 지정합니다:

```bash
ceph dashboard set-grafana-api-url https://gf.ceph.dmslab.re.kr
```

#### Frontend API URL 설정

사용자의 브라우저가 Grafana에 접근할 때 사용하는 URL입니다. HAProxy를 통해 접근하는 외부 도메인을 지정합니다:

```bash
ceph dashboard set-grafana-frontend-api-url https://gf.ceph.dmslab.re.kr
```

우리 환경에서는 HAProxy가 모든 트래픽을 중계하므로 두 URL이 동일하지만, **내부/외부 네트워크가 분리된 환경**에서는 다른 값을 사용할 수 있습니다.

> `grafana-frontend-api-url`을 설정하지 않으면 `grafana-api-url` 값이 자동으로 사용됩니다. 하지만 명시적으로 설정해두는 것을 권장합니다. `grafana-api-url`은 **cephadm에 의해 자동으로 변경될 수 있기 때문**입니다.

#### SSL 인증서 검증 비활성화 (self-signed 인증서 사용 시)

self-signed 인증서를 사용하는 경우, Ceph Manager가 Grafana에 접근할 때 SSL 검증 오류가 발생할 수 있습니다:

```bash
ceph dashboard set-grafana-api-ssl-verify False
```

#### 추가 Grafana 설정 (선택사항)

필요한 경우 Grafana 접근 계정도 설정할 수 있습니다:

```bash
# Grafana API 사용자 설정
ceph dashboard set-grafana-api-username admin

# Grafana API 비밀번호 설정
ceph dashboard set-grafana-api-password <password>
```

#### 설정 확인

변경된 모든 설정을 한 번에 확인합니다:

```bash
ceph dashboard get-grafana-api-url
ceph dashboard get-grafana-frontend-api-url
ceph dashboard get-grafana-api-ssl-verify
```

## ✅ 검증

모든 설정을 완료했으면 실제로 잘 동작하는지 확인해봐야겠죠.

### 서비스 접근 테스트

각 도메인으로 접속이 되는지 확인합니다:

### Ceph Dashboard에서 Grafana 임베딩 확인

1. Ceph Dashboard([`https://ceph.dmslab.re.kr`](https://ceph.dmslab.re.kr/))에 로그인

1. **Cluster → Hosts** 또는 **Performance** 메뉴로 이동

1. Grafana 차트가 iframe 내에 정상적으로 표시되는지 확인

Grafana 차트가 보이지 않는다면 **브라우저 개발자 도구(F12)**의 Console 탭에서 에러 메시지를 확인하세요. 대부분 mixed content 문제이거나 `X-Frame-Options` 관련 이슈입니다.

### HAProxy 상태 확인

```bash
# HAProxy 프로세스 상태
sudo systemctl status haproxy

# Backend 서버 상태 확인 (HAProxy stats socket 사용)
echo "show stat" | sudo socat stdio /var/run/haproxy.sock
```

## 🔧 트러블슈팅

### Grafana 차트가 표시되지 않는 경우

이건 정말 다양한 원인이 있을 수 있습니다. 순서대로 확인해보세요:

1. **Grafana ****`allow_embedding`**** 설정 확인** — 이게 `false`면 iframe이 아예 차단됩니다

1. **anonymous auth 설정 확인** — 로그인 창이 iframe 안에 뜨는 경우

1. **`grafana-frontend-api-url`**** 확인** — 브라우저에서 접근 가능한 URL이어야 합니다

1. **SSL 인증서 확인** — 브라우저가 self-signed 인증서를 차단할 수 있습니다

```bash
# Grafana URL 직접 접근 테스트 (브라우저에서)
# https://gf.ceph.dmslab.re.kr 에 직접 접속하여 인증서를 수락합니다

# Ceph Dashboard 설정 전체 확인
ceph dashboard get-grafana-api-url
ceph dashboard get-grafana-frontend-api-url
ceph dashboard get-grafana-api-ssl-verify
```

> self-signed 인증서를 사용하는 경우, **Grafana URL을 브라우저에서 직접 한 번 접속**하여 보안 예외를 수락해야 합니다. 그래야 Ceph Dashboard 내의 iframe에서도 정상적으로 로드됩니다.

### SSL 인증서 문제

SSL 관련 오류가 발생하는 경우:

```bash
# 인증서 파일 확인
sudo ls -la /etc/haproxy/ssl/

# 특정 도메인의 인증서 유효성 검사
openssl s_client -connect gf.ceph.dmslab.re.kr:443 -servername gf.ceph.dmslab.re.kr
```

### Backend 연결 실패

HAProxy에서 backend 서버로 연결이 안 되는 경우:

```bash
# HAProxy 로그 확인
sudo tail -f /var/log/haproxy.log

# 직접 연결 테스트
curl -k https://172.30.2.101:3000    # Grafana
curl -k https://172.30.2.101:8443    # Ceph Dashboard

# 방화벽 확인
sudo iptables -L -n | grep -E '3000|8443'
```

### Ceph Manager 로그 확인

Grafana 연동에 문제가 있을 때 Ceph Manager 로그를 확인하면 원인을 빠르게 찾을 수 있습니다:

```bash
# Ceph Manager 로그 확인
ceph log last ceph-mgr

# Dashboard 모듈 재시작 (설정 적용이 안 될 때)
ceph mgr module disable dashboard
ceph mgr module enable dashboard
```

## 정리

이 문서에서는 HAProxy를 사용하여 Ceph 클러스터의 여러 서비스에 대한 reverse proxy를 구성하고, Ceph Dashboard에서 Grafana를 연동하는 전체 과정을 다루었습니다.

처음에는 단순히 HAProxy 설정만 하면 될 줄 알았는데, Grafana의 `allow_embedding` 설정, `frontend-api-url`과 `api-url`의 차이, standby Manager의 리다이렉트 문제 등 생각보다 신경 쓸 부분이 많았습니다.

#### 핵심 정리

✅ **HAProxy Frontend**: SNI 기반 라우팅으로 도메인별 서비스 분리 및 SSL 종료

✅ **Backend 설정**: Ceph 3개 노드에 roundrobin 로드 밸런싱 + 헬스 체크

✅ **standby 동작 변경**: `standby_behaviour`를 `error`로 설정하여 HAProxy와의 호환성 확보

✅ **Grafana 임베딩**: `allow_embedding = true` 및 anonymous auth 설정 필수

✅ **URL 이중 설정**: `grafana-api-url`(backend용)과 `grafana-frontend-api-url`(브라우저용) 분리

✅ **SSL 검증**: self-signed 인증서 사용 시 `set-grafana-api-ssl-verify False` 설정

#### 참고 자료

- [Ceph Dashboard 공식 문서](https://docs.ceph.com/en/latest/mgr/dashboard/)

- [Grafana - Run behind a reverse proxy](https://grafana.com/tutorials/run-grafana-behind-a-proxy/)

- [Red Hat Ceph Storage - Monitoring HAProxy on the dashboard](https://docs.redhat.com/en/documentation/red_hat_ceph_storage/5/html/dashboard_guide/monitor-the-cluster-on-the-ceph-dashboard_dash)

- [HAProxy 공식 문서](https://www.haproxy.org/#docs)
