본문 바로가기
MLOps/Docker

[Docker] Dockerfile 작성 및 실행

by Toritol 2023. 12. 15.
728x90

 

이번 글에서는 예제를 통해 Dockerfile을 작성하고 실행하는 방법에 대해 알아보도록 하겠습니다. 예제는 https://mlops-for-mle.github.io/tutorial/을 학습하며 작성하였음을 미리 알려드립니다. 우선 예제의 시나리오는 PostgreSQL container는 이미 존재하는 상황이고, data_generator.py을 실행할 수 있는 container를 생성하여 iris 데이터를 PostgreSQL DB에 입력하는 것입니다.

 

data_generator.py 코드는 다음과 같습니다. 해당 코드는 python을 통해 iris 데이터를 DB에 입력하는 코드입니다. 자세한 설명은 넘어가도록 하겠습니다.

# data_generator.py
import time
from argparse import ArgumentParser

import pandas as pd
import psycopg2
from sklearn.datasets import load_iris


def get_data():
    X, y = load_iris(return_X_y=True, as_frame=True)
    df = pd.concat([X, y], axis="columns")
    rename_rule = {
        "sepal length (cm)": "sepal_length",
        "sepal width (cm)": "sepal_width",
        "petal length (cm)": "petal_length",
        "petal width (cm)": "petal_width",
    }
    df = df.rename(columns=rename_rule)
    return df


def create_table(db_connect):
    create_table_query = """
    CREATE TABLE IF NOT EXISTS iris_data (
        id SERIAL PRIMARY KEY,
        timestamp timestamp,
        sepal_length float8,
        sepal_width float8,
        petal_length float8,
        petal_width float8,
        target int
    );"""
    print(create_table_query)
    with db_connect.cursor() as cur:
        cur.execute(create_table_query)
        db_connect.commit()


def insert_data(db_connect, data):
    insert_row_query = f"""
    INSERT INTO iris_data
        (timestamp, sepal_length, sepal_width, petal_length, petal_width, target)
        VALUES (
            NOW(),
            {data.sepal_length},
            {data.sepal_width},
            {data.petal_length},
            {data.petal_width},
            {data.target}
        );
    """
    print(insert_row_query)
    with db_connect.cursor() as cur:
        cur.execute(insert_row_query)
        db_connect.commit()


def generate_data(db_connect, df):
    while True:
        insert_data(db_connect, df.sample(1).squeeze())
        time.sleep(1)


if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument("--db-host", dest="db_host", type=str, default="localhost")
    args = parser.parse_args()

    db_connect = psycopg2.connect(
        user="myuser",
        password="mypassword",
        host=args.db_host,
        port=5432,
        database="mydatabase",
    )
    create_table(db_connect)
    df = get_data()
    generate_data(db_connect, df)

 

다음은 해당 python 파일을 실행하기 위한 Dockerfile을 작성해 보겠습니다. Dockerfile 내용은 아래와 같습니다.

FROM amd64/python:3.9-slim

RUN apt-get update && apt-get install -y \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /usr/app

RUN pip install -U pip &&\
    pip install scikit-learn pandas psycopg2-binary

COPY data_generator.py data_generator.py

ENTRYPOINT ["python", "data_generator.py", "--db-host"]
# Change CMD to solve host finding error
CMD ["localhost"]

 

  • 1) FROM
    - base가 되는 image를 설정하는 부분
    - 해당 코드에서는 python 3.9 버전을 설치하고, 일반적인 python보다 가벼운 slim image를 사용
  • 2) RUN
    - image를 생성할 때 실행할 코드를 작성하는 부분
    - apt-get : low-level 패키지 매니저로, 먼저 update를 진행하고 설치 과정에서의 확인 프롬프트에서 자동으로 y를 선택
    - postgresql-client : psql을 실행하기 위해 설치

    - rm -rf /var/lib/apt/lists/* : 해당 경로는 apt 패키지 관리 시스템이 사용하는 캐시 및 리스트 파일들이 위치한 디렉토리로, 해당 경로를 삭제하여 image 크기를 줄이고 불필요한 캐시를 방지
  • 3) WORKDIR
    - 작업 경로를 지정하는 부분으로, 이후 작업은 모두 해당 경로에서 진행
    - 지정한 경로가 없을 경우 자동으로 경로 생성
  • 4) RUN
    - pip install -U pip : python 패키지 관리 매니저로, 먼저 update 진행
    - pip install scikit-learn pandas psycopg2-binary : python 내 필요한 패키지 설치
  • 5) COPY
    - 파일 혹은 폴더를 생성하는 image에 복사하는 부분
    - 절대 경로를 사용하지 않을 경우, 위에서 설정한 작업 경로에 복사
  • 6) ENTRYPOINT
    - 생성한 image를 바탕으로 container를 실행할 때, 진행하고자 하는 프로세스 작성
    - space를 기준으로 명령어를 구분하여 작성
  • 7) CMD
    - ENTRYPOINT에서 실행되는 코드에 argument를 전달

 

해당 Dockerfile을 바탕으로 image를 build하는 명령어는 아래와 같습니다. -t 옵션은 image의 tag를 설정하는 부분이고, 마지막에 위치한 . 은 Dockerfile이 현재 경로에 위치한 것을 의미합니다.

$ docker build -t data-generator .

 

생성된 image를 그대로 실행할 경우, DB에 연결되지 않는다는 오류가 발생할 것입니다. 이는 이전에 생성한 PostgreSQL container와 지금 생성한 python 실행 container가 연결되어있지 않기 때문입니다. 이를 위해서는 두 container를 위한 network를 생성해줘야 합니다.

 

다음과 같이 network를 생성하고 PostgreSQL 서버에 연결해줍니다. 그리고 다음과 같은 명령어로 image를 실행하면, 정상적으로 작동하게 됩니다. 여기서 알아두면 좋은 점은 Dockerfile을 작성할 때 마지막 CMD에서 "localhost"를 지정하였지만, "postgre-server"와 같이 다른 서버를 입력해 주면 argument가 바뀌어 전달됩니다.

$ docker network create my-network

$ docker network connect my-network postgres-server

$ docker run -d \
  --name data-generator \
  --network "my-network" \
  data-generator "postgres-server"

 

실무에서는 이런 방식으로 하기보다는 Docker compose를 통해 진행하게 됩니다. 다음 글에서는 Docker compose에 대해 설명하도록 하겠습니다.

 

 

<참고>

https://mlops-for-mle.github.io/tutorial/

 

MLOps for MLE | ML Engineer를 위한 MLOps

Description will go into a meta tag in <head />

mlops-for-mle.github.io

 

 

 

728x90

댓글