feat : initial commit

This commit is contained in:
hehe 2025-06-20 05:56:38 +00:00
commit 9a7e75865a
28 changed files with 1260 additions and 0 deletions

View File

@ -0,0 +1,24 @@
# Build stage
FROM openjdk:21-jdk-slim AS builder
ARG BUILD_LIB_DIR
ARG ARTIFACTORY_FILE
COPY ${BUILD_LIB_DIR}/${ARTIFACTORY_FILE} app.jar
# Run stage
FROM openjdk:21-jdk-slim
ENV USERNAME k8s
ENV ARTIFACTORY_HOME /home/${USERNAME}
ENV JAVA_OPTS=""
# Add a non-root user
RUN adduser --system --group ${USERNAME} && \
mkdir -p ${ARTIFACTORY_HOME} && \
chown ${USERNAME}:${USERNAME} ${ARTIFACTORY_HOME}
WORKDIR ${ARTIFACTORY_HOME}
COPY --from=builder app.jar app.jar
RUN chown ${USERNAME}:${USERNAME} app.jar
USER ${USERNAME}
ENTRYPOINT [ "sh", "-c" ]
CMD ["java ${JAVA_OPTS} -jar app.jar"]

View File

@ -0,0 +1,120 @@
# HealthSync Backend 통합 Dockerfile
# 전체 멀티프로젝트를 한 번에 빌드하고 특정 서비스를 선택 실행
# =============================================================================
# Build Stage: 전체 멀티프로젝트 빌드
# =============================================================================
FROM openjdk:21-jdk-slim AS builder
# 빌드에 필요한 패키지 설치
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
# Gradle Wrapper 및 설정 파일 복사
COPY gradle/ gradle/
COPY gradlew .
COPY gradle.properties .
COPY settings.gradle .
COPY build.gradle .
# 각 서비스 소스코드 복사
COPY common/ common/
COPY api-gateway/ api-gateway/
COPY user-service/ user-service/
COPY health-service/ health-service/
COPY intelligence-service/ intelligence-service/
COPY goal-service/ goal-service/
COPY motivator-service/ motivator-service/
# Gradle 실행 권한 부여
RUN chmod +x gradlew
# 전체 프로젝트 빌드 (테스트 제외)
RUN ./gradlew clean build -x test
# 빌드된 JAR 파일들 확인
RUN find . -name "*.jar" -type f
# =============================================================================
# Runtime Stage: 실행 환경
# =============================================================================
FROM openjdk:21-jdk-slim
# 런타임 사용자 생성
RUN addgroup --system --gid 1001 healthsync && \
adduser --system --uid 1001 --gid 1001 healthsync
# 작업 디렉토리 설정
WORKDIR /app
# 빌드된 JAR 파일들 복사
COPY --from=builder /workspace/api-gateway/build/libs/*.jar ./jars/api-gateway.jar
COPY --from=builder /workspace/user-service/build/libs/*.jar ./jars/user-service.jar
COPY --from=builder /workspace/health-service/build/libs/*.jar ./jars/health-service.jar
COPY --from=builder /workspace/intelligence-service/build/libs/*.jar ./jars/intelligence-service.jar
COPY --from=builder /workspace/goal-service/build/libs/*.jar ./jars/goal-service.jar
COPY --from=builder /workspace/motivator-service/build/libs/*.jar ./jars/motivator-service.jar
# 실행 스크립트 생성
RUN cat > /app/start-service.sh << 'EOF'
#!/bin/bash
SERVICE_NAME=${SERVICE_NAME:-user-service}
JAVA_OPTS=${JAVA_OPTS:-"-Xms256m -Xmx1024m"}
echo "Starting HealthSync ${SERVICE_NAME}..."
echo "Java Options: ${JAVA_OPTS}"
case ${SERVICE_NAME} in
"api-gateway")
exec java ${JAVA_OPTS} -jar /app/jars/api-gateway.jar
;;
"user-service")
exec java ${JAVA_OPTS} -jar /app/jars/user-service.jar
;;
"health-service")
exec java ${JAVA_OPTS} -jar /app/jars/health-service.jar
;;
"intelligence-service")
exec java ${JAVA_OPTS} -jar /app/jars/intelligence-service.jar
;;
"goal-service")
exec java ${JAVA_OPTS} -jar /app/jars/goal-service.jar
;;
"motivator-service")
exec java ${JAVA_OPTS} -jar /app/jars/motivator-service.jar
;;
*)
echo "Error: Unknown service name '${SERVICE_NAME}'"
echo "Available services: api-gateway, user-service, health-service, intelligence-service, goal-service, motivator-service"
exit 1
;;
esac
EOF
# 스크립트 실행 권한 부여
RUN chmod +x /app/start-service.sh
# 디렉토리 소유자 변경
RUN chown -R healthsync:healthsync /app
# 사용자 변경
USER healthsync
# 헬스체크 스크립트 생성
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1
# 기본 포트 노출 (환경변수로 오버라이드 가능)
EXPOSE 8080 8081 8082 8083 8084 8085
# 환경변수 기본값 설정
ENV SERVICE_NAME=user-service
ENV JAVA_OPTS="-Xms256m -Xmx1024m"
ENV SPRING_PROFILES_ACTIVE=docker
# 실행 명령
ENTRYPOINT ["/app/start-service.sh"]

View File

@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: health-service
namespace: team1tier-healthsync-ns
spec:
replicas: 2
selector:
matchLabels:
app: health-service
template:
metadata:
labels:
app: health-service
spec:
imagePullSecrets:
- name: acr-secret
containers:
- name: health-service
image: acrhealthsync01.azurecr.io/team1tier/health-service:1.0.0
imagePullPolicy: Always
ports:
- containerPort: 8082
envFrom:
- configMapRef:
name: common-config
- configMapRef:
name: health-config
- secretRef:
name: common-secret
- secretRef:
name: database-secret
- secretRef:
name: redis-secret
- secretRef:
name: health-db-secret
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi

View File

@ -0,0 +1,60 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: common-config
namespace: team1tier-healthsync-ns
data:
ALLOWED_ORIGINS: "http://20.249.193.105"
DDL_AUTO: "update"
SHOW_SQL: "false"
LOG_LEVEL: "INFO"
WEB_LOG_LEVEL: "INFO"
GOOGLE_REDIRECT_ID : http://team1tier.20.214.196.128.nip.io/login/oauth2/code/google
GOOGLE_CLIENT_ID: 198383870460-s1s72vgu91nq9qvg5dai28vafj7mlag1.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET: 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
OAUTH2_REDIRECT_URL : http://team1tier.20.214.196.128.nip.io/login
---
apiVersion: v1
kind: ConfigMap
metadata:
name: user-config
namespace: team1tier-healthsync-ns
data:
SERVER_PORT: "8081"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: health-config
namespace: team1tier-healthsync-ns
data:
SERVER_PORT: "8082"
USER_SERVICE_URL: "http://user-service:80"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: goal-config
namespace: team1tier-healthsync-ns
data:
SERVER_PORT: "8084"
USER_SERVICE_URL: "http://user-service:80"
INTELLIGENCE_SERVICE_URL: "http://team1tier.20.214.196.128.nip.io"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
namespace: team1tier-healthsync-ns
data:
# Azure Cache for Redis 설정
REDIS_HOST: "redis-digitalgarage-01.redis.cache.windows.net"
REDIS_PORT: "6380" # SSL 포트
REDIS_SSL: "true" # SSL 활성화
REDIS_TIMEOUT: "2000"
REDIS_DATABASE: "0"
# Connection Pool 설정
REDIS_LETTUCE_POOL_MAX_ACTIVE: "8"
REDIS_LETTUCE_POOL_MAX_IDLE: "8"
REDIS_LETTUCE_POOL_MIN_IDLE: "0"
---

View File

@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: goal-service
namespace: team1tier-healthsync-ns
spec:
revisionHistoryLimit: 3
replicas: 2
selector:
matchLabels:
app: goal-service
template:
metadata:
labels:
app: goal-service
spec:
imagePullSecrets:
- name: acr-secret
containers:
- name: goal-service
image: acrhealthsync01.azurecr.io/team1tier/goal-service:1.0.8
imagePullPolicy: Always
ports:
- containerPort: 8084
envFrom:
- configMapRef:
name: common-config
- configMapRef:
name: goal-config
- configMapRef:
name: redis-config
- secretRef:
name: common-secret
- secretRef:
name: database-secret
- secretRef:
name: redis-secret
- secretRef:
name: goal-db-secret
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi
# startupProbe:
# httpGet:
# path: /actuator/health
# port: 8084
# failureThreshold: 30
# periodSeconds: 10
# livenessProbe:
# httpGet:
# path: /actuator/health
# port: 8084
# initialDelaySeconds: 60
# periodSeconds: 15
# readinessProbe:
# httpGet:
# path: /actuator/health
# port: 8084
# initialDelaySeconds: 10
# periodSeconds: 5

View File

@ -0,0 +1,56 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: health-service
namespace: team1tier-healthsync-ns
spec:
revisionHistoryLimit: 3
replicas: 2
selector:
matchLabels:
app: health-service
template:
metadata:
labels:
app: health-service
spec:
imagePullSecrets:
- name: acr-secret
containers:
- name: health-service
image: acrhealthsync01.azurecr.io/team1tier/health-service:1.0.13
imagePullPolicy: Always
ports:
- containerPort: 8082
env:
- name: SPRING_DATA_REDIS_HOST
value: "redis-digitalgarage-01.redis.cache.windows.net"
- name: SPRING_DATA_REDIS_PORT
value: "6380"
- name: SPRING_DATA_REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-secret
key: REDIS_PASSWORD
- name: SPRING_DATA_REDIS_SSL_ENABLED
value: "true"
- name: SPRING_DATA_REDIS_TIMEOUT
value: "2000ms"
- name: DB_URL
value: "jdbc:postgresql://psql-digitalgarage-01.postgres.database.azure.com:5432/healthsync_db"
- name: DB_USERNAME
value: "team1tier"
- name: DB_PASSWORD
value: "Hi5Jessica!"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: common-secret
key: JWT_SECRET
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi

View File

@ -0,0 +1,42 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: team1tier-healthsync-ns
spec:
revisionHistoryLimit: 3
replicas: 1
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
imagePullSecrets:
- name: acr-secret
containers:
- name: user-service
image: acrhealthsync01.azurecr.io/team1tier/user-service:1.0.11
imagePullPolicy: Always
ports:
- containerPort: 8081
envFrom:
- configMapRef:
name: common-config
- configMapRef:
name: user-config
- secretRef:
name: database-secret
- secretRef:
name: redis-secret
- secretRef:
name: user-db-secret
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi

View File

@ -0,0 +1,55 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: healthsync-ingress
namespace: team1tier-healthsync-ns
annotations:
kubernetes.io/ingress.class: nginx
spec:
ingressClassName: nginx
rules:
- host: team1tier.20.214.196.128.nip.io
http:
paths:
- path: /login/oauth2
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /api/user
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /api/auth
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /oauth2
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /api/health
pathType: Prefix
backend:
service:
name: health-service
port:
number: 80
- path: /api/goals
pathType: Prefix
backend:
service:
name: goal-service
port:
number: 80

View File

@ -0,0 +1,46 @@
---
apiVersion: v1
kind: Secret
metadata:
name: database-secret
namespace: team1tier-healthsync-ns
type: Opaque
stringData:
DB_USERNAME: "team1tier"
DB_PASSWORD: "Hi5Jessica!"
---
apiVersion: v1
kind: Secret
metadata:
name: redis-secret
namespace: team1tier-healthsync-ns
type: Opaque
stringData:
REDIS_PASSWORD: "HUezXQsxbphIeBy8FV9JDA3WaZDwOozGEAzCaByUk40="
---
apiVersion: v1
kind: Secret
metadata:
name: user-db-secret
namespace: team1tier-healthsync-ns
type: Opaque
stringData:
DB_URL: "jdbc:postgresql://psql-digitalgarage-01.postgres.database.azure.com:5432/healthsync_db"
---
apiVersion: v1
kind: Secret
metadata:
name: health-db-secret
namespace: team1tier-healthsync-ns
type: Opaque
stringData:
DB_URL: "jdbc:postgresql://psql-digitalgarage-01.postgres.database.azure.com:5432/healthsync_db"
---
apiVersion: v1
kind: Secret
metadata:
name: goal-db-secret
namespace: team1tier-healthsync-ns
type: Opaque
stringData:
DB_URL: "jdbc:postgresql://psql-digitalgarage-01.postgres.database.azure.com:5432/healthsync_db"

View File

@ -0,0 +1,38 @@
apiVersion: v1
kind: Service
metadata:
name: user-service
namespace: team1tier-healthsync-ns
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8081
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: health-service
namespace: team1tier-healthsync-ns
spec:
selector:
app: health-service
ports:
- port: 80
targetPort: 8082
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: goal-service
namespace: team1tier-healthsync-ns
spec:
selector:
app: goal-service
ports:
- port: 80
targetPort: 8084
type: ClusterIP

View File

@ -0,0 +1,32 @@
# Node.js Multi-stage build for React
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production stage with simple Nginx
FROM nginx:alpine
# Copy built app
COPY --from=builder /app/build /usr/share/nginx/html
# Create simple nginx config for React SPA
RUN echo 'server {' > /etc/nginx/conf.d/default.conf && \
echo ' listen 80;' >> /etc/nginx/conf.d/default.conf && \
echo ' server_name localhost;' >> /etc/nginx/conf.d/default.conf && \
echo ' root /usr/share/nginx/html;' >> /etc/nginx/conf.d/default.conf && \
echo ' index index.html;' >> /etc/nginx/conf.d/default.conf && \
echo ' location / {' >> /etc/nginx/conf.d/default.conf && \
echo ' try_files $uri $uri/ /index.html;' >> /etc/nginx/conf.d/default.conf && \
echo ' }' >> /etc/nginx/conf.d/default.conf && \
echo ' location /health {' >> /etc/nginx/conf.d/default.conf && \
echo ' return 200 "healthy";' >> /etc/nginx/conf.d/default.conf && \
echo ' add_header Content-Type text/plain;' >> /etc/nginx/conf.d/default.conf && \
echo ' }' >> /etc/nginx/conf.d/default.conf && \
echo '}' >> /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -0,0 +1,50 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
location / {
try_files $uri $uri/ /index.html;
}
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
}
}

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: frontend-config
namespace: team1tier-healthsync-front-ns
data:
runtime-env.js: |
window.__runtime_config__ = {
GOOGLE_CLIENT_ID: '198383870460-s1s72vgu91nq9qvg5dai28vafj7mlag1.apps.googleusercontent.com',
AUTH_URL: 'http://team1tier.20.214.196.128.nip.io/api/auth',
HEALTH_URL: 'http://team1tier.20.214.196.128.nip.io/api/health',
INTELLIGENCE_URL: 'http://team1tier.20.214.196.128.nip.io/api/intelligence',
GOAL_URL: 'http://team1tier.20.214.196.128.nip.io/api/goals',
MOTIVATOR_URL: 'http://team1tier.20.214.196.128.nip.io/api/motivator',
USER_URL: 'http://team1tier.20.214.196.128.nip.io/api/user'
};

View File

@ -0,0 +1,41 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: healthsync-front
namespace: team1tier-healthsync-front-ns
labels:
app: healthsync-front
spec:
revisionHistoryLimit: 3
replicas: 1
selector:
matchLabels:
app: healthsync-front
template:
metadata:
labels:
app: healthsync-front
spec:
imagePullSecrets:
- name: acr-secret
containers:
- name: healthsync-front
image: acrhealthsync01.azurecr.io/team1tier/healthsync-front:1.0.23
imagePullPolicy: Always
ports:
- containerPort: 80
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi
volumeMounts:
- name: runtime-config
mountPath: /usr/share/nginx/html/runtime-env.js
subPath: runtime-env.js
volumes:
- name: runtime-config
configMap:
name: frontend-config

View File

@ -0,0 +1,21 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: healthsync-frontend-ingress
namespace: team1tier-healthsync-front-ns
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: team1tier.20.214.196.128.nip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: healthsync-front-service
port:
number: 80

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: healthsync-front-service
namespace: team1tier-healthsync-front-ns
labels:
app: healthsync-front
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: healthsync-front

View File

@ -0,0 +1,52 @@
# ==================================================
# deployment/manifest/kustomization.yaml
# ==================================================
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: team1tier-healthsync-intelligence-ns
resources:
- configmap/intelligence-service-configmap.yaml
- secret/intelligence-service-secret.yaml
- deployment/intelligence-service-deployment.yaml
- service/intelligence-service-service.yaml
- hpa/intelligence-service-hpa.yaml
- ingress/intelligence-service-ingress.yaml
# 이미지 자동 치환 (빌드 스크립트에서 설정)
images:
- name: intelligence-service
newName: acrhealthsync01.azurecr.io/team1tier/intelligence-service
newTag: "1.0.0"
# 공통 라벨
commonLabels:
app: intelligence-service
team: team1tier
environment: production
# 네임스페이스 설정
namespace: team1tier-healthsync-intelligence-ns
# 변수 치환 (ConfigMap Generator 사용 가능)
configMapGenerator:
- name: build-info
literals:
- build.version=1.0.0
- build.timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Patch 설정 (환경별 설정 오버라이드)
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligence-service
namespace: team1tier-healthsync-intelligence-ns
spec:
template:
metadata:
annotations:
build.version: "1.0.0"
deployment.timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")

View File

@ -0,0 +1,51 @@
# ==================================================
# deployment/manifest/configmap/intelligence-service-configmap.yaml
# ==================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: intelligence-service-configmap
namespace: team1tier-healthsync-intelligence-ns
labels:
app: intelligence-service
component: config
data:
# 서비스 기본 설정
APP_NAME: "HealthSync Intelligence Service"
APP_VERSION: "1.0.0"
DEBUG: "false"
HOST: "0.0.0.0"
PORT: "8083"
API_V1_PREFIX: "/api/intelligence"
CORS_ORIGINS: '["*"]'
# 로깅 설정
LOG_LEVEL: "INFO"
# 토큰 설정
ALGORITHM: "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: "30"
# Claude AI 설정
CLAUDE_MODEL: "claude-3-5-sonnet-20241022"
CLAUDE_MAX_TOKENS: "1500"
CLAUDE_TEMPERATURE: "0.7"
CLAUDE_TIMEOUT: "30"
CLAUDE_API_BASE_URL: "https://api.anthropic.com"
# 다른 마이크로서비스 URL
USER_SERVICE_URL: "http://user-service:8081"
HEALTH_SERVICE_URL: "http://health-service:8082"
# Redis 설정
REDIS_HOST: "redis-digitalgarage-01.redis.cache.windows.net"
REDIS_PORT: "6380"
REDIS_DB: "0"
REDIS_SSL: "true"
# PostgreSQL 설정
DB_HOST: "psql-digitalgarage-01.postgres.database.azure.com"
DB_PORT: "5432"
DB_NAME: "healthsync_db"
DB_MIN_SIZE: "1"
DB_MAX_SIZE: "10"

View File

@ -0,0 +1,168 @@
# ==================================================
# deployment/manifest/deployment/intelligence-service-deployment.yaml
# ==================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligence-service
namespace: team1tier-healthsync-intelligence-ns
labels:
app: intelligence-service
component: backend
tier: api
spec:
revisionHistoryLimit: 3
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: intelligence-service
template:
metadata:
labels:
app: intelligence-service
component: backend
environment: production
team: team1tier
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8083"
prometheus.io/path: "/metrics"
spec:
# Image Pull Secret
imagePullSecrets:
- name: acr-secret
# 보안 컨텍스트
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: intelligence-service
# 이미지는 Kustomize에서 자동으로 치환됨
image: acrhealthsync01.azurecr.io/team1tier/intelligence-service:1.3.3
imagePullPolicy: Always
ports:
- name: http
containerPort: 8083
protocol: TCP
# 환경변수 설정
envFrom:
- configMapRef:
name: intelligence-service-configmap
- secretRef:
name: intelligence-service-secret
# 추가 환경변수
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
# 리소스 제한
resources:
requests:
cpu: "200m"
memory: "512Mi"
ephemeral-storage: "1Gi"
limits:
cpu: "1000m"
memory: "1Gi"
ephemeral-storage: "2Gi"
# Startup Probe: 초기 시작 확인 (최대 3분 대기)
# startupProbe:
# httpGet:
# path: /api/v1/health/status
# port: http
# scheme: HTTP
# initialDelaySeconds: 30
# periodSeconds: 10
# timeoutSeconds: 5
# failureThreshold: 18 # 30초 + (18 * 10초) = 최대 3분
# successThreshold: 1
# Readiness Probe: 트래픽 수신 준비 확인
# readinessProbe:
# httpGet:
# path: /api/v1/health/status
# port: http
# scheme: HTTP
# initialDelaySeconds: 5
# periodSeconds: 5
# timeoutSeconds: 3
# failureThreshold: 3
# successThreshold: 1
# Liveness Probe: 서비스 생존 확인
# livenessProbe:
# httpGet:
# path: /api/v1/health/status
# port: http
# scheme: HTTP
# initialDelaySeconds: 60
# periodSeconds: 10
# timeoutSeconds: 5
# failureThreshold: 3
# successThreshold: 1
# 보안 컨텍스트
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
# 볼륨 마운트 (로그 수집용)
volumeMounts:
- name: tmp-volume
mountPath: /tmp
- name: cache-volume
mountPath: /app/cache
# 볼륨 정의
volumes:
- name: tmp-volume
emptyDir: {}
- name: cache-volume
emptyDir: {}
# DNS 설정
dnsPolicy: ClusterFirst
# 재시작 정책
restartPolicy: Always
# 스케줄링 설정
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- intelligence-service
topologyKey: kubernetes.io/hostname

View File

@ -0,0 +1,44 @@
# ==================================================
# deployment/manifest/hpa/intelligence-service-hpa.yaml
# ==================================================
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: intelligence-service-hpa
namespace: team1tier-healthsync-intelligence-ns
labels:
app: intelligence-service
component: autoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: intelligence-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 30

View File

@ -0,0 +1,32 @@
# ==================================================
# deployment/manifest/ingress/intelligence-service-ingress.yaml
# ==================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: intelligence-service-ingress
namespace: team1tier-healthsync-intelligence-ns
labels:
app: intelligence-service
component: ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/proxy-send-timeout: "30"
nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
ingressClassName: nginx
rules:
- host: team1tier.20.214.196.128.nip.io # 실제 환경에서는 실제 도메인으로 변경
http:
paths:
- path: /api/intelligence
pathType: Prefix
backend:
service:
name: intelligence-service
port:
number: 8083

View File

@ -0,0 +1,29 @@
# ==================================================
# deployment/manifest/secret/intelligence-service-secret.yaml
# ==================================================
apiVersion: v1
kind: Secret
metadata:
name: intelligence-service-secret
namespace: team1tier-healthsync-intelligence-ns
labels:
app: intelligence-service
component: secret
type: Opaque
stringData:
# JWT 보안키 (openssl rand -base64 32로 생성)
SECRET_KEY: "aHVlYWx0aHN5bmMtaW50ZWxsaWdlbmNlLXNlY3JldC1rZXktMjAyNQ=="
# Claude API 키 (실제 환경에서는 Azure Key Vault 연동 권장)
CLAUDE_API_KEY: "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"
# 데이터베이스 접속 정보
DB_USERNAME: "team1tier"
DB_PASSWORD: "Hi5Jessica!"
# Redis 접속 정보
REDIS_PASSWORD: "HUezXQsxbphIeBy8FV9JDA3WaZDwOozGEAzCaByUk40="
# PineCone
PINECONE_API_KEY: "pcsk_2bcssc_wVP3hmKVfo8We9Cd4mdo2PM5s4Ab7hPahxWmyG1v6AbpEhXPKuNjK2qKb9KbJJ"
PINECONE_ENVIRONMENT: "aped-4627-b74a"

View File

@ -0,0 +1,26 @@
# ==================================================
# deployment/manifest/service/intelligence-service-service.yaml
# ==================================================
apiVersion: v1
kind: Service
metadata:
name: intelligence-service
namespace: team1tier-healthsync-intelligence-ns
labels:
app: intelligence-service
component: backend
tier: api
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "false"
spec:
type: ClusterIP
sessionAffinity: None
ports:
- name: http
port: 8083
targetPort: http
protocol: TCP
selector:
app: intelligence-service
component: backend

View File

@ -0,0 +1,31 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: motivator-config
namespace: team1tier-healthsync-motivator-ns
data:
# Application Settings
APP_NAME: "HealthSync Motivator Batch"
APP_VERSION: "1.0.0"
DEBUG: "false"
LOG_LEVEL: "INFO"
# Database Configuration
DB_HOST: "psql-digitalgarage-01.postgres.database.azure.com"
DB_PORT: "5432"
DB_NAME: "healthsync_db"
DB_USERNAME: "team1tier"
# Claude AI Configuration
CLAUDE_MODEL: "claude-3-5-sonnet-20241022"
CLAUDE_MAX_TOKENS: "150"
CLAUDE_TEMPERATURE: "0.7"
CLAUDE_TIMEOUT: "30"
# Batch Configuration
BATCH_SIZE: "100"
MAX_RETRIES: "3"
# Azure Service Bus Configuration
AZURE_SERVICEBUS_NAMESPACE: "sb-healthsync.servicebus.windows.net"
AZURE_SERVICEBUS_QUEUE_NAME: "healthsync-notifications"

View File

@ -0,0 +1,48 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: healthsync-motivator-batch
namespace: team1tier-healthsync-motivator-ns
labels:
app: healthsync-motivator
type: batch
spec:
# 10분마다 실행
schedule: "0 */10 * * *"
timeZone: "Asia/Seoul"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
metadata:
labels:
app: healthsync-motivator-batch
spec:
imagePullSecrets:
- name: acr-secret
restartPolicy: OnFailure
containers:
- name: motivator-batch
image: acrhealthsync01.azurecr.io/team1tier/motivator-service:1.1.1
imagePullPolicy: Always
command: ["python", "app/batch_runner.py"]
envFrom:
- configMapRef:
name: motivator-config
- secretRef:
name: motivator-secret
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi
# 배치 실행 타임아웃 설정
env:
- name: PYTHONPATH
value: "/app"
- name: PYTHONUNBUFFERED
value: "1"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Secret
metadata:
name: motivator-secret
namespace: team1tier-healthsync-motivator-ns
type: Opaque
stringData:
# Database Password
DB_PASSWORD: "Hi5Jessica!"
# Claude AI API Key (실제 키로 교체 필요)
CLAUDE_API_KEY: "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"
# Azure Service Bus Connection String (실제 연결 문자열로 교체 필요)
AZURE_SERVICEBUS_CONNECTION_STRING: "Endpoint=sb://sb-healthsync.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Zur5rLIi8MNQ8sk3T/TvsdVu+i02bbxaE+ASbCAXvZI="

View File

@ -0,0 +1,39 @@
apiVersion: batch/v1
kind: Job
metadata:
name: healthsync-motivator-manual
namespace: team1tier-healthsync-motivator-ns
labels:
app: healthsync-motivator
type: manual
spec:
template:
metadata:
labels:
app: healthsync-motivator-manual
spec:
imagePullSecrets:
- name: acr-secret
restartPolicy: Never
containers:
- name: motivator-batch
image: acrhealthsync01.azurecr.io/team1tier/motivator-service:1.1.1
imagePullPolicy: Always
command: ["python", "app/batch_runner.py"]
envFrom:
- configMapRef:
name: motivator-config
- secretRef:
name: motivator-secret
resources:
requests:
cpu: 256m
memory: 256Mi
limits:
cpu: 1024m
memory: 1024Mi
env:
- name: PYTHONPATH
value: "/app"
- name: PYTHONUNBUFFERED
value: "1"

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# HealthSync_Manifest
ArgoCD용 Manifest 파일입니다.