Docker

[Docker]Env를 활용해서 컨테이너 외부에서 값을 받아보자!

easysheep 2023. 10. 31. 11:16

0. 목표

보안을 위해

docker image에 api key 값을 저장한 뒤 이미지를 생성하는 방식을 docker container 를 생성할 때 키를 환경 변수로 입력 받는 방식으로 바꾸려고 한다.

1. python code 수정

다음 코드에서 import sys를 추가하고 getStockInfo 함수만을 수정 할 것 이다.

import requests
import pandas as pd
import time
from sqlalchemy import create_engine,text
from datetime import datetime, timedelta
from bs4 import BeautifulSoup as bs

def getStockCode():
    """
    현재 있는 모든 종목의 종목명 , isin Code , 종목 코드를 크롤링 하여 가지고 Json형식으로 반환하는 함수 
    request와 BeautifulSoup4를 사용하였다.
    """

    # requests를 이용하여 해당 페이지를 가지고 온다.
    page = requests.get("https://www.ktb.co.kr/trading/popup/itemPop.jspx")
    # BeautifulSoup4를 이용하여 해당 페이지의 html 내용을 전부 가지고 온다.
    soup = bs(page.text ,"html.parser")
    # 그 중 tbody.tbody_content  중 tr 태그 중 td 중 a 태그 안에 있는 데이터를 전부 가지도 온다.
    elements = soup.select("tbody.tbody_content tr td a")
    stockCodeJson = {}


    for e in elements:
        # 필요한 데이터만 추출 하여 dict 형식으로 저장한다.
        data = str(e).split(",")
        code , codeName, isincode = data[1][1:-1] , data[2][1:-1].strip(), data[-1][1:13]
        stockCodeJson[code] = (codeName,isincode)
    return stockCodeJson

def saveStockCode(stockCodeJson:dict ,db_connection):
    """
    json 변수와 sqlalchemy의 create_engine으로 생성한 객체를 인수로 받아서 Json데이터를 MySql에 저장하는 함수
    """
    # 데이터를 넣기 편하게 Pandas DataFrame 으로 변환
    stockCodeDf = pd.DataFrame(stockCodeJson).T.reset_index()
    # 컬럼명 설정
    stockCodeDf.columns = ['srtnCd','itmsNm','isinCd']
    # 과거 종목 정보 데이터 버리기
    with db_connection.connect() as conn:
        conn.execute(text('TRUNCATE TABLE stock_code;'))
        conn.commit()
    # 데이터 Mysql에 넣기
    stockCodeDf.to_sql(name='stock_code', con=db_connection, if_exists='append',index=False)  
    


def getStockInfo(db_connection):
    ## 날짜 가져오기
    last_day = getLastDay(db_connection)

    # request data to API
    headers = {'Content-Type': 'application/json', 'charset': 'UTF-8', 'Accept': '*/*'}
    # for school
    # key_path = "/Users/c05/Desktop/learn/web/PipeLine_Project/key.txt"
    # for home
    # key_path = "C:\\Users\\AW17R4\\.appkey\\open_stock_api_key.txt"
    # for ec2
    key_path = "./key.txt"
    url = "https://apis.data.go.kr/1160100/service/GetStockSecuritiesInfoService/getStockPriceInfo"


    with open(key_path,'r',encoding="UTF-8") as f:
        key = f.readline()

    params = {'serviceKey' : key
            , 'numOfRows' : 10000
            , 'pageNo' : 1
            , 'resultType' : "json"
            , 'beginBasDt' : last_day
            }

    response = requests.get(url ,params=params)
    total_count = response.json()['response']['body']['totalCount']
    if total_count == 0 :
        print("Nothing to Update")
        return 0
    df = pd.DataFrame(response.json()['response']['body']['items']['item'])
    df.to_sql(name='stockinfotable', con=db_connection, if_exists='append',index=False)  
    if total_count > params['numOfRows']:
        for i in range(2,total_count//10000+1):
            params['pageNo'] = i
            errorCount = 1
            while True:
                try:
                    response = requests.get(url ,params=params,verify=True)
                    temp_df =  pd.DataFrame(response.json()['response']['body']['items']['item'])
                    temp_df.to_sql(name='stockinfotable', con=db_connection, if_exists='append',index=False)
                    break
                except Exception:
                    time.sleep(5)
                    print(f'error:{errorCount}')
                    if errorCount==10:
                        print(f"Error: {temp_df}")
                        break
                    continue
            time.sleep(0.5)



def getDatabaseConnection():
    # connect to  MySql DataBase
    # db_connection_str = 'mysql+pymysql://root:@localhost/stockDB'
    # for home
    # db_connection_str = 'mysql+pymysql://root:1234@localhost/stock_db'
    # for ec2
    db_connection_str = 'mysql+pymysql://stock_user:1234asde@172.31.13.248/stockDB'
    db_connection = create_engine(db_connection_str)
    conn = db_connection.connect()
    return db_connection ,conn


def getLastDay(db_connection):
    with db_connection.connect() as conn:
        last_day = pd.read_sql_query(text("SELECT basDt FROM stockDB.stockinfotable ORDER BY basDt DESC LIMIT 1;"), conn)
    start_day = (last_day + timedelta(days=1))['basDt'][0]
    result  = str(start_day.date()).replace("-","")
    return result



if __name__ == "__main__":
    db_connection,conn = getDatabaseConnection()
    getStockInfo(db_connection)
    stockCodeJson = getStockCode()
    saveStockCode(stockCodeJson,db_connection)
    conn.close()
# sys.argv의 첫번째 값은 path/파일명 다음 값부터는 입력받은 인수값이다.
## ex) python test.py ho 에서의 sys.argv[1] 는 test.py이고 sys.argv[1] 는 "ho"이다.

# 다음의 코드를 getStockInfo에 추가
 if len(sys.argv) != 2:
        print(sys.argv)
        print("키값이 입력이 되지 않았습니다.")
        sys.exit()
    

key = sys.argv[1]

if key == "test":
    print(sys.argv)
    print("키값이 제대로 입력되지 않았습니다.")
    sys.exit()

2. docker file 수정 

ENV 를 추가해 주었다..

# docker file

#사용할 베이스 도커 이미지
FROM python:3.9.18
#API KEY 값
## 임의의 KEY값을 넣은 후에 container run 할 때 새로운 값을 입력 받는다. 
ENV KEY test

# 해당 디렉토리로 이동
## copy . . 과 같이 쓰면 : 이미지를 빌드한 디렉터리의 모든 파일을 컨테이너의 /usr/src/app 디렉터리로 복사
WORKDIR /usr/src/app
COPY . .

# python pip 설치 후 필수 라이브러리 설치 
RUN python -m pip install --upgrade pip 
RUN pip install -r requirements.txt



CMD python daily.py ${KEY}

3. 전역 환경 변수 추가(옵션)

.bashrc 에 마지막 줄에

export API_KEY=`cat $HOME/apikey/key.txt`

을 추가해주었다.

그 후  source ~/.bashrc 명령어를 통해 바뀐 내용을 적용하였다.

 

4. docker container 생성

 # container 명은 stock-data , --env 옵션을 통해 KEY값을 다시 지정해준다.
 docker container run --name stock-data --env KEY=$API_KEY stock-data