Files
baron_qa_write/docs/qa-production-pipeline.md
T
root 0755155368
Deploy EG-BIM QA Gateway / deploy (push) Failing after 7s
feat: add manual production deployment pipeline
2026-10-28 14:14:02 +09:00

118 lines
10 KiB
Markdown

# Q&A production 배포 파이프라인
## 배포 대상과 실행 방식
| 항목 | 운영 값 |
|---|---|
| Workflow | `.gitea/workflows/deploy-production.yml` |
| Actions 표시 이름 | `Deploy EG-BIM QA Production` |
| 실행 | Gitea Actions에서 `main`을 선택해 수동 실행 |
| Worker | `baron-qa-gateway-prod` |
| Custom Domain | `https://qa.baroncs.co.kr` |
| Cloudflare account ID | `81fa2d48964d31dd0da9558f9ce601d1` |
| 정적 파일 R2 bucket | `baron-qa-write` |
| R2 binding 이름 | `QA_BUCKET` |
| 정적 파일 저장 경로 | `releases/<전체 커밋 SHA>/egbim/...`, `releases/<전체 커밋 SHA>/assets/...` |
운영 Worker와 도메인 연결, 버킷 생성은 사용자가 Cloudflare 대시보드에서 완료했다. 실제 Q&A 코드 배포는 이 workflow로 수행한다. `main` push는 기존 스테이징 workflow를 실행하며, 운영 workflow는 수동 실행만 지원한다. 운영 실행도 `main`의 선택된 커밋과 checkout 결과가 같아야 진행한다.
배포 파일은 `wrangler.production.toml`을 사용한다. 운영 세션 쿠키·source namespace는 별도로 지정했고 `workers.dev`와 preview URL은 비활성화한다. 운영 접속 주소는 `qa.baroncs.co.kr`이다. 이미 연결한 custom domain은 Cloudflare 대시보드에서 관리한다. Wrangler 설정에는 `route`와 `routes`를 지정하지 않아 배포 시 기존 도메인 연결을 유지하며 zone 라우팅을 변경하지 않는다.
## Gitea에 등록할 Secrets
저장소 **Settings → Actions → Secrets**에 등록한다. 기존 기본 Secret이나 `STAGING_*` 값을 자동으로 가져오지 않는다. 기존 키가 운영 리소스에도 권한이 있다면 값은 재사용할 수 있지만, 아래 운영 설정 이름으로 등록해야 한다. `SESSION_SECRET`은 운영 전용 값을 사용한다.
| 이름 | 값 |
|---|---|
| `PROD_CLOUDFLARE_API_TOKEN` | 기존 운영 Worker를 배포하고 R2 파일을 업로드할 수 있는 Cloudflare token |
| `PROD_SESSION_SECRET` | 최소 32자 운영 세션 서명키. 예: `openssl rand -hex 32`로 생성한 값 |
| `PROD_ABC_API_KEY` | **등록 보류:** 관리페이지 서버 확정 후 운영 ABC project/channel API key 등록 |
| `PROD_ORG_CONTEXT_KEY_ID` | 운영 조직 컨텍스트 연동 Key ID |
| `PROD_ORG_CONTEXT_KEY_SECRET` | 운영 조직 컨텍스트 연동 Key Secret |
Cloudflare 프로필 **API Tokens → Create Token → Create Custom Token**에서 운영 배포 token을 생성하거나 기존 token의 권한을 확인한다.
| 범위 | 권한 | 수준 | 용도 |
|---|---|---|---|
| Account / Workers product | Workers | Editor | 기존 Worker 코드·Secret·binding 배포. 레거시 Workers Scripts Edit도 허용 |
| Account | Workers R2 Storage | Edit / Write | 정적 파일을 R2에 업로드 |
| Account | Account Settings | Read | Wrangler 계정 정보 조회 |
**Account Resources**는 `바른기술개발센터` 계정으로 지정한다. 생성한 token 값은 `PROD_CLOUDFLARE_API_TOKEN` Secret에 넣는다. 도메인 연결은 대시보드에서 이미 완료했으므로 운영 배포 token에는 Workers Routes Write와 Zone Read를 요구하지 않는다. 현재 파이프라인은 Wrangler의 Cloudflare REST API를 사용하므로 R2 S3 Access Key ID / Secret Access Key는 필요하지 않다.
R2 권한은 계정 범위의 **Workers R2 Storage Write/Edit**를 선택한다. `R2 → baron-qa-write` 범위의 **Workers R2 Storage Bucket Item Read/Write**는 S3 객체 접근용이므로 현재 Wrangler REST 업로드에 필요한 권한을 대신하지 않는다. 운영 배포에는 Zone Write 정책도 필요하지 않다.
근거: [Workers 권한](https://developers.cloudflare.com/workers/authorization/workers/), [R2 token 권한](https://developers.cloudflare.com/r2/api/tokens/), [대시보드에서 route를 관리하는 Wrangler 설정](https://developers.cloudflare.com/workers/wrangler/configuration/#source-of-truth).
## Gitea에 등록할 Variables
저장소 **Settings → Actions → Variables**에 등록한다.
| 이름 | 필수 | 값 |
|---|---|---|
| `PROD_AUTH_CLIENT_ID` | 필수 | 운영 callback URI가 허용된 SSO client ID |
| `PROD_ABC_API_BASE_URL` | 필수 / 등록 보류 | 관리페이지 서버 확정 후 운영 ABC HTTPS origin 등록. `/api` 경로를 붙이지 않는다. |
| `PROD_ABC_PROJECT_ID` | 필수 / 등록 보류 | 관리페이지 서버 확정 후 운영 ABC project UUID 등록 |
| `PROD_ABC_CHANNEL_ID` | 필수 / 등록 보류 | 관리페이지 서버 확정 후 운영 ABC channel UUID 등록 |
| `PROD_ORG_CONTEXT_ENABLED` | 필수 | `true` |
| `PROD_ORG_CONTEXT_TENANT_IDS` | 필수 | 기준 tenant UUID JSON 배열. 예: `["tenant-UUID"]` |
| `PROD_ORG_CONTEXT_API_URL` | 선택 | 기본 `https://sadmin.hmac.kr/api/v1/integrations/org-context` |
| `PROD_ORG_CONTEXT_SCOPE_MODE` | 선택 | 기본 `SCOPED` |
| `PROD_ORG_CONTEXT_EXPOSURE` | 선택 | 기본 `identifiers` |
| `PROD_ORG_CONTEXT_CACHE_TTL_SECONDS` | 선택 | 기본 `300`, 허용 범위 1~3600 |
SSO에는 `https://qa.baroncs.co.kr/auth/callback`을 허용하고, 로그아웃 후 이동 URI로 `https://qa.baroncs.co.kr/`을 확인한다. 등록된 SSO client나 ABC project/channel이 운영 대상인지 확정한 후 배포한다.
지금 준비할 Variables는 `PROD_AUTH_CLIENT_ID`와 `PROD_ORG_CONTEXT_*` 6개다. `PROD_AUTH_CLIENT_ID`의 참고값은 기존 스테이징 client `e16898a7-acd2-420a-a224-cdcd89ff66cd`이며, 이 client에 운영 callback이 허용된 경우에만 운영에서도 재사용한다. 별도 운영 client를 만들었다면 그 ID를 등록한다. 기준 tenant UUID와 조직 연동 키는 실제 발급받은 값을 사용한다. 기존 `STAGING_*` 등록은 `PROD_*` 등록으로 간주하지 않는다.
회사·부서 조회용 sadmin 조직 컨텍스트 API 설정은 ABC 관리페이지 연동 설정과 별개다. 조직 API 주소·기준 tenant·키의 운영 사용이 확정된 경우 미리 등록할 수 있다. Secret 값이나 테스트 fixture UUID를 실제 설정값으로 문서에 기록하지 않는다.
Worker runtime에는 `PROD_`를 제거한 이름으로 주입한다. `DEPLOY_COMMIT_SHA`는 workflow가 선택된 커밋에서 자동으로 전달하므로 Gitea에 따로 등록하지 않는다.
## Workflow 순서
1. 소스를 checkout하고 `main` 브랜치·커밋 일치를 확인한다.
2. 운영 필수 설정, UUID, 조직 API 설정과 세션 서명키 길이를 검사한다. 누락 시 배포 전에 중단한다.
3. `npm ci`로 고정된 의존성을 설치한다.
4. `npm run check`와 `npm test`를 실행한다.
5. `npm run org:verify`로 실제 운영 조직 API의 인증·범위·구성원 응답을 검증한다.
6. `npm run deploy:production -- --dry-run`으로 Worker 번들을 만든다. 이 단계는 Cloudflare 리소스를 변경하지 않는다.
7. 해당 커밋의 정적 파일을 `baron-qa-write/releases/<SHA>/...`에 올린다.
8. `npm run deploy:production`으로 코드·runtime 변수·Secret을 같은 Worker 배포에 반영한다.
9. `npm run production:verify`로 익명 세션, CSS 응답·커밋 일치, 비로그인 글쓰기 redirect를 확인한다. 전파 지연에 대비해 최대 5회 확인한다.
Secret 파일은 임시 디렉터리에 접근 권한 `0600`으로 만들고 배포 명령 종료 후 삭제한다. Secret 값은 CLI 인자나 로그로 출력하지 않는다.
## 첫 운영 배포 실행
현재 `PROD_ABC_API_KEY`, `PROD_ABC_API_BASE_URL`, `PROD_ABC_PROJECT_ID`, `PROD_ABC_CHANNEL_ID` 등록은 관리페이지 서버 확정까지 보류한다. 이 값들이 없으면 필수 설정 검증에서 배포를 중단한다. SSO·조직 컨텍스트·Cloudflare·세션 서명키 설정은 먼저 준비할 수 있다.
- [x] 관리페이지 연동 `PROD_ABC_*` 4개를 제외한 운영 Secrets와 Variables 등록을 완료했다. 사용자 보고 기준이며 실제 값·권한 검증은 아직 수행하지 않았다.
- [ ] 관리페이지 서버 확정 후 `PROD_ABC_API_KEY`, `PROD_ABC_API_BASE_URL`, `PROD_ABC_PROJECT_ID`, `PROD_ABC_CHANNEL_ID`를 등록한다.
- [x] 운영 SSO callback과 관련 변수 등록을 완료했다. 사용자 완료 보고 기준이다.
- [ ] ABC project/channel과 조직 API의 실제 접근 권한을 확인한다.
- [ ] 이 변경을 `main`에 반영한다.
- [ ] Gitea **Actions → Deploy EG-BIM QA Production → Run workflow**에서 `main`을 선택한다.
- [ ] 설정 검증·조직 API 검증·정적 파일 업로드·Worker 배포·HTTP 확인이 모두 성공하는지 확인한다.
- [ ] 실제 계정으로 로그인하고 문의·첨부·댓글 기능과 저장된 회사·부서를 확인한다. 자동 HTTP 확인은 로그인 이후의 실제 사용자 작업까지 검증하지 않는다.
## 실패와 롤백
- 설정·조직 API·빌드 검증 실패는 운영 정적 파일 업로드와 Worker 배포 전에 중단된다.
- 정적 파일 업로드가 실패해도 기존 Worker는 이전 커밋 경로를 계속 읽는다. 다시 실행하면 같은 커밋 경로로 업로드한다.
- Worker 배포 후 HTTP 확인이 실패하면 workflow는 실패로 표시되지만 이미 반영한 배포를 자동으로 되돌리지는 않는다. 마지막 정상 Worker 버전으로 명시적으로 롤백한다.
- Cloudflare의 `baron-qa-gateway-prod` **Deployments**에서 마지막 정상 버전으로 rollback하면 해당 버전의 `QA_ASSET_PREFIX`와 정적 파일을 다시 사용한다. 이전 release 경로는 보관한다.
- 첫 배포의 이전 버전은 기본 Hello World일 수 있다. 첫 배포 실패 시에는 설정을 수정해 다시 실행하고, 필요하면 기본 버전으로 되돌릴 수 있다.
- R2 release 파일 정리 시 활성 버전과 롤백에 필요한 버전의 prefix를 삭제하지 않는다. 정적 파일 롤백은 ABC에 저장된 문의·댓글 데이터를 되돌리지 않는다.
## 로컬 확인
```sh
npm run check
npm test
node scripts/deploy-production.mjs --validate-only
npm run deploy:production -- --dry-run
```
마지막 두 명령은 위 runtime 이름의 운영 설정과 `DEPLOY_COMMIT_SHA`가 실행 환경에 필요하다. `--validate-only`는 설정 형식만 확인하고, `--dry-run`은 로컬 번들만 확인한다. 실제 자격증명과 API의 유효성은 Gitea workflow의 조직 API 검증과 실제 배포에서 확인한다.