dev-pilots 표준 파이프라인 적용: deploy.yml + docker-compose (포트 3000)
- 공식 워크플로우(.gitea/workflows/deploy.yml) 그대로 사용 (repo secrets 주입) - deploy/docker-compose.yml: Next.js 포트 3000으로 Traefik 라벨 변경 - 커스텀 배포 방식 제거 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
이 커밋은 다음에 포함됨:
@@ -1,62 +0,0 @@
|
||||
# Gitea Actions — 빌드 & 배포
|
||||
#
|
||||
# 동작:
|
||||
# 1. main 브랜치 push 시 자동 실행
|
||||
# 2. 러너가 호스트 docker 데몬으로 이미지 빌드 (레지스트리 불필요)
|
||||
# 3. 같은 호스트에서 컨테이너 교체 → Traefik이 jskim.vexplor.com HTTPS 라우팅
|
||||
#
|
||||
# DATABASE_URL은 서버의 /home/three/jskim/.env (chmod 600)에서 읽는다.
|
||||
# 저장소에는 어떤 비밀값도 커밋하지 않는다.
|
||||
|
||||
name: build-and-deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
IMAGE: harbor.wace.me/dev-pilots/jskim:latest
|
||||
CONTAINER_NAME: jskim
|
||||
DOMAIN: jskim.vexplor.com
|
||||
HOST_ENV_DIR: /home/three/jskim
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: docker CLI 준비
|
||||
run: command -v docker || (apt-get update -qq && apt-get install -y -qq docker.io)
|
||||
|
||||
- name: 이미지 빌드
|
||||
run: docker build -t "$IMAGE" .
|
||||
|
||||
- name: 컨테이너 교체
|
||||
run: |
|
||||
# 호스트의 env 파일을 호스트 docker 데몬 경유로 읽는다 (러너 컨테이너에는 미마운트)
|
||||
DATABASE_URL=$(docker run --rm -v "$HOST_ENV_DIR":/cfg:ro alpine sh -c 'grep "^DATABASE_URL=" /cfg/.env | cut -d= -f2-')
|
||||
if [ -z "$DATABASE_URL" ]; then echo "호스트 $HOST_ENV_DIR/.env 에 DATABASE_URL 없음"; exit 1; fi
|
||||
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
|
||||
docker run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--restart always \
|
||||
--network web \
|
||||
-e TZ=Asia/Seoul \
|
||||
-e DATABASE_URL="$DATABASE_URL" \
|
||||
--label "traefik.enable=true" \
|
||||
--label "traefik.docker.network=web" \
|
||||
--label "traefik.http.routers.jskim.rule=Host(\`$DOMAIN\`)" \
|
||||
--label "traefik.http.routers.jskim.entrypoints=websecure" \
|
||||
--label "traefik.http.routers.jskim.tls.certresolver=le" \
|
||||
--label "traefik.http.services.jskim.loadbalancer.server.port=3000" \
|
||||
"$IMAGE"
|
||||
docker network connect pgnet "$CONTAINER_NAME"
|
||||
|
||||
- name: 헬스체크
|
||||
run: |
|
||||
sleep 10
|
||||
docker exec "$CONTAINER_NAME" wget -qO- http://localhost:3000/api/health || (docker logs --tail 50 "$CONTAINER_NAME"; exit 1)
|
||||
@@ -0,0 +1,35 @@
|
||||
name: build-and-deploy
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
env:
|
||||
HARBOR: harbor.wace.me
|
||||
PROJECT: dev-pilots
|
||||
APP: jskim
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: tools
|
||||
run: |
|
||||
apt-get update -qq
|
||||
apt-get install -y ca-certificates curl git docker.io docker-compose-v2 >/dev/null 2>&1 || apt-get install -y ca-certificates curl git docker.io >/dev/null
|
||||
- name: checkout
|
||||
run: |
|
||||
rm -rf src && git clone --depth 1 --branch "$GITHUB_REF_NAME" http://oauth2:${{ github.token }}@gitea:3000/${GITHUB_REPOSITORY}.git src
|
||||
- name: build
|
||||
run: |
|
||||
cd src && docker build -t $HARBOR/$PROJECT/$APP:latest .
|
||||
- name: push
|
||||
env:
|
||||
HUSER: ${{ secrets.HARBOR_USER }}
|
||||
HSECRET: ${{ secrets.HARBOR_SECRET }}
|
||||
run: |
|
||||
echo "$HSECRET" | docker login $HARBOR -u "$HUSER" --password-stdin
|
||||
docker push $HARBOR/$PROJECT/$APP:latest
|
||||
- name: deploy
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
run: |
|
||||
cd src && docker compose -f deploy/docker-compose.yml up -d --pull always --force-recreate
|
||||
@@ -25,13 +25,14 @@ npm run dev
|
||||
|
||||
## 배포
|
||||
|
||||
`main` 브랜치에 push하면 Gitea Actions(`.gitea/workflows/build-and-deploy.yml`)가 자동으로:
|
||||
dev-pilots 교육 파이프라인을 그대로 사용한다. `main` 브랜치에 push하면
|
||||
Gitea Actions(`.gitea/workflows/deploy.yml`)가 자동으로:
|
||||
|
||||
1. 서버(THREEDEV)에서 Docker 이미지 빌드 (레지스트리 불필요)
|
||||
2. 컨테이너 교체 (`web` + `pgnet` 네트워크, Traefik 라벨)
|
||||
3. `/api/health` 헬스체크
|
||||
1. Docker 이미지 빌드 → Harbor push (`harbor.wace.me/dev-pilots/jskim`)
|
||||
2. `deploy/docker-compose.yml`로 컨테이너 교체 (`web` + `pgnet` 네트워크, Traefik 라벨, 포트 3000)
|
||||
3. Traefik이 `jskim.vexplor.com` HTTPS 라우팅
|
||||
|
||||
운영 `DATABASE_URL`은 서버의 `/home/three/jskim/.env`(chmod 600)에서 읽는다.
|
||||
운영 `DATABASE_URL`과 Harbor 인증(`HARBOR_USER`/`HARBOR_SECRET`)은 저장소 Secrets로 이미 등록되어 있다.
|
||||
|
||||
## 구조
|
||||
|
||||
@@ -42,5 +43,6 @@ src/
|
||||
lib/db.ts # pg Pool (DATABASE_URL 사용)
|
||||
scripts/db-tunnel.sh
|
||||
Dockerfile # standalone 빌드, node:20-alpine
|
||||
.gitea/workflows/ # CI/CD
|
||||
deploy/docker-compose.yml # 배포 정의 (Traefik 라벨, 앱 포트)
|
||||
.gitea/workflows/deploy.yml # CI/CD (dev-pilots 표준)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
jskim:
|
||||
image: harbor.wace.me/dev-pilots/jskim:latest
|
||||
container_name: jskim
|
||||
restart: always
|
||||
environment:
|
||||
TZ: Asia/Seoul
|
||||
DATABASE_URL: ${DATABASE_URL:?set DATABASE_URL in repo secrets}
|
||||
networks: [web, pgnet]
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.jskim.rule=Host(`jskim.vexplor.com`)
|
||||
- traefik.http.routers.jskim.entrypoints=websecure
|
||||
- traefik.http.routers.jskim.tls.certresolver=le
|
||||
# Next.js standalone 서버 포트 (기존 nginx 플레이스홀더는 80이었음)
|
||||
- traefik.http.services.jskim.loadbalancer.server.port=3000
|
||||
- traefik.docker.network=web
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
pgnet:
|
||||
external: true
|
||||
새 이슈에서 참조
사용자 차단