> For the complete documentation index, see [llms.txt](https://docs.tibero.com/sysmasterdb/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/sysmasterdb/8.4_manual/installation-guide/installation/kubernetes-environment/up-down.md).

# 기동 / 로그 확인 / 종료 / 초기화

Kubernetes 환경에서 SysMasterDB를 기동 / 로그 확인 / 종료 / 초기화하는 방법이다.

### 1. 기동

클러스터 사전 준비(setup-once.sh)가 완료된 상태에서, 배포 파일이 위치한 디렉터리에서 아래 명령을 수행하면 전체 스택이 배포된다.

{% code collapsedlinecount="10" %}

```shell
kubectl apply -k kubernetes
```

{% endcode %}

적용 전에 최종 결과를 확인하려면 아래 명령을 사용한다.

{% code collapsedlinecount="10" %}

```shell
kubectl kustomize kubernetes                       # 병합된 매니페스트 출력
kubectl apply -k kubernetes --dry-run=server       # 클러스터 검증만 수행
```

{% endcode %}

#### 1.1. 기동 확인

기동 순서는 별도로 제어할 필요가 없다. 각 모듈이 자신의 의존 대상이 준비될 때까지 대기하므로 아래 순서로 준비된다.

{% code collapsedlinecount="10" %}

```
1. metadb, repodb, zookeeper, client-tibero    (의존 대상 없음)
2. broker                                      (zookeeper 필요)
3. sdm                                         (metadb, repodb 필요)
4. analyzer, dbmanager, chatbot                (sdm 필요)
5. collector                                   (sdm, analyzer 필요)
6. client-gateway                              (sdm, dbmanager, collector 필요)
```

{% endcode %}

**최초 배포는 데이터베이스 초기화로 인해 전체가 준비되기까지 수 분이 소요된다.**

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb get pods
```

{% endcode %}

전 모듈에 헬스 체크가 설정되어 있어 **`1/1 Running`으로 표시되면 실제로 요청을 처리할 수 있는 상태**를 의미한다. 별도로 기동 완료 로그를 확인할 필요가 없다.

{% hint style="info" %}
**참고**

앞 순서의 모듈을 기다리는 동안 뒤 순서의 파드가 몇 차례 재시작하는 것은 정상이며, 앞 모듈이 준비되면 이어서 정상 상태가 된다. 모듈별 기동 대기 한도는 「Kubernetes 환경」 문서의 **매니페스트 구성**의 헬스 체크 항목을 참고한다.
{% endhint %}

파드가 계속 준비되지 않는 경우 아래로 원인을 확인한다.

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb describe pod {파드명}       # Events 항목에서 실패 사유 확인
kubectl -n sysmasterdb logs {파드명}
kubectl -n sysmasterdb logs {파드명} --previous    # 재시작 직전 로그
```

{% endcode %}

#### 1.2. 접속

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb get svc client-gateway
```

{% endcode %}

할당된 `EXTERNAL-IP`로 브라우저에서 `https://{EXTERNAL-IP}` 로 접속한다. 사이트 인증서를 반입하지 않은 경우 브라우저에 인증서 경고가 표시되는 것은 정상이다.

***

### 2. 로그 확인

각 컨테이너의 표준 출력 로그는 아래 명령으로 확인할 수 있다.

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb logs deployment/sdm        # 터미널 출력 로그
kubectl -n sysmasterdb logs deployment/sdm -f     # 실시간 확인
```

{% endcode %}

다만 각 모듈의 애플리케이션 로그는 표준 출력이 아니라 컨테이너 내 `/sysmaster/logs` 경로의 로그 파일에 적재되므로, 상세 로그는 아래와 같이 확인한다.

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb exec deployment/sdm -- ls /sysmaster/logs
kubectl -n sysmasterdb exec deployment/sdm -- sh -c 'tail -f /sysmaster/logs/sdm.v.{version}.log'
```

{% endcode %}

{% hint style="warning" %}
**주의**

로그는 기본적으로 파드 내부 임시 볼륨에 기록된다. 컨테이너가 재시작해도 로그는 유지되지만 **파드가 교체되면(배포, 노드 drain, eviction) 사라진다.** 장애 조사를 위해 로그를 보존해야 하는 환경에서는 「설치 및 파라미터 설정」 문서의 **9. 선택 구성**의 로그 영속화를 적용한다.

client-tibero는 정적 파일 서버로 파일 로그를 기록하지 않고 표준 출력으로만 내보낸다. `/sysmaster/logs`가 비어 있는 것이 정상이며, 로그는 `kubectl logs`로 확인한다.
{% endhint %}

#### 2.1. 문제 분석용 디버그 컨테이너

셸 도구가 없는 파드를 조사할 때는 함께 제공되는 유틸리티 이미지를 사용한다. `kubectl debug`는 **파드 이름만 받으므로** 디플로이먼트 이름을 전달하면 실패한다.

{% code collapsedlinecount="10" %}

```shell
POD=$(kubectl -n sysmasterdb get pod -l app=sdm -o jsonpath='{.items[0].metadata.name}')

kubectl -n sysmasterdb debug -it "$POD" \
  --image={registry}/sysmaster-db-alpine-linux:{version} --target=sdm
```

{% endcode %}

***

### 3. 라이선스 갱신

라이선스는 `sdm-license` Secret으로만 주입되며, **파드를 재시작하지 않고** 교체할 수 있다. 아래 세 단계를 순서대로 수행한다.

#### 3.1. Secret을 새 라이선스로 교체

{% code collapsedlinecount="10" %}

```shell
kubectl create secret generic sdm-license -n sysmasterdb \
  --from-file=sysmaster-db-license.xml=/path/to/new-license.xml \
  --dry-run=client -o yaml | kubectl apply -f -
```

{% endcode %}

#### 3.2. 파드 내부 파일에 반영되었는지 확인

Secret을 교체해도 파드 안의 파일이 즉시 바뀌지는 않는다. 아래 명령의 출력이 **새 라이선스의 값으로 바뀔 때까지** 기다린다(반영에 **최대 1분** 소요).

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb exec deploy/sdm -- grep serial_id /sysmaster/license/sysmaster-db-license.xml
```

{% endcode %}

#### 3.3. sdm에 리로드 지시

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb exec deploy/sdm -- refresh-license
```

{% endcode %}

정상적으로 갱신되면 아래와 같이 응답한다. `expirationDate`가 새 라이선스의 만료일과 일치해야 갱신이 완료된 것이다.

{% code collapsedlinecount="10" %}

```
{"message":"License store refresh succeeded.","licenseType":"SUBSCRIPTION","maxMonitoringCores":1000,"expirationDate":"..."}
```

{% endcode %}

{% hint style="danger" %}
**주의 —** [**3.2 단계(반영 확인)**](#id-3.2)**를 생략하지 않는다.**

Secret을 적용한 직후에는 파드 내부 파일이 아직 이전 내용이다. 그 상태에서 [3.3](#id-3.3.-sdm)의 `refresh-license`를 수행하면 **이전 라이선스를 다시 읽고 성공으로 응답**하므로, 갱신된 것처럼 보이지만 실제로는 갱신되지 않는다.

반영 여부는 `serial_id`나 `signature`처럼 라이선스마다 값이 다른 항목으로 판정한다. `start_date` / `end_date`는 갱신 전 라이선스의 만료일과 갱신 후 라이선스의 시작일이 동일한 경우가 많아, **아직 교체되지 않은 파일에도 일치**할 수 있다.
{% endhint %}

{% hint style="info" %}
**참고**

* 갱신이 실패하면 HTTP 500과 함께 사유가 응답된다. 이 경우에도 **파드는 재시작하지 않고 서비스는 계속 동작하며**, 기존 라이선스가 유지된다.
* 라이선스의 `identified_by_host` 항목은 기록용이며 실행 중 파드의 호스트명과 대조하지 않는다. 파드 이름이 배포마다 변경되어도 라이선스에는 영향이 없다.
* setup-once.sh를 새 라이선스 파일로 다시 실행하는 방법도 가능하다. 이때 크리덴셜 환경변수를 지정하지 않으면 기존 크리덴셜 Secret은 그대로 유지된다.
  {% endhint %}

***

### 4. 설정 변경과 재시작

| 변경 대상                            | 반영 방법                                                                           |
| -------------------------------- | ------------------------------------------------------------------------------- |
| kustomization.yml (이미지, patch 등) | `kubectl apply -k kubernetes`                                                   |
| 디플로이먼트의 `env` 항목                 | `kubectl apply -k kubernetes` (파드가 자동 교체된다)                                     |
| `smdb-env` ConfigMap             | `kubectl apply -k kubernetes` 후 `kubectl -n sysmasterdb rollout restart deploy` |
| `sdm-license` Secret             | 위 [**3. 라이선스 갱신**](#id-3) 절차 (재시작 불필요)                                          |
| meta.conf / repo.conf            | 데이터베이스 최초 초기화 시에만 사용된다. 이미 초기화된 데이터베이스에는 반영되지 않는다                               |

특정 모듈만 재시작하는 경우는 다음과 같다.

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb rollout restart deploy/sdm
kubectl -n sysmasterdb rollout status  deploy/sdm
```

{% endcode %}

{% hint style="warning" %}
**주의**

전 모듈이 단일 인스턴스로 구성되어 있어 재시작 중에는 해당 기능이 일시 중단된다. 특히 client-gateway를 재시작하면 사용자 UI 접속이 끊긴다.
{% endhint %}

***

### 5. 종료

아래 명령으로 워크로드만 삭제하면 SysMasterDB가 종료된다. 이때 Meta DB와 Repository DB의 데이터 파일은 PVC에 유지되므로, 다시 기동하면 이전에 저장한 데이터를 사용할 수 있다.

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb delete deploy,svc --all
kubectl -n sysmasterdb get pvc                 # 3개가 Bound 상태로 남아 있어야 한다
```

{% endcode %}

다시 기동할 때는 `kubectl apply -k kubernetes`만 수행한다.

***

### 6. 백업

아래 데이터는 PVC 3종에만 존재하며 제품이 생성하는 사본은 없다. **초기화를 수행하기 전에 반드시 백업한다.**

| PVC           | 저장 내용            |
| ------------- | ---------------- |
| `metadb-data` | 설정, 계정, 관제 대상 목록 |
| `repodb-data` | 수집된 메트릭 전체       |
| `sdm-reports` | 생성된 리포트 파일       |

스토리지 계층 백업(CSI VolumeSnapshot 등)을 권장한다. 대상 볼륨은 아래로 확인한다.

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb get pvc -o custom-columns=PVC:.metadata.name,PV:.spec.volumeName
```

{% endcode %}

{% hint style="warning" %}
**주의**

논리 백업(`pg_dump`)을 사용하는 경우, Meta DB와 Repository DB가 모두 TimescaleDB 확장을 사용한다는 점을 고려해야 한다. 일반적인 `pg_dump` / `pg_restore` 절차만으로는 복원 결과가 정상적이지 않을 수 있으므로, TimescaleDB의 `timescaledb_pre_restore()` / `timescaledb_post_restore()`를 함께 사용해야 하며 **복원까지 성공하는 것을 실제 환경에서 확인한 후에만** 백업 수단으로 채택한다.
{% endhint %}

***

### 7. 초기화

아래 명령을 수행하면 네임스페이스를 포함한 모든 오브젝트와 데이터가 삭제되고, 최초 설치 상태와 동일하게 동작한다.

{% code collapsedlinecount="10" %}

```shell
kubectl delete -k kubernetes
kubectl get ns sysmasterdb             # NotFound 가 될 때까지 대기한다
kubectl get pv | grep sysmasterdb      # 남아 있으면 스토리지 정책에 따라 처리한다
```

{% endcode %}

{% hint style="danger" %}
**주의 — 이 명령은 데이터까지 삭제한다.**

**kubernetes/base/foundation.yml**에 네임스페이스와 PVC 3종이 함께 정의되어 있어 삭제 대상에 포함된다. PVC가 삭제되면 StorageClass의 reclaimPolicy가 `Delete`(동적 프로비저닝 기본값)인 경우 볼륨 데이터도 함께 삭제되며, setup-once.sh가 생성한 Secret 2종도 네임스페이스와 함께 사라진다.

**데이터를 유지해야 하는 경우 위** [**5. 종료**](#id-5)**를 사용한다.** 재설치하려면 setup-once.sh 실행부터 다시 수행해야 한다.
{% endhint %}

실수로 삭제하더라도 데이터가 보존되도록 하려면, 배포 직후 PVC에 연결된 PV의 reclaim 정책을 `Retain`으로 변경해 둔다.

{% code collapsedlinecount="10" %}

```shell
for pvc in metadb-data repodb-data sdm-reports; do
  pv=$(kubectl -n sysmasterdb get pvc "$pvc" -o jsonpath='{.spec.volumeName}')
  kubectl patch pv "$pv" -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
done
```

{% endcode %}

***

### 8. 문제 해결

| 증상                                 | 원인                                                    | 조치                                                                |
| ---------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------- |
| 전체 파드 `ImagePullBackOff`           | 레지스트리 주소 미수정, 또는 인증 필요                                | kustomization.yml의 `newName` 확인 / 사설 레지스트리 인증 구성 추가               |
| 전체 파드 `Pending`                    | PVC가 `Pending` (default StorageClass 없음)              | `kubectl -n sysmasterdb get pvc`                                  |
| sdm이 기동하지 않음                       | 라이선스 Secret 없음, 또는 라이선스 version이 1.1이 아님              | setup-once.sh 실행 여부 확인                                            |
| sdm이 데이터베이스에 접속하지 못함               | meta.conf / repo.conf의 암호와 Secret 값 불일치               | 두 값을 동일하게 설정                                                      |
| UI 접속 시 502                        | `NGINX_RESOLVER`가 실제 클러스터 DNS 주소와 다름                  | `kubectl get svc kube-dns -n kube-system`                         |
| sdm이 데이터베이스를 계속 대기                 | metadb / repodb 서비스에서 7000 포트 누락                      | 서비스 정의 확인                                                         |
| 배포가 완료되지 않음                        | `strategy: Recreate` 설정을 제거함                          | 원복                                                                |
| 파드가 계속 `OOMKilled`                 | 메모리 limits 부족, 또는 JVM 힙이 limits에 비해 큼                 | 두 값을 함께 상향                                                        |
| Kafka `Too many open files`        | 노드의 파일 디스크립터 한계가 낮음                                   | 노드에서 `nofile` 65536 이상으로 설정                                       |
| 대시보드가 비어 있음                        | TPM Agent가 `collector-ingest`에 도달하지 못함                | `EXTERNAL-IP`와 Agent 설정 주소 확인                                     |
| client-gateway만 `CrashLoopBackOff` | 로그 볼륨 마운트를 제거했거나 HTTPS 인증서 파일명이 다름                    | `kubectl logs deploy/client-gateway --previous` 로 nginx 오류 메시지 확인 |
| ConfigMap을 변경했는데 반영되지 않음           | ConfigMap 이름이 동일해 파드 스펙이 변경되지 않음                      | `kubectl -n sysmasterdb rollout restart deploy`                   |
| 오버레이 추가 후 기존 UI/수집이 중단             | kustomization.yml에 `resources:` / `patches:` 키를 중복 작성 | 「Kubernetes 환경」 문서의 **매니페스트 구성**의 주의 사항 참고                        |

{% code collapsedlinecount="10" %}

```shell
kubectl -n sysmasterdb get events --sort-by=.lastTimestamp | tail -30
```

{% endcode %}
