imhamburger 님의 블로그

데이터엔지니어 부트캠프 - API에서 입력받은 데이터를 MariaDB에 저장시키기 with Docker 본문

데이터엔지니어 부트캠프

데이터엔지니어 부트캠프 - API에서 입력받은 데이터를 MariaDB에 저장시키기 with Docker

imhamburger 2024. 9. 19. 15:33

아래와 같은 배포된 웹사이트가 있고,

음식 이름을 입력 후 "저장" 버튼을 누르면 입력값이 MariaDB에 저장이 되게끔 만들고자 한다.

 

 

1. 기능 구현하기

 

웹사이트 화면

 

 

나는 웹사이트(프론트앤드)에서 입력받은 값을 FastAPI(백앤드)에서 받을 것이다.

내가 만든 main.py 은 다음과 같다.

from typing import Union
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from datetime import datetime
import pandas as pd
import pymysql.cursors
import csv

app = FastAPI()

origins = [
    "http://localhost:8899",
    "https://samdul01food.web.app"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"Hello": "n01"}


@app.get("/food")
def food(name: str):
    import os

    time = datetime.now()
    df = pd.DataFrame([[name, time]], columns=['food','time'])

    #CSV
    file_path = os.getenv("FILE_PATH", f"{os.getenv('HOME')}/tmp/foodcsv/food.csv")
    if not os.path.exists(file_path):
        os.makedirs(os.path.dirname(file_path), exist_ok=True)

    with open(file_path, 'a', newline='') as f:
        writer = csv.writer(f)
        writer.writerow({name, datetime.now()})

    return {"food": name, "time": datetime.now()}

 

 

위 파일을 FastAPI로 실행시켜보자.

uvicorn src.samdul01food.main:app --reload

 

 

FastAPI에서 보이는 화면 (http://localhost:8000)

 

 

결과

 

내가 tmp/foodcsv/food.csv로 저장을 시켰으니 확인해보면 food.csv 에 입력한 값이 잘 저장되어있다.

 

 

그렇지만 나는 로컬이 아닌 MariaDB에 저장시켜야 하기때문에 몇가지 더 추가해줘야 한다.

 

 

 

2. MariaDB 생성하기

 

MariaDB는 docker 허브에 올라와있는 것을 pull 받아 run 하였다.

 

db에 접속하기

docker exec -it localdb bash
mariadb -u food -p<비밀번호 입력>

 

db에서 확인해보니, fooddb 라는 데이터베이스에 foodhistory 라는 테이블이 있고 구조는 다음과 같다.

 

이제 테이블 구조에 맞게 데이터를 넣어주면 된다.

 

 

 

3. MariaDB에 저장시키기

 

MariaDB는 오픈 소스 관계형 데이터베이스 관리 시스템(RDBMS)으로, MySQL의 대체 데이터베이스이다.

따라서, sql형식으로 데이터를 넣어주어야 한다.

 

파이썬 모듈인 PyMySql을 이용하면 파이썬 파일 안에서 sql형식으로 데이터를 넣어줄 수 있다.

언제나 그렇듯 사용방법은 공식문서를.....

 

사용방법을 참조하여 main.py를 수정하였다. 수정된 main.py 코드는 다음과 같다.

@app.get("/food")
def food(name: str):
    import os

    time = datetime.now()
    df = pd.DataFrame([[name, time]], columns=['food','time'])

    # Connect to the database
    connection = pymysql.connect(host="localhost",
                             user='food',
                             password='<비밀번호 입력>',
                             database='fooddb',
                             port ='33306',
                             cursorclass=pymysql.cursors.DictCursor)

    sql = "INSERT INTO foodhistory(`username`, `foodname`, `dt`) VALUES(%s,%s,%s)"

    with connection:
        with connection.cursor() as cursor:
            cursor.execute(sql, ('n01', name, datetime.now()))

        connection.commit()

    #CSV
    file_path = os.getenv("FILE_PATH", f"{os.getenv('HOME')}/tmp/foodcsv/food.csv")
    if not os.path.exists(file_path):
        os.makedirs(os.path.dirname(file_path), exist_ok=True)

    with open(file_path, 'a', newline='') as f:
        writer = csv.writer(f)
        writer.writerow({name, datetime.now()})

    return {"food": name, "time": datetime.now()}

 

 

결과

 

MariaDB에 잘 저장된다!

 

 

그렇지만!!!

로컬에서는 테스트때문에 한 것이고 실제는 위에 만든 기능을 Docker image로 만들고 Hub에 푸시하여 AWS에서 pull 받아 Run 하려는 것이 목적이다. 

 

위에 코드에서 db 설정을 로컬 위주로 해두었기때문에 또 다른 환경에 맞게 수정해줘야 하는데...

번거롭게 세팅하지 않아도 되는 방법이 있다!

 

바로, 환경변수 getenv를 사용하면 된다.

 

 

 

4. pymysql 설정에 환경변수를 지정해주기 

 

지정해주는건 간단하다. 방금 위에서 추가한 db 설정 코드에 os.getenv를 넣어주면 된다.

# Connect to the database
    connection = pymysql.connect(host=os.getenv('DB_IP', 'localhost'),
                             user='food',
                             password='<비밀번호 입력>',
                             database='fooddb',
                             port = int(os.getenv('DB_PORT', '33306')),
                             cursorclass=pymysql.cursors.DictCursor)

    sql = "INSERT INTO foodhistory(`username`, `foodname`, `dt`) VALUES(%s,%s,%s)"

    with connection:
        with connection.cursor() as cursor:
            cursor.execute(sql, ('n01', name, datetime.now()))

        connection.commit()

 

os.getenv 'DB_IP'가 있다면 그걸로 실행시켜주고 없다면 'localhost'로 설정해줘! 라고 설정해주었다.

DB_PORT도 마찬가지이다.

 

근데 DB_IP랑 DB_PORT는 뭐야??

그냥 내가 아무렇게나 넣은 변수명인데 이 변수들은 Docker를 run할 때 별도로 지정해줄 수 있는 옵션이 있다. (도커 공식문서)

 

 

 

5. AWS에서 Docker로 실행하기

 

위에 코드를 수정해주었다면 다시 도커 허브에 푸시를 하고 AWS에서 pull 받는다.

그런 다음 Docker run을 해줄 때, DB_IP랑 DB_PORT 변수들을 -e 옵션을 넣어 설정해주면 된다.

docker run -d -p 8001:8080 --name food01 -v /home/ubuntu/data/n01:/code/data -e FILE_PATH=/code/data/food.csv -e DB_PORT=13306 -e DB_IP=172.17.0.1 hamsunwoo/food01:0.3.0

 

-v 가 뭐야?

 

-v 옵션은 Docker에서 볼륨 마운트를 설정하는 데 사용된다.

볼륨 마운트는 호스트 시스템의 디렉터리(파일 시스템)와 컨테이너 내부의 디렉터리를 연결하는 기능을 제공한다. 이를 통해 호스트 시스템과 컨테이너 간에 파일을 공유하거나 데이터를 지속할 수 있다.

 

나의 명령어에는 도커 이미지 안에 /code/data에 있는 파일을 AWS 서버에 /home/ubuntu/data/n01 이라는 폴더에 저장하게끔 설정하였다.

  • /home/ubuntu/data/n01: 호스트 시스템에 있는 디렉터리 경로
  • /code/data: 컨테이너 내부의 디렉터리 경로

 

컨테이너 내부 디렉터리를 안만들었는데 어떻게 가능하지?

안만들어도 상관없다. 디렉토리가 없다면 지정해준 디렉토리를 도커가 알아서 자동적으로 만들어준다.

 

도커 run을 해주었다면, 도커 컨테이너로 들어가 echo $DB_IP ... 해서 환경 변수가 잘 셋팅되었는지 확인할 수 있다.

 

AWS에서도 실행한 기능을 웹사이트에서 입력받으면 로컬에서와 동일하게 MariaDB에 잘 저장된다!

 

**참고로 AWS에서도 MariaDB를 Docker에서 포트번호:13306으로 실행시켜 놓았다.