Files
yakcloud-starter/.claude/skills/yakcloud-deploy/SKILL.md

104 lines
10 KiB
Markdown

---
name: yakcloud-deploy
description: Develop, verify, and deploy this project via the yakcloud CLI (local kind dev → managed val/prod) — never docker-compose. Build/push image → reconcile data sources → deploy workloads → bind env → custom domain. Use when running/testing/verifying the app locally, setting up a local dev environment, deploying to YakCloud, editing yakcloud.yaml, binding data sources, adding a domain, or starting a new project. If tempted to use docker-compose / docker run to run this app, use `yakcloud project deploy` (local kind) instead.
---
# YakCloud 배포 스킬
`yakcloud` CLI 로 **로컬 kind 개발 → 관리형 검증/운영**을 한 줄기로 배포한다. 또는 `v*` 태그 push →
Gitea Actions 가 이미지 빌드/푸시 + 콘솔 API 로 데이터소스 리컨실·워크로드 배포·바인딩을 자동 처리.
## 개발·검증도 yakcloud — docker-compose 금지 (원칙)
이 프로젝트의 앱은 **로컬 개발·검증부터 운영까지 전 구간을 yakcloud 로** 진행한다.
- **로컬 실행·검증 = `yakcloud project deploy`** (로컬 kind 자동 생성, 오프라인·무료, 운영과 동일한 바인딩 env·데이터소스).
- **`docker compose` / `docker-compose` / `docker run` 으로 앱을 띄우지 마라** — 프로젝트 `.claude/settings.json` 이 이를 차단하고,
`Dockerfile` 은 yakcloud 의 이미지 빌드 전용이다. compose 스택은 운영과 환경이 달라져 파리티가 깨진다.
- 데이터소스는 손으로 컨테이너를 띄우지 말고 `yakcloud.yaml``requires` 로 선언한다.
(각 프로젝트 루트 `CLAUDE.md` 에 같은 규칙이 항상 로드되도록 심겨 있다 — `yakcloud project init` 이 생성.)
## 실행 규약 (패스스루) — 최우선
`/yakcloud <args>` 로 호출되면 **`yakcloud <args>` 를 Bash 로 그대로 실행하고 출력만 보여준다.**
사전점검(설치/폴더/설정 확인)·결과 재검증·부연설명을 붙이지 말 것 — 터미널에서 직접 친 것과 동일하게 동작.
CLI 가 스스로 전제조건을 검사하고 오류를 안내하므로, 실패하면 그 출력을 그대로 전달하고 필요한 만큼만 후속 조치.
**안전장치 — 아래 "되돌리기 어렵거나 외부로 나가는" 서브커맨드만 실행 전 1줄로 확인**하고 승인 시 실행:
`project deploy` · `project confirm` · `project promote` · `domain` · `cluster create` · `dev down` · `source add` · `datasource migrate apply`
(그 외 `project init/info/check` · `dev up/deploy/status` · `datasource ls`/`capture` · `cluster ls` · `config`/`login`
읽기·로컬·멱등 명령은 확인 없이 즉시 실행.)
**⚠ 운영 승격 게이트 — `project confirm` 은 사람이 dev 검증을 마쳤다는 사인오프다. 에이전트가 임의로 실행하지 마라.**
`project promote --to prod <ver>` 는 그 버전의 `project confirm`(로컬 게이트, Phase1)이 선행돼야 통과한다(val 은 자유).
게다가 **클러스터에 '운영 승격 승인'이 켜져 있으면**(콘솔 클러스터 설정) 백엔드가 그 버전을 막고, **소유자가 콘솔의 배포 탭
'운영 승격 승인'에서 사람이 직접 승인**해야만 배포된다(Phase2 — 배포토큰/에이전트는 승인 불가). 에이전트는 confirm/prod-promote 를
임의로 하지 말고 "dev 에서 확인 후 confirm, 콘솔에서 승인"을 안내만 한다.
## CLI (권장)
설치: `curl -fsSL https://gitea.yakenator.io/yakenator/yakcloud-starter/raw/branch/main/install.sh | sh`
(전역 `/yakcloud` 슬래시 명령도 설치됨. `yakcloud upgrade` 로 최신화.) 자격 저장 = `yakcloud login`.
**라이프사이클**
- `yakcloud project init [name]` — 빈 폴더에 스캐폴딩(앱·CI·매니페스트·엔진).
- `yakcloud cluster create <name> [--template S|M] [--nodes N] [--golden]` · `yakcloud cluster ls`.
- **로컬 dev**(반복 루프) — `yakcloud dev up`(최초 1회, 로컬 kind+인그레스) → `yakcloud dev deploy`.
`requires` 소스+`workloads` 가 로컬에 **관리형과 동일한 바인딩 env**로 뜬다(오프라인). `dev status/clean/down`.
로그인 시 콘솔 '내 로컬 클러스터'에 자동 등록/해제. 상세 `docs/local-dev-cluster.md`.
- **배포/승격** — `yakcloud project deploy` (v* 태그 push → CI → 개발 클러스터) · `--local`(러너 없이 로컬
build+push+리컨실) · `yakcloud project promote [vX.Y.Z] --to val|prod` (같은 이미지, 재빌드 없이 승격).
- **앱 설정** — `domain <fqdn> [wl]` · `scale <wl> <n>` · `set <wl> --image/--port/--health/--cpu/--mem/--path/--rewrite`
· `env <wl> KEY=VAL [--secret K] [--unset K]` · `datasource ls [--env prod|val] [--cluster <id>] [--json]` · `datasource options <type> [--env prod|val] [--cluster <id>] [--json]`
· `source add <name> <type> [plan] [--shared]` · `bind|unbind <wl> <source> <alias>`.
### 운영 소스 모드 결정 (에이전트 흐름 — 사람은 거의 안 씀)
로컬(dev)은 도커로 논리 이름 그대로 뜨므로 결정 불필요. **운영(prod/val)은 소스마다 모드를 골라야** 한다:
1. **정보 조회**: `yakcloud datasource options <type> --env prod --json`
`{ modes:[…], sharedAvailable:bool, existing:[…], existingError? }``modes`=이 타입이 지원하는 모드, `sharedAvailable`=공유 서버가 이 배포에 있는지, `existing`=기존 소스, `existingError`=조회 실패 사유(있으면 빈 `existing` 이 '없음'이 아니라 '조회 실패'임).
2. **결정 → 매니페스트**:
- **공유 가능**(`sharedAvailable:true`)하고 원하면 → `requires``{ mode: shared }` (백엔드가 격리 DB 민팅).
- **기존 외부 소스에 붙임** → `datasource ls --env prod` 로 이름 확인 후 `environments.prod.requires``{ name:<논리>, clusterName:<그 이름> }`.
- **외부 새로 등록**(remote) → 접속정보는 **매니페스트/git 금지**(자격 격리) → 콘솔/API로 등록 후 이름 참조.
- **그 외** → `{ mode: local, plan: small }`(인클러스터 전용 생성).
- **타입 제약**: `oracle`=remote 전용, `redis`·`minio`=shared 없음, 나머지 6종(postgresql·mysql·mariadb·mongodb·rabbitmq·solr)=local/remote/shared 3모드. `datasource options``modes` 가 그 타입에 실제 가능한 모드를 알려준다.
## 매니페스트 (`yakcloud.yaml`)
- `project` — 이름(이미지 경로).
- `environments.{dev,val,prod}.cluster` — 대상 클러스터 이름/id (val 선택, +`prod.domains`). 로컬 dev 는 `yakcloud dev`.
- **환경 오버레이 병합** — 최상위 `requires`/`workloads`=base(공유 정체), `environments.<env>` 는 그 환경의 '차이만'. `requires` 는 논리 `name` 매칭 병합(같은 name=덮어쓰기, 새 name=추가). 오버레이는 **base 하고만** 병합되고 dev↔val↔prod 끼리 상속 안 함. `type` 은 base 또는 그 환경에 반드시 있어야 하며, 없으면 배포 전 검증 오류로 막는다.
- `requires[]``{ name, type, plan | mode: shared, clusterName? }`. type ∈ postgresql·mysql·mariadb·mongodb·redis·minio·rabbitmq·solr·oracle.
이름이 클러스터의 READY 소스와 일치하면 스킵, 없으면 프로비저닝(shared=공유 외부, 백엔드가 격리 DB 민팅; 원격 기본은 인클러스터 아님).
`clusterName` = 논리 `name` 과 다른 '실제 클러스터 소스 이름'(기본=name) — 주로 `environments.<env>` 오버레이에서
로컬↔운영 소스명이 다를 때 매핑. binds 는 항상 논리 `name` 참조(불변).
- `workloads[]``build`(도커 컨텍스트)·`image`(`gitea.yakenator.io/<GITEA_USER>/<project>-<name>:${TAG}``port`·
`health`·`replicas`(≥2 무중단)·`resources{cpu,mem}`·`expose{path,rewrite,host|hosts}`·`binds[]{alias,source}`.
## 데이터소스 바인딩 env (앱은 하드코딩 말고 이 env 로 접속)
`binds: {alias, source}``alias`(대문자, 하이픈→언더스코어)가 env 프리픽스. 공통 `<A>_HOST`·`<A>_PORT`.
| type | 추가 키 | `<A>_URL` |
|---|---|---|
| postgresql | `_USERNAME _PASSWORD _DB` | `postgresql://u:pw@host:port/db` |
| mysql·mariadb | `_USERNAME _PASSWORD _DB` | `mysql://u:pw@host:port/db` |
| mongodb | `_USERNAME _PASSWORD _DB` | `mongodb://u:pw@host:port/db?authSource=db` |
| redis | `_USERNAME`(빈) `_PASSWORD _DB` | `redis://:pw@host:port/db` |
| minio | `_ACCESS_KEY _SECRET_KEY _ENDPOINT _BUCKET _REGION` | =`_ENDPOINT` |
비밀번호/키는 인클러스터 Secret 으로 주입되고 브라우저/포털에 노출되지 않는다.
## 1회 준비 (CI 배포 시 — 사용자가 설정, 에이전트 대행 불가)
1. **배포 토큰**(콘솔 → 설정 → 배포 토큰, `yakd_…`) · **Gitea 토큰**(`read:repository`+`write:package`+`read:package`).
2. 레포 시크릿(Gitea → Settings → Actions → Secrets): `YAKCLOUD_URL`=`https://console.yakenator.io` ·
`YAKCLOUD_TOKEN`=배포토큰 · `YAKCLOUD_CLUSTER`=대상 이름/id · `REGISTRY_TOKEN`=Gitea 토큰.
3. 러너: 전용 러너(라벨 `ci-polyglot`)가 공유로 떠 있으면 레포 Actions 만 켠다.
(러너 없이 확실히 하려면 `yakcloud project deploy --local` — 로컬 build/push/리컨실.)
## 배포 / 도메인
```sh
yakcloud project deploy # 또는: git tag v0.1.0 && git push origin v0.1.0
yakcloud domain app.example.com web # 관리형 도메인=즉시 Active, 외부=TXT 검증
```
사전 점검: `yakcloud project check`. 성공 시 콘솔 앱 탭 / 데이터 소스 / 도메인에 반영.
## 규약 / 트러블슈팅
- 트리거 = `v*` 태그 push 만(일반 커밋 배포 안 함). 기존 배포는 이미지 PATCH 롤링(replicas≥2+health=무중단).
- 소스 프로비저닝 `405` → 콘솔에서 소스 먼저 만들거나 `mode: shared`(소용량 S). `yakcloud project check` 로 사전 확인.
- 태그 push 후 무반응 → 레포 러너 0개. `yakcloud project deploy --local`.
- 헬스 `/healthz` 404 → 엣지가 가로챔. 배포 검증은 앱 실제 경로(`/`)로(컨테이너 readiness 엔 `health:` OK).
- `ModuleNotFoundError: yaml`(직접 배포) → `pip install pyyaml`(CI 이미지엔 포함).