diff --git a/README.md b/README.md index 8da4624..ac3585c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # AI DEV 개발·배포 매뉴얼 (개발자용) -행번 계정 하나로 **Coder에서 개발**하고, **Gitea에 push**, **Kubero로 배포**합니다. +행번 계정 하나로 **Coder에서 개발**하고, **Gitea에 push**, **Kubero 또는 Coolify로 배포**합니다. +(배포 도구는 Kubero·Coolify 중 검토 중입니다. [9번](#9-배포-gitea--kubero--coolify)에 두 방법을 모두 정리해 두었습니다.) DB(PostgreSQL)·파일저장소(MinIO)·AI(LiteLLM - key 제외)는 워크스페이스에 미리 연결되어 있습니다. @@ -11,11 +12,11 @@ DB(PostgreSQL)·파일저장소(MinIO)·AI(LiteLLM - key 제외)는 워크스페 [1. 로그인](#1-로그인) → [2. 워크스페이스 만들기](#2-워크스페이스-만들기-최초-1회) → [3. VS Code 열기](#3-vs-code-열기) → [5. AI 키 등록](#5-ai-키-등록-최초-1회) **앱마다** -[6. 새 프로젝트 시작](#6-새-프로젝트-시작) → [7. 개발](#7-개발) → [8. 로컬 실행·확인](#8-로컬-실행확인) → [9. 배포](#9-배포-gitea--kubero) +[6. 새 프로젝트 시작](#6-새-프로젝트-시작) → [7. 개발](#7-개발) → [8. 로컬 실행·확인](#8-로컬-실행확인) → [9. 배포](#9-배포-gitea--kubero--coolify) ``` [최초 1회] 로그인(1) → 워크스페이스 생성(2) → VS Code(3) → AI 키 등록(5) -[앱마다] new-project + git init(6) → 개발·커밋(7) → 로컬 확인(8) → 레포 생성·push·Kubero 배포(9) +[앱마다] new-project + git init(6) → 개발·커밋(7) → 로컬 확인(8) → 레포 생성·push·배포(Kubero/Coolify)(9) ``` ## 0. 서비스 주소 @@ -25,7 +26,8 @@ DB(PostgreSQL)·파일저장소(MinIO)·AI(LiteLLM - key 제외)는 워크스페 | AI DEV 포털 (시작점) | https://portal.bokdev.in | | Coder (개발 워크스페이스) | https://coder.bokdev.in | | Gitea (코드 저장소) | https://gitea.bokdev.in | -| Kubero (배포) | https://kubero.bokdev.in | +| Kubero (배포 · 후보) | https://kubero.bokdev.in | +| Coolify (배포 · 후보) | https://coolify.bokdev.in | | 개발 중 미리보기 | `https://<자동생성>.coder.bokdev.in` | | 배포된 앱 | `https://<레포명>.playground.bokdev.in` | @@ -135,7 +137,7 @@ new-project myapp # 예제를 ~/projects/myapp 으로 복사 + .p ``` `myapp`은 예시입니다. 이 이름은 Gitea 레포명으로 설정할 이름과 동일하게 맞추시면 되고, 소문자·숫자·하이픈만 사용합니다. -**(2) git 초기화** — 개발 시작 시점에 합니다. 커밋 이력을 처음부터 관리하기 위함이며, 원격(Gitea) 연결은 배포 단계([9번](#9-배포-gitea--kubero))에서 합니다: +**(2) git 초기화** — 개발 시작 시점에 합니다. 커밋 이력을 처음부터 관리하기 위함이며, 원격(Gitea) 연결은 배포 단계([9번](#9-배포-gitea--kubero--coolify))에서 합니다: ```bash cd ~/projects/myapp git init -b main @@ -214,18 +216,28 @@ curl 127.0.0.1:3000/s3 # {"ok":true,"bucket":...} S3 연결 ``` `"ok": false` 이면 함께 출력되는 `error` 메시지가 원인입니다. -미리보기 URL은 본인 전용이며 워크스페이스를 끄면 사라집니다. 정식 배포는 [9번](#9-배포-gitea--kubero). +미리보기 URL은 본인 전용이며 워크스페이스를 끄면 사라집니다. 정식 배포는 [9번](#9-배포-gitea--kubero--coolify). -## 9. 배포 (Gitea + Kubero) +## 9. 배포 (Gitea → Kubero / Coolify) -배포 단위: Gitea `playground` 조직의 레포 1개 = Kubero 앱 1개. +배포 단위: Gitea `playground` 조직의 레포 1개 = 배포 앱 1개. 배포 주소: `https://<레포명>.playground.bokdev.in` +배포 도구는 **Kubero**와 **Coolify** 중 하나를 사용합니다. +**Gitea 레포 생성([9-1](#9-1-gitea-원격-레포-생성-앱당-1회))과 push([9-2](#9-2-push))는 두 도구 공통**이며, 이후 사용하는 도구에 따라 [9-3A(Kubero)](#9-3a-kubero에-앱-추가-앱당-1회) 또는 [9-3B(Coolify)](#9-3b-coolify에-앱-추가-앱당-1회)를 따릅니다. + +| 항목 | Kubero | Coolify | +|---|---|---| +| 배포 위치 | `playground` 파이프라인에 앱 추가 | `ai-dev` 팀 → `ai-dev` 프로젝트에 앱 추가 | +| 코드 수정 반영 | push 후 **수동 재빌드** (자동 빌드 미연동) | push 시 **자동 재빌드·배포** (webhook 설정 시, [9-3B](#9-3b-coolify에-앱-추가-앱당-1회)) | +| 환경변수 입력 | `.project-env` 업로드 → 자동 파싱 | `.project-env` 값을 붙여넣기 (Developer view) | +| 빌드 방식 | Dockerfile | Dockerfile | + ### 9-1. Gitea 원격 레포 생성 (앱당 1회) 1. https://gitea.bokdev.in/playground → 우측 상단 **`+` → New Repository** ![새 저장소 만들기](images/make-new-repository.png) -2. **Owner: `playground`** 로 변경, Repository Name 입력 (예: `myapp`) +2. **Owner: `playground`** 로 변경, Repository Name 입력 (예: `myapp`), public 설정 ![새 저장소 옵션 설정](images/make-new-repository-2.png) 3. README / .gitignore / License 는 체크하지 않음(빈 저장소여야 함) → **Create Repository** @@ -239,11 +251,9 @@ git push -u origin main - 최초 push 시 Gitea 승인 화면이 뜨면 **Authorize** 클릭([2번](#2-워크스페이스-만들기-최초-1회)에서 승인했다면 생략됨). - 이후 수정 반영: `git add . && git commit -m "..." && git push` -### 9-3. Kubero에 앱 추가 (앱당 1회) +### 9-3A. Kubero에 앱 추가 (앱당 1회) -> TODO: Kubero 배포 오류 수정 필요 - -`ai-dev` pipeline을 사용하시면 되며, 사용자는 그 안에 본인 앱만 추가합니다. +`playground` pipeline을 사용하시면 되며, 사용자는 그 안에 본인 앱만 추가합니다. 1. https://kubero.bokdev.in 접속 2. **`playground`** 파이프라인 선택 @@ -254,6 +264,47 @@ git push -u origin main > 자동 빌드는 현재 미연동입니다. **코드 수정 후에는 push 하고 Kubero에서 해당 앱의 빌드를 다시 실행합니다.** +### 9-3B. Coolify에 앱 추가 (앱당 1회) + +Coolify는 "**push → Dockerfile로 자동 빌드·배포**" 방식입니다. 사용자는 `ai-dev` 프로젝트에 본인 앱만 추가합니다. + +1. https://coolify.bokdev.in 접속 → 우측 상단에서 **`aidev`** 팀 선택 +2. 좌측 **`Projects` → `aidev`** (서버·DB가 연결된 프로젝트) → **`+ Add Resource`** + ![Coolify 팀 선택](images/coolify-team.png) +3. 리소스 종류에서 **`Public Repository`** 선택 + ![Coolify 리소스 종류 선택](images/coolify-new-resource.png) +4. **Repository URL**에 **전체 주소**를 입력 후 **`Check Repository`**: + `https://gitea.bokdev.in/playground/myapp.git` + (`playground/myapp` 처럼 줄여 쓰면 실패합니다.) + > **비공개(private) 레포일 때** — `Public Repository` 로도 받을 수 있습니다. URL에 Gitea 토큰을 끼워 넣습니다: + > `https://<토큰>@gitea.bokdev.in/playground/myapp.git` + > - 토큰 발급: Gitea → 우측 상단 프로필 → **Settings → Applications → Generate New Token**. 이름 지정 후 **`repository` 읽기 권한(Read)** 만 체크 → 생성. 표시되는 토큰은 **이때 한 번만** 보이므로 복사해 둡니다. + > - 발급한 토큰을 위 URL의 `<토큰>` 자리에 넣고 **`Check Repository`**. (토큰이 URL·Coolify 설정에 저장되므로 읽기 전용 권한만 부여합니다.) +5. **Build Pack: `Dockerfile`**, Branch `main`, Port `3000`. + ![Coolify 저장소 연결](images/coolify-repo.png) +6. **Configuration → Domains** 에서 **`Generate Domain`** 클릭 → `https://<레포명>.apps.bokdev.in` 형태로 지정. +7. **Environment Variables** 에 `.project-env` 값 등록: + - **Developer view** 에서 `.project-env` 내용을 그대로 붙여넣으면 일괄 등록됩니다. (`cat ~/projects/myapp/.project-env`) + - `DATABASE_URL` 은 `%20`·`%3D` 인코딩까지 **그대로** 넣습니다(빼면 DB 연결이 깨집니다). +8. **Deploy** 클릭 → **Deployments** 탭에서 빌드 로그 확인. `New container started` / `Deployment finished` 가 보이면 성공. + +#### 자동 배포(auto deploy) 설정 (앱당 1회) + +Public Repository 방식은 webhook을 걸어야 push가 자동 배포로 이어집니다(Coolify가 push를 스스로 감지하지 못함). Coolify에 별도의 "Auto Deploy" 켜기 단계는 없고, **Webhooks 탭의 URL·Secret을 Gitea에 등록하는 것이 곧 자동 배포 설정**입니다. 앱마다 한 번만 하면 됩니다. + +1. **Coolify 앱** → **Configuration → Webhooks** 탭에서, **Gitea** 항목의 **Webhook URL** 과 **Secret** 을 복사합니다. (Secret 칸이 비어 있으면 값을 입력/생성 후 저장) + ![Coolify Webhook URL·Secret](images/coolify-webhook.png) +2. **Gitea 레포** → `https://gitea.bokdev.in/playground/myapp` → **Settings → Webhooks → Add Webhook → Gitea** 에 등록: + - **Target URL**: 1번의 Webhook URL + - **Secret**: 1번의 Secret + - **Content Type**: `application/json` + - **Trigger**: Push events, **Active** 체크 → **Add Webhook** + ![Gitea Webhook 등록](images/gitea-webhook.png) +3. Gitea webhook 화면의 **Test Delivery** 를 누르거나 실제로 `git push` → Coolify **Deployments** 에 새 빌드가 자동으로 뜨면 완료. + +> push가 배포를 트리거할지는 앱 **Advanced** 탭의 **`Auto Deploy`** 옵션이 결정하며, **기본값이 켜짐**이라 따로 켤 필요는 없습니다(자동 배포를 끄고 싶을 때만 여기서 해제). +> 설정 후에는 코드를 고쳐 **`git push` 하면 자동으로 다시 빌드·배포**됩니다(Deployments에서 새 빌드 로그 확인). webhook을 걸지 않았다면 앱 화면에서 **Deploy** 를 눌러 수동 배포합니다. + ### 9-4. 확인 배포·재시작 직후 약 1~2분은 초기화(코드 다운로드·설치) 시간입니다. 일시적으로 404가 나오는 경우, 잠시 기다린 후 Ctrl + Shift + R로 강력 새로고침 후 확인해주세요. @@ -285,9 +336,9 @@ curl https://<레포명>.playground.bokdev.in/s3 - **`npm run dev` 가 `Cannot find package ...`** → `npm install` 미실행([6번](#6-새-프로젝트-시작)). - **push 인증을 물어봄** → Gitea 승인을 아직 안 한 경우. 승인 화면에서 Authorize([9-2](#9-2-push)). - **다른 계정으로 push/연동됨** → 브라우저에 남아 있던 Gitea 로그인 세션 때문입니다. Gitea 로그아웃 후 재승인하거나 시크릿 창 사용([2번](#2-워크스페이스-만들기-최초-1회)). -- **배포 주소가 404** → ① 배포 직후 1~2분 대기 ② `/healthz` 확인 ③ Kubero 로그 확인([9-4](#9-4-확인)). -- **`/healthz` 는 되는데 `/db`·`/s3` 가 500** → 먼저 로컬에서 `npm run db:check` / `minio:check` 통과 확인([8번](#8-로컬-실행확인)). 로컬에서 되면 Kubero 로그의 에러 메시지 확인. -- **배포가 옛날 코드** → push 됐는지 확인 후 Kubero에서 빌드 재실행([9-3](#9-3-kubero에-앱-추가-앱당-1회)). +- **배포 주소가 404** → ① 배포 직후 1~2분 대기 ② `/healthz` 확인 ③ Kubero/Coolify 배포 로그 확인([9-4](#9-4-확인)). +- **`/healthz` 는 되는데 `/db`·`/s3` 가 500** → 먼저 로컬에서 `npm run db:check` / `minio:check` 통과 확인([8번](#8-로컬-실행확인)). 로컬에서 되면 배포 로그의 에러 메시지 확인. Coolify는 `.project-env` 값(특히 `DATABASE_URL` 의 `%20`/`%3D`)이 그대로인지도 확인. +- **배포가 옛날 코드** → push 됐는지 먼저 확인. Kubero는 빌드 재실행([9-3A](#9-3a-kubero에-앱-추가-앱당-1회)), Coolify는 push 시 자동 재배포([9-3B](#9-3b-coolify에-앱-추가-앱당-1회)). - **DB가 비어 있음** → 정상입니다. 빈 전용 스키마가 제공되며 테이블은 직접 생성합니다. - **K8s에 직접 접근하고 싶어요** → 직원은 K8s에 직접 접근하지 않습니다. Coder·Gitea·Kubero로 개발·배포가 완결됩니다. diff --git a/images/coolify-new-resource.png b/images/coolify-new-resource.png new file mode 100644 index 0000000..133c1f2 Binary files /dev/null and b/images/coolify-new-resource.png differ diff --git a/images/coolify-repo.png b/images/coolify-repo.png new file mode 100644 index 0000000..c7844a8 Binary files /dev/null and b/images/coolify-repo.png differ diff --git a/images/coolify-team.png b/images/coolify-team.png new file mode 100644 index 0000000..cc0a8d9 Binary files /dev/null and b/images/coolify-team.png differ diff --git a/images/coolify-webhook.png b/images/coolify-webhook.png new file mode 100644 index 0000000..55034bf Binary files /dev/null and b/images/coolify-webhook.png differ diff --git a/images/gitea-webhook.png b/images/gitea-webhook.png new file mode 100644 index 0000000..a7fd9e7 Binary files /dev/null and b/images/gitea-webhook.png differ