콘텐츠로 이동

Docker로 배포

Chorus는 공식 Docker 이미지 chorusaidlc/chorus-app을 제공합니다. 이 가이드에서는 두 가지 Compose 구성(내장 데이터베이스를 쓰는 스탠드얼론, 외부 PostgreSQL과 Redis를 쓰는 프로덕션), 기존 데이터베이스에 연결하는 docker run, 전체 환경 변수 참고 정보, 그리고 컨테이너가 시작할 때 하는 일을 설명합니다.

먼저 이미지를 가져옵니다(pull).

Terminal window
docker pull chorusaidlc/chorus-app:latest

스탠드얼론 구성과 프로덕션 구성 중 어느 것을 고를지는 배포 개요를 참고하세요.

스탠드얼론(내장 데이터베이스)

섹션 제목: “스탠드얼론(내장 데이터베이스)”

외부 데이터베이스가 필요 없습니다. 이미지에 PGlite(내장 PostgreSQL)가 포함되어 있으며 모든 구성 요소를 자동으로 시작합니다. 데이터는 Docker 볼륨에 저장되어 컨테이너를 재시작해도 유지됩니다.

docker-compose.local.yml을 만듭니다.

# Standalone Chorus — embedded PGlite, no external PostgreSQL or Redis
services:
app:
image: chorusaidlc/chorus-app:latest
ports:
- "8637:8637"
environment:
# No DATABASE_URL — entrypoint auto-starts embedded PGlite
- REDIS_URL=
# Leave empty: the container generates a random session secret on first
# start and keeps it in the chorus-local-data volume. Set it explicitly
# (openssl rand -base64 32) if you prefer to manage it yourself.
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-}
- COOKIE_SECURE=false
- DEFAULT_USER=${DEFAULT_USER:-admin@example.com}
- DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-changeme}
volumes:
- chorus-local-data:/app/data
volumes:
chorus-local-data:

그런 다음 실행합니다.

Terminal window
docker compose -f docker-compose.local.yml up -d

http://localhost:8637을 열고 admin@example.com / changeme로 로그인합니다(DEFAULT_USER / DEFAULT_PASSWORD 환경 변수로 바꿀 수 있습니다).

스탠드얼론 모드에서 컨테이너는 다음을 수행합니다.

  • PGlite를 외부에 노출되지 않는 내부 포트(5433)에서 시작합니다.
  • 데이터를 chorus-local-data Docker 볼륨에 저장하므로 재시작해도 유지됩니다.
  • NEXTAUTH_SECRET을 비워 두면 첫 시작 시 무작위 세션 서명 비밀 키를 생성해 같은 볼륨에 저장합니다(세션 비밀 키 참고).
  • Redis를 비활성화합니다(메모리 내 이벤트 전달로 대체되므로 단일 인스턴스 전용입니다).
  • 시작 시 Prisma 마이그레이션을 자동으로 실행합니다.

프로덕션 환경, 특히 여러 레플리카를 둘 때는 앱을 외부 PostgreSQL과 Redis에 연결하세요. docker-compose.yml을 만듭니다.

services:
app:
image: chorusaidlc/chorus-app:latest
ports:
- "8637:8637"
environment:
- DATABASE_URL=postgresql://chorus:chorus@db:5432/chorus
- REDIS_URL=redis://default:chorus-redis@redis:6379
# Session-signing secret. Leave empty and the container generates one on
# first start and persists it in the chorus-app-data volume (single
# replica only). For production, or more than one app container, set it
# explicitly in .env or the environment: openssl rand -base64 32
# Changing the secret invalidates Default Auth and Super Admin sessions.
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-}
- COOKIE_SECURE=${COOKIE_SECURE:-false}
- DEFAULT_USER=admin@example.com
- DEFAULT_PASSWORD=your-password
volumes:
- chorus-app-data:/app/data
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
redis:
image: redis:7-alpine
command: redis-server --requirepass chorus-redis
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "chorus-redis", "ping"]
interval: 5s
timeout: 3s
retries: 5
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: chorus
POSTGRES_PASSWORD: chorus
POSTGRES_DB: chorus
volumes:
- chorus-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U chorus -d chorus"]
interval: 5s
timeout: 5s
retries: 5
volumes:
chorus-app-data:
chorus-data:
redis-data:

세션 비밀 키를 직접 지정하려면 시작하기 전에 한 번 생성해 compose 파일과 같은 위치의 .env 파일에 적어 둡니다.

Terminal window
echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)" >> .env

그런 다음 실행합니다.

Terminal window
docker compose up -d

http://localhost:8637을 열고 DEFAULT_USER / DEFAULT_PASSWORD에 설정한 인증 정보로 로그인합니다.

기존 PostgreSQL에 연결해 실행하기

섹션 제목: “기존 PostgreSQL에 연결해 실행하기”

이미 PostgreSQL과 Redis가 실행 중이라면 컨테이너를 직접 시작합니다.

Terminal window
docker run -d \
-p 8637:8637 \
-e DATABASE_URL=postgresql://user:pass@your-db-host:5432/chorus \
-e REDIS_URL=redis://default:password@your-redis-host:6379 \
-v chorus-app-data:/app/data \
-e COOKIE_SECURE=false \
-e DEFAULT_USER=admin@example.com \
-e DEFAULT_PASSWORD=your-password \
chorusaidlc/chorus-app:latest

이 예제에서는 생성된 비밀 키가 chorus-app-data에 보관되므로 컨테이너를 다시 만들어도 유지됩니다. 직접 관리하려면 고정된 NEXTAUTH_SECRET을 주입하고 시작할 때마다 같은 값을 사용하세요.

변수설명
DATABASE_URLPostgreSQL 연결 문자열. 형식: postgresql://user:password@host:port/dbname. 대신 개별 DB_* 변수를 설정할 수도 있습니다(아래 참고). 생략하면 엔트리포인트가 내장 PGlite 인스턴스를 자동으로 시작합니다.
NEXTAUTH_SECRET세션 토큰에 서명하기 위한 비밀 키. 무작위 문자열을 사용하세요(예: openssl rand -base64 32). Docker에서는 비워 둘 수 있습니다. 이 경우 컨테이너가 첫 시작 시 생성해 /app/data/.secret에 보관합니다. 앱 컨테이너를 여러 개 실행할 때는 모든 레플리카에 같은 값을 명시적으로 설정해야 합니다. 세션 비밀 키를 참고하세요.

데이터베이스(DATABASE_URL의 대안)

섹션 제목: “데이터베이스(DATABASE_URL의 대안)”

DATABASE_URL이 설정되지 않은 경우, 엔트리포인트는 다음 개별 변수로 그것을 조립합니다.

변수설명
DB_HOSTPostgreSQL 호스트
DB_PORTPostgreSQL 포트(기본값: 5432)
DB_USERNAMEPostgreSQL 사용자 이름
DB_PASSWORDPostgreSQL 비밀번호
DB_NAME데이터베이스 이름
변수설명
REDIS_URL전체 Redis 연결 문자열. 형식: redis://username:password@host:port. 개별 변수보다 우선합니다.
REDIS_HOSTRedis 호스트(REDIS_URL이 설정되지 않은 경우 사용)
REDIS_PORTRedis 포트(기본값: 6379)
REDIS_USERNAMERedis 사용자 이름(기본값: default)
REDIS_PASSWORDRedis 비밀번호
변수설명
DEFAULT_USER내장 로그인용 이메일 주소(OIDC를 우회합니다). 첫 로그인 시 사용자와 해당 워크스페이스를 자동으로 생성합니다.
DEFAULT_PASSWORD기본 사용자의 비밀번호(평문으로 지정하며, 런타임에 bcrypt로 비교합니다).
NEXTAUTH_URL앱의 외부 공개 기본 URL(기본값: http://localhost:8637). 리버스 프록시 뒤에서 실행할 때 설정합니다.
COOKIE_SECUREHTTP 전용 배포에서 보안 쿠키를 끄려면 "false"로 설정합니다(docker-compose에서는 기본값 "false"). 프로덕션에서 HTTPS로 배포할 때는 "true"로 설정합니다.
변수기본값설명
LOG_LEVELinfo(프로덕션) / debug(개발)서버 최소 로그 레벨. 허용 값: trace, debug, info, warn, error, fatal, silent. Prisma 쿼리 로그를 억제하려면 info로 설정합니다.
NEXT_PUBLIC_LOG_LEVELwarn(프로덕션) / debug(개발)브라우저 최소 로그 레벨. 허용 값: debug, info, warn, error.

프로덕션 Docker 이미지는 항상 JSON을 stdout으로 출력합니다(CloudWatch / ELK에 바로 연결 가능). 색상이 입혀진 보기 좋은 출력은 로컬 개발에서만 사용할 수 있습니다.

변수설명
SUPER_ADMIN_EMAIL슈퍼 관리자 계정의 이메일(/admin 패널에 접근할 수 있습니다).
SUPER_ADMIN_PASSWORD_HASH슈퍼 관리자 비밀번호의 bcrypt 해시. 생성 방법: node -e "console.log(require('bcryptjs').hashSync('your-password', 10))"

컨테이너의 엔트리포인트는 시작할 때마다 같은 순서를 실행합니다.

  1. NEXTAUTH_SECRET을 확인합니다. 값이 비어 있거나 예전에 배포된 예제에 들어 있던 자리 표시자 값 중 하나이면, 컨테이너는 /app/data/.secret에서 비밀 키를 읽거나 새로 생성하고 무엇을 했는지 로그에 남깁니다(값 자체는 절대 출력하지 않습니다). 직접 명시적으로 설정한 비밀 키는 그대로 사용됩니다.
  2. DATABASE_URL이 설정되지 않고 DB_* 변수도 하나도 제공되지 않으면, 엔트리포인트는 내부 포트(5433)에서 내장 PGlite 인스턴스를 시작합니다. 외부 데이터베이스가 구성되어 있으면 PGlite는 시작하지 않습니다.
  3. prisma migrate deploy를 실행해 보류 중인 데이터베이스 마이그레이션을 적용합니다.
  4. 데이터베이스가 아직 준비되지 않았으면 10초마다 재시도합니다(최대 30회, 약 5분).
  5. 마이그레이션이 성공하면 Next.js 서버가 8637 포트에서 시작합니다.

NEXTAUTH_SECRET은 Chorus가 직접 발급하는 로그인 세션(내장 이메일/비밀번호 로그인과 슈퍼 관리자 세션)에 서명합니다. OIDC 토큰은 ID 제공자가 서명하며, 에이전트 API 키는 별도로 검증됩니다. 이 비밀 키를 교체하면 Chorus가 서명한 세션만 무효화됩니다. API 키나 ID 제공자의 OIDC 토큰은 바뀌지 않습니다.

  • 명시적으로 설정한, 자리 표시자가 아닌 값: 파일을 읽거나 쓰지 않고 그대로 사용합니다.
  • 미설정, 빈 값, 알려진 공개 자리 표시자: /app/data/.secret을 재사용합니다. 파일이 없거나 비어 있으면 무작위 32바이트를 64자리 16진수로 생성해 0600 권한으로 저장합니다. 환경 변수의 자리 표시자에는 경고도 남깁니다.
  • 사용할 수 없는 저장된 키: 일반 파일이 아니거나 읽을 수 없는 경우, 공개 자리 표시자가 저장되어 있는 경우, 또는 생성·쓰기에 실패한 경우에는 마이그레이션 전에 시작을 중단합니다. 저장된 잘못된 자리 표시자를 조용히 받아들이거나 교체하지 않습니다.

키 값은 로그에 출력하지 않습니다. 자동 생성된 키를 유지하려면 /app/data를 영구 볼륨에 두세요. 여러 레플리카에는 같은 키를 명시적으로 주입해야 합니다. AWS CDK 스택은 이미 Secrets Manager를 통해 키를 공유합니다.

openssl rand -base64 32로 한 번 생성해 배포 환경의 비밀 저장소나 보호된 .env에 보관하고 이후 시작할 때 재사용하세요. 실제로 사용되는 키가 바뀌면 내장 이메일/비밀번호 로그인 사용자와 슈퍼 관리자가 다시 로그인해야 합니다. .secret을 삭제하면 자동 생성된 키만 교체되며, 명시적으로 설정한 키가 있으면 아무 영향이 없습니다.

이 자동 생성은 Docker 엔트리포인트와 npm chorus 런처가 수행합니다. next start나 node server.js로 직접 실행한다면 안전한 값을 직접 설정해야 합니다. 앱은 알려진 자리 표시자를 오류 로그에 기록하지만 교체하지는 않습니다. 키가 없으면 Chorus는 자체 로그인 세션을 발급할 수 없습니다.

  • 기본 이미지: node:22-alpine
  • 내부 포트: 8637
  • 아키텍처: linux/amd64, linux/arm64