κ΅¬κΈ ν΄λΌμ°λ(GCP) VM μΈμ€ν΄μ€ μμ± μ¬μ μ€λΉ κ³Όμ λΆν° κ²μ μλ² λΉλ, λμ€μ½λ κ΄λ¦¬ λ΄ μ°λ, μ€μκ° νν©ν, 1:1 DM κΈ°λ° λνν μ€μ κ΄λ¦¬ λ° Rclone κΈ°λ° κ΅¬κΈ λλΌμ΄λΈ μ격 λ°±μ μμ€ν μ΄μκΉμ§μ μ κ³Όμ μ μ 리ν λ¬Έμμ λλ€.
(AI μμ΄μ νΈλ₯Ό μν κ°λ° μ μ½ λ° μ»¨λ²€μ
μ λ£¨νΈ κ²½λ‘μ AGENTS.md νμΌμ μ°Έμ‘°νμμμ€.)
- 1. μμ€ν κ΅¬μ± λ° μ£Όμ κ°μ κΈ°λ₯ μμ½
- 2. μ 체 μμ€ν ꡬ쑰 λ° ν΅μ νλ¦
- 3. 보μ λ° μμ μ± μ€κ³ (Security & Safety)
- 4. λ©΄μ± μ‘°ν λ° μ± μμ νκ³ (Disclaimer)
- 5. λλ ν 리 ꡬ쑰 λ° κ΅¬μ± νμΌ μμΈ
- 6. νκ²½ λ³μ μ΄μ€ν κ°μ΄λ (env vs .env)
- 7. μ¬μ μ€λΉ λ° ν΄λΌμ°λ μΈν κ°μ΄λ
- 8. λ¨κ³λ³ κ΅¬μΆ κ°μ΄λ
- 9. μ€μκ° νν©ν λμ보λ λ° μ§λ₯ν μμ§ μμΈ λ©μ»€λμ¦
- 10. μλ¦Ό, λ‘κ·Έ λ° μ κΈ° μ±λ μ λ μμ€ν
- 11. 1:1 DM κΈ°λ° λνν .env μ€μ κ΄λ¦¬ λ° λ‘€λ°± μμ€ν
- 12. μ€μκ° μ΄μ λ° λͺ¨λν°λ§ λͺ λ Ήμ΄ κ°μ΄λ
- 13. λμ€μ½λ μ¬λμ λͺ λ Ήμ΄ κ°μ΄λ
-
RCON Save λ°μ΄ν° νλ¬μ μ μ λκΈ° λ° λ°μ΄ν° μ ν©μ± 보μ₯
- λ°±μ
, μ¬μμ, 컨ν
μ΄λ μ μ§/μ
λ°μ΄νΈ μ RCON
Save컀맨λλ₯Ό μ μ‘ν ν μ¦μ νλ‘μΈμ€λ₯Ό λ©μΆμ§ μκ³ 3μ΄~10μ΄κ°μ λμ€ν¬ νλ¬μ μ μ λκΈ° μκ°(asyncio.sleep)μ 보μ₯ν©λλ€. - λμ€ν¬ I/O μλ μ°¨μ΄λ‘ μΈν΄ μμ λ©λͺ¨λ¦¬ μΈμ μμ¬ λ°μ΄ν°κ° μ μ€λκ±°λ μΈμ΄λΈ νμΌμ΄ κΉ¨μ§λ νμμ μμ² μ°¨λ¨ν©λλ€.
- λ°±μ
, μ¬μμ, 컨ν
μ΄λ μ μ§/μ
λ°μ΄νΈ μ RCON
-
λΉλκΈ° λ½(asyncio.Lock) κΈ°λ° νν©ν μ€λ³΅ μμ± λ° κ²½ν© μν(Race Condition) λ°©μ§
- μ§§μ μ£ΌκΈ°μ λ€μ€ μ΄λ²€νΈ(μ μ μ
ν΄μ₯, μμ€ν
μ£ΌκΈ° 루ν΄, μλ λ²νΌ ν΄λ¦)κ° λμ μ€νλ λ νν©νμ΄ μ€λ³΅ μμ±λλ νμμ λ§κΈ° μν΄
self.dashboard_lock = asyncio.Lock()λΉλκΈ° λ½μ μ μ©νμ΅λλ€. - νν©ν μ 보 μμ§ λ° κ°±μ μμ μ μ€μ§ λ¨μΌ μμ°¨(Thread-safe)λ‘λ§ μννμ¬ λμ보λ μλ² λμ μν΄μ μλ²½ν μλ°©ν©λλ€.
- μ§§μ μ£ΌκΈ°μ λ€μ€ μ΄λ²€νΈ(μ μ μ
ν΄μ₯, μμ€ν
μ£ΌκΈ° 루ν΄, μλ λ²νΌ ν΄λ¦)κ° λμ μ€νλ λ νν©νμ΄ μ€λ³΅ μμ±λλ νμμ λ§κΈ° μν΄
-
μ€μκ° λ¬Όλ¦¬ λ©λͺ¨λ¦¬ λͺ¨λν°λ§ λ° μλ μ¬λΆν (Memory Leak Protection)
psutilλΌμ΄λΈλ¬λ¦¬λ₯Ό ν΅ν΄ κ°μ μλ²μ RAM μ¬μ©λ₯ μ μ€μκ° μΆμ νλ©°, μκ³μΉ(MEMORY_RESTART_THRESHOLD, κΈ°λ³Έ 80%) μ΄κ³Ό μ μλμΌλ‘ μΈκ²μ μλ΄ λ°©μ‘ λ° RCON μ μ₯μ μννκ³ λ컀 컨ν μ΄λλ₯Ό μ¬λ°°μΉ(docker-compose up -d --force-recreate)ν©λλ€.- μ¬μμ μ§ν μ°μ μ¬λΆν 루νμ λΉ μ§μ§ μλλ‘ 30λΆ(1800μ΄)μ μ¬μμ μΏ¨λ€μ΄ νμμ€ν¬ν λ³΄νΈ λ‘μ§μ΄ ν¨κ» ꡬλλ©λλ€.
-
κ³ μ λ©μμ§(Pinned Message) λ³΄νΈ κΈ°λ° μ±λ μλ/μ κΈ° μ λ
/cleanλͺ λ Ήμ΄ μ€ν μ λ° 4μκ°λ§λ€ μλ ꡬλλλ μ κΈ° μ±λ μ λ νμ€ν¬(scheduled_4hr_clean_task) μν μ λμ€μ½λchannel.pins()λͺ©λ‘μ νμ±ν©λλ€.- κ΄λ¦¬μκ° μ±λ μλ¨μ κ³ μ ν΄λ κ°μ΄λ λ©μμ§ λ° νμ±νλ λμ보λ λ©μμ§λ₯Ό μ§λ₯μ μΌλ‘ κ°μ§νμ¬ μμ λμμμ μ μΈ(Filter)ν¨μΌλ‘μ¨ μ€μ 곡μ§λ₯Ό μμ νκ² λ³΄νΈν©λλ€.
-
μ€ν μ§μ μκ° μ λ°μ΄νΈ μ²΄ν¬ λ° μ μ ν΄μ₯ μ°λ μνμ€
- λ§€μΌ μ§μ λ μκ°(
02:00,04:00,09:00,17:00)μ μ€ν API(AppID: 2394010)λ₯Ό μ λ° μ‘°ννμ¬ κ²μ μ΅μ λΉλ μ λ°μ΄νΈ μ¬λΆλ₯Ό νλ³ν©λλ€. - μ
λ°μ΄νΈ κ°μ§ μ μ μμκ° 0λͺ
μ΄λ©΄ μ¦μ 1λΆ ν μλ μ
λ°μ΄νΈλ₯Ό μ§ννκ³ , νλ μ΄μ΄κ° μ‘΄μ¬ν κ²½μ° μλ΄ λ°©μ‘μ μ‘μΆν λ€
update_pending_on_playersνλκ·Έλ₯Ό μ립νμ¬ λͺ¨λ μ μ κ° ν΄μ₯νλ μ¦μ μλμΌλ‘ μ λ°μ΄νΈ μνμ€λ₯Ό κ°λν©λλ€.
- λ§€μΌ μ§μ λ μκ°(
-
λμ€μ½λ 1:1 DM κΈ°λ° λνν
.envμ€μ κ΄λ¦¬ λ° μλ λ°±μ /λ‘€λ°±- κ΄λ¦¬μκ° λ΄μκ² κ°μΈ λ©μμ§(DM)λ‘
.env,μ€μ λ©μμ§λ₯Ό 보λ΄λ©΄ νμ¬ κ°μ μλ²μ μ μ©λ.envνμΌμ μ²¨λΆ νμΌλ‘ μ¦μ μ λ¬λ°μ΅λλ€. - μμ λ
.envνμΌμ DM μ°½μ μ λ‘λνλ©΄ λ΄μ΄ μ΄μ μ€μ κ³Όμ μ°¨μ΄μ (Diff)μ νμ νμ¬ μλ² λλ‘ μΆλ ₯νκ³ , RCON μ μ₯ β λ°μ΄ν° λ°±μ β 컨ν μ΄λ μ¬λ°°μΉλ₯Ό μλ μνν©λλ€. - 컨ν
μ΄λ μ¬λ°°μΉ μ μ€λ₯κ° λ°μνλ©΄ μλμΌλ‘ λ°±μ
λ³Έ(
.env.bak)μ ν΅ν΄ μ΄μ μ μ μλ μ€μ μΌλ‘ μ¦μ μλ λ‘€λ°±(Rollback)ν©λλ€.
- κ΄λ¦¬μκ° λ΄μκ² κ°μΈ λ©μμ§(DM)λ‘
-
κ²μ λ΄μ₯ λ°±μ ν΄λ μμΆ μ μΈ λ° 2μ°¨ κ΅¬κΈ λλΌμ΄λΈ λΉλκΈ° μ μ‘
.tar.gzμμΉ΄μ΄λΉ μ κ²μ μ체 λ΄μ₯ λ°±μ ν΄λ(SaveGames/0/<world_id>/backup)λ₯Ό μμΆ λμμμ νν°λ§(exclude_internal_backup)νμ¬ μ΄μ€ μμΆμΌλ‘ μΈν μ©λ λλΉ λ° νμΌ μ¦λ° μμΈ(Errno 2)λ₯Ό λ°©μ§ν©λλ€.- λ°±μ
νμΌ μμ± μ§ν Rclone λΉλκΈ° μλΈνλ‘μΈμ€λ₯Ό νΈμΆνμ¬ κ΅¬κΈ λλΌμ΄λΈ(
gdrive:backup)λ‘ μ¬λ³Έμ μ μ‘ν©λλ€.
-
μλ² λ΄μ₯ μμ λ°±μ ν΄λ λ° λ§λ£ μ격 νμΌ μλ μκ±°
- μ€μ λ 보쑴 κΈ°ν(
BACKUP_RETENTION_DAYS)μ μ΄κ³Όν λ‘컬 λ°±μ νμΌ(tar.gz)κ³Ό κ΅¬κΈ λλΌμ΄λΈ μ격μ§μ μ€λλ λ°±μ μrclone delete --min-ageλͺ λ ΉμΌλ‘ μκ±°ν©λλ€. - μΈμ΄λΈ ν΄λ λ΄λΆμ λ΄μ₯ λ°±μ
ν΄λλ€κΉμ§
shutil.rmtreeλ‘ μΆμ μκ±°νμ¬ κ°μ μλ² λμ€ν¬ μ©λμ μΆκ°λ‘ ν보ν©λλ€.
- μ€μ λ 보쑴 κΈ°ν(
-
Fast-Path μ΅μ ν νν©ν κ°±μ λ° μκ° μΉμ (Self-Healing) μ€μΊ
- κΈ°μ‘΄ λ©μμ§ IDλ₯Ό νμ©ν Direct Edit(1ν API νΈμΆ) Fast-Path λ°©μμ μ°μ μ μ©νλ©°, λ©μμ§κ° μ μ€λ κ²½μ° μμ 50κ° λ΄μμ μνμνμ¬ λμ보λλ₯Ό μκ° λ³΅κ΅¬νκ³ μ€λ³΅ λ©μμ§λ₯Ό μ²μν©λλ€.
-
νκ²½ μ€μ μΌκ΄ λ³κ²½ λ° μ΄μ€ μΉμΈ (
/μ€μ λ³κ²½)- μ¬λ¬ νκ²½ λ³μ(
.env) κ°μ μΌν(,)λ‘ κ΅¬λΆνμ¬ ν λ²μ λ³κ²½νκ³ , μ λ ₯ ν€ μ ν¨μ± κ²μ¦ λ° λ³κ²½ νμΈ μλ² λμ μΉμΈ λ²νΌμ ν΅ν΄ μμ ν μ΄μ€ μ μ°¨λ₯Ό μ 곡ν©λλ€.
- μ¬λ¬ νκ²½ λ³μ(
-
12μκ° μ£ΌκΈ° λμ€ν¬ μλ λͺ¨λν°λ§ λ° μ€μκ° λμ€ν¬ μ‘°ν (
/λμ€ν¬)- 12μκ°λ§λ€ λμ€ν¬ μ¬μ©λ₯ μ μ§λ¨νμ¬ 80% μ΄κ³Ό μ μλ¦Όμ μ‘μΆνλ©°,
/λμ€ν¬λͺ λ Ήμ ν΅ν΄ μ€μκ° μ 체/μ¬μ©/μμ¬ μ©λμ μ¦μ νμΈν μ μμ΅λλ€.
- 12μκ°λ§λ€ λμ€ν¬ μ¬μ©λ₯ μ μ§λ¨νμ¬ 80% μ΄κ³Ό μ μλ¦Όμ μ‘μΆνλ©°,
Discord User / Admin (DM & Slash Commands)
β
βΌ
Discord Bot (main.py)
β
βββββββββΌββββββββ¬βββββββββββββββ
β β β β
βΌ βΌ βΌ βΌ
Backup Docker Monitor Config (DM/Slash)
β β β β
β β βββΊ Steam API βββΊ .env Read/Write
β β
βΌ βΌ
Local Backup Rclone Upload (gdrive:backup)
β β
βΌ βΌ
[Saved Path] [Google Drive Remote]
[λμ€μ½λ μ¬μ©μ / κ΄λ¦¬μ] ββ(μ¬λμ λͺ
λ Ήμ΄ / DM / UI λ²νΌ)ββ> [PalworldBot (main.py)]
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββ
β (MCRcon TCP 25575) β (REST API TCP 8212) β (docker.from_env API / subprocess)
βΌ βΌ βΌ
[ν°μλ μλ² μ»¨ν
μ΄λ] [REST API μμ§ μμ§] [Docker Engine]
β β
β (RCON Save νλ¬μ λ° λ°±μ
) β (컨ν
μ΄λ μν μ μ΄)
ββββββββββββββββββββββββββββββββββββββββββββ> [Saved λ°μ΄ν° ν΄λ] β
β β
βΌ (νλ₯΄ μμΆ μμΉ΄μ΄λΉ) βΌ
[backups/ λλ ν 리] <ββββββββββββββββββββββββββββββββββ
β
βΌ (rclone copy λΉλκΈ° μ€ν)
[κ΅¬κΈ λλΌμ΄λΈ μ격 λ°±μ
]
λ³Έ μμ€ν μ μΈλΆ ν΄νΉ 곡격 λ° νκ°λμ§ μμ μ¬μ©μμ μλ² μ€μ©μ λ°©μ§νκΈ° μν΄ λ€μκ³Ό κ°μ λ€μ€ 보μ μ€κ³κ° μ μ©λμ΄ μμ΅λλ€.
-
1. νμν λ΄λΆλ§ ν΅μ (Localhost Isolation)
- λ΄(
main.py)κ³Ό κ²μ μλ² μ»¨ν μ΄λλ λμΌ κ°μ μλ² λ΄μ μμ£Όνλ©°, REST API ν¬νΈ(8212)μ RCON ν¬νΈ(25575)λ μΈλΆ IPκ° μλ λ‘컬 루νλ°± μ£Όμ(127.0.0.1)λ₯Ό ν΅ν΄μλ§ λ΄λΆ ν΅μ ν©λλ€.
- λ΄(
-
2. κ°μ μλ² λ°©νλ²½ μ΅μν (Port Protection)
- κ²μ νλ μ΄λ₯Ό μν΄ μ΄μ΄λμ΄μΌ νλ κ²μ ν¬νΈ(UDP 8211) μΈμ λͺ¨λ μ μ΄ ν¬νΈλ μΈλΆλ‘λΆν° μ°¨λ¨ν μ μμ΅λλ€.
- κ΅¬κΈ ν΄λΌμ°λ λ°©νλ²½ μ€μ μ RCON λ° REST API ν¬νΈλ₯Ό μΈλΆμ κ°λ°©νμ§ μμλ λ΄λΆ ν΅μ λ§μ ν΅ν΄ λ΄μ΄ μ§μ μ 보λ₯Ό μ μ΄ν©λλ€.
-
3. λμ€μ½λ κ΄λ¦¬μ ID κ²μ¦ (Role Verification)
- μ£Όμ μ μ΄ λͺ
λ Ή,
/μ€μ λ³κ²½, DMμ ν΅ν.envνμΌ μ‘μμ λ° μ μ© κΈ°λ₯μenvλ΄ μ§μ λADMIN_IDSμ«μ IDμ λμ‘°νμ¬ λ―ΈμΈμ¦ μ¬μ©μμ μ κ·Όμ μ격ν μ°¨λ¨ν©λλ€.
- μ£Όμ μ μ΄ λͺ
λ Ή,
-
4. μ¬μ νκ²½ λ³μ κ²μ¦ (Fail-Fast Validation)
- λ΄ κΈ°λ μ
validator.pyλ₯Ό νΈμΆνμ¬ λμ€μ½λ ν ν°, μ±λ ID, κ²½λ‘ μ‘΄μ¬ μ 무, ν¬νΈ λ²μ(1~65535), μ μν μμΉλ₯Ό μ¬μ κ²μ¦νλ©°, μ ν¨νμ§ μμ νλͺ© λ°κ²¬ μ λ΄ κ΅¬λμ μ¦κ° μ€λ¨νμ¬ μ€μλμ μ°¨λ¨ν©λλ€.
- λ΄ κΈ°λ μ
-
5. μμ ν Subprocess μ€ν λ° μ μΈμ μ λ°©μ§
- μ μ£Όμ
μ·¨μ½μ μ μ°¨λ¨νκΈ° μν΄
os.system()λμsubprocess.run()λλasyncio.create_subprocess_exec()μ 리μ€νΈ μΈμ νν(["docker", "exec", ...])λ₯Ό μ μ©νμ¬ μμ νκ² κ΅¬λν©λλ€. - RCON μ€ν μ λ°±κ·ΈλΌμ΄λ νκ²½ TTY μ€λ₯λ₯Ό λ°©μ§νκΈ° μν΄
-itμ΅μ μ λ°°μ νκ³ λΉλκΈ° νλ‘μΈμ€λ‘ μ²λ¦¬ν©λλ€.
- μ μ£Όμ
μ·¨μ½μ μ μ°¨λ¨νκΈ° μν΄
-
6. νμ μ νμΌ λ‘κΉ λ° μμΈ μΆμ (RotatingFileHandler & Traceback)
logger.pyλ₯Ό ν΅ν΄palworld_bot.logμ λ‘κ·Έλ₯Ό μ μ₯νλ©°, 5MB λ¬μ± μ μλ μν(μ΅λ 3κ° νμΌ λ³΄μ‘΄)νμ¬ λμ€ν¬ μ€λ²νλ‘μ°λ₯Ό μλ°©ν©λλ€.- λͺ¨λ μμΈ λ°μ μ μμΈν μλ¬ λ‘κ·Έμ νΈμΆ μ€ν(
traceback)μ κΈ°λ‘νλλ‘ μ λΉλμ΄ μμ΅λλ€.
- μ 곡 λͺ©μ : λ³Έ νλ‘μ νΈμμ μ 곡νλ μ€ν¬λ¦½νΈ λ° κ°μ΄λλ μλ² κ΄λ¦¬ νΈμλ₯Ό λκΈ° μν μ°Έκ³ μ© μ€νμμ€ μλ£μ λλ€.
- 보μ¦μ λΆμ¬: λ³Έ μμ€ν μ λͺ μμ /묡μμ λ³΄μ¦ μμ΄ "μλ κ·Έλλ‘(As-Is)" μ 곡λ©λλ€. κ°λ°μλ μμ€ν μ 무결μ±, μμ μ± λ° νΉμ λͺ©μ μ λν μ ν©μ±μ 보μ¦νμ§ μμ΅λλ€.
- μ± μμ κ·μ: μμ€ν μ€μΉ, μ΄μ© μ€ λ°μνλ λ°μ΄ν° μ μ€, VM μ€μλ, GCP κ³ΌκΈ λ°μ λ±μ λͺ¨λ κ²°κ³Όμ λν λ²μ , μ¬μ μ , κΈ°μ μ μ± μμ μ μ μΌλ‘ μλ²λ₯Ό ꡬμΆνκ³ μ΄μνλ μ¬μ©μ λ³ΈμΈμκ² μμ΅λλ€.
- κΆμ₯ μ‘°μΉ: μμ€ν λ³κ²½μ΄λ μ€μ μ μ© μ μ€μ μΈμ΄λΈ λ°μ΄ν°λ μΈλΆ μ μ₯μμ λ³λ μλ λ°±μ μ λ°μλλ κ²μ κ°λ ₯ν κΆμ₯ν©λλ€.
κ°μ μλ²μ νλ‘μ νΈ λλ ν 리 λ΄λΆ ꡬ쑰μ νμ κ΅¬μ± νμΌλ€μ 물리μ μν λͺ μΈμ λλ€.
/home/<μ¬μ©μ_κ³μ λͺ
>/palworld/
βββ AGENTS.md # AI μμ΄μ νΈ(LLM)μ© κ°λ° κ·μΉ, 컨벀μ
λ° λͺ¨λ μμ μμ κ°μ΄λ
βββ docker-compose.yml # ν°μλ λ컀 컨ν
μ΄λ λ° TCP ν¬μ€μ²΄ν¬ κ²μ¬ μ μ νμΌ
βββ env # λμ€μ½λ λ΄ κ΅¬λ λ° κ°μ μλ² κ²½λ‘ μ€μ μ© νκ²½ λ³μ (μ μμ)
βββ .env # ν°μλ κ²μ λ΄λΆ λμ κ·μΉ(κ²½νμΉ, ν¬νλ₯ λ±) μ€μ μ© νκ²½ λ³μ (μ μμ)
βββ main.py # λ΄ μ§μ
μ , λ°±κ·ΈλΌμ΄λ μ€μΌμ€λ¬ νμ€ν¬ λ° μ΄λ²€νΈ/μ¬λμ 컀맨λ νΈλ€λ¬
βββ setup.sh # μμ‘΄μ± ν¨ν€μ§ μ€μΉ, κ°μνκ²½ κ΅¬μΆ λ° μμ€ν
μλΉμ€ λ±λ‘ μ€ν¬λ¦½νΈ
βββ status.json # νν©ν λ©μμ§ ID, λΉλ μ 보 λ° μ΅μ’
μνκ° μΊμ± νμΌ (μλ μμ±)
βββ palworld_bot.log # λ΄ μ€ν λ‘κ·Έ νμΌ (μλ μμ±, RotatingFileHandler μ μ©)
βββ backups/ # μΈμ΄λΈ μμΆ νμΌμ΄ μ μ₯λλ λ°±μ
ν΄λ (μλ μμ±, κ΅¬κΈ λλΌμ΄λΈ μ°λ)
βββ utils/ # μ νΈλ¦¬ν° λͺ¨λ μ§ν©
β βββ __init__.py # utils ν¨ν€μ§ μ΄κΈ°ν νμΌ
β βββ logger.py # 5MB μ ν, 3κ° νμΌ λ³΄μ‘΄ κΈ°μ€μ RotatingFileHandler λ‘κΉ
ꡬμ±
β βββ validator.py # λ΄ κΈ°λ μ νμ νκ²½ λ³μ λλ½ λ° ν¬νΈ λ²μ(1~65535) λ¬΄κ²°μ± κ²μ¦
βββ services/ # λΉμ¦λμ€ λ‘μ§ μλΉμ€ λͺ¨λ μ§ν©
β βββ __init__.py # services ν¨ν€μ§ μ΄κΈ°ν νμΌ
β βββ config_service.py # env λ° .env νμΌ μ½κΈ°/μ°κΈ°, DM νμΌ λ°μ‘/μμ μ μ© λ° λμ 리λ‘λ
β βββ status_service.py # status.json ꡬ쑰μ λ‘λ λ° μ°κΈ° λ¬΄κ²°μ± κ΄λ¦¬
β βββ docker_service.py # Docker API μμ§ μ°λ λ° λΉλκΈ° RCON λͺ
λ Ήμ΄ μ μ‘ μ²λ¦¬
β βββ backup_service.py # RCON Save νλ¬μ, μΈμ΄λΈ μμΆ λ°±μ
, Rclone μ
λ‘λ λ° λ΄μ₯/μ격 μ 리
β βββ monitor_service.py # μ€ν API μ΅μ λΉλ μ‘°ν, REST API μμ§ λ° μμ€ν
리μμ€ λͺ¨λν°λ§
βββ bot/ # λμ€μ½λ λ΄ UI/UX λͺ¨λ μ§ν©
βββ __init__.py # bot ν¨ν€μ§ μ΄κΈ°ν νμΌ
βββ embeds.py # ONLINE/STARTING/OFFLINE μνλ³ λμ보λ μλ² λ λμμΈ
βββ views.py # λμ보λ μλ μ
λ°μ΄νΈ λ²νΌ λ° μ€μ λ³κ²½ confirmation λ·° μ μ
λ³Έ μμ€ν μ κ΄λ¦¬ νΈμμ±κ³Ό 보μμ±μ κ·ΉλννκΈ° μν΄ νκ²½ λ³μ νμΌμ μμ ν μ΄μ€ννμ¬ λ 립μ μΌλ‘ μ΄μν©λλ€.
| νμΌ μ΄λ¦ | μ (.) μ¬λΆ |
μ£Όμ μν λ° λμ μ½λ | ν¬ν¨λλ μ£Όμ λ³μ μμ |
|---|---|---|---|
env |
μμ | λμ€μ½λ λ΄μ μμ€ν κ΄λ¦¬ λ° λ¬Όλ¦¬ ꡬλμ μν ν΅μ¬ νκ²½ μ€μ (λ΄ κ΅¬λ μλΉμ€ μ€ν μ μ§μ λ‘λ) | DISCORD_TOKEN, ADMIN_IDS, CHANNEL_ID, LOG_CHANNEL_ID, BASE_PATH, SAVE_DATA_PATH, BACKUP_PATH, MEMORY_RESTART_THRESHOLD |
.env |
μμ | ν°μλ κ²μ μλ² μ»¨ν μ΄λ λ΄λΆ λμ κ·μΉ μ‘°μ (λ컀 μ»΄ν¬μ¦ λΉλ μ 컨ν μ΄λ λ΄λΆλ‘ νκ²½ λ³μ μ£Όμ ) | EXP_RATE, CATCH_RATE, PAL_EGG_DEFAULT_HATCHING_TIME, SERVER_PASSWORD, ADMIN_PASSWORD |
- π‘ μ£Όμμ¬ν: DMμ ν΅ν μ€μ κ΄λ¦¬λ
/μ€μ λ³κ²½λͺ λ Ήμ΄λ κ²μ λ£° μ‘°μ μ λͺ©μ μΌλ‘ μμ(.env) νμΌμ μμ νκ³ λκΈ°νν©λλ€. λ°λ©΄ λ΄ κ΅¬λ ν ν°μ΄λ λμ€μ½λ μ±λ ID λ±μ μμ ν λλ μμ(env) νμΌμ μ§μ μμ ν΄μΌ ν©λλ€.
ν°μλ μλ²λ₯Ό μνν ꡬλνκ³ κ΄λ¦¬νκΈ° μν΄ κ°μ μλ² μμ± μ΄μ μ μλμ μΈν μ μμλλ‘ μ§νν΄μΌ ν©λλ€.
- κ΅¬κΈ ν΄λΌμ°λ νλ«νΌ(GCP)μ μ μνμ¬ μ κ· κ³μ μ λ±λ‘νκ³ λ¬΄λ£ μ²΄ν ν¬λ λ§ λλ ν°μ΄ 쑰건μ νμΈν©λλ€.
- Compute Engine > VM μΈμ€ν΄μ€ λ©λ΄μμ κ°μ μλ² μΈμ€ν΄μ€λ₯Ό νλ μμ±ν©λλ€.
- OS: Ubuntu 22.04 LTS (μ΅μ x86/64 μ΄λ―Έμ§)
- κΆμ₯ νλμ¨μ΄ μ¬μ: e2-standard-4 (4 vCPU, 16GB RAM) μ΄μ
κ²μ ν΄λΌμ΄μΈνΈ μ μμ νμ©νκΈ° μν΄ μΈμ€ν΄μ€ λ€νΈμν¬ μ€μ μ λ€μ κ·μΉμ μΆκ°ν΄μΌ ν©λλ€.
- κ²μ μ μ ν¬νΈ: UDP 8211 (μμ€λ‘λΆν°μ IP λ²μ: 0.0.0.0/0 - μ 체 κ°λ°©)
- μ£Όμ: 보μμ μν΄ RCON ν¬νΈ(TCP 25575) λ° REST API ν¬νΈ(TCP 8212)λ κ΅¬κΈ λ°©νλ²½ μμμ μΈλΆλ‘ λ ΈμΆλμ§ μλλ‘ νμνκ³ λ‘컬 λ΄λΆμμλ§ ν΅μ νλλ‘ μ€μ νμμμ€.
- Discord Developer Portal(https://discord.com/developers/applications)μ μ μνμ¬ New Applicationμ μμ±ν©λλ€.
- μΌμͺ½ Bot νμΌλ‘ μ΄λνμ¬ λ΄ κ³μ μ μμ±νκ³ Tokenμ μμ ν κ³³μ 볡μ¬ν΄ λ‘λλ€. (
DISCORD_TOKEN) - κ°μ Bot ν νλ©΄ νλ¨μ Privileged Gateway Intents μΉμ
μμ λ€μ μΈ κ°μ§ νμ κΆνμ νμ±νν©λλ€.
- Presence Intent
- Server Members Intent
- Message Content Intent
- μΌμͺ½ OAuth2 > URL Generator νμΌλ‘ μ΄λν©λλ€.
- Scopes: bot, applications.commands 체ν¬
- Bot Permissions: Administrator (λλ μ±λ μ½κΈ°/μ°κΈ°, μν μ μ΄ κΆν νμ 체ν¬)
- νλ¨μ μλ μμ±λ URL μ£Όμλ₯Ό 볡μ¬νμ¬ λΈλΌμ°μ μ μ μν λ€ λ³ΈμΈμ΄ κ΄λ¦¬νλ λμ€μ½λ μλ²μ λ΄μ μ΄λν©λλ€.
- MobaXterm μλ¨ λ©λ΄μμ Tools > MobaKeyGen (SSH Key Generator)μ μ€νν©λλ€.
- νλ¨μ Parameters νλͺ©μμ RSAλ₯Ό μ ννκ³ Generate λ²νΌμ ν΄λ¦ν©λλ€.
- μ§ν νμμ€μ΄ μ°¨μ€λ₯΄λ λμ λ§μ°μ€λ₯Ό 무μμλ‘ κ³μ μμ§μ¬ μμμ λμλ₯Ό μμ±ν©λλ€.
- ν€ μμ±μ΄ μλ£λλ©΄ νλ©΄ μ€κ°μ Key comment λΆλΆμ μνλ
<μ¬μ©μ_κ³μ λͺ >μΌλ‘ λ³κ²½ν©λλ€. (μ: gcp-user λ± μμ΄ μλ¬Έμ κΆμ₯) - Save private key λ²νΌμ ν΄λ¦νμ¬ μμ ν λ‘컬 ν΄λμ λΉκ³΅κ° ν€ νμΌ(.ppk)μ μ μ₯ν©λλ€.
- 맨 μ νμ ν μ€νΈ μμ(Public key for pasting into OpenSSH...) μμ μλ κΈ΄ λ¬Έμμ΄ μ 체λ₯Ό 볡μ¬ν©λλ€.
- GCP μ½μμ Compute Engine > VM μΈμ€ν΄μ€ μμΈ νμ΄μ§μμ μλ¨μ μμ λ²νΌμ ν΄λ¦ν©λλ€.
- νλ©΄μ μλλ‘ λ΄λ € SSH ν€ νλͺ©μ μ°Ύμ ν νλͺ© μΆκ°λ₯Ό λλ¦ λλ€.
- μμμ 볡μ¬ν κ³΅κ° ν€ μ 체λ₯Ό μ λ ₯λμ κ·Έλλ‘ λΆμ¬λ£μ΅λλ€. (μ μ λ±λ‘ μ λμ μ λ ₯ν κ³μ λͺ μ΄ κ°μ§λ©λλ€.)
- 맨 μλλ‘ λ΄λ € μ μ₯μ λλ¦ λλ€.
- MobaXterm μΌμͺ½ μ Session λ²νΌμ λλ₯΄κ³ SSH μΈμ μ μΆκ°ν©λλ€.
- Remote host: GCPμμ ν λΉλ κ°μ μλ²μ μΈλΆ IP μ£Όμλ₯Ό μ λ ₯ν©λλ€.
- Specify username: ν€ μμ± μ Key commentμ μ
λ ₯νλ
<μ¬μ©μ_κ³μ λͺ >μ κ·Έλλ‘ μ λ ₯ν©λλ€. - Advanced SSH settings νμ ν΄λ¦ν λ€ Use private key νλͺ©μ 체ν¬νκ³ μ μ₯ν΄ λμλ λΉκ³΅κ° ν€ νμΌ(.ppk)μ λ±λ‘ν©λλ€.
- OKλ₯Ό λλ₯΄λ©΄ κ°μ μλ²μ μνΈ μ λ ₯ μμ΄ μμ νκ² μ΅μ΄ μ μλ©λλ€.
κ°μ μλ² λ°±μ λ°μ΄ν°λ₯Ό κ΅¬κΈ λλΌμ΄λΈ μ격 μ μ₯μμ μ λ‘λνκΈ° μν μ¬μ μμ μ λλ€.
-
Rclone μ€μΉ νλ‘κ·Έλ¨ μ€ν:
sudo -v && curl https://rclone.org/install.sh | sudo bash
-
μ€μ μμ± λ§λ²μ¬ μ§μ :
rclone config
-
μλ μ λ ₯ λ¨κ³μ λ§κ² λ§€νμ μ§νν©λλ€:
- New Remote μ νμ μν΄
nμ λ ₯ ν μν° - μ μ₯μ μλ³ μ΄λ¦ μ
λ ₯:
gdriveμ λ ₯ ν μν° (λμλ¬Έμ μΌμΉ νμ) - μ€ν λ¦¬μ§ μ ν μ ν:
driveλλ λͺ©λ‘μ μλ΄λλ κ΅¬κΈ λλΌμ΄λΈ κ³ μ λ²νΈ μ λ ₯ ν μν° - client_id λ° client_secret: 곡백μΌλ‘ μν° (κΈ°λ³Έκ° μ¬μ©)
- κΆν λ²μ(scope):
1μ λ ₯ ν μν° (μ 체 μ κ·Ό κΆν λΆμ¬) - root_folder_id λ° service_account_file: 곡백μΌλ‘ μν°
- Edit advanced config?:
nμ λ ₯ ν μν° - Use auto-config?:
nμ λ ₯ ν μν° (κ°μ μλ² νκ²½μ΄λ―λ‘ νμn)
- New Remote μ νμ μν΄
-
κ°μΈ PCμμμ μ°λ μΈμ¦ μλ£ μ μ°¨:
- μΈμ€ν΄μ€ νλ©΄μ νμλλ
rclone authorize "drive"ꡬ문μ νμΈν©λλ€. - λ³ΈμΈμ κ°μΈ λ‘컬 PC(Windows λλ Mac) ν°λ―Έλ μ°½μ μ΄κ³
rclone authorize "drive"λͺ λ Ήμ΄λ₯Ό λκ°μ΄ μ λ ₯νμ¬ κ°λν©λλ€. - μ°λμ μνλ κ΅¬κΈ κ³μ λΈλΌμ°μ μΉμΈ μ°½μ΄ μ΄λ¦¬λ©΄ νμ©μ λ§μΉ©λλ€.
- λ‘컬 PC ν°λ―Έλ μμ μΆλ ₯λλ κΈ΄ JSON ν ν° μ½λ(
{"access_token": ...})λ₯Ό μ²μλΆν° λκΉμ§ μ λ°νκ² λ³΅μ¬ν©λλ€. - λ€μ κ΅¬κΈ κ°μ μλ² SSH ν°λ―Έλλ‘ λμμ 볡μ¬ν μ½λ λ¬Έμμ΄ μ 체λ₯Ό
result>μ°½μ λΆμ¬λ£κ³ μν°λ₯Ό ν΄λ¦ν©λλ€. - Shared Drive κ΅¬μ± μ¬λΆ:
nμ λ ₯ ν μν° - μ μ μ
λ ₯ κ²°κ³Ό νμΈ ν
yμ λ ₯μΌλ‘ μ΅μ’ λ§λ¬΄λ¦¬νκ³qλ₯Ό λλ¬ λ§λ²μ¬λ₯Ό μ’ λ£ν©λλ€.
- μΈμ€ν΄μ€ νλ©΄μ νμλλ
-
ν μ€νΈλ‘ μ°λ νμΈ:
rclone lsd gdrive:
GCP ν°λ―Έλ(μ¬μ©μ@νΈμ€νΈλͺ :~$)μ μ μνμλ§μ μ€νν©λλ€. νμΌ μ λ‘λ λ° λ컀 ꡬλ κ³Όμ μμ κΆν μΆ©λμ΄ μΌμ΄λμ§ μλλ‘ ν΄λ μμ κΆμ μ¬μ μ μ§μ νλ κ³Όμ μ λλ€.
mkdir -p ~/palworld
sudo chown -R $USER:$USER ~/palworld
chmod 755 ~/palworld
cd ~/palworld
MobaXterm μΌμͺ½ νμΌ νμ ν¨λμ μλ‘κ³ μΉ¨νμ¬ μμ±λ /home/<μ¬μ©μ_κ³μ λͺ
>/palworld ν΄λλ‘ μ§μ
ν ν μ€λΉλ νμΌλ€κ³Ό ν΄λλ€μ μ
λ‘λν©λλ€.
env νμΌμ λμ€μ½λ λ΄ λ° κ²½λ‘ λ§€νμ μν νμΌμ λλ€. μλ ν¬λ§· μμμ λ§μΆμ΄ μ 보λ₯Ό μ λ ₯νμ¬ μ λ‘λνμμμ€.
# --- λμ€μ½λ μ€μ ---
DISCORD_TOKEN=λ°κΈλ°μ_λμ€μ½λ_λ΄_ν ν°
ADMIN_IDS=κ΄λ¦¬μ_λμ€μ½λ_μ«μ_ID (μ¬λ¬ λͺ
μΌ μ μ½€λ§ κ΅¬λΆ μ: 1234,5678)
CHANNEL_ID=νν©ν_μΆλ ₯μ©_λμ€μ½λ_μ±λ_μ«μ_ID
LOG_CHANNEL_ID=λ°±μ
λ° μμ€ν
_λ‘κ·Έ_μμ μ©_μ±λ_μ«μ_ID
# --- μλ² μ 보 ---
SERVER_NAME=Palworld Korea
SERVER_IP=μμ μ_GCP_μΈλΆ_IP_μ£Όμ
SERVER_PORT=8211
# --- κ²μ λ΄λΆ μ€μ ---
ADMIN_PASSWORD=μνλ_RCON_λΉλ°λ²νΈ
RCON_PORT=25575
REST_API_PORT=8212
# --- κ²½λ‘ μ€μ (<μ¬μ©μ_κ³μ λͺ
> μ리μλ λ³ΈμΈμ μ€μ κ³μ λͺ
μ μ
λ ₯νμμμ€) ---
BASE_PATH=/home/<μ¬μ©μ_κ³μ λͺ
>/palworld
SAVE_DATA_PATH=/home/<μ¬μ©μ_κ³μ λͺ
>/palworld/palworld/Pal/Saved
BACKUP_PATH=/home/<μ¬μ©μ_κ³μ λͺ
>/palworld/backups
# --- μ΄μ μ μ±
---
BACKUP_RETENTION_DAYS=2 # λ°±μ
νμΌ λ³΄μ‘΄ κΈ°κ° (μΌ λ¨μ)
MEMORY_RESTART_THRESHOLD=80 # RAM μ¬μ©λ n% μ΄κ³Ό μ μλ μ¬μμ
RESTART_TIME=04:00 # λ§€μΌ μλ² μλ μ¬μμ μκ° (HH:MM)
λ컀 컨ν μ΄λκ° λ¨μν μλ μ€μΈ κ²κ³Ό μ€μ κ²μ μλ² λ΄λΆ νλ‘μΈμ€κ° μ μμ μΌλ‘ ν΅μ μ λ°μλ€μΌ μ μλ μνμΈ κ²μ λ€λ¦ λλ€. μ΄λ₯Ό μ λ°νκ² κ²μ¦νκΈ° μν΄ RCON ν¬νΈ κ°μ§ κΈ°λ°μ ν¬μ€μ²΄ν¬ μ€μ μ ν¬ν¨νμ¬ κ΅¬μ±ν©λλ€.
version: '3.8'
services:
palworld:
image: thijsvanloef/palworld-server-docker:latest
container_name: palworld-server
stop_grace_period: 30s
ports:
- 8211:8211/udp
- 27015:27015/udp
- 25575:25575/tcp
- 8212:8212/tcp
environment:
- TZ=Asia/Seoul
- PUID=1000
- PGID=1000
env_file:
- .env
volumes:
- ./palworld:/palworld/
healthcheck:
test: ["CMD-SHELL", "timeout 5s bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/25575' || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
- test (μ§λ¨ λͺ
λ Ή): 컨ν
μ΄λ λ΄λΆμ λ‘컬 루νλ°± μ£Όμ(
127.0.0.1)μ RCON μ μ΄ ν¬νΈ(25575) κ°μ TCP μ°κ²°μ΄ μ ν¨νμ§ κ²μ¬ν©λλ€. Bash λ΄μ₯/dev/tcpμ±λμ ν΅ν΄ λΉ λ°μ΄ν°(cat < /dev/null)λ₯Ό μ μ‘νμ¬ λ¦¬μμ€ μλͺ¨λ₯Ό μ΅μνν©λλ€. - interval: 컨ν μ΄λ ꡬλ μ΄ν λ§€ 10μ΄ κ°κ²©μΌλ‘ μ§λ¨ λͺ λ Ήμ λ°λ³΅ μνν©λλ€.
- timeout: μ§λ¨ λͺ λ Ή μ§μ ν 5μ΄ μ΄λ΄μ μ μμ μΈ TCP μλ΅μ΄ λλ¬νμ§ μμΌλ©΄ μ€ν¨ μ²λ¦¬λ©λλ€.
- retries: μ°μ 3ν μ΄μ κ²μ¦ μ€ν¨ μ λ컀 μμ§μ 컨ν
μ΄λμ κ±΄κ° μνλ₯Ό
unhealthyλ‘ μ΅μ’ 격νν©λλ€. - start_period: 컨ν μ΄λ κΈ°λ μ§ν 첫 60μ΄ λμμ μν κ²μ¬κ° μ€ν¨νλλΌλ λμ μ€ν¨ νμμμ λ°°μ νμ¬ μ΄κΈ° λ‘λ© μ€ μ¬μμ 루νμ λΉ μ§λ νμμ λ°©μ§ν©λλ€.
μ
λ‘λκ° μλ£λμλ€λ©΄ ν°λ―Έλ μ°½μμ μλμ° μ€νμΌ μ€λ°κΏ λ¬Έμ(CRLF) μ ν μμ
μ κ±°μΉ λ€ μ€μΉ μ€ν¬λ¦½νΈλ₯Ό ꡬλν©λλ€.
sed -i 's/\r$//' setup.sh env .env
chmod +x setup.sh
./setup.sh
μ€μΉ μ€ν¬λ¦½νΈκ° μμ ν λλλ©΄ μμ κ°λ μ€μΈ λμ€μ½λ λ΄μ΄ sudo μμ΄λ λ컀 λ°±μ
λͺ
λ Ήμ΄(docker exec)λ₯Ό μμ νκ² μ§μν μ μλλ‘ κΆνμ 보μν©λλ€.
sudo usermod -aG docker $USER
newgrp docker
sudo systemctl restart palbot.service
[OFFLINE] (λ컀 컨ν
μ΄λ μ μ§ μν)
β
βΌ (컨ν
μ΄λ κΈ°λ / docker-compose up)
[STARTING] (λ컀 running / ν¬μ€μ²΄ν¬ starting)
β
βΌ (REST API /v1/api/info μ±κ³΅)
[ONLINE] (νν©ν λ
Ήμ κ°±μ λ° μ μ ꡬλ)
β
βββΊ [0λͺ
μ μ§ 15λΆ κ²½κ³Ό / λμ λ°±μ
] ββββββββββββΊ [BACKUP & GDRIVE COPY]
βββΊ [λ©λͺ¨λ¦¬ μκ³μΉ μ΄κ³Ό (80%) / μΏ¨λ€μ΄ 30λΆ] βββββΊ [AUTO REBOOT]
βββΊ [μ€ν API λΉλ λΆμΌμΉ κ°μ§] ββββββββββββββββΊ [UPDATING PENDING]
- μ΄μ€ λ¨κ³ μν κ²μ¦: λ컀 μμ§ μνκ°
startingμ΄λ©΄ λμ보λλ₯ΌSTARTINGμΌλ‘ λ§€ννκ³ μ μμ ν μ€νΈλ₯Ό "λ‘λ© μ€..."μΌλ‘ νκΈ°ν©λλ€. REST API μ μ μλ΅ μONLINEμΌλ‘ μ νλ©λλ€. - λΉλκΈ° λ½(asyncio.Lock) 보νΈ:
refresh_dashboardνΈμΆ μdashboard_lockμ νλνμ¬ λμ κ°±μ λͺ λ Ήμ΄ κ²ΉμΉλλΌλ λμ보λ λ©μμ§κ° μ€λ³΅ λ°νλλ νμμ μ°¨λ¨ν©λλ€. - Fast-Path λ° μκ° μΉμ (Self-Healing):
status.jsonμ μ μ₯λmsg_idλ‘get_partial_messageλ₯Ό μνν΄ 1ν API νΈμΆλ‘ μ¦μ μμ ν©λλ€. λ©μμ§κ° μ μ€λ κ²½μ° μ΅κ·Ό 50κ° λ΄μμ μ€μΊνμ¬ λ³΅κ΅¬νλ©° μ€λ³΅ λμ보λλ μμ μ²λ¦¬ν©λλ€. - μ€μκ° λ©λͺ¨λ¦¬ λͺ¨λν°λ§ λ° 30λΆ μΏ¨λ€μ΄:
psutil.virtual_memory()λ‘ μ μ μ¨μ κ³μ°νλ©° 80% μ΄κ³Ό μ RCON μ μ₯μ κ±°μ³ μ»¨ν μ΄λλ₯Ό μ¬λ°°μΉν©λλ€.last_restart_timeνμμ€ν¬νλ₯Ό λμ‘°νμ¬ μ¬μμ ν 30λΆ(1800μ΄)κ°μ μΆκ° μλ μ¬λΆν μ μ μν©λλ€. - μ§μ μκ° μ€ν μ
λ°μ΄νΈ λμ‘° μ€μΌμ€λ¬: λ§€μΌ
02:00,04:00,09:00,17:00μ μ€ν APIλ₯Ό μ‘°ννλ©° μ΅μ λΉλ λ³κ²½ μ μ μμκ° 0λͺ μ΄λ©΄ μ¦μ μ λ°μ΄νΈλ₯Ό μ€ννκ³ , μ μ κ° μ‘΄μ¬νλ©΄ ν΄μ₯ λκΈ°(update_pending_on_players) λͺ¨λλ‘ μ νν©λλ€.
[κ°μ μλ² λ¬Όλ¦¬ RAM 80% μ μ μ¨ μ΄κ³Ό κ°μ§]
β
βΌ
[μΈκ²μ RCON κ²½κ³ λ°©μ‘] ("Server is stopping for maintenance" 곡μ§)
β
βΌ
[μΈμ΄λΈ νλ¬μ (Force Flush)] (RCON Save 컀맨λ μ€ν λ° μ μ λκΈ°)
β
βΌ
[컨ν
μ΄λ κ°μ μμ μ€μ§ λ° μ¬λ°°μΉ] (docker-compose up -d --force-recreate)
β
βΌ
[REST API ν¬μ€ ν΄λ§ μ€ν] (API μ μ μλ΅ κ°μ§ μκΉμ§ STARTING μν κ³ μ )
β
βΌ
[ONLINE μ ν λ° 30λΆ μΏ¨λ€μ΄ μ€μ ] (νν©ν κ°±μ λ° λμ€μ½λ λ‘κ·Έ μ μ‘ μλ£)
- λμ보λ μ±λ (
CHANNEL_ID): νν©ν λμ보λ μλ² λμ μ λ°μ΄νΈ μλ μ€ν λ²νΌλ§ κ³ μ κ°λλ©λλ€. μ΄ μ±λμ λ°νλλ μ μ΄ κ³΅μ§ λ± μμ λ©μμ§λ 15λΆ(900μ΄) ν μλ μν μμ μ²λ¦¬λ©λλ€. - λ°±μ
/λ‘κ·Έ μ μ© μ±λ (
LOG_CHANNEL_ID): μΈμ΄λΈ λ°±μ κ²°κ³Ό, κ΅¬κΈ λλΌμ΄λΈ μ λ‘λ λ³΄κ³ , λ§λ£ λ°±μ μ 리, λμ€ν¬ μ©λ κ²½κ³ λ±μ κΈ°λ‘μ΄ μꡬ 보κ΄λ©λλ€.
- β μ κΈ° μ κ²/μ
λ°μ΄νΈ 루ν΄: μ κ² 5λΆ μ , 1λΆ μ , 30μ΄ μ RCON λ°©μ‘ λ° κ³΅μ§ μ‘μΆ β RCON
Saveμ μ‘ ν 3~10μ΄ νλ¬μ λκΈ° β 컨ν μ΄λ μ μ§ β λ‘컬 λ° κ΅¬κΈ λλΌμ΄λΈ λ°±μ βdocker-compose pullλ°up -d --force-recreateμ€ν. - β‘ μΈμ΄λΈ λ°±μ
λ° κ΅¬κΈ λλΌμ΄λΈ λ³΅μ¬ λ³΄κ³ : λ‘컬
.tar.gzμμ± μλ£ ν Rclone μ λ‘λ κ²°κ³Όλ₯Ό νλμ ν΅ν© λ©μμ§λ‘ λ°μ‘. - β’ μλ² λμ€ν¬ μ€νμ΄μ€ μ΄κ³Ό μ‘°κΈ° 경보: 12μκ° μ£ΌκΈ°λ‘ λμ€ν¬ μ¬μ©λ₯ μ§λ¨ β 80% μ΄κ³Ό μ μ μ κ²½κ³ μλ² λ μ‘μΆ.
- β£ μ€μ κ° μλ μΌκ΄ λ³κ²½ μ΄μ€ κ²μ¦ (
/μ€μ λ³κ²½): μ λ ₯ ν€ μ ν¨μ± κ²μ¦ β μΉμΈ λ²νΌ ν¨λ μ‘μΆ β μΉμΈ μ μΈμ΄λΈ μ μ₯, λ°±μ , μ¬κΈ°λ μ€ν. - β€ 4μκ° μ κΈ° μ±λ μ λ νμ€ν¬ (
scheduled_4hr_clean_task): 4μκ°λ§λ€channel.pins()νμ± β κ³ μ λ©μμ§ λ° νμ±νλ λμ보λλ₯Ό μ μΈν μμ λ©μμ§ μΌκ΄ μ²μ.
λμ€μ½λ λ΄κ³Όμ 1:1 κ°μΈ λ©μμ§(DM)λ₯Ό μ¬μ©νμ¬ κ°μ μλ²μ κ²μ μ€μ (.env)μ μμ νκ² μ‘°ννκ³ μ§μ κ΅μ²΄ λ° λ°μν μ μμ΅λλ€.
[κ΄λ¦¬μ DM] βββ(".env" / "μ€μ " λ©μμ§ μ μ‘)ββββΊ [PalworldBot]
β
[κ΄λ¦¬μ DM] ββββ(νμ¬ μ μ© μ€μΈ .env νμΌ λ°μ‘)ββββββ
β
βΌ (μμ λ .env νμΌμ DM μ°½μ λλκ·Έ μ€ λλ‘ μ
λ‘λ)
[PalworldBot]
β
βββΊ 1. κΈ°μ‘΄ .envλ₯Ό .env.bakμΌλ‘ μμ λ°±μ
보κ΄
βββΊ 2. μ΄μ μ€μ κ³Ό μ κ· μ€μ λ³κ²½μ (Diff) μ λ° λμ‘° λ° μλ² λ μΆλ ₯
βββΊ 3. RCON "Broadcast" λ° "Save" λͺ
λ Ή μ μ‘ (νλ¬μ μ μ λκΈ°)
βββΊ 4. μμ λ°±μ
(run_backup) μ€ν λ° κ΅¬κΈ λλΌμ΄λΈ μ μ‘
βββΊ 5. docker-compose up -d --force-recreate 무μ€λ¨ μ¬λ°°μΉ
βββΊ 6. μ¬λ°°μΉ μλ¬ λ°μ μ .env.bak νμΌλ‘ μλ λ‘€λ°± (Rollback)
λμ€μ½λ λ΄μ κ°μ μλ²κ° μΌμ§ λ μλ μ€νλλ systemd μλΉμ€λ‘ λ±λ‘λμ΄ μμ΅λλ€.
-
λ΄ μν νμΈ:
sudo systemctl status palbot.service
-
λ΄ μ¬μμ:
sudo systemctl restart palbot.service
λ°±μ μ§ν μν©, Rclone μ°λ μν, μ μμ λ³κ²½ κ°μ§, λ©λͺ¨λ¦¬/λμ€ν¬ κ²½κ³ λ‘κ·Έλ₯Ό λͺ¨λν°λ§ν μ μμ΅λλ€.
-
μ€μκ° λ‘κ·Έ μ§μΌλ³΄κΈ° (μ’ λ£:
Ctrl + C):tail -f /home/<μ¬μ©μ_κ³μ λͺ >/palworld/palworld_bot.log
-
μ€ν μ€μΈ 컨ν μ΄λ λͺ©λ‘ νμΈ:
docker ps
-
κ²μ μλ² κ°μ μ μ§:
cd ~/palworld && docker-compose down
-
κ²μ μλ² μ¬μμ± μ¬κΈ°λ:
cd ~/palworld && docker-compose up -d --force-recreate
λ΄μ μλ²μ μ΄λν λ botκ³Ό applications.commands κΆνμ 체ν¬νλ€λ©΄ λμ€μ½λ μ±λμμ μ¬λμ(/)λ₯Ό μ
λ ₯ν΄ κ΄λ¦¬ λͺ
λ Ήμ΄λ₯Ό νΈμΆν μ μμ΅λλ€. (ADMIN_IDSμ λ±λ‘λ κ΄λ¦¬μ μ μ©)
- /start: μ μ§λ ν°μλ μλ²λ₯Ό μμν©λλ€.
- /stop: μΈκ²μ μλ΄ λ°©μ‘ μ‘μΆ ν μ μ₯μ κ±°μ³ μλ²λ₯Ό μμ νκ² μ μ§ν©λλ€. (μ½ 10μ΄ μμ)
- /restart: μΈκ²μ λ°©μ‘ β μ μ₯μ κ±°μ³ μμ λ°±μ μ μνν ν λ컀 컨ν μ΄λλ₯Ό μ¬λ°°μΉνμ¬ μ¬μμν©λλ€.
- /backup: ν°μλ μΈμ΄λΈ λ°μ΄ν°λ₯Ό μ¦μ μ μ₯μ κ±°μ³ νλ₯΄ μμΆν ν λ‘컬 λ°±μ ν΄λ λ° κ΅¬κΈ λλΌμ΄λΈλ‘ λμ μλ λ°±μ ν©λλ€.
- /clean: μ±λ μλ¨ κ³ μ (Pinned) λ©μμ§ λ° λμ보λλ₯Ό μ μΈν μμ λ©μμ§λ€μ μμ μ 리ν©λλ€.
- /λμ€ν¬: νμ¬ μλ²μ μ 체 λμ€ν¬ μ©λ, μ¬μ© μ€μΈ μ©λ, μ¬μ©λ₯ (%)μ μ€μκ° μλ² λλ‘ μ‘°νν©λλ€.
- /μ€μ λ³κ²½ :
- μ¬μ© μ:
/μ€μ λ³κ²½ query:EXP_RATE=2.0, CATCH_RATE=1.5 .envμ€μ κ°μ λ³κ²½νκ³ λ³κ²½ λ΄μ νμΈ μλ² λ λ° μΉμΈ λ²νΌμ ν΅ν΄ μμ νκ² λ°μ ν μλ²λ₯Ό μ¬μμν©λλ€.
- μ¬μ© μ:
λ²κ·Έ μ κ³ λ° κΈ°λ₯ κ°μ μ μμ GitHub Issueλ₯Ό μ΄μ©ν΄ μ£ΌμΈμ.
μ€μΉ λλ μ΄μ© μ€ λ¬Έμ κ° λ°μν κ²½μ° palworld_bot.log νμΌμ μμΈ λ‘κ·Έ λ΄μμ ν¨κ» 첨λΆν΄ μ£Όμλ©΄ μ μν λΆμμ΄ κ°λ₯ν©λλ€.