Skip to content

English {#english}

Ansible Frappe bench rc:127 Resolution Plan

Purpose: Document the structural cause of repeated rc: 127 (No such file or directory) when running bench new-site, and plan short-, mid-, and long-term fixes.
References: ansible-implementation-plan.md, ansible-clean-and-reinstall.md, pulumi-ansible-step1-step2-plan.md §2.5 (image-based provisioning).


1. Core error

  • Symptom: bash: line 1: /home/frappe/frappe-bench/env/bin/bench: No such file or directoryrc: 127 (binary not found).
  • Implication: The failure is not bench new-site logic; the bench binary path is wrong or the file does not exist (structural).

Four structural issues: (1) bench CLI location vs workspace separation (CLI at ~/.local/bin/bench, workspace frappe-bench/ empty). (2) Docker volume mount vs bench init: mounting an empty host dir over frappe-bench overlays (hides) the image’s bench tree. (3) Init + new-site at runtime is not idempotent; failure leaves unclear state. (4) Ansible trusts “init changed” without verifying that the workspace was actually populated.


2–5. Log analysis, root cause, bench layers, essence

  • Logs: BENCH_DIR=/home/frappe/frappe-bench is empty (only . / ..); env/bin/bench missing; which bench = /home/frappe/.local/bin/bench (user-local pip). So CLI exists, workspace does not.
  • Root cause: Path mismatch (Ansible assumes env/bin/bench; image has user-local bench). Virtualenv/workspace not created or host volume overlay hid image workspace. Most likely: empty host dir mounted over /home/frappe/frappe-bench so image’s bench tree is hidden.
  • Bench layers: CLI layer (~/.local/bin/bench) ✅; workspace layer (frappe-bench/ with apps, sites, config, env) ❌ empty.
  • Conclusion: Not a simple path typo; it’s Docker volume + runtime bench init design conflict. Fix: resolve path usage and fix volume/init strategy.

6–8. Gaps, solution strategy, structural options

  • Current plan gaps: Short-term PATH/which bench removes 127 but can lead to “Not a bench directory”. Init check should validate full workspace (sites, apps, Procfile). Golden Image alone is insufficient if the whole workspace is volume-mounted; only sites should be on a volume.
  • Short-term: Use existing bench (PATH or which bench), single-string command. Mid-term: Post-init verification task (sites, apps, Procfile); fail playbook if missing. Long-term: Complete bench in image (Dockerfile) or Golden Image; Ansible does new-site + orchestration only.
  • Structural (recommended): (A) Volume only sites, not whole workspace. (B) Bench init in Dockerfile; Ansible runs new-site only. (C) Image = fixed workspace; volume = sites only. (D) Idempotent site creation (check before new-site).

9–12. SRE checklist, priority, conclusion, production structure

  • SRE: DB connection vars to new-site; directory ownership (frappe vs root); idempotency and cleanup of partial DB on retry.
  • Priority: (1) Immediately inspect docker inspect Mounts; confirm whether /home/frappe/frappe-bench is a host bind (likely root cause). (2) Stabilize: bench init in Dockerfile; sites-only volume. (3) Production: versioned app image; Ansible orchestration only.
  • Conclusion: Fast fix = use existing bench + fix volume (no whole-workspace mount; sites only). Final: build = bench init in image; run = sites-only volume; provision = new-site only.
  • Production Docker (§12): No runtime bench init; image contains complete workspace; persist only sites + logs + assets; never bind-mount over workspace.

Full tables (§1.1, §2.1–§2.2, §3.3–§3.5, §4, §6, §7.1–§7.3, §8, §10.1–§10.3) and exact wording are in the Korean section below.


한국어 {#korean}

Ansible Frappe bench rc:127 해결 기획서

목적: bench new-site 실행 시 반복되는 rc: 127 (No such file or directory) 의 구조적 원인을 정리하고, 단기·중기·장기 해결안을 기획한다.
참조: ansible-implementation-plan.md, ansible-clean-and-reinstall.md, pulumi-ansible-step1-step2-plan.md §2.5(이미지 기반 프로비저닝).


1. 핵심 에러 요약

반복적으로 발생하는 치명적 에러는 다음 한 줄이다.

bash: line 1: /home/frappe/frappe-bench/env/bin/bench: No such file or directory
rc: 127
  • Exit code 127 = 실행하려 한 바이너리를 찾을 수 없음.
  • 즉, bench new-site 로직 자체의 실패가 아니라 bench 바이너리 경로가 잘못되었거나 해당 경로에 파일이 없다는 구조적 문제이다.

1.1 문제 재정의 (단순 경로 오류가 아님)

현재 문제는 “env/bin/bench 경로 오류” 하나가 아니다.
Docker + Frappe Bench 실제 동작을 고려하면 구조적 오해와 설계 리스크가 있으며, 단순 “경로 수정”을 넘어 벤치 초기화 모델 자체가 불안정한 구조이다.

실제 구조적 문제 4가지:

#문제설명
1bench 실행 위치와 bench workspace 분리CLI(~/.local/bin/bench)는 있는데, workspace(frappe-bench/)가 비어 있음.
2Docker volume 마운트와 bench init 충돌host 빈 디렉터리를 frappe-bench에 마운트하면, 이미지 내부 bench 결과가 가려짐(overlay).
3벤치 초기화와 사이트 생성의 책임 분리 실패runtime에 init + new-site를 연달아 실행하는 설계는 idempotent하지 않고, 실패 시 상태 불명확.
4Ansible이 상태를 신뢰하지만 검증하지 않음init “changed”만 보고 성공으로 간주하고, workspace가 실제로 채워졌는지 검증하지 않음.

2. 로그 기반 구조 분석

2.1 디버그 결과 (실제 로그)

[frappe_site] PWD=/home/frappe
[frappe_site] BENCH_DIR=/home/frappe/frappe-bench
total 12
drwxr-xr-x 2 root root 4096 Feb 21 15:30 .
drwxr-xr-x 1 frappe frappe 4096 Feb 21 15:30 ..
[frappe_site] BENCH_BIN
ls: cannot access '/home/frappe/frappe-bench/env/bin/bench': No such file or directory
[frappe_site] which bench=/home/frappe/.local/bin/bench
[frappe_site] env/bin/bench exists? no

2.2 의미 정리

항목상태의미
/home/frappe/frappe-bench비어 있음 (. / .. 만 존재)bench init 결과물이 이 경로에 없음. sites/, config/, env/ 미존재.
/home/frappe/frappe-bench/env/bin/bench❌ 없음virtualenv가 이 경로에 생성되지 않았거나, 디렉터리 자체가 없음.
which bench/home/frappe/.local/bin/bench이미지에서 bench는 user-local pip 설치 형태로 존재.
bench init 태스크changedinit은 실행되었으나, 마운트된 frappe-bench 디렉터리는 비어 있음.

3. 구조적 원인 (근본 원인)

3.1 경로 불일치 (Path Mismatch)

  • Ansible이 가정한 경로: /home/frappe/frappe-bench/env/bin/bench (virtualenv 내부 bench).
  • 실제 사용 가능한 경로: /home/frappe/.local/bin/bench (전역·user-local bench).
  • 이 이미지/실행 환경에서는 bench가 virtualenv 기반이 아니라 user-local pip 방식으로 설치되어 있다.

3.2 virtualenv 미생성 / 디렉터리 비어 있음

  • bench init 은 실행되었으나, 마운트된 /home/frappe/frappe-bench 에는 env/, sites/, config/ 등이 생성되지 않음.
  • 따라서 env/bin/bench 는 “경로만 잘못된 것”이 아니라 해당 디렉터리·파일이 아예 없는 상태이다.

3.3 원인 가능성 (우선순위)

가능성설명
1 (가장 유력)bench init--skip-redis-config-generation 또는 Python/venv 관련 옵션·환경 문제로 virtualenv 생성 단계가 실패했으나, Ansible은 exit code 0 등으로 성공으로 간주.
2bench init다른 경로에 결과를 썼고, 마운트된 /home/frappe/frappe-bench 는 비어 있음 (마운트 포인트가 이미 존재해 init이 별도 경로에 생성했을 수 있음).
3이미지 내부: Python venv 생성 권한 문제, python3-venv 미설치, root로 init 실행 등으로 venv가 생성되지 않음.
4pip install --user frappe-bench 형태로만 설치된 환경이라, venv를 만들지 않는 이미지 설계.

3.4 Docker 관점: Volume이 bench 결과를 덮어씀 (가장 유력)

로그에서 /home/frappe/frappe-bench. / .. 만 존재하는 이유로 가장 가능성 높은 시나리오:

  1. Docker 이미지 안에서 이미 bench init이 완료된 상태일 수 있음.
  2. 컨테이너 실행 시 -v host_dir:/home/frappe/frappe-bench빈 host_dir을 마운트.
  3. 빈 host_dir이 마운트되며 이미지 내부 bench 결과가 가려짐(overlay).

즉, init은 이미지에서 되었지만, 실행 시 host volume이 덮어써서 bench 구조가 사라진다.
이는 Docker에서 흔히 발생하는 실수이며, 가장 먼저 확인할 것docker inspect <container>Mounts 항목에서 /home/frappe/frappe-bench가 host bind mount인지 여부이다. 이게 root cause일 가능성이 매우 높다.

3.5 기타 Docker/설계 관점 문제

문제설명
Runtime bench init컨테이너 기동 후 docker exec bench initbench new-site 방식은 idempotent하지 않고, race·실패 시 상태 불명확. Frappe bench workspace는 “build artifact”에 가깝고, runtime에서 생성하는 것은 불안정.
root vs frappe 권한로그에서 frappe-benchroot:root 소유. bench는 보통 frappe 유저 기반. 권한 불일치 시 venv/sites 생성 실패·silent failure 후 exit 0 가능.
virtualenv 전제 가정“env/bin/bench가 항상 존재해야 한다”는 가정 자체가 위험. 최근 Frappe/bench는 global bench + workspace env, poetry, custom image 등 다양한 구조 존재.

4. Frappe Bench 실제 동작 원리

bench는 두 레이어로 구분된다.

레이어경로/역할현재 상태
CLI Layer~/.local/bin/bench — CLI 실행 파일✅ 존재
Workspace Layerfrappe-bench/ 아래 apps/, sites/, config/, env/❌ 비어 있음

즉, CLI만 있고 project workspace는 없는 상태이다.
Workspace가 비어 있으면 PATH 수정으로 127은 제거할 수 있으나, bench new-site 실행 시 “Not a bench directory” 또는 “sites directory not found” 등 다음 에러가 발생할 수 있다.


5. 반복 에러의 본질

  • 에러 패턴은 항상 동일: rc: 127, No such file or directory.
  • 정리하면 다음 두 가지가 동시에 존재한다.
    1. 경로 하드코딩: 존재하지 않는 env/bin/bench 를 호출.
    2. venv/디렉터리 미생성 또는 Volume overlay: bench init 이 기대한 구조를 해당 경로에 만들지 못했거나, host volume 마운트가 이미지 내 workspace를 덮어썼음.

결론: 현재 문제는 단순 “env/bin/bench 경로 오류”가 아니라 Docker volume + runtime bench init 설계 충돌 문제이다.


6. 현재 기획서의 개선 필요 부분

#기획서 내용한계
1단기: PATH/which benchWorkspace가 비어 있으면 127 제거는 가능하지만, 이어서 “Not a bench directory” / “sites directory not found” 발생. 근본 해결 아님.
2init 검증: test -f env/bin/benchenv만 보면 불완전. 더 정확한 검증은 test -d sites, test -d apps, test -f Procfile 등 workspace 구조 전체.
3Golden ImageGolden Image만으로는 부족. Volume을 그대로 전체 workspace에 마운트하면 동일 문제 반복. sites만 volume화해야 함.

7. 해결 전략 (단계별)

7.1 단기: 즉시 적용 (경로 수정)

목표: 존재하는 bench 실행 파일을 사용하여 rc:127 제거.

방안내용장점비고
A. 전역 bench + PATHexport PATH=$PATH:/home/frappe/.local/bin, cd /home/frappe/frappe-bench, bench new-site ...구현 간단, 127 제거 가능frappe-bench 가 비어 있으면 new-site 단계에서 “bench 디렉터리 아님” 등 다른 에러 가능.
B. 동적 경로실행 전 which bench(또는 docker exec ... which bench)로 경로를 구하고, 그 경로로 bench 호출.경로 하드코딩 제거, 이미지/환경 차이에 강함.한 번만 조회해 변수로 재사용하면 됨.
C. 단일 문자열 명령bench new-site ...bench 로만 호출 (PATH 보정 후). Ansible에서 command문자열 하나로 전달해 exec: "[bash," 같은 오류 방지.이전에 발생한 docker exec 리스트 파싱 문제 회피.현재 역할에서 이미 문자열 command 사용 중이면 유지.

권장 (단기): B + C — 컨테이너에서 which bench 로 경로를 한 번 구하고, 해당 경로(또는 PATH 보정 후 bench)로 문자열 command 실행.
이때 옵션은 현재 스펙 유지: DB 분리형이므로 --db-host, --db-port, --db-root-password, --no-mariadb-socketsite_name/canonical_hostname.


7.2 중기: bench init 검증 및 venv 보장

목표: init이 실패했거나 venv를 만들지 않았을 때, new-site 전에 감지하고 실패 처리.

방안내용
init 옵션 명시bench init frappe-bench --python python3 등으로 virtualenv 생성 유도 (이미지·bench 버전에서 지원 시).
검증 태스크init 직후 docker exec ... test -f /home/frappe/frappe-bench/env/bin/bench 또는 test -d /home/frappe/frappe-bench/sites 실행. 실패 시 playbook 실패 처리.
init 실패 시 로그init 태스크의 stdout/stderr를 등록하고, 실패 시 “[frappe_bench] bench init failed: …” 형태로 출력해 원인 파악 가능하게 함.

권장 (중기): init 직후 검증 태스크 추가. env/bin/bench 뿐 아니라 sites/, apps/, Procfile 등 workspace 구조 전체 확인. 없으면 실패로 두고, init 옵션·이미지·Volume 마운트를 함께 점검.


7.3 장기: 아키텍처 개선 (안정성·멀티테넌트)

목표: runtime bench init 의존도를 낮추고, 경로·환경을 단일하게 만든다.

방안내용효과
이미지에서 bench 완성Dockerfile 또는 별도 빌드에서 RUN bench init ...bench 완성 상태까지 이미지에 포함. Ansible은 사이트 생성(new-site)·API 키만 수행.runtime init 제거, 경로·venv 항상 동일.
Golden Image첫 성공 설치 후 App 서버를 Hetzner 스냅샷/이미지로 저장하고, 이후 프로비저닝 시 그 이미지에서 기동. pulumi-ansible-step1-step2-plan.md §2.5 참고.설치 시간 단축, init 실패 가능성 감소.
경로 동적 탐지Ansible에서 bench 경로를 하드코딩하지 않고, 컨테이너 내 which bench 또는 env/bin/bench 존재 시 그 경로 사용.이미지/버전이 바뀌어도 동일 playbook 유지.

권장 (장기): 이미지에서 bench 완성 또는 Golden Image 중 하나를 도입하고, Ansible은 orchestration(사이트 생성·API 키·아티팩트) 만 담당하도록 정리.


8. 구조적 개선안 (권장 아키텍처)

내용효과
A. Workspace를 Volume 전체가 아니라 sites만현재(추정): -v host_dir:/home/frappe/frappe-bench. 권장: bench workspace는 이미지 내부에 두고, sites만 별도 volume-v sites_data:/home/frappe/frappe-bench/sites. apps/config/venv는 고정, sites만 영속.Volume overlay로 workspace가 가려지는 문제 제거.
B. Bench init은 Dockerfile에서 수행Dockerfile에서 RUN bench init frappe-bench --frappe-branch version-15, WORKDIR /home/frappe/frappe-bench. Ansible에서는 new-site만 실행, init 제거.runtime init 제거, 가장 안정적.
C. 컨테이너는 immutable, Site는 mutableImage = bench workspace 고정. Container = 실행 환경. Volume = sites만. Ansible = new-site orchestration만.책임 분리, drift 제거.
D. Idempotent site 생성new-site 전에 bench --site <site> list-apps 또는 MariaDB SHOW DATABASES LIKE 'site_db' 로 검증 후 생성. 실패 시 부분 생성(DB만 등) cleanup 옵션.재시도·멱등성 보장.

9. SRE 관점 체크리스트 (현재 적용 가능)

  • DB 연결: --db-host, --db-port, --db-root-password 가 인벤토리·play 변수에서 new-site까지 정확히 전달되는지 확인.
  • 권한: /home/frappe/frappe-bench 가 root 소유이면 frappe 유저로 쓰기 시 실패할 수 있음. init/new-site 실행 사용자와 디렉터리 소유권 일치 확인.
  • 멱등성: 실패한 new-site 시도로 MariaDB에 DB만 생성됐을 수 있음. 재시도 전 해당 DB 존재 여부 확인 후 필요 시 drop 하고 재실행.

10. 구현 우선순위 요약

10.1 기존 우선순위 (참고)

순서구분작업산출물
1단기frappe_site: 동적 경로(which bench) 또는 PATH 보정 + bench, command 문자열 유지.roles/frappe_site/tasks/main.yml
2단기(선택) frappe_bench: init 직후 sites/apps/Procfile 등 검증, 실패 시 명시적 실패.roles/frappe_bench/tasks/main.yml
3중기bench init 옵션·이미지·Volume 마운트 점검.문서화, defaults
4장기Dockerfile에서 bench 완성 또는 Golden Image, Ansible은 orchestration만.별도 기획

10.2 개선된 우선순위 제안

단계내용
1단계 (즉시)Volume 마운트 구조 점검. docker inspect <container>Mounts에서 /home/frappe/frappe-bench가 host bind mount인지 확인. bench init 결과가 실제로 어디에 생성되는지 확인. init 직후 ls -la 전체 출력 로그 남김.
2단계 (구조 안정화)Dockerfile에서 bench init. Ansible에서 init 제거. sites만 volume화 (workspace 전체 마운트 제거).
3단계 (Production)App Image versioned build. DB external(이미 분리형 OK). Site provisioning pipeline 분리. Ansible은 orchestration only.

10.3 가장 먼저 확인할 것

Terminal window
docker inspect <container>

에서 Mounts 항목 확인. /home/frappe/frappe-bench가 host bind mount이면 root cause일 가능성 매우 높음.


11. 결론 및 최종 권장 방향

  • 현재 장애의 정확한 원인:
    /home/frappe/frappe-bench/env/bin/bench 경로가 존재하지 않는 상태. 원인은 (1) 경로 하드코딩 + (2) virtualenv/workspace 미생성 또는 Docker volume이 이미지 내 workspace를 덮어쓴 것.
    즉, “env/bin/bench 경로 오류”가 아니라 Docker volume + runtime bench init 설계 충돌 문제.

  • 가장 빠른 수정 (임시):
    Ansible에서 존재하는 bench 사용(PATH 보정 또는 which bench). 동시에 Volume 마운트를 점검하고, 가능하면 workspace 전체 마운트 제거·sites만 volume으로 전환.

  • 최종 권장 구조:

    • Build 단계: bench init 포함한 App Image 생성 (Dockerfile에서 수행).
    • Run 단계: sites만 volume (workspace 전체 마운트 금지).
    • Provision 단계: bench new-site만 실행 (init 제거).
    • Ansible: orchestration 전용. 테넌트 생성은 Provisioner Job으로 분리 권장.
  • Production에서는 bench 경로: which bench는 예비 수단. 이미지 내부 고정 경로(예: /opt/bench/env/bin/bench)가 더 안전.


12. 완전 안정형 Production Docker 구조 (권장)

12.1 목표

  • 컨테이너 기동 시점에 bench init 금지 (runtime init 제거).
  • 이미지에 완성된 bench workspace 포함 (apps/, env/, config 고정).
  • 영속 데이터는 sites + logs + assets 만 분리.
  • “한 번 빌드 → 어디서나 동일 실행” (drift 제거).

12.2 권장 디렉터리/볼륨 설계

  • 이미지 내부(Immutable): /opt/bench (workspace 전체).
  • 영속 볼륨(Mutable): /var/lib/frappe/sites, /var/log/frappe, /var/lib/frappe/assets(옵션).

절대 금지: /opt/bench(또는 workspace 전체)를 bind mount로 덮어쓰기.
빈 디렉터리 마운트가 workspace를 “가려서” 현재와 동일한 장애를 재발시킴.

12.3 컨테이너 역할 분리 (최소)

  • backend (gunicorn / frappe)
  • nginx (reverse proxy + static)
  • queue-short, queue-long, scheduler (worker)
  • socketio (옵션)
  • job-provisioner (원샷 job: new-site / migrate / install-app 등)
flowchart TB
  subgraph Image[Immutable App Image]
    W[bench workspace /opt/bench\napps, env, config fixed]
  end
  subgraph Volumes[Persistent Volumes]
    S[sites volume\n/var/lib/frappe/sites]
    L[logs volume\n/var/log/frappe]
    A[assets volume optional\n/var/lib/frappe/assets]
  end
  subgraph Runtime[Containers]
    N[nginx]
    B[backend]
    QS[queue-short]
    QL[queue-long]
    SC[scheduler]
    P[job-provisioner one-shot]
  end
  N --> B
  B --> S
  QS --> S
  QL --> S
  SC --> S
  P --> S
  B --> L
  QS --> L
  QL --> L
  SC --> L
  N --> A

12.4 운영 안정성 체크포인트

  • read-only rootfs(가능하면): backend/worker는 workspace에 쓰지 못하게.
  • 컨테이너는 frappe 유저로 실행, sites/logs 볼륨만 write.
  • healthcheck는 “포트 오픈”이 아니라 “site 접근/DB 연결” 기준.

13. 멀티테넌트 SaaS용 Bench 아키텍처

13.1 핵심 원칙

  1. 벤치(workspace)는 앱 버전 단위로 고정: bench@vX.Y.
  2. 테넌트는 site 단위로만 증가: sites/<tenant-domain>.
  3. 신규 테넌트 생성은 별도 프로비저닝 파이프라인이 수행 (backend 컨테이너가 하지 않음).
  4. 테넌트 메타데이터는 Control Plane DB에서 관리 (site 존재/상태/버전/리전).

13.2 데이터/메타 모델 (권장)

  • tenant_id, site_fqdn, db_name, db_host, db_user (권장: per-tenant user), bench_image_tag, status (provisioning|active|failed|migrating|disabled), region, node_pool, created_at.

13.3 요청 흐름 (테넌트 생성)

  • User/Signup → Control Plane API → Provision Queue → Provisioner Job → MariaDB(db+user) + Sites Volume → bench new-site + install-apps + migrate → Control Plane status=active. Runtime는 Host 헤더 기반으로 해당 site 서빙.

13.4 프로비저닝 안정화 규칙 (필수)

  • Idempotent: 동일 tenant_id 재시도 시 안전하게 재실행.
  • : tenant 단위 분산락(Redis lock / DB row lock).
  • 사전검증: sites에 이미 디렉터리 있으면 상태 확인. DB에 db_name 존재하면 소유/권한 확인.
  • 실패정리: 실패 시 status=failed + 원인 로그. “DB만 생기고 site 없음” 같은 부분 실패 감지 후 cleanup 옵션.

13.5 bench 경로 정책

  • 프로비저너는 이미지 내부 고정 경로만 사용 (예: /opt/bench/env/bin/bench). Production에서는 고정 경로가 더 안전.

14. Hetzner + Pulumi + Ansible 정합 설계

14.1 계층별 책임 분리

  • Pulumi (Infra as Code): 네트워크/서버/볼륨/방화벽/로드밸런서/DNS/서브넷/서버그룹 생성.
  • Ansible (Config + Orchestration): OS baseline, Docker/Compose 설치, secrets 배치, systemd, 런타임 배포.
  • Provisioning Job (앱 레벨): bench new-site, install-app, migrate, set-config 등 테넌트 생성/변경.

Ansible이 테넌트를 직접 만들지 말고, Provisioner Job 실행을 “트리거”하는 쪽이 안정적.

14.2 권장 인프라 구성 (Hetzner)

  • DB 노드: 1~3대 (초기 1, 성장 시 Galera/Primary-Replica).
  • App 노드: 2대 이상 (롤링/무중단).
  • Shared storage: 단순은 각 App 노드 로컬 볼륨 + 테넌트 pinning; 권장은 S3 호환 Object Storage(백업/첨부) + sites 노드별 분산.
  • LB: Hetzner Load Balancer + healthcheck. DNS: wildcard *.example.com → LB.

14.3 실전 충돌 방지 규칙

  1. Pulumi가 만든 리소스 식별자(IP/볼륨ID)를 Ansible 인벤토리에 자동 주입 (Pulumi output → inventory.yml 또는 dynamic inventory).
  2. Ansible은 볼륨 마운트까지 책임, 마운트 지점 표준화: /var/lib/frappe/sites, /var/log/frappe.
  3. Ansible은 앱 이미지 태그를 변수로 고정 (app_image_tag=erpnext-v15.XX). 배포/롤백은 태그 교체로만.
  4. Provisioner Job도 동일 이미지 사용 (runtime와 provisioner bench 버전 drift 방지).

14.4 Ansible Role 분해 (권장)

  • common_base, docker_engine, mount_volumes (sites/logs), app_runtime (compose up), app_provisioner (job 실행 트리거), observability.

14.5 정합 아키텍처 (계층 관계)

flowchart LR
  subgraph Pulumi
    VPC[Network/Subnet]
    FW[Firewall]
    LB[Load Balancer]
    APP[App Servers xN]
    DB[(DB Server/Cluster)]
    VOL[Volumes sites/logs]
    DNS[DNS Wildcard]
  end
  subgraph Ansible
    BASE[OS baseline users/ssh/ufw]
    DOCKER[Docker/Compose]
    RUNTIME[Deploy runtime stack]
    SECRETS[Secrets + env]
    MON[Monitoring/Log shipping]
  end
  subgraph AppOps
    CP[Control Plane API]
    Q[Queue]
    PROV[Provisioner Job]
  end
  DNS --> LB --> APP
  APP --> DB
  APP --> VOL
  APP --> BASE --> DOCKER --> RUNTIME
  RUNTIME --> SECRETS
  CP --> Q --> PROV
  PROV --> DB
  PROV --> VOL

14.6 배포/프로비저닝 운영 플로우

  • Step 1 (Pulumi): infra up → outputs 생성.
  • Step 2 (Ansible): 앱 런타임 up (테넌트 없음 상태도 정상).
  • Step 3 (Control Plane): 테넌트 생성 요청 → Provisioner Job 실행.
  • Step 4 (검증): /api/method/ping + site별 헬스체크 + DB/권한 확인.
  • Step 5 (백업/DR): DB daily logical + binlog; sites는 rsync 스냅샷 또는 object storage sync.

15. 즉시 적용 우선순위 (rc:127 재발 방지에 직결)

  1. workspace 전체 마운트 제거: /home/frappe/frappe-bench 같은 전체 덮어쓰기 금지.
  2. 이미지 내부 고정 경로로 bench 실행 (프로비저너): bench init 없는 상태에서 new-site만 수행하도록 설계 전환.
  3. sites만 persistent volume화: /var/lib/frappe/sites.
  4. Provisioner Job 분리: runtime 컨테이너에서 테넌트 생성 수행 금지.

16. 참조 문서

문서용도
ansible-implementation-plan.mdFrappe/bench 설치 방식 및 role 구조
ansible-clean-and-reinstall.md초기화 후 재설치 절차
pulumi-ansible-step1-step2-plan.md§2.5 이미지 기반 프로비저닝, Runbook 리소스
provision-tenant-pipeline.mdAnsible 실행 순서 및 변수

이 기획서에 따른 구체적 수정안(role별 task 단위)은 별도 이슈 또는 구현 단계에서 정리한다.

Help