개요 — 무엇을 하나
백업 스크립트 빌더는 SE·DBA 가 백업 스크립트를 읽고 검토한 뒤 대상 호스트에 직접 배치하도록, 자격증명이 은닉된 셸 스크립트 골격을 생성·안내합니다. 앱은 원격 서버에 접속해 백업을 실행하거나 crontab 을 직접 쓰지 않습니다. 대신:
- 프리필 — Projects 의 백업 정의(
db_refs·web_refs·backup_targets)를 읽어 실행 가능한 작업 초안을 만듭니다. - 생성(골격) — 소스 유형에 맞는 스크립트를 렌더합니다: DB =
mysqldump --defaults-extra-file(자격증명 은닉), 파일 =rsync증분, 볼륨 =tar, 커스텀 = 자유 명령. - 저장 — 작업(
infr_backup_job)과 스크립트 버전(infr_generated_script)을 저장합니다. 스케줄은"N U"문법(ScheduleGrammar)으로next_run_at을 계산합니다. - 실행·보고 — 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 페이지의 빌더 모달이 그대로 사용합니다.
핵심 보안 원칙: 자격증명을 스크립트에 굽지 않습니다.
- mysqldump →
--defaults-extra-file={cnf_path}경로만 참조(SE 가0600으로 생성). - rsync/ssh → ssh 키 경로만(
sshpass·평문 비밀 없음). - API 키 → 대상 호스트 env
INFR_API_KEY우선(스크립트엔 자리표시자만).
언제 쓰나
- 새 프로젝트의 DB·웹 루트 백업을 표준화된 스크립트로 시작할 때.
- 기존
mysqldump/rsync크론잡을 자격증명 은닉·무결성 보고·보존 정책이 포함된 골격으로 이관할 때. - 오프사이트 2차 사본(3-2-1)을 스크립트 하단에 연쇄로 붙일 때.
- 백업 스케줄을
"6 H"·"1 D"같은 단순 문법으로 정의하고 다음 실행 시각을 미리 확인할 때.
화면 구성
빌더 모달은 좌측 폼(작업 정의)과 우측 미리보기(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() 를 호출합니다.
- DB 소스 —
project.db_refs[](=infr_collector.key,access=database) →DbCollectorService::resolveConn()로 host/port/db/user 를 채웁니다. 비밀번호는 초안에 넣지 않습니다.database가information_schema면 비웁니다.ident=slug(프로젝트명_별칭_db). - 파일/웹 소스 —
project.web_refs[](=infr_web_service.id) → 연결된 인스턴스의ssh_host/ssh_port(기본 22)/ssh_user(기본 root),src_path=/var/www/{도메인}, 기본 제외node_modules .git vendor storage/logs,key_path=~/.ssh/id_rsa. - 힌트 —
project.backup_targets[]자유텍스트에서 스케줄·저장 대상을 뽑아"N U"로 정규화 시도하고(예: "매일"→1 D, "hourly"→6 H), 경로가 겹치는 활성 볼트 id 를 추정합니다. - 폴백 — 참조가 없으면 빈 DB 초안 1개를 제공합니다.
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) 를 호출합니다.
- 요청 필드는
InfrBackupJob화이트리스트로 검증됩니다.schedule은ScheduleGrammar::isValid()통과 시 대문자화, 아니면1 D로 폴백.source_kind·status·criticality는 enum 화이트리스트, 이름 미입력 시 "백업 작업". next_run_at=ScheduleGrammar::nextRun(schedule, start_at, now()).- 스크립트 버전 =
infr_generated_script에 새version(직전 max+1)으로 저장하며, 사용자가 CodeMirror 에서 손댄body가 있으면 그것을 우선하고 비어 있으면 골격을 저장합니다.checksum = 본문 체크섬로 드리프트를 감지합니다. InfrAuditLog에backup.job.create/backup.job.update를 기록합니다.
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.
nextRun(spec, start_at, from)—start_at이 미래면 그 자체가 다음 실행. 과거면 앵커로부터 경과 분을 인터벌로 올림해 다음 경계를 반환.start_at없으면from + interval.isDue(spec, start_at, now, tolerance)— 앵커 경과 분이 인터벌의 배수(±tolerance)면 참. 러너가 매분 tick 할 때 "이번 분에 돌 차례인가"를 정합니다.cronPrefix(spec, start_at)— 인터벌을 표준 crontab 5필드로 근사(분=*/N, 시=정시*/N, 일/주=start_at의 시:분 앵커). crontab 러너를 쓰는 대체 모델에서 사용.
현재 빌더가 저장하는
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
권한
- 읽기(빌더 열람·프리필·생성 미리보기·스크립트 조회) —
/backup/*그룹 전체가infr.level:2로 게이팅됩니다. 즉 staff(5)·admin(10) 접근, demo(1)는 백업 영역 자체 접근 불가. - 쓰기(작업·스크립트 저장
/backup/builder/save) — 컨트롤러 self-gateinfr_user_level >= 2로 이중 방어. 저장은infr_audit_log에 남습니다. - 생성 스크립트는 자격증명을 담지 않으므로 미리보기 노출은 안전합니다.
.cnf실제 값·ssh 개인키는 대상 호스트에만 존재합니다.
문제해결 · FAQ
스크립트가 아무것도 안 하고
already running만 출력하면 이전 실행의flock이 남은 것입니다./tmp/infr_backup_{job_id}.lock을 잡은 프로세스를 확인하세요. 정상 종료 시 lock 은 자동 해제됩니다.
mysqldump: unknown option/ 접속 실패 —--defaults-extra-file대상.cnf를 대상 호스트에chmod 600으로 만들었는지 확인. 값은 앱에 저장되지 않으므로 SE 가 직접 작성합니다.- 프리필이 비었습니다 — 프로젝트의
db_refs(수집기 키)·web_refs(웹서비스 id)가 실제 존재하는 레코드를 가리키는지 확인. 없으면 빈 DB 초안 1개만 제공됩니다. - DB 이름이 비어 나옵니다 — 수집기 연결의 기본 DB 가
information_schema면 의도적으로 비웁니다. 덤프 대상 DB 를 폼에서 지정하세요. - 다음 실행 시각이 예상과 다릅니다 —
next_run_at은start_at앵커 기준입니다.start_at이 미래면 그 시각이 다음 실행이고, 과거면 인터벌 경계로 올림됩니다. - 저장했는데 스크립트가 골격 그대로입니다 — CodeMirror 에서 편집한
body가 비어 있으면 골격이 저장됩니다. 편집 후 저장하면 사용자 body 가 우선하며 새version이 붙습니다. - 보고가 안 들어옵니다 — 러너가
/api/v1/backup/report로 POST 하는지, 대상 호스트에서report_url에 도달 가능한지 확인. 자세한 계약은 백업 리포터 참고.
관련 항목
- 백업 스케줄 보드 — 저장한 작업의 다음 실행·이력 시각화
- 백업 스크립트 그리드 — 빌더 모달을 여는 등록 스크립트 목록
- 백업 볼트 — 저장 대상 볼트 관리
- 백업 리포터·리포트 콜백 — 실행 결과 보고 계약
- Projects — 프리필 소스가 되는 백업 정의
- 권한 레벨 — level 게이팅 규칙
