쿠버네티스 미니 프로젝트
1. 일단 db데이터 저장할 스토리지를 구성해보자!!!
nfs스토리지 구성!!! 이 작업은 NFS 서버로 구성할 kube-control1 호스트에서 작업
1) nfs 서버 패키지를 설치 >>> sudo apt install -y nfs-kernel-server
2) nfs 공유 디렉터리를 설정한다 >>> sudo mkdir /srv/nfs-volume
3) NFS 내보내기를 설정한다 >>> echo "/srv/nfs-volume *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports
4) NFS 내보내기 설정을 다시 읽어 들인다. >>> sudo exportfs -arv
5) NFS 클라이언트 구성(노드1, 노드2, 노드3) NFS클라이언트로 설정할 노드1, 노드2, 노드3에 NFS 클라이언트 패키지를 설치한다!!
vagrant ssh kube-node1
sudo apt install -y nfs-common
vagrant ssh kube-node2
sudo apt install -y nfs-common
vagrant ssh kube-node3
sudo apt install -y nfs-common
6) GitHub에서 Kubernetes NFS Subdir External Provisioner 프로젝트의 소스 코드를 복제 >>> cd ~ , git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
GitHub - kubernetes-sigs/nfs-subdir-external-provisioner: Dynamic sub-dir volume provisioner on a remote NFS server.
Dynamic sub-dir volume provisioner on a remote NFS server. - GitHub - kubernetes-sigs/nfs-subdir-external-provisioner: Dynamic sub-dir volume provisioner on a remote NFS server.
github.com
7) 방금 복제한 Kubernetes NFS Subdir External Provisioner 프로젝트의 소스 코드 디렉토리 안에 있는 "deploy" 디렉토리로 이동 >>> cd ~/nfs-subdir-external-provisioner/deploy/
8) deployment.yaml 파일 수정하기

- 컨테이너:
- 이름: nfs-client-provisioner
- 이미지: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
- 환경 변수:
- PROVISIONER_NAME: k8s-sigs.io/nfs-subdir-external-provisioner (프로비저너 이름)
- NFS_SERVER: 192.168.56.11 (NFS 서버 주소)
- NFS_PATH: /srv/nfs-volume (NFS 서버 내의 경로)
- 볼륨 마운트:
- 이름: nfs-client-root
- 마운트 경로: /persistentvolumes
- 볼륨:
- 이름: nfs-client-root
- 타입: nfs (NFS 프로토콜)
- NFS 서버 및 경로:
- 서버: 192.168.56.11
- 경로: /srv/nfs-volume
이 설정을 사용하여 Kubernetes 클러스터에 NFS Subdir External Provisioner를 배포하면, 클러스터 내에서 PV을 동적으로 프로비저닝할 수 있게 됩니다.
9) Kubernetes에서 현재 디렉토리에 있는 Kubernetes 리소스의 YAML 파일을 사용하여 배포하는 명령 >>> kubectl apply -k .
10) NFS 클라이언트 Provisioner의 StorageClass를 기본 StorageClass로 설정하고 있습니다. 기본 StorageClass로 설정된 StorageClass는 PVC(Persistent Volume Claim)를 만들 때, 별도의 StorageClass를 명시하지 않은 경우에 사용 >>> kubectl patch storageclasses.storage.k8s.io nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
2. 스테이트풀셋을 활용해 mysql 백업 복제본 구성
1) mysql 설정 파일 컨피그맵 생성
vim mydb-cm-mysql.yaml
---------------------------------------------------------------------------------------
apiVersion: v1
kind: ConfigMap
metadata:
name: mydb-config
labels:
app: mydb
app.kubernetes.io/name: mydb
data:
primary.cnf: |
[mysqld]
log-bin
replica.cnf: |
[mysqld]
super-read-only
-------------------------------------------------------------------------------------------------------
data: ConfigMap의 데이터를 정의하는 섹션입니다. 여기서는 두 가지 설정 파일을 포함하고 있습니다.
- primary.cnf: primary 서버를 위한 설정 파일로 [mysqld] 섹션 아래에 log-bin 옵션이 설정되어 있습니다. 이 설정은 MySQL의 이진 로그(binlog)를 활성화시키는 것을 의미합니다.
- replica.cnf: replica 서버를 위한 설정 파일로 [mysqld] 섹션 아래에 super-read-only 옵션이 설정되어 있습니다. 이 설정은 MySQL의 읽기 전용(super read-only) 모드를 활성화시키는 것을 의미합니다.
kubectl create -f mydb-cm-mysql.yaml
2) 서비스 생성(두개 생성해야됨 헤드리스, 비헤드리스)
- 데이터베이스 쓰기를 위한 헤드리스 서비스
vim mydb-svc-write.yaml
-----------------------------------------------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: mydb
labels:
app: mydb
app.kubernetes.io/name: mydb
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mydb
app.kubernetes.io/name: mydb
-----------------------------------------------------------------------------------------------
spec: Service의 스펙을 정의하는 섹션입니다.
- ports: Service가 노출할 포트를 지정합니다. 여기서는 port: 3306으로 MySQL의 포트인 3306을 노출하고 있습니다.
- clusterIP: None: Headless Service의 특성을 나타내는 부분으로, 클러스터 IP를 할당하지 않음을 의미합니다.
- selector: Service가 연결할 파드를 선택하는 데 사용됩니다. 여기서는 app: mydb와 app.kubernetes.io/name: mydb 라벨이 mydb인 파드를 선택하도록 지정하고 있습니다. 이렇게 지정된 파드들이 해당 Headless Service와 연결되며, 클러스터 IP가 없으므로 외부에서 바로 파드의 IP 주소로 접근할 수 있습니다.
- 데이터베이스 읽기를 위한 비-헤드리스 서비스
vim mydb-svc-read.yaml
-------------------------------------------------------------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: mydb-read
labels:
app: mydb
app.kubernetes.io/name: mydb
spec:
ports:
- name: mysql
port: 3306
selector:
app: mydb
app.kubernetes.io/name: mydb
-------------------------------------------------------------------------------------------------------------
spec: Service의 스펙을 정의하는 섹션입니다.
- ports: Service가 노출할 포트를 지정합니다. 여기서는 port: 3306으로 MySQL의 포트인 3306을 노출하고 있습니다.
- selector: Service가 연결할 파드를 선택하는 데 사용됩니다. 여기서는 app: mydb와 app.kubernetes.io/name: mydb 라벨이 mydb인 파드를 선택하도록 지정하고 있습니다. 이렇게 지정된 파드들이 해당 Service와 연결되며, 클러스터 IP가 부여되어 클러스터 내부에서 서비스에 접근할 수 있게 됩니다.
kubectl create -f mydb-svc-write.yaml -f mydb-svc-read.yaml
3) 고가용성을 위한 mysql 데이터베이스 생성
vim mydb-sts-mysql.yaml
------------------------------------------------------------------------------------------------------------------
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mydb
spec:
selector:
matchLabels:
app: mydb
app.kubernetes.io/name: mydb
serviceName: mydb
replicas: 2
template:
metadata:
labels:
app: mydb
app.kubernetes.io/name: mydb
spec:
initContainers:
- name: init-mysql
image: mysql:5.7
command:
- bash
- "-c"
- |
set -ex
[[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/primary.cnf /mnt/conf.d/
else
cp /mnt/config-map/replica.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: gcr.io/google-samples/xtrabackup:1.0
command:
- bash
- "-c"
- |
set -ex
[[ -d /var/lib/mysql/mysql ]] && exit 0
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal -eq 0 ]] && exit 0
ncat --recv-only mydb-$(($ordinal-1)).mydb 3307 | xbstream -x -C /var/lib/mysql
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
- name: xtrabackup
image: gcr.io/google-samples/xtrabackup:1.0
ports:
- name: xtrabackup
containerPort: 3307
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
rm -f xtrabackup_slave_info xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
[[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
rm -f xtrabackup_binlog_info xtrabackup_slave_info
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
fi
if [[ -f change_master_to.sql.in ]]; then
echo "Waiting for mysqld to be ready (accepting connections)"
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
mysql -h 127.0.0.1 \
-e "$(<change_master_to.sql.in), \
MASTER_HOST='mydb-0.mydb', \
MASTER_USER='root', \
MASTER_PASSWORD='', \
MASTER_CONNECT_RETRY=10; \
START SLAVE;" || exit 1
mv change_master_to.sql.in change_master_to.sql.orig
fi
exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
volumes:
- name: conf
emptyDir: {}
- name: config-map
configMap:
name: mydb-config
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
------------------------------------------------------------------------------------------------------------------
kubectl create -f mydb-sts-mysql.yaml
쓰기 db에 접속해 테이블을 만들 testdb데이터베이스를 만들어야됨!!!
3. 신용등급 예측을 위한 파이썬코드를 컨테이너로 만들고, 크론잡 파드를 생성해 주기별로 실행시켜 데이터베이스에 데이터를 최신화 시켜준다
1) 도커 컨테이너
main.py
import pymysql
import pandas as pd
import numpy as np
# CSV 파일을 데이터프레임으로 읽어오기
data = pd.read_csv("https://raw.githubusercontent.com/sy0218/kubernet/main/Desktop/%EB%B9%85%EB%B6%84%EA%B8%B0%20%EC%A4%80%EB%B9%84/csv%EB%AA%A8%EC%9D%8C/train.csv")
data1 = pd.read_csv("https://raw.githubusercontent.com/sy0218/kubernet/main/test.csv")
data.fillna('NAN', inplace=True)
data1.fillna('NAN', inplace=True)
# MariaDB 연결 설정
host = 'mydb-0.mydb' # external IP
port = 3306 # 포트번호
database = 'testdb' # 데이터베이스명
# MariaDB 연결
conn = pymysql.connect(host=host, port=port, database=database)
cursor = conn.cursor()
# 테이블 생성 SQL 문 작성
table_name = 'train'
table_name1 = 'test'
# 테이블이 이미 존재하는 경우 삭제
drop_table_sql = "DROP TABLE IF EXISTS `{}`".format(table_name)
drop_table_sql1 = "DROP TABLE IF EXISTS `{}`".format(table_name1)
cursor.execute(drop_table_sql)
cursor.execute(drop_table_sql1)
create_table_sql = '''
CREATE TABLE `{}` (
`index` INT,
`gender` VARCHAR(1),
`car` VARCHAR(1),
`reality` VARCHAR(1),
`child_num` INT,
`income_total` FLOAT,
`income_type` VARCHAR(50),
`edu_type` VARCHAR(50),
`family_type` VARCHAR(50),
`house_type` VARCHAR(50),
`DAYS_BIRTH` INT,
`DAYS_EMPLOYED` INT,
`FLAG_MOBIL` INT,
`work_phone` INT,
`phone` INT,
`email` INT,
`occyp_type` VARCHAR(50),
`family_size` FLOAT,
`begin_month` FLOAT,
`credit` FLOAT
)
'''.format(table_name)
create_table_sql1 = '''
CREATE TABLE `{}` (
`index` INT,
`gender` VARCHAR(1),
`car` VARCHAR(1),
`reality` VARCHAR(1),
`child_num` INT,
`income_total` FLOAT,
`income_type` VARCHAR(50),
`edu_type` VARCHAR(50),
`family_type` VARCHAR(50),
`house_type` VARCHAR(50),
`DAYS_BIRTH` INT,
`DAYS_EMPLOYED` INT,
`FLAG_MOBIL` INT,
`work_phone` INT,
`phone` INT,
`email` INT,
`occyp_type` VARCHAR(50),
`family_size` FLOAT,
`begin_month` FLOAT
)
'''.format(table_name1)
# 테이블 생성
cursor.execute(create_table_sql)
cursor.execute(create_table_sql1)
# 데이터프레임의 데이터를 테이블에 삽입하는 SQL 문 실행
insert_data_sql = '''
INSERT INTO `{}` (
`index`, `gender`, `car`, `reality`, `child_num`, `income_total`,
`income_type`, `edu_type`, `family_type`, `house_type`, `DAYS_BIRTH`,
`DAYS_EMPLOYED`, `FLAG_MOBIL`, `work_phone`, `phone`, `email`,
`occyp_type`, `family_size`, `begin_month`, `credit`
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
'''.format(table_name)
insert_data_sql1 = '''
INSERT INTO `{}` (
`index`, `gender`, `car`, `reality`, `child_num`, `income_total`,
`income_type`, `edu_type`, `family_type`, `house_type`, `DAYS_BIRTH`,
`DAYS_EMPLOYED`, `FLAG_MOBIL`, `work_phone`, `phone`, `email`,
`occyp_type`, `family_size`, `begin_month`
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
'''.format(table_name1)
# 데이터프레임의 각 행을 튜플로 변환하여 삽입
for row in data.itertuples(index=False):
cursor.execute(insert_data_sql, row)
# 데이터프레임의 각 행을 튜플로 변환하여 삽입
for row in data1.itertuples(index=False):
cursor.execute(insert_data_sql1, row)
# 변경사항 저장 및 연결 종료
conn.commit()
# SQL 쿼리 실행하여 테이블 가져오기
query = "SELECT * FROM train"
query1 = "SELECT * FROM test"
df = pd.read_sql(query, conn)
df1 = pd.read_sql(query1, conn)
# 데이터베이스 연결 종료
conn.close()
# 가져온 데이터프레임 출력
#df.head()
#필요한 데이터 가져오기
X_train = df
X_test = df1
#이상치 처리
X_train = X_train.loc[(X_train['family_size'] <= 7)]
X_test = X_test.loc[(X_test['family_size'] <= 7)]
X_train = X_train.reset_index(drop = True)
X_test = X_test.reset_index(drop = True)
#DAYS_EMPLOYED(양수인 데이터는 현재 무직자로 판단, 0 처리)
X_train['DAYS_EMPLOYED'] = X_train['DAYS_EMPLOYED'].map(lambda x: 0 if x > 0 else x)
X_test['DAYS_EMPLOYED'] = X_test['DAYS_EMPLOYED'].map(lambda x: 0 if x > 0 else x)
#DAYS_BIRTH, begin_month, DAYS_EMPLOYED 컬럼 음수값 >>> 양수 변환
for i in ['DAYS_BIRTH', 'begin_month', 'DAYS_EMPLOYED']:
X_train[i]=X_train[i].map(lambda x : abs(x))
X_test[i]=X_test[i].map(lambda x : abs(x))
#수치형 컬럼이지만 범주형 데이터인 경우 object로 변경
for i in ['work_phone', 'phone' , 'email']:
X_train[i] = X_train[i].astype('object')
X_test[i] = X_test[i].astype('object')
#파생변수 제작
for df in [X_train,X_test]:
# before_EMPLOYED: 고용되기 전까지의 일수
df['before_EMPLOYED'] = df['DAYS_BIRTH'] - df['DAYS_EMPLOYED']
df['income_total_befofeEMP_ratio'] = df['income_total'] / df['before_EMPLOYED']
df['before_EMPLOYED_m'] = np.floor(df['before_EMPLOYED'] / 30) - ((np.floor(df['before_EMPLOYED'] / 30) / 12).astype(int) * 12)
df['before_EMPLOYED_w'] = np.floor(df['before_EMPLOYED'] / 7) - ((np.floor(df['before_EMPLOYED'] / 7) / 4).astype(int) * 4)
#DAYS_BIRTH 파생변수- Age(나이), 태어난 월, 태어난 주(출생연도의 n주차)
df['Age'] = df['DAYS_BIRTH'] // 365
df['DAYS_BIRTH_m'] = np.floor(df['DAYS_BIRTH'] / 30) - ((np.floor(df['DAYS_BIRTH'] / 30) / 12).astype(int) * 12)
df['DAYS_BIRTH_w'] = np.floor(df['DAYS_BIRTH'] / 7) - ((np.floor(df['DAYS_BIRTH'] / 7) / 4).astype(int) * 4)
#DAYS_EMPLOYED_m 파생변수- EMPLOYED(근속연수), DAYS_EMPLOYED_m(고용된 달) ,DAYS_EMPLOYED_w(고용된 주(고용연도의 n주차))
df['EMPLOYED'] = df['DAYS_EMPLOYED'] // 365
df['DAYS_EMPLOYED_m'] = np.floor(df['DAYS_EMPLOYED'] / 30) - ((np.floor(df['DAYS_EMPLOYED'] / 30) / 12).astype(int) * 12)
df['DAYS_EMPLOYED_w'] = np.floor(df['DAYS_EMPLOYED'] / 7) - ((np.floor(df['DAYS_EMPLOYED'] / 7) / 4).astype(int) * 4)
#ability: 소득/(살아온 일수+ 근무일수)
df['ability'] = df['income_total'] / (df['DAYS_BIRTH'] + df['DAYS_EMPLOYED'])
#income_mean: 소득/ 가족 수
df['income_mean'] = df['income_total'] / df['family_size']
#ID 생성: 각 컬럼의 값들을 더해서 고유한 사람을 파악(*한 사람이 여러 개 카드를 만들 가능성을 고려해 begin_month는 제외함)
df['ID'] = \
df['child_num'].astype(str) + '_' + df['income_total'].astype(str) + '_' +\
df['DAYS_BIRTH'].astype(str) + '_' + df['DAYS_EMPLOYED'].astype(str) + '_' +\
df['work_phone'].astype(str) + '_' + df['phone'].astype(str) + '_' +\
df['email'].astype(str) + '_' + df['family_size'].astype(str) + '_' +\
df['gender'].astype(str) + '_' + df['car'].astype(str) + '_' +\
df['reality'].astype(str) + '_' + df['income_type'].astype(str) + '_' +\
df['edu_type'].astype(str) + '_' + df['family_type'].astype(str) + '_' +\
df['house_type'].astype(str) + '_' + df['occyp_type'].astype(str)
# 업무시작일 대비 신용카드 사용기간 (0값 존재하므로 분모와 분자에 출생년도 합산 후 계산)
df['card_employed'] = (df['begin_month']+df['DAYS_BIRTH'])/(df['DAYS_EMPLOYED']+df['DAYS_BIRTH'])
# 자동차와 부동산 모두 보유하고 있는경우 1 / else 0
df['stable'] = df.apply(lambda x: 1 if (x['car']=='Y') & (x['reality']=='Y') else 0, axis=1)
df['stable'] = df['stable'].astype('object')
import pymysql
import pandas as pd
import numpy as np
from scipy.stats import skew
# CSV 파일을 데이터프레임으로 읽어오기
data = X_train
data1 = X_test
# MariaDB 연결 설정
host = 'mydb-0.mydb' # external IP
port = 3306 # 포트번호
database = 'testdb' # 데이터베이스명
# MariaDB 연결
conn = pymysql.connect(host=host, port=port, database=database)
cursor = conn.cursor()
# 테이블 생성 SQL 문 작성
table_name = 'train'
table_name1 = 'test'
# 테이블이 이미 존재하는 경우 삭제
drop_table_sql = "DROP TABLE IF EXISTS `{}`".format(table_name)
drop_table_sql1 = "DROP TABLE IF EXISTS `{}`".format(table_name1)
cursor.execute(drop_table_sql)
cursor.execute(drop_table_sql1)
create_table_sql = '''
CREATE TABLE `{}` (
`index` INT,
`gender` VARCHAR(1),
`car` VARCHAR(1),
`reality` VARCHAR(1),
`child_num` INT,
`income_total` FLOAT,
`income_type` VARCHAR(50),
`edu_type` VARCHAR(50),
`family_type` VARCHAR(50),
`house_type` VARCHAR(50),
`DAYS_BIRTH` INT,
`DAYS_EMPLOYED` INT,
`FLAG_MOBIL` INT,
`work_phone` INT,
`phone` INT,
`email` INT,
`occyp_type` VARCHAR(50),
`family_size` FLOAT,
`begin_month` FLOAT,
`credit` FLOAT,
`before_EMPLOYED` INT,
`income_total_befofeEMP_ratio` FLOAT,
`before_EMPLOYED_m` FLOAT,
`before_EMPLOYED_w` FLOAT,
`Age` INT,
`DAYS_BIRTH_m` FLOAT,
`DAYS_BIRTH_w` FLOAT,
`EMPLOYED` INT,
`DAYS_EMPLOYED_m` FLOAT,
`DAYS_EMPLOYED_w` FLOAT,
`ability` FLOAT,
`income_mean` FLOAT,
`ID` VARCHAR(200),
`card_employed` FLOAT,
`stable_column` INT
)
'''.format(table_name)
create_table_sql1 = '''
CREATE TABLE `{}` (
`index` INT,
`gender` VARCHAR(1),
`car` VARCHAR(1),
`reality` VARCHAR(1),
`child_num` INT,
`income_total` FLOAT,
`income_type` VARCHAR(50),
`edu_type` VARCHAR(50),
`family_type` VARCHAR(50),
`house_type` VARCHAR(50),
`DAYS_BIRTH` INT,
`DAYS_EMPLOYED` INT,
`FLAG_MOBIL` INT,
`work_phone` INT,
`phone` INT,
`email` INT,
`occyp_type` VARCHAR(50),
`family_size` FLOAT,
`begin_month` FLOAT,
`before_EMPLOYED` INT,
`income_total_befofeEMP_ratio` FLOAT,
`before_EMPLOYED_m` FLOAT,
`before_EMPLOYED_w` FLOAT,
`Age` INT,
`DAYS_BIRTH_m` FLOAT,
`DAYS_BIRTH_w` FLOAT,
`EMPLOYED` INT,
`DAYS_EMPLOYED_m` FLOAT,
`DAYS_EMPLOYED_w` FLOAT,
`ability` FLOAT,
`income_mean` FLOAT,
`ID` VARCHAR(200),
`card_employed` FLOAT,
`stable_column` INT
)
'''.format(table_name1)
# 테이블 생성
cursor.execute(create_table_sql)
cursor.execute(create_table_sql1)
# 데이터프레임의 데이터를 테이블에 삽입하는 SQL 문 실행
insert_data_sql = '''
INSERT INTO `{}` (
`index`, `gender`, `car`, `reality`, `child_num`, `income_total`,
`income_type`, `edu_type`, `family_type`, `house_type`, `DAYS_BIRTH`,
`DAYS_EMPLOYED`, `FLAG_MOBIL`, `work_phone`, `phone`, `email`,
`occyp_type`, `family_size`, `begin_month`, `credit`, `before_EMPLOYED`, `income_total_befofeEMP_ratio`,
`before_EMPLOYED_m`, `before_EMPLOYED_w`, `Age`, `DAYS_BIRTH_m`, `DAYS_BIRTH_w`, `EMPLOYED`, `DAYS_EMPLOYED_m`, `DAYS_EMPLOYED_w`,
`ability` , `income_mean`, `ID`, `card_employed`, `stable_column`
) VALUES (%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s)
'''.format(table_name)
insert_data_sql1 = '''
INSERT INTO `{}` (
`index`, `gender`, `car`, `reality`, `child_num`, `income_total`,
`income_type`, `edu_type`, `family_type`, `house_type`, `DAYS_BIRTH`,
`DAYS_EMPLOYED`, `FLAG_MOBIL`, `work_phone`, `phone`, `email`,
`occyp_type`, `family_size`, `begin_month`, `before_EMPLOYED`, `income_total_befofeEMP_ratio`,
`before_EMPLOYED_m`, `before_EMPLOYED_w`, `Age`, `DAYS_BIRTH_m`, `DAYS_BIRTH_w`, `EMPLOYED`, `DAYS_EMPLOYED_m`, `DAYS_EMPLOYED_w`,
`ability` , `income_mean`, `ID`, `card_employed`, `stable_column`
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
'''.format(table_name1)
# 데이터프레임의 각 행을 튜플로 변환하여 삽입
for row in data.itertuples(index=False):
cursor.execute(insert_data_sql, row)
# 데이터프레임의 각 행을 튜플로 변환하여 삽입
for row in data1.itertuples(index=False):
cursor.execute(insert_data_sql1, row)
# 변경사항 저장 및 연결 종료
conn.commit()
# SQL 쿼리 실행하여 테이블 가져오기
query = "SELECT * FROM train"
query1 = "SELECT * FROM test"
df = pd.read_sql(query, conn)
df1 = pd.read_sql(query1, conn)
# 데이터베이스 연결 종료
conn.close()
# 가져온 데이터프레임 출력
#df.head()
#필요한 데이터 가져오기
X_train = df.drop(columns = ['index','credit','FLAG_MOBIL'])
X_test = df1.drop(columns = ['index','FLAG_MOBIL'])
y_train = df[['index','credit']]
#display(X_train)
#display(y_train.head())
y_train = y_train.loc[y_train.index.isin(X_train.index)]
y_train = y_train['credit']
#데이터 분할
from sklearn.model_selection import train_test_split
X_train, X_validation, y_train, y_validation = train_test_split(X_train, y_train, test_size=0.33, random_state=43)
#print(X_train.shape, X_validation.shape, y_train.shape, y_validation.shape)
#왜도있는지 확인
from scipy.stats import skew
log_list = []
for i in X_train.select_dtypes(exclude='object').columns:
skewness = skew(X_train[i])
#print(i, skewness)
#절대값 해줘야됨 음수있을수도 있음
if abs(skewness) > 0.8:
log_list.append(i)
#print(log_list)
#로그변환해주자
import numpy as np
#왜도 있는 컬럼 모은 리스트만 돌려주기
for i in log_list:
if X_train[i].min() < 0:
X_train[i] = X_train.map(lambda x : x+abs(X_train[i]))
X_train[i] = np.log1p(X_train[i])
if X_validation[i].min() < 0:
X_validation[i] = X_validation.map(lambda x : x+abs(X_validation[i]))
X_validation[i] = np.log1p(X_validation[i])
if X_test[i].min() < 0:
X_test[i] = X_test.map(lambda x : x+abs(X_test[i]))
X_test[i] = np.log1p(X_test[i])
#로부스트 스케일링 해주자
ocj_col = X_train.select_dtypes(include='object').columns
from sklearn.preprocessing import RobustScaler
RuScaler = RobustScaler()
#train 데이터 분포 저장과 스케일링
RuScaler.fit(X_train.drop(columns = ocj_col))
X_train_sc = RuScaler.transform(X_train.drop(columns = ocj_col))
X_train_sc = pd.DataFrame(X_train_sc, columns = X_train.drop(columns = ocj_col).columns)
#validation 데이터의 스케일링
X_validation_sc = RuScaler.transform(X_validation.drop(columns = ocj_col))
X_validation_sc = pd.DataFrame(X_validation_sc, columns = X_validation.drop(columns = ocj_col).columns)
#test 데이터의 스케일링
X_test_sc = RuScaler.transform(X_test.drop(columns = ocj_col))
X_test_sc = pd.DataFrame(X_test_sc, columns = X_test.drop(columns = ocj_col).columns)
#다시 object 컬럼 붙여주기
for i in ocj_col:
X_train_sc[i] = X_train[i]
X_validation_sc[i] = X_validation[i]
X_test_sc[i] = X_test[i]
#display(X_train_sc.head())
#display(X_validation_sc.head())
#display(X_test_sc.head())
# 원핫인코딩
#혹시 원핫 돌리고 다시 분할하고 잘 분할했는지 확인 하기 위해 이전 데이터 랑 비교
#display(X_train_sc)
#display(X_validation_sc)
#display(X_test_sc)
X_full = pd.concat([X_train_sc, X_validation_sc, X_test_sc])
X_full = pd.get_dummies(X_full)
#X_full
X_train_sc = X_full[:len(X_train_sc)]
X_validation_sc = X_full[len(X_train_sc):len(X_train_sc)+len(X_validation_sc)]
X_test_sc = X_full[len(X_train_sc)+len(X_validation_sc):]
#스모팅 해주기
from imblearn.over_sampling import SMOTE
smote = SMOTE()
X_train_sc, y_train = smote.fit_resample(X_train_sc, y_train)
#X_train_sc
# 라이트gbm
from sklearn.ensemble import RandomForestClassifier
#lgbm
RFC = RandomForestClassifier(random_state=13)
RFC.fit(X_train_sc, y_train)
#pred_lgbm_train = lgbm.predict(X_train_sc)
#pred_lgbm_validaiton = lgbm.predict(X_validation_sc)
#test데이터 예측
pred_lgbm_train = RFC.predict(X_test_sc)
#최종 예측 데이터
df1['credit'] = pred_lgbm_train
df1['begin_month_mean'] = df['begin_month'].mean()
df1['before_EMPLOYED_m_mean'] = df['before_EMPLOYED_m'].mean()
df1['income_mean_mean'] = df['income_mean'].mean()
df1['ability_mean'] = df['ability'].mean()
df1['DAYS_BIRTH_m_mean'] = df['DAYS_BIRTH_m'].mean()
import pymysql
import pandas as pd
import numpy as np
from scipy.stats import skew
# CSV 파일을 데이터프레임으로 읽어오기
data = df1
# MariaDB 연결 설정
host = 'mydb-0.mydb' # external IP
port = 3306 # 포트번호
database = 'testdb' # 데이터베이스명
# MariaDB 연결
conn = pymysql.connect(host=host, port=port, database=database)
cursor = conn.cursor()
# 테이블 생성 SQL 문 작성
table_name = 'testpredict'
# 테이블이 이미 존재하는 경우 삭제
drop_table_sql = "DROP TABLE IF EXISTS `{}`".format(table_name)
cursor.execute(drop_table_sql)
create_table_sql = '''
CREATE TABLE `{}` (
`index` INT,
`gender` VARCHAR(1),
`car` VARCHAR(1),
`reality` VARCHAR(1),
`child_num` INT,
`income_total` FLOAT,
`income_type` VARCHAR(50),
`edu_type` VARCHAR(50),
`family_type` VARCHAR(50),
`house_type` VARCHAR(50),
`DAYS_BIRTH` INT,
`DAYS_EMPLOYED` INT,
`FLAG_MOBIL` INT,
`work_phone` INT,
`phone` INT,
`email` INT,
`occyp_type` VARCHAR(50),
`family_size` FLOAT,
`begin_month` FLOAT,
`before_EMPLOYED` INT,
`income_total_befofeEMP_ratio` FLOAT,
`before_EMPLOYED_m` FLOAT,
`before_EMPLOYED_w` FLOAT,
`Age` INT,
`DAYS_BIRTH_m` FLOAT,
`DAYS_BIRTH_w` FLOAT,
`EMPLOYED` INT,
`DAYS_EMPLOYED_m` FLOAT,
`DAYS_EMPLOYED_w` FLOAT,
`ability` FLOAT,
`income_mean` FLOAT,
`ID` VARCHAR(200),
`card_employed` FLOAT,
`stable_column` INT,
`credit` FLOAT,
`begin_month_mean` FLOAT,
`before_EMPLOYED_m_mean` FLOAT,
`income_mean_mean` FLOAT,
`ability_mean` FLOAT,
`DAYS_BIRTH_m_mean` FLOAT
)
'''.format(table_name)
# 테이블 생성
cursor.execute(create_table_sql)
# 데이터프레임의 데이터를 테이블에 삽입하는 SQL 문 실행
insert_data_sql = '''
INSERT INTO `{}` (
`index`, `gender`, `car`, `reality`, `child_num`, `income_total`,
`income_type`, `edu_type`, `family_type`, `house_type`, `DAYS_BIRTH`,
`DAYS_EMPLOYED`, `FLAG_MOBIL`, `work_phone`, `phone`, `email`,
`occyp_type`, `family_size`, `begin_month`, `before_EMPLOYED`, `income_total_befofeEMP_ratio`,
`before_EMPLOYED_m`, `before_EMPLOYED_w`, `Age`, `DAYS_BIRTH_m`, `DAYS_BIRTH_w`, `EMPLOYED`, `DAYS_EMPLOYED_m`, `DAYS_EMPLOYED_w`,
`ability` , `income_mean`, `ID`, `card_employed`, `stable_column`, `credit`,
`begin_month_mean`, `before_EMPLOYED_m_mean`, `income_mean_mean`, `ability_mean`,
`DAYS_BIRTH_m_mean`
) VALUES (%s, %s, %s, %s, %s,
%s, %s, %s ,%s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s)
'''.format(table_name)
# 데이터프레임의 각 행을 튜플로 변환하여 삽입
for row in data.itertuples(index=False):
cursor.execute(insert_data_sql, row)
# 변경사항 저장 및 연결 종료
conn.commit()
# SQL 쿼리 실행하여 테이블 가져오기
query = "SELECT * FROM testpredict"
df = pd.read_sql(query, conn)
# 데이터베이스 연결 종료
conn.close()
requirements.txt
pandas
numpy
pymysql
scikit-learn
scipy
imblearn
lightgbm
imbalanced-learn
dockerfile
FROM python:latest
WORKDIR /app
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/credit_predict:latest . >>> 현재 디렉토리에서 Docker 이미지를 빌드하고, 그 이미지를 sy02229/credit_predict:latest라는 이름과 태그로 태깅하는 명령
docker login >>> 도커허브에 업로드 하기위해 로그인
docker push sy02229/credit_predict >>> Docker 이미지를 Docker Hub 또는 기타 컨테이너 레지스트리에 푸시하여 공유하는 명령
이슈발생 ... smote가 리소스 아님 cpu 문제로 실행중 kill이뜸 일단은 주석처리 해주자!!!
2) 크론잡 컨트롤러
apiVersion: batch/v1
kind: CronJob
metadata:
name: syapp-cj
spec:
schedule: "*/39 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: syapp-cj
spec:
restartPolicy: OnFailure
nodeSelector:
manyresource : "yes" # 노드 선택 레이블 지정
containers:
- name: sy0218-cronjob
image: sy02229/credit_predict:latest
syapp-cj라는 이름의 CronJob이 생성되고, 스케줄에 따라 sy02229/credit_predict:latest 이미지를 사용하는 작업이 반복적으로 실행
노드1에만 생성되게 하기위해 노드1에 레이블을 달아준다 >>> kubectl label node kube-node1 manyresource=yes
yaml파일에는 노드셀렉터로 레이블 지정해준다
노드1 cpu2개 메모리4096으로 변경!!!
파드 실행후 데이터가 들어갔는지 확인해보자!!!
kubectl run mysql-client -it --image=ghcr.io/c1t1d0s7/network-multitool --rm bash
mysql -h mydb-0.mydb로 접속

올 잘들어간거 확인!!!! 이제 최종적으로 장고에 띄워보자
3. 장고 레플리카셋 컨트롤러
1) 장고 이미지
app_pro >>> 장고 디렉토리임
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
dockerfile
# Dockerfile
# 베이스 이미지를 선택합니다.
FROM python:latest
# 작업 디렉토리를 설정합니다.
WORKDIR /app
# 호스트의 현재 디렉토리의 모든 파일을 작업 디렉토리로 복사
COPY app_pro/ /app
COPY requirements.txt /app
# 필요한 패키지를 설치합니다.
#RUN apt-get update && apt-get install -y python3-dev default-libmysqlclient-dev
# 필요한 Python 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# 컨테이너가 실행될 때 실행할 명령을 지정합니다. (예시로 Django 서버를 실행)
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
docker build -t sy02229/django:latest .
docker push sy02229/django:latest
2) 레플리카셋
vim sydjngo-rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: sydjango-rs
spec:
replicas: 2
selector:
matchLabels:
app: sydjango-rs
template:
metadata:
labels:
app: sydjango-rs
spec:
containers:
- name: sydjango
image: sy02229/django:latest
ports:
- containerPort: 8000
여기서 sydjango-rs라는 이름의 ReplicaSet을 정의하고, 해당 ReplicaSet은 2개의 파드 인스턴스를 유지하도록 지정되어 있습니다.
ReplicaSet의 selector 부분은 해당 ReplicaSet에 의해 관리되는 파드를 선택하는 방법을 정의합니다. 여기서는 app: sydjango-rs 라벨을 가진 파드들이 선택되도록 지정되어 있습니다.
template 섹션에서는 새로운 파드가 생성될 때 사용되는 템플릿을 정의합니다. 해당 템플릿에서는 컨테이너를 정의하고, 컨테이너의 이름을 sydjango로 하고, 이미지로 sy02229/django:latest를 사용하여 실행하도록 지정되어 있습니다. 또한 컨테이너는 포트 8000을 열어서 해당 포트로 들어오는 트래픽을 처리할 수 있도록 설정
3) LoadBalancer 서비스 생성하자
vim sydjango-svc-lb.yaml
apiVersion: v1
kind: Service
metadata:
name: sydjango-svc-lb
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8000
selector:
app: sydjango-rs
ports 섹션에서는 서비스가 80 포트로 외부로 노출되며, 이 요청은 파드의 8000 포트로 전달되도록 설정되어 있습니다. 이는 웹 서버가 8000 포트에서 실행되고 있기 때문에, 외부에서 80 포트로 요청이 오면 해당 파드의 8000 포트로 연결되어 웹 서버에 접근할 수 있도록 합니다.
selector 부분은 해당 서비스가 연결할 대상 파드를 선택하는 방법을 지정합니다. 여기서는 app: sydjango-rs 라벨을 가진 파드들이 선택되도록 지정되어 있습니다. 이는 앞서 정의한 ReplicaSet인 sydjango-rs에서 파드들을 관리하고 있으므로, 해당 서비스가 이 파드들을 대상으로 연결할 수 있게 됩니다.
따라서 이 YAML 파일을 Kubernetes 클러스터에 배포하면 sydjango-svc-lb라는 이름의 LoadBalancer 타입 서비스가 생성되어, 클러스터 외부에서 해당 파드의 8000 포트로 접근할 수 있게 됩니다.
4. 로드벨런서의 external-ip에 접속하여 django에 데이터들이 뜨는지 확인해보자

성공성공 ㅎㅎㅎ
5. 최종 업그레이드
웹에 띄우는 장고가 업데이터가 되어 이미지가 변경될수도 있고 웹 트래픽이 증가하게되면 오류가 발생할 가능성이 있다고 생각해서
컨트롤러를 디플로이먼트로 바꾸고, 오토스케일링을 추가해보자 생각했음!!!
1) 디플로이먼트로 컨트롤러 바꿔주자
디플로이먼트 yaml 파일 생성!!!
vim sydjango-dp.yaml
apiVersion : apps/v1
kind: Deployment
metadata:
name: sydjango-dp
spec:
strategy :
type : RollingUpdate
rollingUpdate :
maxUnavailable : 1
maxSurge : 1
minReadySeconds : 20
replicas: 3
selector:
matchLabels:
app: sydjango-dp
template:
metadata:
labels:
app: sydjango-dp
spec:
containers:
- image: sy02229/django:latest
name: sydjango
ports:
- containerPort: 8000
- apiVersion: apps/v1: 사용할 Kubernetes API 버전을 나타냅니다. apps/v1은 Deployment 리소스를 생성하기 위해 사용되는 API 버전입니다.
- kind: Deployment: 이 리소스의 종류를 "Deployment"로 지정합니다. Deployment 리소스는 파드를 관리하고 애플리케이션을 릴리즈하며 업데이트하는 데 사용됩니다.
- metadata: 메타데이터 섹션으로, 리소스의 이름(name)을 "sydjango-dp"로 지정합니다.
- spec: Deployment의 스펙을 정의합니다.
- strategy: Deployment의 업데이트 전략을 설정합니다.
- type: RollingUpdate: 업데이트를 롤링 업데이트(순차적으로 업데이트) 방식으로 수행하도록 설정합니다.
- rollingUpdate: 롤링 업데이트의 세부 옵션을 설정합니다.
- maxUnavailable: 1: 업데이트 중에 사용 가능한 파드의 최대 수를 1개로 유지합니다. 즉, 하나의 파드가 업데이트 중에 비활성화될 수 있습니다.
- maxSurge: 1: 업데이트 중에 동시에 추가로 생성될 수 있는 파드의 최대 수를 1개로 제한합니다. 즉, 하나의 파드가 새로운 버전으로 업데이트되면 새로운 버전의 파드가 1개 추가로 생성될 수 있습니다.
- minReadySeconds: 20: 롤링 업데이트 후에 업데이트된 파드가 사용 가능해지기까지의 최소 대기 시간을 설정합니다. 즉, 새로운 버전의 파드가 모두 준비되기 전에 이전 버전의 파드를 제거하지 않습니다. 20초 후에 이전 버전의 파드를 삭제하고 다음 업데이트를 진행합니다.
- replicas: 3: 파드의 복제본(replica)을 3개 유지하도록 설정합니다. 이는 Django 애플리케이션을 3개의 파드로 병렬로 운영하고, 고가용성과 부하 분산을 달성하기 위함입니다.
- selector: 파드를 선택하는 라벨 셀렉터입니다. app: sydjango-dp 라는 라벨을 가진 파드를 선택하도록 지정합니다.
- template: 파드를 생성할 템플릿을 정의합니다.
- metadata: 파드 템플릿의 메타데이터로, 라벨을 app: sydjango-dp로 설정합니다.
- spec: 파드 스펙을 정의합니다.
- containers: 파드 내부에 생성될 컨테이너의 리스트를 정의합니다.
- image: sy02229/django:latest: sy02229/django 이미지의 최신 태그(latest)를 사용하여 컨테이너를 생성합니다. 이 이미지는 Django 애플리케이션을 포함하고 있습니다.
- name: sydjango: 컨테이너의 이름을 "sydjango"로 설정합니다.
- ports: 컨테이너에서 개방할 포트를 정의합니다.
- containerPort: 8000: 컨테이너 내부에서 8000번 포트를 개방하여 Django 애플리케이션이 외부와 통신할 수 있도록 합니다
- containers: 파드 내부에 생성될 컨테이너의 리스트를 정의합니다.
- strategy: Deployment의 업데이트 전략을 설정합니다.
vim sydjango-svc-lb.yaml 서비스 셀렉터를 변경해줘야됨
apiVersion: v1
kind: Service
metadata:
name: sydjango-svc-lb
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8000
selector:
app: sydjango-dp
이제 디플로이먼트 롤링업데이트를 할수가 있게됨
롤링업데이트 명령어
kubectl set image deployment <디플로이먼트 이름><컨테이너 이름>=<새 이미지>
kubectl set images image -f <디플로이먼트 파일><컨테이너 이름>=<새 이미지>
2) 오토스케일링 추가해주자
HPA리소스가 측정된 메트릭을 가져오면, 스케일링에 필요한 파드의 수를 계산한다!!!
복제본 수를 계산했으면, 파드를 관리하는 컨트롤러의 복제본 수를 조정!!!
HPA 리소스를 정의하자
vim sydjango-dp-hpa.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: sydjango-dp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: sydjango-dp
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
spec: HorizontalPodAutoscaler의 스펙을 정의합니다.
- scaleTargetRef: 자동 스케일링 대상 파드를 지정합니다.
- apiVersion: apps/v1: 자동 스케일링 대상 파드가 속한 API 그룹과 버전을 나타냅니다. 해당 예제에서는 apps/v1 API 그룹의 리소스를 사용합니다.
- kind: Deployment: 자동 스케일링 대상 파드가 Deployment 리소스에 의해 관리되는 것을 나타냅니다.
- name: sydjango-dp: 자동 스케일링 대상 파드의 이름을 "sydjango-dp"로 지정합니다. 이는 앞서 정의한 Deployment 리소스의 이름과 일치해야 합니다.
- minReplicas: 2: 자동 스케일링의 최소 복제본(replica) 수를 2개로 설정합니다. 이는 파드의 최소 개수를 나타냅니다.
- maxReplicas: 10: 자동 스케일링의 최대 복제본(replica) 수를 10개로 설정합니다. 이는 파드의 최대 개수를 나타냅니다.
- targetCPUUtilizationPercentage: 70: 자동 스케일링을 트리거하는 CPU 사용률의 목표 값을 70%로 설정합니다. 즉, 파드의 CPU 사용률이 70%를 넘어가면 자동으로 파드의 수가 증가하여 부하를 분산시킬 수 있습니다.
실습확인을 위해 디플로이먼트 리소스 제한을 걸어두자

잘 동작하는지 HPA동작을 확인해보자
kubectl get korizontalpodautoscalers.autoscaling

디플로이먼트 특정 파드에 부하를 주자!!

kubectl exec sydjango-dp-7f8d59f48b-67n29 -- sha256sum /dev/zero
kubectl exec sydjango-dp-7f8d59f48b-945c2 -- sha256sum /dev/zero
hpa 상태를 확인시 cpu사용이 목표값을 넘었음 !!! 아마 스케일링이 이뤄 질것???

파드의 목록을 확인해보자( 파드가 늘어난것을 확인 가능!!!)

다시 부하를 준것을 없애주자
kubectl exec sydjango-dp-7f8d59f48b-67n29 -- pkill -9 sha256sum
kubectl exec sydjango-dp-7f8d59f48b-945c2 -- pkill -9 sha256sum
이상 쿠버네티스 미니프로젝트 끝!!!