์๊ธฐ๋ฅ #49
์งํ์ค์๊ธฐ๋ฅ #44: ๐ Mangement Cloud server FastAPI ํ๋ก์ ํธ
๐ฅ Epic 1 - Task 4 ๏ธ๐๏ธ PostgreSQL ์ฐ๊ฒฐ ๋ฐ SQLAlchemy ๋ชจ๋ธ ์์ฑ
0%
์ค๋ช
- ๋ชฉ์ฐจ
- ๐ฅ Epic 1 โ Task 4
๐ฅ Epic 1 โ Task 4¶
๐๏ธ PostgreSQL ์ฐ๊ฒฐ ๋ฐ SQLAlchemy ๋ชจ๋ธ ์์ฑ (์ค๋ช ํ Markdown)¶
๐ ์์ ๊ฐ์¶
FastAPI mgmt ์๋ฒ๊ฐ ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์กฐํํ ์ ์๋๋ก
PostgreSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ค์ ํ๊ณ ,
๊ธฐ์กด Django ORM ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก SQLAlchemy ๋ชจ๋ธ์ ์์ฑํ๋ ๋จ๊ณ์ด๋ค.
์ด ์์
์ ์ดํ ๋ชจ๋ API ๊ฐ๋ฐ, MQTT worker ๋ฐ์ดํฐ ์ฒ๋ฆฌ, reverse SSH ์ํ ์ ์ฅ ๋ฑ
์ ์ฒด ์์คํ
์ ๋ฐ์ดํฐ ํ๋ฆ์ ์ฑ
์์ง๋ ํต์ฌ ๊ธฐ๋ฐ์ด ๋๋ค.
๐ฏ ์์ ๋ชฉ์ ¶
-
FastAPI ์๋ฒ์์ PostgreSQL์ ์์ ์ ์ผ๋ก ์ฐ๊ฒฐํ ์ ์๋๋ก ํ๋ค.
-
SQLAlchemy ORM์ ์ฌ์ฉํด Django ๋ชจ๋ธ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ์ฌํํ๋ค.
-
JSONField โ JSONB ๋ณํ์ ํฌํจํ์ฌ PostgreSQL์ ๊ณ ๊ธ ๊ธฐ๋ฅ์ ํ์ฉํ๋ค.
-
DB ์ฐ๊ฒฐ ๋ชจ๋(db.py)์ ํ์คํํ์ฌ ๋ชจ๋ ๋ผ์ฐํฐ/worker์์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
-
ํฅํ ๋ง์ด๊ทธ๋ ์ด์ ๋ฐ ์คํค๋ง ํ์ฅ์ ๋๋นํ ๊ตฌ์กฐ๋ฅผ ๋ง๋ จํ๋ค.
๐ ๏ธ ์งํ ๋ด์ฉ¶
๐๏ธ 1. PostgreSQL ์ฐ๊ฒฐ ์ค์ ํ์ผ ์์ฑ (app/db.py)¶
FastAPI์์ DB ์ฐ๊ฒฐ์ ๋ด๋นํ๋ ๋ชจ๋์ ์์ฑํ๋ค.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
DATABASE_URL = "postgresql://postgres:password@localhost:5432/mgmt"
engine = create_engine(
DATABASE_URL,
pool_pre_ping=True,
echo=False
)
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base = declarative_base()
๐ ์ค๊ณ ํฌ์ธํธ
-
pool_pre_ping=True โ DB ์ฐ๊ฒฐ ๋๊น ๋ฐฉ์ง
-
autocommit=False โ ํธ๋์ญ์ ์์ ์ฑ ํ๋ณด
-
declarative_base() โ SQLAlchemy ๋ชจ๋ธ์ ๊ธฐ๋ฐ ํด๋์ค
๐๏ธ 2. Django ๋ชจ๋ธ โ SQLAlchemy ๋ชจ๋ธ ๋ณํ¶
๊ธฐ์กด Django ๋ชจ๋ธ์ ๋ถ์ํ์ฌ SQLAlchemy ๋ชจ๋ธ๋ก ๋ณํํ๋ค.
์๋๋ Park์ ServerMaster ๋ชจ๋ธ์ SQLAlchemy๋ก ์ฎ๊ธด ์์์ด๋ค.
from sqlalchemy import Column, String, DateTime
from datetime import datetime
from app.db import Base
class ServerMaster(Base):
__tablename__ = "server_master"
hostname = Column(String(100), primary_key=True)
alias = Column(String(100))
version = Column(String(20))
created_at = Column(DateTime, default=datetime.utcnow)
๐ ์ค๊ณ ํฌ์ธํธ
-
Django์ auto_now_add=True โ SQLAlchemy์์๋ default=datetime.utcnow
-
PK, max_length, null ์ต์ ๊ทธ๋๋ก ์ ์ง
-
ํ ์ด๋ธ๋ช ์ Django์ ๋์ผํ๊ฒ ์ ์งํ์ฌ ๋ง์ด๊ทธ๋ ์ด์ ์ฉ์ด
๐๏ธ 3. JSONField โ JSONB ๋ณํ¶
PostgreSQL์ ๊ฐ๋ ฅํ JSONB ํ์ ์ ์ฌ์ฉํ๋ค.
์: ServerStatus์ CPU/RAM ํ์คํ ๋ฆฌ
from sqlalchemy.dialects.postgresql import JSONB
cpu_history = Column(JSONB, default=list)
ram_history = Column(JSONB, default=list)
๐ ์ค๊ณ ํฌ์ธํธ
-
JSONB๋ ์ธ๋ฑ์ฑ ๊ฐ๋ฅ
-
๋ฐฐ์ด ๊ฒ์ ๋ฐ ๋ถ๋ถ ์ ๋ฐ์ดํธ ๊ฐ๋ฅ
-
Django JSONField๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๊ณ ์์ ์
๐๏ธ 4. ๊ด๊ณ(OneToOne, ForeignKey) ๋ณํ¶
Django์ OneToOneField โ SQLAlchemy์์๋ ForeignKey + relationship ์กฐํฉ์ผ๋ก ๊ตฌํํ๋ค.
์:
from sqlalchemy import Column, Integer, Float, ForeignKey
from sqlalchemy.orm import relationship
server = Column(String(100), ForeignKey("server_master.hostname"), primary_key=True)
master = relationship("ServerMaster", back_populates="status")
๊ทธ๋ฆฌ๊ณ ServerMaster์๋ ๊ด๊ณ๋ฅผ ์ถ๊ฐํ๋ค:
status = relationship("ServerStatus", uselist=False, back_populates="master")
๐ ์ค๊ณ ํฌ์ธํธ
-
uselist=False โ OneToOne ๊ด๊ณ
-
Django์ related_name โ SQLAlchemy์ back_populates
๐๏ธ 5. DB ํ ์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ ์์ฑ¶
์ด๊ธฐ ํ ์ด๋ธ ์์ฑ์ ์ํ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ๋ค.
scripts/migrate.sh:
#!/bin/bash
python -c "from app.db import Base, engine; Base.metadata.create_all(bind=engine)"
๐๏ธ 6. DB ์ฐ๊ฒฐ ํ ์คํธ¶
FastAPI ๋ผ์ฐํฐ์์ DB ์ฐ๊ฒฐ์ด ์ ์์ธ์ง ํ ์คํธํ๋ค.
from fastapi import APIRouter, Depends
from app.db import SessionLocal
router = APIRouter()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.get("/db-test")
def db_test(db=Depends(get_db)):
return {"status": "ok"}
๐ ์ฐ์ถ๋ฌผ¶
-
PostgreSQL ์ฐ๊ฒฐ ๋ชจ๋ (app/db.py)
-
SQLAlchemy ๋ชจ๋ธ 4์ข (ServerMaster, ServerStatus, ServerControl, ServerDetail)
-
JSONB ํ๋ ์ ์ฉ
-
๊ด๊ณ(OneToOne) ๋ณํ ์๋ฃ
-
DB ํ ์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ ์์ฑ
-
/db-test API๋ก ์ฐ๊ฒฐ ๊ฒ์ฆ ์๋ฃ
ํ์ํ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.