Postgres
Postgres 설정을 위해서 docker-compose.yml 파일에 새로운 서비스를 명세해야합니다.
그리고 asyncpg 파이썬 라이브러리 설치도 해야합니다.
project 디렉토리 아래 db 디렉토리를 만들고 그 안에 create.sql 파일을 생성하며 아래와 같이 명세합니다.
CREATE DATABASE web_dev;
CREATE DATABASE web_test;
Dokcerfile을 같은 디렉토리에서 만들어 줍니다.
# pull official base image
FROM postgres:16
# run create.sql on init
ADD create.sql /docker-entrypoint-initdb.d
DB 컨테이너의 `docker-entrypoint-initdb.d` 디렉토리에 `create.sql`파일을 넣어두면 이 파일을 실행을 통해서 초기화가 수행됩니다.
`web-db`라는 서비스 명을 명세해줍니다.
version: '3.8'
services:
web:
build: ./project
command: uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 8000
volumes:
- ./project:/usr/src/app
ports:
- 8004:8000
environment:
- ENVIRONMENT=dev
- TESTING=0
- DATABASE_URL=postgres://postgres:postgres@web-db:5432/web_dev # new
- DATABASE_TEST_URL=postgres://postgres:postgres@web-db:5432/web_test # new
depends_on: # new
- web-db
# new
web-db:
build:
context: ./project/db
dockerfile: Dockerfile
expose:
- 5432
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
web-db 컨테이너가 가동되면 5432 포트를 통해서 다른 포트와 통신 할 수 있습니다.
`entrypoint.sh`파일에서 web-db라는 컨테이너의 5432 포트를 통해서 정상적으로 PostgreSQL이 시작되었는지 확인 할 수 있습니다.
#!/bin/sh
echo "Waiting for postgres..."
while ! nc -z web-db 5432; do
sleep 0.1
done
echo "PostgreSQL started"
exec "$@"
app컨테이너의 Dockerfile을 아래와 같이 명세해줍니다.
# pull official base image
FROM python:3.12.1-slim-bookworm
# set working directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install system dependencies
RUN apt-get update \
&& apt-get -y install netcat-traditional gcc postgresql \
&& apt-get clean
# install python dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# add app
COPY . .
# add entrypoint.sh
COPY ./entrypoint.sh .
RUN chmod +x /usr/src/app/entrypoint.sh
# run entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
asyncpg 라이브러리를 requirements.txt에 추가해줍니다.
asyncpg==0.29.0
config.py 모듈에서 Settings클래스의 변수로 databse_url을 추가해줍니다.
# project/app/config.py
import logging
from functools import lru_cache
from pydantic import AnyUrl
from pydantic_settings import BaseSettings
log = logging.getLogger("uvicorn")
class Settings(BaseSettings):
environment: str = "dev"
testing: bool = 0
database_url: AnyUrl = None
@lru_cache()
def get_settings() -> BaseSettings:
log.info("Loading config settings from the environment...")
return Settings()
앱, 디비 컨테이너 두개를 기동하도록 해보겠습니다.
$ chmod +x project/entrypoint.sh
$ docker-compose up -d --build
web 서비스의 로그를 확인해 보겠습니다.
$docker-compose logs web
web_1 | Waiting for postgres...
web_1 | PostgreSQL started
web_1 | INFO: Will watch for changes in these directories: ['/usr/src/app']
web_1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
web_1 | INFO: Started reloader process [1] using statreload
web_1 | INFO: Started server process [37]
web_1 | INFO: Waiting for application startup.
web_1 | INFO: Application startup complete.
이전 `chmod +x` 명령어를 사용하였지만 환경에 따라서 chmod 755, 777 을 사용해야 할 수 있습니다.
psql을 통해서 DB에 접속 해보겠습니다.
$ docker-compose exec web-db psql -U postgres
postgres=# \c web_dev
postgres=# \q
Tortoise ORM
여러 비동기 ORM중 Tortoise ORM을 사용해 보겠습니다.
우선 requirements.txt 파일에 라이브러리와 버전을 명세합니다.
tortoise-orm==0.20.0
app 디렉토리 아래 models 디렉토리를 만들고 초기화 파일인 __init__.py 파일과 tortoise.py를 만듭니다.
그리고 TextSummary 클래스를 명세해 줍니다.
# project/app/models/tortoise.py
from tortoise import fields, models
class TextSummary(models.Model):
url = fields.TextField()
summary = fields.TextField()
created_at = fields.DatetimeField(auto_now_add=True)
def __str__(self):
return self.url
main.py 모듈에서 register_tortoise 헬퍼 메소드를 사용하여 Tortoise 구성과 해제를 하도록 하겠습니다.
# project/app/main.py
import os
from fastapi import FastAPI, Depends
from tortoise.contrib.fastapi import register_tortoise
from app.config import get_settings, Settings
app = FastAPI()
register_tortoise(
app,
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=True,
add_exception_handlers=True,
)
@app.get("/ping")
async def pong(settings: Settings = Depends(get_settings)):
return {
"ping": "pong!",
"environment": settings.environment,
"testing": settings.testing
}
sanity check하겠습니다.
$ docker-compose up -d --build
textsummary 테이블이 정상적으로 생성되었는지 확인합니다.
$ docker-compose exec web-db psql -U postgres
psql (16.1)
Type "help" for help.
postgres=# \c web_dev
You are now connected to database "web_dev" as user "postgres".
web_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | textsummary | table | postgres
(1 row)
web_dev=# \q
지금까지 필요한 기능 개발을 위한 모듈 생성과 코드를 구성하였다면 프로젝트 트리 구조는 아래와 같습니다.
├── .gitignore
├── docker-compose.yml
└── project
├── .dockerignore
├── Dockerfile
├── app
│ ├── __init__.py
│ ├── config.py
│ ├── main.py
│ └── models
│ ├── __init__.py
│ └── tortoise.py
├── db
│ ├── Dockerfile
│ └── create.sql
├── entrypoint.sh
└── requirements.txt
마이그레이션
Tortoise는 Aerich을 통하여 데이터베이스 마이그레이션을 지원합니다.
우선 Aerich이 schema관리를 하도록 하기 위해서 기존 컨테이너 볼륨을 삭제하도록 하겠습니다.
$ docker-compose down -v
기존에는 서버 기동시 자동으로 스키마가 생성되도록 하였는데요. 자동으로 생성되는 것을 방지하고자 register_tortoise 헬퍼 함수를 수정하겠습니다.
register_tortoise(
app,
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=False, # updated
add_exception_handlers=True,
)
이미지를 다시 빌드하고 컨테이너를 돌려보겠습니다.
$ docker-compose up -d --build
textsummary 테이블이 생성되지 않아야 합니다. 확인해보세요.
$ docker-compose exec web-db psql -U postgres
psql (16.1)
Type "help" for help.
postgres=# \c web_dev
You are now connected to database "web_dev" as user "postgres".
web_dev=# \dt
Did not find any relations.
web_dev=# \q
requirements 파일에 aerich라이브러리 버전과 이름을 명세합니다.
aerich==0.7.2
컨테이너를 다시 빌드합니다.
$ docker-compose up -d --build
Aerich을 사용하려면 Tortoise 설정이 필요합니다. app디렉토리 아래 db.py 파일을 작성합니다.
# project/app/db.py
import os
TORTOISE_ORM = {
"connections": {"default": os.environ.get("DATABASE_URL")},
"apps": {
"models": {
"models": ["app.models.tortoise", "aerich.models"],
"default_connection": "default",
},
},
}
aerich 사용이 가능합니다. 초기화 명령어를 실행합니다.
$ docker-compose exec web aerich init -t app.db.TORTOISE_ORM
해당 명령어를 실행하면 project/pyproject.toml 파일이 생성 됩니다.
[tool.aerich]
tortoise_orm = "app.db.TORTOISE_ORM"
location = "./migrations"
src_folder = "./."
첫 마이그레이션을 만들어 보겠습니다.
$ docker-compose exec web aerich init-db
Success create app migrate location migrations/models
Success generate schema for app "models"
그렇다면 아래 마이그레이션 결과를 migrations/models에서 볼 수 있습니다.
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
CREATE TABLE IF NOT EXISTS "textsummary" (
"id" SERIAL NOT NULL PRIMARY KEY,
"url" TEXT NOT NULL,
"summary" TEXT NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "aerich" (
"id" SERIAL NOT NULL PRIMARY KEY,
"version" VARCHAR(255) NOT NULL,
"app" VARCHAR(100) NOT NULL,
"content" JSONB NOT NULL
);"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
"""
실제 psql에서 테이블이 생성 되었는지 확인 할 수 있습니다.
$ docker-compose exec web-db psql -U postgres
psql (16.1)
Type "help" for help.
postgres=# \c web_dev
You are now connected to database "web_dev" as user "postgres".
web_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | aerich | table | postgres
public | textsummary | table | postgres
(2 rows)
web_dev=# \q
'프로그래밍 언어 > 파이썬' 카테고리의 다른 글
FastAPI를 이용한 TDD 개발 - 5 (0) | 2024.03.14 |
---|---|
FastAPI를 이용한 TDD 개발 - 4 (0) | 2024.03.14 |
FastAPI를 이용한 TDD 개발 - 2 (0) | 2024.03.11 |
FastAPI를 이용한 TDD 개발 - 1 (0) | 2024.03.10 |
FastAPI를 이용한 TDD 개발 (0) | 2024.03.10 |