Skip to content

Latest commit

ย 

History

History
372 lines (274 loc) ยท 22 KB

DEPLOY.md

File metadata and controls

372 lines (274 loc) ยท 22 KB

HoneyRest ๋‹จ์ผ ์„œ๋ฒ„ ๋ฐฐํฌ ๊ฐ€์ด๋“œ (Oracle Cloud Always Free + Docker)

์„ธ ์ €์žฅ์†Œ(์‚ฌ์šฉ์ž API ยท ๊ด€๋ฆฌ์ž ์•ฑ ยท ์‚ฌ์šฉ์ž SPA)๋ฅผ VM ํ•œ ๋Œ€์— Docker Compose ๋กœ ์˜ฌ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ ์—†์ด ๊ณต์ธ IP ๊ธฐ๋ฐ˜ sslip.io ์ด๋ฆ„๊ณผ Let's Encrypt HTTPS ๋ฅผ ์”๋‹ˆ๋‹ค.

ํ•ญ๋ชฉ ๊ฐ’
์„œ๋ฒ„ Oracle Cloud Always Free ยท Ampere A1 (aarch64) ยท 1 OCPU / 6GB (์ตœ๋Œ€ 4 OCPU / 24GB ๊นŒ์ง€ ๋ฌด๋ฃŒ๋กœ ๋Š˜๋ฆด ์ˆ˜ ์žˆ์Œ)
OS Ubuntu 26.04 Minimal (aarch64) ยท ๋ฆฌ์ „: Tokyo
์‚ฌ์šฉ์ž ์‚ฌ์ดํŠธ https://<IP-ํ•˜์ดํ”ˆ>.sslip.io (์˜ˆ: https://152-70-12-34.sslip.io)
๊ด€๋ฆฌ์ž ์‚ฌ์ดํŠธ https://admin.<IP-ํ•˜์ดํ”ˆ>.sslip.io
๋ฐฐํฌ ํŒŒ์ผ ์ด ์ €์žฅ์†Œ์˜ deploy/

1. ๊ตฌ์„ฑ

flowchart LR
    B["๋ธŒ๋ผ์šฐ์ €"] -- "HTTPS 443" --> C

    subgraph VM["OCI VM (Docker Compose)"]
        C["caddy<br/>์ž๋™ HTTPS ยท SPA ์ •์  ํŒŒ์ผ(/srv)"]
        U["user-api :8080<br/>Spring Boot ยท Flyway"]
        H["host-admin :8081<br/>Spring Boot ยท Thymeleaf"]
        M[("mysql 8.0<br/>๋ณผ๋ฅจ mysql-data")]
        R[("redis 7<br/>์˜์†ํ™” ์—†์Œ")]
        V[("๋ณผ๋ฅจ uploads<br/>/app/uploads")]
        C -- "&lt;๋„๋ฉ”์ธ&gt;/api/*, /uploads/*,<br/>/oauth2/*, /login/oauth2/*" --> U
        C -- "admin.&lt;๋„๋ฉ”์ธ&gt;" --> H
        U --> M & R & V
        H --> M & R & V
    end
Loading
  • ๊ฐ™์€ Origin: SPA ์™€ API ๊ฐ€ https://<๋„๋ฉ”์ธ> ํ•˜๋‚˜์— ์žˆ์Šต๋‹ˆ๋‹ค. SPA ๋Š” VITE_BACKEND_URL ์„ ๋น„์šด ์ฑ„ ๋นŒ๋“œ๋˜์–ด /api/... ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ์š”์ฒญํ•˜๊ณ  Caddy ๊ฐ€ user-api ๋กœ ๋„˜๊น๋‹ˆ๋‹ค โ†’ CORSยทSameSite ๋ฌธ์ œ ์—†์Œ (๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์ฟ ํ‚ค Secure; HttpOnly; SameSite=Strict).
  • SPA ๋ผ์šฐํŒ…: ํŒŒ์ผ์ด ์—†๋Š” ๊ฒฝ๋กœ(/reserve, /user/mypage/...)๋Š” index.html ๋กœ ๋Œ€์ฒด(try_files). /login/google/callback ๊ฐ™์€ ์†Œ์…œ ์ฝœ๋ฐฑ๋„ SPA ๋ผ์šฐํŠธ์ž…๋‹ˆ๋‹ค.
  • ์Šคํ‚ค๋งˆ: user-api ๊ฐ€ ๊ธฐ๋™ํ•˜๋ฉฐ Flyway V1~์ตœ์‹ ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. host-admin ์€ ddl-auto=validate ๋ผ์„œ user-api ๊ฐ€ healthy ๊ฐ€ ๋œ ๋’ค์— ๋œน๋‹ˆ๋‹ค(depends_on: service_healthy).
  • ์—…๋กœ๋“œ ์ด๋ฏธ์ง€: ๋‘ ์•ฑ์ด ๊ฐ™์€ uploads ๋ณผ๋ฅจ์„ ์”๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž๊ฐ€ ์˜ฌ๋ฆฐ ์ˆ™์†Œ ์ด๋ฏธ์ง€(/uploads/...)๊ฐ€ ์‚ฌ์šฉ์ž ์‚ฌ์ดํŠธ์—๋„ ๊ฐ™์€ ๊ฒฝ๋กœ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.
  • ํฌํŠธ: ์™ธ๋ถ€์—๋Š” Caddy ์˜ 80/443 ๋งŒ ์—ด๋ฆฝ๋‹ˆ๋‹ค. MySQLยทRedisยท๋‘ ์•ฑ์€ Docker ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ์—๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์šด์˜ ํ”„๋กœํ•„: ๋‘ ๋ฐฑ์—”๋“œ ๋ชจ๋‘ application-prod.properties(๋น„๋ฐ€๊ฐ’ ์—†์Œ, ์ „๋ถ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜). ๋ฉ”์ผยท๊ฒฐ์ œยท์†Œ์…œ ๋กœ๊ทธ์ธยท๋‚ ์”จ ํ‚ค๊ฐ€ ์—†์–ด๋„ ๊ธฐ๋™์€ ์„ฑ๊ณตํ•˜๊ณ  ๊ทธ ๊ธฐ๋Šฅ๋งŒ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ์˜ˆ์‚ฐ(6GB ๊ธฐ์ค€, deploy/.env ์—์„œ ์กฐ์ •): MySQL 768MB ยท Redis 160MB ยท user-api 1GB ยท host-admin 900MB ยท Caddy 192MB โ‰ˆ 3GB. ์‹ค์ธก(์‹œ๋“œ ํ›„ ์œ ํœด): ์•ฝ 1.1GB. ๋‚˜๋จธ์ง€๋Š” OSยท์ด๋ฏธ์ง€ ๋นŒ๋“œ์šฉ์ด๋ฉฐ, setup.sh ๊ฐ€ 4GB ์Šค์™‘์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐํฌ ํŒŒ์ผ

ํŒŒ์ผ ์—ญํ• 
deploy/docker-compose.yml mysql ยท redis ยท user-api ยท host-admin ยท caddy, ํ—ฌ์Šค ์ฒดํฌยท๊ธฐ๋™ ์ˆœ์„œยท๋ฉ”๋ชจ๋ฆฌ ์ƒํ•œ
deploy/Caddyfile ์‚ฌ์šฉ์ž/๊ด€๋ฆฌ์ž ํ˜ธ์ŠคํŠธ ๋ผ์šฐํŒ…, ์ž๋™ HTTPS, SPA ๋Œ€์ฒด, ์บ์‹œ ํ—ค๋”
deploy/caddy/Dockerfile Node 20 ์œผ๋กœ SPA ๋นŒ๋“œ โ†’ caddy:2-alpine ์˜ /srv (VITE_E2E ์ฝ”๋“œ๊ฐ€ ์„ž์ด๋ฉด ๋นŒ๋“œ ์‹คํŒจ)
deploy/.env.example ๋ชจ๋“  ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค๋ช… ([ํ•„์ˆ˜]/[๋น„๋ฐ€]/[์„ ํƒ]/[๋นŒ๋“œ] ํ‘œ๊ธฐ)
deploy/setup.sh ์ƒˆ VM ์„ค์น˜: ํŒจํ‚ค์ง€ยท์Šค์™‘ยท๋ฐฉํ™”๋ฒฝยทDockerยทํด๋ก ยท.env ์ƒ์„ฑยท๋นŒ๋“œยท๊ธฐ๋™ยท์‹œ๋“œยทcron
deploy/seed.sh ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ (๊ธฐ์ดˆ ๋ฐ์ดํ„ฐ๋Š” ํ•œ ๋ฒˆ๋งŒ, ๋‚˜๋จธ์ง€๋Š” ๋ฐ˜๋ณต ์‹คํ–‰ ์•ˆ์ „)
deploy/update.sh ๋ฐฑ์—… โ†’ git pull โ†’ ๋นŒ๋“œ โ†’ ์žฌ๊ธฐ๋™ โ†’ ์ด๋ฏธ์ง€ ์ •๋ฆฌ
deploy/rollback.sh ์ง์ „ ์ด๋ฏธ์ง€(:previous)๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ
deploy/backup.sh MySQL ๋คํ”„ + uploads โ†’ /opt/honeyrest/backups (์ตœ๊ทผ 7๊ฐœ)
Dockerfile (๊ฐ ๋ฐฑ์—”๋“œ ์ €์žฅ์†Œ ๋ฃจํŠธ) JDK 17 ๋นŒ๋“œ โ†’ JRE 17 ์‹คํ–‰, ๋น„๋ฃจํŠธ ์‚ฌ์šฉ์ž, arm64 ์ง€์›

2. ์‚ฌ์ „ ์ค€๋น„ (OCI ์ฝ˜์†”)

  1. ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ: Compute โ†’ Instances โ†’ Create

    • Image: Canonical Ubuntu 26.04 Minimal (aarch64)
    • Shape: VM.Standard.A1.Flex, 1 OCPU / 6GB (๋‚˜์ค‘์— 4 / 24 ๊นŒ์ง€ ๋Š˜๋ฆด ์ˆ˜ ์žˆ์Œ)
    • ๋ถ€ํŠธ ๋ณผ๋ฅจ ๊ธฐ๋ณธ๊ฐ’(47GB)์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. SSH ๊ณต๊ฐœ ํ‚ค๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
  2. ๊ณต์ธ IP ๊ณ ์ •(๊ถŒ์žฅ): sslip.io ์ฃผ์†Œ๊ฐ€ IP ์—์„œ ๋งŒ๋“ค์–ด์ง€๋ฏ€๋กœ IP ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ฃผ์†Œ๋„ ๋ฐ”๋€๋‹ˆ๋‹ค. ์ธ์Šคํ„ด์Šค โ†’ Attached VNICs โ†’ IPv4 Addresses โ†’ ๊ณต์ธ IP ๋ฅผ Reserved public IP ๋กœ ๋ฐ”๊ฟ” ๋‘ก๋‹ˆ๋‹ค.

  3. ์ธ๊ทธ๋ ˆ์Šค ๊ทœ์น™ (์ง์ ‘ ํ•ด์•ผ ํ•จ): Networking โ†’ Virtual Cloud Networks โ†’ (VCN) โ†’ Subnet โ†’ Security List โ†’ Add Ingress Rules

    Source CIDR IP Protocol Destination Port
    0.0.0.0/0 TCP 80
    0.0.0.0/0 TCP 443

    (๋„คํŠธ์›Œํฌ ๋ณด์•ˆ ๊ทธ๋ฃน(NSG)์„ ์“ฐ๊ณ  ์žˆ๋‹ค๋ฉด NSG ์— ๊ฐ™์€ ๊ทœ์น™์„ ๋„ฃ์Šต๋‹ˆ๋‹ค.) 80 ์€ Let's Encrypt ์ธ์ฆ(HTTP-01)๊ณผ HTTPโ†’HTTPS ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ์— ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. OS ์•ˆ์˜ ๋ฐฉํ™”๋ฒฝ(iptables)์€ setup.sh ๊ฐ€ ์—ฝ๋‹ˆ๋‹ค.


3. ์„ค์น˜ (SSH ์ ‘์†๋ถ€ํ„ฐ ์ ‘์† ํ™•์ธ๊นŒ์ง€)

# ๋กœ์ปฌ PC ์—์„œ
ssh -i ~/.ssh/<ํ‚คํŒŒ์ผ> ubuntu@<๊ณต์ธIP>

# ์„œ๋ฒ„์—์„œ
sudo apt-get update && sudo apt-get install -y git
sudo mkdir -p /opt/honeyrest && sudo chown "$USER:$USER" /opt/honeyrest
git clone https://github.com/Seongwonp/honeyRest_user.git /opt/honeyrest/honeyRest_user

sudo /opt/honeyrest/honeyRest_user/deploy/setup.sh

setup.sh ๊ฐ€ ํ•˜๋Š” ์ผ (๋‹ค์‹œ ์‹คํ–‰ํ•ด๋„ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค):

  1. ๊ธฐ๋ณธ ํŒจํ‚ค์ง€(curlยทgitยทopensslยทcron), ์‹œ๊ฐ„๋Œ€ Asia/Seoul
  2. ์Šค์™‘ 4GB (SWAP_SIZE=2G ์ฒ˜๋Ÿผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ), vm.swappiness=10
  3. OS ๋ฐฉํ™”๋ฒฝ TCP 80/443 ํ—ˆ์šฉ โ€” Oracle ์ด๋ฏธ์ง€์˜ iptables ... REJECT ์•ž์— ๊ทœ์น™์„ ๋„ฃ๊ณ  /etc/iptables/rules.v4 ์—๋„ ๋ฐ˜์˜. ufw ๊ฐ€ ์ผœ์ ธ ์žˆ์œผ๋ฉด ufw ๋กœ ์ฒ˜๋ฆฌ
  4. Docker Engine + compose/buildx ํ”Œ๋Ÿฌ๊ทธ์ธ (๊ณต์‹ ์ €์žฅ์†Œ, arm64)
  5. /opt/honeyrest ์•„๋ž˜์— honeyRest_host(์„œ๋ธŒ๋ชจ๋“ˆ ํฌํ•จ)ยทhoneyrest_user_react ํด๋ก 
  6. deploy/.env ์ƒ์„ฑ โ€” DBยทJWTยท๋ฐ๋ชจ ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ openssl rand ๋กœ ๋งŒ๋“ค๊ณ , ๊ณต์ธ IP ๋กœ SITE_DOMAIN=<IP-ํ•˜์ดํ”ˆ>.sslip.io ์„ค์ • (๊ถŒํ•œ 600)
  7. ์ด๋ฏธ์ง€ ๋นŒ๋“œ(์„œ๋น„์Šค๋ณ„ ์ˆœ์ฐจ) โ†’ docker compose up -d โ†’ healthy ๋Œ€๊ธฐ ์ฒซ ๋นŒ๋“œ๋Š” 1 OCPU ์—์„œ 15~30๋ถ„ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (Gradle 2ํšŒ + npm).
  8. seed.sh โ€” ์ˆ™์†Œ 33๊ฐœยท๊ฐ์‹ค 101๊ฐœยท์ง€์—ญยทํƒœ๊ทธยท๋ฐ๋ชจ ๋ฆฌ๋ทฐ, ์ด๋ฏธ์ง€ ํ”Œ๋ ˆ์ด์Šคํ™€๋”
  9. cron /etc/cron.d/honeyrest โ€” 5๋ถ„๋งˆ๋‹ค ํ—ฌ์Šค ์ฒดํฌ, ๋งค์ผ 04:30 ๋ฐฑ์—…

๋๋‚˜๋ฉด ์ฃผ์†Œ์™€ ๊ด€๋ฆฌ์ž ๋ฐ๋ชจ ๊ณ„์ •์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์‚ฌ์ดํŠธ : https://152-70-12-34.sslip.io
๊ด€๋ฆฌ์ž ์‚ฌ์ดํŠธ : https://admin.152-70-12-34.sslip.io
  ์—…์ฒด ๊ด€๋ฆฌ์ž : contact@honeyrest.com / <DEMO_COMPANY_PASSWORD>
  ์ด๊ด€๋ฆฌ์ž   : admin@honeyrest.com / <DEMO_ADMIN_PASSWORD>

ํ™•์ธ:

cd /opt/honeyrest/honeyRest_user/deploy
sudo docker compose ps                         # 5๊ฐœ ์„œ๋น„์Šค Up, user-api/host-admin (healthy)
curl -I https://$(grep ^SITE_DOMAIN= .env | cut -d= -f2)/

์ฒซ HTTPS ์ ‘์† ์ง์ „์— Caddy ๊ฐ€ ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค(์ˆ˜์‹ญ ์ดˆ). ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ธ์ฆ์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๋ฉด 1๋ถ„์ฏค ๋’ค ๋‹ค์‹œ ์—ด์–ด ๋ณด๊ณ , ๊ณ„์†๋˜๋ฉด ๋ฌธ์ œ ํ•ด๊ฒฐ์˜ HTTPS ํ•ญ๋ชฉ์„ ๋ด…๋‹ˆ๋‹ค. setup.sh ์งํ›„ docker ๋ฅผ sudo ์—†์ด ์“ฐ๋ ค๋ฉด SSH ์— ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ฉ๋‹ˆ๋‹ค(docker ๊ทธ๋ฃน ๋ฐ˜์˜).

์„ ํƒ ์—ฐ๋™ ์ผœ๊ธฐ

deploy/.env ๋ฅผ ํŽธ์ง‘ํ•œ ๋’ค ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

cd /opt/honeyrest/honeyRest_user/deploy
nano .env
sudo ./update.sh --no-pull        # [๋นŒ๋“œ] ๊ฐ’(SITE_DOMAINยทTOSS_CLIENT_KEYยท์†Œ์…œ IDยท์ง€๋„ ํ‚ค)์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด SPA ๋ฅผ ๋‹ค์‹œ ๋นŒ๋“œ
๊ธฐ๋Šฅ ์„ค์ • ๋น„์›Œ ๋‘๋ฉด
๊ฒฐ์ œ TOSS_CLIENT_KEY(test_gck_...), TOSS_SECRET_KEY(test_gsk_...) โ€” ํ† ์ŠคํŽ˜์ด๋จผ์ธ  ๊ฐœ๋ฐœ์ž์„ผํ„ฐ์˜ ํ…Œ์ŠคํŠธ ํ‚ค ๊ฒฐ์ œ ์Šน์ธ๋งŒ ์‹คํŒจ
๋ฉ”์ผ MAIL_USERNAME(Gmail ์ฃผ์†Œ), MAIL_PASSWORD(Google ๊ณ„์ • โ†’ ๋ณด์•ˆ โ†’ 2๋‹จ๊ณ„ ์ธ์ฆ โ†’ ์•ฑ ๋น„๋ฐ€๋ฒˆํ˜ธ 16์ž๋ฆฌ) ๋ฉ”์ผ ๋ฐœ์†ก ์ƒ๋žต(no-op). ํšŒ์›๊ฐ€์ž… ์ธ์ฆ ๋ฉ”์ผ์ด ๊ฐ€์ง€ ์•Š์•„ ์ผ๋ฐ˜ ๊ฐ€์ž…์ž๊ฐ€ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์—†์Œ โ†’ ์•„๋ž˜ ๋ฌธ์ œ ํ•ด๊ฒฐ ์ฐธ๊ณ 
์นด์นด์˜ค ๋กœ๊ทธ์ธ KAKAO_CLIENT_ID(REST API ํ‚ค), KAKAO_CLIENT_SECRET ยท ์นด์นด์˜ค ๊ฐœ๋ฐœ์ž ์ฝ˜์†” Redirect URI: https://<๋„๋ฉ”์ธ>/login/kakao/callback ์†Œ์…œ ๋กœ๊ทธ์ธ๋งŒ ์‹คํŒจ
๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET ยท ์Šน์ธ๋œ ๋ฆฌ๋””๋ ‰์…˜ URI: https://<๋„๋ฉ”์ธ>/login/google/callback (Google ์€ sslip.io ๊ฐ™์€ ๊ณต์šฉ ์ ‘๋ฏธ์‚ฌ ๋„๋ฉ”์ธ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์–ด ์‹ค์ œ ๋„๋ฉ”์ธ ๊ถŒ์žฅ) ์†Œ์…œ ๋กœ๊ทธ์ธ๋งŒ ์‹คํŒจ
๋‚ ์”จ WEATHER_API_KEY (OpenWeather) ๋‚ ์”จ ์œ„์ ฏ๋งŒ ์‹คํŒจ
์ง€๋„ / ๊ณตํœด์ผ GOOGLE_MAPS_API_KEY, GOOGLE_MAP_ID, HOLIDAY_API_KEY ์ง€๋„ยท๊ณตํœด์ผ ํ‘œ์‹œ๋งŒ ์•ˆ ๋จ

๋ฐ๋ชจ ๊ณ„์ •์ด ํ•„์š” ์—†์œผ๋ฉด HOST_SPRING_PROFILES=prod ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค. ๋ฐ๋ชจ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๊ณ„์ •์ด ์ฒ˜์Œ ๋งŒ๋“ค์–ด์งˆ ๋•Œ๋งŒ ์“ฐ์ž…๋‹ˆ๋‹ค(์ดํ›„ .env ๋ฅผ ๋ฐ”๊ฟ”๋„ ๊ธฐ์กด ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ์ง€).


4. ๋กœ๊ทธ์™€ ์ƒํƒœ

๋ชจ๋“  ๋ช…๋ น์€ /opt/honeyrest/honeyRest_user/deploy ์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค (docker ๊ทธ๋ฃน์ด ์•„๋‹ˆ๋ฉด ์•ž์— sudo).

docker compose ps                              # ์ƒํƒœยทํ—ฌ์Šค
docker compose logs -f --tail=200 user-api     # ์‚ฌ์šฉ์ž API (Flyway, ์˜ˆ์™ธ)
docker compose logs -f --tail=200 host-admin   # ๊ด€๋ฆฌ์ž ์•ฑ
docker compose logs -f --tail=100 caddy        # ์ ‘์† ๋กœ๊ทธยท์ธ์ฆ์„œ ๋ฐœ๊ธ‰
docker compose logs --since 1h mysql
docker stats --no-stream                       # ์ปจํ…Œ์ด๋„ˆ๋ณ„ ๋ฉ”๋ชจ๋ฆฌ
free -h && swapon --show                       # ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌยท์Šค์™‘
journalctl -t honeyrest-health --since today   # cron ํ—ฌ์Šค ์ฒดํฌ ์‹คํŒจ ๊ธฐ๋ก
tail -n 50 /var/log/honeyrest-backup.log       # ๋ฐฑ์—… ๊ธฐ๋ก

์ปจํ…Œ์ด๋„ˆ ๋กœ๊ทธ๋Š” ์„œ๋น„์Šค๋งˆ๋‹ค 10MB ร— 3๊ฐœ๋กœ ํšŒ์ „ํ•ฉ๋‹ˆ๋‹ค.

์žฌ์‹œ์ž‘: docker compose restart user-api ยท ์ „์ฒด ์ค‘์ง€/์‹œ์ž‘: docker compose stop / docker compose up -d

docker compose down -v ๋Š” ์ ˆ๋Œ€ ์“ฐ์ง€ ๋งˆ์„ธ์š”. -v ๋Š” DB(mysql-data)ยท์—…๋กœ๋“œยท์ธ์ฆ์„œ(caddy-data) ๋ณผ๋ฅจ๊นŒ์ง€ ์ง€์›๋‹ˆ๋‹ค. ์ธ์ฆ์„œ๋ฅผ ์ง€์šฐ๊ณ  ์žฌ๋ฐœ๊ธ‰์„ ๋ฐ˜๋ณตํ•˜๋ฉด Let's Encrypt ๋ฐœ๊ธ‰ ํ•œ๋„์— ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.

DB ์ฝ˜์†”: docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"'


5. ์—…๋ฐ์ดํŠธ

sudo /opt/honeyrest/honeyRest_user/deploy/update.sh
  1. backup.sh ๋กœ DBยท์—…๋กœ๋“œ ๋ฐฑ์—… (์‹คํŒจํ•˜๋ฉด ์ค‘๋‹จ)
  2. ์„ธ ์ €์žฅ์†Œ git pull --ff-only, ๊ด€๋ฆฌ์ž ์ €์žฅ์†Œ ์„œ๋ธŒ๋ชจ๋“ˆ์„ ๊ณ ์ • ์ปค๋ฐ‹์œผ๋กœ ๋งž์ถค(submodule update --init --recursive)
  3. user-api โ†’ host-admin โ†’ caddy ์ˆœ์„œ๋กœ ํ•˜๋‚˜์”ฉ ๋นŒ๋“œ. BuildKit ์บ์‹œ ๋•๋ถ„์— ๋ฐ”๋€Œ์ง€ ์•Š์€ ์„œ๋น„์Šค๋Š” ๋ช‡ ์ดˆ ๋งŒ์— ๋๋‚˜๊ณ  ์ด๋ฏธ์ง€๋„ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค
  4. ์ด๋ฏธ์ง€๊ฐ€ ๋ฐ”๋€ ์„œ๋น„์Šค๋Š” ์ง์ „ ์ด๋ฏธ์ง€๋ฅผ :previous ๋กœ ๋‚จ๊น€ โ†’ docker compose up -d ๊ฐ€ ๋ฐ”๋€ ์ปจํ…Œ์ด๋„ˆ๋งŒ ๋‹ค์‹œ ๋งŒ๋“ฆ โ†’ healthy ๋Œ€๊ธฐ
  5. Caddyfile ์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด caddy ์ปจํ…Œ์ด๋„ˆ๋งŒ ๋‹ค์‹œ ๋งŒ๋“ฆ (์ธ์ฆ์„œ ์œ ์ง€)
  6. ์ด๋ฆ„ ์—†๋Š” ์ด๋ฏธ์ง€ ์ •๋ฆฌ(docker image prune -f). Gradle/npm ๋นŒ๋“œ ์บ์‹œ๋Š” ๋‚จ๊น๋‹ˆ๋‹ค

์˜ต์…˜: --no-pull(.env ๋งŒ ๋ฐ”๊ฟจ์„ ๋•Œ) ยท --no-backup

์‚ฌ์šฉ์ž API ์— ์ƒˆ Flyway ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์žˆ์œผ๋ฉด ์žฌ๊ธฐ๋™ ๋•Œ ์ž๋™ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์€ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์œผ๋ฏ€๋กœ 1๋‹จ๊ณ„ ๋ฐฑ์—…์„ ์ง€์šฐ์ง€ ๋งˆ์„ธ์š”.

์„œ๋ฒ„๋ณ„ ์กฐ์ •(์˜ˆ: ๋ฉ”๋ชจ๋ฆฌ ์ƒํ•œ์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ)์€ ์ถ”์ ๋˜์ง€ ์•Š๋Š” deploy/docker-compose.override.yml ์— ๋‘๋ฉด ์Šคํฌ๋ฆฝํŠธ์™€ docker compose ๋ช…๋ น ๋ชจ๋‘์— ํ•จ๊ป˜ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.


6. ๋กค๋ฐฑ

์ด๋ฏธ์ง€ ๋กค๋ฐฑ (๊ฐ€์žฅ ๋น ๋ฆ„, 1๋ถ„ ์ด๋‚ด)

sudo /opt/honeyrest/honeyRest_user/deploy/rollback.sh user-api          # ๋˜๋Š” host-admin, caddy (์—ฌ๋Ÿฌ ๊ฐœ ๊ฐ€๋Šฅ)

:previous ์ด๋ฏธ์ง€๋ฅผ :latest ๋กœ ๋˜๋Œ๋ฆฌ๊ณ  ๊ทธ ์ปจํ…Œ์ด๋„ˆ๋งŒ ๋‹ค์‹œ ๋งŒ๋“ญ๋‹ˆ๋‹ค(๋ฌธ์ œ ์ด๋ฏธ์ง€๋Š” :rolled-back ์œผ๋กœ ๋ณด๊ด€). ์ €์žฅ์†Œ๋Š” ์ƒˆ ์ปค๋ฐ‹์— ๊ทธ๋Œ€๋กœ ์žˆ์œผ๋ฏ€๋กœ, ์›์ธ์„ ๊ณ ์น˜๊ธฐ ์ „์—๋Š” update.sh ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๋‹ค์‹œ ์ƒˆ ์ฝ”๋“œ๋กœ ๋นŒ๋“œ๋จ).

์ฝ”๋“œ ๋กค๋ฐฑ (ํŠน์ • ์ปค๋ฐ‹์œผ๋กœ)

cd /opt/honeyrest/honeyRest_user && git log --oneline -5
git checkout <์ •์ƒ ์ปค๋ฐ‹>                       # ํ™•์ธ์ด ๋๋‚˜๋ฉด git checkout main ์œผ๋กœ ๋ณต๊ท€
sudo ./deploy/update.sh --no-pull

DB ๊นŒ์ง€ ๋˜๋Œ๋ ค์•ผ ํ•  ๋•Œ

์ƒˆ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์ด๋ฏธ ์ ์šฉ๋œ ๋’ค ์˜ˆ์ „ ์ด๋ฏธ์ง€๋กœ ๋Œ์•„๊ฐ€๋ฉด ddl-auto=validate ์— ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋•Œ๋Š” ์—…๋ฐ์ดํŠธ ์ง์ „ ๋ฐฑ์—…์œผ๋กœ DB ๋ฅผ ๋ณต์›ํ•œ ๋’ค ์ด๋ฏธ์ง€ ๋กค๋ฐฑ์„ ํ•ฉ๋‹ˆ๋‹ค โ†’ ๋ฐฑ์—…๊ณผ ๋ณต์›.


7. ๋ฐฑ์—…๊ณผ ๋ณต์›

setup.sh ๊ฐ€ ๋งค์ผ 04:30(์„œ๋ฒ„ ์‹œ๊ฐ„ Asia/Seoul) ๋ฐฑ์—…์„ cron ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ๋“ฑ๋กํ•˜๋ ค๋ฉด:

# /etc/cron.d/honeyrest
30 4 * * * root /opt/honeyrest/honeyRest_user/deploy/backup.sh >> /var/log/honeyrest-backup.log 2>&1
  • ๊ฒฐ๊ณผ: /opt/honeyrest/backups/honeyrest_db-YYYYMMDD-HHMMSS.sql.gz, uploads-YYYYMMDD-HHMMSS.tar.gz (๊ฐ๊ฐ ์ตœ๊ทผ 7๊ฐœ, KEEP=14 ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)
  • mysqldump --single-transaction ์ด๋ผ ์„œ๋น„์Šค ์ค‘์—๋„ ์ž ๊ธˆ ์—†์ด ์ผ๊ด€๋œ ์Šค๋ƒ…์ƒท์ž…๋‹ˆ๋‹ค
  • ํšŒ์› ์ •๋ณด๊ฐ€ ๋“ค์–ด ์žˆ์œผ๋ฏ€๋กœ ๊ถŒํ•œ 600. ์„œ๋ฒ„ ๋ฐ–์—๋„ ๋ณด๊ด€ํ•˜์„ธ์š”: scp ubuntu@<IP>:/opt/honeyrest/backups/*.gz ./

๋ณต์›:

cd /opt/honeyrest/honeyRest_user/deploy
sudo docker compose stop user-api host-admin
gunzip -c /opt/honeyrest/backups/honeyrest_db-<์‹œ๊ฐ>.sql.gz | \
  sudo docker compose exec -T mysql sh -c 'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql -uroot "$MYSQL_DATABASE"'
# ์—…๋กœ๋“œ ํŒŒ์ผ๊นŒ์ง€ ๋˜๋Œ๋ฆด ๋•Œ
sudo docker compose start user-api
sudo docker compose exec -T user-api tar -C /app -xzf - < /opt/honeyrest/backups/uploads-<์‹œ๊ฐ>.tar.gz
sudo docker compose up -d

8. ๋ฐ์ดํ„ฐ ์‹œ๋“œ

setup.sh ๊ฐ€ ์ฒ˜์Œ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ์‹คํ–‰:

sudo /opt/honeyrest/honeyRest_user/deploy/seed.sh                 # ์ „์ฒด (๊ธฐ์ดˆ ๋ฐ์ดํ„ฐ๋Š” ํ•œ ๋ฒˆ๋งŒ)
sudo /opt/honeyrest/honeyRest_user/deploy/seed.sh --demo-refresh  # ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ๋งŒ
์ˆœ์„œ ํŒŒ์ผ ๋ฐ˜๋ณต ์‹คํ–‰
1 ๊ด€๋ฆฌ์ž ์ €์žฅ์†Œ db/seed/insert.sql โ†’ insert_pk_1-20.sql โ†’ insert_pk_21-30.sql โ†’ insert_pk_31-33.sql (FK ์ˆœ์„œ: ์ง€์—ญยท์นดํ…Œ๊ณ ๋ฆฌยทํƒœ๊ทธยท์—…์ฒด โ†’ ์ˆ™์†Œยท๊ฐ์‹ค) ํ•œ ๋ฒˆ๋งŒ. ๋„ค ํŒŒ์ผ๊ณผ ์™„๋ฃŒ ํ‘œ์‹œ(deploy_seed_history)๋ฅผ ํ•œ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ ์žฌ. ํ‘œ์‹œ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ์ˆ™์†Œ๊ฐ€ ์ด๋ฏธ ์žˆ์œผ๋ฉด ๊ฑด๋„ˆ๋œ€
2 scripts/seed-demo-data.sql โ€” ๋ฐ๋ชจ ๋ฆฌ๋ทฐ 12๊ฑด, ์˜ค๋Š˜๋ถ€ํ„ฐ 90์ผ ๊ฐ€๊ฒฉ ๋‹ฌ๋ ฅ, min_priceยทrating ์žฌ๊ณ„์‚ฐ ์•ˆ์ „
3 scripts/seed-local-images.sql โ€” ๋งŒ๋ฃŒ๋œ Firebase ์ด๋ฏธ์ง€ URL โ†’ /uploads/placeholder/*.svg ์•ˆ์ „
4 uploads/placeholder/*.svg โ†’ uploads ๋ณผ๋ฅจ ์•ˆ์ „
5 Redis ๊ฒ€์ƒ‰ ์บ์‹œ ์„ธ๋Œ€ ๋ฒˆํ˜ธ ์ฆ๊ฐ€ ยท ๋ฐฐ๋„ˆ ์บ์‹œ ์‚ญ์ œ ์•ˆ์ „
6 scripts/audit-data-quality.sql ๊ฒฐ๊ณผ ์ถœ๋ ฅ (*_mismatch ๊ฐ€ 0 ์ด๋ฉด ์ •์ƒ) ์ฝ๊ธฐ ์ „์šฉ

๊ฐ€๊ฒฉ ๋‹ฌ๋ ฅ์€ ์‹œ๋“œ ์‹œ์ ๋ถ€ํ„ฐ 90์ผ์น˜์ž…๋‹ˆ๋‹ค(๋‹ฌ๋ ฅ์ด ์—†๋Š” ๋‚ ์งœ๋Š” ๊ฐ์‹ค ๊ธฐ๋ณธ๊ฐ€๋กœ ๊ณ„์‚ฐ๋˜๋ฏ€๋กœ ์˜ˆ์•ฝ์€ ๊ณ„์† ๋ฉ๋‹ˆ๋‹ค). ๋ฐ๋ชจ ๋‹ฌ๋ ฅ์„ ๋Š˜๋ฆฌ๋ ค๋ฉด ๋ช‡ ๋‹ฌ์— ํ•œ ๋ฒˆ seed.sh --demo-refresh ๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ cron ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.


9. ๋ฌธ์ œ ํ•ด๊ฒฐ

HTTPS ์ธ์ฆ์„œ๊ฐ€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ

docker compose logs caddy | grep -iE 'error|challenge|acme'

  • 80/443 ์ด ๋ง‰ํž˜: OCI ๋ณด์•ˆ ๋ชฉ๋ก ์ธ๊ทธ๋ ˆ์Šค(2์žฅ)์™€ OS ๋ฐฉํ™”๋ฒฝ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    sudo iptables -L INPUT -n --line-numbers | head -20   # 80/443 ACCEPT ๊ฐ€ REJECT ๋ณด๋‹ค ์œ„์— ์žˆ์–ด์•ผ ํ•จ
    sudo ufw status                                         # ufw ๋ฅผ ์“ฐ๋Š” ๊ฒฝ์šฐ
    ์™ธ๋ถ€์—์„œ ํ™•์ธ: ๋กœ์ปฌ PC ์—์„œ curl -I http://<IP-ํ•˜์ดํ”ˆ>.sslip.io โ†’ 308 ์ด๋ฉด 80 ์€ ์—ด๋ ค ์žˆ์Šต๋‹ˆ๋‹ค.
  • Let's Encrypt ๋ฐœ๊ธ‰ ํ•œ๋„: ๊ฐ™์€ ์ด๋ฆ„์˜ ์ธ์ฆ์„œ๋Š” ์ฃผ 5ํšŒ, ๋“ฑ๋ก ๋„๋ฉ”์ธ๋‹น ์ฃผ 50๊ฐœ, ์‹คํŒจํ•œ ๊ฒ€์ฆ์€ ์‹œ๊ฐ„๋‹น 5ํšŒ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. sslip.io ๋Š” ๋งŽ์€ ์‚ฌ๋žŒ์ด ํ•จ๊ป˜ ์“ฐ๋Š” ๋„๋ฉ”์ธ์ด๋ผ ํ•œ๋„์— ๊ฑธ๋ฆด ๊ฐ€๋Šฅ์„ฑ์ด ์ƒ๋Œ€์ ์œผ๋กœ ๋†’์Šต๋‹ˆ๋‹ค.
    • ์ธ์ฆ์„œ๋Š” caddy-data ๋ณผ๋ฅจ์— ์žˆ์œผ๋ฏ€๋กœ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค์‹œ ๋งŒ๋“ค์–ด๋„ ์žฌ๋ฐœ๊ธ‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณผ๋ฅจ์„ ์ง€์šฐ์ง€ ๋งˆ์„ธ์š”.
    • Caddy ๋Š” ์‹คํŒจํ•˜๋ฉด ๊ฐ„๊ฒฉ์„ ๋Š˜๋ ค ๊ฐ€๋ฉฐ ์ž๋™ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ํ•œ๋„์— ๊ฑธ๋ ธ๋‹ค๋ฉด ๊ธฐ๋‹ค๋ฆฌ๊ฑฐ๋‚˜ ์‹ค์ œ ๋„๋ฉ”์ธ์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
    • ์„ค์ •์„ ์‹คํ—˜ํ•  ๋•Œ๋Š” Caddyfile ์ „์—ญ ๋ธ”๋ก์— acme_ca https://acme-staging-v02.api.letsencrypt.org/directory ๋ฅผ ์ž ์‹œ ๋„ฃ์–ด ์Šคํ…Œ์ด์ง• CA ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค(๋ธŒ๋ผ์šฐ์ € ๊ฒฝ๊ณ ๋Š” ์ •์ƒ). ๋๋‚˜๋ฉด ์ง€์šฐ๊ณ  docker compose up -d --force-recreate caddy.
  • IP ๊ฐ€ ๋ฐ”๋€œ: .env ์˜ SITE_DOMAIN ์„ ์ƒˆ IP ๋กœ ๊ณ ์นœ ๋’ค sudo ./update.sh --no-pull (SPA ์˜ ๊ด€๋ฆฌ์ž ๋งํฌยทOAuth ์ฃผ์†Œ๊ฐ€ ๋นŒ๋“œ ์‹œ์ ์— ๋“ค์–ด๊ฐ€๋ฏ€๋กœ ๋‹ค์‹œ ๋นŒ๋“œ ํ•„์š”).

๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ (OOM)

  • ์ฆ์ƒ: ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ฐ˜๋ณต ์žฌ์‹œ์ž‘(docker compose ps ์˜ Restarting), docker inspect <์ปจํ…Œ์ด๋„ˆ> --format '{{.State.OOMKilled}}' ๊ฐ€ true, dmesg | grep -i oom.
  • JVM ์€ -XX:+ExitOnOutOfMemoryError ๋กœ ๋ฐ”๋กœ ์ข…๋ฃŒ๋˜๊ณ  restart: unless-stopped ๋กœ ๋‹ค์‹œ ๋œน๋‹ˆ๋‹ค.
  • ์กฐ์น˜: .env ์˜ USER_API_MEM_LIMITยทHOST_ADMIN_MEM_LIMIT ์„ ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜(ํž™์€ ์ƒํ•œ์˜ 70%), ๋นŒ๋“œ ์ค‘์ด๋ผ๋ฉด ์Šค์™‘์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค(swapon --show).
  • ๋นŒ๋“œ๊ฐ€ ๋А๋ฆฌ๊ฑฐ๋‚˜ ๋ฉˆ์ถ˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉด ๋Œ€๊ฐœ ์Šค์™‘์„ ์“ฐ๋Š” ์ค‘์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ฅผ ์ž ์‹œ ๋ฉˆ์ถ”๊ณ  ๋นŒ๋“œํ•˜๋ฉด ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค: docker compose stop host-admin && sudo ./update.sh.
  • ์ธ์Šคํ„ด์Šค๋ฅผ 4 OCPU / 24GB ๋กœ ๋Š˜๋ ธ๋‹ค๋ฉด: MYSQL_INNODB_BUFFER_POOL_SIZE=1G, MYSQL_MEM_LIMIT=2g, USER_API_MEM_LIMIT=2g, HOST_ADMIN_MEM_LIMIT=1536m ์ •๋„๋กœ ์˜ฌ๋ฆฌ๊ณ  docker compose up -d.

user-api ๊ฐ€ healthy ๊ฐ€ ๋˜์ง€ ์•Š์Œ

  • docker compose logs user-api | grep -iE 'flyway|error|caused' โ€” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํŒจ, DB ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ถˆ์ผ์น˜ ๋“ฑ.
  • .env ์˜ MYSQL_PASSWORD ๋ฅผ MySQL ๋ณผ๋ฅจ์ด ๋งŒ๋“ค์–ด์ง„ ๋’ค์— ๋ฐ”๊ฟจ๋‹ค๋ฉด DB ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค. ์›๋ž˜ ๊ฐ’์œผ๋กœ ๋Œ๋ฆฌ๊ฑฐ๋‚˜ MySQL ์—์„œ ALTER USER ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฒซ ๊ธฐ๋™์€ 1 OCPU ์—์„œ 1~3๋ถ„ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค(ํ—ฌ์Šค ์ฒดํฌ ์œ ์˜ˆ 240์ดˆ).

host-admin ์ด ๊ธฐ๋™ํ•˜์ง€ ์•Š์Œ (Schema-validation ์˜ค๋ฅ˜)

๊ด€๋ฆฌ์ž ์•ฑ์€ ์Šคํ‚ค๋งˆ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๊ฒ€์ฆ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ์ €์žฅ์†Œ์˜ ์„œ๋ธŒ๋ชจ๋“ˆ(๊ณต์œ  ๋„๋ฉ”์ธ ๋ชจ๋“ˆ)์ด ์‚ฌ์šฉ์ž API ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋ณด๋‹ค ์•ž์„  ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ์œผ๋ฉด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž API ๋ฅผ ๋จผ์ € ์ตœ์‹ ์œผ๋กœ ์˜ฌ๋ฆฌ๊ณ , ๊ด€๋ฆฌ์ž ์ €์žฅ์†Œ์˜ ์„œ๋ธŒ๋ชจ๋“ˆ ๊ณ ์ • ์ปค๋ฐ‹์ด ๋งž๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค(git -C /opt/honeyrest/honeyRest_host submodule status).

๋ฉ”์ผ ์—†์ด ๊ฐ€์ž…ํ•œ ๊ณ„์ •์ด ๋กœ๊ทธ์ธ๋˜์ง€ ์•Š์Œ

๋ฉ”์ผ ์„ค์ •(MAIL_*)์ด ๋น„์–ด ์žˆ์œผ๋ฉด ๊ฐ€์ž… ์ธ์ฆ ๋ฉ”์ผ์ด ๋ฐœ์†ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๋กœ๊ทธ์— [mail-disabled]). ๋ฐ๋ชจ ๋ชฉ์ ์œผ๋กœ ์ง์ ‘ ์ธ์ฆ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด:

sudo docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" \
  -e "UPDATE user SET is_verified = 1 WHERE email = '\''๊ฐ€์ž…ํ•œ@์ด๋ฉ”์ผ'\''"'

๊ณต๊ฐœ ๋ฐ๋ชจ๋ผ๋ฉด Gmail ์•ฑ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉํ™”๋ฒฝ ๊ทœ์น™์ด ์žฌ๋ถ€ํŒ… ํ›„ ์‚ฌ๋ผ์ง

setup.sh ๋Š” ์‹คํ–‰ ์ค‘์ธ ๊ทœ์น™๊ณผ /etc/iptables/rules.v4 ๋ฅผ ํ•จ๊ป˜ ๊ณ ์นฉ๋‹ˆ๋‹ค. netfilter-persistent save ๋Š” Docker ๊ฐ€ ๋งŒ๋“  ๊ทœ์น™๊นŒ์ง€ ์ €์žฅํ•˜๋ฏ€๋กœ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ™•์ธ: grep -E 'dport (80|443)' /etc/iptables/rules.v4

๋””์Šคํฌ๊ฐ€ ๋ถ€์กฑํ•จ

df -h /
docker system df
docker builder prune -f --filter until=168h    # 1์ฃผ ๋„˜์€ ๋นŒ๋“œ ์บ์‹œ (๋‹ค์Œ ๋นŒ๋“œ๊ฐ€ ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Œ)
docker image rm honeyrest/user-api:rolled-back # ๋กค๋ฐฑ ํ›„ ๋‚จ์€ ์ด๋ฏธ์ง€

10. Always Free ์œ ํœด ์ธ์Šคํ„ด์Šค ํšŒ์ˆ˜

Oracle ์€ Always Free ์ธ์Šคํ„ด์Šค๊ฐ€ 7์ผ ๋™์•ˆ ๋‹ค์Œ์„ ๋ชจ๋‘ ๋งŒ์กฑํ•˜๋ฉด ์œ ํœด๋กœ ๋ณด๊ณ  ํšŒ์ˆ˜(์ค‘์ง€)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: CPU ์‚ฌ์šฉ๋ฅ  95๋ฒˆ์งธ ๋ฐฑ๋ถ„์œ„ 20% ๋ฏธ๋งŒ ยท ๋„คํŠธ์›Œํฌ ์‚ฌ์šฉ๋ฅ  20% ๋ฏธ๋งŒ ยท ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋ฅ  20% ๋ฏธ๋งŒ(A1 ์…ฐ์ดํ”„).

  • ์ด ์Šคํƒ์€ ์œ ํœด ์ƒํƒœ์—์„œ๋„ ์•ฝ 1.1GB(6GB ์˜ ์•ฝ 18%)๋ฅผ ์“ฐ๊ณ  JVM ํž™์ด ์ปค์ง€๋ฉด์„œ ๋” ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ์ค€์— ๊ฑธ๋ฆด ๊ฐ€๋Šฅ์„ฑ์€ ๋‚ฎ์ง€๋งŒ ์žฅ๋‹ดํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.
  • setup.sh ์˜ cron ํ—ฌ์Šค ์ฒดํฌ(5๋ถ„๋งˆ๋‹ค ๊ณต๊ฐœ ์ฃผ์†Œ๋กœ APIยท๊ด€๋ฆฌ์ž ์•ฑ ํ˜ธ์ถœ)๋Š” ์žฅ์• ๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ์„œ๋น„์Šค๋ฅผ ๊นจ์›Œ ๋‘๋Š” ๊ฐ€๋ฒผ์šด ํ•‘์ž…๋‹ˆ๋‹ค. CPU ๋ฅผ ์ผ๋ถ€๋Ÿฌ ์†Œ๋ชจํ•˜์ง€๋Š” ์•Š์œผ๋ฏ€๋กœ ํšŒ์ˆ˜ ๊ธฐ์ค€ ์ž์ฒด๋ฅผ ๋ง‰์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
  • ํ™•์‹คํžˆ ํ”ผํ•˜๋ ค๋ฉด ๊ณ„์ •์„ Pay As You Go ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•ฉ๋‹ˆ๋‹ค. Always Free ํ•œ๋„ ์•ˆ์˜ ๋ฆฌ์†Œ์Šค๋Š” ๊ณ„์† ๋ฌด๋ฃŒ์ด๋ฉฐ ์œ ํœด ํšŒ์ˆ˜ ๋Œ€์ƒ์—์„œ ๋น ์ง‘๋‹ˆ๋‹ค.
  • ํšŒ์ˆ˜๋˜์–ด ์ค‘์ง€๋˜์—ˆ๋‹ค๋ฉด ์ฝ˜์†”์—์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ๋Š” restart: unless-stopped ๋ผ Docker ์™€ ํ•จ๊ป˜ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ๋œน๋‹ˆ๋‹ค(์˜ˆ์•ฝ ๊ณต์ธ IP ๋ฅผ ์“ฐ๋ฉด ์ฃผ์†Œ๋„ ๊ทธ๋Œ€๋กœ).

11. ๋„๋ฉ”์ธ์„ ์‚ฐ ๋’ค

  1. DNS A ๋ ˆ์ฝ”๋“œ 2๊ฐœ: @ โ†’ ์„œ๋ฒ„ IP, admin โ†’ ์„œ๋ฒ„ IP
  2. .env: SITE_DOMAIN=example.com
  3. sudo ./update.sh --no-pull โ€” SPA ๋‹ค์‹œ ๋นŒ๋“œ, Caddy ๊ฐ€ ์ƒˆ ์ด๋ฆ„์œผ๋กœ ์ธ์ฆ์„œ ๋ฐœ๊ธ‰
  4. ์นด์นด์˜ค/๊ตฌ๊ธ€ ์ฝ˜์†”์˜ Redirect URI ๋ฅผ ์ƒˆ ์ฃผ์†Œ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

12. ๋ณด์•ˆ ๋ฉ”๋ชจ

  • deploy/.env ์— ๋ชจ๋“  ๋น„๋ฐ€๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค(๊ถŒํ•œ 600, git ์ œ์™ธ). ์„œ๋ฒ„ ๋ฐ–์— ์•ˆ์ „ํ•˜๊ฒŒ ๋”ฐ๋กœ ๋ณด๊ด€ํ•˜์„ธ์š”.
  • ๋น„๋ฐ€๊ฐ’ ํŒŒ์ผ(application_security.properties)ยทFirebase ํ‚ค๋Š” .dockerignore ๋กœ ์ด๋ฏธ์ง€์— ๋“ค์–ด๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ํ† ์Šค๋Š” ํ…Œ์ŠคํŠธ ํ‚ค๋งŒ ์“ฐ์„ธ์š”. ๋ผ์ด๋ธŒ ํ‚ค๋ฅผ ๋„ฃ์œผ๋ฉด ์‹ค์ œ ๊ฒฐ์ œ๊ฐ€ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ์ž ์‚ฌ์ดํŠธ๋Š” ๊ณต๊ฐœ ์ฃผ์†Œ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ๋ชจ ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” setup.sh ๊ฐ€ ๋ฌด์ž‘์œ„๋กœ ๋งŒ๋“  ๊ฐ’์„ ์“ฐ๊ณ , ์™ธ๋ถ€์— ๊ณต๊ฐœํ•  ๋•Œ๋Š” ์—…์ฒด ๊ด€๋ฆฌ์ž ๊ณ„์ •๋งŒ ์•Œ๋ ค ์ฃผ์„ธ์š”.
  • SSH ๋Š” ํ‚ค ์ธ์ฆ๋งŒ ์“ฐ๊ณ , MySQL/Redis ํฌํŠธ๋Š” ์™ธ๋ถ€์— ์—ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค(compose ์— ports ์—†์Œ).