InfraGuard

백업 스크립트 빌더

프로젝트 정의에서 대상을 프리필해 mysqldump·rsync 백업 스크립트 골격을 생성하고, "N U" 스케줄과 함께 작업·스크립트 버전을 저장합니다.

개요 — 무엇을 하나

백업 스크립트 빌더는 SE·DBA 가 백업 스크립트를 읽고 검토한 뒤 대상 호스트에 직접 배치하도록, 자격증명이 은닉된 셸 스크립트 골격을 생성·안내합니다. 앱은 원격 서버에 접속해 백업을 실행하거나 crontab 을 직접 쓰지 않습니다. 대신:

  1. 프리필 — Projects 의 백업 정의(db_refs·web_refs·backup_targets)를 읽어 실행 가능한 작업 초안을 만듭니다.
  2. 생성(골격) — 소스 유형에 맞는 스크립트를 렌더합니다: DB = mysqldump --defaults-extra-file(자격증명 은닉), 파일 = rsync 증분, 볼륨 = tar, 커스텀 = 자유 명령.
  3. 저장 — 작업(infr_backup_job)과 스크립트 버전(infr_generated_script)을 저장합니다. 스케줄은 "N U" 문법(ScheduleGrammar)으로 next_run_at 을 계산합니다.
  4. 실행·보고 — SE 가 배치한 스크립트를 백업 전용 서버의 **pm2 러너(infr_backup_runner.cjs)**가 due 시점에 실행하고, stdout 의 INFR_* 라인을 파싱해 리포트 콜백으로 infr_backup_run 에 기록합니다.

빌더는 딥링크가 통합된 모달입니다. /backup/builder?job=ID 는 /backup/scripts?builder=1&job=ID 로 리다이렉트되고, 프리필·생성·저장·조회 AJAX 엔드포인트는 Backup Scripts 페이지의 빌더 모달이 그대로 사용합니다.

핵심 보안 원칙: 자격증명을 스크립트에 굽지 않습니다.

언제 쓰나

화면 구성

빌더 모달은 좌측 폼(작업 정의)과 우측 미리보기(CodeMirror 스크립트)로 구성됩니다.

영역 내용
프로젝트 선택 infr_project 드롭다운 → [프리필] 로 초안 후보 로드
초안 후보 DB/파일 소스별 카드(참조 미지정 시 빈 DB 초안 1개)
소스 폼 source_kind(db·files·volume·custom) + host/port/user/db/tables 또는 src_path/exclude
볼트·보존 저장 볼트(target_id), 오프사이트 볼트, keep_last·keep_days
스케줄 "N U" 입력(schedule) + start_at 앵커
스크립트 미리보기 CodeMirror(bash) — 생성 골격, 직접 편집 가능
.cnf 안내 DB 소스일 때 --defaults-extra-file 대상 파일 힌트

사용법

1. 프로젝트에서 프리필

프로젝트를 고르고 [프리필]을 누르면 POST /backup/builder/prefill 이 BackupBuilderService::prefillFromProject() 를 호출합니다.

2. 미리보기 생성

폼을 편집하고 [생성]을 누르면 POST /backup/builder/generate 가 ScriptGeneratorService::buildFromDraft() 로 저장 없이 골격을 렌더합니다.

DB 백업 골격 예시(placeholder — 실제 IP·계정은 대상 호스트에서 결정):

#!/bin/sh
# InfraGuard 백업 스크립트(골격) — job #12 "myproj · DB(db@blue)"
# 생성: 2026-08-13 09:30 · 이 스크립트는 백업 러너(pm2 · infr_backup_runner.cjs)가 실행합니다.
# 러너가 종료코드로 성공/실패를 보고하고, 아래 INFR_ARTIFACT/INFR_BYTES 출력으로 아티팩트·용량을 수집합니다.
# 자격증명은 이 스크립트에 없습니다 — /etc/infr/myproj_blue_db.cnf (chmod 600) 를 백업 서버에 생성합니다.
# 이 골격을 직접 완성하세요 — 자격증명은 절대 인라인하지 마세요(defaults-file·ssh 키만 참조).
set -eu
JOB_ID=12
IDENT="myproj_blue_db"
TS="$(date +%Y%m%d%H%M)"
TARGET="/data/bk"
LOCK="/tmp/infr_backup_12.lock"
BYTES=0; SUM=""; OUT=""
mkdir -p "$TARGET"
exec 9>"$LOCK"; flock -n 9 || { echo "already running"; exit 0; }
# ── 백업 실행 ──
OUT="$TARGET/${IDENT}_${TS}.sql"
mysqldump --defaults-extra-file="/etc/infr/myproj_blue_db.cnf" --routines --opt --events -h203.0.113.10 -P3306 mydb > "$OUT"

# ── 무결성·용량(러너가 INFR_* 출력을 파싱해 보고) ──
BYTES=$(wc -c < "$OUT" 2>/dev/null || echo 0)
SUM=$(sha256sum "$OUT" 2>/dev/null | cut -d' ' -f1)
printf 'INFR_ARTIFACT=%s\n' "$OUT"
printf 'INFR_BYTES=%s\n' "$BYTES"
[ -n "$SUM" ] && printf 'INFR_CHECKSUM=%s\n' "$SUM"

# ── 보존 정책 정리 ──
# 보존: 14일 초과 정리
find "$TARGET" -maxdepth 1 -name "${IDENT}_*" -mtime +14 -exec rm -rf {} \; 2>/dev/null || true

파일 백업은 mysqldump 대신 증분 rsync 로 렌더됩니다:

OUT="$TARGET/${IDENT}_$(date +%Y%m%d)"
mkdir -p "$OUT"
rsync -az --delete --exclude=node_modules --exclude=.git --exclude=vendor \
  -e "ssh -i ~/.ssh/id_rsa -p 22 -o StrictHostKeyChecking=accept-new" \
  root@203.0.113.10:/var/www/example.com "$OUT/"

.cnf 자격 파일은 앱이 저장하지 않고 안내만 합니다(DB 소스일 때 미리보기에 함께 표시):

# /etc/infr/myproj_blue_db.cnf (chmod 600) — SE 가 대상 호스트에 생성. 값은 이 앱에 저장되지 않습니다.
[mysqldump]
host=203.0.113.10
user=백업_읽기전용_계정
password=계정_비밀번호

3. 저장(작업 + 스크립트 버전)

[저장]은 POST /backup/builder/save(쓰기 level ≥ 2) 를 호출합니다.

GET /backup/builder/script?job_id=ID 는 해당 작업의 최신 스크립트 버전(없으면 즉석 골격)을 반환합니다.

필드 레퍼런스

필드 의미 기본/폴백
name 작업 표시명 "백업 작업"
source_kind db · files · volume · custom db
collector_key DB 소스 출처 표시(infr_collector.key) — 입력 필드 아님, 프로젝트 프리필이 남긴다 —
web_service_id 파일 소스 웹서비스 —
target_id 저장 볼트(infr_backup_target) config('backup.default_target')=/data/bk
params.host/port/user/db/tables[] DB 접속·대상(비밀 제외) —
params.src_path/exclude[]/key_path 파일 소스 경로·제외·ssh 키 key_path ~/.ssh/id_rsa
params.ident 아티팩트 파일명 접두 slug(name)
params.cnf_path mysqldump 자격 — 정적 my.cnf 경로 또는 backup_dbkey.json(런타임 복호) /etc/infr/{ident}.cnf
params.offsite_target_id 오프사이트 2차 볼트 null
schedule "N U" 스케줄 1 D
keep_last / keep_days 보존(최근 N개 / N일) null

"N U" 스케줄 문법 (ScheduleGrammar)

문자열 "N U" — N = 개수, U = 단위. 로케일 독립 정적 SOT.

단위 뜻 분
M 분 1
H 시 60
D 일 1440
W 주 10080

예: 6 H(6시간마다) · 1 D(하루마다) · 30 M(30분마다) · 1 W(주 1회). 파싱 실패 시 1 D.

현재 빌더가 저장하는 infr_generated_script.crontab_line 은 빈 문자열입니다 — 실행 스케줄을 **pm2 러너(infr_backup_runner.cjs)**가 infr_backup_job 을 조회해 "N U" due 판정으로 담당하기 때문입니다. crontab 라인을 직접 등록하는 대체 배치 모델을 쓰려면 ScheduleGrammar::cronPrefix() 로 5필드를 파생해 {prefix} /bin/sh {script} 라인을 만들면 됩니다. 어느 쪽이든 앱은 원격 crontab 을 직접 쓰지 않습니다 — SE 가 스크립트를 배치합니다.

데이터 소스

테이블 역할
infr_backup_job (000402) 백업 작업 정의(소스·볼트·스케줄·보존·params JSON)
infr_generated_script (000403) 생성 스크립트 버전(body·crontab_line·cnf_path·checksum·version)
infr_backup_target 저장 볼트(로컬/원격 경로)
infr_project (000715) 프리필 소스(db_refs·web_refs·backup_targets)
infr_collector (access=database) DB 접속 해석(resolveConn, 비밀 제외)
infr_web_service · infr_instance 파일 소스 ssh 접속 유도
infr_backup_run 실행 보고(리포트 콜백)
infr_audit_log 저장 감사(backup.job.*)

설정 기본값(config/backup.php, 전부 env 오버라이드):

report_url     = {APP_URL}/api/v1/backup/report   ; INFR_BACKUP_REPORT_URL
script_dir     = /data/infr/scripts               ; INFR_BACKUP_SCRIPT_DIR
cnf_dir        = /etc/infr                         ; INFR_BACKUP_CNF_DIR
log_dir        = /var/log/infr                     ; INFR_BACKUP_LOG_DIR
default_target = /data/bk                          ; INFR_BACKUP_DEFAULT_TARGET

권한

문제해결 · FAQ

스크립트가 아무것도 안 하고 already running 만 출력하면 이전 실행의 flock 이 남은 것입니다. /tmp/infr_backup_{job_id}.lock 을 잡은 프로세스를 확인하세요. 정상 종료 시 lock 은 자동 해제됩니다.

관련 항목

스크립트 빌더 진입 화면
스크립트 빌더 진입 화면