--- name: yakcloud-deploy description: Scaffold and wire up YakCloud declarative CI/CD for a project — Gitea Actions (tag push) → build/push images → reconcile data sources → deploy workloads → bind → tenant cluster. Use when the user wants to deploy this project to YakCloud, set up the Gitea Actions workflow, create the yakcloud.yaml manifest, bind data sources, add a custom domain, or start a brand-new project from a minimal starter. --- # YakCloud 배포 스킬 (선언적 CI/CD) VS Code로 개발 → 커밋 → **`v*` 태그 push** 하면, Gitea Actions가 이미지를 빌드/푸시하고 YakCloud 콘솔 API로 **데이터소스 리컨실 + 워크로드 배포 + 바인딩**을 자동 처리한다. 이 스킬은 **아무 프로젝트에나 이 파이프라인을 심는다.** 번들된 자산(`assets/`)을 프로젝트에 복사하고 매니페스트를 채운 뒤, 1회 시크릿만 설정하면 끝. 빈 프로젝트면 프레임워크 중립 **스타터**로 바로 시작. ## 언제 쓰나 - "이 프로젝트를 YakCloud에 배포/자동화" / "Gitea CI/CD 붙여줘" / "yakcloud.yaml 만들어줘" - "새 프로젝트 시작해서 배포까지" (→ `assets/starter/` 로 스캐폴딩) - "데이터소스(Postgres/Mongo/…) 연결/바인딩" / "커스텀 도메인으로 노출" ## CLI 가 있으면 우선 사용 `yakcloud` CLI 가 설치돼 있으면(`command -v yakcloud`) 스캐폴딩/배포/환경설정을 이걸로 처리한다. 설치: `curl -fsSL https://gitea.yakenator.io/yakenator/yakcloud-starter/raw/branch/main/install.sh | sh` **프로젝트 라이프사이클** - **"프로젝트 초기화"** → 빈 폴더에서 `yakcloud project init [name]` (스타터+CI+매니페스트+이 스킬) - **"yakcloud 에 배포"** → `yakcloud project deploy` (버전 미지정 시 **자동 버전업**: 최신 태그 patch+1 → 태그 push) - **"프로젝트 개괄"** → `yakcloud project info` (매니페스트 + 라이브 상태) - 사전 점검 → `yakcloud project check` (dry-run) **배포환경 설정(앱별) — 매니페스트 수정 + 배포중이면 재빌드 없이 라이브 반영** - `yakcloud domain [wl]` 도메인 등록 + 워크로드 할당 - `yakcloud scale ` replicas - `yakcloud set --image/--port/--health/--cpu/--mem/--path/--rewrite` - `yakcloud env KEY=VAL … [--secret KEY] [--unset KEY]` - `yakcloud source add [plan]` · `yakcloud source rm ` - `yakcloud bind ` · `yakcloud unbind ` CLI 가 없거나 세밀 조정이 필요하면 아래 수동 절차를 따른다. ## 에이전트 실행 절차 ### 0) 상황 파악 - git 레포인지, 앱 코드가 이미 있는지 확인. **빈/신규**면 §1-A(스타터), **기존 앱**이면 §1-B. - Gitea 원격이 있는지(`git remote -v`) 확인. 없으면 사용자에게 Gitea 레포 URL을 물어 추가하도록 안내. ### 1-A) 신규 — 스타터로 스캐폴딩 1. `assets/starter/`(app.py·Dockerfile·README) 를 프로젝트 루트에 복사. (프레임워크 무의존 최소 HTTP 서버: `PORT` 리슨, `/healthz` 200, `/` 는 바인딩된 소스의 `_URL` 키만 표시) 2. 아래 §2 공통 파일 복사 + §3 매니페스트 작성. ### 1-B) 기존 앱에 부착 1. 앱이 **`PORT` env로 리슨 + `/healthz`(또는 health 경로) 제공**하는지 확인. 없으면 추가 제안. 2. 앱이 데이터소스 접속정보를 **하드코딩하지 말고 주입 env**(`_URL` 등)로 읽도록 수정 제안. 키 규약: `reference/DATA-SOURCES.md`. 3. 각 배포 단위마다 `Dockerfile` 필요(없으면 언어에 맞게 생성). 베이스 이미지는 Gitea 미러(`gitea.yakenator.io/yakenator/{node,python,...}`) 권장. ### 2) 공통 파일 복사 (모든 경우) 프로젝트 루트에 그대로 복사: - `assets/yakcloud.yaml` → `./yakcloud.yaml` (매니페스트 — §3에서 채움) - `assets/workflows/deploy.yml` → `./.gitea/workflows/deploy.yml` (매니페스트 구동형 CI — **수정 불필요**) - `assets/scripts/yakcloud_deploy.py` → `./scripts/yakcloud_deploy.py` (리컨실 배포 CLI — 그대로) - `assets/reference/DATA-SOURCES.md` → `./DATA-SOURCES.md` (선택 — 소스별 주입 env 표) ### 3) 매니페스트(`yakcloud.yaml`) 채우기 (핵심) 사용자와 함께 값을 정한다: - `project`: 프로젝트 이름(이미지 경로에 사용). - `cluster`: 배포 대상 클러스터 **이름 또는 콘솔 id** (콘솔 대시보드에서 확인). 여러 개면 여기서 특정. - `requires`: 필요한 데이터 소스 목록 `{ name, type, plan }`. 없으면 `[]`. - `type` ∈ `postgresql·mysql·mariadb·mongodb·redis·minio·rabbitmq·solr·oracle`, `plan` ∈ `small·medium·large`. - 이 이름의 소스가 클러스터에 이미 READY면 스킵, 없으면 자동 프로비저닝. - `workloads[]`: - `build`: 도커 빌드 컨텍스트 경로(예 `./` 또는 `./api`). **CI가 이 컨텍스트를 빌드해 `image`로 push.** - `image`: `gitea.yakenator.io//-:${TAG}` (`${TAG}`=릴리스 태그로 치환). - `port`·`health`·`replicas`(≥2 권장 → 무중단 롤링)·`resources{cpu,mem}`. - `expose`: `{ path, rewrite, host? }` — `host` 지정 시 등록 도메인으로 노출(미지정=클러스터 기본 도메인). 여러 도메인은 `hosts: [a, b]`. - `binds[]`: `{ alias, source }` — `source`는 위 `requires` 이름, `alias`는 env 프리픽스(대문자화). 예 `alias: db` → `DB_URL/DB_HOST/…` 주입. ### 4) 1회 준비 (사용자에게 안내 — 에이전트가 대신 못 함) 아래를 **사용자가** 설정하도록 명확히 출력: 1. **배포 토큰(PAT)**: 콘솔 → **설정 → 배포 토큰** → 발급(`yakd_…`, 한 번만 표시). 계정 스코프(어느 클러스터든). 2. **Gitea 토큰**: 스코프 `read:repository` + `write:package` + `read:package`. (레포 read 없으면 CI `git clone` 403) 3. **레포 시크릿**(Gitea: Settings → Actions → Secrets): | 이름 | 값 | |---|---| | `YAKCLOUD_URL` | `https://console.yakenator.io` | | `YAKCLOUD_TOKEN` | 1의 배포 토큰(`yakd_…`) | | `YAKCLOUD_CLUSTER` | 대상 클러스터 이름/ id (생략 시 매니페스트 `cluster:`) | | `REGISTRY_TOKEN` | 2의 Gitea 토큰 | 4. **러너**: 전용 러너(라벨 `ci-polyglot`)가 이미 공유로 떠 있으면 레포 **Actions만 켜면** 됨. 없으면 GUIDE의 러너 등록 참고. ### 5) 배포 ```sh git add -A && git commit -m "yakcloud: CI/CD 스캐폴딩" git push origin main git tag v0.1.0 && git push origin v0.1.0 # v* 태그만 배포 트리거(일반 커밋은 배포 안 함) ``` → Gitea **Actions** 탭에서 실행 확인. 성공 시 콘솔 **앱 탭 / 데이터 소스 / 도메인**에 반영. 로컬 사전 점검: `YAKCLOUD_URL=… YAKCLOUD_TOKEN=… python3 scripts/yakcloud_deploy.py yakcloud.yaml --dry-run` ### 6) 커스텀 도메인(선택) 콘솔 **설정 → 도메인 → 도메인 추가**로 등록(관리형 도메인은 즉시 Active). 매니페스트 `expose.host`에 지정하거나 콘솔 앱 편집 폼에서 선택. 여러 도메인은 `expose.hosts: [a.example.com, b.example.com]`. ## 동작/규약 요점 - **트리거**: `v*` 태그 push만. 일반 커밋은 배포 안 함. - **멱등/무중단**: 기존 배포는 이미지 PATCH(→ kubectl apply 롤링). replicas≥2 + health면 무중단. - **자격**: 데이터소스 비밀번호/키는 인클러스터 Secret으로 주입되고 브라우저/포털에 노출되지 않음. 앱은 `_URL` 등으로 접속. ## 트러블슈팅(실제 겪은 것) | 증상 | 원인 → 해결 | |---|---| | CI `git clone … 403` | REGISTRY_TOKEN에 레포 read 없음 → `read:repository` 추가 | | `docker create` 실패(잡 시작 안 됨) | 러너 config에서 docker.sock **중복 마운트** → act 기본 마운트만(수동 제거) | | 배포 중 `429 RATE_LIMITED` | CLI가 `retryAfterSec` 백오프로 재시도(내장) | | 다른 러너가 잡 가져감 | 라벨 충돌 → 전용 라벨(`runs-on`) 사용 |