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
182 changes: 182 additions & 0 deletions .github/workflows/cd-release-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
name: CD (release) - Deploy to Prod

on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: "Deploy version tag (e.g. 1.2.3)"
required: true
sha:
description: "Commit SHA to deploy (optional if version tag exists)"
required: false
update_latest:
description: "Also move :latest to this version?"
required: false
type: choice
options: ["true", "false"]
default: "true"

permissions:
contents: read
packages: write
id-token: write

concurrency:
group: deploy-prod-${{ github.ref }}
cancel-in-progress: false

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

# App 설정
APP_NAME: book-preprocessing-worker
APP_PORT: 9002

# AWS 설정
AWS_REGION_PROD: ap-northeast-2

# SSM Tags (서버 1대)
PROD_ENV_TAG_KEY: "tag:Env"
PROD_ENV_TAG_VALUE: "prod"
PROD_NODE_TAG_KEY: "tag:Node"
PROD_NODE_TAG_VALUE: "worker-bundle"

jobs:
# 1) 이미지 준비 및 태그 승격
prepare-image:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.out.outputs.version }}
update_latest: ${{ steps.out.outputs.update_latest }}
steps:
- name: Resolve VERSION & UPDATE_LATEST
shell: bash
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo "VERSION=${GITHUB_REF_NAME}" >> "$GITHUB_ENV"
echo "UPDATE_LATEST=true" >> "$GITHUB_ENV"
else
echo "VERSION=${{ inputs.version }}" >> "$GITHUB_ENV"
echo "UPDATE_LATEST=${{ inputs.update_latest || 'true' }}" >> "$GITHUB_ENV"
fi

- name: Resolve SHA
shell: bash
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo "SHA=${{ github.sha }}" >> "$GITHUB_ENV"
else
echo "SHA=${{ inputs.sha }}" >> "$GITHUB_ENV"
fi

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Ensure/Promote Image
shell: bash
run: |
set -euo pipefail
TARGET="${REGISTRY}/${IMAGE_NAME}:${VERSION}"
if docker buildx imagetools inspect "$TARGET" >/dev/null 2>&1; then
echo "✅ Image $TARGET already exists."
else
SRC="${REGISTRY}/${IMAGE_NAME}:main-${SHA}"
echo "🚀 Promoting $SRC to $TARGET"
docker buildx imagetools create "$SRC" --tag "$TARGET"
fi

- id: out
shell: bash
run: |
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "update_latest=${UPDATE_LATEST}" >> "$GITHUB_OUTPUT"

# 2) PROD 배포 (서버 1대)
deploy-prod:
needs: prepare-image
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ env.AWS_REGION_PROD }}
role-to-assume: ${{ secrets.AWS_DEPLOY_PROD_ROLE_ARN }}

- name: Deploy to prod (worker-bundle)
shell: bash
env:
VERSION: ${{ needs.prepare-image.outputs.version }}
GHCR_USER: ${{ secrets.GHCR_USER }}
GHCR_PAT: ${{ secrets.GHCR_PAT }}
APP_NAME: ${{ env.APP_NAME }}
APP_PORT: ${{ env.APP_PORT }}
run: |
set -euo pipefail

RAW_SCRIPT=$(cat <<'EOF'
#!/bin/bash
set -euo pipefail

export APP_NAME="$APP_NAME"
export APP_PORT="$APP_PORT"
export VERSION="$VERSION"

cd "/srv/todaybook/${APP_NAME}"
echo "$GHCR_PAT" | sudo docker login ghcr.io -u "$GHCR_USER" --password-stdin
chmod +x deploy.sh
./deploy.sh
EOF
)

export GHCR_USER GHCR_PAT APP_NAME APP_PORT VERSION
export SCRIPT=$(echo "$RAW_SCRIPT" | envsubst '$GHCR_USER $GHCR_PAT $APP_NAME $APP_PORT $VERSION')

PARAMS=$(python3 -c "import json, os; print(json.dumps({'commands': os.environ['SCRIPT'].splitlines()}))")

CMD_ID=$(aws ssm send-command \
--document-name "AWS-RunShellScript" \
--targets "Key=${PROD_ENV_TAG_KEY},Values=${PROD_ENV_TAG_VALUE}" "Key=${PROD_NODE_TAG_KEY},Values=${PROD_NODE_TAG_VALUE}" \
--parameters "$PARAMS" \
--query "Command.CommandId" --output text)

echo "Waiting for SSM Command: $CMD_ID"
while true; do
STATUS=$(aws ssm list-command-invocations \
--command-id "$CMD_ID" --details \
--query "CommandInvocations[0].Status" --output text 2>/dev/null || echo "Pending")
echo "Current status: $STATUS"
if [[ "$STATUS" == "Success" ]]; then break;
elif [[ "$STATUS" =~ ^(Failed|Cancelled|TimedOut)$ ]]; then
aws ssm list-command-invocations --command-id "$CMD_ID" --details
exit 1
fi
sleep 5
done

# 3) latest 태그 갱신 (배포 성공 후)
update-latest:
needs: [prepare-image, deploy-prod]
if: ${{ needs.prepare-image.outputs.update_latest == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Tag Latest
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.prepare-image.outputs.version }}"
docker buildx imagetools create "${REGISTRY}/${IMAGE_NAME}:${VERSION}" --tag "${REGISTRY}/${IMAGE_NAME}:latest"
69 changes: 69 additions & 0 deletions .github/workflows/ci-main-build-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: CI (main) - Build & Push Image

on:
push:
branches: ["main"]
workflow_dispatch:

permissions:
contents: read
packages: write # GHCR push에 필요

concurrency:
group: main-image-${{ github.ref }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build_push:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: gradle

- name: Build (skip tests optional)
run: ./gradlew clean build --no-daemon

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# 태그: main-<sha>, main-latest
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=main-${{ github.sha }}
type=raw,value=main-latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max