English {#english}
prego-docker Implementation Plan (Custom Apps Build + Auto Rebuild)
Purpose: Specify files, folders, workflows, and Secrets for prego-docker so implementation can follow directly.
No code generation — plan, file tree, requirements, tag policy, integration only.
- Target:
/Users/marco/prego-docker(image build, config, custom app definition). - Two axes: (1) Single optimized image from Frappe official custom Containerfile (ERPNext + HRMS + prego-saas-app). (2) Auto rebuild when custom app changes (prego-saas-app → repository_dispatch → prego-docker).
- Related: prego-docker-github-actions-docker-hub-plan, frappe-docker-hrms-custom-image-build-plan, docker-hub-stack-ansible-deploy-plan; resource-optimization-safe-adoption-plan (Gunicorn, plan limits).
Current: apps.json with ERPNext, HRMS, prego-saas-app (tag e.g. v1.0.0); single bench init; no APP_SHA block; clone_token for private; repository_dispatch only; Verify image apps (4 apps); trivy-scan (Critical 0).
1. Design: single image (no merge)
Recommended: Base on official Frappe/ERPNext image; one runtime image with apps.json (ERPNext + HRMS + prego-saas-app). Not: merging multiple images. Forbidden: bench get-app/install-app at runtime; production = immutable image.
2–7. File tree, Dockerfile, apps.json, build.yml, repository_dispatch, secrets
- §2 Tree: Dockerfile, apps.json, .dockerignore, README; resources/core/nginx/; .github/workflows/build.yml, trivy-scan.yml.
- §3 Vendoring from frappe_docker: Containerfile → Dockerfile; nginx-template.conf, nginx-entrypoint.sh. Dockerfile: Base (frappe user, nginx, node, wkhtmltopdf), Builder (APPS_JSON_BASE64, bench init), backend (COPY, VOLUME, CMD gunicorn).
- §4 apps.json: ERPNext version-15, HRMS version-15, prego-saas-app tag (e.g. v1.0.0). Workflow Base64 for APPS_JSON_BASE64; tag validated via GitHub API.
- §5 build.yml: Trigger repository_dispatch
custom_app_updatedonly. Steps: checkout, validate tag, setup-qemu/buildx, Docker Hub login, APPS_JSON_BASE64, set tag, build & push (iamfork/prego-repo:$TAG, latest), Verify image apps. - §6 prego-saas-app: notify-image-rebuild.yml — on push/tag v*.., POST dispatches to prego-docker with event_type custom_app_updated; PREGODOCKER_DISPATCH_PAT.
- §7 Secrets: prego-docker (DOCKERHUB_*, PREGO_SAAS_APP_CLONE_TOKEN); prego-saas-app (PREGODOCKER_DISPATCH_PAT).
Full tables (§1–§7) and exact wording are in the Korean section below.
한국어 {#korean}
기획서: prego-docker 코드 구현 상세 (Custom Apps 포함 빌드 + 자동 재빌드 전략)
목적: prego-docker 레포(/Users/marco/prego-docker)에 생성할 파일·폴더·워크플로우·Secrets를 구현 단계에서 그대로 따라 쓸 수 있는 형태로 명세한다.
코드 생성 없음 — 기획·파일 트리·요구사항·태그 정책·연동 시나리오만 정리.
- 대상 폴더:
/Users/marco/prego-docker(GitHub base repository: 이미지 빌드·설정·custom app 정의). - 두 가지 구현 축: (1) Frappe 공식 production/custom Containerfile 기반 최적화 빌드(Custom Apps 포함), (2) custom app 변경 시 자동 이미지 재빌드(Repo A → Repo B 트리거).
- 연관 문서: prego-docker-github-actions-docker-hub-plan.md, frappe-docker-hrms-custom-image-build-plan.md, docker-hub-stack-ansible-deploy-plan.md. Gunicorn 옵션·컨테이너 자원 제한(플랜별): resource-optimization-safe-adoption-plan.md.
현재 반영 (tag 기반 pinning·검증·취약점): apps.json에 ERPNext(version-15), HRMS(version-15), prego-saas-app(태그 예: v1.0.0) 포함. 단일 bench init 만 사용하며, Dockerfile의 APP_SHA 블록은 제거됨. private 레포인 경우 clone_token으로 bench init 시 clone URL만 치환. 빌드 트리거는 repository_dispatch 전용(push 없음). 푸시 후 Verify image apps 단계로 4개 앱 존재 검증, trivy-scan.yml로 취약점 스캔(정책: Critical 0). 상세: prego-docker-exit-128-duplicate-clone-resolution-plan.md, prego-docker-image-verify-and-vulnerability-plan.md.
1. 설계 원칙: 이미지 통합 빌드 (합치기 방식 비권장)
| 구분 | 내용 |
|---|---|
| 권장 | 공식 Frappe/ERPNext 이미지를 베이스로 두고, apps.json으로 ERPNext + HRMS + custom app(prego-saas-app)을 포함한 단일 Runtime 이미지를 재빌드. |
| 비권장 | 여러 이미지(erpnext-image + hrms-image + custom-app-image)를 “합쳐서” 하나로 쓰는 방식. Docker는 실행 시 이미지 병합을 지원하지 않음. |
| 금지 | 실행 중 bench get-app / bench install-app으로 앱 추가. Production에서는 코드가 이미지에 포함된 immutable image가 전제. |
Bench 환경은 apps/ 아래에 erpnext, hrms, prego_saas_app 등이 물리적으로 설치되어 있어야 하며, SaaS 멀티테넌트는 코드=이미지, Site=데이터 구조로 단일 통합 Runtime 이미지가 표준이다.
2. prego-docker 최종 파일 트리 (구현 시 산출물)
대상: Pregoi/prego-docker (로컬 경로: /Users/marco/prego-docker).
prego-docker/├── Dockerfile├── apps.json├── .dockerignore├── README.md│├── resources/│ └── core/│ └── nginx/│ ├── nginx-template.conf│ └── nginx-entrypoint.sh│└── .github/ └── workflows/ ├── build.yml └── trivy-scan.yml- Dockerfile: frappe_docker
images/custom/Containerfile패턴 기반. Base·Builder·backend 멀티 스테이지,APPS_JSON_BASE64·FRAPPE_BRANCH·PYTHON_VERSION빌드 인자, VOLUME·gunicorn CMD. - resources/core/nginx/ : 공식 custom Containerfile이
COPY하는 두 파일. Vendoring(복사) 필수. 소스: frappe_docker 리포 동일 경로. - apps.json: 앱 목록(ERPNext version-15, HRMS version-15, prego-saas-app 태그 예: v1.0.0). 단일 소스; 워크플로는 checkout된 파일을 Base64로 전달.
- .dockerignore: 빌드 컨텍스트 경량화(.git, .github, README 등).
- README.md: 빌드 원리, Secrets, 멀티아키텍처, custom app 자동 재빌드 흐름, (선택) Hetzner/운영 안내.
- .github/workflows/build.yml: repository_dispatch 전용 (custom_app_updated). 태그 유효성 검사(GitHub API), 레포 apps.json → APPS_JSON_BASE64, build-push·캐시·태그, 푸시 후 Verify image apps 단계.
- .github/workflows/trivy-scan.yml: 취약점 스캔(workflow_dispatch·주기). 정책: CRITICAL 시 실패. prego-docker-image-verify-and-vulnerability-plan.md §5.
3. Frappe 공식 custom Containerfile 기반 Vendoring
3.1 복사할 소스 (frappe_docker)
| prego-docker 경로 | frappe_docker 소스 | 비고 |
|---|---|---|
Dockerfile | images/custom/Containerfile | 내용 기반. 버전 고정: FRAPPE_BRANCH=version-15, PYTHON_VERSION=3.11. |
resources/core/nginx/nginx-template.conf | resources/core/nginx/nginx-template.conf | 그대로 복사. |
resources/core/nginx/nginx-entrypoint.sh | resources/core/nginx/nginx-entrypoint.sh | 그대로 복사. |
공식 custom Containerfile은 위 nginx 두 파일을 COPY하므로 prego-docker에도 동일 경로로 두어야 빌드가 성공한다.
3.2 Dockerfile 요구사항 (구현 시 반영)
| 항목 | 내용 |
|---|---|
| 베이스 | python:${PYTHON_VERSION}-slim-bookworm. ARG: PYTHON_VERSION=3.11, WKHTMLTOPDF, NODE_VERSION=20. |
| Base 스테이지 | frappe 사용자 생성, nginx·node(nvm)·wkhtmltopdf·frappe-bench 설치, templates/nginx·nginx-entrypoint.sh 복사·권한. |
| Builder 스테이지 | APPS_JSON_BASE64 있으면 /opt/frappe/apps.json으로 decode. USER frappe 전환 후 bench init --apps_path=... --frappe-branch=version-15 등으로 앱 설치. common_site_config.json 초기화. |
| backend 스테이지 | Builder 결과물 COPY, WORKDIR, VOLUME(sites, sites/assets, logs), CMD gunicorn. |
| 고정 인자 | FRAPPE_BRANCH=version-15, (선택) FRAPPE_PATH=https://github.com/frappe/frappe. |
구현 시 frappe_docker 최신 images/custom/Containerfile을 참고해 위 구조와 동일하게 작성한다.
4. apps.json 정의
현재 구현: apps.json에 ERPNext(version-15), HRMS(version-15), prego-saas-app(태그 예: v1.0.0) 세 가지 모두 포함. bench init 한 번으로 설치. prego-saas-app이 private이면 빌드 시 PREGO_SAAS_APP_CLONE_TOKEN으로 clone URL만 치환. Dockerfile에 APP_SHA 블록 없음.
4.1 레포에 커밋된 apps.json
| 앱 | url | branch |
|---|---|---|
| ERPNext | https://github.com/frappe/erpnext | version-15 |
| HRMS | https://github.com/frappe/hrms | version-15 |
| prego-saas-app | https://github.com/Pregoi/prego-saas-app | v1.0.0 (Git 태그) |
워크플로는 checkout된 apps.json을 Base64로 APPS_JSON_BASE64에 넣어 Dockerfile에 전달. prego-saas-app 태그는 build.yml의 “Validate prego-saas-app tag exists” 단계에서 GitHub API로 존재 여부 검사.
(4.2 구 절 제거: 현재는 tag 기반 단일 bench init만 사용, commit pin/APP_SHA 블록 없음.)
5. prego-docker GitHub Actions: build.yml
5.1 트리거
| 트리거 | 용도 |
|---|---|
repository_dispatch types: custom_app_updated | prego-saas-app에서 custom app 변경 시 prego-docker 빌드 자동 실행. 유일한 빌드 트리거(push·tag 트리거 없음). |
5.2 단계 요구사항 (순서)
| 순서 | 단계 | 내용 |
|---|---|---|
| 1 | checkout | actions/checkout@v4. |
| 2 | Validate prego-saas-app tag exists | apps.json에서 prego-saas-app의 branch(태그) 추출, GitHub API로 Pregoi/prego-saas-app refs/tags/ |
| 3 | setup-qemu · setup-buildx | docker/setup-qemu-action@v3, docker/setup-buildx-action@v3. |
| 4 | login Docker Hub | docker/login-action@v3, DOCKERHUB_USERNAME, DOCKERHUB_TOKEN. |
| 5 | Prepare APPS_JSON_BASE64 | 레포의 apps.json을 base64로 GITHUB_ENV에 설정. |
| 6 | Set Tag | client_payload.tag 또는 pregoapp-<short_sha>, 항상 latest도 푸시. |
| 7 | Build & Push | build-args: APPS_JSON_BASE64, FRAPPE_BRANCH=version-15. secrets: clone_token(PREGO_SAAS_APP_CLONE_TOKEN). tags: iamfork/prego-repo:$TAG, iamfork/prego-repo:latest. |
| 8 | Verify image apps | 푸시한 이미지 pull 후 docker run ... ls .../apps/ 로 frappe, erpnext, hrms, prego_saas_app 존재 검사. 누락 시 job 실패. |
이미지 이름: iamfork/prego-repo.
5.3 태그 정책 요약
| 상황 | 이미지 태그 예시 |
|---|---|
| repository_dispatch (custom_app_updated) | pregoapp-<short_sha>, latest (또는 client_payload.tag가 있으면 해당 태그 + latest). |
6. custom app 변경 시 자동 재빌드: repository_dispatch
6.1 전략
prego-saas-app(Repo A) push 시 prego-docker(Repo B)의 workflow를 repository_dispatch로 호출하는 방식. GitHub 커뮤니티 표준 패턴.
6.2 prego-saas-app 쪽 산출물 (구현 시)
대상: Pregoi/prego-saas-app (별도 레포).
| 경로 | 내용 |
|---|---|
.github/workflows/notify-image-rebuild.yml | prego-saas-app의 main/staging/develop push 및 tag v*.. 시, prego-docker로 repository_dispatch 전송. payload에 tag·app_sha 포함. |
6.3 notify-image-rebuild.yml 요구사항
| 항목 | 내용 |
|---|---|
| 트리거 | push branches: main, staging, develop / tags: v*... |
| 단계 | curl로 POST https://api.github.com/repos/Pregoi/prego-docker/dispatches. event_type: custom_app_updated. client_payload: tag(태그 push면 태그명, 아니면 pregoapp-<short_sha>), app_sha: GITHUB_SHA. |
| 인증 | Authorization: Bearer ${{ secrets.PREGODOCKER_DISPATCH_PAT }}. |
prego-docker가 private이면 PAT scope repo 필요. public이면 최소 권한 PAT 또는 GitHub App 토큰 권장.
6.4 prego-saas-app GitHub Secret
| Secret | 설명 |
|---|---|
| PREGODOCKER_DISPATCH_PAT | prego-docker 레포를 호출할 수 있는 Personal Access Token. prego-docker가 private이면 repo scope. |
7. GitHub Secrets 정리
7.1 prego-docker
| Secret | 용도 |
|---|---|
| DOCKERHUB_USERNAME | Docker Hub 로그인 아이디. |
| DOCKERHUB_TOKEN | Docker Hub Access Token (Account Settings → Security). |
| APPS_JSON_BASE64 | 기본 빌드용. 레포의 apps.json 내용을 base64 인코딩한 값. repository_dispatch가 아닌 push 빌드에서 사용. |
7.2 prego-saas-app
| Secret | 용도 |
|---|---|
| PREGODOCKER_DISPATCH_PAT | prego-docker repository_dispatch 호출용 PAT. |
8. 동작 흐름 요약
[기본 빌드] main/staging/develop push 또는 v*.*.* tag push → prego-docker build.yml 실행 → APPS_JSON_BASE64 = Secret 또는 레포 apps.json base64 → 멀티아키텍처 빌드·푸시 (latest / 태그)
[Custom app 변경 시 재빌드] prego-saas-app push (main/staging/develop 또는 tag) → notify-image-rebuild.yml 실행 → repository_dispatch → prego-docker (event_type: custom_app_updated, client_payload: app_sha, tag) → prego-docker build.yml 실행 → pinned apps.json 생성 (prego-saas-app = app_sha) → 멀티아키텍처 빌드·푸시 (pregoapp-<sha> 등)9. (선택) Production Compose · Staging→Prod 승격 · Blue/Green
아래는 구현 순서에서 나중 단계 또는 별도 Runbook/레포로 둘 수 있는 항목이다. 기획만 명세.
9.1 Production docker-compose
- 목적: MariaDB·Redis 분리, sites·logs 볼륨 분리. 이미지:
iamfork/prego-repo:${IMAGE_TAG}또는 digest. - 구성: mariadb, redis, admin(사이트 생성/마이그레이션용 1회성·대기 컨테이너), internal 네트워크. 환경변수: DB_HOST, REDIS_CACHE/QUEUE/SOCKETIO, SOCKETIO_PORT.
- 볼륨: mariadb_data, redis_data, sites, logs. 멀티테넌트 SaaS는 sites 볼륨이 핵심.
- 배치: 구현 시
prego-docker에compose/또는deploy/하위에 예시로 두거나, prego-ansible·Hetzner runbook에서 참조할 수 있도록 경로를 문서화.
9.2 Staging → Production 승격 (digest 고정)
- 원칙: Staging에서 검증한 동일 이미지(digest) 를 Production에 승격. 태그 재빌드가 아닌 digest 고정으로 배포.
- prego-docker: build 단계에서
docker/build-push-action의outputs.digest를 artifact 또는 로그로 보관. (선택) digest를 파일로 저장해 upload-artifact. - 승격 워크플로우: workflow_dispatch로 “Staging 검증 digest” 입력받아, Production 환경(GitHub Environment·Required reviewers)에서만 배포 스크립트 실행. SSH 등으로 서버에 동일 digest 이미지 배포.
9.3 Zero-downtime Blue/Green
- 구성: app_blue, app_green 두 서비스 + nginx(또는 Traefik). 프록시가 active 쪽만 upstream으로 사용.
- 배포: 새 digest를 inactive 쪽에 먼저 배포 → healthcheck 통과 후 nginx upstream을 전환 → nginx reload. 스크립트 예:
deploy-bluegreen.sh가 현재 active 판단, inactive에만 새 이미지로 recreate, healthcheck 후 upstream 교체·reload. - 이미지 참조:
image: iamfork/prego-repo@${IMAGE_DIGEST}형태로 digest 고정 사용 권장.
구현 완료 반영: prego-docker 레포에 다음이 추가됨.
- 9.1:
compose/docker-compose.prod.example.yml,compose/README.md. - 9.2: 빌드 워크플로에서 digest 출력;
.github/workflows/promote.yml.example(workflow_dispatch + digest 입력). - 9.3:
compose/bluegreen/(docker-compose.bg.example.yml, nginx upstream 예시, deploy-bluegreen.sh, README).
서버 Runbook: Prego 레포 docs/runbook/prego-docker-image-deploy.md.
10. 구현 시 체크리스트
prego-docker
-
resources/core/nginx/nginx-template.conf,nginx-entrypoint.shvendoring (frappe_docker 동일 경로에서 복사). -
Dockerfile작성 (frappe_docker images/custom/Containerfile 기반, version-15·3.11 고정, Base/Builder/backend, APPS_JSON_BASE64·FRAPPE_BRANCH). -
apps.json커밋 (ERPNext, HRMS, prego-saas-app main). -
.dockerignore작성. -
.github/workflows/build.yml작성: 트리거(push·repository_dispatch), QEMU·buildx·login, (dispatch 시) pinned apps.json 생성, Set Tag, Build & Push(cache, multi-arch, build-args). - README.md: 빌드 원리, Secrets, 멀티아키텍처, custom app 자동 재빌드 흐름.
- GitHub Secrets: DOCKERHUB_USERNAME, DOCKERHUB_TOKEN, APPS_JSON_BASE64.
prego-saas-app
-
.github/workflows/notify-image-rebuild.yml작성: push·tag 시 repository_dispatch to prego-docker, client_payload (tag, app_sha). - GitHub Secret: PREGODOCKER_DISPATCH_PAT.
검증
- prego-docker main push → 이미지 빌드·푸시(latest) 확인.
- prego-saas-app push → prego-docker 빌드 트리거·pregoapp-* 태그 푸시 확인.
- Docker Hub에서 해당 태그에 linux/amd64, linux/arm64 매니페스트 확인.
11. 연관 문서
| 문서 | 연계 |
|---|---|
| prego-docker-github-actions-docker-hub-plan.md | 기본 워크플로우·Secrets·멀티아키텍처·Hetzner CPX. |
| frappe-docker-hrms-custom-image-build-plan.md | apps.json·custom Containerfile·buildx. |
| docker-hub-stack-ansible-deploy-plan.md | Ansible에서 사용할 이미지 소스·패턴 A(서버 분리). |
| naming-conventions.md | 이미지·태그 네이밍 일관성. |
다음 단계: 이 기획서에 따라 prego-docker·prego-saas-app에 위 파일 트리·워크플로우·Secrets를 구현한다. 코드는 본 기획서의 요구사항을 만족하는 범위에서 별도 생성한다.