장고를 쿠버네티스에 올려보자
1. requirements.txt 필요한 패키지 확인해보자
pip freeze

장고를 이미지로 만들어보자
일단 도커 깔린 가상환경으로 들어가자
ssh 192.168.56.104
1) 도커 이미지 만들 디렉토리 생성
mkdir donpa_django
cd donpa_django
2) sftp로 donpa장고 디렉토리 가져오기
sftp user@192.168.35.184 >>> sftp접속
get -r donpa >>> 던파 디렉토리 가져오기

가져와 진거 확인
3) setting.py 수정해주자
실제 운영 환경에서 모든 호스트를 허용하려는 경우, ALLOWED_HOSTS 설정을 다음과 같이 수정

데이터 베이스 관련 정보 수정 host를 db클러스터ip로 수정해주자!!!

4) requirements.txt
vim requirements.txt(설치할 패키지 목록)
asgiref==3.7.2
pytz
Django==4.0.3
mysqlclient==2.2.0
PyMySQL==1.1.0
sqlparse==0.4.4
Typing-extensions==4.7.1
4) vim dockerfile
# Base image
FROM python:latest
# 설정할 작업 디렉토리
WORKDIR /app
# 호스트의 현재 디렉토리의 모든 파일을 작업 디렉토리로 복사
COPY donpa/ /app
COPY requirements.txt /app
# 필요한 종속성 설치
RUN pip install --no-cache-dir -r requirements.txt
# 컨테이너 실행 시 실행할 명령어
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
~
5) 이제 이미지 만들자
docker build -t sy02229/donpa_django:latest .
docker push sy02229/donpa_django:latest
1시간뒤 가격을 lstm으로 예측, 결과를 donpa_item1 테이블에 넣는 최종 이미지
import pandas as pd
from pymongo import MongoClient
from datetime import datetime
from sklearn.preprocessing import StandardScaler
import numpy as np
# TensorFlow에서 Keras 모듈을 불러옵니다
import tensorflow as tf
# 레이어를 선형으로 쌓아 순차적으로 연결하여 모델을 만드는 방식
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import Huber
from tensorflow.keras.callbacks import EarlyStopping
import pymysql
from kubeflow import fairing
from kubeflow.fairing.builders.append.append import AppendBuilder
from kubeflow.fairing.preprocessors.converted_notebook import ConvertNotebookPreprocessor
from kubeflow.fairing.kubernetes import utils as k8s_utils
import os
class MyFashionMnist(object):
def train(self):
import pymysql
# 연결 정보 설정
host = "10.103.132.127"
port = 3306
database = "donpa_item"
user = "root"
password = "1234"
# 연결
connection = pymysql.connect(
host=host,
port=port,
user=user,
password=password,
database=database
)
# SQL 쿼리
query = "SELECT * FROM goldprice"
date_df = pd.read_sql(query, connection)
# 연결 종료
connection.close()
# MongoDB 연결 설정
client = MongoClient("mongodb://3.38.178.84:27017/")
db = client["donpa"] # 데이터베이스명
collection = db["donpa_data"] # 컬렉션명
# 컬렉션 데이터 조회
data = collection.find()
# 데이터프레임으로 변환
df = pd.DataFrame(data)
# _id 컬럼은 제거해주자
df = df.drop(columns="_id")
# object타입의 데이터 datetime타입으로 변경하는 코드
df['now_datetime'] = pd.to_datetime(df['now_datetime'])
# 시간까지만 잘라내기
df['now_datetime'] = df['now_datetime'].dt.strftime("%Y-%m-%d %H:%M")
# now_datetime 다시 datetime타입으로 변경하는 코드
df['now_datetime'] = pd.to_datetime(df['now_datetime'])
# unitPrice 컬럼 float타입으로 변경
df['unitPrice'] = df['unitPrice'].astype('float')
#soldDate 컬럼 인덱스로 변경
df.set_index('now_datetime', inplace=True)
# itemName컬럼은 사용하지 안을거임
item_name = df.iloc[0,0] # 지우기전 아이템 이름 저장해주자
df = df.drop(columns= 'itemName')
df = df.reset_index(drop=False)
# 피처 엔지니어링
# 30분 이평선
ma3 = df['unitPrice'].rolling(window=3).mean()
df.insert(len(df.columns), 'ma3', ma3)
# 기존 데이터가 있다면 NaN에 이동평균선 값을 채워넣어주는 방법
# 기존 데이터가 없다면 NaN에 0 값을 채워줘야 함
df = df.fillna(0)
# unitPrice 컬럼 float타입으로 변경
df['unitPrice'] = df['unitPrice'].astype('float')
# sell, buy컬럼을 합쳐주기 위해 date컬럼 생성
df['date'] = pd.to_datetime(df['now_datetime']).dt.strftime('%Y%m%d')
# sell, buy컬럼 붙여주자!!
df = df.merge(date_df, left_on='date', right_on='date', how='left')
# sell, buy값이 널값이면 가장 최근의 sell, buy값으로 대체해주자
null_check = df['sell'].isnull().sum()
if null_check != 0:
# date_df의 맨 마지막 값을 가져와서 NaN 값을 채우기
last_date_df_row = date_df.iloc[-1]
df['sell'].fillna(last_date_df_row['sell'], inplace=True)
df['buy'].fillna(last_date_df_row['buy'], inplace=True)
# 붙여주고 date는 이제 필요없으므로 drop시켜줌
df.drop(columns = 'date', inplace=True)
# 이전가격 변수에 저장해 두기
before_now = df.iloc[-1,1]
before_one = df.iloc[-2,1]
before_two = df.iloc[-3,1]
before_three = df.iloc[-4,1]
before_four = df.iloc[-5,1]
before_five = df.iloc[-6,1]
before_six = df.iloc[-7,1]
before_seven = df.iloc[-8,1]
before_eight = df.iloc[-9,1]
before_nine = df.iloc[-10,1]
before_ten = df.iloc[-11,1]
# 실제 값
original_open = df['unitPrice'].values
# 날짜 값
dates = pd.to_datetime(df['now_datetime'])
# 사용할 컬럼만 가져오기
cols = list(df)[1:]
# 데이터타입 float로 변경
df = df[cols].astype(float)
# standardscaler스케일링
scaler = StandardScaler()
scaler = scaler.fit(df)
df_scaler = scaler.transform(df)
# 학습용, 테스트 9대1로 나눔
n_train = int(0.9*df_scaler.shape[0]) # 90퍼 비율
train_data_scaled = df_scaler[:n_train] # train 데이터
train_dates = dates[:n_train] # train 날짜
test_data_scaled = df_scaler[n_train:] # test 데이터
test_dates = dates[n_train:] # test 날짜
# 직전 24개의 데이터를 기반으로 10분뒤 가격을 예측하는 것을 목표
# 데이터 구조를 lstm의 입력과 출력에 맞게 바꿔주자
pred_days = 1
seq_len = 24
input_dim = 4
X_train = []
Y_train = []
X_test = []
Y_test = []
for i in range(seq_len, n_train-pred_days +1):
X_train.append(train_data_scaled[i - seq_len:i, 0:train_data_scaled.shape[1]])
Y_train.append(train_data_scaled[i + pred_days - 1:i + pred_days, 0])
for i in range(seq_len, len(test_data_scaled)-pred_days +1):
X_test.append(test_data_scaled[i - seq_len:i, 0:test_data_scaled.shape[1]])
Y_test.append(test_data_scaled[i + pred_days - 1:i + pred_days, 0])
X_train, Y_train = np.array(X_train), np.array(Y_train)
X_test, Y_test = np.array(X_test), np.array(Y_test)
# lstm모델
model = Sequential()
model.add(LSTM(64, input_shape=(X_train.shape[1], X_train.shape[2]), # (seq length, input dimension)
return_sequences=True))
model.add(LSTM(32, return_sequences=False))
model.add(Dense(Y_train.shape[1]))
# 모델 피팅
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import Huber
from tensorflow.keras.callbacks import EarlyStopping
# specify your learning rate
learning_rate = 0.01
# create an Adam optimizer with the specified learning rate
optimizer = Adam(learning_rate=learning_rate)
# compile your model using the custom optimizer
model.compile(optimizer=optimizer, loss=Huber(), metrics=['mse'])
earlystopping = EarlyStopping(monitor='val_loss', patience=5)
# 모델 피팅
history = model.fit(X_train, Y_train, epochs=100, batch_size=32,
validation_split=0.1, verbose=1, callbacks = [earlystopping])
# 마지막 데이터에 대한 예측을 위한 입력 데이터 준비
last_input = df_scaler[-seq_len:]
# 하나만 예측하므로 배치크기는 1
last_input = last_input.reshape(1, seq_len, input_dim)
# 10분 뒤의 unitPrice 예측
predicted_unitPrice = model.predict(last_input)
# 스케일링을 원래 데이터 범위로 되돌리기
predicted_unitPrice = scaler.inverse_transform(np.array([[predicted_unitPrice[0][0], 0, 0, 0]]))[0][0]
# 연결 정보 설정
host = "10.103.132.127"
port = 3306
database = "donpa_item"
user = "root"
password = "1234"
# 연결
connection = pymysql.connect(
host=host,
port=port,
user=user,
password=password,
database=database
)
# item_img, item_name, price 컬럼 값 설정
item_name = item_name # 변수 값
item_img = "https://img-api.neople.co.kr/df/items/8b998230ab012e695e9a53d67187cc3f"
price = predicted_unitPrice
# 쿼리 생성
update_query = f"""
UPDATE donpa_item1
SET price = {price}, item_img = '{item_img}', before_now = {before_now}, before_one = {before_one}, before_two = {before_two}, before_three = {before_three}, before_four = {before_four}, before_five = {before_five}, before_six = {before_six}, before_seven = {before_seven}, before_eight = {before_eight}, before_nine = {before_nine}, before_ten = {before_ten}
WHERE item_name = '{item_name}'
"""
insert_query = f"""
INSERT INTO donpa_item1 (item_name, item_img, before_now, before_one, before_two, before_three, before_four, before_five, before_six, before_seven, before_eight, before_nine, before_ten, price)
VALUES ('{item_name}', '{item_img}', {before_now}, {before_one}, {before_two}, {before_three}, {before_four}, {before_five}, {before_six}, {before_seven}, {before_eight}, {before_nine}, {before_ten}, {price})
"""
# 데이터베이스 작업 수행
try:
with connection.cursor() as cursor:
cursor.execute(update_query) # item_name 값이 있는 경우 업데이트 시도
if cursor.rowcount == 0:
cursor.execute(insert_query) # item_name 값이 없는 경우 새로운 행 추가
connection.commit() # 변경 내용을 커밋
except Exception as e:
connection.rollback() # 에러가 발생한 경우 롤백
# 연결 종료
connection.close()
if __name__ == '__main__':
if os.getenv('FAIRING_RUNTIME', None) is None:
# Kubeflow Fairing의 필요한 모듈을 가져옵니다.
from kubeflow.fairing.builders.append.append import AppendBuilder
from kubeflow.fairing.preprocessors.converted_notebook import ConvertNotebookPreprocessor
# Docker 이미지 관련 설정
DOCKER_REGISTRY = 'kubeflow-registry.default.svc.cluster.local:30000'
base_image = 'sy02229/new_image:latest'
image_name = 'fairing-job1'
# Fairing 빌더 설정
builder = AppendBuilder(
registry=DOCKER_REGISTRY,
image_name=image_name,
base_image=base_image,
push=True,
preprocessor=ConvertNotebookPreprocessor(
notebook_file="Untitled1.ipynb" # 노트북 파일 지정
)
)
builder.build() # Docker 이미지 빌드 및 푸시
else:
# Kubeflow Fairing 런타임 환경에서 직접 실행되는 경우
myModel = MyFashionMnist()
myModel.train() # 모델 훈련 실행
던파 뉴스 크롤링, db에 데이터 넣는 이미지
main.py
import requests
from bs4 import BeautifulSoup
import pymysql
# MySQL 연결 설정
conn = pymysql.connect(
host='10.103.132.127', # 호스트명
user='root', # 데이터베이스 사용자명
password='1234', # 데이터베이스 암호
db='donpa_item', # 데이터베이스 이름
port = 3306
)
# 커서 생성
cursor = conn.cursor()
# 크롤링 코드 작성
photo_list = []
title_list = []
link_list = []
# 검색 결과 페이지 URL
url = "https://search.naver.com/search.naver?where=news&query=%EB%8D%98%ED%8C%8C&sm=tab_opt&sort=1&photo=1&field=0&pd=0&ds=&de=&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Add%2Cp%3Aall&is_sug_officeid=0&office_category=0&service_area=0"
# HTTP GET 요청
response = requests.get(url)
# 응답 데이터 파싱
soup = BeautifulSoup(response.text, "html.parser")
select_num = 1
while select_num<=10:
# 이미지 선택자
selector = f"#sp_nws{select_num} > div > a > img"
# 선택자에 해당하는 이미지 요소를 찾음
image = soup.select_one(selector)
# 각 링크의 href 속성값을 출력
photo = image.get("data-lazysrc")
photo_list.append(photo)
select_num+=1
# 뉴스 타이틀, 링크 선택자
selector = "#main_pack > section.sc_new.sp_nnews._prs_nws > div > div.group_news > ul > li> div > div > a"
# 선택자에 해당하는 요소들을 모두 찾음
title_link = soup.select(selector)
# 각 타이틀,링크 속성값을 출력
for link in title_link:
title = link.get("title")
href = link.get("href")
title_list.append(title)
link_list.append(href)
# 테이블 초기화 (기존 데이터 삭제)
truncate_sql = "TRUNCATE TABLE donpa_news"
cursor.execute(truncate_sql)
# 데이터베이스에 데이터 삽입
for i in range(len(photo_list)):
photo = photo_list[i]
title = title_list[i]
link = link_list[i]
sql = "INSERT INTO donpa_news (photo, title, link) VALUES (%s, %s, %s)"
cursor.execute(sql, (photo, title, link))
# 변경사항 커밋
conn.commit()
# 연결 종료
conn.close()
requirements.txt
requests
beautifulsoup4
pymysql
dockerfile
# 베이스 이미지 설정
FROM python:latest
# 작업 디렉토리 설정
WORKDIR /app
# main, requirements 두 파일 작업디렉토리로 복사
COPY main.py /app/main.py
COPY requirements.txt /app
# 필요한 종속성 설치
RUN pip install --no-cache-dir -r requirements.txt
CMD [ "python" , "main.py" ]
이미지 저장하는 명령어
docker build -t sy02229/donpa_news:latest .
이미지 푸쉬하는 명령어
docker push sy02229/donpa_news
던파 뉴스 이미지 크론잡
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: donpa-news-cj
spec:
schedule: "*/2 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: donpa-news-cj
spec:
restartPolicy: OnFailure
containers:
- name: donpa-news-cronjob
image: sy02229/donpa_news:latest
파드 실행후 테이블에 데이터가 저장되었는지 확인
# MySQL 클라이언트 컨테이너에 접속
kubectl exec -it mysql-client-75c74cbc5c-6xmb9 -- bash
# MySQL 클라이언트 실행 (예: root 계정으로 접속)
mysql -h 10.103.132.127 -u root -p
use donpa_item
show tables;
select * from donpa_news;
던파 이벤트 크롤링, db에 데이터 넣는 이미지
main.py
import requests
from bs4 import BeautifulSoup
import re
import pymysql
# MySQL 연결 설정
conn = pymysql.connect(
host='10.103.132.127', # 호스트명
user='root', # 데이터베이스 사용자명
password='1234', # 데이터베이스 암호
db='donpa_item', # 데이터베이스 이름
port = 3306
)
# 커서 생성
cursor = conn.cursor()
eve_img = []
eve_text = []
eve_date = []
eve_herf = []
# 검색 결과 페이지 URL
url = "https://df.nexon.com/community/news/event/list"
# HTTP GET 요청
response = requests.get(url)
# 응답 데이터 파싱
soup = BeautifulSoup(response.text, "html.parser")
# 이벤트 img 셀렉터
selector_img = "#wrap > section.content.news > article.board_eventlist > ul > li > img"
# 이벤트 b 셀렉터
selector_text = "#wrap > section.content.news > article.board_eventlist > ul > li > b"
# 이벤트 날짜 span 셀렉터
selector_date = "#wrap > section.content.news > article.board_eventlist > ul > li > span"
# 이벤트 링크가 포함된 li 태그들 선택
selector_href = "#wrap > section.content.news > article.board_eventlist > ul > li"
# 선택자에 해당하는 요소들을 모두 찾음
event_img = soup.select(selector_img)
# URL에 프로토콜을 추가하여 출력
for img in event_img:
src = img.get('src')
if src.startswith('//'):
src = 'https:' + src
eve_img.append(src)
# 선택자에 해당하는 요소들을 모두 찾음
event_text = soup.select(selector_text)
# 각 링크의 텍스트만 추출하여 리스트에 저장
event_text = [link.get_text(strip=True) for link in event_text]
# 결과 출력
for text in event_text:
eve_text.append(text)
# 선택자에 해당하는 요소들을 모두 찾음
event_dates = soup.select(selector_date)
# 결과 출력
for span in event_dates:
date_text = span.text.strip().replace('\n', '').strip().replace(' ','') # span 태그 안의 텍스트>에서 공백 및 줄바꿈 제거
eve_date.append(date_text)
event_href = soup.select(selector_href)
# 결과 출력
for li in event_href:
if li.get("data-no"):
event_url = f"http://df.nexon.com/community/news/event/{li.get('data-no')}?categoryType=0"
eve_herf.append(event_url)
else:
li = li.get("onclick")
if 'window.location.href' in li:
# 정규 표현식을 사용하여 값을 추출합니다.
li = re.findall(r"'(.*?)';", li)
eve_herf.append("https://df.nexon.com"+''.join(li))
elif 'neople.openCouponPop()' in li:
li = "https://df.nexon.com/community/news/event/list"
eve_herf.append(li)
elif 'window.open' in li:
# 정규 표현식을 사용하여 URL 추출
li = re.search(r"https://[a-zA-Z0-9./-]+", li).group()
eve_herf.append(li)
# 테이블 초기화 (기존 데이터 삭제)
truncate_sql = "TRUNCATE TABLE donpa_event"
cursor.execute(truncate_sql)
# 데이터베이스에 데이터 삽입
for i in range(len(eve_img)):
img = eve_img[i]
text = eve_text[i]
date = eve_date[i]
herf = eve_herf[i]
sql = "INSERT INTO donpa_event (img, text, date, herf) VALUES (%s, %s, %s, %s)"
cursor.execute(sql, (img, text, date, herf))
# 변경사항 커밋
conn.commit()
# 연결 종료
conn.close()
requirements.txt
requests
beautifulsoup4
pymysql
dockerfile
FROM python:latest
# 작업 디렉토리 설정
WORKDIR /app
# main, requirements 두 파일 작업디렉토리로 복사
COPY main.py /app/main.py
COPY requirements.txt /app
# 필요한 종속성 설치
RUN pip install --no-cache-dir -r requirements.txt
CMD [ "python" , "main.py" ]
이미지 저장하는 명령어
docker build -t sy02229/donpa_event:latest .
이미지 푸쉬하는 명령어
docker push sy02229/donpa_event
던파 이벤트 이미지 크론잡
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: donpa-event-cj
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: donpa-event-cj
spec:
restartPolicy: OnFailure
containers:
- name: donpa-event-cronjob
image: sy02229/donpa_event:latest
파드 실행후 테이블에 데이터가 저장되었는지 확인
# MySQL 클라이언트 컨테이너에 접속
kubectl exec -it mysql-client-75c74cbc5c-6xmb9 -- bash
# MySQL 클라이언트 실행 (예: root 계정으로 접속)
mysql -h 10.103.132.127 -u root -p
use donpa_item
show tables;
select * from donpa_news;
이제 만들었던 장고 이미지를 쿠버네티스에 띄워보자!!!
일단 테스트용으로 레플리카셋으로 띄워보자
vim donpa-django-rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: donpa-django-rs
spec:
replicas: 2
selector:
matchLabels:
app: donpa-django-rs
template:
metadata:
labels:
app: donpa-django-rs
spec:
containers:
- name: donpa-django
image: sy02229/donpa_django:latest
ports:
- containerPort: 8000
외부 노출을 위해 노드포트 서비스 생성
vim donpa-svc-np.yaml
apiVersion: v1
kind: Service
metadata:
name: donpa-django-service
spec:
type: NodePort
selector:
app: donpa-django-rs
ports:
- protocol: TCP
port: 8000
targetPort: 8000 # 실제 MariaDB 파드의 포트
nodePort: 30001
http://35.194.179.62:30001 로 접속이 되는지 확인해보자

성공!!!!
'쿠버네티스,쿠버플로우' 카테고리의 다른 글
| 던전앤파이터 시세 예측[7] (1) | 2023.09.14 |
|---|---|
| 던전앤파이터 시세 예측[6] (1) | 2023.09.12 |
| 던전앤파이터 시세 예측[5] (1) | 2023.09.06 |
| 던전앤파이터 시세 예측[4] (0) | 2023.09.02 |
| 던전앤파이터 시세 예측[장고] (0) | 2023.09.01 |