Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6f25d5c
Chore: docker-compose.yml, nginx.conf - staging ์„ค์ • ์ˆ˜์ •
lunarbae628 Oct 20, 2025
13c57b9
Chore: ๋„์ปค ์ด๋ฏธ์ง€ ํƒœ๊ทธ (dev, staging)์œผ๋กœ ๋ณ€๊ฒฝ/๋ถ„๊ธฐ ์ฒ˜๋ฆฌ ๋ฐ ๋ฐฐํฌ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
lunarbae628 Oct 20, 2025
8e773d0
Chore: docker-compose.stg.yml ๋ถ„๋ฆฌ
lunarbae628 Oct 20, 2025
3287313
Chore: gitignore ์ˆ˜์ •
lunarbae628 Oct 20, 2025
cbfa594
Chore: docker-compose.stg.yml ์ˆ˜์ •
lunarbae628 Oct 20, 2025
3fa3de6
Chore: deploy.sh ์ˆ˜์ • (dev/staging ๋ถ„๊ธฐ ๋ฐฐํฌ ์ˆ˜์ •)
lunarbae628 Oct 20, 2025
8808bf1
Chore: ci-cd.yml ์ˆ˜์ • (Deploy ํƒœ๊ทธ ๋ฐ TARGET ์ธ์ž ์ „๋‹ฌ ์ฒ˜๋ฆฌ)
lunarbae628 Oct 20, 2025
4f0dfcd
Chore: ci-cd.yml mysql username/password ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฒ˜๋ฆฌ
lunarbae628 Oct 20, 2025
474762e
Chore: nginx.conf/ngins.stg.conf ๋ถ„๋ฆฌ
lunarbae628 Oct 20, 2025
8fc86f5
Chore: ngins.stg.conf ssh์ธ์ฆ์„œ ์ตœ์ดˆ๋ฐœ๊ธ‰์šฉ ์ฃผ์„
lunarbae628 Oct 20, 2025
088afcd
Chore: application-prod.yml ddl_auto ํ™˜๊ฒฝ๋ณ€์ˆ˜
lunarbae628 Oct 20, 2025
e28ce83
Chore: DDL_AUTO docker-compose.stg.yml ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ถ”๊ฐ€
lunarbae628 Oct 20, 2025
588788c
Chore: .env.example ddl_auto์ถ”๊ฐ€
lunarbae628 Oct 20, 2025
272d262
Chore: ์˜คํƒ€์ˆ˜์ •
lunarbae628 Oct 20, 2025
6cec26f
Chore: nginx.stg.conf 443 ์ฃผ์„ ์ œ๊ฑฐ
lunarbae628 Oct 20, 2025
6737ea7
Chore: deploy.sh ROOT ์ˆ˜์ •
lunarbae628 Oct 20, 2025
c1f29c9
Fix: docker-compose.stg.yml nginx ํฌํŠธ ๋ณ€๊ฒฝ
lunarbae628 Oct 20, 2025
26b21b6
Chore: deploy.sh ROOT ์ˆ˜์ •(/docsa/infra)
lunarbae628 Oct 20, 2025
64873d5
Chore: deploy.sh ROOT ์ˆ˜์ •(OVR๊ฒฝ๋กœ ์ˆ˜์ •)
lunarbae628 Oct 20, 2025
5d2afa0
Chore: deploy.sh ROOT ์˜คํƒ€ ์ˆ˜์ •
lunarbae628 Oct 20, 2025
3e58e29
Chore: application-stg.yml ๋ถ„๋ฆฌ ๋ฐ mail host mailpit ์‚ฌ์šฉ(ํšŒ์›๊ฐ€์ž… ํ…Œ์ŠคํŠธ ์šฉ๋„)
lunarbae628 Oct 21, 2025
5a47035
Chore: docker-compose.stg.yml (mailpit ์ถ”๊ฐ€)
lunarbae628 Oct 21, 2025
a828ac9
Chore: docker-compose.stg.yml (mailpit - networks ์ถ”๊ฐ€)
lunarbae628 Oct 21, 2025
7d627eb
Feat: mailpit ๊ตฌ์ถ•
lunarbae628 Oct 21, 2025
9aba1e7
Chore: docker-compose.stg.yml (mailpit - username/password ๊ฐ’์ถ”๊ฐ€)
lunarbae628 Oct 22, 2025
06ba1ba
Chore: mailpitํ—ฌ์Šค์ฒดํฌ
lunarbae628 Oct 22, 2025
7533627
Fix: mailpit ํ—ฌ์Šค์ฒดํฌ ๋ฌธ๋ฒ• ์ˆ˜์ •
lunarbae628 Oct 22, 2025
b8a452e
Chore: ci-cd.yml inputs description์ˆ˜์ •
lunarbae628 Oct 22, 2025
cdea59e
Chore: mail ์„ค์ • ๋ฐ management ์œ„์น˜ ์ •๋ฆฌ, local/stg/prod ํ”„๋กœํ•„ ๊ตฌ์กฐ ๊ฐœ์„ 
lunarbae628 Oct 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 33 additions & 13 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ name: CI/CD โ€” Backend (Build โ†’ Push โ†’ Deploy)

on:
push:
branches: [ dev ]
branches: [ dev, staging ]
pull_request:
branches: [ dev ]
branches: [ dev, staging ]
workflow_dispatch:
inputs:
tag:
description: "๋ฐฐํฌํ•  ์ด๋ฏธ์ง€ ํƒœ๊ทธ (ex. release or <GIT_SHA>)"
description: "๋ฐฐํฌํ•  ์ด๋ฏธ์ง€ ํƒœ๊ทธ (ex. dev or dev-SHORT_SHA)"
required: false
default: "release"
default: "dev"

concurrency:
group: cicd-${{ github.ref_name }}
Expand Down Expand Up @@ -81,17 +81,32 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build & Push image
- name: Decide tags by branch
if: github.event_name == 'push'
id: tags
shell: bash
run: |
REF="${{ github.ref_name }}" # dev / staging
SHORT_SHA="${GITHUB_SHA::7}"
SAFE_REF=$(echo "$REF" | tr '[:upper:]' '[:lower:]' | sed -E 's#[^a-z0-9._-]+#-#g')
echo "channel=$SAFE_REF" >> $GITHUB_OUTPUT # ex) dev, staging
echo "version=${SAFE_REF}-${SHORT_SHA}" >> $GITHUB_OUTPUT # ex) dev-abc1234

- name: Build & Push (branch-tagged)
if: github.event_name == 'push'
run: |
GIT_SHA=${{ github.sha }}
docker build -f "${{ steps.df.outputs.path }}" -t $IMAGE:release -t $IMAGE:$GIT_SHA "${{ steps.df.outputs.ctx }}"
docker push $IMAGE:release
docker push $IMAGE:$GIT_SHA
CHAN=${{ steps.tags.outputs.channel }} # dev | staging
VER=${{ steps.tags.outputs.version }} # dev-<sha> | staging-<sha>
docker build -f "${{ steps.df.outputs.path }}" \
-t $IMAGE:$CHAN \
-t $IMAGE:$VER \
"${{ steps.df.outputs.ctx }}"
docker push $IMAGE:$CHAN
docker push $IMAGE:$VER

deploy:
needs: build-and-push
if: github.event_name == 'push' && (github.ref == 'refs/heads/dev') &&
if: github.event_name == 'push' && (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/staging') &&
(needs.build-and-push.result == 'success')
|| (github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
Expand All @@ -103,7 +118,7 @@ jobs:
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.tag }}" ]; then
echo "value=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
else
echo "value=release" >> $GITHUB_OUTPUT
echo "value=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi

- name: Deploy
Expand All @@ -114,5 +129,10 @@ jobs:
key: ${{ secrets.CD_SSH_KEY }}
port: ${{ secrets.CD_PORT }}
script: |
export DEPLOY_TAG='${{ steps.tag.outputs.value }}'
bash -lc '/srv/docsa/infra/deploy.sh'
if [ "${{ github.ref_name }}" = "staging" ]; then
export DEPLOY_TAG='${{ steps.tag.outputs.value }}'
bash -lc '/srv/docsa/infra/deploy.sh staging'
else
export DEPLOY_TAG='${{ steps.tag.outputs.value }}'
bash -lc '/srv/docsa/infra/deploy.sh dev'
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ out/

### Secrets & Runtime
infra/.env
infra/*.env
infra/**/*.env
infra/certbot
infra/mysql/exporter.cnf
Expand Down
1 change: 1 addition & 0 deletions infra/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ SPRING_MAIL_PASSWORD=
SPRING_DATASOURCE_URL=
SPRING_DATASOURCE_USERNAME=
SPRING_DATASOURCE_PASSWORD=
DDL_AUTO=

# ------------ SESSION/COOKIE ------------
SESSION_COOKIE_NAME=
Expand Down
88 changes: 66 additions & 22 deletions infra/deploy.sh
Original file line number Diff line number Diff line change
@@ -1,44 +1,88 @@
#!/usr/bin/env bash
set -euo pipefail

cd /srv/docsa/infra
# ์‚ฌ์šฉ๋ฒ•:
# /srv/docsa/infra/deploy.sh dev
# /srv/docsa/infra/deploy.sh staging
# ํ™˜๊ฒฝ๋ณ€์ˆ˜(์„ ํƒ):
# DEPLOY_TAG : ๋ฎ์–ด์“ธ ์ด๋ฏธ์ง€ ํƒœ๊ทธ(dev, dev-<sha>, staging, staging-<sha> ๋“ฑ)
# IMAGE_BASE : GHCR ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ (๊ธฐ๋ณธ: ghcr.io/prgrms-web-devcourse-final-project/docsa-backend)
# SERVICE : ๋ฐฐํฌํ•  ์„œ๋น„์Šค๋ช… (๊ธฐ๋ณธ app)
# HEALTH_TIMEOUT : ํ—ฌ์Šค ๋Œ€๊ธฐ์‹œ๊ฐ„ ์ดˆ (๊ธฐ๋ณธ 120)

# ํŠน์ • ํƒœ๊ทธ๋กœ ๋ฐฐํฌ์‹œ DEPLOY_TAG=...
IMAGE_BASE="ghcr.io/prgrms-web-devcourse-final-project/docsa-backend"
TAG="${DEPLOY_TAG:-}" # ๋น„์–ด์žˆ์œผ๋ฉด compose์— ์ ํžŒ ํƒœ๊ทธ ์‚ฌ์šฉ
TARGET="${1:-}" # โ† dev ๋˜๋Š” staging ์ธ์ž ํ•„์ˆ˜
if [[ "$TARGET" != "dev" && "$TARGET" != "staging" ]]; then
echo "Usage: $0 <dev|staging>"
exit 2
fi

IMAGE_BASE="${IMAGE_BASE:-ghcr.io/prgrms-web-devcourse-final-project/docsa-backend}"
SERVICE="${SERVICE:-app}"
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-120}"

cd "$(dirname "$0")"
ROOT="$(pwd)"

# ํƒ€๊นƒ์— ๋”ฐ๋ผ compose/env ์ž๋™ ์„ ํƒ
if [[ "$TARGET" == "dev" ]]; then
COMPOSE_FILE="$ROOT/docker-compose.yml"
ENV_FILE="$ROOT/.env"
else
COMPOSE_FILE="$ROOT/docker-compose.stg.yml"
ENV_FILE="$ROOT/.stg.env"
fi

TAG="${DEPLOY_TAG:-$TARGET}" # โ† ๊ธฐ๋ณธ์€ ์ฑ„๋„ ํƒœ๊ทธ(dev|staging), ์ž…๋ ฅ ์žˆ์œผ๋ฉด ์šฐ์„ 

# ์ž„์‹œ override ํŒŒ์ผ๋กœ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋งŒ ๋ฎ์–ด์“ฐ๊ธฐ
OVR=""
if [[ -n "$TAG" ]]; then
OVR="docker-compose.override.deploy.yml"
OVR="$ROOT/docker-compose.override.deploy.yml"
cat > "$OVR" <<EOF
services:
app:
${SERVICE}:
image: ${IMAGE_BASE}:${TAG}
EOF
fi

FILES=(-f docker-compose.yml)
[[ -n "$OVR" ]] && FILES+=(-f "$OVR")
# docker compose ์ธ์ž ๊ตฌ์„ฑ (-p๋กœ ํ”„๋กœ์ ํŠธ ๊ฒฉ๋ฆฌ, --env-file๋กœ env ๋ช…์‹œ)
ARGS=(-f "$COMPOSE_FILE")
[[ -n "$OVR" ]] && ARGS+=(-f "$OVR")
ARGS+=(--env-file "$ENV_FILE")

echo "[deploy] target=$TARGET tag=$TAG"
echo "[deploy] compose=$COMPOSE_FILE env=$ENV_FILE service=$SERVICE"

# 1) ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ(๋ฌธ๋ฒ•/์น˜ํ™˜ ํ™•์ธ)
docker compose "${ARGS[@]}" config >/dev/null

# ์ตœ์‹  ์ด๋ฏธ์ง€ ๋ฐ›๊ณ  ๊ต์ฒด
docker compose "${FILES[@]}" pull app
docker compose "${FILES[@]}" up -d app
# 2) ์ด๋ฏธ์ง€ ํ’€ + ๋Œ€์ƒ ์„œ๋น„์Šค๋งŒ ์—…๋ฐ์ดํŠธ
docker compose "${ARGS[@]}" pull "$SERVICE" || true
docker compose "${ARGS[@]}" up -d "$SERVICE"

# ํ—ฌ์Šค์ฒดํฌ ๋Œ€๊ธฐ (์ตœ๋Œ€ 120s)
echo -n "Waiting for app (docsa-app) to be healthy"
# 3) ์ปจํ…Œ์ด๋„ˆ ID๋ฅผ compose๋กœ ์กฐํšŒ(์ด๋ฆ„ ํ•˜๋“œ์ฝ”๋”ฉ ํšŒํ”ผ)
CID="$(docker compose "${ARGS[@]}" ps -q "$SERVICE" | tail -n1 || true)"
if [[ -z "$CID" ]]; then
echo "No container found for service '$SERVICE' (project $PROJECT)"; exit 1
fi

# 4) ํ—ฌ์Šค์ฒดํฌ ๋Œ€๊ธฐ
echo -n "Waiting for $SERVICE to be healthy"
ok=0
for i in {1..60}; do
status="$(docker inspect -f '{{.State.Health.Status}}' docsa-app 2>/dev/null || echo none)"
if [[ "$status" == "healthy" ]]; then ok=1; echo -e "\nApp healthy"; break; fi
ITER=$(( HEALTH_TIMEOUT / 2 ))
for _ in $(seq 1 "$ITER"); do
status="$(docker inspect -f '{{.State.Health.Status}}' "$CID" 2>/dev/null || echo none)"
if [[ "$status" == "healthy" ]]; then ok=1; echo -e "\n$SERVICE healthy"; break; fi
sleep 2; echo -n "."
done

# ์ž„์‹œ override ์ œ๊ฑฐ & ์ฒญ์†Œ
[[ -n "$OVR" ]] && rm -f "$OVR"
# 5) ์ž„์‹œ override ์ฒญ์†Œ + ์ด๋ฏธ์ง€ ์ •๋ฆฌ
[[ -n "$OVR" ]] && rm -f "$OVR" || true
docker image prune -f >/dev/null 2>&1 || true

# ์‹คํŒจ ์ฒ˜๋ฆฌ
# 6) ์‹คํŒจ ์‹œ ๋กœ๊ทธ ์ถœ๋ ฅ ํ›„ ์ข…๋ฃŒ
if [[ $ok -eq 0 ]]; then
echo -e "\nApp failed to become healthy (status=$status)"
docker logs --tail=200 docsa-app || true
echo -e "\n$SERVICE failed to become healthy (status=${status:-unknown})"
docker logs --tail=200 "$CID" || true
exit 1
fi
fi
Loading
Loading