commit 9ebafb97fbb93e6a6f70901f16a0d5be70d849c2 Author: admin Date: Sun Jul 26 21:33:02 2026 +0800 feat: centralized K8s infra — dev/prod environments diff --git a/README.md b/README.md new file mode 100644 index 0000000..b71341f --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# LPT Infrastructure + +Kubernetes 部署清单,按环境隔离。 + +``` +lpt-infra/ +├── dev/ +│ ├── deployment-lpt-fe.yaml +│ ├── deployment-lpt-be.yaml +│ ├── deployment-lpt-ai.yaml +│ ├── service-lpt-ai.yaml +│ ├── configmap.yaml +│ ├── lpt-secrets.yaml (template) +│ └── regcred-secret.yaml (template) +├── prod/ +│ ├── deployment-lpt-fe.yaml +│ ├── deployment-lpt-be.yaml +│ ├── deployment-lpt-ai.yaml +│ ├── service-lpt-ai.yaml +│ ├── configmap.yaml +│ ├── lpt-secrets.yaml (template) +│ └── regcred-secret.yaml (template) +└── README.md +``` + +## 首次部署 + +### 1. 创建 namespace +```bash +kubectl create namespace lpt-dev +kubectl create namespace lpt-prod +``` + +### 2. 创建 Secret(模板不包含真实值,需手动创建) +```bash +# dev +kubectl create secret generic lpt-secrets \ + --from-literal=llm-api-key=sk-xxx \ + --from-literal=mysql-root-password=xxx \ + -n lpt-dev + +kubectl create secret docker-registry regcred \ + --docker-server=192.168.123.199:5000 \ + --docker-username=admin \ + --docker-password=xxx \ + -n lpt-dev + +# prod +kubectl create secret generic lpt-secrets \ + --from-literal=llm-api-key=sk-xxx \ + --from-literal=mysql-root-password=xxx \ + -n lpt-prod + +kubectl create secret docker-registry regcred \ + --docker-server=192.168.123.199:5000 \ + --docker-username=admin \ + --docker-password=xxx \ + -n lpt-prod +``` + +### 3. Apply 全部配置 +```bash +# dev +kubectl apply -f dev/ + +# prod +kubectl apply -f prod/ +``` + +## 日常更新(CI/CD) + +CI/CD 流程使用 `kubectl set image` 更新镜像,仓库内 `imagePullSecrets` 已配置,无需额外操作。 + +```bash +kubectl set image deployment/lpt-fe lpt-fe=192.168.123.199:5000/lpt-fe: -n lpt-dev +kubectl set image deployment/lpt-be lpt-be=192.168.123.199:5000/lpt-be: -n lpt-dev +kubectl set image deployment/lpt-ai lpt-ai=192.168.123.199:5000/lpt-ai: -n lpt-dev +``` + +## 验证 +```bash +kubectl get pods -n lpt-dev +kubectl get pods -n lpt-prod +``` diff --git a/dev/configmap.yaml b/dev/configmap.yaml new file mode 100644 index 0000000..53dc155 --- /dev/null +++ b/dev/configmap.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: lpt-config + namespace: lpt-dev +data: + # LPT AI Service + LLM_API_URL: https://api.siliconflow.cn/v1/chat/completions + LLM_MODEL: Qwen/Qwen2.5-32B-Instruct + LLM_TIMEOUT_MS: "60000" + LPT_AI-SERVICE_URL: http://lpt-ai:5199 + PORT: "5199" + + # Spring Boot Database + SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/learning_progress_tracker?allowPublicKeyRetrieval=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 diff --git a/dev/deployment-lpt-ai.yaml b/dev/deployment-lpt-ai.yaml new file mode 100644 index 0000000..4c705ca --- /dev/null +++ b/dev/deployment-lpt-ai.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lpt-ai + namespace: lpt-dev + labels: + app: lpt-ai +spec: + replicas: 1 + selector: + matchLabels: + app: lpt-ai + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: lpt-ai + spec: + imagePullSecrets: + - name: regcred + containers: + - name: lpt-ai + image: 192.168.123.199:5000/lpt-ai:dev + imagePullPolicy: IfNotPresent + ports: + - containerPort: 5199 + env: + - name: PORT + valueFrom: + configMapKeyRef: + name: lpt-config + key: PORT + - name: LLM_API_URL + valueFrom: + configMapKeyRef: + name: lpt-config + key: LLM_API_URL + - name: LLM_MODEL + valueFrom: + configMapKeyRef: + name: lpt-config + key: LLM_MODEL + - name: LLM_TIMEOUT_MS + valueFrom: + configMapKeyRef: + name: lpt-config + key: LLM_TIMEOUT_MS + - name: LLM_API_KEY + valueFrom: + secretKeyRef: + name: lpt-secrets + key: llm-api-key + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + livenessProbe: + httpGet: + path: /health + port: 5199 + initialDelaySeconds: 10 + periodSeconds: 15 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 5199 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/dev/deployment-lpt-be.yaml b/dev/deployment-lpt-be.yaml new file mode 100644 index 0000000..797a064 --- /dev/null +++ b/dev/deployment-lpt-be.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lpt-be + namespace: lpt-dev + labels: + app: lpt-be +spec: + replicas: 1 + selector: + matchLabels: + app: lpt-be + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: lpt-be + spec: + imagePullSecrets: + - name: regcred + containers: + - name: lpt-be + image: 192.168.123.199:5000/lpt-be:dev + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8888 + env: + - name: SPRING_PROFILES_ACTIVE + value: dev + - name: SPRING_DATASOURCE_URL + valueFrom: + configMapKeyRef: + name: lpt-config + key: SPRING_DATASOURCE_URL + - name: SPRING_DATASOURCE_USERNAME + value: root + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: lpt-secrets + key: mysql-root-password + - name: LPT_AI-SERVICE_URL + valueFrom: + configMapKeyRef: + name: lpt-config + key: LPT_AI-SERVICE_URL + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 1Gi + livenessProbe: + httpGet: + path: /actuator/health + port: 8888 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8888 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/dev/deployment-lpt-fe.yaml b/dev/deployment-lpt-fe.yaml new file mode 100644 index 0000000..1434eab --- /dev/null +++ b/dev/deployment-lpt-fe.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lpt-fe + namespace: lpt-dev + labels: + app: lpt-fe +spec: + replicas: 1 + selector: + matchLabels: + app: lpt-fe + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: lpt-fe + spec: + imagePullSecrets: + - name: regcred + containers: + - name: lpt-fe + image: 192.168.123.199:5000/lpt-fe:dev + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + livenessProbe: + httpGet: + path: /health + port: 80 + initialDelaySeconds: 5 + periodSeconds: 15 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 80 + initialDelaySeconds: 3 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/dev/lpt-secrets.yaml b/dev/lpt-secrets.yaml new file mode 100644 index 0000000..a29c292 --- /dev/null +++ b/dev/lpt-secrets.yaml @@ -0,0 +1,20 @@ +# LPT Application Secrets — TEMPLATE +# DO NOT commit real secrets to git! +# +# Create in the cluster: +# kubectl create secret generic lpt-secrets \ +# --from-literal=llm-api-key= \ +# --from-literal=mysql-root-password= \ +# --namespace=lpt-dev +--- +apiVersion: v1 +kind: Secret +metadata: + name: lpt-secrets + namespace: lpt-dev +type: Opaque +data: {} +# Use stringData when applying: +# stringData: +# llm-api-key: "sk-xxxxxxxxxxxxxxxx" +# mysql-root-password: "your-mysql-password" diff --git a/dev/regcred-secret.yaml b/dev/regcred-secret.yaml new file mode 100644 index 0000000..a903bd4 --- /dev/null +++ b/dev/regcred-secret.yaml @@ -0,0 +1,18 @@ +# Docker Registry Secret — TEMPLATE +# DO NOT commit real credentials! +# +# Managed by Gitea Actions workflow or created manually: +# kubectl create secret docker-registry regcred \ +# --docker-server=192.168.123.199:5000 \ +# --docker-username=admin \ +# --docker-password= \ +# --namespace=lpt-dev +--- +apiVersion: v1 +kind: Secret +metadata: + name: regcred + namespace: lpt-dev +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: "" diff --git a/dev/service-lpt-ai.yaml b/dev/service-lpt-ai.yaml new file mode 100644 index 0000000..fbd01c6 --- /dev/null +++ b/dev/service-lpt-ai.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: lpt-ai + namespace: lpt-dev + labels: + app: lpt-ai +spec: + type: ClusterIP + selector: + app: lpt-ai + ports: + - port: 5199 + targetPort: 5199 + protocol: TCP diff --git a/prod/configmap.yaml b/prod/configmap.yaml new file mode 100644 index 0000000..38d785d --- /dev/null +++ b/prod/configmap.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: lpt-config + namespace: lpt-prod +data: + # LPT AI Service + LLM_API_URL: https://api.siliconflow.cn/v1/chat/completions + LLM_MODEL: Qwen/Qwen2.5-32B-Instruct + LLM_TIMEOUT_MS: "60000" + LPT_AI-SERVICE_URL: http://lpt-ai:5199 + PORT: "5199" + + # Spring Boot Database + SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/learning_progress_tracker?allowPublicKeyRetrieval=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 diff --git a/prod/deployment-lpt-ai.yaml b/prod/deployment-lpt-ai.yaml new file mode 100644 index 0000000..c42dfad --- /dev/null +++ b/prod/deployment-lpt-ai.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lpt-ai + namespace: lpt-prod + labels: + app: lpt-ai +spec: + replicas: 1 + selector: + matchLabels: + app: lpt-ai + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: lpt-ai + spec: + imagePullSecrets: + - name: regcred + containers: + - name: lpt-ai + image: 192.168.123.199:5000/lpt-ai:prod + imagePullPolicy: IfNotPresent + ports: + - containerPort: 5199 + env: + - name: PORT + valueFrom: + configMapKeyRef: + name: lpt-config + key: PORT + - name: LLM_API_URL + valueFrom: + configMapKeyRef: + name: lpt-config + key: LLM_API_URL + - name: LLM_MODEL + valueFrom: + configMapKeyRef: + name: lpt-config + key: LLM_MODEL + - name: LLM_TIMEOUT_MS + valueFrom: + configMapKeyRef: + name: lpt-config + key: LLM_TIMEOUT_MS + - name: LLM_API_KEY + valueFrom: + secretKeyRef: + name: lpt-secrets + key: llm-api-key + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + livenessProbe: + httpGet: + path: /health + port: 5199 + initialDelaySeconds: 10 + periodSeconds: 15 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 5199 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/prod/deployment-lpt-be.yaml b/prod/deployment-lpt-be.yaml new file mode 100644 index 0000000..68188b9 --- /dev/null +++ b/prod/deployment-lpt-be.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lpt-be + namespace: lpt-prod + labels: + app: lpt-be +spec: + replicas: 1 + selector: + matchLabels: + app: lpt-be + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: lpt-be + spec: + imagePullSecrets: + - name: regcred + containers: + - name: lpt-be + image: 192.168.123.199:5000/lpt-be:prod + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8888 + env: + - name: SPRING_PROFILES_ACTIVE + value: prod + - name: SPRING_DATASOURCE_URL + valueFrom: + configMapKeyRef: + name: lpt-config + key: SPRING_DATASOURCE_URL + - name: SPRING_DATASOURCE_USERNAME + value: root + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: lpt-secrets + key: mysql-root-password + - name: LPT_AI-SERVICE_URL + valueFrom: + configMapKeyRef: + name: lpt-config + key: LPT_AI-SERVICE_URL + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 1Gi + livenessProbe: + httpGet: + path: /actuator/health + port: 8888 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8888 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/prod/deployment-lpt-fe.yaml b/prod/deployment-lpt-fe.yaml new file mode 100644 index 0000000..21e8519 --- /dev/null +++ b/prod/deployment-lpt-fe.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lpt-fe + namespace: lpt-prod + labels: + app: lpt-fe +spec: + replicas: 1 + selector: + matchLabels: + app: lpt-fe + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: lpt-fe + spec: + imagePullSecrets: + - name: regcred + containers: + - name: lpt-fe + image: 192.168.123.199:5000/lpt-fe:prod + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + livenessProbe: + httpGet: + path: /health + port: 80 + initialDelaySeconds: 5 + periodSeconds: 15 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 80 + initialDelaySeconds: 3 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/prod/lpt-secrets.yaml b/prod/lpt-secrets.yaml new file mode 100644 index 0000000..cfa7e23 --- /dev/null +++ b/prod/lpt-secrets.yaml @@ -0,0 +1,10 @@ +# LPT Application Secrets — TEMPLATE +# DO NOT commit real secrets to git! +--- +apiVersion: v1 +kind: Secret +metadata: + name: lpt-secrets + namespace: lpt-prod +type: Opaque +data: {} diff --git a/prod/regcred-secret.yaml b/prod/regcred-secret.yaml new file mode 100644 index 0000000..bdf36ff --- /dev/null +++ b/prod/regcred-secret.yaml @@ -0,0 +1,11 @@ +# Docker Registry Secret — TEMPLATE +# DO NOT commit real credentials! +--- +apiVersion: v1 +kind: Secret +metadata: + name: regcred + namespace: lpt-prod +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: "" diff --git a/prod/service-lpt-ai.yaml b/prod/service-lpt-ai.yaml new file mode 100644 index 0000000..89dd435 --- /dev/null +++ b/prod/service-lpt-ai.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: lpt-ai + namespace: lpt-prod + labels: + app: lpt-ai +spec: + type: ClusterIP + selector: + app: lpt-ai + ports: + - port: 5199 + targetPort: 5199 + protocol: TCP