Skip to content

Commit 6a53ba1

Browse files
committed
chore(deploy): GH Actions SSH deploy pipeline (#22)
.github/workflows/deploy.yml + deploy/README.md secret 등록 절차. Workflow: - trigger: push to dev (→ staging), main (→ prod), 또는 workflow_dispatch - build job: Docker Buildx + GHCR (ghcr.io) push, GHA cache 사용 태그: '<target>-<sha7>' + '<target>' (latest 역할) - deploy job: appleboy/ssh-action 으로 N100 접속, docker compose pull && up -d (project name 으로 staging/prod 분리) - concurrency: 같은 ref 의 동시 배포 금지 - environment: GitHub Environments 의 'staging' / 'prod' 사용 (prod 는 수동 승인 가능) - Sanity check 단계: secret 누락 시 명시적 에러로 즉시 실패 필요 secrets (사용자 직접 등록): - N100_HOST / N100_USER / N100_SSH_KEY - GITHUB_TOKEN 은 자동 (GHCR push 권한 위해 permissions.packages: write) deploy/README.md: - '0. CI/CD 사용 시' 섹션 신설 — secret 등록 절차, SSH key 생성, prod environment 보호. - 기존 '1. 첫 배포 (수동)' 은 'CI/CD 우회/디버깅' 으로 강등 (호스트 init 1회는 여전히 필요)
1 parent fb11509 commit 6a53ba1

2 files changed

Lines changed: 176 additions & 1 deletion

File tree

.github/workflows/deploy.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Deploy
2+
3+
on:
4+
push:
5+
branches: [dev, main]
6+
workflow_dispatch:
7+
inputs:
8+
target:
9+
description: "배포 대상 (staging | prod)"
10+
required: true
11+
default: "staging"
12+
type: choice
13+
options: [staging, prod]
14+
15+
# 동시 배포 금지. 같은 대상에 새 push 가 오면 in-progress 취소.
16+
concurrency:
17+
group: deploy-${{ github.ref_name }}
18+
cancel-in-progress: false
19+
20+
permissions:
21+
contents: read
22+
packages: write # ghcr.io push 권한
23+
24+
env:
25+
REGISTRY: ghcr.io
26+
IMAGE: ${{ github.repository }}
27+
28+
jobs:
29+
# ---------------------------------------------------------------------------
30+
# 1) build image → ghcr.io 푸시
31+
# ---------------------------------------------------------------------------
32+
build:
33+
name: Build & push image
34+
runs-on: ubuntu-latest
35+
timeout-minutes: 15
36+
outputs:
37+
image_tag: ${{ steps.meta.outputs.image_tag }}
38+
target: ${{ steps.meta.outputs.target }}
39+
40+
steps:
41+
- name: Checkout
42+
uses: actions/checkout@v4
43+
44+
- name: Resolve target + tag
45+
id: meta
46+
run: |
47+
# 우선순위: workflow_dispatch.inputs.target → branch
48+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
49+
target="${{ inputs.target }}"
50+
elif [ "${{ github.ref_name }}" = "main" ]; then
51+
target="prod"
52+
else
53+
target="staging"
54+
fi
55+
sha_short="$(echo "${{ github.sha }}" | cut -c1-7)"
56+
image_tag="${target}-${sha_short}"
57+
echo "target=$target" >> "$GITHUB_OUTPUT"
58+
echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT"
59+
echo "image_ref=${REGISTRY}/${IMAGE}:${image_tag}" >> "$GITHUB_OUTPUT"
60+
echo "image_ref_target=${REGISTRY}/${IMAGE}:${target}" >> "$GITHUB_OUTPUT"
61+
62+
- name: Set up Docker Buildx
63+
uses: docker/setup-buildx-action@v3
64+
65+
- name: Login to GHCR
66+
uses: docker/login-action@v3
67+
with:
68+
registry: ${{ env.REGISTRY }}
69+
username: ${{ github.actor }}
70+
password: ${{ secrets.GITHUB_TOKEN }}
71+
72+
- name: Build & push
73+
uses: docker/build-push-action@v6
74+
with:
75+
context: .
76+
file: deploy/Dockerfile
77+
push: true
78+
tags: |
79+
${{ steps.meta.outputs.image_ref }}
80+
${{ steps.meta.outputs.image_ref_target }}
81+
cache-from: type=gha
82+
cache-to: type=gha,mode=max
83+
84+
# ---------------------------------------------------------------------------
85+
# 2) SSH 로 N100 접속 → docker compose pull && up -d
86+
# ---------------------------------------------------------------------------
87+
deploy:
88+
name: Deploy to ${{ needs.build.outputs.target }}
89+
needs: build
90+
runs-on: ubuntu-latest
91+
timeout-minutes: 10
92+
# prod 는 환경(environment) 보호로 수동 승인 가능
93+
environment:
94+
name: ${{ needs.build.outputs.target }}
95+
96+
steps:
97+
- name: Sanity check secrets
98+
run: |
99+
missing=""
100+
for v in N100_HOST N100_USER N100_SSH_KEY; do
101+
eval val="\$$v"
102+
if [ -z "$val" ]; then missing="$missing $v"; fi
103+
done
104+
if [ -n "$missing" ]; then
105+
echo "::error::Missing required secrets:$missing"
106+
echo "GitHub repo Settings → Secrets and variables → Actions 에서 등록하세요."
107+
exit 1
108+
fi
109+
env:
110+
N100_HOST: ${{ secrets.N100_HOST }}
111+
N100_USER: ${{ secrets.N100_USER }}
112+
N100_SSH_KEY: ${{ secrets.N100_SSH_KEY }}
113+
114+
- name: Pull & restart on N100
115+
uses: appleboy/ssh-action@v1.0.3
116+
with:
117+
host: ${{ secrets.N100_HOST }}
118+
username: ${{ secrets.N100_USER }}
119+
key: ${{ secrets.N100_SSH_KEY }}
120+
# 호스트의 deploy/ 디렉터리에서 pull → up.
121+
# IMAGE_TAG 가 .env 의 placeholder 를 덮어쓰며 docker compose 가 사용.
122+
script: |
123+
set -e
124+
cd ${MURUN_DEPLOY_DIR:-~/murun-peterabcd/deploy}
125+
export IMAGE_TAG="${{ needs.build.outputs.image_tag }}"
126+
docker compose -p murun-${{ needs.build.outputs.target }} pull
127+
docker compose -p murun-${{ needs.build.outputs.target }} up -d --remove-orphans
128+
docker image prune -f

deploy/README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,54 @@
22

33
자체 N100 호스트에서 Docker Compose 로 띄우는 절차. 사람(호스트)만 본다.
44

5-
## 1. 첫 배포 (한 번만)
5+
## 0. CI/CD (자동 배포) 사용 시 (권장)
6+
7+
GitHub Actions 가 dev push → staging, main push → prod 로 자동 배포한다.
8+
첫 셋업 한 번만 호스트에서 manual init 후, 그 다음부턴 코드만 push 하면 됨.
9+
10+
### 호스트 1회 init
11+
12+
```bash
13+
# 1) repo clone (한 번만)
14+
git clone https://github.com/boostcampwm-snu-2026-1/murun-peterabcd.git
15+
cd murun-peterabcd/deploy
16+
17+
# 2) staging / prod 각각 .env 준비
18+
cp .env.example .env.staging
19+
cp .env.example .env.prod
20+
# (DOMAIN, AUTH_*, GOOGLE_*, DATABASE_URL, NEXT_PUBLIC_APP_URL 채우기)
21+
22+
# 3) ghcr.io pull 인증 (한 번만, repo 가 public 이면 skip 가능)
23+
echo "<PAT_with_read:packages>" | docker login ghcr.io -u <github_user> --password-stdin
24+
25+
# 4) 첫 가동 (이미지 빌드는 GH Actions 가 대신, 여기선 pull 만)
26+
docker compose -p murun-staging --env-file .env.staging pull
27+
docker compose -p murun-staging --env-file .env.staging up -d
28+
```
29+
30+
### GitHub Actions secrets 등록 (Settings → Secrets and variables → Actions)
31+
32+
|||
33+
|----|------|
34+
| `N100_HOST` | N100 의 공인 IP 또는 DDNS 도메인 |
35+
| `N100_USER` | SSH 로그인 유저 |
36+
| `N100_SSH_KEY` | private SSH key 내용 전체 (BEGIN/END 포함). 공개키는 N100 의 `~/.ssh/authorized_keys` 에 등록 |
37+
38+
SSH key 생성 (호스트에서):
39+
```bash
40+
ssh-keygen -t ed25519 -C "gh-actions-murun-deploy" -f ~/.ssh/n100_gh_deploy
41+
cat ~/.ssh/n100_gh_deploy.pub >> ~/.ssh/authorized_keys
42+
# private key 를 GitHub secret 에 등록:
43+
gh secret set N100_SSH_KEY < ~/.ssh/n100_gh_deploy
44+
gh secret set N100_HOST --body "<ip-or-domain>"
45+
gh secret set N100_USER --body "<user>"
46+
```
47+
48+
### (옵션) prod environment 보호
49+
50+
main 으로의 자동 배포 전에 수동 승인이 필요하면 Settings → Environments → "prod" 생성 후 "Required reviewers" 본인 추가.
51+
52+
## 1. 수동 배포 (CI/CD 우회) — 첫 배포 또는 디버깅
653

754
```bash
855
# 호스트에 git clone

0 commit comments

Comments
 (0)