ironclad production cutover (2026-04-21): apex → Worker 전환
products/ironclad-website.md: 배포 표에 라우팅 방식 컬럼 추가. production만 zone route 방식(custom_domain 대신 zone_name + /*)을 써서 기존 APISIX A record를 건드리지 않고 Cloudflare 엣지에서 Worker가 매칭 요청을 가로채는 구조. 전제 조건(crowdsec bouncer wildcard가 apex를 가로채지 않도록 *.ironclad.it.com/* 수정) 명시. history/2026-04-21: 3차에 걸친 cutover 경로(DNS 충돌 → route 전환 → crowdsec wildcard dot 누락 수정) + 현재 prod 상태 + 후속 정리 항목.
This commit is contained in:
76
history/2026-04-21-ironclad-production-cutover.md
Normal file
76
history/2026-04-21-ironclad-production-cutover.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
title: 2026-04-21 Ironclad production cutover — apex 도메인 Worker 전환
|
||||||
|
updated: 2026-04-21
|
||||||
|
tags: [history, ironclad, website, cloudflare, production, crowdsec]
|
||||||
|
---
|
||||||
|
|
||||||
|
## 요약
|
||||||
|
|
||||||
|
`ironclad.it.com` (apex) production이 이전 경로(Cloudflare → APISIX → Caddy 컨테이너, Stamp/Flux volume_shared) 에서 **Cloudflare Worker `ironclad-site` (Next.js 16 + opennextjs-cloudflare)** 로 전환됨. 고객이 보는 화면이 6주 묵은 영문 "Ironclad Corp — Managed Security Hosting" 에서 최신 한국어 "Ironclad — 올어라운드 호스팅 & 보안서비스" 로 즉시 교체.
|
||||||
|
|
||||||
|
## 진행 경로
|
||||||
|
|
||||||
|
### 1차 시도 — `v2026.04.21` 태그 push (실패)
|
||||||
|
- `deploy-production.yml` 트리거, wrangler 빌드·업로드 성공
|
||||||
|
- DNS 등록 단계에서 Cloudflare API 실패:
|
||||||
|
```
|
||||||
|
code: 100117
|
||||||
|
Hostname 'ironclad.it.com' already has externally managed DNS records (A, CNAME).
|
||||||
|
Either delete them, try a different hostname, or use the option
|
||||||
|
'override_existing_dns_record' to override.
|
||||||
|
```
|
||||||
|
- 원인: 기존 `ironclad.it.com A 172.233.93.180 (Proxied)` 레코드가 먼저 있어 `custom_domain: true` 등록 거부
|
||||||
|
|
||||||
|
### 2차 시도 — custom_domain 제거하고 zone route로 전환 (성공)
|
||||||
|
- `wrangler.jsonc` production env routes 변경:
|
||||||
|
- Before: `{ pattern: "ironclad.it.com", custom_domain: true }`
|
||||||
|
- After: `{ pattern: "ironclad.it.com/*", zone_name: "ironclad.it.com" }`
|
||||||
|
- 로컬에서 `CLOUDFLARE_API_TOKEN=... pnpm deploy:production` 실행 → Worker 업로드 + route 등록 성공
|
||||||
|
- 결과: Cloudflare에 `ironclad.it.com/* → ironclad-site` route binding 생성됨
|
||||||
|
|
||||||
|
### 3차 보정 — crowdsec bouncer wildcard route 버그 수정 (결정타)
|
||||||
|
- 배포 후에도 prod 응답이 여전히 구 HTML. 원인 조사 결과 Cloudflare Workers Routes 상태:
|
||||||
|
```
|
||||||
|
www.ironclad.it.com/* -> ironclad-site
|
||||||
|
ironclad.it.com/* -> ironclad-site
|
||||||
|
*ironclad.it.com/* -> crowdsec-cloudflare-worker-bouncer ← 문제
|
||||||
|
```
|
||||||
|
- `*ironclad.it.com/*` 패턴이 의도는 "subdomain wildcard" 였으나 **dot 누락**으로 apex까지 포괄(prefix wildcard로 해석되어 `ironclad.it.com/*` 도 매칭). crowdsec bouncer Worker가 먼저 잡아서 origin(APISIX → Caddy)로 pass-through.
|
||||||
|
- Cloudflare API로 zone route `c148ae77ebb74f20b61ecd71d0e86b77`를 `*.ironclad.it.com/*`로 변경:
|
||||||
|
```bash
|
||||||
|
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE/workers/routes/$ROUTE_ID" \
|
||||||
|
-d '{"pattern":"*.ironclad.it.com/*","script":"crowdsec-cloudflare-worker-bouncer"}'
|
||||||
|
```
|
||||||
|
- 수정 직후 `ironclad.it.com` 응답에 `x-opennext: 1` + `x-powered-by: Next.js` 확인 → Worker가 정상 서빙
|
||||||
|
|
||||||
|
## 현재 prod 상태 (2026-04-21 post-cutover)
|
||||||
|
|
||||||
|
- `https://ironclad.it.com/` → Cloudflare → `ironclad-site` Worker (Next.js 16)
|
||||||
|
- title: `Ironclad — 올어라운드 호스팅 & 보안서비스 | Ironclad`
|
||||||
|
- `x-opennext: 1`, `x-powered-by: Next.js`
|
||||||
|
- DNS A record `172.233.93.180`은 건드리지 않음 (롤백 준비)
|
||||||
|
- apex 도메인의 bot 차단은 Next.js Worker 내부의 CrowdSec middleware (KV binding `CROWDSECCFBOUNCERNS = 9af0d1c1c14a4bc1a3835c2a5b22fd7a`)가 담당. Edge Worker bouncer는 서브도메인(`*.ironclad.it.com`)만 커버.
|
||||||
|
|
||||||
|
## 후속 정리 필요
|
||||||
|
|
||||||
|
| 항목 | 담당 | 우선순위 |
|
||||||
|
|------|------|----------|
|
||||||
|
| APISIX 라우트 `ironclad.it.com` 제거 (volume_shared 모드) | 씬 | 낮음 (트래픽 안 감, 남아있어도 무해) |
|
||||||
|
| Stamp-Flux 파이프라인에서 ironclad.it.com 배포 경로 deprecate | 헤임달 | 중 (혼선 방지) |
|
||||||
|
| `openclaw/openclaw-agents.md` volume_shared 모드 ironclad 관련 기록 정리 | 헤임달 | 중 |
|
||||||
|
| Cloudflare DNS A record 변경 (172.233.93.180 → Cloudflare 기본) | kappa | 낮음 (zone route 방식이라 origin 무관) |
|
||||||
|
| `v2026.04.21` 태그는 `deploy-production.yml`에서 실패로 남음 → 재태깅 불필요 (로컬에서 직접 배포 완료) | — | — |
|
||||||
|
|
||||||
|
## 커밋
|
||||||
|
|
||||||
|
- `hosting/ironclad`:
|
||||||
|
- `v2026.04.21` (태그, f736487) — Header dark nav 승격 시점
|
||||||
|
- `6f2e2cc` fix(deploy): custom_domain → zone route로 전환 (DNS 충돌 회피)
|
||||||
|
- Cloudflare API 변경: zone route `c148ae77ebb74f20b61ecd71d0e86b77` pattern 업데이트 (wrangler 소스 외)
|
||||||
|
|
||||||
|
## 학습 포인트
|
||||||
|
|
||||||
|
- **apex 도메인에 `custom_domain: true`를 쓰려면 기존 DNS 레코드가 없어야 한다**. Stamp/Flux 같이 기존 정적 배포 경로가 있는 도메인은 `zone_name` 기반 route 방식이 자연스럽다.
|
||||||
|
- **Cloudflare Worker Routes wildcard의 dot 누락은 apex까지 포괄**. `*example.com/*` vs `*.example.com/*`는 전혀 다른 패턴. `*example.com/*`은 prefix wildcard로 apex 포함.
|
||||||
|
- 기존 wildcard route가 apex로 해석되면 "내가 새로 만든 route가 우선" 될 거라 기대하기 어렵다. Cloudflare는 specificity tie-breaker가 있지만 명확히 의도한 대로 동작하지 않음. 가장 안전한 건 **wildcard pattern이 apex를 포함하지 않게 수정**.
|
||||||
|
- `x-opennext: 1` 헤더는 OpenNext가 서빙하고 있음을 확인하는 가장 빠른 시그널.
|
||||||
@@ -31,11 +31,15 @@ ironclad.it.com 공식 홈페이지. 호스팅 상품 소개, 가격표, 회사
|
|||||||
→ Worker ironclad-site-{staging|preview|} + R2 ironclad-cache-{staging|preview|prod}
|
→ Worker ironclad-site-{staging|preview|} + R2 ironclad-cache-{staging|preview|prod}
|
||||||
```
|
```
|
||||||
|
|
||||||
| 환경 | Worker | R2 버킷 | 도메인 | 배포 트리거 |
|
| 환경 | Worker | R2 버킷 | 도메인 | 배포 트리거 | 라우팅 방식 |
|
||||||
|------|--------|---------|--------|------------|
|
|------|--------|---------|--------|------------|------------|
|
||||||
| production | `ironclad-site` | `ironclad-cache-prod` | `ironclad.it.com` · `www.ironclad.it.com` | manual workflow_dispatch (`deploy-production.yml`) |
|
| production | `ironclad-site` | `ironclad-cache-prod` | `ironclad.it.com` · `www.ironclad.it.com` | `v*` 태그 push (`deploy-production.yml`) | **zone route** (`pattern: ironclad.it.com/*, zone_name: ironclad.it.com`) — 기존 A record(APISIX 172.233.93.180) 유지, Worker가 엣지에서 가로챔 |
|
||||||
| staging | `ironclad-site-staging` | `ironclad-cache-staging` | `staging.ironclad.it.com` | main push 자동 (`deploy-staging.yml`) |
|
| staging | `ironclad-site-staging` | `ironclad-cache-staging` | `staging.ironclad.it.com` | main push 자동 (`deploy-staging.yml`) | `custom_domain: true` |
|
||||||
| preview | `ironclad-site-preview` | `ironclad-cache-preview` | `preview.ironclad.it.com` | manual workflow_dispatch (`deploy-preview.yml`) |
|
| preview | `ironclad-site-preview` | `ironclad-cache-preview` | `preview.ironclad.it.com` | manual workflow_dispatch (`deploy-preview.yml`) | `custom_domain: true` |
|
||||||
|
|
||||||
|
**왜 production만 zone route?** apex 도메인(`ironclad.it.com`)은 이미 APISIX origin을 가리키는 A record가 등록되어 있어 wrangler의 `custom_domain` 등록이 실패(`code: 100117`). `custom_domain` 대신 zone route를 쓰면 기존 DNS 레코드를 건드리지 않고 Cloudflare 엣지에서 Worker가 매칭된 요청만 처리. 롤백 시 DNS 수동 복구 불필요.
|
||||||
|
|
||||||
|
**전제 조건**: Cloudflare zone에 등록된 Worker route 중 apex를 가로채는 wildcard가 없어야 한다. [[crowdsec-safeline|crowdsec-cloudflare-worker-bouncer]]의 zone route가 `*ironclad.it.com/*`로 오설정되어 있었고 이걸 `*.ironclad.it.com/*`로 수정해야 apex가 `ironclad-site` Worker로 직행 (2026-04-21 조치).
|
||||||
|
|
||||||
- 공통 KV: `CROWDSECCFBOUNCERNS` (id `9af0d1c1c14a4bc1a3835c2a5b22fd7a`) — CrowdSec bouncer 미들웨어 상태 공유
|
- 공통 KV: `CROWDSECCFBOUNCERNS` (id `9af0d1c1c14a4bc1a3835c2a5b22fd7a`) — CrowdSec bouncer 미들웨어 상태 공유
|
||||||
- 배포 후 smoke test: `curl -skL -w '%{http_code}' https://<domain>/` 5xx면 실패
|
- 배포 후 smoke test: `curl -skL -w '%{http_code}' https://<domain>/` 5xx면 실패
|
||||||
|
|||||||
Reference in New Issue
Block a user