Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

데이터베이스 마이그레이션 도구 alembic

서비스를 운영하려다 보니 자연히 데이터베이스 스키마를 관리할 수 있는 마이그레이션 도구가 필요했다. 현재 백엔드에 Node.js를 사용하고 있기는 하지만 마이그레이션 도구도 Node.js일 필요는 없다고 생각했다. Node.js는 데이터베이스쪽이 좀 약하다고 생각하는 편인데 데이터베이스 라이브러리도 쓸만한 게 있고 드라이버도 꽤 안정적으로 개발되고 있지만, 역사나 인구가 적다 보니 마이그레이션 도구 같은 부분은 선택의 폭이 높지 않다. 그리고 마이그레이션 도구를 애플리케이션 내에서 자동으로 돌릴 게 아니라 별로도 실행할 것이므로 Node.js에 국한하지 않고 쓸만한 도구를 찾기 시작했다.

Alembic

주변 사람들이 어떤 마이그레이션 도구를 사용하는지 수소문해서 알게 된 도구가 Alembic이다. Alembic은 SQLAlchemy 기반의 마이그레이션 도구로 Python을 사용하지만, 별도로 사용할 수 있으므로 프로젝트가 꼭 파이썬이거나 SQLAlchemy를 사용할 필요는 없다. 문서도 잘 작성되어 있고 사용법이 간단해서 금새 익힐 수 있다.

설치는 pip install alembic로 간단히 설치할 수 있다. 물론 Python이나 pip는 설치되어 있어야 한다. 만약 No module named psycopg2라는 오류가 발생하면 sudo easy_install psycopg2로 설치해야 한다.

초기화

설치하면 커맨드라인에서 alembic 명령어를 사용할 수 있고 다음 명령어로 초기화를 할 수 있다.

$ alembic init myproject

alembic으로 초기화를 하면 다음과 같은 파일들이 생성된다.

├── alembic.ini
└── myproject
    ├── README
    ├── env.py
    ├── env.pyc
    ├── script.py.mako
    └── versions

alembic.ini이 설정 파일이므로 여기서 데이터베이스 설정을 할 수 있다.

[alembic]
# path to migration scripts
script_location = myproject
sqlalchemy.url = driver://user:pass@localhost/dbname

...

위 파일에서 sqlalchemy.url부분에 사용하는 데이터베이스를 지정하면 된다.

마이그레이션 관리

alembic은 리비전을 사용해서 데이터베이스 스키마를 관리한다.

$ alembic revision -m "create user table"
  Generating /myproject/versions/2987e9c41e5e_create_user_table.py ... done

위처럼 새로운 리비전을 추가하면 versions폴더 아래 새로운 마이그래이션 파일이 생기고 이 파일은 다음과 같이 생겼다.

"""create user table

Revision ID: 2987e9c41e5e
Revises:
Create Date: 2015-05-10 22:10:18.235580

"""

# revision identifiers, used by Alembic.
revision = '2987e9c41e5e'
down_revision = None
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa


def upgrade():
  pass


def downgrade():
  pass

upgrade()는 마이그래이션 버전을 적용할 때 실행되고 downgrade()는 적용한 마이그래이션을 취소할 때 실행된다. 처음에는 아무 동작도 하지 않게 pass로 되어 있으므로 여기에 테이블 추가나 칼럼변경 같은 코드를 작성해야 한다.

def upgrade():
    op.create_table(
        'users',
        sa.Column('id', sa.Integer, primary_key=True),
        sa.Column('name', sa.String(50), nullable=False)
    )

def downgrade():
    op.drop_table('account')

alembic이 SQLAlchemy를 사용하므로 위처럼 sa객체를 이용해서 마이그레이션 코드를 작성할 수 있다. 애플리케이션도 SQLAlchemy를 사용하고 있다면 이 마이그레이션 코드도 자동으로 생성하는 등의 장점이 있는 것 같은데 그렇지 않다면 직접 작성해야 한다. SQLAlchemy를 사용해 봤다면 쉽게 작성할 수 있지만, 나처럼 SQLAlchemy를 사용해 본 적이 없다면 SQLAlchemy로 코드를 작성하는 것이 좀 힘 들 수 있다.

def upgrade():
    op.execute("""
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY,
            name VARCHAR(50) NOT NULL
        )
    """)

def downgrade():
    op.execute("""
        DROP TABLE users
    """)

위처럼 op.execute()를 사용하면 SQL을 사용할 수 있으므로 SQLAlchemy에 익숙하지 않다면 차라리 이 방법이 더 편한다.

$ alembic upgrade head
INFO  [alembic.migration] Context impl PostgresqlImpl.
INFO  [alembic.migration] Will assume transactional DDL.
INFO  [alembic.migration] Running upgrade  -> 2987e9c41e5e, create user table

이제 alembic upgrade head을 실행하면 위처럼 upgrade()함수가 실행되어 users 테이블이 생성된다. 데이터베이스에는 alembic_version라는 테이블이 생성되고 여기서 현재 적용된 리비전을 관리하게 된다.

$ alembic revision -m "add age column"
  Generating /myproject/versions/4c32125f4f5a_add_age_column.py ... done

4c32125f4f5a_add_age_column.py 파일을 다음과 같이 수정해서 스키마 변경을 계속 관리할 수 있다.

"""add age column

Revision ID: 4c32125f4f5a
Revises: 40686f5279e2
Create Date: 2015-05-11 00:20:47.794986

"""

# revision identifiers, used by Alembic.
revision = '4c32125f4f5a'
down_revision = '40686f5279e2'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    op.execute("""
        ALTER TABLE users
        ADD age INTEGER
    """)


def downgrade():
    op.execute("""
        ALTER TABLE users
        DROP COLUMN age
    """)

이 파일을 보면 아까 추가한 리비전이 지정되어 있음을 알 수 있고 여기서는 users테이블에 새로운 칼럼을 추가했다.

$ alembic upgrade head
INFO  [alembic.migration] Context impl PostgresqlImpl.
INFO  [alembic.migration] Will assume transactional DDL.
INFO  [alembic.migration] Running upgrade 40686f5279e2 -> 4c32125f4f5a, add age column

이런 식으로 스키마에 변경사항이 생길 때마다 마이그레이션 파일을 추가하고 적용할 수 있으며 개발 데이터베이스나 프로덕션 데이터베이스에서도 쉽게 적용하고 관리할 수 있다.

$ alembic history
40686f5279e2 -> 4c32125f4f5a (head), add age column
<base> -> 40686f5279e2, create user table

alembic history 명령어를 사용하면 스키마 히스토리도 확인할 수 있다. 러닝 커브도 높지 않고 간단히 사용할 수 있어서 현재 만족하며 사용 중이다.

2015/05/11 01:17 2015/05/11 01:17