์ต์ข ์ ๋ฐ์ดํธ: 2025-12-29 ํ์ฌ ์ํ: โ ํ๋ก๋์ ๋ฐฐํฌ ์๋ฃ (Vultr ์์ธ ์๋ฒ) GitHub: https://github.com/mr-joo/naver-blog-bot
- ํ๋ก์ ํธ ๊ฐ์
- ์์คํ ์ํคํ ์ฒ
- ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
- ์ค์น ๋ฐ ์คํ
- ์๋ฒ ๋ฐฐํฌ (Vultr ์์ธ)
- CI/CD ์๋ ๋ฐฐํฌ
- ์ด์ ๊ฐ์ด๋
- ํ๊ฒฝ ๋ณ์ ์ค์
- ์๋ ค์ง ์ด์ ๋ฐ ํด๊ฒฐ์ฑ
- ํฅํ ๊ฐ์ ์ฌํญ
AI ๊ธฐ๋ฐ ๋ค์ด๋ฒ ๋ธ๋ก๊ทธ ์๋ ํฌ์คํ ์์คํ
- ์ค์๊ฐ ๋ด์ค/ํธ๋ ๋ ์์ง โ AI ๊ธ ์์ฑ โ ์ด๋ฏธ์ง ์์ฑ โ ์๋ ๋ฐํ
- 24์๊ฐ ๋ฌด์ธ ์ด์ (1-2์๊ฐ ๊ฐ๊ฒฉ, ์ผ์ผ ์ต๋ 12๊ฐ)
- ๋ณธ๋ฌธ ์ค๊ฐ์ ์ด๋ฏธ์ง 3-4๊ฐ ์๋ ์ฝ์
- ์ค๋งํธ๊ฐ๋ฏธ ์ฝ์ธ๋ด: ์ํธํํ/ํฌ์ ์ ๋ฌธ ๋ธ๋ก๊ฑฐ
- ์นด์นด์คํก ์คํ์ฑํ ์ ๋ ๋ง์ผํ
| ๊ตฌ์ฑ์์ | ๊ธฐ์ |
|---|---|
| ์ธ์ด | Python 3.11+ |
| ๋ธ๋ผ์ฐ์ ์๋ํ | Playwright (Chromium) |
| ์ฝํ ์ธ ์์ฑ | Claude API (Haiku/Sonnet) |
| ๋ฆฌ์์น | Perplexity API |
| ์ด๋ฏธ์ง ์์ฑ | Google Gemini Imagen 3 |
| ์ค์ผ์ค๋ง | APScheduler |
| ์๋ฆผ | Telegram Bot API |
| ์ปจํ ์ด๋ | Docker + Docker Compose |
| CI/CD | GitHub Actions |
| ์๋ฒ | Hetzner CPX31 (4 vCPU / 8 GB RAM) |
๋ค์ด๋ฒ ID: wncksdid0750
๋ธ๋ก๊ทธ URL: https://blog.naver.com/pakrsojang
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ์๋ ์ค์ผ์ค๋ฌ โ
โ (scheduler/auto_scheduler.py) โ
โ 1-2์๊ฐ ๊ฐ๊ฒฉ ์คํ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ํ์ดํ๋ผ์ธ (pipeline.py) โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ ๋ฆฌ์์น โโโ ์ฝํ
์ธ ์์ฑ โโโ ์ด๋ฏธ์ง ์์ฑ โ โ
โ โ (Perplexity)โ โ (Claude) โ โ (Gemini) โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๋ธ๋ก๊ทธ ํฌ์คํฐ (auto_post.py) โ
โ ์ธ์
๋ก๋ โ ๊ธ์ฐ๊ธฐ โ ์ด๋ฏธ์ง ์ฝ์
โ ๋ฐํ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ํ
๋ ๊ทธ๋จ ์๋ฆผ (utils/telegram_notifier.py) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| ์์ด์ ํธ | ํ์ผ | ์ญํ |
|---|---|---|
| Research Agent | agents/research_agent.py | Perplexity API๋ก ์ค์๊ฐ ๋ด์ค/ํธ๋ ๋ ์์ง |
| Content Agent | agents/content_agent.py | Claude Haiku/Sonnet์ผ๋ก ๋ธ๋ก๊ทธ ๊ธ ์์ฑ |
| Visual Agent | agents/visual_agent.py | ์ด๋ฏธ์ง ํ๋กฌํํธ ์์ฑ |
| QA Agent | agents/qa_agent.py | ์ฝํ ์ธ ํ์ง ๊ฒ์ฆ (70์ ๋ฏธ๋ง ์ฌ์์ฑ) |
| Upload Agent | agents/upload_agent.py | ๋ค์ด๋ฒ ๋ธ๋ก๊ทธ ์ ๋ก๋ |
๋ค์ด๋ฒ๋ธ๋ก๊ทธ๋ด/
โโโ main.py # ๋ฉ์ธ ์ค์ผ์คํธ๋ ์ดํฐ
โโโ pipeline.py # ํตํฉ ํ์ดํ๋ผ์ธ (ํต์ฌ)
โโโ auto_post.py # ๋ค์ด๋ฒ ๋ธ๋ก๊ทธ ํฌ์คํ
(ํต์ฌ)
โโโ manual_login_clipboard.py # ์๋ ๋ก๊ทธ์ธ (์ธ์
์ ์ฅ์ฉ)
โ
โโโ agents/ # AI ์์ด์ ํธ
โ โโโ research_agent.py # ๋ฆฌ์์น (Perplexity)
โ โโโ content_agent.py # ์ฝํ
์ธ ์์ฑ (Claude)
โ โโโ visual_agent.py # ๋น์ฃผ์ผ ์์ฑ
โ โโโ qa_agent.py # ํ์ง ๊ฒ์ฆ
โ โโโ upload_agent.py # ์
๋ก๋
โ โโโ marketing_content.py # ๋ง์ผํ
์ฝํ
์ธ
โ โโโ blog_content_generator.py # ๋ค๋ชฉ์ ์์ฑ๊ธฐ
โ
โโโ scheduler/ # ์ค์ผ์ค๋ง
โ โโโ __init__.py
โ โโโ auto_scheduler.py # 24์๊ฐ ์๋ ์ค์ผ์ค๋ฌ (์ํธ๋ฆฌํฌ์ธํธ)
โ โโโ topic_rotator.py # ์ฃผ์ ์ํ
โ
โโโ utils/ # ์ ํธ๋ฆฌํฐ
โ โโโ gemini_image.py # Imagen ์ด๋ฏธ์ง ์์ฑ
โ โโโ telegram_notifier.py # ํ
๋ ๊ทธ๋จ ์๋ฆผ
โ โโโ clipboard_input.py # ํด๋ฆฝ๋ณด๋ ์
๋ ฅ (ํค๋๋ฆฌ์ค ํธํ)
โ โโโ human_behavior.py # ์ธ๊ฐ ํ๋ ์๋ฎฌ๋ ์ด์
โ โโโ cost_optimizer.py # API ๋น์ฉ ์ต์ ํ
โ โโโ error_recovery.py # ์๋ฌ ๋ณต๊ตฌ
โ
โโโ security/ # ๋ณด์
โ โโโ credential_manager.py # API ํค ๊ด๋ฆฌ (macOS ํค์ฒด์ธ)
โ โโโ session_manager.py # ์ธ์
๊ด๋ฆฌ (์ํธํ)
โ
โโโ config/ # ์ค์
โ โโโ settings.py # ํ๊ฒฝ ์ค์
โ โโโ human_timing.py # ํ์ด๋ฐ ์ค์
โ
โโโ models/ # ๋ฐ์ดํฐ
โ โโโ database.py # SQLite DB
โ
โโโ monitoring/ # ๋ชจ๋ํฐ๋ง
โ โโโ health_checker.py # ํฌ์ค์ฒดํฌ
โ โโโ reporter.py # ํต๊ณ ๋ฆฌํฌํฐ
โ
โโโ deploy/ # ๋ฐฐํฌ ์คํฌ๋ฆฝํธ
โ โโโ server-init.sh # ์๋ฒ ์ด๊ธฐํ
โ โโโ setup-github-secrets.sh # GitHub Secrets ์ค์
โ โโโ deploy-to-server.sh # ์ํด๋ฆญ ๋ฐฐํฌ
โ โโโ setup-env-interactive.sh # ๋ํํ .env ์ค์
โ
โโโ .github/workflows/
โ โโโ deploy.yml # CI/CD ํ์ดํ๋ผ์ธ
โ
โโโ Dockerfile # Docker ์ด๋ฏธ์ง (Playwright ๊ธฐ๋ฐ)
โโโ docker-compose.yml # Docker Compose ์ค์
โโโ requirements.txt # Python ์์กด์ฑ
โโโ .env.example # ํ๊ฒฝ๋ณ์ ์์
โโโ .gitignore # Git ์ ์ธ ๋ชฉ๋ก
# ์ ์ฅ์ ํด๋ก
git clone https://github.com/joocy75-hash/Naver-Blog.git
cd Naver-Blog
# ๊ฐ์ํ๊ฒฝ ์์ฑ
python -m venv venv
source venv/bin/activate # macOS/Linux
# or: venv\Scripts\activate # Windows
# ์์กด์ฑ ์ค์น
pip install -r requirements.txt
# Playwright ๋ธ๋ผ์ฐ์ ์ค์น
playwright install chromium
# ํ๊ฒฝ๋ณ์ ์ค์
cp .env.example .env
# .env ํ์ผ ํธ์งํ์ฌ API ํค ์
๋ ฅpython manual_login_clipboard.py
# ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ฆฌ๋ฉด ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ํ
# ๋ก๊ทธ์ธ ํ ์๋์ผ๋ก ์ธ์
์ ์ฅ๋จ# 1ํ ํ
์คํธ (๋ฐํ ์ ํจ)
python pipeline.py research --dry
# ์ค์ ๋ฐํ
python pipeline.py research
# 24์๊ฐ ์๋ ์ค์ผ์ค๋ฌ
python -m scheduler.auto_scheduler
# ์ปค์คํ
์ค์ (1-2์๊ฐ ๊ฐ๊ฒฉ, ์ผ์ผ 10๊ฐ)
python -m scheduler.auto_scheduler --interval 1 2 --limit 10| ํญ๋ชฉ | ๊ฐ |
|---|---|
| ์๋ฒ IP | 141.164.55.245 |
| ์๋ฒ ์ ๊ณต์ฌ | Vultr |
| ์์น | Seoul, South Korea ๐ฐ๐ท |
| ์ฌ์ | vc2-1c-1gb (1 vCPU / 1 GB RAM / 25 GB SSD) |
| OS | Ubuntu 24.04 LTS |
| ๋ฐฐํฌ ๊ฒฝ๋ก | /root/naver-blog-bot |
| ์ ๋น์ฉ | $5.00 |
| ํน์ด์ฌํญ | ํ๊ตญ IP๋ก ๋ค์ด๋ฒ ๋ธ๋ก๊ทธ ๋ฐํ ์ ํ ์ฐํ |
โ ํ์ฌ ๋ฐฐํฌ ์ํ:
- Docker ์ปจํ
์ด๋
naver-blog-bot์คํ ์ค (healthy) - ๋ค์ ์๋ ํฌ์คํ ์์ฝ๋จ (1-2์๊ฐ ๊ฐ๊ฒฉ)
- ํ ๋ ๊ทธ๋จ ์๋ฆผ ์๋ ์ค
- ํฌ์ค์ฒดํฌ ํต๊ณผ
# ๋น๋ฐ๋ฒํธ ์ ์ (ํ์ฌ ์ค์ )
ssh root@141.164.55.245
# ๋น๋ฐ๋ฒํธ: [Br76r(6mMDr%?ia
# SSH ํค ์ ์ (GitHub Actions์ฉ)
ssh -i ~/.ssh/vultr_naver_bot root@141.164.55.245์๋ฒ์ ์ฒ์ ๋ฐฐํฌํ๋ ๊ฒฝ์ฐ:
# 1. SSH ์ ์
ssh root@141.164.55.245
# 2. Docker ์ค์น
curl -fsSL https://get.docker.com | sh
systemctl start docker
systemctl enable docker
# 3. Git ์ค์น
apt-get update && apt-get install -y git
# 4. ํ๋ก์ ํธ ํด๋ก (๋ฐฉ๋ฒ A: GitHub - private repo์ ๊ฒฝ์ฐ ํ ํฐ ํ์)
git clone https://github.com/mr-joo/naver-blog-bot.git /root/naver-blog-bot
# ๋๋ (๋ฐฉ๋ฒ B: ๋ก์ปฌ์์ tar๋ก ์ ์ก)
# ๋ก์ปฌ์์ ์คํ:
cd /Users/mr.joo/Desktop/๋ค์ด๋ฒ๋ธ๋ก๊ทธ๋ด
tar czf - --exclude='.git' --exclude='__pycache__' . | ssh root@141.164.55.245 'mkdir -p /root/naver-blog-bot && cd /root/naver-blog-bot && tar xzf -'
# 5. ํ๊ฒฝ๋ณ์ ์ค์
cd /root/naver-blog-bot
cp .env.example .env
vim .env # API ํค, ๋น๋ฐ๋ฒํธ ์
๋ ฅ
# 6. Docker ์ด๋ฏธ์ง ๋น๋ ๋ฐ ์คํ
docker build -t naver-blog-bot:latest .
docker run -d \
--name naver-blog-bot \
--restart unless-stopped \
-v $(pwd)/data:/app/data \
-v $(pwd)/logs:/app/logs \
-v $(pwd)/secrets:/app/secrets \
-v $(pwd)/config:/app/config \
-v $(pwd)/.env:/app/.env:ro \
--shm-size=2gb \
naver-blog-bot:latest
# 7. ์ํ ํ์ธ
docker ps
docker logs naver-blog-bot --tail 50์ ์๋ฒ๋ก ์ด์ ํ๋ ๊ฒฝ์ฐ:
-
์๋ฒ ์ค๋น:
- Docker ์ค์น
- Git ์ค์น
- SSH ํค ์์ฑ (GitHub Actions์ฉ)
-
์ฝ๋ ๋ฐฐํฌ:
- ์์ค ์ฝ๋ ํด๋ก ๋๋ ์ ์ก
- .env ํ์ผ ์ค์ (API ํค, ๋ค์ด๋ฒ ๊ณ์ )
- Docker ์ด๋ฏธ์ง ๋น๋
- ์ปจํ ์ด๋ ์คํ
-
GitHub Secrets ์ ๋ฐ์ดํธ:
-
HETZNER_HOSTโ ์ ์๋ฒ IP -
HETZNER_USERโroot -
HETZNER_SSH_KEYโ ์ ์๋ฒ SSH private key
-
-
์ธ์ ํ์ผ ์ ์ก (์ค์!):
# ๋ก์ปฌ โ ์ ์๋ฒ scp -r data/sessions/*.encrypted root@141.164.55.245:/root/naver-blog-bot/data/sessions/
-
ํ ์คํธ:
- ์ปจํ ์ด๋ ์ ์ ์คํ ํ์ธ
- ๋ก๊ทธ ํ์ธ
- ํ ๋ ๊ทธ๋จ ์๋ฆผ ์์ ํ์ธ
- GitHub Actions ๋ฐฐํฌ ํ ์คํธ
๋ฌธ์ ์ํฉ: ๋ค์ด๋ฒ๋ ์ธ๊ตญ IP์์ ๋ธ๋ก๊ทธ ๋ฐํ์ ์ฐจ๋จํฉ๋๋ค. ๋ฏธ๊ตญ/์ ๋ฝ ์๋ฒ์์ ๋ฐํ ์ "ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค" ์ค๋ฅ ๋ฐ์.
ํด๊ฒฐ์ฑ : โ Vultr ์์ธ ์๋ฒ ์ฌ์ฉ (ํ์ฌ ์ค์ )
- ํ๊ตญ IP (141.164.55.245)๋ก ๋ค์ด๋ฒ ์ฐจ๋จ ์ฐํ
- ๋ฐํ ์ฑ๊ณต๋ฅ 100%
- ์ถ๊ฐ ํ๋ก์ ์ค์ ๋ถํ์
์ด์ ์๋ํ๋ ๋ฐฉ๋ฒ๋ค:
- โ Hetzner (๋ ์ผ/๋ฏธ๊ตญ): ๋ฐํ ์ฐจ๋จ๋จ
- โ HTTP ํ๋ก์: ๋ณต์กํ๊ณ ๋น์ฉ ๋ฐ์
- โ ํ๊ตญ ์๋ฒ (Oracle Cloud ๋๋ Vultr): ์ฑ๊ณต
ํ์ฌ ์ค์ (Vultr):
- ์ $5.00
- 1GB RAM (์ถฉ๋ถ - ํ์ฌ ์ฌ์ฉ๋ 47%)
- 25GB SSD (์ถฉ๋ถ - ํ์ฌ ์ฌ์ฉ๋ 55%)
๋ฌด๋ฃ ๋์ (์ ํ์ฌํญ):
- Oracle Cloud Always Free: 4 vCPU, 24GB RAM (์์ธ ๋ฆฌ์ )
- ์ค์ ๋ณต์ก (VCN, ๋ณด์ ๋ชฉ๋ก ๋ฑ)
- ๋ฌด๋ฃ์ง๋ง ์ด๊ธฐ ์ค์ ์๊ฐ ์์
- ์ด๋ฏธ Vultr๋ก ๋ฐฐํฌ ์๋ฃ๋์ด ๊ถ์ฅํ์ง ์์
main ๋ธ๋์น Push
โ
โผ
โโโโโโโโโโโโโโโโโโโโโ
โ Test & Lint โ flake8, black, mypy (1๋ถ 30์ด)
โโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโ
โ Build Docker โ ์ด๋ฏธ์ง ๋น๋ (6๋ถ)
โโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโ
โ Deploy to Server โ SSH๋ก ์๋ฒ ๋ฐฐํฌ (2๋ถ)
โโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโ
โ Send Notificationโ Telegram ์๋ฆผ
โโโโโโโโโโโโโโโโโโโโโ
Settings โ Secrets and variables โ Actions์์ ์ค์ :
| Secret ์ด๋ฆ | ๊ฐ | ์ค๋ช |
|---|---|---|
| HETZNER_HOST | 141.164.55.245 | Vultr ์๋ฒ IP |
| HETZNER_USER | root | SSH ์ฌ์ฉ์ |
| HETZNER_SSH_KEY | (์๋ SSH ํค) | SSH ๊ฐ์ธํค ์ ์ฒด |
| TELEGRAM_BOT_TOKEN | (์ ํ) | ์๋ฆผ์ฉ |
| TELEGRAM_CHAT_ID | (์ ํ) | ์๋ฆผ์ฉ |
HETZNER_SSH_KEY์ ์ ๋ ฅํ ๊ฐ:
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
โ ๏ธ ์ฃผ์: ์ด SSH ํค๋ ์๋ฒ ์ ์ฉ์ ๋๋ค. ์ ๋ GitHub ์ ์ฅ์์ ์ปค๋ฐํ์ง ๋ง์ธ์!
# ์ฝ๋ ์์ ํ Pushํ๋ฉด ์๋ ๋ฐฐํฌ
git add .
git commit -m "Fix: something"
git push origin main
# GitHub Actions ์ํ ํ์ธ
gh run list --repo joocy75-hash/Naver-Blog# SSH ์ ์
ssh root@141.164.55.245
# ์ปจํ
์ด๋ ์ํ ํ์ธ
docker ps
# ๋ก๊ทธ ํ์ธ (์ค์๊ฐ)
docker logs naver-blog-bot -f
# ์ฌ์์
cd /root/naver-blog-bot
docker restart naver-blog-bot
# ์ค์ง
docker stop naver-blog-bot
# ์์
docker start naver-blog-bot
# ์ปจํ
์ด๋ ์ญ์ ๋ฐ ์ฌ์์ฑ
docker stop naver-blog-bot
docker rm naver-blog-bot
docker run -d \
--name naver-blog-bot \
--restart unless-stopped \
-v $(pwd)/data:/app/data \
-v $(pwd)/logs:/app/logs \
-v $(pwd)/secrets:/app/secrets \
-v $(pwd)/config:/app/config \
-v $(pwd)/.env:/app/.env:ro \
--shm-size=2gb \
naver-blog-bot:latest
# ์ด๋ฏธ์ง ์ฌ๋น๋
docker build -t naver-blog-bot:latest . && docker restart naver-blog-bot# Docker ๋ก๊ทธ (์ค์๊ฐ)
docker logs naver-blog-bot -f
# Docker ๋ก๊ทธ (์ต๊ทผ 100์ค)
docker logs naver-blog-bot --tail 100
# ์ ํ๋ฆฌ์ผ์ด์
๋ก๊ทธ
tail -f /root/naver-blog-bot/logs/*.log
# ํน์ ์๊ฐ๋ ๋ก๊ทธ ๊ฒ์
docker logs naver-blog-bot --since "2025-12-29T00:00:00"# Docker ๋ฆฌ์์ค ์ฌ์ฉ๋
docker stats naver-blog-bot
# ์์คํ
๋ฆฌ์์ค (top)
top
# ๋ฉ๋ชจ๋ฆฌ ํ์ธ
free -h
# ๋์คํฌ ์ฌ์ฉ๋
df -h
# Docker ๋์คํฌ ์ฌ์ฉ๋
docker system df| ๋ ๋ฒจ | ์ด๋ชจ์ง | ์ค๋ช | ์ฟจ๋ค์ด |
|---|---|---|---|
| INFO | โน๏ธ | ์ ๋ณด์ฑ ์๋ฆผ | 30๋ถ |
| SUCCESS | โ | ์ฑ๊ณต ์๋ฆผ | ์ฆ์ |
| WARNING | ๊ฒฝ๊ณ ์๋ฆผ | 10๋ถ | |
| ERROR | โ | ์ค๋ฅ ์๋ฆผ | 5๋ถ |
| CRITICAL | ๐จ | ์ฌ๊ฐํ ์ค๋ฅ | ์ฆ์ |
| ์ํฉ | ์๋ฆผ ๋ฉ์๋ | ํธ๋ฆฌ๊ฑฐ |
|---|---|---|
| ํฌ์คํ ์ฑ๊ณต/์คํจ | send_post_success/failure() |
ํฌ์คํ ์๋ฃ ์ |
| ์์คํ ๋ฆฌ์์ค | send_system_status() |
CPU/๋ฉ๋ชจ๋ฆฌ/๋์คํฌ ๊ฒฝ๊ณ ์ |
| API ์ํ | send_api_status() |
์๋ต ๋๋ฆผ ๋๋ ์ค๋ฅ ์ |
| ์ธ์ ๋ง๋ฃ | send_session_warning() |
์ธ์ ๋ง๋ฃ 1~3์ผ ์ |
| ์๋ฌ ๋ถ์ | send_error_analysis() |
์๋ฌ ๋ฐ์ ์ (๊ถ์ฅ ์กฐ์น ํฌํจ) |
| ํฌ์ค์ฒดํฌ | send_health_check_result() |
WARNING/CRITICAL ๊ฐ์ง ์ |
| Rate Limit | send_rate_limit_warning() |
์ฌ์ฉ๋ 80%/95% ๋๋ฌ ์ |
| ์ผ์์ ์ง | send_alert() |
์ฐ์ 3ํ ์๋ฌ ์ |
| ๋ณต๊ตฌ ์ฑ๊ณต | send_recovery_notification() |
์๋ ๋ณต๊ตฌ ์๋ฃ ์ |
| ์์/์ข ๋ฃ | send_startup/shutdown_alert() |
๋ด ์์/์ข ๋ฃ ์ |
| ์ฃผ๊ธฐ | ์์ | ์๋ฆผ ์กฐ๊ฑด |
|---|---|---|
| 15๋ถ | ๋น ๋ฅธ ๋ฆฌ์์ค ์ฒดํฌ | CPU 70%โ, ๋ฉ๋ชจ๋ฆฌ 80%โ, ๋์คํฌ 85%โ |
| 1์๊ฐ | ์ ์ฒด ํฌ์ค์ฒดํฌ | API/DB/์ธ์ ๋ฌธ์ ๊ฐ์ง ์ |
| 3์๊ฐ | ์ธ์ ์ํ ์ฒดํฌ | ๋ง๋ฃ 3์ผ ์ ๋ถํฐ ๊ฒฝ๊ณ |
| ๋งค์ผ 21:00 | ์ผ๊ฐ ๋ฆฌํฌํธ | ํญ์ (ํต๊ณ) |
| ๋งค์ฃผ ์ผ์์ผ 20:00 | ์ฃผ๊ฐ ๋ฆฌํฌํธ | ํญ์ (ํต๊ณ) |
์์ ์๋ฆผ:
๐ ๋ธ๋ก๊ทธ ๋ด ์์
๐ค ๊ณ์ : wncksdid0750
โฐ ํฌ์คํ
๊ฐ๊ฒฉ: 1-2์๊ฐ
๐ ์ผ์ผ ์ ํ: 12๊ฐ
๐ค ๋ชจ๋ธ: haiku
๐ ์์คํ
์ํ:
โข CPU: 15.2%
โข ๋ฉ๋ชจ๋ฆฌ: 45.3%
โข ๋์คํฌ: 32.1%
โ
ํฌ์ค์ฒดํฌ: HEALTHY
์๋ฌ ๋ถ์ ์๋ฆผ:
โ ๏ธ ์๋ฌ ๋ฐ์
์๋ฌ ์ ํ: session_expired
๋ด์ฉ: ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค
๋ฐ์ ํ์: 2ํ
์ต์ด ๋ฐ์: 14:23:45
๐ก ๊ถ์ฅ ์กฐ์น:
์ธ์
์ฌ๋ก๊ทธ์ธ
๋ค์ด๋ฒ ์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค. ์๋์ผ๋ก ์ฌ๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.
โ ๏ธ ์๋ ์กฐ์น๊ฐ ํ์ํฉ๋๋ค.
์ผ์์ ์ง ์๋ฆผ:
๐จ ์๋ ํฌ์คํ
์ผ์์ ์ง
์ฐ์ ์๋ฌ 3ํ ๋ฐ์์ผ๋ก ์ผ์์ ์ง๋ฉ๋๋ค.
โฑ ์ฟจ๋ค์ด: 30๋ถ
๐ ์ฌ๊ฐ ์์ : 15:30:00
๐ ์๋ฌ ์ ํ๋ณ ํต๊ณ:
โข session_expired: 2ํ
โข network_error: 1ํ
๐ ์ต๊ทผ ์๋ฌ:
1. [session_expired] ์ธ์
๋ง๋ฃ...
2. [network_error] ์ฐ๊ฒฐ ์คํจ...
3. [session_expired] ๋ก๊ทธ์ธ ํ์...
๐ก ๊ถ์ฅ ์กฐ์น:
์ธ์
์ฌ๋ก๊ทธ์ธ
๋ค์ด๋ฒ ์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค. ์๋์ผ๋ก ์ฌ๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.
ํฌ์ค์ฒดํฌ ๊ฒฝ๊ณ :
๐ก ์์คํ
ํฌ์ค์ฒดํฌ ๊ฒฝ๊ณ
์ปดํฌ๋ํธ ์ํ:
โ
claude_api: Claude API ์ฐ๊ฒฐ ์ ์ (245ms)
โ
perplexity_api: Perplexity API ํค ์ค์ ๋จ
โ ๏ธ memory: ๋ฉ๋ชจ๋ฆฌ ๋์: 82.5% ์ฌ์ฉ ์ค
โ
disk_space: ๋์คํฌ ๊ณต๊ฐ ์ ์ (45.2GB ๋จ์)
โ
database: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ ์ (12ms)
โ ๋ฌธ์ ์ปดํฌ๋ํธ: memory
# ===================
# Naver ๊ณ์ ์ ๋ณด
# ===================
NAVER_ID=your_naver_id
NAVER_PW=your_naver_password
# ===================
# AI API Keys
# ===================
ANTHROPIC_API_KEY=sk-ant-xxxxx
GOOGLE_API_KEY=AIzaSyxxxxx
GCP_PROJECT_ID=your-project-id
GCP_LOCATION=us-central1
PERPLEXITY_API_KEY=pplx-xxxxx
# ===================
# ๋ฐ์ดํฐ๋ฒ ์ด์ค
# ===================
DATABASE_URL=sqlite:///./data/blog_bot.db
# ===================
# ํ
๋ ๊ทธ๋จ ์๋ฆผ
# ===================
TELEGRAM_BOT_TOKEN=1234567890:ABCdefGHI
TELEGRAM_CHAT_ID=your_chat_id
# ===================
# Rate Limiting
# ===================
MAX_DAILY_POSTS=12
MIN_POST_INTERVAL_HOURS=1
API_COOLDOWN_SECONDS=60
# ===================
# ํ๋ก๋์
์ค์
# ===================
DEBUG=False
TEST_MODE=False
HEADLESS=True
LOG_LEVEL=INFOssh root@141.164.55.245
cd /root/naver-blog-bot
vim .env
docker restart naver-blog-bot์ฆ์: ์ค์ผ์ค๋ฌ๊ฐ 24์๊ฐ ๊ฐ๋๋์์ผ๋ ํฌ์คํ 0๊ฐ, ํ ๋ ๊ทธ๋จ ์๋ฆผ 0๊ฐ
- ๋ก๊ทธ์
RuntimeWarning: coroutine was never awaited๊ฒฝ๊ณ ๋ค์ ๋ฐ์ _post_job,_run_health_check๋ฑ async ํจ์๊ฐ ์คํ๋์ง ์์
์์ธ: BackgroundScheduler(๋๊ธฐ์)๋ก async def ํจ์๋ฅผ ์ง์ ํธ์ถ
- APScheduler์ BackgroundScheduler๋ ๋๊ธฐ ์ค์ผ์ค๋ฌ
- async ํจ์๋ฅผ ์ง์ ๋ฑ๋กํ๋ฉด ์ฝ๋ฃจํด ๊ฐ์ฒด๋ง ๋ฐํ๋๊ณ ์ค์ ์คํ์ด ์ ๋จ
ํด๊ฒฐ: AsyncIOScheduler๋ก ๋ณ๊ฒฝ (async ํจ์ ๋ค์ดํฐ๋ธ ์ง์)
# ๋ณ๊ฒฝ ์
from apscheduler.schedulers.background import BackgroundScheduler
self.scheduler = BackgroundScheduler()
# ๋ณ๊ฒฝ ํ
from apscheduler.schedulers.asyncio import AsyncIOScheduler
self.scheduler = AsyncIOScheduler()์์ ๋ ํ์ผ:
scheduler/auto_scheduler.py: AsyncIOScheduler ์ ์ฉ, ๋น๋๊ธฐ ๋ฉ์ธ ๋ฃจํ ๊ตฌํutils/error_recovery.py:asyncio.create_task()์์ ํธ์ถ ์ฒ๋ฆฌ
์ฆ์: ๋ณธ๋ฌธ ์ ๋ ฅ ์ ์ทจ์์ ์์์ด ์ ์ฉ๋จ
ํด๊ฒฐ: se-is-selected ํด๋์ค๋ก ํ์ฑํ ์ํ ๊ฐ์ง
const btn = document.querySelector('button.se-strikethrough-toolbar-button');
if (btn && btn.classList.contains('se-is-selected')) {
btn.click();
}์ฆ์: ํค๋๋ฆฌ์ค ์๋ฒ์์ KeyError: 'DISPLAY'
ํด๊ฒฐ: utils/clipboard_input.py์์ ์กฐ๊ฑด๋ถ import
_headless_mode = os.environ.get('HEADLESS', 'False').lower() == 'true' or 'DISPLAY' not in os.environ
if _headless_mode:
pyautogui = None์ฆ์: ๋ฐํ ๋ฒํผ ํด๋ฆญ ํ "ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค" ์ค๋ฅ
์์ธ: ๋ค์ด๋ฒ๊ฐ ์ธ๊ตญ IP์์ ๋ธ๋ก๊ทธ ๋ฐํ์ ์ฐจ๋จ
ํด๊ฒฐ: Vultr ์์ธ ์๋ฒ๋ก ์ด์ (ํ๊ตญ IP ์ฌ์ฉ)
- ์ด์ ์๋ฒ: Hetzner (๋ฏธ๊ตญ) - ๋ฐํ ์คํจ
- ํ์ฌ ์๋ฒ: Vultr Seoul (ํ๊ตญ 141.164.55.245) - ๋ฐํ ์ฑ๊ณต
์ฆ์: ๋ก๊ทธ์ธ ์คํจ, ์ธ์ ๋ฌดํจ
ํด๊ฒฐ: ๋ก์ปฌ์์ ์๋ ๋ก๊ทธ์ธ ํ ์ธ์ ํ์ผ ์๋ฒ๋ก ์ ์ก
# ๋ก์ปฌ์์
python manual_login_clipboard.py
# ์๋ฒ๋ก ์ ์ก
scp -r data/sessions/*.encrypted root@141.164.55.245:/root/naver-blog-bot/data/sessions/์ฆ์: ์ปจํ ์ด๋ OOMKilled
ํด๊ฒฐ:
# ๋ถํ์ํ ์ด๋ฏธ์ง ์ ๋ฆฌ
docker system prune -a
# Swap ํ์ธ
swapon --show
# ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ํ์ธ
free -h
docker stats naver-blog-bot- ์ธ์ ์๋ ๊ฐฑ์ ์์คํ
- ์๋ฌ ๋ณต๊ตฌ ๋ก์ง ๊ฐํ
- ์ค๋ณต ์ฃผ์ ๋ฐฉ์ง
- ์น ๋์๋ณด๋ (๋ชจ๋ํฐ๋ง UI)
- ํ ๋ ๊ทธ๋จ ์๊ฒฉ ์ ์ด
- ๋ค์ค ๊ณ์ ์ํ ํฌ์คํ
- SEO ์ต์ ํ
- ํต๊ณ ๋ฐ ๋ถ์ ๊ธฐ๋ฅ
- ๋ค์ํ ์ฝํ ์ธ ์นดํ ๊ณ ๋ฆฌ ์ถ๊ฐ
| ํ์ผ | ์ญํ | ํต์ฌ ํจ์ |
|---|---|---|
| auto_post.py | ๋ธ๋ก๊ทธ ํฌ์คํ ์๋ํ | NaverBlogPoster.post() |
| pipeline.py | ์ ์ฒด ํ์ดํ๋ผ์ธ | run_marketing(), run_research() |
| scheduler/auto_scheduler.py | 24์๊ฐ ์ค์ผ์ค๋ฌ | AutoPostingScheduler.start() |
| utils/gemini_image.py | ์ด๋ฏธ์ง ์์ฑ | generate_image() |
| manual_login_clipboard.py | ์ธ์ ์ ์ฅ | ์๋ ๋ก๊ทธ์ธ |
# SSH ์ ์
ssh root@141.164.55.245
# ์ปจํ
์ด๋ ์ํ
docker ps
# ๋ก๊ทธ (์ค์๊ฐ)
docker logs naver-blog-bot -f
# ์ฌ์์
docker restart naver-blog-bot
# ์ค์ง/์์
docker stop naver-blog-bot
docker start naver-blog-bot
# ์ด๋ฏธ์ง ์ฌ๋น๋
cd /root/naver-blog-bot
docker build -t naver-blog-bot:latest .
docker restart naver-blog-bot
# ํ๊ฒฝ๋ณ์ ์์
vim /root/naver-blog-bot/.env
docker restart naver-blog-bot
# GitHub Actions ์ํ
gh run list --repo mr-joo/naver-blog-bot
# ๋ฆฌ์์ค ๋ชจ๋ํฐ๋ง
docker stats naver-blog-bot
free -h
df -h๋ณธ ์์คํ ์ ๊ต์ก ๋ฐ ์ฐ๊ตฌ ๋ชฉ์ ์ผ๋ก ์ค๊ณ๋์์ต๋๋ค. ๋ค์ด๋ฒ ์ด์ฉ์ฝ๊ด์ ์ค์ํด์ผ ํ๋ฉฐ, ์๋ํ๋ก ์ธํ ๊ณ์ ์ ์ง ๋ฆฌ์คํฌ๋ ์ฌ์ฉ์์๊ฒ ์์ต๋๋ค.
๋ฌธ์ ๋ | ๋ง์ง๋ง ์ ๋ฐ์ดํธ: 2025-12-29
1. ์ธ์ ๋ง๋ฃ ๋ฌธ์ (7์ผ ๊ณ ์ ๋ง๋ฃ)
- ๋ฌธ์ : ์ธ์ ์ด ์์ฑ์ผ ๊ธฐ์ค 7์ผ ํ ๋ฌด์กฐ๊ฑด ๋ง๋ฃ๋์ด ์ฌ๋ก๊ทธ์ธ ํ์
- ํด๊ฒฐ: ํฌ์คํ ์ฑ๊ณต ์ ์ธ์ ์๋ ๊ฐฑ์ ๊ธฐ๋ฅ ์ถ๊ฐ
- ์์ ํ์ผ:
security/session_manager.py - ์ ๋ฉ์๋:
renew_session(): ์ธ์ ๊ฐฑ์ (last_renewed_at ํ์์คํฌํ ์ ๋ฐ์ดํธ)get_days_until_expiry(): ๋ง๋ฃ๊น์ง ๋จ์ ์ผ์ ๊ณ์ฐcheck_expiry_warning(): ๋ง๋ฃ ๊ฒฝ๊ณ ํ์ ์ฌ๋ถ ํ์ธ (1, 2, 3์ผ ์ ๊ฒฝ๊ณ )renew_playwright_session(): Playwright ์ธ์ ๊ฐฑ์ ํฌํผ ํจ์
2. Docker ํ๊ฒฝ ํค์ฒด์ธ ์ ๊ทผ ์คํจ
- ๋ฌธ์ : Docker ์ปจํ ์ด๋์์ macOS ํค์ฒด์ธ ์ ๊ทผ ๋ถ๊ฐ๋ก API ํค ๋ก๋ ์คํจ
- ํด๊ฒฐ: Docker ํ๊ฒฝ ์๋ ๊ฐ์ง ๋ฐ ํ๊ฒฝ ๋ณ์ ์ฐ์ ๋ชจ๋ ์ถ๊ฐ
- ์์ ํ์ผ:
security/credential_manager.py - ์ ํจ์/๋ณ์:
is_docker_environment(): Docker ํ๊ฒฝ ์๋ ๊ฐ์ง (/.dockerenv, cgroup, ํ๊ฒฝ๋ณ์ ํ์ธ)IS_DOCKER: ๋ชจ๋ ๋ก๋ ์ Docker ์ฌ๋ถ ์บ์KEYRING_AVAILABLE: Docker์์๋ ์๋์ผ๋ก False
3. CDP ์ฐ๊ฒฐ ํ์์์ (10์ด ๋๊ธฐ)
- ๋ฌธ์ : ์๋ฒ ํ๊ฒฝ์์ CDP ์๋ํฌ์ธํธ๊ฐ ์์ด ๋งค๋ฒ 10์ด ํ์์์ ๋ฐ์
- ํด๊ฒฐ:
use_cdp๊ธฐ๋ณธ๊ฐ์TrueโFalse๋ก ๋ณ๊ฒฝ- CDP ํ์์์์ 10์ด โ 3์ด๋ก ๋จ์ถ
- ํ๊ฒฝ ๋ณ์๋ก ์ ์ด ๊ฐ๋ฅํ๋๋ก ์์
- ์์ ํ์ผ:
agents/upload_agent.py
| ํ์ผ | ๋ณ๊ฒฝ ๋ด์ฉ |
|---|---|
security/session_manager.py |
์ธ์ ์๋ ๊ฐฑ์ ๊ธฐ๋ฅ, ๋ง๋ฃ ๊ฒฝ๊ณ ์์คํ , ํ๊ฒฝ๋ณ์ ์ง์ |
security/credential_manager.py |
Docker ํ๊ฒฝ ์๋ ๊ฐ์ง, ํ๊ฒฝ ๋ณ์ ์ฐ์ ๋ชจ๋ |
agents/upload_agent.py |
CDP ๊ธฐ๋ณธ๊ฐ False, ํ์์์ 3์ด, ์ธ์ ์๋ ๊ฐฑ์ ํธ์ถ |
Dockerfile |
์ ํ๊ฒฝ ๋ณ์ ๋ฌธ์ํ ๋ฐ ๊ธฐ๋ณธ๊ฐ ์ค์ |
.env.example |
USE_CDP, CDP_TIMEOUT, SESSION_MAX_AGE_DAYS ์ถ๊ฐ |
# CDP (Chrome DevTools Protocol) ์ค์
USE_CDP=False # CDP ์ฌ์ฉ ์ฌ๋ถ (๋ก์ปฌ: True, ์๋ฒ/Docker: False)
CDP_TIMEOUT=3 # CDP ์ฐ๊ฒฐ ํ์์์ (์ด)
# ์ธ์
๊ด๋ฆฌ
SESSION_MAX_AGE_DAYS=7 # ์ธ์
์ต๋ ์ ํจ ๊ธฐ๊ฐ (์ผ)ํฌ์คํ
์ฑ๊ณต
โ
โผ
renew_playwright_session() ํธ์ถ
โ
โผ
session_manager.renew_session()
โ
โโ last_renewed_at ํ์์คํฌํ ์
๋ฐ์ดํธ
โโ storage_state ๊ฐฑ์ (์ฟ ํค/์คํ ๋ฆฌ์ง)
โโ ์ํธํํ์ฌ ์ ์ฅ
โ
โผ
์ธ์
์ ํจ๊ธฐ๊ฐ 7์ผ ์ฐ์ฅ (๊ฐฑ์ ์์ ๊ธฐ์ค)
def is_docker_environment() -> bool:
# 1. /.dockerenv ํ์ผ ์กด์ฌ ํ์ธ
# 2. /proc/1/cgroup์์ docker ๋ฌธ์์ด ํ์ธ
# 3. RUNNING_IN_DOCKER ํ๊ฒฝ๋ณ์ ํ์ธ- โ
Vultr ์์ธ ์๋ฒ๋ก ์ด์ (141.164.55.245)
- ๋ค์ด๋ฒ IP ์ฐจ๋จ ๋ฌธ์ ํด๊ฒฐ (ํ๊ตญ IP ์ฌ์ฉ)
- ์๋ฒ ์ ๋ณด ๋ฐ ๋ชจ๋ ๋ช ๋ น์ด ์ ๋ฐ์ดํธ
- GitHub Actions Secrets ์ ๋ณด ์ ๋ฐ์ดํธ
- SSH ํค ์์ฑ ๋ฐ ๋ฌธ์ํ
- AsyncIOScheduler ๋น๋๊ธฐ ์คํ ๋ฌธ์ ํด๊ฒฐ
- ์ทจ์์ ๋ฒ๊ทธ ํด๊ฒฐ
- Hetzner ์๋ฒ ๋ฐฐํฌ ์๋ฃ
-
์ค์ผ์ค๋ฌ์์ ์ธ์ ๊ฐฑ์ ํธ์ถ ์ถ๊ฐ
- ํ์ผ:
scheduler/auto_scheduler.py - ๋ด์ฉ: ํฌ์คํ
์ฑ๊ณต ์
session_manager.renew_session()๋ช ์์ ํธ์ถ - ํ์ฌ:
upload_agent.py์์๋ง ๊ฐฑ์ ํธ์ถ๋จ - ํ์: ์ค์ผ์ค๋ฌ ๋ ๋ฒจ์์๋ ์ธ์ ์ํ ์ฒดํฌ ๋ฐ ๊ฐฑ์
- ํ์ผ:
-
์ธ์ ๋ง๋ฃ ๊ฒฝ๊ณ ํ ๋ ๊ทธ๋จ ์๋ฆผ ์ฐ๋
- ํ์ผ:
scheduler/auto_scheduler.py,utils/telegram_notifier.py - ๋ด์ฉ:
check_expiry_warning()๊ฒฐ๊ณผ๋ฅผ ํ ๋ ๊ทธ๋จ์ผ๋ก ์ ์ก - ๊ตฌํ: ๋งค์ผ ๋๋ ํฌ์คํ ์ ์ธ์ ๋ง๋ฃ D-3, D-2, D-1 ๊ฒฝ๊ณ
- ํ์ผ:
-
์๋ฒ์์ ์ค์ ๋ฐฐํฌ ํ ์คํธ
- Docker ์ด๋ฏธ์ง ์ฌ๋น๋ ๋ฐ ๋ฐฐํฌ
- ์ธ์ ๊ฐฑ์ ๊ธฐ๋ฅ ๋์ ํ์ธ
- ๋ก๊ทธ ๋ชจ๋ํฐ๋ง
-
์๋ ์ฌ๋ก๊ทธ์ธ ์์คํ ๊ตฌ์ถ
- ์ธ์ ๋ง๋ฃ ์ ์๋์ผ๋ก ์ฌ๋ก๊ทธ์ธ ์๋
- 2FA/์บก์ฑ ๊ฐ์ง ์ ํ ๋ ๊ทธ๋จ ์๋ฆผ + ์๋ ๊ฐ์ ์์ฒญ
-
ํฌ์ค์ฒดํฌ์ ์ธ์ ์ํ ์ถ๊ฐ
- ํ์ผ:
monitoring/health_checker.py - ๋ด์ฉ: ์ธ์ ์ ํจ์ฑ ๋ฐ ๋จ์ ์ผ์๋ฅผ ํฌ์ค์ฒดํฌ ํญ๋ชฉ์ ํฌํจ
- ํ์ผ:
-
requirements.txt ๋ฒ์ ๊ณ ์
- ํ์ฌ ์ผ๋ถ ํจํค์ง ๋ฒ์ ๋ฏธ์ง์
- ํ๋ก๋์ ์์ ์ฑ์ ์ํด ๋ชจ๋ ํจํค์ง ๋ฒ์ ๊ณ ์ ๊ถ์ฅ
-
์ธ์ ๋ฐฑ์ ์์คํ
- ์ธ์ ํ์ผ ์๋ ๋ฐฑ์ (์ผ์ผ/์ฃผ๊ฐ)
- ๋ณต๊ตฌ ์คํฌ๋ฆฝํธ ์์ฑ
-
๋ค์ค ์ธ์ ์ง์
- ์ฌ๋ฌ ๋ค์ด๋ฒ ๊ณ์ ์ธ์ ๊ด๋ฆฌ
- ๊ณ์ ๋ณ ์ธ์ ๊ฐฑ์ ๋ฐ ๋ชจ๋ํฐ๋ง
-
์น ๋์๋ณด๋
- ์ธ์ ์ํ ์๊ฐํ
- ์๋ ๊ฐฑ์ ๋ฒํผ
- ๋ก๊ทธ ๋ทฐ์ด
# 1. ์๋ฒ ์ ์
ssh root@141.164.55.245
# 2. ์ฝ๋ ์
๋ฐ์ดํธ
cd /root/naver-blog-bot
git pull origin main # ๋๋ ํ์ผ ์ ์ก
# 3. Docker ์ด๋ฏธ์ง ์ฌ๋น๋
docker build -t naver-blog-bot:latest .
# 4. ์ปจํ
์ด๋ ์ฌ์์
docker stop naver-blog-bot
docker rm naver-blog-bot
docker run -d \
--name naver-blog-bot \
--restart unless-stopped \
-v $(pwd)/data:/app/data \
-v $(pwd)/logs:/app/logs \
-v $(pwd)/secrets:/app/secrets \
-v $(pwd)/config:/app/config \
-v $(pwd)/.env:/app/.env:ro \
--shm-size=2gb \
naver-blog-bot:latest
# 5. ๋ก๊ทธ ํ์ธ
docker logs naver-blog-bot -f