본문 바로가기

ML,DL/Ops

MLOps - 1. DB 사용하기 (feat. Docker)

본 포스트는 'MLOps for MLE'를 참고해서 공부한 내용으로 작성되었습니다.
MLOps for MLE

부스트캠프때부터 engineering에 관련된 툴을 익히기 위해서 참고했던 'MLOps for MLE'에 대한 공부를 다시 진행해보려고 한다. 서비스 개발 측면에서는 아직 첫 걸음 수준이라 꾸준히 학습을 해나가려고 한다. 학습 순서는 위의 참고자료에 정리된 순서대로 진행할 예정이다.

우선, DB를 사용해보자!

DB(Database)라는 말을 정말 많이 접했고 또 대략적으로는 어떤 것인지에 대해서도 알고 있지만, 프로젝트를 진행해보면서 DB에 대한 조금 더 디테일한 정보를 알고 있다면 처음 시스템을 설계하고 서비스의 목적에 맞춘 DB를 선택하는데 시간을 아낄 수 있고 더 효율적인 시스템을 구축할 수 있을 것이라고 생각했다.

(DB에 대한 종류와 그 특징에 대해서는 따로 포스팅을 할 예정이다.)

여기서는 PostgreSQL을 사용하는데, 이에 대한 추가적인 내용은 아래 포스트를 더 읽어보면 좋을 것 같다!

https://d2.naver.com/helloworld/227936

1. PostgreSQL 서버 만들기

postgresql을 직접 로컬에 다운받아서 사용할 수도 있지만, 굳이 로컬에서 작업할 필요성은 없기에 docker를 사용해서 진행한다.(docker의 사용법에 대해서도 포스트를 작성 할 예정)

docker를 설치했다고 가정하고,

  1. postgresql DB서버를 생성
    1. docker run -d \ --name postgres-server \ -p 5432:5432 \ -e POSTGRES_USER=myuser \ -e POSTGRES_PASSWORD=mypassword \ postgres:14.0
    2. 위 커맨드를 실행하면 docker에서 postgres의 14.0 버전 이미지를 pull해서 container를 생성해준다. (나머지는 option)

docker image를 가져오는 docker pull이라는 명령어도 따로 존재하지만, docker run을 실행하면 Image가 없는 경우 알아서 Pull을 해주기 때문에 run만 실행해도 가능

option에 대한 정리를 좀 해보자면,

-d : container가 detached 모드로 실행됨. -d가 없는 경우, 터미널이 종료되면 컨테이너도 함께 종료됨
-name : 컨테이너의 이름 지정
-p : 컨테이너에서 외부로 노출할 port forwarding 설정 (host:container)
-e : 환경변수 설정

container가 잘 띄워졌는지에 대한 확인은 docker ps

postgresql DB에 대한 서버를 확인할 때에는 postgresql의 CLI인 psql을 활용한다고 한다. 따라서 psql을 설치해주자

(psql 설치 : https://www.postgresql.org/download/)

이제, psql을 통해 생성된 서버로 접속해서 만들어진 DB 서버 확인
PGPASSWORD=mypassword psql -h localhost -p 5432 -U myuser -d mydatabase

  1. DB의 role name과 attribute를 확인하려면 \du를 입력하면 된다
  2. Options
    1. PGPASSWORD : 접속할 유저의 비밀번호 입력 \ -h : 호스트 지정 \ -p : 포트 지정 \ -U : 접속할 유저 이름 입력 \ -d : DB의 이름 입력

2. DB에 Table 생성하기

python을 사용해서 PostgreSQL DB서버에 접근하는 방법 중, psycopg2패키지를 이용해서 진행

# 패키지 설치

pip install pandas psycopg2-binary

python 스크립트로 DB에 연결하기

import psycopg2

db_connect = psycopg2.connect(
user="myuser",
pssword="mypassword",
host="localhost",
port=5432,
database="mydatabase"
)

본 자료에서는 scikit-learn에서 제공하는 'iris data'를 사용하기 때문에

pip install scikit-learn

를 먼저 해주자

iris data의 경우, Dtype이 int64, float64로 구성이 되어있는데 DB에서 지원하지 않는 dtype이기 때문에 각각 int, float8로 선언 해준다

또한, 컬럼 이름 중에 '('가 포함된 경우도 지원하지 않으므로 제거

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
);"""

쿼리를 작성했으니, 이제 작성한 쿼리를 DB에 전송한다.

  1. Connector 에서 cursor를 열고, cursor에 query를 전달
  2. query를 실행시키기 위해 connector에 Commit
  3. cursor close
with db_connect.cursor() as cur:
    cur.execute(create_table_query)
    db.connect.commit()

테이블 생성 쿼리 작성, connector로 DB 연결, cursor로 쿼리 전달 및 실행의 과정을 하나의 함수로 합쳐서 사용하는 것이 더 깔끔하고 사용하기 편할듯 하다

3. 데이터 삽입

테이블을 생성했으니 테이블에 데이터를 삽입할 차례이다
테이블 생성과 마찬가지로 데이터 삽입 쿼리 작성, connector로 연결, cursor로 쿼리 전달 및 실행의 과정을 함수화 해서 작성하면 된다.

# 데이터 삽입 query
INSERT INTO {table_name} (col_1, col_2, ...) VALUES (val_1, val_2, ...)

4. docker로 python script 띄우기

  1. 1~3의 과정을 하나의 python script file(ex. data_generator.py)로 만들고,
  2. 해당 스크립트를 docker image로 build한 뒤에
  3. container로 실행시킨다.

우선, python script file을 만드는 과정은 1~3을 참고해서 만들면 되기 때문에 따로 적어놓지는 않겠다.

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"]

이렇게 작성된 dockerfile을 통해서 docker image를 build한다.

docker build -t data-generator .

여기서 -t 옵션은 image의 tag를 설정할 수 있는 옵션을 나타낸다.

그 다음으로, build된 image를 통해서 docker container를 생성한다.

docker run data-generator

하지만 run을 하게 되면 error가 발생하면서 container가 생성되지 않는다. 왜일까??

DB container를 띄울 때는 5432:5432옵션을 사용했기 때문에, localhost:5432로 접근하게 되면 DB의 5432 포트로 연결되어 사용이 가능했다.
하지만, data-generator를 띄울 때는 해당 옵션을 사용하지 않았기 때문에, data-generator의 입장에서는 localhost:5432로 접근을 해도 아무것도 찾을 수 없는 상황이다.

이때, 사용할 수 있는 방법이 docker network이다.

docker network

  • 두 컨테이너를 연결할 수 있는 방법의 하나

사용법은 다음과 같다.

  1. 컨테이너간 통신할 수 있는 네트워크 생성
    docker network create my-network
  2. 실행 중인 컨테이너(DB)를 네트워크에 연결
    docker network connect my-network postgres-server
  3. data-generator를 DB가 연결된 네트워크를 통해 생성
    • 컨테이너간 통신을 하는 경우, localhost가 아닌 각 컨테이너의 이름이 host가 되기 때문에 이미지 이름 뒤의 값("postgres-server") 옵션을 통해서 CMD를 수정
  4. docker run -d \ --name data-generator \ --network "my-network" \ data-generator "postgres-server"
  • network의 한계
    • 컨테이너의 이름을 알아야 한다. 서로 연결이 필요한 컨테이너에게 다른 컨테이너의 이름은 전달해 주어야 한다.
    • 컨테이너가 예상치 못하게 종료되는 경우, 사용된 이름을 이미 점유하고 있어 다시 실행하려면 삭제 후 다시 생성해야한다.
    • 컨테이너의 실행 순서를 보장할 수 없다.

다음과 같은 이유로 인해 Container Orchestration이 필요하고, docker 자체적으로 docker compose를 통해 제공한다.

docker compose

최종적으로 docker compose 파일(.yaml)을 이용해서 container orchestration을 적용한다.

 

 

Q. postgresql server container, data-generator container로 구분짓지 않고 하나의 container에서 다 해결할 수 있는 방법은 없나?

Further

  1. docker network
  2. docker compose
  3. dockerfile
    등에 대한 정리

'ML,DL > Ops' 카테고리의 다른 글

Github Actions - CI/CD  (2) 2023.11.24
SaaS란?  (0) 2023.02.20