Python/FastAPI

FastAPI settings 관리

코닝구 2020. 10. 5. 00:25

대부분의 웹 개발은 웹앱에 필요한 구성(Configuration)정보를 필요로 합니다. 그리고 그 구성 중에서 secret keys, database credentials 등은 각각의 환경마다 정보가 다를 수 있습니다. 장고 프레임워크에서는 이를 위한 모범사례로 환경마다 파일을 구분하고 Git에서 관리하는 것이었습니다. 다만, 이럴 경우 민감한(Sensitive) 정보들에 대해서는 환경 변수로 별도 관리되어야 합니다.

FastAPI에서는 내부적으로 pydantic 패키지를 사용하는데 이 패키지가 제공하는 모듈로 환경 변수를 읽어 settings를 관리합니다. 자 그럼 pydantic를 활용하여 간단한 예제를 보겠습니다.

from fastapi import FastAPI
from pydantic import BaseSettings


class Settings(BaseSettings):
    APP_ENV: str = 'dev'


settings = Settings()
app = FastAPI()


@app.get("/")
async def root():
    return {"app_env": settings.APP_ENV
  1. pydantic.BaseSettings를 import 하여 서브 클래스를 만듭니다.
  2. 서브 클래스에서 type hints, default 값으로 클래스 속성(class attribute)을 선언합니다.
  3. 서브 클래스인 Settings()를 인스턴스화 합니다.
  4. settings로 클래스 속성을 호출하여 리소스 내부에서 사용하면 됩니다.

다음은 환경 변수를 앱에 전달하여 APP_ENV 값을 치환해보도록 하겠습니다.

아래는 리눅스의 export 명령어로 환경 변수를 정의하여 치환합니다.

$ export APP_ENV=test
$ uvicorn main:app --reload

다른 방법으로 run server 시 환경 변수를 전달하여 치환합니다.

$ APP_ENV=test uvicorn main:app --reload

위 두 방법 모두 APP_ENV가 test로 치환됩니다.


Dotenv (.env) 지원.

Dotenv은 환경변수를 파일로 관리하여 쉽게 사용할 수 있게 하는 일반적인 패턴입니다. 이를 활용하면 환경별로 Config를 관리할 수 있습니다. pydantic에서는 외부 라이브러리를 사용하여 이를 지원합니다. 외부 라이브러리는 아래 커맨드로 설치해줍니다.

$  pip install pydantic[dotenv]

.env 파일을 생성하여 아래처럼 환경변수를 정의합니다.

APP_ENV=prod
REDIS_HOST=localhost
...

다음으로 Settings 클래스를 수정합니다.

from fastapi import FastAPI
from pydantic import BaseSettings


class Settings(BaseSettings):
    APP_ENV: str = 'dev'

    class Config:
        env_file = '.env'


settings = Settings()
app = FastAPI()


@app.get("/")
async def root():
    return {"app_env": settings.APP_ENV}

Settings의 inner class로 Config를 선언하고 .env 파일이 위치한 값으로 env_file을 정의합니다. 

환경별로 .env 파일 분리하기.

제품을 서비스함에 있어서 통상적으로 다수의 환경을 필요로 합니다. 예를 들면, production, staging, development, test 환경들입니다. Dotenv를 활용하면 환경별로 Config를 쉽게 관리할 수 있습니다. 아래는 이를 위한 두 가지 방법입니다. 두 가지 방법 모두 환경별로 .env(prod.env, staging.env, test.env) 파일을 생성하는 것은 동일합니다. 시작하기 전에 ENV_STATE 환경변수를 우선 정의합니다.

$ export ENV_STATE=prod

1. 단일 클래스에서 _env_file kwargs를 사용하여 환경파일 정의.

import os

from pydantic import BaseSettings


class Settings(BaseSettings):
    APP_ENV: str = 'dev'

    class Config:
        env_file = '.env'


settings = Settings(_env_file=f'{os.getenv("ENV_STATE")}.env')

 

2. 팩토리 메소드 패턴 사용.

from fastapi import FastAPI
from pydantic import BaseSettings


class GlobalSettings(BaseSettings):
    ENV_STATE: str = 'dev'
    APP_ENV: str = 'dev'

    class Config:
        env_file = '.env'


class DevSettings(GlobalSettings):
    class Config:
        env_file = 'dev.env'


class ProdSettings(GlobalSettings):
    class Config:
        env_file = 'prod.env'


class FactorySettings:
    @staticmethod
    def load():
        env_state = GlobalSettings().ENV_STATE
        if env_state == 'dev':
            return DevSettings()
        elif env_state == 'prod':
            return ProdSettings()


settings = FactorySettings.load()

※ Dotenv 파일과 환경변수에 동일한 변수가 선언되었다면 Dotenv에서 load된 값보다 환경변수가 항상 우선시 됩니다.

 

'Python > FastAPI' 카테고리의 다른 글

FastAPI 살펴보기  (0) 2020.09.20