fix: selectCompanyName LIMIT 1 추가 및 K8s 배포 설정 추가
Build & Deploy to K8s / build-and-deploy (push) Failing after 7s

This commit is contained in:
chpark
2026-04-11 17:04:26 +00:00
parent 9c36191ebf
commit 39fdc2683e
14 changed files with 705 additions and 1 deletions
+108
View File
@@ -0,0 +1,108 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-node
namespace: invyone
labels:
app: backend-node
spec:
replicas: 1
selector:
matchLabels:
app: backend-node
template:
metadata:
labels:
app: backend-node
spec:
containers:
- name: backend-node
image: localhost:5000/invyone/backend-node:latest
ports:
- containerPort: 8080
env:
- name: NODE_ENV
valueFrom:
configMapKeyRef:
name: invyone-config
key: NODE_ENV
- name: PORT
valueFrom:
configMapKeyRef:
name: invyone-config
key: NODE_PORT
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: invyone-config
key: DATABASE_URL
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: invyone-secrets
key: JWT_SECRET
- name: CORS_ORIGIN
valueFrom:
configMapKeyRef:
name: invyone-config
key: CORS_ORIGIN
- name: BOK_API_KEY
valueFrom:
secretKeyRef:
name: invyone-secrets
key: BOK_API_KEY
- name: KMA_API_KEY
valueFrom:
secretKeyRef:
name: invyone-secrets
key: KMA_API_KEY
- name: ITS_API_KEY
valueFrom:
secretKeyRef:
name: invyone-secrets
key: ITS_API_KEY
- name: EXWAY_API_KEY
valueFrom:
secretKeyRef:
name: invyone-secrets
key: EXWAY_API_KEY
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: invyone-secrets
key: ENCRYPTION_KEY
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: backend-node
namespace: invyone
spec:
type: NodePort
selector:
app: backend-node
ports:
- port: 8080
targetPort: 8080
nodePort: 30080
+105
View File
@@ -0,0 +1,105 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-spring
namespace: invyone
labels:
app: backend-spring
spec:
replicas: 1
selector:
matchLabels:
app: backend-spring
template:
metadata:
labels:
app: backend-spring
spec:
containers:
- name: backend-spring
image: localhost:5000/invyone/backend-spring:latest
ports:
- containerPort: 8081
env:
- name: SPRING_PROFILES_ACTIVE
valueFrom:
configMapKeyRef:
name: invyone-config
key: SPRING_PROFILES_ACTIVE
- name: SERVER_PORT
valueFrom:
configMapKeyRef:
name: invyone-config
key: SERVER_PORT
- name: SPRING_DATASOURCE_URL
valueFrom:
configMapKeyRef:
name: invyone-config
key: SPRING_DATASOURCE_URL
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
configMapKeyRef:
name: invyone-config
key: SPRING_DATASOURCE_USERNAME
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: invyone-secrets
key: SPRING_DATASOURCE_PASSWORD
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: invyone-secrets
key: JWT_SECRET
- name: JWT_EXPIRATION
valueFrom:
configMapKeyRef:
name: invyone-config
key: JWT_EXPIRATION
- name: FILE_UPLOAD_DIR
valueFrom:
configMapKeyRef:
name: invyone-config
key: FILE_UPLOAD_DIR
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 1024Mi
livenessProbe:
tcpSocket:
port: 8081
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumeMounts:
- name: uploads
mountPath: /app/uploads
volumes:
- name: uploads
persistentVolumeClaim:
claimName: invyone-uploads-pvc
---
apiVersion: v1
kind: Service
metadata:
name: backend-spring
namespace: invyone
spec:
type: NodePort
selector:
app: backend-spring
ports:
- port: 8081
targetPort: 8081
nodePort: 30081
+18
View File
@@ -0,0 +1,18 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: invyone-config
namespace: invyone
data:
SPRING_PROFILES_ACTIVE: "prod"
SERVER_PORT: "8081"
SPRING_DATASOURCE_URL: "jdbc:postgresql://211.115.91.141:11134/vexplor"
SPRING_DATASOURCE_USERNAME: "postgres"
JWT_EXPIRATION: "86400000"
FILE_UPLOAD_DIR: "./uploads"
NODE_ENV: "production"
NODE_PORT: "8080"
DATABASE_URL: "postgresql://postgres:vexplor0909!!@211.115.91.141:11134/vexplor"
CORS_ORIGIN: "https://solution.invyone.com"
NEXT_PUBLIC_API_URL: "https://solution.invyone.com/api"
NEXT_TELEMETRY_DISABLED: "1"
Executable
+129
View File
@@ -0,0 +1,129 @@
#!/bin/bash
set -euo pipefail
# ================================================
# invyone Kubernetes 배포 스크립트
# 사용법: ./k8s/deploy.sh [build|deploy|all]
# ================================================
REGISTRY="localhost:5000"
PROJECT="invyone"
TAG="${GIT_COMMIT_SHORT:-latest}"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[DEPLOY]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
# 프로젝트 루트 디렉토리로 이동
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_ROOT"
build_images() {
log "Docker 이미지 빌드 시작..."
log "1/3 Backend Spring 빌드..."
docker build -t "$REGISTRY/$PROJECT/backend-spring:$TAG" \
-f docker/deploy/backend-spring.Dockerfile \
backend-spring/
log "2/3 Backend Node 빌드..."
docker build -t "$REGISTRY/$PROJECT/backend-node:$TAG" \
-f docker/deploy/backend-node.Dockerfile \
backend-node/
log "3/3 Frontend 빌드..."
docker build -t "$REGISTRY/$PROJECT/frontend:$TAG" \
-f docker/deploy/frontend.Dockerfile \
frontend/
log "Docker 이미지 빌드 완료!"
}
push_images() {
log "Docker Registry로 이미지 Push..."
docker push "$REGISTRY/$PROJECT/backend-spring:$TAG"
docker push "$REGISTRY/$PROJECT/backend-node:$TAG"
docker push "$REGISTRY/$PROJECT/frontend:$TAG"
# latest 태그도 push
if [ "$TAG" != "latest" ]; then
docker tag "$REGISTRY/$PROJECT/backend-spring:$TAG" "$REGISTRY/$PROJECT/backend-spring:latest"
docker tag "$REGISTRY/$PROJECT/backend-node:$TAG" "$REGISTRY/$PROJECT/backend-node:latest"
docker tag "$REGISTRY/$PROJECT/frontend:$TAG" "$REGISTRY/$PROJECT/frontend:latest"
docker push "$REGISTRY/$PROJECT/backend-spring:latest"
docker push "$REGISTRY/$PROJECT/backend-node:latest"
docker push "$REGISTRY/$PROJECT/frontend:latest"
fi
log "이미지 Push 완료!"
}
deploy_k8s() {
log "Kubernetes 배포 시작..."
# Namespace 생성
kubectl apply -f k8s/namespace.yaml
# Secret & ConfigMap (Secret은 이미 존재하면 skip)
kubectl apply -f k8s/secrets.yaml
kubectl apply -f k8s/configmap.yaml
# PVC
kubectl apply -f k8s/pvc.yaml
# Deployments & Services
kubectl apply -f k8s/backend-spring.yaml
kubectl apply -f k8s/backend-node.yaml
kubectl apply -f k8s/frontend.yaml
# NetworkPolicy
kubectl apply -f k8s/networkpolicy.yaml
# 이미지 태그가 latest가 아니면 이미지 업데이트
if [ "$TAG" != "latest" ]; then
kubectl set image deployment/backend-spring \
backend-spring="$REGISTRY/$PROJECT/backend-spring:$TAG" \
-n invyone
kubectl set image deployment/backend-node \
backend-node="$REGISTRY/$PROJECT/backend-node:$TAG" \
-n invyone
kubectl set image deployment/frontend \
frontend="$REGISTRY/$PROJECT/frontend:$TAG" \
-n invyone
fi
# Rollout 대기
log "Rollout 대기 중..."
kubectl rollout status deployment/backend-spring -n invyone --timeout=180s || warn "backend-spring rollout timeout"
kubectl rollout status deployment/backend-node -n invyone --timeout=120s || warn "backend-node rollout timeout"
kubectl rollout status deployment/frontend -n invyone --timeout=120s || warn "frontend rollout timeout"
log "Kubernetes 배포 완료!"
kubectl get pods -n invyone
}
case "${1:-all}" in
build)
build_images
push_images
;;
deploy)
deploy_k8s
;;
all)
build_images
push_images
deploy_k8s
;;
*)
echo "사용법: $0 [build|deploy|all]"
exit 1
;;
esac
+73
View File
@@ -0,0 +1,73 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: invyone
labels:
app: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: localhost:5000/invyone/frontend:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: NEXT_PUBLIC_API_URL
valueFrom:
configMapKeyRef:
name: invyone-config
key: NEXT_PUBLIC_API_URL
- name: SERVER_API_URL
value: "http://backend-spring.invyone.svc.cluster.local:8081"
- name: NEXT_TELEMETRY_DISABLED
value: "1"
- name: PORT
value: "3000"
- name: HOSTNAME
value: "0.0.0.0"
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 20
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: invyone
spec:
type: NodePort
selector:
app: frontend
ports:
- port: 3000
targetPort: 3000
nodePort: 30000
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: invyone
labels:
app.kubernetes.io/part-of: invyone
+58
View File
@@ -0,0 +1,58 @@
# NetworkPolicy: frontend는 backend에만 접근 가능
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: invyone
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Egress
egress:
# backend-spring 접근 허용
- to:
- podSelector:
matchLabels:
app: backend-spring
ports:
- port: 8081
# backend-node 접근 허용
- to:
- podSelector:
matchLabels:
app: backend-node
ports:
- port: 8080
# DNS 허용
- to: []
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
---
# NetworkPolicy: backend는 외부 DB 접근만 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: invyone
spec:
podSelector:
matchLabels: {}
policyTypes:
- Ingress
ingress:
# 같은 네임스페이스 내 트래픽 허용
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: invyone
# NodePort 외부 트래픽 허용 (Traefik에서 들어오는 트래픽)
- from: []
ports:
- port: 3000
- port: 8080
- port: 8081
+12
View File
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: invyone-uploads-pvc
namespace: invyone
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 10Gi
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Secret
metadata:
name: invyone-secrets
namespace: invyone
type: Opaque
stringData:
DB_PASSWORD: "vexplor0909!!"
SPRING_DATASOURCE_PASSWORD: "vexplor0909!!"
JWT_SECRET: "b354a8587eaded4c9e7fdcaf139a3ab8a23fcdb8a80232587c894b757bf13bf9"
BOK_API_KEY: "OXIGPQXH68NUKVKL5KT9"
KMA_API_KEY: "ogdXr2e9T4iHV69nvV-IwA"
ITS_API_KEY: "d6b9befec3114d648284674b8fddcc32"
EXWAY_API_KEY: "7820214492"
+51
View File
@@ -0,0 +1,51 @@
# Traefik 동적 설정 파일
# 기존 Traefik Docker 컨테이너의 dynamic config 디렉토리에 배치
# 예: /etc/traefik/dynamic/ 또는 Traefik volume mount 위치
http:
routers:
# Frontend (Next.js)
invyone-frontend:
rule: "Host(`v1.invion.com`)"
entryPoints:
- web
- websecure
service: invyone-frontend
tls:
certResolver: le
# Backend Spring Boot API
invyone-api:
rule: "Host(`api.invion.com`)"
entryPoints:
- web
- websecure
service: invyone-api
tls:
certResolver: le
# Backend Node.js API (필요시)
invyone-node-api:
rule: "Host(`node-api.invion.com`)"
entryPoints:
- web
- websecure
service: invyone-node-api
tls:
certResolver: le
services:
invyone-frontend:
loadBalancer:
servers:
- url: "http://127.0.0.1:30000"
invyone-api:
loadBalancer:
servers:
- url: "http://127.0.0.1:30081"
invyone-node-api:
loadBalancer:
servers:
- url: "http://127.0.0.1:30080"