4. 기동 · 정지 운영

이 장을 마치면: 상황에 맞는 기동 방식과 정지 방식을 고르고, 비정상 종료 뒤 안전하게 다시 띄운다.

4.1 기동 순서 · 완료 판정

기동 명령과 예상 출력은 QuickStart 4.1에 있다. gritdb-start는 아래 단계를 차례로 밟는다.

순서 단계 log/grit_admin_main.log의 단계 이름 시한
1 감독 프로세스 준비 BeginStart, StartChannelServer, PostgreSQLRecoveryPrep —
2 PostgreSQL 기동 LaunchPostgreSQL —
3 시스템 테이블 준비 PostgreSQLAwaitSystemTableReady 60초(비정상 종료 뒤에는 없음)
4 GritDB 카탈로그 기동 LaunchCatalog 60초
5 GritDB 엔진 기동 LaunchEngine 30초
6 GritDB 엔진 복구(못 받은 변경을 WAL에서 이어받는다) PostgreSQLEngineRecovery 없음
7 내부 작업자 준비 AwaitBgwReady 60초
8 따라잡기 PostgreSQLStartSyncWatermark 없음
9 접속 허용 OpenSessionGate —
  • 시한을 넘기면 기동이 실패하고 GritDB가 스스로 내려간다. 이때는 QuickStart GDB-BOOT-005를 본다.

  • 시한이 없는 단계는 GritDB 엔진 복구, 따라잡기, 비정상 종료 뒤의 PostgreSQL 복구다. 이 동안 gritdb-stop은 GritDB를 멈추지 않는다(4.2). 감독 프로세스는 받은 kill(SIGTERM)을 기동이 끝난 뒤 즉시 정지로 처리한다.

  • gritdb-start는 끝날 때까지 출력 없이 기다린다. 데이터가 적어도 약 30초 걸리므로 중간에 끊지 않는다.

  • 진행을 보려면 다른 터미널에서 아래 명령을 실행한다. 지금 단계 이름이 나온다.

    grep 'GritAdminPhaseRunner: ===' ~/gritdb1/log/grit_admin_main.log | tail -1
    
  • GritDB started successfully는 접속 허용(9단계) 뒤에 나온다. 이 줄이 보이면 바로 접속할 수 있다.

기동 중에 접속하면 아래 문구로 거부된다. 이때는 기다린다.

문구 뜻
GritSurfer is not accepting connections GritDB가 아직 접속을 허용하지 않았다
the database system is starting up, the database system is not yet accepting connections PostgreSQL이 기동 중이거나 복구 중이다
Connection refused PostgreSQL이 아직 포트를 열지 않았다
  • pg_isready로 보면 rejecting connections나 no response가 나온다.

기동을 확인한다. 20초 뒤 한 번 더 실행해 기동 직후 내려가는 경우를 잡는다.

~/gritdb1/bin/pg_isready -p 5432 -d postgres
  • pg_isready가 두 번 모두 accepting connections를 내면 정상이다.
  • -d postgres를 붙이면 서버 로그에 접속 실패 줄이 남지 않는다.

기동하는 셸:

  • PG로 시작하는 환경 변수와 LD_LIBRARY_PATH가 없는 셸에서 기동한다. 기동한 셸의 환경이 GritDB 프로세스로 넘어간다.

    env | grep -E '^(PG|LD_LIBRARY_PATH)'
    

    출력이 없으면 정상이다.

  • TMPDIR을 1.9의 10번에서 정했으면 기동할 때마다 같은 값을 준다(1.4 ④). 서비스 계정의 ~/.profile에 export TMPDIR=<디렉토리> 한 줄을 두면 손으로 기동할 때 빠뜨리지 않는다. 부팅 때 기동은 4.6의 Environment=TMPDIR을 쓴다. echo "${TMPDIR:-/tmp}"를 실행해 정한 값이 나오는지 확인한다.

    • 설치 스크립트의 TMPDIR은 실행할 수 있는 디렉토리여야 한다(1.4 ④). 그래서 그 디렉토리가 noexec이면 설치할 때만 다른 값을 준다.
  • 기동 명령을 겹쳐 실행하지 않는다. 화면 출력 로그(log/grit_admin_stdout.log)가 비워질 수 있다.

  • 1.1에는 운영에 쓰는 gritdb-start 옵션이 없다. 명령과 옵션 전체는 A.1에 있다.

4.2 정지 방식 · 걸리는 시간

방식 명령 동작
정상 정지 gritdb-stop 실행 중인 문장이 끝나기를 최대 300초 기다린 뒤 정지한다. 열린 세션(쉬는 세션 포함)이 남아 있으면 약 30초 뒤 즉시 정지로 넘어간다. 이때 core 파일이 생길 수 있고(9.6), 다음 기동은 복구 과정을 거친다. 정지 전에 남은 세션을 끊는다(아래)
즉시 정지 gritdb-stop --quick 진행 중 작업을 끊고 정지한다. 다음 기동 때 복구 과정을 거친다. ⚠ GritDB 엔진 core 파일(수십 GB)이 생길 수 있다. 자세한 것은 9.6을 본다
  • 정상 정지는 PostgreSQL을 Smart Shutdown(SIGTERM)으로 내린다. 즉시 정지는 Immediate Shutdown(SIGQUIT)으로 내린다.
  • 화면에 [WARN] Client command failed: …, [WARN] Falling back to direct signal..., [INFO] Sending SIGTERM to GritAdmin이 나오면 정상 정지 명령이 닿지 않아 즉시 정지로 내려갔다는 뜻이다. 이때 다음 기동은 복구 경로다(4.3의 5번, ABNORMAL_RESTART).
  • 정상 정지는 세션이 없을 때 약 1분 걸린다. 즉시 정지는 약 10~15초 걸린다. 단계 시한을 다 쓰면 정지에 최대 약 11분 걸린다.

정지 완료 판정에서는 아래가 모두 맞아야 한다. 화면의 GritDB stopped.만으로 판정하지 않는다. 기동 중에도 이 줄이 나오기 때문이다.

  1. 프로세스가 0개다. QuickStart 4.5의 정지 확인을 실행해 출력이 없는지 확인한다.

  2. 상태 파일과 잠금

    ls ~/gritdb1/run/
    cat ~/gritdb1/run/grit_admin_clean_shutdown
    flock -n ~/gritdb1/run/gritdb.lock true && echo "잠금 풀림 = 정지"
    
    • ls에 grit_admin.pid와 grit_admin.sock이 없고 마지막 줄이 잠금 풀림 = 정지면 정상이다.
    • 정상 정지였으면 cat이 CLEAN_SHUTDOWN을 낸다. 즉시 정지나 비정상 종료 뒤에는 이 파일이 없다.

남은 세션 끊기는 정지하기 전에 한다. 정지가 시작되면 새 접속은 막히지만, 열린 세션은 계속 SQL을 쓴다.

  1. 응용 프로그램, 리포트, BI 도구, CDC 도구, ETL 도구를 멈춘다.
  2. ~/gritdb1/bin/psql -p 5432 -d postgres로 접속해 아래 쿼리를 실행한다.
  3. 같은 쿼리를 다시 실행해 0행인지 본다.
  4. ~/gritdb1/bin/gritdb-stop을 실행한다.
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE backend_type = 'client backend' AND pid <> pg_backend_pid()
  AND application_name NOT LIKE 'gritdb\_catalog\_%';
  • gritdb_catalog_로 시작하는 접속은 GritDB 내부 접속이므로 끊지 않는다.
  • 끊지 않고 정지하면 화면 끝에 [WARN] Client command failed: ERROR stop failed …와 [WARN] Falling back to direct signal...이 나온다. 이 두 줄은 즉시 정지로 내려갔다는 뜻이다. 이때는 정상 정지 표식인 run/grit_admin_clean_shutdown이 없고, 다음 기동이 복구 과정을 거친다(4.3).
  • 함수와 뷰: pg_terminate_backend, pg_stat_activity

정지 중이나 기동 중의 정지 명령:

  • 정상 정지가 진행 중일 때 다른 터미널에서 gritdb-stop --quick을 실행해도 끼어들지 못한다. 이 명령은 앞의 정지가 끝날 때까지 기다린다.
  • 기동 중에는 gritdb-stop이 GritDB를 멈추지 않는다. PID 파일이 아직 없어(GritAdmin PID file not found) 아무것도 보내지 않는다. 화면에는 GritDB stopped.가 나오지만, 기동이 끝난 뒤에도 GritDB는 계속 돈다. 그래서 기동이 끝난 뒤 다시 정지한다. 기동 중에 정지 명령을 낸 화면에는 POSIX shared memory segments remaining …과 Remove them manually only when no GritDB process is running 경고도 나온다. 이 공유 메모리는 기동 중인 GritDB가 쓰므로 지우지 않는다(QuickStart GDB-SHUTDOWN-001의 판별). 기동 중인지는 4.5의 “기동 중”을 본다.
  • 급히 내려야 하면 처음부터 gritdb-stop --quick을 쓴다.

자동화:

  • gritdb-stop에는 대기 시한이 없으므로 스크립트에서 시한을 둔다. 시한은 정상 정지 최대 시간(약 11분)보다 길게, 예를 들어 15분으로 잡는다.

  • 시한을 넘기면 정지는 GritDB 안에서 계속된다. --quick을 더 보내도 끼어들지 못한다. 그래서 사람이 QuickStart 7.4대로 살펴본다.

    timeout 900 ~/gritdb1/bin/gritdb-stop || echo "STOP TIMEOUT"
    
  • 감독 프로세스는 직접 받은 kill(SIGTERM)을 즉시 정지로 처리한다. 그래서 정상 정지는 gritdb-stop으로만 한다. gritdb-stop도 정지 명령이 닿지 않으면 이 신호를 보낸다.

  • 재시작은 gritdb-stop을 먼저 실행한 뒤 gritdb-start를 실행한다. 재시작 명령은 따로 없다(QuickStart 4.6).

4.3 크래시 때 동작 · 다시 기동

  • GritDB 프로세스 하나가 비정상 종료하면 GritDB 전체가 정지한다. PostgreSQL 백엔드 하나의 비정상 종료(OS의 강제 종료 포함)도 같다. 자동 재기동은 없다. PostgreSQL의 restart_after_crash를 끈 것과 같다(5.3).
  • GritDB 엔진 프로세스나 GritDB 카탈로그 프로세스 하나만 따로 끝내도 전체가 즉시 정지한다.
  • GritDB 프로세스에 HUP 신호(kill -HUP)를 보내지 않는다. 보내면 프로세스가 끝난다.

비정상 종료 뒤 순서:

  1. QuickStart 7.4의 기록과 수집을 끝낸다. log/grit_admin_stdout.log는 다음 기동이 덮어쓴다. log/GRIT_SURFER_ASSERT.log의 크기를 본다. 0보다 크면 중단 기록이 있다.

  2. 최대 1분 동안 QuickStart 4.5의 정지 확인을 실행해 GritDB 프로세스가 모두 사라졌는지 본다. 1분 뒤에도 남으면 QuickStart 7.4를 보고 지원 문의(0장)를 한다.

  3. ~/gritdb1/bin/gritdb-stop을 실행해 남은 PID 파일을 정리한다. 화면에 GritAdmin PID file exists but process not running 또는 GritAdmin PID file not found 경고가 보이면 정상이다.

  4. ~/gritdb1/bin/gritdb-start를 실행한다.

  5. 기동 경로를 확인한다.

    grep DetectScenario ~/gritdb1/log/grit_admin_main.log | tail -1
    

    ABNORMAL_RESTART가 들어 있으면 정상이다. 비정상 종료 뒤 복구 경로로 기동했다는 뜻이다.

  6. 8.5로 검증한다(반영 확인과 건수 대조).

다음 기동은 비정상 종료 경로로 복구를 거친다. 복구는 시한이 없다(4.1).

  • PostgreSQL 복구(redo) 중에는 log/postgresql.log에 redo starts at <LSN> → 10초마다 redo in progress, elapsed time: <초> s, current LSN: <LSN> → redo done at <LSN> 줄이 차례로 남는다. 걸리는 시간은 마지막 checkpoint 뒤에 쌓인 WAL이 많을수록 길다. 튜닝 블록이 checkpoint_timeout을 1h로, max_wal_size를 64GB로 정하므로(5.2) PostgreSQL 기본값으로 운영할 때보다 길 수 있다. current LSN이 나아가면 기다린다. 강제로 끝내면 다음 기동이 redo를 처음부터 다시 한다.
    • grep -E 'redo (starts|in progress|done)' ~/gritdb1/log/postgresql.log | tail -3으로 확인한다. 간격은 log_startup_progress_interval이 정한다(기본 10초).
  • 복구 진행은 log/grit_recovery_worker_process.log에서 본다. 5초마다 [WARN] 등급의 RecoveryWorker: recovery queue not drained after <ms> ms — Plug/Unify pipeline may be blocked while catalog writes are deferred 줄이 남는다. 줄 끝의 may be blocked …까지가 고정 문구다. <ms>가 늘어 가는 동안은 복구 중이므로 기동을 끊지 않는다(4.1).
  • log/grit_admin_main.log에 === OpenSessionGate === 뒤 PID file written 줄이 남고, 이어서 화면에 GritDB started successfully가 나오면 기동이 끝났다.

4.4 남은 프로세스 · 공유 메모리 정리

gritdb-stop이 스스로 정리하는 것:

  • 남은 PID 파일을 지운다.
  • 감독 프로세스가 내려간 뒤 남은 GritDB 엔진 프로세스와 PostgreSQL 프로세스를 실행 파일 이름으로 대조해 끝낸다.
  • 이 설치본의 공유 메모리 잔여는 지우지 않고 개수만 알린다. 자세한 것은 QuickStart GDB-SHUTDOWN-001을 본다.

정지 뒤 남은 것을 본다.

  1. 프로세스는 QuickStart 4.5의 정지 확인으로 본다. 출력이 있으면 1분 기다린 뒤 다시 본다. 그래도 남으면 kill로 하나씩 끝내지 않는다. 이때는 QuickStart 7.4를 보고 지원 문의(0장)를 한다.
  2. 공유 메모리는 감독 프로세스가 강제로 끝나면 남는다. 정지 뒤 잔여 경고가 나오면 프로세스가 0개인지 확인한 뒤 QuickStart GDB-SHUTDOWN-001대로 정리한다.
  3. 정상 정지나 즉시 정지 뒤에도 같은 경고가 되풀이되면 QuickStart 7.4를 보고 지원 문의(0장)를 한다.

프로세스 이름:

실행 파일(bin/) ps -o comm과 pgrep ps -ef(명령 줄)
grit_admin GritAdmin GritDB for postgres: GritAdmin
grit_surfer_process GritSurfer GritDB for postgres: GritSurfer
grit_catalog_process GritCatalog GritDB for postgres: GritCatalog
postgres postgres 주 프로세스: <설치 경로>/bin/postgres -D …. 하위 프로세스: GritDB for postgres: <이름>(예: checkpointer, 세션은 <계정> <db> [local] idle)
  • 판별은 comm 이름(pgrep -x)으로 한다. 설치 경로가 짧으면 명령 줄 이름이 실행 파일 경로로 보일 수 있다.
  • 설치본이 여럿이면 이름이 같으므로 /proc/<PID>/exe로 가른다(2.9).

4.5 상태 확인

상태 명령은 없다. 잠금, run/grit_admin.pid, 프로세스 이름, pg_isready를 함께 보고 판정한다.

상태 PID 파일 잠금(run/gritdb.lock)
실행 중 있다(4줄) 잡혀 있다
기동 중 없다 잡혀 있다
정지 없다 풀려 있다
비정상 종료 뒤 있다(프로세스는 없다) 풀려 있다. 4.3을 본다
flock -n ~/gritdb1/run/gritdb.lock true && echo "잠금 풀림 = 정지"
while IFS=: read -r k p || [ -n "$k" ]; do
  printf '%-12s %-8s %s\n' "$k" "$p" "$(ps -p "$p" -o comm= || echo DEAD)"
done < ~/gritdb1/run/grit_admin.pid
~/gritdb1/bin/pg_isready -p 5432 -d postgres
  • 정상(실행 중)이면 첫 줄은 출력이 없다. PID 4줄에 GritAdmin, GritSurfer, postgres, GritCatalog가 하나씩 나오고 DEAD가 없다. 마지막 줄은 accepting connections다.
  • 정지 상태면 첫 줄은 잠금 풀림 = 정지다. 둘째 줄은 …/grit_admin.pid: No such file or directory(셸 오류)다. 마지막 줄은 no response다.
  • 기동 중이면 첫 줄은 출력이 없다. 둘째 줄은 같은 No such file or directory다. 마지막 줄은 rejecting connections다. 이때는 잠시 뒤 다시 본다.
  • 설치본이 여럿이면 프로세스 이름 대신 /proc/<PID>/exe로 구분한다(2.9).

4.6 서버 부팅 때 기동

제품에는 서비스 등록 파일이 없다. systemd를 쓰면 아래 예시를 바탕으로 /etc/systemd/system/gritdb.service를 만든다. 이 작업은 root로 한다.

[Unit]
Description=GritDB
After=network-online.target
Wants=network-online.target
RequiresMountsFor=<데이터 볼륨 실경로>

[Service]
Type=oneshot
RemainAfterExit=yes
User=gritdb
Environment=TMPDIR=<데이터 볼륨 안 디렉토리>
ExecStart=<설치 경로>/bin/gritdb-start
ExecStop=<설치 경로>/bin/gritdb-stop
TimeoutStartSec=infinity
TimeoutStopSec=20min
LimitNOFILE=65536
LimitCORE=<core 한도>

[Install]
WantedBy=multi-user.target
줄 이유
RequiresMountsFor 데이터 볼륨(2.4)이 mount된 뒤 기동한다. <데이터 볼륨 실경로>는 readlink -f ~/gritdb1/datafiles의 값이다. 재배치하지 않았으면 이 줄을 뺀다
ExecStart와 ExecStop <설치 경로>에는 절대 경로를 쓴다(gritdb 계정에서 readlink -f ~/gritdb1의 값). 정지는 gritdb-stop으로 한다
Type=oneshot과 RemainAfterExit=yes gritdb-start는 기동이 끝나면 돌아온다. PID 파일 형식이 달라 PIDFile=을 쓰지 않는다
TimeoutStartSec과 TimeoutStopSec 기동에는 시한이 없는 단계가 있다(4.1). 정지 시한은 정상 정지 최대 시간(약 11분)보다 길게 잡는다
LimitNOFILE과 LimitCORE /etc/security/limits.conf는 systemd 서비스에 적용되지 않는다(1.4)
Environment=TMPDIR 1.4의 ④를 본다. /tmp를 쓸 때는 이 줄을 뺀다
ExecReload 없음 HUP 신호를 받으면 GritDB 프로세스가 끝난다(4.3)
Restart= 없음 비정상 종료 뒤에는 4.3 순서를 따른다. 자동 재시작을 걸지 않는다
  • systemd로 띄운 셸 환경에는 PG로 시작하는 변수가 없다. 4.1의 기동하는 셸 조건에 맞는다.
  • 서비스로 띄워도 gritdb로 로그인했다 나가면 공유 메모리가 지워질 수 있다. 그래서 1.4 ⑤(RemoveIPC)를 먼저 한다.
  • PostgreSQL 쪽 서비스 예: 서버 시작

켜기와 기동(root):

systemctl daemon-reload
systemctl enable gritdb
systemctl start gritdb
  • 손으로 띄운 GritDB가 떠 있으면 먼저 gritdb-stop으로 내린 뒤 systemctl start를 실행한다.
  • systemctl status가 activating인 기동 중에는 systemctl stop gritdb를 하지 않는다. active가 된 뒤 정지한다.
  • systemctl status gritdb의 상태 줄이 active (exited)인지 확인한다. 기동 명령이 끝난 뒤에도 GritDB는 돈다(Type=oneshot). 4.5로도 확인한다.
  • systemd로 띄웠으면 정지도 systemctl stop gritdb로 한다. 비정상 종료 뒤 4.3의 3번과 4번은 각각 systemctl stop gritdb와 systemctl start gritdb로 한다.
  • 작업 스케줄러나 CI 잡 실행기에서 기동하면 그 작업이 끝날 때 GritDB가 함께 종료될 수 있다. 그래서 서비스 관리자(systemd)로 기동한다.

4.7 계획 정지 (OS 패치 · 재부팅)

  1. 분석계면 멈춰 둘 수 있는 시간을 먼저 본다(아래).
  2. 작업 전 기준을 수집한다(7.7).
  3. 응용 프로그램을 멈추고 남은 세션을 끊은 뒤 정상 정지한다(4.2).
  4. 멈춘 것을 확인한다(4.5와 QuickStart 4.5의 정지 확인).
  5. OS 작업을 한다.
  6. glibc(QuickStart 2.4 점검표 4, min_glibc 이상)와 1.4의 파일 열기 한도, kernel.core_pattern, 시간 동기화, clocksource, RemoveIPC를 다시 확인한다.
  7. 기동한다(4.1). 그 뒤 4.5로 확인한다. 부팅 때 기동(4.6)을 켜 두었으면 재부팅 뒤 4.5로 확인한다.
  8. 7.3의 “패치 때” 항목을 점검한다. 분석계면 따라잡는지 본다(7.5).

분석계를 멈춰 둘 수 있는 시간은 운영 DB에서 max_slot_wal_keep_size를 WAL 생성 속도로 나눈 값이다. 자세한 것은 6.4.1의 1번과 6.4.3을 본다. max_slot_wal_keep_size가 -1이면 그 대신 운영 DB 디스크 여유를 WAL 생성 속도로 나눈다. 이 시간을 넘으면 처음부터 다시 받는다(8.6).

WAL 생성 속도는 운영 DB에서 간격을 두고(예: 바쁜 시간대 1시간) 두 번 잰다.

SELECT pg_current_wal_lsn();
SELECT pg_size_pretty(pg_wal_lsn_diff('<나중 값>', '<처음 값>'));

문제가 생기면

  • QuickStart 7장의 GDB-BOOT 계열, GDB-SHUTDOWN 계열, TroubleShooting을 본다.

더 알아보기