본문으로 건너뛰기

6) Data Generator on Docker Compose

Chapter Preview


목표

  1. DB 컨테이너와 Data Generator 컨테이너를 함께 띄우기 위한 Docker Compose 파일을 작성합니다.
  2. DB 안에 데이터가 계속해서 삽입되고 있는지 확인합니다.

스펙 명세서

  1. Docker Compose 파일을 작성합니다.

    • Postgres Server
      • Service name : postgres-server
      • Image : postgres:14.0
      • Container name : postgres-server
      • Environment
        • POSTGRES_USER : myuser
        • POSTGRES_PASSWORD : mypassword
        • POSTGRES_DB : mydatabase
      • Port forwarding : 5432:5432
    • Data Generator
      • Service name : data-generator
      • Image : Dockerfile
      • Container name : data-generator
      • Command : ["postgres-server"]
    정보

    Postgres Server 서비스와 Data Generator 서비스를 띄울 때 어떤 서비스가 먼저 생성되어야 하는지 생각해보고, 그 기능을 Compose 파일에 추가합니다.

  2. Docker Compose 파일을 실행합니다.

  3. psql 을 이용하여 DB 에 데이터가 계속해서 쌓이고 있는지 확인합니다.

    • Local 에서 확인합니다.
    • Data Generator server 에서 확인합니다.
https://github.com/mlops-for-mle/mlops-for-mle/tree/main/part1

해당 파트의 전체 코드는 mlops-for-mle/part1/ 에서 확인할 수 있습니다.

part1
├── Dockerfile
├── Makefile
├── create_table.py
├── data_generator.py
├── docker-compose.yaml
├── insert_data.py
└── insert_data_loop.py

0. 환경 설정

주의

📌 이번 챕터에서는 Docker Compose 를 이용하여 컨테이너를 띄우는 법에 대해서 실습합니다.
📌 이를 위해서 앞서 1) DB Server Creation 챕터와 5) Data Generator on Docker 챕터에서 시작한 컨테이너를 종료해야 합니다.

다음 명령어를 통해 실행중인 DB 서버와 Data Generator 를 종료시킵니다.

docker rm --force postgres-server data-generator

1. Dockerfile

5) Data Generator on Docker 챕터에서 작성한 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"]

2. Docker Compose

이제 Compose 파일을 작성해보도록 하겠습니다.

Compose 파일의 아키텍처는 아래와 같습니다.

docker-compose.yaml
version: "3"

services:
postgres-server:
...
data-generator:
...
  • version :
    • Compose 파일의 버전을 의미합니다. 최신 버전은 공식 홈페이지에서 확인하실 수 있습니다.
    • 이번 챕터에서는 작성일 기준 최신 버전인 3 을 사용하겠습니다.
  • services :
    • Compose 에 묶일 서비스들을 의미합니다. 하나의 서비스는 하나의 컨테이너를 의미합니다.
    • postgres-server : 01) DB Server Creation 챕터에서 사용한 DB 서버를 실행시키기 위한 내용을 정의할 서비스 이름입니다.
    • data-generator : 05) Data Generator on Docker 챕터에서 사용한 Data Generator 를 실행시키기 위한 내용을 정의할 서비스 이름입니다.

이제 데이터를 저장할 postgres server 서비스와 데이터를 생성할 Data Generator 서비스를 작성해 보겠습니다.

2.1 Postgres Server Service

먼저, postgres-server 서비스를 아래와 같이 yaml 형태로 작성합니다. postgres-server 서비스에서 사용하는 값들은 01) DB Server Creation 챕터에서 사용한 값과 같은 값을 사용합니다.

docker-compose.yaml
version: "3"

services:
postgres-server:
image: postgres:14.0
container_name: postgres-server
ports:
- 5432:5432
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydatabase
  • postgres-server :
    • 서비스의 이름입니다.
    • 입력한 이름은 실행되는 컨테이너의 호스트 이름이 됩니다.
  • image :
    • 사용할 컨테이너의 이미지를 입력합니다.
  • ports :
    • 컨테이너에서 외부로 노출할 포트 포워딩을 설정합니다.
    • 형식은 host:container 로 사용되며 여러 개를 지정할 수 있습니다.
  • environment :
    • 컨테이너를 실행할 때 사용한 -e 옵션과 같은 역할을 합니다.

2.2 Data Generator Service

다음으로, data-generator 서비스를 아래와 같이 yaml 형태로 작성합니다. postgres-server 서비스와 다르게 이번에는 직접 이미지를 build 해서 사용합니다.

docker-compose.yaml
  data-generator:
build:
context: .
dockerfile: Dockerfile
container_name: data-generator
depends_on:
- postgres-server
command: ["postgres-server"]
  • build :
    • context : 이미지를 build 하기 위해 Dockerfile 이 있는 절대경로 또는 상대경로를 설정합니다.
    • dockerfile : context 에서 설정한 경로에 있는 Dockerfile 의 파일명을 입력합니다.
  • depends_on :
    • Compose 로 띄워지는 서비스 간의 종속성 순서대로 서비스를 시작할 때 사용합니다.
    • 여기서는 postgres server 가 먼저 실행되고 난 뒤에 Data Generator 를 실행해야 하기 때문에 postgres server 의 서비스 이름인 postgres-server 를 입력합니다.
  • command :
    • Dockerfile 에 작성되어 있는 CMD 를 덮어씁니다.
    • 5) Data Generator on Docker 챕터에서 작성한 Data Generator 를 사용하기 위해서는 postgres server 의 호스트를 변경해야 합니다.
    • 호스트 이름은 위에서 언급한 것처럼 컨테이너의 이름으로 주어야 하기 때문에 ["postgres-server"] 로 덮어씁니다.

2.3 Docker Compose Healthcheck

앞서 작성한 docker-compose.yaml 파일의 내용을 함께 나열하면 아래와 같습니다.

docker-compose.yaml
version: "3"

services:
postgres-server:
image: postgres:14.0
container_name: postgres-server
ports:
- 5432:5432
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydatabase

data-generator:
build:
context: .
dockerfile: Dockerfile
container_name: data-generator
depends_on:
- postgres-server
command: ["postgres-server"]

이제 작성한 Compose 파일을 실행해보겠습니다.
Compose 파일은 up 명령어와 down 명령어를 통해 실행과 종료를 할 수 있습니다.

docker compose up -d
  • -d :
    • Detached 모드로 실행합니다. Detached 모드란, 백그라운드에서 컨테이너를 실행 후 유지시키는 모드를 의미합니다.

하지만 docker ps 를 입력해보면 postgres server 만 띄워져있습니다.

정보

하드웨어 스펙에 따라 따라 해당 내용이 재현이 안 되는 경우도 있습니다.
재현이 안되는 경우, 전체 흐름에 초점을 맞춰서 봐주시면 될 것 같습니다.

docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a4a16837f28 postgres:14.0 "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:5432->5432/tcp postgres-server

Docker desktop 으로 봐도 동일한 현상입니다.

Container Networking Connetected
[그림 1-5] Docker Desktop

왜 이럴까요?

이유는 앞서 depends_on 으로 서비스 간의 종속성은 정했지만, 실제로 postgres server 가 띄워진 뒤에 곧바로 Data Generator 가 띄워집니다. Postgres server 는 아직 준비가 되어있지 않은데 Data Generator 가 띄워져서 DB 에 연결을 하려다보니 Data Generator 가 Exited 되는 문제가 발생한 것입니다.

따라서 postgres server 가 사용 가능한 상태가 되어있는지 체크를 한 뒤에 Data Generator 를 띄워야 합니다.
이를 해결하기 위한 방법으로 Docker Compose Healthcheck 가 있습니다.

간단하게 아래와 같이 yaml 파일에 healthcheck 부분과 condition 을 추가하면 됩니다.

docker-compose.yaml
version: "3"

services:
postgres-server:
image: postgres:14.0
container_name: postgres-server
ports:
- 5432:5432
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydatabase
healthcheck:
test: ["CMD", "pg_isready", "-q", "-U", "myuser", "-d", "mydatabase"]
interval: 10s
timeout: 5s
retries: 5

data-generator:
build:
context: .
dockerfile: Dockerfile
container_name: data-generator
depends_on:
postgres-server:
condition: service_healthy
command: ["postgres-server"]
  • test :
    • 테스트 할 명령어를 설정합니다.
    • 이번 챕터에서는 pg_isready 를 이용하여 DB 가 준비상태인지 테스트를 진행하기 때문에 ["CMD", "pg_isready", "-q", "-U", "myuser", "-d", "mydatabase"] 를 입력합니다.
  • interval :
    • Healthcheck 간격을 설정합니다.
    • 이번 챕터에서는 10초로 설정합니다.
  • timeout :
    • Healthcheck 의 timeout 을 설정합니다.
    • 이번 챕터에서는 5초로 설정합니다.
  • retries :
    • Timeout 의 횟수를 설정합니다.
    • 이번 챕터에서는 5번으로 설정합니다.
  • condition :
    • Healthcheck 기능을 사용하기 위해 depends_on 의 parameter 로 condition: service_healthy 를 넣어줍니다.
    • Postgres server 의 healthcheck 를 Data Generator 에서 적용시키기 위해 postgres-server 밑에 condition 을 추가하였습니다.

이렇게 설정된 것을 종합해보면, 10초마다 테스트를 실행했을 때 5초 이내에 DB 가 준비 상태가 되었는지를 체크할 것이며, 실패 시 5번 재시도한다는 뜻이 됩니다. 이외에 더 자세한 내용은 Dockerfile Reference 을 참고해주시기 바랍니다.

이제 다시 서비스들을 실행해보겠습니다.

docker compose up -d

문제없이 잘 실행되는 것을 확인할 수 있습니다.

2.4 Docker Compose Network

2.4.1 Default Network

5) Data Generator on Docker 챕터에서 컨테이너들끼리 네트워크를 연결하는 부분을 설명했었습니다. 위의 서비스를 실행한 후 생성된 네트워크를 확인해 보겠습니다.
docker network ls

실행하면 다음과 같이 part1_default 라는 이름의 네트워크가 생성된 것을 확인할 수 있습니다.

NETWORK ID     NAME          DRIVER    SCOPE
d2a7834f5caa bridge bridge local
d7d45bd4c3b7 part1_default bridge local
0d5dc37e7a9d host host local
703a7cd222a5 none null local

part1_default 를 확인해 보겠습니다.

docker network inspect part1_default

네트워크에 자동으로 postgres-serverdata-generator 컨테이너가 추가된 것을 볼 수 있습니다.

[
{
"Name": "part1_default",
...
"ConfigOnly": false,
"Containers": {
"4043d0d2df3673a5fac77945bb7746e1af4b352fb7617891de0b22357a877c59": {
"Name": "postgres-server",
...
},
"4ffdc089e4a40727645a0cab1952c025cdd583031f3ce7867761305ead2a6d63": {
"Name": "data-generator",
...
}
...
}
]

이처럼 Compose 파일을 사용하면 네트워크에 대해서 신경쓰지 않고 편리하게 컨테이너들을 연결해서 사용할 수 있습니다.

2.4.2 Custom Network

네트워크의 이름은 특별히 지정하지 않을 경우 {디렉토리명}_default 로 자동으로 생성이 됩니다. 그렇기 때문에 서로 같은 디렉토리를 사용하지 않을 경우 네트워크의 이름이 변할 수 있습니다.

앞으로 이어지는 파트에서는 서로 네트워크의 연결이 필요하기 때문에 이름을 지정해서 생성하겠습니다.

먼저 실행한 서비스들을 종료하겠습니다.

docker compose down -v
  • -v :
    • 생성된 볼륨까지 삭제합니다.

네트워크 생성은 services 와 같은 레벨에 networks 를 입력하면 됩니다.

docker-compose.yaml
networks:
default:
name: mlops-network
  • default : 서비스 전체의 기본 네트워크를 수정할 수 있습니다.
    • name : 네트워크의 이름을 작성합니다. 앞으로 이어지는 파트에서도 사용할 수 있도록 mlops-network 로 작성하겠습니다.

2.4.3 실행

네트워크까지 작성한 최종 Compose 파일은 다음과 같습니다.

docker-compose.yaml
version: "3"

services:
postgres-server:
image: postgres:14.0
container_name: postgres-server
ports:
- 5432:5432
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydatabase
healthcheck:
test: ["CMD", "pg_isready", "-q", "-U", "myuser", "-d", "mydatabase"]
interval: 10s
timeout: 5s
retries: 5

data-generator:
build:
context: .
dockerfile: Dockerfile
container_name: data-generator
depends_on:
postgres-server:
condition: service_healthy
command: ["postgres-server"]

networks:
default:
name: mlops-network

다시 docker compose 를 실행해 보겠습니다.

docker compose up -d

실행 후 네트워크를 확인합니다.

docker network ls

다음과 같이 mlops-network 가 추가된 것을 확인할 수 있습니다.

NETWORK ID     NAME            DRIVER    SCOPE
d2a7834f5caa bridge bridge local
0d5dc37e7a9d host host local
e9671c141955 mlops-network bridge local
703a7cd222a5 none null local

3. 데이터 확인

3.1 In Local

psql 을 이용하여 DB 에 접속하고, 계속해서 데이터가 삽입되고 있는지 확인합니다.

PGPASSWORD=mypassword psql -h localhost -p 5432 -U myuser -d mydatabase
psql (14.3, server 14.0 (Debian 14.0-1.pgdg110+1))
Type "help" for help.

mydatabase=# select * from iris_data;
id | sepal_length | sepal_width | petal_length | petal_width | target
----+--------------+-------------+--------------+-------------+--------
1 | 6 | 2.9 | 4.5 | 1.5 | 1
2 | 6.9 | 3.1 | 5.4 | 2.1 | 2
3 | 5.1 | 3.7 | 1.5 | 0.4 | 0
4 | 6.3 | 2.7 | 4.9 | 1.8 | 2
5 | 4.4 | 2.9 | 1.4 | 0.2 | 0
6 | 4.7 | 3.2 | 1.3 | 0.2 | 0

3.2 In Data Generator Container

docker exec 를 이용하여 Data Generator 컨테이너 안으로 접속합니다.

docker exec -it data-generator /bin/bash

그리고 psql 을 이용하여 DB 로 접속합니다. 이 때 local 이 아닌 Data Generator 컨테이너에서 접속해야 하기 때문에 호스트를 localhost 에서 postgres-server 로 변경합니다.

PGPASSWORD=mypassword psql -h postgres-server -p 5432 -U myuser -d mydatabase
psql (13.8 (Debian 13.8-0+deb11u1), server 14.0 (Debian 14.0-1.pgdg110+1))
WARNING: psql major version 13, server major version 14.
Some psql features might not work.
Type "help" for help.

mydatabase=# select * from iris_data;
id | sepal_length | sepal_width | petal_length | petal_width | target
----+--------------+-------------+--------------+-------------+--------
1 | 6 | 2.9 | 4.5 | 1.5 | 1
2 | 6.9 | 3.1 | 5.4 | 2.1 | 2
3 | 5.1 | 3.7 | 1.5 | 0.4 | 0
4 | 6.3 | 2.7 | 4.9 | 1.8 | 2
5 | 4.4 | 2.9 | 1.4 | 0.2 | 0
6 | 4.7 | 3.2 | 1.3 | 0.2 | 0