Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.git
.github
.gradle
.idea
.env
.env.*
build
deploy
docs
node_modules
**/postgres-data
**/redis-data
*.log
128 changes: 114 additions & 14 deletions .github/workflows/deploy-to-dev-ec2-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@ on:
push:
branches: [ develop ]
workflow_dispatch:
inputs:
deploy_mode:
description: 전체 서비스를 배포하거나 이전을 위해 PostgreSQL과 Redis만 준비합니다
type: choice
required: true
default: full
options:
- full
- infrastructure-only

concurrency:
group: deploy-dev
cancel-in-progress: false

jobs:
deploy:
# 서버와 데이터를 옮기는 동안 develop push 자동 배포를 잠근다.
# 새 서버 전환이 끝난 뒤 저장소 변수 DEV_ARM64_AUTODEPLOY를 true로 설정한다.
if: ${{ github.event_name == 'workflow_dispatch' || vars.DEV_ARM64_AUTODEPLOY == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -21,6 +37,11 @@ jobs:
id: normalize
run: echo "image_repo=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"

- name: 🏗️ Setup QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64

- name: 🧱 Setup Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -41,23 +62,37 @@ jobs:
type=sha,format=long,prefix=dev-

- name: 🏗️ Build & Push Image
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ steps.normalize.outputs.image_repo }}:buildcache
cache-to: type=registry,ref=${{ steps.normalize.outputs.image_repo }}:buildcache,mode=max
cache-from: type=registry,ref=${{ steps.normalize.outputs.image_repo }}:buildcache-arm64
cache-to: type=registry,ref=${{ steps.normalize.outputs.image_repo }}:buildcache-arm64,mode=max

- name: 📁 Prepare deployment directory
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
script: |
set -euo pipefail
# 최초 배포 시에도 사용자가 디렉터리를 직접 만들 필요가 없게 한다.
install -d -m 0750 "${HOME}/saerok"

- name: 📤 Copy compose files to EC2
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
source: "deploy/docker-compose.yml,deploy/docker-compose.dev.yml,deploy/postgres-ko/Dockerfile"
source: "deploy/docker-compose.yml,deploy/docker-compose.dev.yml,deploy/postgres-ko/Dockerfile,deploy/nginx/saerok-dev-http.conf"
# 기존 개발 서버와 동일하게 ubuntu 사용자의 홈 아래에 배포한다.
target: ~/saerok/
strip_components: 1

Expand All @@ -70,7 +105,24 @@ jobs:
script: |
set -euo pipefail

cd ~/saerok
DEPLOY_MODE="${{ inputs.deploy_mode || 'full' }}"
IMAGE_REF="${{ steps.normalize.outputs.image_repo }}@${{ steps.build.outputs.digest }}"
DEPLOY_DIR="${HOME}/saerok"

# 최초 배포에서도 별도 서버 설정 없이 필요한 영속 디렉터리를 자동 생성한다.
# 이미 생성된 DB 디렉터리의 소유권은 강제로 바꾸지 않는다.
# 각 컨테이너의 시작 스크립트가 데이터 디렉터리 소유권을 관리한다.
for relative_path in \
data/postgres \
data/redis \
backup \
accesslogs; do
data_path="${DEPLOY_DIR}/${relative_path}"
if [ ! -d "$data_path" ]; then
sudo install -d -m 0750 -o "$(id -un)" -g "$(id -gn)" "$data_path"
fi
done
cd "$DEPLOY_DIR"

# 0) Docker & Compose 확인
docker --version
Expand All @@ -89,7 +141,7 @@ jobs:

add_kv() { printf '%s\n' "$1=$2" >> "$ENV_FILE"; }
add_kv SPRING_PROFILES_ACTIVE "dev"
add_kv DB_URL "${{ secrets.DB_URL }}"
add_kv DB_URL "jdbc:postgresql://postgres:5432/saerok"
add_kv DB_USERNAME "${{ secrets.DB_USERNAME }}"
add_kv DB_PASSWORD "${{ secrets.DB_PASSWORD }}"
add_kv APPLE_CLIENT_ID "${{ secrets.APPLE_CLIENT_ID }}"
Expand All @@ -111,29 +163,65 @@ jobs:
add_kv KAKAO_CLIENT_SECRET "${{ secrets.KAKAO_CLIENT_SECRET }}"
add_kv KAKAO_APP_ID "${{ secrets.KAKAO_APP_ID }}"
add_kv KMS_KEY_ID "${{ secrets.KMS_KEY_ID }}"
add_kv IMAGE_REF "$IMAGE_REF"
add_kv ENV_FILE "$ENV_FILE"

# 3) 커스텀 PostGIS + ko_KR UTF-8 이미지 빌드 (BuildKit 끄고, buildx 안 씀)
DOCKER_BUILDKIT=0 docker build -t saerok-postgres:ko ./postgres-ko
# 3) ARM64 EC2에서 해당 아키텍처용 PostGIS 이미지를 직접 빌드
docker build -t saerok-postgres:17-postgis ./postgres-ko

# 4) app 이미지만 pull
# 4) 데이터가 유지되는 PostgreSQL과 Redis를 먼저 시작
# 최초 이전 때는 앱을 실행하기 전에 이 상태에서 pg_restore를 수행한다.
docker compose \
--env-file "$ENV_FILE" \
-p saerok \
-f docker-compose.yml \
-f docker-compose.dev.yml \
pull --quiet app
up -d postgres redis

# 5) compose up (postgres는 로컬 saerok-postgres:ko 이미지 사용)
echo "Waiting for PostgreSQL and Redis..."
for i in {1..30}; do
postgres_health="$(docker inspect --format '{{.State.Health.Status}}' saerok-postgres-dev 2>/dev/null || true)"
redis_health="$(docker inspect --format '{{.State.Health.Status}}' saerok-redis-dev 2>/dev/null || true)"
if [ "$postgres_health" = "healthy" ] && [ "$redis_health" = "healthy" ]; then
break
fi
if [ "$i" -eq 30 ]; then
docker compose --env-file "$ENV_FILE" -p saerok -f docker-compose.yml -f docker-compose.dev.yml ps
# 공개 GitHub Actions 로그에는 컨테이너 원문 로그를 올리지 않는다.
# 상세 원인은 EC2에서 docker logs 명령으로 직접 확인한다.
docker inspect --format 'postgres status={{.State.Status}} health={{.State.Health.Status}} restart={{.RestartCount}}' saerok-postgres-dev || true
docker inspect --format 'redis status={{.State.Status}} health={{.State.Health.Status}} restart={{.RestartCount}}' saerok-redis-dev || true
exit 1
fi
sleep 4
done

# PostGIS 패키지 설치와 DB 확장 활성화는 별개다.
# 빈 DB에서도 데이터 복원 전에 geometry 타입을 사용할 수 있도록 항상 확인한다.
docker exec saerok-postgres-dev sh -lc \
'psql -v ON_ERROR_STOP=1 \
-U "$POSTGRES_USER" \
-d "$POSTGRES_DB" \
-c "CREATE EXTENSION IF NOT EXISTS postgis;" \
-c "SELECT postgis_version();"'

if [ "$DEPLOY_MODE" = "infrastructure-only" ]; then
docker compose --env-file "$ENV_FILE" -p saerok -f docker-compose.yml -f docker-compose.dev.yml ps
echo "Infrastructure is ready for pg_restore; application was not started."
exit 0
fi

# 5) digest로 고정된 앱 이미지를 받고 앱 컨테이너만 다시 생성
docker pull "$IMAGE_REF"
docker compose \
--env-file "$ENV_FILE" \
-p saerok \
-f docker-compose.yml \
-f docker-compose.dev.yml \
up -d --force-recreate --quiet-pull
up -d --force-recreate --no-deps app

# 6) 상태 확인
docker compose -p saerok -f docker-compose.yml -f docker-compose.dev.yml ps
docker logs --tail=120 saerok-dev || true
docker compose --env-file "$ENV_FILE" -p saerok -f docker-compose.yml -f docker-compose.dev.yml ps

# 7) Healthcheck
echo "Waiting for health endpoint..."
Expand All @@ -149,5 +237,17 @@ jobs:
done

echo "❌ Healthcheck failed after retries"
docker logs --tail=200 saerok-dev || true
# 사용자 인증정보가 포함될 수 있는 앱 원문 로그는 Actions에 출력하지 않는다.
docker inspect --format 'app status={{.State.Status}} health={{.State.Health.Status}} restart={{.RestartCount}}' saerok-dev || true
exit 1

- name: Summarize deployment
if: ${{ success() }}
run: |
{
echo "## Dev arm64 deploy"
echo "- Mode: ${{ inputs.deploy_mode || 'full' }}"
echo "- Platform: linux/arm64"
echo "- Image: ${{ steps.normalize.outputs.image_repo }}@${{ steps.build.outputs.digest }}"
echo "- Commit: ${{ github.sha }}"
} >> "$GITHUB_STEP_SUMMARY"
88 changes: 0 additions & 88 deletions .github/workflows/deploy-to-dev-ec2.yml

This file was deleted.

Loading
Loading