> For the complete documentation index, see [llms.txt](https://docs.tibero.com/tmaxopensql/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tibero.com/tmaxopensql/administration/openproxy/virtual-ip-redundancy.md).

# 가상 IP 및 이중화 구성 관리

## **개요**

OpenProxy 에서 제공하는 가상 라우터 다중화 프로토콜 (VRRP) 기반의 가상 IP (Virtual IP) 설정 및 관제 기능에 대한 설명과 구성 방법에 대하여 서술합니다.

### **가상 IP 관리 기능**

OpenProxy 에서는 가상 라우터 다중화 프로토콜 (VRRP) 기반으로 Virtual IP 를 관리하여 한 OpenProxy 노드가 예기치 않게 종료되어도 같은 가상 IP를 이용해 서비스 고가용성을 유지할 수 있습니다. 이때 PostgreSQL 커넥션 Pooling 및 로드밸런싱 기능에 영향을 미치지 않도록 별도의 비동기 런타임을 통하여 가상 라우터 이벤트를 처리합니다.

지정한 네트워크 인터페이스의 Multicast 주소로 Advertisement Packet을 보내 노드 간 통신하는 Multicast 방식, 혹은 다른 모든 Peer 노드 (OpenProxy가 구성된 다른 노드) 들의 IPv4 주소를 설정하여 통신하는Unicast 방식으로 동작합니다.

* Advertisement Packet의 송/수신은 Linux Raw (L3; IP) 소켓을 열고 네트워크의 Multicast 주소 `224.0.0.18` 에 바인딩 혹은 자신이 구동중인 노드의 IPv4 주소에 바인딩 (Unicast 옵션을 활성화한 경우) 함으로써 이루어집니다.
* 가상 IP 점유 / 해제는 Linux NetLink 소켓을 열고 커널에 `RTM_NEWADDR` 혹은 `RTM_DELADDR` 메세지를 직접 보내는 방식으로 이루어집니다.

가상 IP 기능 활성화를 위해서는 OpenProxy 프로세스에 Linux 시스템 권한 `cap_net_admin` 과 `cap_net_raw` 설정이 필요합니다.

### **Patroni 연동 기능**

Patroni를 이용해 구성된 PostgreSQL 클러스터에 대해 OpenProxy에서 접속할 PostgreSQL 서버의 Role을 정의할 필요 없이 Patroni의 REST API를 통한 Topology Discovery를 수행하도록 지정할 수 있습니다.

* 각 Pool의 Shard 마다 `use_patroni` (Boolean) 키를 true로 설정해 활성화할 수 있으며 정의되어 있지 않은 경우는 해당 기능이 비활성화됩니다.
* Shard 마다 필요한 경우 `patroni_port` 변수를 추가로 설정해 기본 Port `8008` 이 아닌 다른 Port를 리스닝하고 있는 Patroni 서버와도 연동할 수 있습니다.
* Patroni 서버와 연동해 클러스터 토폴로지를 가져오는 경우 Shard의 Server 마다 설정한 PostgreSQL에 접속하기 위한 Port 번호와 PostgreSQL 노드 Role (`Primary` 혹은 `Replica`) 은 무시되며 Patroni REST API 서버가 응답한 값이 사용됩니다.

Patroni와 연동되도록 설정된 Pool은 설정된 `renew_interval` (밀리초 단위, 기본값: 5000) 주기마다 Patroni의 REST API 서버에 요청을 보내, PostgreSQL 서버의 접속 정보와 Primary/Replica 역할(Role) 정보를 가져옵니다.

* Patroni 서버로 보내는 HTTP 요청은 기본적으로 1초의 타임아웃을 가집니다. Shard 내 서버에 요청을 보낸 후, 타임아웃 시간 내에 응답을 받지 못하거나 응답 파싱(Parsing)에 실패할 경우, 다음 서버로 요청을 보내는 방식으로 동작합니다.
* 모든 서버에 질의했음에도 응답을 정상적으로 처리하지 못한 경우, 서버의 역할(Role)을 업데이트하지 않습니다. 이로 인해 트랜잭션 풀링 모드에서의 쿼리 파싱(Query Parsing) 및 읽기-쓰기 분리(Read-Write Splitting) 기능이 정상적으로 동작하지 않을 수 있습니다.

***

## **구성**

### **가상 라우터**

OpenProxy 설정파일인 `openproxy.toml` 에 `[general.virtual_router]` 항목을 설정하여 가상 라우터 기능을 활성화할 수 있습니다.

```toml
[general.virtual_router]
interface = "eno1"
router_id = 50
priority = 150
advert_int = 3
vip_addresses = [ "192.168.0.200/24" ]
pre_promote_script = "/home/opensql/startup.sh"
pre_demote_script = "/home/opensql/cleanup.sh"
unicast_peers = [ "192.168.0.7", "192.168.0.8" ]
```

해당 항목이 정의되면 OpenProxy 프로세스 시작 시 별도 런타임을 통해 가상 라우터 상태 머신 (State Machine) 이 활성화되어 가상 IP 관련 이벤트를 처리합니다.

* `interface` 항목은 다른 OpenProxy 노드들과 통신할 수 있는(즉 실제 물리 네트워크 인터페이스 카드를 통하여 연결된) 이 노드의 네트워크 인터페이스 이름을 지정합니다.
* `router_id` 항목은 이 네트워크에서 가상 라우터 클러스터를 구분할 식별자로 1 \~ 255 사이의 값을 가집니다. 가상 IP MASTER 선출에 참여할 다른 OpenProxy 노드들과 같은 값을 가져야 합니다.
* `priority` 항목은 MASTER 가상 라우터 선출에 있어 이 노드가 가질 우선순위 값으로 1 \~ 255 사이의 값을 가집니다. 우선순위 값이 255 인 노드는 시작과 동시에 BACKUP 상태가 아닌 MASTER 상태로 가상 IP 점유를 시도하게 됩니다. 이 외에는 Advertisement 패킷을 통해 전달되는 우선순위 값을 인식하여 가장 높은 노드가 MASTER 상태가 됩니다. 노드마다 다르게 설정하는 것을 권장합니다.
* `advert_int` 항목은 MASTER 노드가 자신의 상태 및 우선순위 값을 네트워크 내에 전파하는 Advertisement 패킷을 전달할 주기로 단위는 초 (second) 이며 1 \~ 255 사이의 값을 가집니다. 클러스터 내의 모든 노드들이 같은 값을 가져야 하며 Advertisement 패킷을 3번 연속으로 수신하지 못하면 다른 BACKUP 노드들이 MASTER 선출을 시작합니다.
* `vip_addresses` 는 MASTER 노드가 점유할 가상 IP들의 목록으로 쉼표 `,` 로 구분되는 IPv4 주소 및 네트워크의 비트마스크 길이를 포함한 형태로 주어져야 합니다.
* `pre_promote_script` (Optional) 은 BACKUP → MASTER 승격이 일어날 경우 이 노드에서 실행할 명령어를 지정하는 항목입니다. 특정 클라우드 벤더 환경에서는 노드의 네트워크 인터페이스가 가상화 되어 있어 가상 IP 등록을 위해서는 노드의 Secondary IP 등록 이외에 추가적인 작업이 필요할 수 있습니다.
* `pre_demote_script` (Optional) 은 MASTER → BACKUP 강등이 일어날 경우 이 노드에서 실행할 명령어를 지정하는 항목입니다.
* `unicast_peers` (Optional) 은 Multicast 방식이 아닌 Unicast 방식으로 다른 OpenProxy 노드에 Advertisement 패킷을 전달하기 위한 옵션입니다. 가상 IP MASTER 선출에 참여할 다른 OpenProxy 노드들의 IPv4 주소를 쉼표 `,` 로 구분되는 배열로 지정합니다.

### **Patroni REST API 연동**

OpenProxy 설정파일인 `openproxy.toml` 에 Patroni REST API와 연동하고자 하는 Pool이 정의된 섹션의 Shard 정의에 `use_patroni` 키를 설정하여 토폴로지 Discovery 기능을 활성화합니다.

* `[general]` 섹션의 `renew_interval` 값을 설정하여 `openproxy.toml` 파일을 읽어오거나 Patroni 서버에 질의하여 PostgreSQL 서버의 Role을 업데이트 할 주기를 조정할 수 있습니다.
* Port 번호와 Role 값은 OpenProxy Connection 생성 시에 실제로 참조되는 값은 아니지만 하위 호환성을 위해 입력되어야 합니다.

```toml
[general]
renew_interval = 5000  ## 설정파일을 읽어 Config을 업데이트하거나, Patroni 서버에 질의하여
                       ## Postgres 서버 Role을 업데이트 할 주기를 설정할 수 있습니다.
                       ## 단위는 밀리초 (milliseconds) 이며 기본값은 5000 입니다.

[pools.my_pool]

[pools.my_pool.shards.0]
servers = [
  [
    "192.168.0.8",    ## Patroni REST API 서버가 구동중인 호스트들의 IPv4 주소를 입력합니다.
    5432,             ## PostgreSQL 서버의 Port 번호로 Patroni 연동 시에는 무시됩니다.
    "Auto",           ## PostgreSQL 노드의 Role 값으으로 Patroni 연동 시에는 무시됩니다.
  ],
  [
    "192.168.0.9",
    5432,
    "Auto",
  ],
  [
    "192.168.0.10",
    5432,
    "Auto",
  ]
]
database = "postgres"
use_patroni = true
patroni_port = "8008"    ## Patroni의 HTTP REST API 서버의 Port 번호를 지정합니다.
                       ## 비어있는 경우 기본값 8008 이 사용됩니다.
```

***

## **실행**

가상 라우터 기능을 활성화하기 위해서는 OpenProxy 프로세스를 `root` 사용자 권한으로 실행하거나 `cap_net_raw`, `cap_net_admin` 권한이 부여되어야 합니다.

* `cap_net_raw` 는 `RAW` 타입 네트워크 소켓을 열기 위해 필요합니다.
* `cap_net_admin` 은 네트워크 인터페이스에 Secondary IP 추가 / 삭제를 위해 필요합니다.

아래와 같이 Linux `setcap` 을 이용해 실행 바이너리 `openproxy` 에 권한을 부여합니다.

```bash
$ sudo setcap 'cap_net_raw=eip cap_net_admin=eip' openproxy
```

`systemd` 서비스로 설정하여 구동하는 경우 `.service` 파일에 아래 옵션을 추가하여 권한을 부여합니다.

```systemd
[Service]
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
```

###


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tibero.com/tmaxopensql/administration/openproxy/virtual-ip-redundancy.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
