Pods
쿠버네티스 포드는 Linux® 컨테이너를 하나 이상 모아 놓은 것으로, 쿠버네티스 애플리케이션의 최소 단위입니다. 강하게 결합된 여러 개의 컨테이너로 구성된 포드도 있고(고급 활용 사례), 단일 컨테이너로만 이루어진 포드도 있습니다(더 일반적인 활용 사례). 컨테이너를 쿠버네티스 포드로 그룹화하는 이유는 아래의 설명과 같이 리소스를 더 지능적으로 공유하기 위해서입니다.
쿠버네티스 시스템에서는 같은 포드에 속한 컨테이너끼리 동일한 컴퓨팅 리소스를 공유합니다. 이러한 컴퓨팅 리소스를 쿠버네티스에 풀링하여 클러스터를 만들고, 이를 바탕으로 더 강력하고 지능적으로 분산된 애플리케이션 실행 시스템을 제공할 수 있습니다.
Redis
- Tomcat에 적용
-
cat Dockerfile
FROM tomcat:8 ENV TZ=Asia/Seoul RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone #RUN rm -Rf /usr/local/tomcat/webapps/ROOT COPY ./favorit.ico /usr/local/tomcat/webapps/ROOT/favorit.ico VOLUME ["/data/tomcat", "/data/tomcat"] COPY ./com.joang.web.filemetamanager/target/meta.war /usr/local/tomcat/webapps/meta.war RUN mkdir -p /usr/local/tomcat/filebeat-7.4.1-linux-x86_64 COPY ./filebeat-7.4.1-linux-x86_64 /usr/local/tomcat/filebeat-7.4.1-linux-x86_64 COPY ./redisson-all-3.7.5.jar /usr/local/tomcat/lib COPY ./redisson-tomcat8.jar /usr/local/tomcat/lib RUN rm -Rf /usr/local/tomcat/conf/context.xml COPY ./context.xml /usr/local/tomcat/conf COPY ./redisson.conf /usr/local/tomcat/conf COPY ./redis-data-cache.properties /usr/local/tomcat/conf COPY ./tomcat-cluster-redis-session-manager/lib /usr/local/tomcat/lib ENV JAVA_OPTS="-Dstage=PRD -Dlog.dir=/usr/local/tomcat/logs" ENTRYPOINT ["filebeat-7.4.1-linux-x86_64/startfilebeat.sh"] #ENTRYPOINT ["bin/catalina.sh", "run"]
-
-
첨부파일 확인 :
redis-config-lib.zip파일이름 수정됨 크기 context.xml 2021-01-26 16:06:50 1622 Dockerfile 2021-01-26 16:06:50 1007 redis-data-cache.properties 2021-01-26 16:06:52 380 redisson-all-3.7.5.jar 2021-01-26 16:06:52 12145768 redisson-tomcat8.jar 2021-01-26 16:06:50 17713 redisson.conf 2021-01-26 16:06:50 563 tomcat-cluster-redis-session-manager/ 2021-01-26 16:07:00 0 tomcat-cluster-redis-session-manager/conf/ 2021-01-26 16:07:00 0 tomcat-cluster-redis-session-manager/conf/redis-data-cache.properties 2021-01-26 16:07:00 380 tomcat-cluster-redis-session-manager/lib/ 2021-01-26 16:06:58 0 tomcat-cluster-redis-session-manager/lib/commons-logging-1.2.jar 2021-01-26 16:07:00 61829 tomcat-cluster-redis-session-manager/lib/commons-pool2-2.4.2.jar 2021-01-26 16:07:00 111969 tomcat-cluster-redis-session-manager/lib/jedis-2.9.0.jar 2021-01-26 16:07:00 553762 tomcat-cluster-redis-session-manager/lib/tomcat-cluster-redis-session-manager-2.0.4.jar 2021-01-26 16:07:00 93625 tomcat-cluster-redis-session-manager/readMe.txt 2021-01-26 16:06:58 1337
Meta Crontab
meta system crontab job
Dockerfile
FROM python:3.9-slim
ENV PYTHONUNBUFFERED=1
ENV TZ=Asia/Seoul
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN mkdir -p /apps
VOLUME ["/data/tomcat", "/data/tomcat"]
RUN mkdir -p /apps/filebeat-8.6.0-linux-x86_64
COPY ./filebeat-8.6.0-linux-x86_64 /apps/filebeat-8.6.0-linux-x86_64
RUN chmod go-w /apps/filebeat-8.6.0-linux-x86_64/filebeat.yml
RUN pip install --upgrade pip
RUN pip install psycopg2-binary
RUN pip install yt_dlp
COPY ./python-programes /apps
ENTRYPOINT ["/apps/filebeat-8.6.0-linux-x86_64/startfilebeat.sh"]
실행 Shell
#!/bin/bash
VERSION=$1
echo ">> Build Version : " $VERSION
docker build -t tomcat-meta-batch:$VERSION /data/tomcat/scm/dockerimage/meta-batch
docker tag tomcat-meta-batch:$VERSION web.joang.com:9002/tomcat-meta-batch:$VERSION
docker tag tomcat-meta-batch:$VERSION web.joang.com:9002/tomcat-meta-batch:latest
docker push web.joang.com:9002/tomcat-meta-batch:$VERSION
docker push web.joang.com:9002/tomcat-meta-batch:latest
exit 0
실행 : ./dockerbuild.sh 1
docker run -it --name=python-test tomcat-meta-batch:1 /bin/bash
들어가서 확인 !
실행하면 이미 실행하고 이다고 나오면 삭제 :
docker rm 3e8aafcd3e8a77434d6e263b9459ae8e290c4b5f4b0ba58d0007ca7eff778d2e --force
그리고 다시 시작 !
-----------------------
Meta Backup Batch
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: tomcat-meta-batch
namespace: tomcat-apps
spec:
schedule: "*/15 * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: tomcat-meta-batch
image: 192.168.0.10:9002/tomcat-meta-batch:latest
command:
- /bin/sh
- -c
- /apps/startBackUpBatch.sh > /apps/batch.log
volumeMounts:
- name: tomcat-volume
mountPath: /data/tomcat
volumes:
- name: tomcat-volume
hostPath:
path: /data/tomcat
type: Directory
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
Download Youtube
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: tomcat-meta-youtube
namespace: tomcat-apps
spec:
schedule: "*/10 * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: tomcat-meta-youtube
image: 192.168.0.10:9002/tomcat-meta-batch:latest
command:
- /bin/sh
- -c
- /apps/startYouTuBe.sh > /apps/youtube.log
volumeMounts:
- name: tomcat-volume
mountPath: /data/tomcat
volumes:
- name: tomcat-volume
hostPath:
path: /data/tomcat
type: Directory
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
ΑΒΡΑΞΑΣ Container 설치
ΑΒΡΑΞΑΣ Container version
1. PosgreSql Pod 설치
1.1 PV 구성
각 VM에 공유 볼륨을 생명하고 해당 볼륨을 PV(Persistent Volumes)로 설정
각 VM에 NFS로 연계하는 부분은 여기를 참조
kind: PersistentVolume
apiVersion: v1
metadata:
name: postgresql-pv-volume
namespace: tomcat-apps
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/tomcat/postgresql"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-pv-claim
namespace: tomcat-apps
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
1.2 PostgreSql
postgresql pod를 만든다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-postgresql
namespace: tomcat-apps
spec:
replicas: 1
selector:
matchLabels:
app: products
department: tomcat-postgresql
template:
metadata:
labels:
app: products
department: tomcat-postgresql
spec:
containers:
- name: postgresql
image: postgres:11
ports:
- containerPort: 5432
volumeMounts:
- mountPath: "/var/lib/postgresql/data"
name: postgresql-persistent-storage
readOnly: false
env:
- name: POSTGRES_PASSWORD
value: eXXXXXXXrl <-- root 패스워드 기입
volumes:
- name: postgresql-persistent-storage
persistentVolumeClaim:
claimName: postgresql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-postgresql-service
namespace: tomcat-apps
spec:
type: LoadBalancer
externalIPs:
- 192.168.0.100
selector:
app: products
department: tomcat-postgresql
ports:
- protocol: TCP
port: 5432
targetPort: 5432
---
http://web.joang.com:6875/books/abraksas-system/page/abraksas#bkmrk-%C2%A0 데이터베이스 생성
http://web.joang.com:6875/books/abraksas-system/page/abraksas#bkmrk---table-%EC%83%9D%EC%84%B1%C2%A0 테이블 생성
2. ΑΒΡΑΞΑΣ Dockerfile
FROM tomcat:9
ENV TZ=Asia/Seoul
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY ./favorit.ico /usr/local/tomcat/webapps/ROOT/favorit.ico
VOLUME ["/data/tomcat", "/data/tomcat"]
COPY ./com.joang.web.filemetamanager/target/meta.war /usr/local/tomcat/webapps/meta.war
COPY ./com.joang.web.filemetamanager/target/meta.war /usr/local/tomcat/webapps/ROOT/meta.war
RUN mkdir -p /usr/local/tomcat/filebeat-8.6.0-linux-x86_64
COPY ./filebeat-8.6.0-linux-x86_64 /usr/local/tomcat/filebeat-8.6.0-linux-x86_64
RUN chmod go-w /usr/local/tomcat/filebeat-8.6.0-linux-x86_64/filebeat.yml
COPY ./redisson-all-3.7.5.jar /usr/local/tomcat/lib
COPY ./redisson-tomcat8.jar /usr/local/tomcat/lib
RUN rm -Rf /usr/local/tomcat/conf/context.xml
COPY ./context.xml /usr/local/tomcat/conf
RUN rm -Rf /usr/local/tomcat/conf/server.xml
COPY ./server.xml /usr/local/tomcat/conf
COPY ./redisson.conf /usr/local/tomcat/conf
COPY ./redis-data-cache.properties /usr/local/tomcat/conf
COPY ./tomcat-cluster-redis-session-manager/lib /usr/local/tomcat/lib
ENTRYPOINT ["filebeat-8.6.0-linux-x86_64/startfilebeat.sh"]
전체 docker 빌드 환경 파일 :
filebeat-8.6.0-linux-x86_64.tar
tomcat-cluster-redis-session-manager.tar
3. Docker Build
#!/bin/bash
VERSION=$1
echo ">> Build Version : " $VERSION
docker build -t tomcat-meta:$VERSION /data/tomcat/scm/dockerimage/meta
docker tag tomcat-meta:$VERSION xxx.xxxx.com:8888/tomcat-meta:$VERSION
docker tag tomcat-meta:$VERSION xxx.xxxx.com:8888/tomcat-meta:latest
docker push xxx.xxxx.com:8888/tomcat-meta:$VERSION
docker push xxx.xxxx.com:8888/tomcat-meta:latest
exit 0
4. Namespace 만들기
ΑΒΡΑΞΑΣ용 namespace를 정의합니다.
apiVersion: v1
kind: Namespace
metadata:
name: tomcat-apps
labels:
app.kubernetes.io/name: tomcat-apps
app.kubernetes.io/part-of: tomcat-apps
5. ΑΒΡΑΞΑΣ Yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-meta
namespace: tomcat-apps
spec:
selector:
matchLabels:
app: products
department: tomcat-meta
replicas: 2
minReadySeconds: 20
template:
metadata:
labels:
app: products
department: tomcat-meta
spec:
# hostNetwork: true
containers:
- name: tomcat-meta
image: xxx.xxxx.com:8888/tomcat-meta:latest
imagePullPolicy: Always
volumeMounts:
- name: tomcat-volume
mountPath: /data/tomcat
env:
- name: "PORT"
value: "8080"
volumes:
- name: tomcat-volume
hostPath:
path: /data/tomcat
type: Directory
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-meta-service
namespace: tomcat-apps
spec:
type: LoadBalancer
externalIPs:
- 192.168.0.100
selector:
app: products
department: tomcat-meta
ports:
- protocol: TCP
port: 8080
targetPort: 8080
sessionAffinity: ClientIP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: meta-ingress
namespace: tomcat-apps
annotations:
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/proxy-body-size: 5000m
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
nginx.ingress.kubernetes.io/session-cookie-name: route
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
rules:
- host: web.joang.com
http:
paths:
- path: /meta/*
backend:
serviceName: tomcat-meta-service
servicePort: 8080
- path: /*
backend:
serviceName: tomcat-meta-service
servicePort: 8080
---
6. Kubernetes ΑΒΡΑΞΑΣ Pod
kubectl apply -f /data/tomcat/scm/tomcat-meta.yaml
docker image prune -f
kubectl -n tomcat-apps rollout restart deployment tomcat-meta
7. 배치 (Python batch)
백업 프로그램
배치 startBackUpBatch.sh
#!/bin/bash
python /apps/KKBackupFiles.py > /apps/backup.log
exit 0
from postgresqlDatabase import Databases
import yt_dlp
import datetime
import os
import re
import argparse, logging, logging.config, conf
from shutil import copyfile
today = datetime.date.today()
y = today.year
m = today.month
d = today.day
stored_file_name = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
base_dir = "/media/pi/RaspRefp/data"
dir = "/{year}/{month}/{day}/".format(year=y,month=m,day=d)
# Log
logging.config.dictConfig(conf.dictConfig)
logger = logging.getLogger(__name__)
class CRUD(Databases):
def readDB(self,table,colum, condition):
sql = " SELECT {colum} from {table} where 1=1 {condition}".format(colum=colum,table=table, condition=condition)
try:
self.cursor.execute(sql)
result = self.cursor.fetchall()
except Exception as e :
result = (" read DB err",str(e))
return result
def updateDB(self,table,colum,value,condition):
sql = " UPDATE {table} SET {colum}='{value}', sys_backup_date=NOW() WHERE 1=1 and {condition} ".format(table=table , colum=colum ,value=value,condition=condition )
try :
self.cursor.execute(sql)
except Exception as e :
logger.debug(" update DB err , e="+str(e))
def backupFile(self,sys_storage, sys_backup_location):
sql = " SELECT original_file_name, stored_file_name from tb_filemanager_meta_file order by idx "
try:
self.cursor.execute(sql)
result = self.cursor.fetchall()
except Exception as e :
result = (" read DB err",str(e))
for row in result:
toLocation = row[1].rindex("/", 0)
#logger.debug(">> row = " + row[1][:toLocation+1] + " = " + str(toLocation))
#logger.debug(">> row = " + sys_storage + row[1] + " --> TO --> " + sys_backup_location + "/" + row[1][:toLocation] + row[0])
self.copyBackupFile(sys_storage + row[1], sys_backup_location + row[1][:toLocation] , row[0])
def copyBackupFile(self, orgFile, destDir, destFile):
if not os.path.exists(destDir + "/" + destFile):
logger.debug(">> file not exist = " + destFile)
# 디렉토리 만들기
if not os.path.exists(destDir):
logger.debug(">> Make Dir = " + destDir)
os.makedirs(destDir)
# 파일 옮기기
logger.debug(">> Copy Files = " + orgFile + " --> " + destDir + "/" + destFile )
os.system('cp ' + orgFile + ' "' + destDir + "/" + destFile + '"')
else:
logger.debug(">> file exist ! -> " + destDir + "/" + destFile)
if __name__ == "__main__":
db = CRUD()
result = db.readDB('tb_archiver_sys_info','SYS_BACKUP_YN, sys_storage, sys_backup_location', 'and SYS_TITLE=\'ΑΒΡΑΞΑΣ\' and SYS_BACKUP_YN=\'Y\'')
if( len(result) == 0 ):
logger.debug(">> Backup Skip " )
else:
logger.debug(">> result size " + str(len(result)) )
row=result[0]
logger.debug(">> Row detail " + str(row) )
if( str(row[0]) == "Y" ):
logger.debug(">> BACKUP START ! ")
db.updateDB('tb_archiver_sys_info', 'SYS_BACKUP_YN', 'P', 'SYS_TITLE=\'ΑΒΡΑΞΑΣ\'')
db.commit()
sys_storage = str(row[1])
sys_backup_location = str(row[2])
db.backupFile(sys_storage, sys_backup_location)
db.updateDB('tb_archiver_sys_info', 'SYS_BACKUP_YN', 'N', 'SYS_TITLE=\'ΑΒΡΑΞΑΣ\'')
db.commit()
Youtube 다운로드 프로그램
#!/bin/bash
python /apps/KKYouTuBeDownloader.py > /apps/youtube.log
exit 0
from postgresqlDatabase import Databases
import yt_dlp
import datetime
import os
import re
import argparse, logging, logging.config, conf
today = datetime.date.today()
y = today.year
m = today.month
d = today.day
stored_file_name = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
base_dir = ""
dir = "/{year}/{month}/{day}/".format(year=y,month=m,day=d)
downloadfilename = ""
metadata = ""
# Log
logging.config.dictConfig(conf.dictConfig)
logger = logging.getLogger(__name__)
class CRUD(Databases):
def insertDB(self,schema,table,colum,data):
sql = " INSERT INTO {schema}.{table}({colum}) VALUES ('{data}') ;".format(schema=schema,table=table,colum=colum,data=data)
try:
self.cursor.execute(sql)
except Exception as e :
logger.debug(" insert DB err, e="+str(e))
def readDB(self,table,colum, condition):
sql = " SELECT {colum} from {table} where 1=1 {condition}".format(colum=colum,table=table, condition=condition)
try:
self.cursor.execute(sql)
result = self.cursor.fetchall()
except Exception as e :
result = (" read DB err",str(e))
return result
def readDBbySQL(self,sql):
try:
self.cursor.execute(sql)
result = self.cursor.fetchall()
except Exception as e :
result = (" read DB err",str(e))
return result
def updateDB(self,table,colum,value,condition):
sql = " UPDATE {table} SET {colum}='{value}' WHERE 1=1 and {condition}::integer ".format(table=table , colum=colum ,value=value,condition=condition )
try :
self.cursor.execute(sql)
except Exception as e :
logger.debug(" update DB err , e="+str(e))
def deleteDB(self,schema,table,condition):
sql = " delete from {schema}.{table} where {condition} ; ".format(schema=schema,table=table,
condition=condition)
try :
self.cursor.execute(sql)
except Exception as e:
logger.debug( "delete DB err , e="+str(e))
def commit(self):
try :
self.db.commit()
except Exception as e:
logger.debug( "delete DB err , e="+str(e))
def downLoadMovieFileByURL(self,downloadurl, idx):
global downloadfilename
logger.debug( ">> Download Movie URL:" + downloadurl)
ydl_opts = {
'verbose': True,
'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio/best',
'merge_output_format': 'mp4',
'outtmpl': base_dir + dir + stored_file_name + '%(title)s.%(ext)s'
}
try:
yt_dlp.YoutubeDL(ydl_opts).cache.remove()
ydl = yt_dlp.YoutubeDL(ydl_opts).extract_info(downloadurl, download=True)
downloadfilename = ydl["title"]
metadata = ydl['description']
logger.debug(">> Movie file name : " + downloadfilename)
logger.debug(">> Movie metadata : " + metadata )
db.updateDB('tb_filemanager_meta_file', 'file_detail', downloadurl+"\n\r"+ metadata.replace("'","\""), 'idx='+idx)
return True
except Exception as e:
logger.debug("Error e="+str(e))
db.updateDB('tb_filemanager_meta_file', 'down_yn', 'E', 'idx='+idx)
db.updateDB('tb_filemanager_meta_file', 'error', '@downLoadMovieFileByURL:'+str(e), 'idx='+idx)
return False
def downLoadMusicFileByURL(self,downloadurl, idx):
global downloadfilename
logger.debug( ">> Download Music URL:" + downloadurl)
ydl_opts = {
'verbose': True,
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}, {'key': 'FFmpegMetadata'},],
'outtmpl': base_dir + dir + stored_file_name + '%(title)s.%(ext)s'
}
try:
yt_dlp.YoutubeDL(ydl_opts).cache.remove()
ydl = yt_dlp.YoutubeDL(ydl_opts).extract_info(downloadurl, download=True)
downloadfilename = ydl["title"]
metadata = ydl['description']
logger.debug(">> Music file name : " + downloadfilename )
logger.debug(">> Music metadata : " + metadata )
db.updateDB('tb_filemanager_meta_file', 'file_detail', downloadurl+"\n\r"+ metadata.replace("'","\""), 'idx='+idx)
return True
except Exception as e:
logger.debug("Error e=" + str(e))
db.updateDB('tb_filemanager_meta_file', 'down_yn', 'E', 'idx='+idx)
db.updateDB('tb_filemanager_meta_file', 'error', '@downLoadMusicFileByURL:'+str(e), 'idx='+idx)
return False
def updateAndRename(self, idx, downloadFileType):
try:
logger.debug("> Download Original file name = " + base_dir + dir + downloadfilename + ", type=" + downloadFileType)
changeOrgFileName = re.sub("'", "", re.sub("[/]", "_", re.sub('[\/:*?"<>|]','',downloadfilename)))
logger.debug("> Change Original file name = " + base_dir + dir + changeOrgFileName + ", type=" + downloadFileType)
logger.debug("> Change Store file name = " + base_dir + dir + stored_file_name + ", type=" + downloadFileType)
if(downloadFileType == "mov"):
final_changeOrgFileName = changeOrgFileName+'.mp4'
final_stored_file_name = stored_file_name+'.mp4'
elif(downloadFileType == "muc"):
final_changeOrgFileName = changeOrgFileName+'.mp3'
final_stored_file_name = stored_file_name+'.mp3'
for filename in os.listdir(base_dir + dir):
logger.debug(">> filename = " + filename + " , stored_file_name=" + stored_file_name)
if filename.startswith(stored_file_name):
logger.debug("> Change file name ("+idx+")= " + base_dir + dir + filename + " to " + base_dir + dir + final_stored_file_name)
os.rename(base_dir + dir + filename, base_dir+dir + final_stored_file_name)
logger.debug("> Changed file stored name=" + final_stored_file_name)
logger.debug("> Changed file Org name=" + final_changeOrgFileName)
logger.debug("original_file_name ("+idx+")= " + final_changeOrgFileName)
db.updateDB('tb_filemanager_meta_file', 'original_file_name', final_changeOrgFileName, 'idx='+idx)
logger.debug("stored_file_name ("+idx+")= " + dir + final_stored_file_name)
db.updateDB('tb_filemanager_meta_file', 'stored_file_name', dir + final_stored_file_name, 'idx='+idx)
logger.debug("file_size ("+idx+")= " + base_dir + dir + final_stored_file_name)
logger.debug("file_size ("+idx+")= " + str(os.path.getsize(base_dir + dir + final_stored_file_name)))
db.updateDB('tb_filemanager_meta_file', 'file_size', os.path.getsize(base_dir + dir + final_stored_file_name), 'idx='+idx)
logger.debug("crea_dtm ("+idx+") = NOW()")
db.updateDB('tb_filemanager_meta_file', 'crea_dtm', 'NOW()', 'idx='+idx)
logger.debug("crea_dtm ("+idx+") = NOW()")
return True
except Exception as e:
logger.debug("Error e=" + str(e))
db.updateDB('tb_filemanager_meta_file', 'down_yn', 'E', 'idx='+idx)
db.updateDB('tb_filemanager_meta_file', 'error', '@updateAndRename:'+str(e), 'idx='+idx)
return False
def existDirectory(self,directory):
logger.debug(">>" + directory)
isDir = os.path.isdir(directory)
if(isDir):
logger.debug(">>> Exist !")
else:
logger.debug(">>> Make Dir !")
os.makedirs(directory, exist_ok=True)
return isDir
if __name__ == "__main__":
db = CRUD()
result = db.readDB('tb_archiver_sys_info','sys_storage', 'and sys_title=\'ΑΒΡΑΞΑΣ\'')
if( len(result) == 0 ):
logger.debug(">> sys_storage result size 0 " )
else:
logger.debug(">> sys_storage result size " + str(len(result)) )
base_dir = result[0][0]
logger.debug(">> base_dir = " + base_dir)
result = db.readDB('tb_filemanager_meta_file','idx, original_file_name, file_type', 'and down_yn=\'N\'')
resultYN = 'N'
if( len(result) == 0 ):
logger.debug(">> result size 0 " )
else:
logger.debug(">> result size " + str(len(result)) )
row=result[0]
logger.debug(">> Row detail " + str(row) )
db.existDirectory(base_dir+dir)
idx = str(row[0])
downloadUrl = row[1]
downloadFileType = row[2]
db.updateDB('tb_filemanager_meta_file', 'down_yn', 'D', 'idx='+idx)
db.commit()
logger.debug(">>"+idx+" , "+downloadUrl+" , "+downloadFileType)
if(downloadFileType == "mov"):
logger.debug("> MOVIE !")
if(db.downLoadMovieFileByURL(downloadUrl, idx)):
if(db.updateAndRename(idx, downloadFileType)):
resultYN = 'Y'
else:
resultYN = 'E'
else:
resultYN = 'E'
elif(downloadFileType == "muc"):
logger.debug("> MUSIC !")
if(db.downLoadMusicFileByURL(downloadUrl, idx)):
if(db.updateAndRename(idx, downloadFileType)):
resultYN = 'Y'
else:
resultYN = 'E'
else:
resultYN = 'E'
else:
logger.debug("> ERROR FILE TYPE !")
db.updateDB('tb_filemanager_meta_file', 'down_yn', 'E', 'idx='+idx)
db.updateDB('tb_filemanager_meta_file', 'error', '@DownloadType', 'idx='+idx)
db.updateDB('tb_filemanager_meta_file', 'down_yn', resultYN, 'idx='+idx)
if(resultYN == ""):
db.updateDB('tb_filemanager_meta_file', 'error', 'download done', 'idx='+idx)
logger.debug(">> "+idx+" UPDATE=" + resultYN)
db.commit()
공통
from pathlib import Path
p = Path("logs")
if not p.exists():
p.mkdir()
dictConfig = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s:: %(message)s',
},
},
'handlers': {
'default': {
'level': 'DEBUG',
'formatter': 'standard',
'class': 'logging.StreamHandler',
'stream': 'ext://sys.stdout',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'standard',
'filename': 'logs/logfile.log',
'mode': 'a',
'maxBytes': 5_242_880,
'backupCount': 3,
'encoding': 'utf-8',
},
},
'loggers': {
'__main__': {
'handlers': ['default','file'],
'level': 'DEBUG',
'propagate': False,
},
'camera': {
'handlers': ['default', 'file'],
'level': 'DEBUG',
'propagate': False,
},
}
}
import psycopg2
class Databases():
def __init__(self):
self.db = psycopg2.connect(host='xxx.xxxx.com', dbname='archiver',user='pi',password='password',port=5432)
self.cursor = self.db.cursor()
def __del__(self):
self.db.close()
self.cursor.close()
def execute(self,query,args={}):
self.cursor.execute(query,args)
row = self.cursor.fetchall()
return row
def commit(self):
self.db.commit()
8. 배치 (Kubernetes Job)
FROM python:3.9-slim
ENV PYTHONUNBUFFERED=1
ENV TZ=Asia/Seoul
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN mkdir -p /apps
VOLUME ["/data/tomcat", "/data/tomcat"]
RUN mkdir -p /apps/filebeat-8.6.0-linux-x86_64
COPY ./filebeat-8.6.0-linux-x86_64 /apps/filebeat-8.6.0-linux-x86_64
RUN chmod go-w /apps/filebeat-8.6.0-linux-x86_64/filebeat.yml
RUN apt update
RUN apt install ffmpeg -y
RUN pip install --upgrade pip
RUN pip install psycopg2-binary
RUN pip install yt_dlp
RUN pip install ffmpeg-python
COPY ./python-programes /apps
ENTRYPOINT ["/apps/filebeat-8.6.0-linux-x86_64/startfilebeat.sh"]
#!/bin/bash
VERSION=$1
echo ">> Build Version : " $VERSION
docker build -t tomcat-meta-batch:$VERSION /data/tomcat/scm/dockerimage/meta-batch
docker tag tomcat-meta-batch:$VERSION xxx.xxxx.com:8888/tomcat-meta-batch:$VERSION
docker tag tomcat-meta-batch:$VERSION xxx.xxxx.com:8888/tomcat-meta-batch:latest
docker push xxx.xxxx.com:8888/tomcat-meta-batch:$VERSION
docker push xxx.xxxx.com:8888/tomcat-meta-batch:latest
exit 0
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: tomcat-meta-batch
namespace: tomcat-apps
spec:
schedule: "*/15 * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: tomcat-meta-batch
image: web.joang.com:8888/tomcat-meta-batch:latest
command:
- /bin/sh
- -c
- /apps/startBackUpBatch.sh > /apps/batch.log
volumeMounts:
- name: tomcat-volume
mountPath: /data/tomcat
volumes:
- name: tomcat-volume
hostPath:
path: /data/tomcat
type: Directory
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
사용법 : ΑΒΡΑΞΑΣ 사용하기
Adminer - 데이터베이스 Tool
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-adminer
namespace: tomcat-apps
spec:
replicas: 1
selector:
matchLabels:
app: products
department: tomcat-adminer
template:
metadata:
labels:
app: products
department: tomcat-adminer
spec:
containers:
- name: tomcat-adminer
image: adminer:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: adminer-service
namespace: tomcat-apps
spec:
type: LoadBalancer
externalIPs:
- 192.168.0.100
selector:
app: products
department: tomcat-adminer
ports:
- protocol: TCP
port: 8082
targetPort: 8080
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: adminer-ingress
namespace: tomcat-apps
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "5000m"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: db.joang.com
http:
paths:
- path: /*
backend:
serviceName: adminer-service
servicePort: 8082
Bookstack 설정
기본 설정 변경
Bookstack 설정은 PV로 설정된 스토리지에 "/data/shared/common/bookstack/pv/php"의
현재 수정값 -- 파일 업로드 사이즈 수정으로 인하여 override값 정의
php-local.ini
; Edit this file to override php.ini directives
date.timezone = Asia/Seoul
upload_max_filesize = 10000M
post_max_size = 10000M
www2.conf
; Edit this file to override www.conf and php-fpm.conf directives and restart the container
; Pool name
[www]
PDF 출력하기 설정
.env설정
#2025 01 27
SESSION_LIFETIME=12000
SESSION_COOKIE_NAME=common_bookstack_session
SESSION_SECURE_COOKIE=false
EXPORT_PDF_COMMAND_TIMEOUT=300
EXPORT_PDF_COMMAND="weasyprint {input_html_path} {output_pdf_path}"
폰트 업로드
notosanskr.zip notosanskr.zip
/config 아래로 복사
한글 전환 프로그램 설치
weasyprint
apk add weasyprint
Deployment Yaml에 반영
spec:
containers:
- name: bookstack
image: linuxserver/bookstack:latest
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "/config/initBookStack.sh"]
/config/initBookStack.sh
#!/bin/bash
# copy fonts
/usr/bin/unzip /config/notosanskr.zip -d /usr/share/fonts/notosanskr/
# install fonts
/usr/bin/fc-cache -fv
# pdf convert programe install
/sbin/apk add weasyprint
한글 설치 확인
전체 Deployment Yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: bookstack
namespace: common-apps
labels:
app: bookstack
spec:
replicas: 1
selector:
matchLabels:
app: bookstack
template:
metadata:
labels:
app: bookstack
spec:
containers:
- name: bookstack
image: linuxserver/bookstack:latest
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "/config/initBookStack.sh"]
ports:
- containerPort: 80
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: APP_URL
value: http://xxx.xxx.xxx:8084
- name: APP_DEFAULT_DARK_MODE
value: "true"
- name: DB_HOST
value: "192.168.0.xxx"
- name: DB_PORT
value: "xxxxx"
- name: DB_USER
value: "xxxxxxx"
- name: DB_PASS
value: "xxxxxxxxxxx"
- name: DB_DATABASE
value: "xxxxxx"
- name: FILE_UPLOAD_SIZE_LIMIT
value: "10240"
- name: MAIL_FROM_NAME
value: "BookStack"
- name: MAIL_FROM
value: "jframeworkxxxxx"
- name: MAIL_HOST
value: "xxxxxx.gmail.com"
- name: MAIL_USERNAME
value: "jfraxxxxxxx.com"
- name: MAIL_PASSWORD
value: "ezxxxxxxxxt"
volumeMounts:
- mountPath: "/config"
name: bokstackvolume
volumes:
- name: bokstackvolume
hostPath:
path: /data/shared/common/bookstack/pv
type: Directory
# persistentVolumeClaim:
# claimName: bookstack-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: bookstack
namespace: common-apps
labels:
app: bookstack
spec:
type: LoadBalancer
externalIPs:
- 192.168.0.xxxx
selector:
app: bookstack
ports:
- protocol: TCP
port: 8084
targetPort: 80
sessionAffinity: ClientIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bookstack-ingress
namespace: common-apps
annotations:
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/proxy-body-size: 500000m
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
nginx.ingress.kubernetes.io/session-cookie-name: route
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
rules:
- host:
http:
paths:
- pathType: Prefix
path: /*
backend:
service:
name: bookstack
port:
number: 808x
---
Jenkins
Jenkins
apiVersion: v1
kind: Namespace
metadata:
name: hyunsu-cicd
labels:
app.kubernetes.io/name: hyunsu-cicd
app.kubernetes.io/part-of: hyunsu-cicd
---
kind: PersistentVolume
apiVersion: v1
metadata:
name: jenkins-pv-volume
namespace: hyunsu-cicd
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/tomcat/devops/jenkins/pv"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pv-claim
namespace: hyunsu-cicd
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: hyunsu-cicd
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
containers:
- name: jenkins
image: jenkins/jenkins:lts
ports:
- name: http-port
containerPort: 8080
- name: jnlp-port
containerPort: 50000
volumeMounts:
- name: jenkins-vol
mountPath: /var/jenkins_home
volumes:
- name: jenkins-vol
persistentVolumeClaim:
claimName: jenkins-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: hyunsu-cicd
spec:
type: LoadBalancer
ports:
- port: 8080
targetPort: 8080
nodePort: 30001
selector:
app: jenkins
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-jnlp
namespace: hyunsu-cicd
spec:
type: LoadBalancer
ports:
- port: 50000
targetPort: 50000
selector:
app: jenkins
MySql
MySql
PV
kind: PersistentVolume
apiVersion: v1
metadata:
name: mysql-pv-volume
namespace: tomcat-apps
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/tomcat/mysql"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
namespace: tomcat-apps
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
MySql DBMS
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-mysql
namespace: tomcat-apps
spec:
replicas: 1
selector:
matchLabels:
app: products
department: tomcat-mysql
template:
metadata:
labels:
app: products
department: tomcat-mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
volumeMounts:
- mountPath: "/var/lib/mysql"
name: mysql-persistent-storage
readOnly: false
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: MYSQL_ROOT_PASSWORD
value: "xxxxxxxxxxxxxxx"
- name: MYSQL_DATABASE
value: "mysql"
- name: MYSQL_USER
value: "xxxxxxxxx"
- name: MYSQL_PASSWORD
value: "xxxxxxxxxxx"
- name: TZ
value: "Asia/Seoul"
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-mysql-service
namespace: tomcat-apps
spec:
type: LoadBalancer
externalIPs:
- 192.168.0.100
selector:
app: products
department: tomcat-mysql
ports:
- protocol: TCP
port: 33306
targetPort: 3306
---
PostgreSql
PostgreSql
PV
kind: PersistentVolume
apiVersion: v1
metadata:
name: postgresql-pv-volume
namespace: tomcat-apps
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/tomcat/postgresql"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-pv-claim
namespace: tomcat-apps
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
PostgreSql DBMS
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-postgresql
namespace: tomcat-apps
spec:
replicas: 1
selector:
matchLabels:
app: products
department: tomcat-postgresql
template:
metadata:
labels:
app: products
department: tomcat-postgresql
spec:
containers:
- name: postgresql
image: postgres:11
ports:
- containerPort: 5432
volumeMounts:
- mountPath: "/var/lib/postgresql/data"
name: postgresql-persistent-storage
readOnly: false
env:
- name: POSTGRES_PASSWORD
value: xxxxxxxxxxx
volumes:
- name: postgresql-persistent-storage
persistentVolumeClaim:
claimName: postgresql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-postgresql-service
namespace: tomcat-apps
spec:
type: LoadBalancer
externalIPs:
- 192.168.0.100
selector:
app: products
department: tomcat-postgresql
ports:
- protocol: TCP
port: 5432
targetPort: 5432
---