> 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/installation/binary/opensql.md).

# OpenSQL 통합 설치

## 1. 개요

OpenSQL 통합 설치는 `OpenSQL-Installer` 를 이용합니다.

`OpenSQL-Installer`는 OpenSQL 클러스터를 설치하는 Python 기반 인스톨러입니다. 설치 방식은 두 가지 중 하나를 선택합니다.

| 방식        | 스크립트                          | 설명                           |
| --------- | ----------------------------- | ---------------------------- |
| **로컬 설치** | `opensql_local_installer.py`  | 각 노드에 직접 접속하여 개별 실행          |
| **원격 설치** | `opensql_remote_installer.py` | 제어 서버 1대에서 전체 노드에 SSH로 일괄 설치 |

```
[로컬 설치]                             
node1에서 직접 실행                     
  python3 opensql_local_installer.py          
                                              
node2에서 직접 실행                            
  python3 opensql_local_installer.py           

node3에서 직접 실행
  python3 opensql_local_installer.py
  
  
[원격 설치]
python3 opensql_remote_installer.py
│  SSH
├──────▶ node1
├──────▶ node2
└──────▶ node3
```

### 파일 구조

```
opensql-installer/
├── opensql_local_installer.py   # 로컬 설치 스크립트
├── opensql_remote_installer.py  # 원격 설치 스크립트
├── config/                      # 설정 디렉토리
│   ├── common.env               # 공통 설정 (로컬/원격 공통, 필수 수정)
│   ├── remote.env               # 원격 설치 전용 설정 (원격 설치 시 필수 수정)
│   ├── etcd.config.env          # etcd 전용 옵션
│   ├── patroni.config.env       # Patroni/PostgreSQL 전용 옵션
│   ├── openproxy.config.env     # OpenProxy 전용 옵션
│   ├── etcd.env                 # etcd 환경변수 템플릿 
│   ├── etcd.service             # etcd 서비스 파일 템플릿 
│   ├── patroni.yml              # Patroni 설정 템플릿 
│   ├── openproxy.toml           # OpenProxy 설정 템플릿 
│   └── openproxy.service        # OpenProxy 서비스 파일 템플릿 
├── licenses/                    # 노드별 라이선스 XML 배치 디렉토리 (필수 생성)
│   ├── node1_license.xml        (예시)
│   └── ...
└── src/                         # 공통 모듈 (수정 불필요)
    ├── config.py
    ├── functions.py
    └── package_requirements.json
```

***

## 2. 아키텍처 및 구성 요소

### 컴포넌트 목록

설치되는 컴포넌트는 라이선스 에디션에 따라 자동으로 결정됩니다. 수동으로 선택하는 별도 설정은 없습니다.

| 컴포넌트            | 역할                         |
| --------------- | -------------------------- |
| `postgresql`    | OpenSQL 데이터베이스 엔진          |
| `etcd`          | 분산 키-값 저장소 (클러스터 상태 관리)    |
| `patroni`       | PostgreSQL HA 관리 (자동 페일오버) |
| `openproxy`     | 커넥션 풀 및 읽기/쓰기 분리 프록시       |
| `postgis`       | 공간 데이터 확장                  |
| `pg_hint_plan`  | 쿼리 힌트 지원                   |
| `pg_cron`       | 스케줄러 확장                    |
| `pgvector`      | 벡터 유사도 검색                  |
| `pgvectorscale` | pgvector 성능 확장             |
| `credcheck`     | 비밀번호 정책 확장                 |
| `system_stats`  | 시스템 리소스 모니터링               |
| `pgaudit`       | SQL 감사 로그                  |
| `opencrypto`    | 암호화 확장                     |
| `o2`            | Oracle과의 호환성을 위한 확장        |
| `pg_profile`    | 성능 프로파일링                   |
| `tibero_fdw`    | Tibero FDW                 |

### 클러스터 모드별 노드 역할

| 모드                | node1                           | node2                           | node3                           |
| ----------------- | ------------------------------- | ------------------------------- | ------------------------------- |
| **single**        | PG + Patroni + etcd + OpenProxy | -                               | -                               |
| **3node**         | PG + Patroni + etcd             | PG + Patroni + etcd + OpenProxy | PG + Patroni + etcd + OpenProxy |
| **2node-witness** | PG + Patroni + etcd + OpenProxy | PG + Patroni + etcd + OpenProxy | etcd (witness only)             |

***

## 3. 사전 요구사항

### 3.1 지원 OS

| OS                       | 버전           |
| ------------------------ | ------------ |
| Rocky Linux              | 8.x, 9.x     |
| RHEL                     | 8.x, 9.x     |
| Oracle Linux / AlmaLinux | 8.x, 9.x     |
| Ubuntu                   | 22.04, 24.04 |

### 3.2 인스톨러 실행 환경 요구사항

#### 로컬 설치 (`opensql_local_installer.py`)

| 도구        | 용도       |
| --------- | -------- |
| `python3` | 스크립트 실행  |
| `sudo` 권한 | 설치 작업 수행 |

#### 원격 설치 (`opensql_remote_installer.py`)

제어 서버에서 실행하므로 SSH 관련 도구가 필요합니다.

| 도구           | 용도                    |
| ------------ | --------------------- |
| `python3`    | 스크립트 실행               |
| `ssh`, `scp` | 원격 노드 접속              |
| `ssh-keygen` | SSH 키 자동 생성           |
| `sshpass`    | 비밀번호 기반 SSH 키 등록 시 필요 |

```bash
# Rocky Linux / RHEL 설치 예시
dnf install -y openssh-clients python3 sshpass
```

### 3.3 각 노드 필수 OS 패키지

인스톨러가 원격/로컬 노드의 필수 패키지를 자동으로 검증합니다.

* [사전확인사항](/tmaxopensql/installation/prerequisites.md)을 확인하여 필수 패키지 설치해야 합니다.

> **자동 설치**: `common.env`에서 `AUTO_INSTALL_PREREQS=true`로 설정하면 인스톨러가 누락된 필수 패키지를 자동으로 설치합니다.

### 3.4 라이선스 파일

라이선스 XML은 `opensql-installer/licenses/` 하위에 배치합니다. 인스톨러가 설치 중 각 노드로 복사하고 `OPENSQL_LICENSE_PATH`를 자동으로 설정합니다.

```
opensql-installer/
└── licenses/
    ├── node1_license.xml
    ├── node2_license.xml   (3node, 2node-witness)
    └── node3_license.xml   (3node)
```

모드별 필수 라이선스 개수:

| 모드              | 필요 개수 | 대상 노드               |
| --------------- | ----- | ------------------- |
| `single`        | 1개    | node1               |
| `3node`         | 3개    | node1, node2, node3 |
| `2node-witness` | 2개    | node1, node2        |

### 3.5 네트워크 (원격 설치 시)

* 제어 서버 → 각 노드: SSH 접속 가능해야 합니다.
* 인스톨러가 SSH 키(`~/.ssh/id_rsa_opensql`)를 자동 생성하고 각 노드에 등록합니다.
* 키 등록 시 최초 1회 SSH 비밀번호가 필요합니다.

### 3.6 sudo 권한

* **로컬 설치**: `opensql_local_installer.py`를 실행하는 사용자에게 `sudo` 권한이 필요합니다.
* **원격 설치**: 각 노드의 SSH 접속 사용자에게 `sudo` 권한이 필요합니다.

***

## 4. 패키지 압축 해제

인스톨러 실행 전, OpenSQL 배포 패키지(`.tar.gz`)를 먼저 압축 해제해야 합니다.

```bash
# 예시: Rocky Linux 8.10용 패키지
tar -xzf Tmax_OpenSQL_3.18.1.3_rockylinux8.10_buildtime20260223.tar.gz
```

압축을 풀면 `Tmax_OpenSQL_*` 형태의 디렉토리가 생성되며, 그 안에 `opensql-installer/`가 포함됩니다.

```
Tmax_OpenSQL_3.18.1.3_rockylinux8.10_buildtime20260223/
└── opensql-installer/
    ├── opensql_local_installer.py
    ├── opensql_remote_installer.py
    └── config/
        ├── common.env       ← 로컬/원격 공통 설정 (필수 수정)
        └── remote.env       ← 원격 설치 전용 설정 (원격 설치 시 필수 수정)
```

***

## 5. 설정 가이드

설정 파일은 **공통 설정**(`config/common.env`)과 **원격 설치 전용 설정**(`config/remote.env`)으로 나뉩니다. 컴포넌트별 세부 옵션은 각 `*.config.env`에서 관리합니다.

### 5.1 공통 설정 (`config/common.env`)

로컬 설치와 원격 설치 모두에서 사용하는 공통 설정입니다. 인스톨러가 `NODE1_IP ~ NODE3_IP`와 현재 서버 IP를 비교하여 자신의 역할을 자동으로 감지합니다.

#### 필수 설정

```bash
# 클러스터의 모든 노드 IP (현재 노드 포함)
# 인스톨러가 현재 서버 IP와 비교하여 자신의 노드를 감지합니다.
NODE1_IP="<node1_ip>"
NODE2_IP="<node2_ip>"
NODE3_IP="<node3_ip>"

# 현재 노드의 표시 이름 (로컬 설치 시 로그에 표시됨)
NODE_NAME="<node_name>"

# OpenSQL 홈 디렉토리 (절대 경로)
# 바이너리, 데이터, 설정, 로그가 이 경로 하위에 위치합니다.
# 패키지 설치 경로는 자동으로 $OPENSQL_HOME/install 로 설정됩니다.
OPENSQL_HOME="/path/to/opensql/home"
PG_HOME="/path/to/pg/home"
PG_DATA_DIR="/path/to/pg/data"

# 현재 노드에 적용할 라이선스 파일명 (licenses/ 디렉토리 기준)
# 로컬 설치 시 필수. 원격 설치 시에는 remote.env에서 노드별로 설정합니다.
LICENSE_NAME="<license file>"
```

#### 선택 설정

```bash
# 필수 패키지 자동 설치 (기본: false)
# AUTO_INSTALL_PREREQS=false

# OpenSQL 운영 유저/그룹 이름 (기본: opensql)
# OPENSQL_USER=opensql
# OPENSQL_GROUP=opensql

# OpenSQL 패키지 디렉토리 패턴 (기본: Tmax_OpenSQL*)
# OPENSQL_PACKAGE_PATTERN="Tmax_OpenSQL*"

# 2node-witness 모드: witness 노드의 etcd 포트 (필수)
# WITNESS_ETCD_CLIENT_PORT=2379
# WITNESS_ETCD_PEER_PORT=2380
```

* 설치 수행 시 `OPENSQL_USER`와 `OPENSQL_GROUP` 은 자동 생성되며 sudo 권한이 부여됩니다.
  * 이미 동명의 유저와 그룹이 존재한다면 무시가 됩니다.
  * 기존의 유저와 그룹을 이용하려면 해당 유저에 sudo 권한이 부여된 상태여야 정상 설치됩니다.

{% hint style="warning" %}
**노드별 실행 시 주의**

로컬 설치에서 각 노드에서 실행할 때 `NODE_NAME`과 `LICENSE_NAME`을 해당 노드에 맞게 변경한 후 실행합니다. `NODE1_IP ~ NODE3_IP`는 모든 노드에서 동일하게 설정합니다.
{% endhint %}

***

### 5.2 원격 설치 전용 설정 (`config/remote.env`)

제어 서버에서 `opensql_remote_installer.py`를 실행할 때 추가로 사용하는 설정입니다. 노드별 이름, SSH 접속 정보, 라이선스 파일명을 설정합니다. 노드 IP와 공통 설정은 `config/common.env`를 함께 참조합니다.

#### 필수 설정

```bash
# 노드 표시 이름 (로그에 표시됨)
NODE1_NAME="db-node1"
NODE2_NAME="db-node2"
NODE3_NAME="db-node3"

# SSH 접속 유저 (각 노드에 존재해야 함, sudo 권한 필요)
NODE1_SSH_USER="ec2-user"
NODE2_SSH_USER="ec2-user"
NODE3_SSH_USER="ec2-user"

# SSH 포트 (기본 22가 아닌 경우 명시)
NODE1_SSH_PORT="22"
NODE2_SSH_PORT="22"
NODE3_SSH_PORT="22"

# 노드별 OpenSQL 홈 디렉토리 (common.env의 OPENSQL_HOME과 다를 경우)
NODE1_OPENSQL_HOME="/home/opensql"
NODE2_OPENSQL_HOME="/home/opensql"
NODE3_OPENSQL_HOME="/home/opensql"

# 노드별 PG 디렉터리
NODE1_PG_HOME="/path/to/pg/"
NODE2_PG_HOME="/path/to/pg/"
NODE3_PG_HOME="/path/to/pg/"

# 노드별 PG_DATA 위치
NODE1_PG_DATA="/path/to/pg/data"
NODE2_PG_DATA="/path/to/pg/data"
NODE3_PG_DATA="/path/to/pg/data"

# 노드별 라이선스 파일명 (opensql-installer/licenses/ 기준)
# Patroni가 동작하는 노드에만 필요합니다.
# 2node-witness 모드의 witness(node3)는 비워둡니다.
NODE1_LICENSE_NAME="node1_license.xml"
NODE2_LICENSE_NAME="node2_license.xml"
NODE3_LICENSE_NAME="node3_license.xml"
```

#### 비밀번호 설정

비밀번호 지정은 우선순위 순으로 다음과 같습니다.

```
노드별 비밀번호 (NODE{n}_PASSWORD)
  > 전체 공통 비밀번호 (COMMON_PASSWORD)
    > 대화형 입력 (실행 시 프롬프트)
```

**방법 1: 실행 옵션으로 전달 (권장)**

비밀번호를 파일에 저장하지 않고 실행 시점에 옵션으로 전달합니다.

```bash
python3 opensql_remote_installer.py --password "my_password"
python3 opensql_remote_installer.py --node1-password "pw1" --node2-password "pw2" --node3-password "pw3"
```

**방법 2: remote.env에 기재**

```bash
# 전체 공통 비밀번호
# COMMON_PASSWORD=""

# 노드별 개별 지정
# NODE1_PASSWORD=""
# NODE2_PASSWORD=""
# NODE3_PASSWORD=""
```

{% hint style="warning" %}
**보안 권장사항**

비밀번호를 `remote.env`에 평문으로 저장하지 않으려면 빈 값으로 두세요. 인스톨러 실행 시 sudo 비밀번호를 대화형으로 입력받습니다.
{% endhint %}

#### 선택 설정

```bash
# SSH 키 경로 (기본: ~/.ssh/id_rsa_opensql)
# SSH_KEY="$HOME/.ssh/id_rsa_opensql"

# SSH 추가 옵션 (기본: StrictHostKeyChecking=no, ConnectTimeout=10)
# SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
```

***

### 5.3 컴포넌트별 옵션

각 컴포넌트의 포트, 사용자, 타임아웃 등 세부 옵션을 설정합니다. 기본값으로 충분한 경우 수정하지 않아도 됩니다.

| 파일                            | 대상 컴포넌트              |
| ----------------------------- | -------------------- |
| `config/etcd.config.env`      | etcd                 |
| `config/patroni.config.env`   | Patroni / PostgreSQL |
| `config/openproxy.config.env` | OpenProxy            |

#### etcd 옵션 (`config/etcd.config.env`)

```bash
# 2node-witness 모드에서 클러스터 식별자 (기본: c1)
# CLUSTER_ID=c1

# etcd 포트 (기본: 2379/2380)
# ETCD_CLIENT_PORT=2379
# ETCD_PEER_PORT=2380

# etcd 데이터/환경 파일 경로 (비워두면 자동 설정)
# ETCD_DATA_DIR=/etc/opensql/etcd_data
# ETCD_ENV_FILE=/etc/opensql/etcd.env
```

#### Patroni/PostgreSQL 옵션 (`config/patroni.config.env`)

```bash
# Patroni 인스턴스 이름 (기본: postgresql1/2/3)
# NODE1_PATRONI_NAME=postgresql1
# NODE2_PATRONI_NAME=postgresql2
# NODE3_PATRONI_NAME=postgresql3

# Patroni 설정 (기본값)
# PATRONI_SCOPE_BASE=opensql
# PATRONI_API_PORT=8008
# PATRONI_TTL=30
# PATRONI_LOOP_WAIT=10
# PATRONI_RETRY_TIMEOUT=10

# PostgreSQL 포트/계정 (기본값)
# PG_PORT=5432
# PG_SUPERUSER=postgres
# PG_SUPERUSER_PASSWORD=postgres
# PG_REPLICATION_USER=patroni_repl
# PG_REPLICATION_PASSWORD=patroni_repl
# PG_REWIND_USER=patroni_rewind
# PG_REWIND_PASSWORD=patroni_rewind
```

#### OpenProxy 옵션 (`config/openproxy.config.env`)

```bash
# OpenProxy 포트 (기본: 6432/6433)
# OPENPROXY_PORT=6432
# OPENPROXY_ADMIN_PORT=6433

# OpenProxy 동작 설정 (기본값)
# OPENPROXY_CONNECT_TIMEOUT=10000    # 클라이언트 연결 타임아웃 (밀리초)
# OPENPROXY_POOL_MODE=session        # 풀 모드: session / transaction
# OPENPROXY_DEFAULT_ROLE=primary     # 기본 라우팅: primary / replica
# OPENPROXY_QUERY_PARSER_ENABLED=false
# OPENPROXY_POOL_SIZE=10             # 풀당 최대 서버 연결 수
# OPENPROXY_DATABASE=postgres        # 연결 대상 데이터베이스명
```

### 5.4 Witness etcd 포트 설정 (2node-witness 전용, 필수)

2node-witness 모드에서는 node3(witness)의 etcd 포트를 반드시 명시해야 합니다.

`config/common.env`에 설정합니다:

```bash
WITNESS_ETCD_CLIENT_PORT=2379
WITNESS_ETCD_PEER_PORT=2380
```

**여러 클러스터 구성 시**: 클러스터마다 다른 포트로 충돌을 방지합니다.

| 클러스터   | CLUSTER\_ID | CLIENT\_PORT | PEER\_PORT |
| ------ | ----------- | ------------ | ---------- |
| 클러스터 1 | c1          | 2379         | 2380       |
| 클러스터 2 | c2          | 2479         | 2480       |

***

## 6. 클러스터 모드별 설치 가이드

### 6.1 single 모드 (단일 노드)

테스트 환경이나 단일 서버 환경에 적합합니다.

**공통 설정 (**`**config/common.env**`**):**

```bash
NODE1_IP="192.168.1.10"
NODE2_IP=""
NODE3_IP=""

NODE_NAME="db-single"
OPENSQL_HOME="/home/opensql"
LICENSE_NAME="node1_license.xml"
```

**원격 설치 전용 설정 (**`**config/remote.env**`**):**

```bash
NODE1_NAME="db-single"
NODE1_SSH_USER="ec2-user"
NODE1_SSH_PORT="22"
NODE1_OPENSQL_HOME="/home/opensql"
NODE1_LICENSE_NAME="node1_license.xml"
```

**실행:**

```bash
# 로컬 설치 (node1에서 직접)
cd opensql-installer
python3 opensql_local_installer.py --mode single

# 원격 설치 (제어 서버에서)
cd opensql-installer
python3 opensql_remote_installer.py --mode single
```

***

### 6.2 3node 모드 (3노드 대칭 구성, 권장)

운영 환경의 표준 구성입니다.

```
node1: Primary PostgreSQL + Patroni + etcd
node2: Standby PostgreSQL + Patroni + etcd + OpenProxy
node3: Standby PostgreSQL + Patroni + etcd + OpenProxy
```

**공통 설정 (**`**config/common.env**`**):**

`NODE1_IP ~ NODE3_IP`는 모든 노드에서 동일하게 설정합니다. 로컬 설치에서 각 노드마다 실행할 때 `NODE_NAME`과 `LICENSE_NAME`만 해당 노드에 맞게 변경합니다.

```bash
# 모든 노드에서 공통
NODE1_IP="192.168.1.10"
NODE2_IP="192.168.1.11"
NODE3_IP="192.168.1.12"

OPENSQL_HOME="/home/opensql"

# node1에서 실행 시
NODE_NAME="db-primary"
LICENSE_NAME="node1_license.xml"

# node2에서 실행 시 → NODE_NAME="db-standby1", LICENSE_NAME="node2_license.xml"
# node3에서 실행 시 → NODE_NAME="db-standby2", LICENSE_NAME="node3_license.xml"
```

**원격 설치 전용 설정 (**`**config/remote.env**`**):**

```bash
NODE1_NAME="db-primary"
NODE1_SSH_USER="ec2-user"
NODE1_SSH_PORT="22"
NODE1_OPENSQL_HOME="/home/opensql"
NODE1_LICENSE_NAME="node1_license.xml"

NODE2_NAME="db-standby1"
NODE2_SSH_USER="ec2-user"
NODE2_SSH_PORT="22"
NODE2_OPENSQL_HOME="/home/opensql"
NODE2_LICENSE_NAME="node2_license.xml"

NODE3_NAME="db-standby2"
NODE3_SSH_USER="ec2-user"
NODE3_SSH_PORT="22"
NODE3_OPENSQL_HOME="/home/opensql"
NODE3_LICENSE_NAME="node3_license.xml"
```

**실행:**

```bash
# 로컬 설치 (각 노드에서 NODE_NAME/LICENSE_NAME 변경 후 실행)
cd opensql-installer
python3 opensql_local_installer.py --mode 3node

# 원격 설치 (제어 서버에서 한 번에 실행)
cd opensql-installer
python3 opensql_remote_installer.py --mode 3node
```

***

### 6.3 2node-witness 모드

2개의 PostgreSQL 노드와 별도의 etcd witness 노드로 구성합니다.

```
node1: PostgreSQL + Patroni + etcd + OpenProxy
node2: PostgreSQL + Patroni + etcd + OpenProxy
node3: etcd (witness only)
```

**공통 설정 (**`**config/common.env**`**):**

`NODE1_IP ~ NODE3_IP`는 모든 노드에서 동일하게 설정합니다. node1/node2에서 실행 시 `NODE_NAME`과 `LICENSE_NAME`을 해당 노드에 맞게 변경합니다. node3(witness)에는 `LICENSE_NAME`을 비워둡니다.

```bash
# 모든 노드에서 공통
NODE1_IP="192.168.1.10"
NODE2_IP="192.168.1.11"
NODE3_IP="192.168.1.12"

OPENSQL_HOME="/home/opensql"

# witness etcd 포트 (필수 — 모든 노드에서 동일하게 설정)
WITNESS_ETCD_CLIENT_PORT=2379
WITNESS_ETCD_PEER_PORT=2380

# node1에서 실행 시
NODE_NAME="db-primary"
LICENSE_NAME="node1_license.xml"

# node2에서 실행 시 → NODE_NAME="db-standby", LICENSE_NAME="node2_license.xml"
# node3(witness)에서 실행 시 → NODE_NAME="witness", LICENSE_NAME=""
```

**etcd 옵션 (**`**config/etcd.config.env**`**):**

```bash
CLUSTER_ID=c1
```

**원격 설치 전용 설정 (**`**config/remote.env**`**):**

```bash
NODE1_NAME="db-primary"
NODE1_SSH_USER="ec2-user"
NODE1_SSH_PORT="22"
NODE1_OPENSQL_HOME="/home/opensql"
NODE1_LICENSE_NAME="node1_license.xml"

NODE2_NAME="db-standby"
NODE2_SSH_USER="ec2-user"
NODE2_SSH_PORT="22"
NODE2_OPENSQL_HOME="/home/opensql"
NODE2_LICENSE_NAME="node2_license.xml"

NODE3_NAME="witness"
NODE3_SSH_USER="ec2-user"
NODE3_SSH_PORT="22"
NODE3_OPENSQL_HOME="/home/opensql"
# witness(node3)는 Patroni 없음 → LICENSE_NAME 비워둡니다.
NODE3_LICENSE_NAME=""
```

**실행:**

```bash
# 로컬 설치 (각 노드에서 NODE_NAME/LICENSE_NAME 변경 후 실행)
cd opensql-installer
python3 opensql_local_installer.py --mode 2node-witness

# 원격 설치 (제어 서버에서 한 번에 실행)
cd opensql-installer
python3 opensql_remote_installer.py --mode 2node-witness
```

#### 하나의 witness에 여러 클러스터 구성

동일한 node3에 여러 클러스터를 구성할 경우 클러스터마다 다른 포트를 지정합니다.

**클러스터 1:** `CLUSTER_ID=c1`, `WITNESS_ETCD_CLIENT_PORT=2379`, `WITNESS_ETCD_PEER_PORT=2380`

**클러스터 2:** `CLUSTER_ID=c2`, `WITNESS_ETCD_CLIENT_PORT=2479`, `WITNESS_ETCD_PEER_PORT=2480`

***

## 7. 설치 실행

### 7.1 로컬 설치

각 노드에 직접 SSH 접속한 후 실행합니다.

```bash
cd opensql-installer

python3 opensql_local_installer.py --mode 3node
python3 opensql_local_installer.py --mode single
python3 opensql_local_installer.py --mode 2node-witness
```

> node1, node2, node3 각각에서 `NODE_NAME`과 `LICENSE_NAME`을 변경하여 실행합니다. 완료 후 다른 노드에서도 동일하게 실행해야 클러스터가 완성됩니다.

### 7.2 원격 설치

제어 서버에서 한 번 실행하면 모든 노드에 일괄 설치됩니다.

```bash
cd opensql-installer

# 기본 실행 (3node, 대화형 sudo 비밀번호 입력)
python3 opensql_remote_installer.py --mode 3node

# 공통 비밀번호 지정
python3 opensql_remote_installer.py --mode 3node --password "my_password"

# 노드별 비밀번호
python3 opensql_remote_installer.py --mode 3node \
  --node1-password "pw1" \
  --node2-password "pw2" \
  --node3-password "pw3"
```

### 7.3 CLI 옵션 전체 목록 (원격 설치)

<table><thead><tr><th width="265">옵션</th><th>설명</th></tr></thead><tbody><tr><td><code>--mode &#x3C;mode></code></td><td>클러스터 모드 (<code>single</code>, <code>2node-witness</code>, <code>3node</code>). 기본: <code>3node</code></td></tr><tr><td><code>--password &#x3C;pw></code></td><td>SSH/sudo 공통 비밀번호</td></tr><tr><td><code>--node{n}-password &#x3C;pw></code></td><td>nodeN SSH/sudo 공통 비밀번호</td></tr></tbody></table>

### 7.4 원격 설치 진행 단계 (총 16단계)

<table><thead><tr><th width="112">단계</th><th width="214">이름</th><th>설명</th></tr></thead><tbody><tr><td>사전</td><td>SSH 키 설정</td><td>SSH 키 자동 생성 및 각 노드에 등록</td></tr><tr><td>사전</td><td>연결 테스트</td><td>모든 노드에 SSH 키 기반 접속 확인</td></tr><tr><td>사전</td><td>포트 검사</td><td>필요 포트 사용 가능 여부 확인</td></tr><tr><td>1</td><td>유저 생성</td><td>opensql 유저/그룹 생성 및 디렉토리 초기화</td></tr><tr><td>2</td><td>패키지 확인/배포</td><td>패키지 존재 확인 및 누락 노드에 배포</td></tr><tr><td>3</td><td>필수 패키지 검증</td><td>OS별 필수 패키지 버전 확인</td></tr><tr><td>4</td><td>환경 변수 설정</td><td>opensql 유저 환경변수 (<code>~/.opensqlrc</code>) 설정</td></tr><tr><td>5</td><td>라이선스 배포</td><td>노드별 라이선스 XML 복사 및 권한 설정</td></tr><tr><td>6</td><td>라이선스 확인</td><td><code>OPENSQL_LICENSE_PATH</code>, 파일 존재, <code>signature</code> 일치 확인</td></tr><tr><td>7</td><td>컴포넌트 설치</td><td>모든 컴포넌트 설치</td></tr><tr><td>8</td><td>PostgreSQL 버전 확인</td><td>노드 간 PostgreSQL 바이너리 버전 일치 확인</td></tr><tr><td>9</td><td>etcd 설정</td><td>etcd 환경변수 및 서비스 파일 생성</td></tr><tr><td>10</td><td>etcd 시작</td><td>etcd systemd 서비스 시작 및 클러스터 형성 확인</td></tr><tr><td>11</td><td>Patroni 설정</td><td>Patroni 설정 파일(<code>patroni.yml</code>) 생성</td></tr><tr><td>12</td><td>Patroni 시작</td><td>Patroni 프로세스 시작 및 Leader 선출 확인</td></tr><tr><td>13</td><td>OpenProxy 설정/시작</td><td>OpenProxy 설정 생성 및 프로세스 시작</td></tr><tr><td>14</td><td>클러스터 상태 확인</td><td>etcd member list, patronictl list, PostgreSQL 연결 테스트</td></tr><tr><td>15</td><td>설치 경로 정리</td><td>원격 노드의 임시 설치 디렉토리 정리</td></tr><tr><td>16</td><td>설치 완료</td><td>설치 요약 및 완료 메시지 출력</td></tr></tbody></table>

### 7.5 로컬 설치 진행 단계

<table><thead><tr><th width="90">단계</th><th width="199">이름</th><th>설명</th></tr></thead><tbody><tr><td>1</td><td>패키지 확인</td><td>현재 노드의 패키지 존재 확인</td></tr><tr><td>2</td><td>필수 패키지 검증</td><td>OS별 필수 패키지 버전 확인</td></tr><tr><td>3</td><td>유저 생성</td><td>opensql 유저/그룹 생성 및 디렉토리 초기화</td></tr><tr><td>4</td><td>환경 변수 설정</td><td>opensql 유저 환경변수 (<code>~/.opensqlrc</code>) 설정</td></tr><tr><td>5</td><td>라이선스 배포</td><td>라이선스 XML 복사 및 권한 설정</td></tr><tr><td>6</td><td>컴포넌트 설치</td><td>컴포넌트 설치</td></tr><tr><td>7</td><td>etcd 설정/시작</td><td>etcd 설정 생성 및 서비스 시작</td></tr><tr><td>8</td><td>Patroni 설정/시작</td><td>Patroni 설정 생성 및 프로세스 시작</td></tr><tr><td>9</td><td>OpenProxy 설정/시작</td><td>OpenProxy 설정 생성 및 서비스 시작</td></tr><tr><td>10</td><td>로컬 포트 점검</td><td>etcd/Patroni 포트 열림 여부 확인</td></tr></tbody></table>

### 7.6 로그 파일

<table><thead><tr><th width="143">방식</th><th>로그 위치</th></tr></thead><tbody><tr><td>로컬 설치</td><td><code>opensql-installer/logs/YYYY-MM-DD_HH-MM-SS_opensql_install.log</code></td></tr><tr><td>원격 설치</td><td><code>opensql-installer/logs/YYYY-MM-DD_HH-MM-SS_opensql_install.log</code></td></tr></tbody></table>

### 7.7 환경 변수 커스터마이징

인스톨러는 각 노드의 `opensql` 유저 홈에 `~/.opensqlrc` 파일을 생성합니다.

**인스톨러가 기록하는 기본 내용:**

```bash
export OPENSQL_HOME=/home/opensql
export PATH=/home/opensql/bin:$PATH
export LD_LIBRARY_PATH=/home/opensql/lib:$LD_LIBRARY_PATH
export PGHOST=/home/opensql/tmp
export PGDATA=/home/opensql/data
export OPENSQL_LICENSE_PATH=/home/opensql/license/node1_license.xml
```

**커스텀 환경 변수 추가:**

```bash
vi /home/opensql/.opensqlrc

# 파일 끝에 추가
export PGOPTIONS="-c log_min_duration_statement=1000"
export PGTZ="Asia/Seoul"
```

변경 후 재로드:

```bash
source ~/.opensqlrc
```

> **주의**: 인스톨러를 재실행하면 `~/.opensqlrc`가 덮어씌워집니다. 재실행 후에도 유지할 변수는 `~/.bashrc` 끝에 직접 추가하세요. (`~/.bashrc`는 인스톨러가 source 라인만 추가하며 기존 내용을 덮어쓰지 않습니다.)

***

## 8. 설치 후 확인

### 8.1 서비스 확인

```bash
# etcd 서비스 상태
sudo systemctl status opensql-etcd

# Patroni 프로세스
ps aux | grep patroni

# OpenProxy 프로세스
pgrep -f openproxy

# 클러스터 상태 (opensql 유저로)
patronictl -c /home/opensql/etc/patroni.yml list
```

### 8.2 포트 확인

```bash
ss -tunlp | grep -E "5432|6432|6433|2379|2380|8008"
```

| 포트   | 컴포넌트               |
| ---- | ------------------ |
| 5432 | PostgreSQL         |
| 2379 | etcd (client)      |
| 2380 | etcd (peer)        |
| 8008 | Patroni REST API   |
| 6432 | OpenProxy (client) |
| 6433 | OpenProxy (admin)  |

### 8.3 PostgreSQL 접속 테스트

```bash
# opensql 유저로
psql -h 127.0.0.1 -p 5432 -U postgres

# OpenProxy를 통한 접속
psql -h 127.0.0.1 -p 6432 -U postgres
```

***

## 부록: 주요 트러블슈팅

### 현재 서버 IP 불일치 (로컬 설치)

```
RuntimeError: 현재 서버 IP(x.x.x.x)가 설정된 노드 IP와 일치하지 않습니다.
```

* `config/common.env`의 `NODE1_IP ~ NODE3_IP`가 현재 서버의 실제 IP와 일치하는지 확인하세요.
* `hostname -I` 명령으로 현재 서버 IP를 확인하세요.

### SSH 키 등록 실패

```
ERROR: 키 등록 실패
```

* SSH 비밀번호가 올바른지 확인하세요.
* `sshpass`가 설치되어 있는지 확인하세요: `which sshpass`
* 수동으로 키를 등록해볼 수 있습니다:

  ```bash
  ssh-copy-id -i ~/.ssh/id_rsa_opensql ec2-user@192.168.1.10
  ```

### 필수 패키지 검증 실패

```
ERROR: 필수 패키지 요건을 충족하지 않습니다.
ERROR: 누락된 필수 패키지: python3-psycopg2
```

* 로그에 표시된 설치 명령을 해당 노드에서 실행합니다.
* 또는 `AUTO_INSTALL_PREREQS=true` 설정 후 재실행합니다.

### 라이선스 관련 오류

#### 라이선스 파일/디렉토리 없음

```
ERROR: 라이선스 디렉토리를 찾을 수 없습니다: .../licenses
ERROR: 라이선스 파일을 찾을 수 없습니다: .../licenses/node1_license.xml
```

* `opensql-installer/licenses/` 디렉토리를 생성하고 XML 파일을 배치했는지 확인하세요.
* `LICENSE_NAME` (로컬) 또는 `NODE{n}_LICENSE_NAME` (원격) 값이 실제 파일명과 일치하는지 확인하세요.

#### 라이선스 에디션 확인 불가

```
ERROR: 라이선스 edition을 확인할 수 없습니다.
```

* 라이선스 XML 파일에 `<edition>` 태그가 포함되어 있는지 확인하세요.
* 지원 에디션: `standard`, `enterprise`, `ai`

#### signature 중복

```
ERROR: 라이선스 signature가 중복됩니다.
```

* 각 노드에 서로 다른 라이선스 XML을 사용해야 합니다.

#### 라이선스 환경변수 미설정 / 경로 불일치 (배포 후 검증)

```
ERROR: OPENSQL_LICENSE_PATH 미설정
ERROR: OPENSQL_LICENSE_PATH 불일치 (expected=..., actual=...)
```

* 환경 변수 설정 단계 로그에서 오류가 없었는지 확인하세요.

### etcd 시작 실패

```
ERROR: etcd 시작 실패
```

* etcd 서비스 로그를 확인합니다:

  ```bash
  sudo journalctl -u opensql-etcd -n 100 --no-pager
  ```
* etcd 포트가 이미 사용 중인지 확인합니다:

  ```bash
  ss -tunlp | grep 2379
  ```

### OpenProxy 시작 실패

```
ERROR: OpenProxy 프로세스가 실행되지 않았습니다.
```

* OpenProxy 로그를 확인합니다:

  ```bash
  cat /home/opensql/logs/openproxy.log | tail -50
  ```
* 포트가 이미 사용 중인지 확인합니다:

  ```bash
  ss -tunlp | grep 6432
  ```

### Patroni 상태 확인 필요

```
WARN: Patroni 상태 확인 필요
```

* Patroni 로그를 확인합니다:

  ```bash
  cat /home/opensql/logs/patroni.log | tail -50
  ```
* Patroni API 상태를 확인합니다:

  ```bash
  curl http://192.168.1.10:8008/health
  ```


---

# 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/installation/binary/opensql.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.
