<!-- canonical: https://docs.axelabs.ai/architecture/deploy -->
<!-- source: content/architecture/deploy.mdx -->

---
title: 배포 · 무중단
description: blue/green frame swap, axe-frame-proxy, axe CLI deploy.
---

# 배포 · 무중단

## Blue/green 패턴 (frame)

frame 은 **두 개의 컨테이너가 항상 동시 실행**됩니다 — blue (3710) + green (3711). 한쪽이 active, 다른 한쪽이 passive.

```
              ┌──── frame-mcp-blue   :3710  ◀ alias `frame-mcp` (active)
              │
axe-frame-proxy ─── (Caddy upstream)
   :3712      │
              └──── frame-mcp-green  :3711       (passive, 새 코드 받는 곳)
```

배포 시퀀스:

```
1. green container 에 새 코드 build + start
2. green 헬스체크 통과 대기 (poll /health/ready)
3. docker network alias `frame-mcp` 를 green 으로 이동
4. axe-frame-proxy 가 upstream 자동 reload (Caddy graceful)
5. blue 의 in-flight 요청 완료 대기 (60s grace)
6. blue stop → 다음 배포 시점에 새 코드로 build
```

다운타임: **0 초**. 한 시점에 < 1s 의 latency spike 는 수용.

## axe CLI deploy

운영자 머신에서:

```bash
# dry-run (변경사항 미리 표시)
axe deploy frame axe

# 적용
axe deploy frame axe --apply
```

`axe deploy frame {customer}` 가 위 6 step 을 자동 실행. 진행 상황은 stderr 에 step 별 로그.

## 함정

| 함정 | 결과 | 회피 |
|---|---|---|
| `docker compose up --build` (다운타임) | 30-60s 다운타임 | `axe deploy` 사용 |
| `docker compose restart` (코드 미변경 시) | 다운타임 + env 재로딩 X | 코드 변경 → `axe deploy` |
| cloudflared 의 SIGHUP | process 죽음 | host-side proxy 뒤로 분리 |
| blue/green 컨테이너 이름을 cloudflared 에 직접 명시 | swap 불가 | docker network alias 사용 |
| `--no-build` 잊고 deploy | 새 코드 적용 안 됨 | `axe deploy` 가 자동 처리 |

## docker-compose 구조

`/Users/axe/frame/docker-compose.yml`:

```yaml
services:
  postgres:
    image: postgres:16-alpine
    container_name: frame-postgres
    ports: ["3700:5432"]

  frame-mcp-blue: &frame-mcp-blue
    build: . (Dockerfile)
    container_name: frame-mcp-blue
    ports: ["3710:3710"]
    environment:
      FRAME_DEPLOY_COLOR: blue
      FRAME_MCP_PORT: 3710
      AZURE_FRAME_MCP_CLIENT_SECRET: ${AZURE_FRAME_MCP_CLIENT_SECRET:-}
      # ... 기타 env
    networks:
      default:
        aliases: [frame-mcp]    # initial: blue active
      artemis_default:
        aliases: [frame-mcp]

  frame-mcp-green:
    <<: *frame-mcp-blue          # YAML anchor 로 동일 설정 상속
    container_name: frame-mcp-green
    ports: ["3711:3710"]
    environment:
      FRAME_DEPLOY_COLOR: green
      # ... 동일하지만 color=green
    networks:
      default: {}                # green 은 alias 없음 (passive)
      artemis_default: {}
```

## 무엇이 다운타임 0 을 가능케 하는가

1. **컨테이너 1대 만 build 중** — 다른 1대가 traffic 흡수
2. **docker network alias** 가 instant DNS 갱신 (DNS TTL 없음, in-memory)
3. **axe-frame-proxy 의 Caddy** 가 upstream 변경에 graceful (SIGHUP)
4. **cloudflared 가 build 와 무관** — 평소 라우팅 유지

## 다른 서비스 — blueprint

blueprint 은 단일 컨테이너 (현재). Next.js dev/build 시간이 짧고 (수십 초), 사용자 traffic 이 frame 만큼 critical 하지 않아 단순 deploy 사용:

```bash
cd /Users/axe/blueprint && pnpm build && pnpm start
# (launchd 또는 docker compose restart)
```

다운타임 발생 시 사용자가 보는 것은 "Connecting..." 메시지 수 초. 회계 작업과 같은 in-flight transaction 없음.

향후 blueprint 도 blue/green 으로 전환 예정 (D-config-13 패턴 확장).
