Docker Introduction
Docker
์ปจํ ์ด๋๋ผ๋ ํจํค์ง๋ก ์ํํธ์จ์ด๋ฅผ ์ ๊ณตํด์ฃผ๋ ํด๋ก ์๋ก ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์์ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ๊ณผ ๋ฐ์ดํฐ ๋ฒ ์ด์ค๋ฅผ ์ด์ํ ์ ์๋ค.
Docker Image
์ค์ ๋ ํ๊ฒฝ์ ์ค๋ ์ท์ผ๋ก, ๋์ผํ ์ด๋ฏธ์ง๋ฅผ ๋ค์ํ ํ๊ฒฝ์์ ๊ตฌ๋๊ฐ๋ฅํ๊ฒ ํ์ฌ ์ฌํ์ฑ์ ๋ณด์ฅ
๋ฐ์ดํฐ ์์ง๋์ด๊ฐ Docker ๋ฅผ ์ฌ์ฉํด์ผํ๋ ์ด์
- ์ฌํ ๊ฐ๋ฅ์ฑ์ ์ ๊ณต
- ๋ณต์กํ ํ์ดํ๋ผ์ธ์ ํตํฉ ํ ์คํธ ๊ฐ๋ฅ
- Ci/CD๋ฅผ ํตํ ์ํํธ์จ์ด ๊ณตํ ์คํ
- ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ ์์กด์ฑ ์ ๊ณต
- ๋ค์ํ ํ๊ฒฝ์ ์คํ ์ ๊ณต
ํจ๊ณผ
- Test๋ฅผ ์ํ ๋ก์ปฌ ์ธํ
- ํตํฉ ํ ์คํธ, CI/CD
- ๋ฐฐ์น ์์
- Spark ์ฌ์ฉ
- ์๋ฒ๋ฆฌ์ค (AWS Lambda)
ํด๋น ๊ฐ์์์๋ Docker๋ฅผ ํตํ ๊ฐ๋จํ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ์ ๋ชฉ์ ์ผ๋ก Docker๋ฅผ ์๊ฐํฉ๋๋ค.
Pipeline Overview

Docker์ ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
๊ธฐ๋ณธ ์์ฑ ๋ฐฉ๋ฒ
๋์ปค๋ฅผ ํตํด ํ์ด์ฌ์ pandas๋ฅผ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์
FROM python:3.9
RUN pip install pandas
ENTRYPOINT [ "bash" ]
docker build -t test:pandas .
๋ก์ปฌ์ ํ์ผ์ ํฌํจํด์ Docker ์ด๋ฏธ์ง ์์ฑ
FROM python:3.11.9-slim
RUN pip install pandas
WORKDIR /app
COPY pipeline.py pipeline.py
ENTRYPOINT [ "bash" ]
docker build -t test:pandas
docker run -it test:pandas
Docker
docker -it๋ Docker ์ปจํ ์ด๋๋ฅผ ์ํธ์์ฉ ๋ชจ๋๋ก ์คํํ๊ธฐ ์ํ ์ต์ ์ ๋๋ค.
- i : ํ์ค ์ ๋ ฅ(sddin)์ ์ด์ด ์ปจํ ์ด๋์ ์ํธ์์ฉํ ์ ์๋๋ก ํ๋ ๊ฒ
- t : ๊ฐ์ ํฐ๋ฏธ๋์ ํ ๋ค์ป ํฐ๋ฏธ๋ ์ธ์ ์ ์๋ฎฌ๋ ์ดํธ ํ๋ ๊ฒ
docker run -it <Image Name>
์ปจํ ์ด๋์ ๋งค๊ฐ๋ณ์ ์ ๋ฌ
FROM python:3.11.9-slim
RUN pip install pandas
WORKDIR /app
COPY pipeline.py pipeline.py
ENTRYPOINT [ "python", "pipeline.py" ]
import sys
import pandas
print(sys.argv)
day = sys.argv[1] # '2020-01-01'
# some fancy stuff with pandas
print(f'job finished successfully for day = {day}')
buildํ ๋ค์๊ณผ ๊ฐ์ ๋ช ๋ น์ด๋ก ์ฒ๋ฆฌ
docker run -it test:pandas 2025-01-18 ['pipeline.py', '2025-01-18']
>> job finished successfully for day = 2025-01-18
NY ํ์ ๋ฐ์ดํฐ Postgres์ ์์ง
Docker์ฌ์ฉํ Postgres ๋ช ๋ น์ด
์ฌ์ ์์
1. pgcli
pip install pgcli
2. PostgreSQL ์คํ
pgcli -h localhost -p 5432 -u root -d ny_taxi
3. Jupyter Notebook ์คํ
4. ํ์ ๋ฐ์ดํฐ ๋ค์ด๋ก๋
Dataset:
- https://www1.nyc.gov/site/tlc/about/tlc-trip-record-data.page
- https://www1.nyc.gov/assets/tlc/downloads/pdf/data_dictionary_trip_records_yellow.pdf
pgAdmin๊ณผ Postgres ์ฐ๊ฒฐ
while๋ฃจํ๋ฅผ ์ฌ์ฉํด CSV๋ฅผ ์ฒญํฌ ๋จ์๋ก ์ฝ์
from time import time
while True:
t_start = time()
df = next(df_iter) df.tpep_pickup_datetime = pd.to_datetime(df.tpep_pickup_datetime)
df.tpep_dropoff_datetime = pd.to_datetime(df.tpep_dropoff_datetime)
df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append')
print('.', end='')
t_end = time()
print('inserted another chunk..., took: %.3f seconds' % (t_end - t_start))
pgAdmin ์คํ
๋์ปค ๋ช ๋ น์ ํตํด ๋ค์๊ณผ ๊ฐ์ด ์คํํฉ๋๋ค.
docker run -it \ -e PGADMIN_DEFAULT_EMAIL="admin@admin.com" \ -e PGADMIN_DEFAULT_PASSWORD="root" \ -p 8080:80 \ dpage/pgadmin4

์์ง ์คํฌ๋ฆฝํธ ๋์ปคํ
์ฃผํผํฐ ๋ ธํธ๋ถ์ ํ์ด์ฌ ์คํฌ๋ฆฝํธ๋ก ๋ณํ
nbconvert ์๋ธ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด์ Notebook์ ํ์ด์ฌ ์คํฌ๋ฆฝํธ๋ก ๋ณํํ ์ ์๋ค.
jupyter nbconvert --to=script upload-data.ipynb
[NbConvertApp] Converting notebook upload-data.ipynb to script [NbConvertApp] Writing 3466 bytes to upload-data.py
Dockerizing ๋ฐ์ดํฐ ์ฒ๋ฆฌ
- ๋ฐ์ดํฐ Ingestion์ ์ํด pandas๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๋ก์ปฌ์ PostgreSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋ ๊ณผ์
- ๋ฐ์ดํฐ ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ํ username, password, host, port ๋ฑ์ ํ๋ผ๋ฏธํฐ ์ค์
- OS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด CSVํ์ผ ๋ค์ด๋ก๋
#!/usr/bin/env python
# coding: utf-8
import os
import argparse
from time import time
import pandas as pd
from sqlalchemy import create_engine
def main(params):
user = params.user
password = params.password
host = params.host
port = params.port
db = params.db
table_name = params.table_name
url = params.url
# the backup files are gzipped, and it's important to keep the correct extension
# for pandas to be able to open the file
if url.endswith('.csv.gz'):
csv_name = 'output.csv.gz'
else:
csv_name = 'output.csv'
os.system(f"wget {url} -O {csv_name}")
engine = create_engine(f'postgresql://{user}:{password}@{host}:{port}/{db}')
df_iter = pd.read_csv(csv_name, iterator=True, chunksize=100000)
df = next(df_iter)
df.tpep_pickup_datetime = pd.to_datetime(df.tpep_pickup_datetime)
df.tpep_dropoff_datetime = pd.to_datetime(df.tpep_dropoff_datetime)
df.head(n=0).to_sql(name=table_name, con=engine, if_exists='replace')
df.to_sql(name=table_name, con=engine, if_exists='append')
while True:
try:
t_start = time()
df = next(df_iter)
df.tpep_pickup_datetime = pd.to_datetime(df.tpep_pickup_datetime)
df.tpep_dropoff_datetime = pd.to_datetime(df.tpep_dropoff_datetime)
df.to_sql(name=table_name, con=engine, if_exists='append')
t_end = time()
print('inserted another chunk, took %.3f second' % (t_end - t_start))
except StopIteration:
print("Finished ingesting data into the postgres database")
break
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Ingest CSV data to Postgres')
parser.add_argument('--user', required=True, help='user name for postgres')
parser.add_argument('--password', required=True, help='password for postgres')
parser.add_argument('--host', required=True, help='host for postgres')
parser.add_argument('--port', required=True, help='port for postgres')
parser.add_argument('--db', required=True, help='database name for postgres')
parser.add_argument('--table_name', required=True, help='name of the table where we will write the results to')
parser.add_argument('--url', required=True, help='url of the csv file')
args = parser.parse_args()
main(args)
์ ํ์ด์ฌ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๊ธฐ ์ํด ๋ค์์ Dockering Injsetion Script ์์ฑ ๊ฐ๋ฅ
FROM python:3.11.9-slim
RUN apt-get update && apt-get install -y wget
RUN pip install pandas sqlalchemy psycopg2-binary
WORKDIR /app
COPY ingest_data.py ingest_data.py
ENTRYPOINT [ "python", "ingest_data.py" ]
์ดํ ๋ค์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํ ์์ผ ์ฒ๋ฆฌ
URL="https://github.com/DataTalksClub/nyc-tlc-data/releases/download/yellow/yellow_tripdata_2021-01.csv.gz"
docker run -it \
--network=pg-network \
taxi_ingest:v001 \
--user=root \
--password=root \
--host=pg-database \
--port=5432 \
--db=ny_taxi \
--table_name=yellow_taxi_trips \
--url=${URL}
๋์ปค๋ฅผ ํตํด ์ด๋ฏธ์ง ์ ์ฅ ์ ์ HTTP ์๋ฒ๋ฅผ ์ด์ฉํด ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ก๋ ๋ฐ์ ์ ์๋๋ก ์ฒ๋ฆฌ
Docker Compose์ด์ฉํ ์๋น์ค ๊ตฌ์ฑ
Docker Compose
์ฌ๋ฌ ์ปจํ ์ด๋์ ๊ตฌ์ฑ์ ํ๋ฒ์ ์ ๋ฆฌํ๊ณ ๊ด๋ฆฌ, ์คํ
๋ธ๋ก๊ทธ : https://data-newbie.tistory.com/930#deployments ์ค์น ๊ณผ์ : https://docs.docker.com/compose/install/
Postgres ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ PgAdmin์ค์
services:
pgdatabase:
image: postgres:13
environment:
- POSTGRES_USER=root
- POSTGRES_PASSWORD=root
- POSTGRES_DB=ny_taxi
volumes:
- ./ny_taxi_postgres_data:/var/lib/postgresql/data:rw
ports:
- "5432:5432"
pgadmin:
image: dpage/pgadmin4
environment:
- PGADMIN_DEFAULT_EMAIL=admin@admin.com
- PGADMIN_DEFAULT_PASSWORD=root
ports:
- "8080:80"
- Postgres์ ํฌํธ : 5432๋ก ์ค์ ํ ์ปจํ ์ด๋์์๋ ๊ฐ์ ํฌํธ๋ก ๋งคํ
- pgAdmin์ ์ค์ ์, env ์ค์ ๋ฐ ํฌํธ 8080 โ 80์ผ๋ก ๋งคํ
docker compose up์ ํตํด ์คํdocker compose down์ ํตํด ์ปจํ ์ด๋ ์ค์ง
Docker Compose ์ฅ์
- ๋คํธ์ํฌ ์๋ ์์ฑ ํ์ x
- Detached mode๋ก ์คํํ์ฌ ํฐ๋ฏธ๋์ ๋ค์ ์ฌ์ฉํ ์ ์๋ ์ฅ์
docker compose down์ผ๋ก ์ฌ๋ฌ ํฐ๋ฏธ๋ ์ฐฝ์ ๋ณด์ง ์๊ณ ํ ๋ฒ์ ์ข ๋ฃ ๊ฐ๋ฅ- ๋ก์ปฌ ์ธํ , ํตํฉ ํ ์คํธ์ ์ ์ฉ
[pgadmin์์์ ํ ์คํธ ์ฟผ๋ฆฌ ์คํ]

GCP
Google์์ ์ ๊ณตํ๋ ํด๋ผ์ฐ๋ ์ปดํจํ ์๋น์ค๋ก ๋ค์ํ ์ปดํจํ , ์คํ ๋ฆฌ์ง, ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์ํ ๋ค์ํ ํธ์คํ ์๋น์ค๊ฐ ์ ๊ณต๋ฉ๋๋ค.

Terraform
HashiCorp์ ์คํ์์ค ๋๊ตฌ๋ก ์ฝ๋๋ฅผ ์ฌ์ฉํด์ ํด๋ผ์ฐ๋ ์ธํ๋ผ๋ฅผ ๋ง๋ค๊ณ ๊ด๋ฆฌํ๋ ๋๊ตฌ์ ๋๋ค. ๋ง์น ๋ ๊ณ ๋ธ๋ก์ผ๋ก ๊ฑด๋ฌผ์ ์ง๋ฏ์ด, Terraform ์ฝ๋๋ฅผ ์ฌ์ฉํด์ ์๋ฒ, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ๋คํธ์ํฌ ๊ฐ์ ํด๋ผ์ฐ๋ ์์๋ค์ ์ ์ํ๊ณ ๋ง๋ค ์ ์์ต๋๋ค.
ํน์ง
- ์ธํ๋ผ ์๋ช ์ฃผ๊ธฐ ๊ด๋ฆฌ
- ๋ฒ์ ์ ์ด ์ปค๋ฐ
- ์คํ ๊ธฐ๋ฐ ๋ฐฐํฌ์ ๋งค์ฐ ์ ์ฉํ๋ฉฐ AWS, GCP, Azure, K8S์ ๊ฐ์ ํด๋ผ์ฐ๋ ๊ณต๊ธ์์ ํจ๊ป ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๋ฐฐํฌ ์ ๋ฐ์ ๊ฑธ์ณ ๋ฆฌ์์ค ๋ณ๊ฒฝ ์ฌํญ์ ์ถ์ ํ๊ธฐ ์ํ ์ํ ๊ธฐ๋ฐ ์ ๊ทผ ๋ฐฉ์
์ฌ์ฉํ๋ ์ด์
- ์ธํ๋ผ ์ถ์ ์ ๋จ์ํ
- ๋ ์ฌ์ด ํ์
- ์ฌํ์ฑ
- ๋ฆฌ์์ค ์ ๊ฑฐ ๋ณด์ฅ
ํ ๋ผํผ ์๋ ๋ฐฉ์
Terraform์ ์ฝ๋ํ ์ธํ๋ผ(IaC) ๋๊ตฌ๋ก, ์ธํ๋ผ๋ฅผ ์ ์ธ์ ๊ตฌ์ฑ ํ์ผ๋ก ์ ์ํ๊ณ ํ๋ก๋น์ ๋ ๋ฐ ๊ด๋ฆฌ
Terraform Core
โข Terraform์ ํต์ฌ ์์๋ก, RPC(์๊ฒฉ ํ๋ก์์ ํธ์ถ)๋ฅผ ํตํด ํ๋ฌ๊ทธ์ธ๊ณผ ํต์ .
์ฃผ์ ์ญํ
โข ๊ตฌ์ฑ ํ์ผ ์ฝ๊ธฐ ๋ฐ ์ข
์์ฑ ๊ทธ๋ํ ์์ฑ.
โข ๋ฆฌ์์ค ์ํ ๊ด๋ฆฌ(terraform.tfstate ํ์ผ).
โข ๊ณํ ์คํ(terraform plan) ๋ฐ ์ ์ฉ(terraform apply).
Terraform Plugin โข ์ธ๋ถ ๋ฐ์ด๋๋ฆฌ๋ก ๊ตฌํ๋ ํ๋ฌ๊ทธ์ธ์ผ๋ก, ํน์ ํด๋ผ์ฐ๋ ์ ๊ณต์์ ํต์ . ์ฃผ์ ์ญํ โข API ํธ์ถ์ ํตํด ๋ฆฌ์์ค ์์ฑ/์์ /์ญ์ (CRUD) ์ฒ๋ฆฌ. โข ์ธ์ฆ ๋ฐ ์๋น์ค ๋งคํ.
State Management
โข Terraform์ ๋ฆฌ์์ค์ ์ํ๋ฅผ .tfstate ํ์ผ์ ์ ์ฅํ์ฌ ์ธํ๋ผ์ ์ฝ๋ ๊ฐ์ ๋๊ธฐํ๋ฅผ ์ ์ง.
โข ์ํ ํ์ผ์ ๋ฆฌ์์ค ๊ฐ ์ข
์์ฑ์ ์ถ์ ํ๋ฉฐ, ์ฑ๋ฅ์ ์ต์ ํ.
Declarations
terraformโข ๊ธฐ๋ณธ ์ค์ ์ ์ ์ํ๋ฉฐ, ์ธํ๋ผ๋ฅผ ํ๋ก๋น์ ๋ํ๋ ๋ฐ ํ์ํ ์ ๋ณด๋ฅผ ์ค์ ํฉ๋๋ค. โข ์ฃผ์ ์์ฑ: โขrequired_version: Terraform ๋ฒ์ ์๊ตฌ์ฌํญ. โขbackend: ์ํ ํ์ผ ์ ์ฅ ์์น ์ง์ (์:local,remote). โขrequired_providers: ์ฌ์ฉํด์ผ ํ ํด๋ผ์ฐ๋ ์ ๊ณต์ ์ค์ .providerโข Terraform์ด ๊ด๋ฆฌํ ๋ฆฌ์์ค ์ ํ๊ณผ ๋ฐ์ดํฐ ์์ค๋ฅผ ์ถ๊ฐํ๋ ๋ชจ๋. โข ์: AWS, Google Cloud, Azure ๋ฑ.resourceโข ์ธํ๋ผ์ ๊ตฌ์ฑ ์์๋ฅผ ์ ์ํ๋ ๋ธ๋ก. โข ์:aws_instance,google_storage_bucket.variable&localsโขvariable: ๋ฐํ์ ์ ๋์ ์ผ๋ก ๊ฐ์ ์ ๋ฌํ๊ธฐ ์ํ ์ ๋ ฅ ๋ณ์. โขlocals: ๊ณ ์ ๋ ๊ฐ์ด๋ ๊ณ์ฐ๋ ๊ฐ์ ์ ์ฅํ๋ ๋ก์ปฌ ๋ณ์.
ํ์ผ ๊ตฌ์ฑ
main.tf: Terraform์ ๋ฉ์ธ ๊ตฌ์ฑ ํ์ผ๋ก, ๋ฆฌ์์ค์ ์ ๊ณต์๋ฅผ ์ ์.variables.tf: ๋ณ์ ์ ์ธ ๋ฐ ๊ธฐ๋ณธ๊ฐ ์ค์ .resources.tf: ๋ฆฌ์์ค ๋ธ๋ก์ ๋ถ๋ฆฌํ์ฌ ์์ฑ(์ ํ ์ฌํญ).output.tf: ์์ฑ๋ ๋ฆฌ์์ค์ ์ถ๋ ฅ๊ฐ์ ์ ์(์ ํ ์ฌํญ)..tfstate: ํ์ฌ ์ธํ๋ผ ์ํ๋ฅผ ์ ์ฅํ๋ ํ์ผ.
Terraform ์๋ ํ๋ฆ
- Write: ์ ์ธ์ ๊ตฌ์ฑ ํ์ผ ์์ฑ(
main.tf,variables.tf๋ฑ). - Plan: ํ์ฌ ์ํ์ ์ํ๋ ์ํ๋ฅผ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ ๊ณํ(
terraform plan). - Apply: ๊ณํ๋ ๋ณ๊ฒฝ ์ฌํญ์ ์ค์ ๋ก ์ ์ฉ(
terraform apply). - Destroy: ๊ธฐ์กด ์ธํ๋ผ๋ฅผ ์ญ์ (
terraform destroy).
์ฃผ์ ๋ช ๋ น์ด
terraform init: Terraform ํ๋ก์ ํธ๋ฅผ ์ด๊ธฐํํ๊ณ ํ์ํ ํ๋ฌ๊ทธ์ธ์ ๋ค์ด๋ก๋ํฉ๋๋ค.terraform plan: ๋ณ๊ฒฝ ์ฌํญ์ ๋ฏธ๋ฆฌ ํ์ธํ์ฌ ์ด๋ค ๋ฆฌ์์ค๊ฐ ์์ฑ, ์์ , ์ญ์ ๋ ์ง ๊ณํ์ ์ธ์๋๋ค.terraform apply: Terraform ์ฝ๋๋ฅผ ์ค์ ๋ก ์ ์ฉํ์ฌ ์ธํ๋ผ๋ฅผ ์์ฑํ๊ฑฐ๋ ์์ ํฉ๋๋ค.terraform destroy: Terraform์ผ๋ก ์์ฑ๋ ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ์ญ์ ํฉ๋๋ค.
Terraform ๋จ์ผ ํ์ผ ๋ฐฐํฌ, ๋ณ์ ํ์ผ์ ์ฌ์ฉํ ๋ฐฐํฌ
GCP์ ๊ธฐํ์ด ์ฌ๋ผ์ ธ์ ์ ๊ฐ ์ ์์ด ์๋์ด์โฆ ์ฌ๊ธฐ๋ฅผ ๋ค์ ๋ชปํ์ต๋๋ค..