feat: add Gitea CI/CD workflows and Kubernetes deployment

Add comprehensive Gitea Actions workflows for automated CI/CD pipeline with
Harbor image registry and Kubernetes deployment support.

Gitea Actions Workflows:
- ci-test.yml: Automated testing pipeline (unit tests + code quality)
- build-and-deploy.yml: Build Docker image, push to Harbor, deploy to K8s
- Automatic deployment on push to main/develop branches
- Manual deployment trigger with environment selection

Kubernetes Configuration:
- deployment.yml: 2-replica deployment with rolling update strategy
- service.yml: ClusterIP service + headless service
- configmap.yml: Application configuration with prod profile
- secret.yml.template: Secret template for sensitive data
- ingress.yml: Nginx Ingress with SSL and health check support
- deploy.sh: Quick deployment script with interactive prompts

Key Features:
✅ Zero-downtime rolling updates (maxUnavailable: 0)
✅ Harbor private registry integration (harbor.omniverseai.net)
✅ Multi-stage health checks (liveness, readiness, startup probes)
✅ Resource limits (CPU: 2 cores, Memory: 2Gi)
✅ Auto-rollback on deployment failure
✅ Session affinity support
✅ Prometheus metrics endpoint (/actuator/prometheus)
✅ Base64 KUBECONFIG support for secure K8s access

Image Tagging Strategy:
- main branch: latest + latest-<commit-sha>
- develop branch: dev-<commit-sha>
- feature/* branch: <branch>-<commit-sha>

Deployment Architecture:
Gitea → GitHub Actions → Harbor → K8s (local cluster)

Required Gitea Secrets:
- HARBOR_USERNAME: Harbor registry username
- HARBOR_PASSWORD: Harbor registry password
- KUBECONFIG: Base64-encoded Kubernetes config

Documentation:
- .gitea/README.md: Gitea Actions setup and secrets configuration
- k8s/README.md: Detailed K8s deployment guide with troubleshooting
- CICD_DEPLOYMENT_GUIDE.md: Complete CI/CD workflow overview

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-15 21:31:17 +08:00
co-authored by Claude
parent 09a0bc1f56
commit e04dd12266
12 changed files with 2376 additions and 0 deletions
+348
View File
@@ -0,0 +1,348 @@
# Gitea Actions 配置指南
## 📋 概述
本项目使用Gitea Actions实现CI/CD自动化,包括测试、构建Docker镜像、推送到Harbor和部署到Kubernetes。
## 🏗️ Workflow结构
```
.gitea/workflows/
├── ci-test.yml # CI测试流水线(单元测试+代码质量检查)
└── build-and-deploy.yml # 构建和部署流水线(构建镜像+推送+K8s部署)
```
## 🔐 配置Gitea Secrets
在Gitea仓库设置中配置以下Secrets:
### 导航路径
```
仓库首页 -> Settings -> Secrets and Variables -> Actions -> New secret
```
### 必需的Secrets列表
| Secret名称 | 说明 | 示例值 | 如何获取 |
|-----------|------|--------|----------|
| `HARBOR_USERNAME` | Harbor镜像仓库用户名 | `admin` | Harbor管理员提供 |
| `HARBOR_PASSWORD` | Harbor镜像仓库密码 | `Harbor12345` | Harbor管理员提供 |
| `KUBECONFIG` | Kubernetes配置文件(base64编码) | `YXBpVmVyc2lvb...` | 见下方生成方法 |
### 生成KUBECONFIG Secret
#### 方法1: 使用现有kubeconfig(开发环境)
```bash
# 将kubeconfig文件编码为base64(单行)
cat ~/.kube/config | base64 -w 0
# Mac系统使用:
cat ~/.kube/config | base64
# 复制输出结果,粘贴到Gitea Secret的value字段
```
#### 方法2: 创建专用ServiceAccount(生产环境推荐)
```bash
# 1. 创建ServiceAccount
kubectl create serviceaccount gitea-deployer -n ljwx-health
# 2. 创建ClusterRoleBinding(授予必要权限)
kubectl create clusterrolebinding gitea-deployer-binding \
--clusterrole=cluster-admin \
--serviceaccount=ljwx-health:gitea-deployer
# 3. 获取ServiceAccount的Token(K8s 1.24+)
kubectl create token gitea-deployer -n ljwx-health --duration=87600h
# 4. 生成kubeconfig文件
cat > kubeconfig-gitea.yml <<EOF
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: $(kubectl get secret -n ljwx-health $(kubectl get sa gitea-deployer -n ljwx-health -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.ca\.crt}')
server: https://<YOUR_K8S_API_SERVER>:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: gitea-deployer
namespace: ljwx-health
name: gitea-deployer@kubernetes
current-context: gitea-deployer@kubernetes
users:
- name: gitea-deployer
user:
token: <TOKEN_FROM_STEP_3>
EOF
# 5. Base64编码
cat kubeconfig-gitea.yml | base64 -w 0
# 6. 删除临时文件(重要!)
rm kubeconfig-gitea.yml
```
#### 方法3: 从K8s集群获取(简化版)
```bash
# 适用于K8s 1.24+
kubectl -n ljwx-health create sa gitea-deployer
kubectl create clusterrolebinding gitea-deployer --clusterrole=cluster-admin --serviceaccount=ljwx-health:gitea-deployer
# 获取token并自动生成kubeconfig
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl create token gitea-deployer -n ljwx-health --duration=87600h)
kubectl config set-cluster k8s-cluster --server=$APISERVER --insecure-skip-tls-verify=true
kubectl config set-credentials gitea-deployer --token=$TOKEN
kubectl config set-context gitea-context --cluster=k8s-cluster --user=gitea-deployer --namespace=ljwx-health
kubectl config use-context gitea-context
# Base64编码
kubectl config view --flatten | base64 -w 0
```
## 🚀 Workflow触发方式
### 自动触发
| Workflow | 触发条件 | 说明 |
|----------|---------|------|
| `ci-test.yml` | Push到main/develop/feature分支<br>PR到main/develop | 自动运行测试 |
| `build-and-deploy.yml` | Push到main/develop分支 | 自动构建和部署 |
### 手动触发
1. 进入Gitea仓库页面
2. 点击顶部 **Actions** 标签
3. 选择要运行的Workflow
4. 点击 **Run workflow** 按钮
5. 选择分支和环境(如果有选项)
6. 点击确认运行
## 📊 Workflow执行流程
### CI测试流水线 (ci-test.yml)
```
┌──────────────┐
│ Push代码 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 检出代码 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 设置Java 21 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 运行单元测试 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 代码质量检查 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 上传测试报告 │
└──────────────┘
```
### 构建和部署流水线 (build-and-deploy.yml)
```
┌──────────────┐
│ Push到main │
└──────┬───────┘
│
▼
┌──────────────┐
│ 构建Docker镜像│
└──────┬───────┘
│
▼
┌──────────────┐
│ 推送到Harbor │
└──────┬───────┘
│
▼
┌──────────────┐
│ 部署到K8s │
└──────┬───────┘
│
▼
┌──────────────┐
│ 健康检查 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 部署成功通知 │
└──────────────┘
```
## 🐳 Docker镜像标签策略
| 分支 | 镜像标签 | 示例 |
|------|---------|------|
| main | `latest` + `latest-<commit-sha>` | `latest`, `latest-abc1234` |
| develop | `dev-<commit-sha>` | `dev-abc1234` |
| feature/* | `<branch>-<commit-sha>` | `feature-new-api-abc1234` |
## 📦 镜像仓库信息
| 项目 | 值 |
|------|---|
| Harbor地址 | https://harbor.omniverseai.net |
| 项目名称 | ljwx-health |
| 镜像名称 | ljwx-boot |
| 完整路径 | harbor.omniverseai.net/ljwx-health/ljwx-boot |
## 🔍 监控Workflow执行
### 查看运行状态
1. 进入仓库 -> Actions
2. 查看最近的workflow runs列表
3. 点击run查看详细日志
### 查看执行日志
```
Actions -> 选择workflow run -> 点击job名称 -> 查看step日志
```
### 下载构建产物
```
Actions -> 选择workflow run -> Artifacts区域 -> 下载
```
## 🛠️ 常见问题
### Q1: Workflow执行失败,提示"No runner available"
**原因**: Gitea Actions Runner未运行或未注册
**解决方案**:
```bash
# 检查runner状态
kubectl get pods -n gitea-runner
# 查看runner日志
kubectl logs -f <runner-pod-name> -n gitea-runner
# 重启runner
kubectl rollout restart deployment/gitea-runner -n gitea-runner
```
### Q2: 推送到Harbor失败,提示"unauthorized"
**原因**: Harbor认证失败
**解决方案**:
1. 检查Gitea Secrets中的`HARBOR_USERNAME`和`HARBOR_PASSWORD`是否正确
2. 在Harbor中验证用户权限(需要push权限)
3. 检查Harbor项目`ljwx-health`是否存在
### Q3: 部署到K8s失败,提示"Unable to connect to the server"
**原因**: KUBECONFIG配置错误或K8s集群不可达
**解决方案**:
1. 重新生成KUBECONFIG Secret(参考上方方法)
2. 确保K8s API Server可从Gitea Runner访问
3. 检查ServiceAccount权限是否足够
### Q4: 如何跳过CI/CD?
在commit message中添加 `[skip ci]` 或 `[ci skip]`:
```bash
git commit -m "docs: update README [skip ci]"
```
### Q5: 如何查看构建的Docker镜像?
访问Harbor Web UI:
```
https://harbor.omniverseai.net
-> 登录
-> ljwx-health项目
-> ljwx-boot仓库
-> 查看镜像标签列表
```
## 📝 Workflow配置说明
### 环境变量
在 `build-and-deploy.yml` 中定义:
```yaml
env:
HARBOR_REGISTRY: harbor.omniverseai.net
HARBOR_PROJECT: ljwx-health
IMAGE_NAME: ljwx-boot
IMAGE_TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || 'dev' }}-${{ github.sha }}
```
### 超时设置
| Job | 超时时间 | 说明 |
|-----|---------|------|
| unit-tests | 15分钟 | 单元测试 |
| build-and-push | 30分钟 | 构建和推送镜像 |
| deploy-to-k8s | 10分钟 | K8s部署 |
### 部署环境
| 分支 | 环境 | 说明 |
|------|-----|------|
| main | production | 生产环境 |
| develop | development | 开发环境 |
## 🔄 回滚策略
如果部署失败,workflow会自动回滚:
```yaml
- name: 回滚处理 (Rollback on Failure)
if: failure()
run: |
kubectl rollout undo deployment/ljwx-boot -n ljwx-health
```
手动回滚:
```bash
kubectl rollout undo deployment/ljwx-boot -n ljwx-health
```
## 📚 相关文档
- [Kubernetes部署指南](../k8s/README.md)
- [Gitea Actions文档](https://docs.gitea.com/usage/actions/overview)
- [Harbor用户指南](https://goharbor.io/docs/)
## 📧 支持
如有问题,请联系:
- **Email**: gaojunivas@gmail.com
- **Gitea Issue**: 在仓库中创建Issue
---
**版本**: 1.0
**更新日期**: 2025-12-15
@@ -0,0 +1,195 @@
name: 构建和部署到K8s (Build and Deploy to K8s)
on:
push:
branches:
- main
- develop
workflow_dispatch:
inputs:
environment:
description: '部署环境 (Environment)'
required: true
default: 'dev'
type: choice
options:
- dev
- staging
- prod
env:
HARBOR_REGISTRY: harbor.omniverseai.net
HARBOR_PROJECT: ljwx-health
IMAGE_NAME: ljwx-boot
# 版本号策略: main分支用latest + commit sha, develop分支用dev-sha
IMAGE_TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || 'dev' }}-${{ github.sha }}
jobs:
build-and-push:
name: 构建并推送镜像 (Build and Push Image)
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- name: 检出代码 (Checkout Code)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 登录 Harbor 镜像仓库 (Login to Harbor)
uses: docker/login-action@v3
with:
registry: ${{ env.HARBOR_REGISTRY }}
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: 提取镜像元数据 (Extract Image Metadata)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ env.IMAGE_TAG }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=sha,prefix={{branch}}-,format=short
type=ref,event=branch
- name: 构建并推送 Docker 镜像 (Build and Push Image)
uses: docker/build-push-action@v5
with:
context: ljwx-boot
file: ljwx-boot/ljwx-boot-admin/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
VERSION=${{ steps.meta.outputs.version }}
- name: 验证镜像推送 (Verify Image Push)
run: |
echo "✅ 镜像构建并推送成功!"
echo "🐳 镜像地址: ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
echo "📦 拉取命令: docker pull ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
- name: 镜像安全扫描 (Image Security Scan - Optional)
continue-on-error: true
run: |
echo "⏭️ 镜像安全扫描(可选,需要配置 Trivy 或 Harbor 内置扫描)"
# docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
# aquasec/trivy image \
# ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
deploy-to-k8s:
name: 部署到 K8s (Deploy to K8s)
runs-on: ubuntu-latest
needs: build-and-push
timeout-minutes: 10
environment:
name: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}
steps:
- name: 检出代码 (Checkout Code)
uses: actions/checkout@v4
- name: 设置 kubectl (Setup kubectl)
uses: azure/setup-kubectl@v4
with:
version: 'latest'
- name: 配置 kubeconfig (Configure kubeconfig)
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config
- name: 验证 K8s 连接 (Verify K8s Connection)
run: |
kubectl cluster-info
kubectl get nodes
- name: 创建命名空间 (Create Namespace if not exists)
run: |
NAMESPACE="ljwx-health"
if ! kubectl get namespace $NAMESPACE > /dev/null 2>&1; then
kubectl create namespace $NAMESPACE
echo "✅ 命名空间 $NAMESPACE 已创建"
else
echo "ℹ️ 命名空间 $NAMESPACE 已存在"
fi
- name: 创建 Harbor 拉取密钥 (Create Harbor Pull Secret)
run: |
kubectl create secret docker-registry harbor-secret \
--docker-server=${{ env.HARBOR_REGISTRY }} \
--docker-username=${{ secrets.HARBOR_USERNAME }} \
--docker-password=${{ secrets.HARBOR_PASSWORD }} \
--namespace=ljwx-health \
--dry-run=client -o yaml | kubectl apply -f -
- name: 更新部署镜像 (Update Deployment Image)
run: |
# 使用 kubectl set image 更新镜像
kubectl set image deployment/ljwx-boot \
ljwx-boot=${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} \
--namespace=ljwx-health \
--record
# 或者使用 kubectl apply -f k8s/deployment.yml(需要先替换镜像标签)
# sed -i "s|IMAGE_TAG_PLACEHOLDER|${{ env.IMAGE_TAG }}|g" ljwx-boot/k8s/deployment.yml
# kubectl apply -f ljwx-boot/k8s/deployment.yml
- name: 等待部署完成 (Wait for Deployment Rollout)
run: |
kubectl rollout status deployment/ljwx-boot \
--namespace=ljwx-health \
--timeout=300s
- name: 验证部署状态 (Verify Deployment Status)
run: |
echo "📊 部署状态:"
kubectl get deployment ljwx-boot -n ljwx-health
echo ""
echo "📦 Pod 状态:"
kubectl get pods -n ljwx-health -l app=ljwx-boot
echo ""
echo "🔗 服务状态:"
kubectl get svc ljwx-boot -n ljwx-health
- name: 健康检查 (Health Check)
run: |
echo "等待应用健康检查..."
sleep 30
# 获取服务端点
SERVICE_IP=$(kubectl get svc ljwx-boot -n ljwx-health -o jsonpath='{.spec.clusterIP}')
# 从集群内部检查健康状态
kubectl run healthcheck --image=curlimages/curl:latest --rm -i --restart=Never -- \
curl -f http://$SERVICE_IP:9998/actuator/health || echo "⚠️ 健康检查失败,请手动验证"
- name: 部署成功通知 (Deployment Success Notification)
if: success()
run: |
echo "🎉 部署成功!"
echo "环境: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Development' }}"
echo "镜像: ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
echo "命名空间: ljwx-health"
- name: 回滚处理 (Rollback on Failure)
if: failure()
run: |
echo "❌ 部署失败,执行回滚..."
kubectl rollout undo deployment/ljwx-boot -n ljwx-health
kubectl rollout status deployment/ljwx-boot -n ljwx-health --timeout=180s
echo "⚠️ 已回滚到上一个版本,请检查错误日志"
+82
View File
@@ -0,0 +1,82 @@
name: CI 测试流水线 (CI Test Pipeline)
on:
push:
branches:
- main
- develop
- 'feature/**'
paths:
- 'ljwx-boot-modules/**'
- 'ljwx-boot-common/**'
- 'pom.xml'
- '**/pom.xml'
pull_request:
branches:
- main
- develop
jobs:
unit-tests:
name: 单元测试 (Unit Tests)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: 检出代码 (Checkout Code)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 设置 Java 21 (Setup Java)
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
- name: 运行单元测试 (Run Unit Tests)
run: |
cd ljwx-boot
mvn clean test -B -V \
-Dmaven.test.failure.ignore=false \
-Dsurefire.useFile=true
env:
MAVEN_OPTS: "-Xmx2048m -Xms512m"
- name: 生成测试报告 (Generate Test Report)
if: always()
run: |
cd ljwx-boot
./run-tests.sh coverage
- name: 上传测试报告 (Upload Test Reports)
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-${{ github.run_number }}
path: |
ljwx-boot/ljwx-boot-modules/target/surefire-reports/
ljwx-boot/ljwx-boot-modules/target/site/jacoco/
retention-days: 30
code-quality:
name: 代码质量检查 (Code Quality)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 检出代码 (Checkout Code)
uses: actions/checkout@v4
- name: 设置 Java 21 (Setup Java)
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
- name: 编译检查 (Compile Check)
run: |
cd ljwx-boot
mvn clean compile test-compile -B -V
+561
View File
@@ -0,0 +1,561 @@
# LJWX Boot CI/CD 和 K8s 部署完整指南
## 🎯 架构概览
```
┌─────────────────────────────────────────────────────────────────────┐
│ 开发流程 │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Gitea 代码仓库 │
│ http://192.168.1.83:33000/gao/ljwx-health │
└─────────────────────────────────────────────────────────────────────┘
│
Push代码 / 创建PR
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Gitea Actions Workflow │
│ .gitea/workflows/ │
│ ├── ci-test.yml # 测试流水线 │
│ └── build-and-deploy.yml # 构建部署流水线 │
└─────────────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 测试阶段 │ │ 构建阶段 │ │ 部署阶段 │
└──────────┘ └──────────┘ └──────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Harbor 镜像仓库 │
│ harbor.omniverseai.net/ljwx-health/ljwx-boot │
│ (本地私有仓库) │
└─────────────────────────────────────────────────────────────────────┘
│
K8s拉取镜像
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Kubernetes 集群 (本地) │
│ │
│ Namespace: ljwx-health │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Deployment: ljwx-boot (2 replicas) │ │
│ │ ├── Pod: ljwx-boot-xxxx-1 │ │
│ │ └── Pod: ljwx-boot-xxxx-2 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Service: ljwx-boot (ClusterIP: 9998) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Ingress: ljwx-boot-ingress (Nginx) │ │
│ │ Host: api.ljwx.local │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────┐
│ 最终用户 │
└───────────────┘
```
## 🚀 快速开始(5分钟部署)
### 前提条件检查
```bash
# 1. 检查kubectl连接
kubectl cluster-info
# 2. 检查Gitea Actions Runner
kubectl get pods -n gitea-runner # 确保runner正在运行
# 3. 检查Harbor可访问性
curl -I https://harbor.omniverseai.net
# 4. 验证kubeconfig
kubectl get nodes
```
### 一键部署步骤
#### Step 1: 配置Gitea Secrets
访问: `http://192.168.1.83:33000/gao/ljwx-health/settings/secrets`
添加以下3个Secrets:
| Secret名称 | 值 | 获取方式 |
|-----------|---|----------|
| `HARBOR_USERNAME` | Harbor用户名 | Harbor管理员提供 |
| `HARBOR_PASSWORD` | Harbor密码 | Harbor管理员提供 |
| `KUBECONFIG` | base64编码的kubeconfig | `cat ~/.kube/config \| base64 -w 0` |
#### Step 2: 推送代码触发部署
```bash
# 已完成(刚才推送了两个commit)
git push origin main
```
#### Step 3: 监控部署进度
访问: `http://192.168.1.83:33000/gao/ljwx-health/actions`
查看workflow执行状态:
- ✅ CI测试 (ci-test.yml)
- ✅ 构建和部署 (build-and-deploy.yml)
#### Step 4: 验证部署成功
```bash
# 检查Pod状态
kubectl get pods -n ljwx-health -l app=ljwx-boot
# 检查服务
kubectl get svc ljwx-boot -n ljwx-health
# 健康检查
kubectl port-forward svc/ljwx-boot 9998:9998 -n ljwx-health &
curl http://localhost:9998/actuator/health
# 预期输出: {"status":"UP"}
```
## 📁 项目文件结构
```
ljwx-boot/
├── .gitea/
│ ├── workflows/
│ │ ├── ci-test.yml # CI测试流水线
│ │ └── build-and-deploy.yml # 构建部署流水线
│ └── README.md # Gitea Actions配置指南
│
├── k8s/
│ ├── deployment.yml # K8s部署配置
│ ├── service.yml # K8s服务配置
│ ├── configmap.yml # 配置映射
│ ├── secret.yml.template # 密钥模板(不提交)
│ ├── ingress.yml # Ingress配置
│ ├── deploy.sh # 快速部署脚本
│ ├── .gitignore # 忽略secret.yml
│ └── README.md # K8s部署详细指南
│
├── ljwx-boot-admin/
│ └── Dockerfile # Docker多阶段构建文件
│
└── CICD_DEPLOYMENT_GUIDE.md # 本文档
```
## 🔧 配置详解
### 1. Gitea Workflow配置
#### ci-test.yml - CI测试流水线
**触发条件**:
- Push到 `main`, `develop`, `feature/**` 分支
- PR到 `main`, `develop` 分支
**执行步骤**:
1. 检出代码
2. 设置Java 21环境
3. 运行单元测试
4. 代码质量检查
5. 上传测试报告
**超时**: 15分钟
#### build-and-deploy.yml - 构建部署流水线
**触发条件**:
- Push到 `main`, `develop` 分支
- 手动触发 (workflow_dispatch)
**执行步骤**:
**Job 1: build-and-push**
1. 检出代码
2. 设置Docker Buildx
3. 登录Harbor镜像仓库
4. 构建Docker镜像(多阶段构建)
5. 推送镜像到Harbor
6. 镜像标签策略:
- `main` 分支: `latest` + `latest-<commit-sha>`
- `develop` 分支: `dev-<commit-sha>`
**Job 2: deploy-to-k8s** (依赖Job 1)
1. 配置kubectl和kubeconfig
2. 验证K8s集群连接
3. 创建命名空间(如不存在)
4. 创建Harbor拉取密钥
5. 更新Deployment镜像
6. 等待部署完成
7. 健康检查
8. 失败时自动回滚
### 2. Kubernetes配置
#### Deployment配置亮点
```yaml
spec:
replicas: 2 # 高可用:2个副本
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 滚动更新策略
maxUnavailable: 0 # 零停机部署
resources:
requests:
cpu: "500m" # 请求0.5核
memory: "1Gi"
limits:
cpu: "2000m" # 限制2核
memory: "2Gi"
# 三种健康检查
livenessProbe: ... # 存活探针
readinessProbe: ... # 就绪探针
startupProbe: ... # 启动探针(防止慢启动被杀)
```
#### Service配置
```yaml
type: ClusterIP # 集群内部访问
sessionAffinity: ClientIP # 会话亲和性
```
#### Ingress配置(可选)
```yaml
host: api.ljwx.local # 域名
ingressClassName: nginx # 使用Nginx Ingress
```
### 3. Harbor镜像仓库
**地址**: https://harbor.omniverseai.net
**项目**: ljwx-health
**镜像**: ljwx-boot
**完整镜像路径**:
```
harbor.omniverseai.net/ljwx-health/ljwx-boot:latest
harbor.omniverseai.net/ljwx-health/ljwx-boot:latest-abc1234
harbor.omniverseai.net/ljwx-health/ljwx-boot:dev-abc1234
```
## 🛠️ 手动部署(备用方案)
如果Gitea Actions不可用,可手动部署:
### 方式一: 使用快速部署脚本
```bash
cd ljwx-boot/k8s
# 1. 创建secret.yml
cp secret.yml.template secret.yml
vim secret.yml # 填充真实值
# 2. 一键部署
./deploy.sh
# 3. 验证
./deploy.sh verify
```
### 方式二: 逐步手动部署
```bash
cd ljwx-boot/k8s
# 1. 创建命名空间
kubectl create namespace ljwx-health
# 2. 创建Harbor密钥
kubectl create secret docker-registry harbor-secret \
--docker-server=harbor.omniverseai.net \
--docker-username=<username> \
--docker-password=<password> \
--namespace=ljwx-health
# 3. 创建应用密钥
kubectl apply -f secret.yml
# 4. 创建ConfigMap
kubectl apply -f configmap.yml
# 5. 部署应用
kubectl apply -f deployment.yml
kubectl apply -f service.yml
# 6. 部署Ingress(可选)
kubectl apply -f ingress.yml
# 7. 等待部署完成
kubectl rollout status deployment/ljwx-boot -n ljwx-health
```
## 🔍 监控和调试
### 查看Pod日志
```bash
# 实时日志
kubectl logs -f deployment/ljwx-boot -n ljwx-health
# 查看所有Pod日志
kubectl logs -l app=ljwx-boot -n ljwx-health --all-containers=true
# 查看前一个容器日志(Pod重启后)
kubectl logs <pod-name> -n ljwx-health --previous
```
### 查看事件
```bash
# 查看命名空间事件
kubectl get events -n ljwx-health --sort-by='.lastTimestamp'
# 查看特定Pod事件
kubectl describe pod <pod-name> -n ljwx-health
```
### 端口转发(本地访问)
```bash
# 转发服务端口
kubectl port-forward svc/ljwx-boot 9998:9998 -n ljwx-health
# 访问应用
curl http://localhost:9998/actuator/health
curl http://localhost:9998/doc.html # Swagger文档
```
### 进入Pod调试
```bash
# 进入Pod Shell
kubectl exec -it <pod-name> -n ljwx-health -- sh
# 在Pod内部测试
wget -O- http://localhost:9998/actuator/health
```
### 查看资源使用
```bash
# Pod资源使用
kubectl top pods -n ljwx-health
# 节点资源使用
kubectl top nodes
```
## 🔄 常见运维操作
### 更新应用版本
```bash
# 方式1: 推送代码(自动触发)
git push origin main
# 方式2: 手动更新镜像
kubectl set image deployment/ljwx-boot \
ljwx-boot=harbor.omniverseai.net/ljwx-health/ljwx-boot:v1.2.0 \
-n ljwx-health
# 查看更新进度
kubectl rollout status deployment/ljwx-boot -n ljwx-health
```
### 回滚版本
```bash
# 查看历史版本
kubectl rollout history deployment/ljwx-boot -n ljwx-health
# 回滚到上一版本
kubectl rollout undo deployment/ljwx-boot -n ljwx-health
# 回滚到指定版本
kubectl rollout undo deployment/ljwx-boot -n ljwx-health --to-revision=2
```
### 扩缩容
```bash
# 扩展到3个副本
kubectl scale deployment ljwx-boot --replicas=3 -n ljwx-health
# 缩减到1个副本
kubectl scale deployment ljwx-boot --replicas=1 -n ljwx-health
```
### 更新配置
```bash
# 更新ConfigMap
kubectl edit configmap ljwx-boot-config -n ljwx-health
# 更新Secret
kubectl edit secret ljwx-boot-secret -n ljwx-health
# 重启Pod使配置生效
kubectl rollout restart deployment/ljwx-boot -n ljwx-health
```
### 清理资源
```bash
# 删除应用
cd ljwx-boot/k8s
./deploy.sh clean
# 或手动删除
kubectl delete deployment ljwx-boot -n ljwx-health
kubectl delete svc ljwx-boot -n ljwx-health
kubectl delete ingress ljwx-boot-ingress -n ljwx-health
# 删除命名空间(慎用!)
kubectl delete namespace ljwx-health
```
## ❓ 常见问题排查
### 1. Gitea Actions Runner无法执行任务
**症状**: Workflow一直显示"Waiting for runner"
**排查步骤**:
```bash
# 检查runner Pod状态
kubectl get pods -n gitea-runner
# 查看runner日志
kubectl logs -f <runner-pod> -n gitea-runner
# 重启runner
kubectl rollout restart deployment/gitea-runner -n gitea-runner
```
### 2. 镜像拉取失败 (ImagePullBackOff)
**症状**: Pod状态为ImagePullBackOff
**排查步骤**:
```bash
# 查看Pod详情
kubectl describe pod <pod-name> -n ljwx-health
# 常见原因:
# 1. Harbor密钥错误
kubectl get secret harbor-secret -n ljwx-health -o yaml
# 2. 镜像不存在
# 访问Harbor UI检查: https://harbor.omniverseai.net
# 3. 网络不通
kubectl run test --image=busybox --rm -it --restart=Never -- \
wget -O- https://harbor.omniverseai.net
```
### 3. Pod崩溃循环 (CrashLoopBackOff)
**症状**: Pod反复重启
**排查步骤**:
```bash
# 查看Pod日志
kubectl logs <pod-name> -n ljwx-health --previous
# 查看Pod事件
kubectl describe pod <pod-name> -n ljwx-health
# 常见原因:
# 1. 数据库连接失败
# 检查ConfigMap中的database.url
# 2. JVM内存不足
# 调整deployment.yml中的resources.limits.memory
# 3. 配置文件错误
# 检查ConfigMap和Secret
```
### 4. 服务无法访问
**症状**: 通过Ingress或Service访问失败
**排查步骤**:
```bash
# 1. 检查Service
kubectl get svc ljwx-boot -n ljwx-health
kubectl get endpoints ljwx-boot -n ljwx-health
# 2. 检查Pod健康检查
kubectl get pods -n ljwx-health -o wide
# 3. 端口转发测试
kubectl port-forward svc/ljwx-boot 9998:9998 -n ljwx-health
curl http://localhost:9998/actuator/health
# 4. 检查Ingress(如果使用)
kubectl get ingress -n ljwx-health
kubectl describe ingress ljwx-boot-ingress -n ljwx-health
```
## 📚 参考文档
| 文档 | 路径 | 说明 |
|------|-----|------|
| Gitea Actions配置 | `.gitea/README.md` | Secrets配置、Workflow说明 |
| K8s部署详细指南 | `k8s/README.md` | 手动部署、验证、问题排查 |
| 快速测试指南 | `TESTING_QUICK_START.md` | 测试执行、报告查看 |
| 内网消息总线 | `docs/intranet-message-bus/` | 系统架构文档 |
## 🎯 最佳实践
### 开发流程
1. **功能开发**: 在 `feature/*` 分支开发
2. **本地测试**: `./run-tests.sh full`
3. **提交代码**: Push到feature分支
4. **创建PR**: 发起PR到develop分支
5. **CI验证**: Gitea Actions自动运行测试
6. **合并代码**: PR合并后自动部署到dev环境
7. **发布生产**: develop合并到main后自动部署到生产环境
### 版本管理
- **develop分支**: 开发环境(自动部署)
- **main分支**: 生产环境(自动部署)
- **标签管理**: 重要版本打tag(`v1.0.0`, `v1.1.0`等)
### 监控建议
- 配置Prometheus + Grafana监控K8s集群
- 使用EFK(Elasticsearch + Fluentd + Kibana)收集日志
- 配置告警规则(Pod重启、资源使用率等)
## 📧 支持
如有问题,请联系:
- **Email**: gaojunivas@gmail.com
- **Gitea**: 在仓库中提交Issue
- **文档**: 查看项目docs目录
---
**版本**: 1.0
**更新日期**: 2025-12-15
**维护者**: LJWX Health Team
+9
View File
@@ -0,0 +1,9 @@
# Kubernetes Secrets(不要提交到Git)
secret.yml
# Backup files
*.backup
*.bak
# Temporary kubectl files
*.tmp
+485
View File
@@ -0,0 +1,485 @@
# LJWX Boot Kubernetes 部署指南
## 📋 目录
1. [环境准备](#环境准备)
2. [配置Gitea Secrets](#配置gitea-secrets)
3. [部署步骤](#部署步骤)
4. [验证部署](#验证部署)
5. [常见问题](#常见问题)
6. [维护操作](#维护操作)
---
## 环境准备
### 前置条件
- ✅ Kubernetes 集群 (v1.24+)
- ✅ kubectl 命令行工具
- ✅ Gitea + Gitea Actions Runner (运行在K8s集群中)
- ✅ Harbor 私有镜像仓库 (https://harbor.omniverseai.net)
- ✅ Nginx Ingress Controller (可选)
- ✅ MySQL 数据库 (v8.0+)
- ✅ Redis 服务 (v6.0+)
### 资源要求
| 组件 | CPU请求 | CPU限制 | 内存请求 | 内存限制 | 副本数 |
|------|---------|---------|----------|----------|--------|
| ljwx-boot | 500m | 2核 | 1Gi | 2Gi | 2 |
---
## 配置Gitea Secrets
在Gitea仓库设置中配置以下Secrets(Settings -> Secrets and Variables -> Actions):
### 必需的Secrets
```bash
# Harbor镜像仓库认证
HARBOR_USERNAME=<your-harbor-username>
HARBOR_PASSWORD=<your-harbor-password>
# Kubernetes配置(base64编码的kubeconfig文件)
KUBECONFIG=<base64-encoded-kubeconfig>
```
### 生成KUBECONFIG Secret
```bash
# 方法1: 使用当前kubectl配置
cat ~/.kube/config | base64 -w 0
# 方法2: 创建专用ServiceAccount(推荐)
kubectl create serviceaccount gitea-deployer -n ljwx-health
kubectl create clusterrolebinding gitea-deployer \
--clusterrole=cluster-admin \
--serviceaccount=ljwx-health:gitea-deployer
# 获取token并生成kubeconfig
# 参考: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
```
---
## 部署步骤
### 方式一: 通过Gitea Workflow自动部署(推荐)
#### 1. 推送代码触发自动部署
```bash
git push origin main # 推送到main分支自动触发构建和部署
```
#### 2. 手动触发部署
在Gitea UI中:
1. 进入仓库 -> Actions
2. 选择 "Build and Deploy to K8s" workflow
3. 点击 "Run workflow"
4. 选择环境(dev/staging/prod)
5. 点击确认运行
**Workflow执行流程**:
```
Push代码 → 运行测试 → 构建Docker镜像 → 推送到Harbor → 部署到K8s → 健康检查
```
---
### 方式二: 手动部署到K8s
如果需要手动部署(不通过Gitea workflow),按以下步骤操作:
#### 1. 创建命名空间
```bash
kubectl create namespace ljwx-health
```
#### 2. 配置Secrets
```bash
# 复制模板并填充真实值
cp k8s/secret.yml.template k8s/secret.yml
# 编辑secret.yml,替换所有<BASE64_ENCODED_*>占位符
vim k8s/secret.yml
# 应用Secret配置
kubectl apply -f k8s/secret.yml
```
#### 3. 创建Harbor拉取密钥
```bash
kubectl create secret docker-registry harbor-secret \
--docker-server=harbor.omniverseai.net \
--docker-username=<your-username> \
--docker-password=<your-password> \
--namespace=ljwx-health
```
#### 4. 部署ConfigMap
```bash
kubectl apply -f k8s/configmap.yml
```
#### 5. 部署应用
```bash
# 部署Deployment
kubectl apply -f k8s/deployment.yml
# 部署Service
kubectl apply -f k8s/service.yml
# 部署Ingress(可选)
kubectl apply -f k8s/ingress.yml
```
#### 6. 验证部署
```bash
# 查看Pod状态
kubectl get pods -n ljwx-health -l app=ljwx-boot
# 查看部署状态
kubectl get deployment ljwx-boot -n ljwx-health
# 查看服务
kubectl get svc ljwx-boot -n ljwx-health
# 查看Ingress
kubectl get ingress -n ljwx-health
```
---
## 验证部署
### 1. 检查Pod状态
```bash
# 查看Pod运行状态
kubectl get pods -n ljwx-health -l app=ljwx-boot
# 预期输出:
# NAME READY STATUS RESTARTS AGE
# ljwx-boot-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
# ljwx-boot-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
```
### 2. 查看Pod日志
```bash
# 查看最新日志
kubectl logs -f deployment/ljwx-boot -n ljwx-health
# 查看特定Pod日志
kubectl logs -f <pod-name> -n ljwx-health
# 查看前一个容器日志(如果Pod重启)
kubectl logs <pod-name> -n ljwx-health --previous
```
### 3. 健康检查
```bash
# 方式1: 通过kubectl端口转发
kubectl port-forward svc/ljwx-boot 9998:9998 -n ljwx-health
curl http://localhost:9998/actuator/health
# 方式2: 从集群内部检查
kubectl run curl-test --image=curlimages/curl:latest --rm -i --restart=Never -- \
curl -f http://ljwx-boot.ljwx-health.svc.cluster.local:9998/actuator/health
# 预期输出:
# {"status":"UP"}
```
### 4. 访问应用
```bash
# 方式1: 通过Ingress(如果已配置)
curl http://api.ljwx.local/actuator/health
# 方式2: 通过NodePort(如果Service类型为NodePort)
curl http://<node-ip>:30998/actuator/health
# 方式3: 通过kubectl代理
kubectl proxy &
curl http://localhost:8001/api/v1/namespaces/ljwx-health/services/ljwx-boot:9998/proxy/actuator/health
```
---
## 常见问题
### Q1: Pod一直处于ImagePullBackOff状态
**原因**: 无法从Harbor拉取镜像
**解决方案**:
```bash
# 检查Secret是否正确创建
kubectl get secret harbor-secret -n ljwx-health
# 重新创建Harbor密钥
kubectl delete secret harbor-secret -n ljwx-health
kubectl create secret docker-registry harbor-secret \
--docker-server=harbor.omniverseai.net \
--docker-username=<username> \
--docker-password=<password> \
--namespace=ljwx-health
# 重启Pod
kubectl rollout restart deployment/ljwx-boot -n ljwx-health
```
### Q2: Pod一直处于CrashLoopBackOff状态
**原因**: 应用启动失败
**解决方案**:
```bash
# 查看Pod日志
kubectl logs <pod-name> -n ljwx-health
# 查看Pod详细信息
kubectl describe pod <pod-name> -n ljwx-health
# 常见原因:
# - 数据库连接失败(检查database.url配置)
# - Redis连接失败(检查redis.host配置)
# - ConfigMap或Secret缺失
# - JVM内存不足(调整resources.requests.memory)
```
### Q3: 如何查看Gitea workflow执行日志?
在Gitea UI中:
1. 进入仓库 -> Actions
2. 找到对应的workflow运行记录
3. 点击查看详细日志
### Q4: 数据库连接失败
**检查清单**:
```bash
# 1. 检查ConfigMap中的数据库配置
kubectl get configmap ljwx-boot-config -n ljwx-health -o yaml
# 2. 检查Secret中的数据库密码
kubectl get secret ljwx-boot-secret -n ljwx-health -o jsonpath='{.data.database\.password}' | base64 -d
# 3. 测试数据库连接(从Pod内部)
kubectl exec -it <pod-name> -n ljwx-health -- sh
# 在Pod内部测试连接...
# 4. 检查MySQL Service是否可访问
kubectl get svc -n ljwx-health | grep mysql
```
### Q5: 如何回滚到上一个版本?
```bash
# 查看部署历史
kubectl rollout history deployment/ljwx-boot -n ljwx-health
# 回滚到上一个版本
kubectl rollout undo deployment/ljwx-boot -n ljwx-health
# 回滚到指定版本
kubectl rollout undo deployment/ljwx-boot -n ljwx-health --to-revision=2
# 查看回滚状态
kubectl rollout status deployment/ljwx-boot -n ljwx-health
```
---
## 维护操作
### 更新应用版本
```bash
# 方式1: 通过Gitea workflow(推荐)
# 推送代码到main分支,workflow会自动构建和部署
# 方式2: 手动更新镜像
kubectl set image deployment/ljwx-boot \
ljwx-boot=harbor.omniverseai.net/ljwx-health/ljwx-boot:v1.2.0 \
-n ljwx-health
# 查看更新状态
kubectl rollout status deployment/ljwx-boot -n ljwx-health
```
### 扩缩容
```bash
# 扩展到3个副本
kubectl scale deployment ljwx-boot --replicas=3 -n ljwx-health
# 查看扩容状态
kubectl get pods -n ljwx-health -l app=ljwx-boot -w
```
### 更新ConfigMap
```bash
# 修改ConfigMap
kubectl edit configmap ljwx-boot-config -n ljwx-health
# 或者重新应用配置文件
kubectl apply -f k8s/configmap.yml
# 重启Pod使配置生效
kubectl rollout restart deployment/ljwx-boot -n ljwx-health
```
### 更新Secret
```bash
# 修改Secret
kubectl edit secret ljwx-boot-secret -n ljwx-health
# 或者删除后重新创建
kubectl delete secret ljwx-boot-secret -n ljwx-health
kubectl apply -f k8s/secret.yml
# 重启Pod使配置生效
kubectl rollout restart deployment/ljwx-boot -n ljwx-health
```
### 查看资源使用情况
```bash
# 查看Pod资源使用
kubectl top pods -n ljwx-health -l app=ljwx-boot
# 查看节点资源使用
kubectl top nodes
# 查看Pod详细资源配置
kubectl describe pod <pod-name> -n ljwx-health | grep -A 10 "Requests\|Limits"
```
### 导出配置
```bash
# 导出当前部署配置
kubectl get deployment ljwx-boot -n ljwx-health -o yaml > deployment-backup.yml
# 导出所有资源
kubectl get all -n ljwx-health -o yaml > ljwx-health-backup.yml
```
### 清理资源
```bash
# 删除整个命名空间(慎用!)
kubectl delete namespace ljwx-health
# 仅删除应用
kubectl delete deployment ljwx-boot -n ljwx-health
kubectl delete svc ljwx-boot -n ljwx-health
kubectl delete ingress ljwx-boot-ingress -n ljwx-health
```
---
## 监控和告警
### Prometheus监控(如果已配置)
应用已暴露Prometheus metrics端点:
```
http://ljwx-boot:9998/actuator/prometheus
```
ServiceMonitor配置示例:
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: ljwx-boot
namespace: ljwx-health
spec:
selector:
matchLabels:
app: ljwx-boot
endpoints:
- port: http
path: /actuator/prometheus
interval: 30s
```
### 日志收集(建议配置)
使用EFK(Elasticsearch + Fluentd + Kibana)或Loki收集日志:
```bash
# 安装Fluentd DaemonSet收集所有Pod日志
# 或使用Loki + Promtail
```
---
## 架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ Gitea Workflow │
│ (Push to main) → Test → Build Image → Push to Harbor → Deploy │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Harbor Registry │
│ harbor.omniverseai.net/ljwx-health/ljwx-boot │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ ljwx-boot Pod1 │ │ ljwx-boot Pod2 │ │
│ │ (Replica 1) │ │ (Replica 2) │ │
│ └────────────────┘ └────────────────┘ │
│ │ │ │
│ └──────────┬───────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Service (9998) │ │
│ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Ingress (Nginx) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
External Users
```
---
## 联系支持
如有问题,请联系:
- **Email**: gaojunivas@gmail.com
- **Gitea**: 在仓库中提交Issue
- **文档**: 查看项目文档 `ljwx-boot/docs/`
---
**版本**: 1.0
**更新日期**: 2025-12-15
**维护者**: LJWX Health Team
+80
View File
@@ -0,0 +1,80 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ljwx-boot-config
namespace: ljwx-health
labels:
app: ljwx-boot
data:
# 数据库配置
database.url: "jdbc:mysql://mysql-service.ljwx-health.svc.cluster.local:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false"
database.driver: "com.mysql.cj.jdbc.Driver"
# Redis配置
redis.host: "redis-service.ljwx-health.svc.cluster.local"
redis.port: "6379"
# 应用配置
application-prod.yml: |
server:
port: 9998
tomcat:
threads:
max: 200
min-spare: 20
max-connections: 10000
accept-count: 100
spring:
application:
name: ljwx-boot
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 10
maximum-pool-size: 30
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
redis:
database: 1
timeout: 10000
lettuce:
pool:
max-active: 200
max-idle: 50
min-idle: 10
max-wait: 10000
# Actuator配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
base-path: /actuator
endpoint:
health:
show-details: when-authorized
probes:
enabled: true
metrics:
export:
prometheus:
enabled: true
# 日志配置
logging:
level:
root: INFO
com.ljwx: INFO
com.ljwx.modules.health: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file:
name: /app/logs/ljwx-boot.log
max-size: 100MB
max-history: 30
+271
View File
@@ -0,0 +1,271 @@
#!/bin/bash
# ============================================================================
# LJWX Boot Kubernetes 快速部署脚本
#
# 用法:
# ./deploy.sh # 部署所有资源
# ./deploy.sh configmap # 仅部署ConfigMap
# ./deploy.sh secret # 仅部署Secret
# ./deploy.sh app # 仅部署应用(Deployment + Service)
# ./deploy.sh ingress # 仅部署Ingress
# ./deploy.sh clean # 清理所有资源
# ============================================================================
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 配置
NAMESPACE="ljwx-health"
APP_NAME="ljwx-boot"
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查kubectl
check_kubectl() {
if ! command -v kubectl &> /dev/null; then
log_error "kubectl 未安装,请先安装 kubectl"
exit 1
fi
log_info "kubectl 版本: $(kubectl version --client --short 2>/dev/null || kubectl version --client)"
}
# 检查集群连接
check_cluster() {
log_info "检查Kubernetes集群连接..."
if ! kubectl cluster-info &> /dev/null; then
log_error "无法连接到Kubernetes集群,请检查kubeconfig配置"
exit 1
fi
log_info "✅ 集群连接正常"
}
# 创建命名空间
create_namespace() {
log_info "创建命名空间: $NAMESPACE"
if kubectl get namespace $NAMESPACE &> /dev/null; then
log_warn "命名空间 $NAMESPACE 已存在"
else
kubectl create namespace $NAMESPACE
log_info "✅ 命名空间已创建"
fi
}
# 部署ConfigMap
deploy_configmap() {
log_info "部署 ConfigMap..."
kubectl apply -f configmap.yml
log_info "✅ ConfigMap 部署完成"
}
# 部署Secret
deploy_secret() {
log_info "部署 Secret..."
if [ ! -f "secret.yml" ]; then
log_error "secret.yml 文件不存在!"
log_info "请复制 secret.yml.template 并填充真实值:"
log_info " cp secret.yml.template secret.yml"
log_info " vim secret.yml # 替换所有 <BASE64_ENCODED_*> 占位符"
exit 1
fi
# 检查是否还有占位符
if grep -q "<BASE64_ENCODED_" secret.yml; then
log_error "secret.yml 中仍包含占位符,请先填充真实值"
exit 1
fi
kubectl apply -f secret.yml
log_info "✅ Secret 部署完成"
}
# 部署应用
deploy_app() {
log_info "部署 Deployment 和 Service..."
kubectl apply -f deployment.yml
kubectl apply -f service.yml
log_info "✅ 应用部署完成"
# 等待部署完成
log_info "等待 Deployment 就绪..."
kubectl rollout status deployment/$APP_NAME -n $NAMESPACE --timeout=300s
log_info "✅ Deployment 已就绪"
}
# 部署Ingress
deploy_ingress() {
log_info "部署 Ingress..."
kubectl apply -f ingress.yml
log_info "✅ Ingress 部署完成"
}
# 创建Harbor密钥
create_harbor_secret() {
log_info "创建 Harbor 拉取密钥..."
if kubectl get secret harbor-secret -n $NAMESPACE &> /dev/null; then
log_warn "Harbor secret 已存在,跳过创建"
return
fi
log_warn "请提供Harbor认证信息:"
read -p "Harbor用户名: " HARBOR_USER
read -s -p "Harbor密码: " HARBOR_PASS
echo ""
kubectl create secret docker-registry harbor-secret \
--docker-server=harbor.omniverseai.net \
--docker-username=$HARBOR_USER \
--docker-password=$HARBOR_PASS \
--namespace=$NAMESPACE
log_info "✅ Harbor secret 已创建"
}
# 验证部署
verify_deployment() {
log_info "验证部署状态..."
echo ""
echo "=== Deployment 状态 ==="
kubectl get deployment $APP_NAME -n $NAMESPACE
echo ""
echo "=== Pod 状态 ==="
kubectl get pods -n $NAMESPACE -l app=$APP_NAME
echo ""
echo "=== Service 状态 ==="
kubectl get svc $APP_NAME -n $NAMESPACE
echo ""
echo "=== Ingress 状态 ==="
kubectl get ingress -n $NAMESPACE
echo ""
# 健康检查
log_info "执行健康检查..."
SERVICE_IP=$(kubectl get svc $APP_NAME -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
if [ -n "$SERVICE_IP" ]; then
log_info "服务ClusterIP: $SERVICE_IP"
log_info "健康检查端点: http://$SERVICE_IP:9998/actuator/health"
# 尝试从集群内部检查(如果有curl pod)
if kubectl run healthcheck --image=curlimages/curl:latest --rm -i --restart=Never -n $NAMESPACE -- \
curl -f -s http://$SERVICE_IP:9998/actuator/health &> /dev/null; then
log_info "✅ 健康检查通过"
else
log_warn "⚠️ 健康检查失败或超时,请手动验证"
fi
fi
}
# 清理资源
clean_resources() {
log_warn "⚠️ 即将删除所有 $APP_NAME 相关资源"
read -p "确认删除? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
log_info "取消删除"
exit 0
fi
log_info "删除资源..."
kubectl delete -f ingress.yml --ignore-not-found=true
kubectl delete -f service.yml --ignore-not-found=true
kubectl delete -f deployment.yml --ignore-not-found=true
kubectl delete -f configmap.yml --ignore-not-found=true
kubectl delete secret $APP_NAME-secret -n $NAMESPACE --ignore-not-found=true
kubectl delete secret harbor-secret -n $NAMESPACE --ignore-not-found=true
log_info "✅ 资源已清理"
read -p "是否删除命名空间 $NAMESPACE? (yes/no): " DELETE_NS
if [ "$DELETE_NS" == "yes" ]; then
kubectl delete namespace $NAMESPACE
log_info "✅ 命名空间已删除"
fi
}
# 完整部署
deploy_all() {
check_kubectl
check_cluster
create_namespace
create_harbor_secret
deploy_configmap
deploy_secret
deploy_app
deploy_ingress
verify_deployment
echo ""
log_info "🎉 部署完成!"
echo ""
log_info "快速访问命令:"
echo " # 查看Pod日志"
echo " kubectl logs -f deployment/$APP_NAME -n $NAMESPACE"
echo ""
echo " # 端口转发"
echo " kubectl port-forward svc/$APP_NAME 9998:9998 -n $NAMESPACE"
echo ""
echo " # 查看详细信息"
echo " kubectl describe deployment $APP_NAME -n $NAMESPACE"
}
# 主函数
main() {
cd "$(dirname "$0")"
case "${1:-all}" in
configmap)
check_kubectl
check_cluster
create_namespace
deploy_configmap
;;
secret)
check_kubectl
check_cluster
create_namespace
deploy_secret
;;
app)
check_kubectl
check_cluster
create_namespace
deploy_app
;;
ingress)
check_kubectl
check_cluster
deploy_ingress
;;
verify)
check_kubectl
verify_deployment
;;
clean)
check_kubectl
clean_resources
;;
all|*)
deploy_all
;;
esac
}
main "$@"
+172
View File
@@ -0,0 +1,172 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ljwx-boot
namespace: ljwx-health
labels:
app: ljwx-boot
version: v1
component: backend
spec:
replicas: 2 # 生产环境建议至少2个副本实现高可用
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 滚动更新时最多新增1个Pod
maxUnavailable: 0 # 滚动更新时最多不可用0个Pod(保证服务不中断)
selector:
matchLabels:
app: ljwx-boot
template:
metadata:
labels:
app: ljwx-boot
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9998"
prometheus.io/path: "/actuator/prometheus"
spec:
imagePullSecrets:
- name: harbor-secret # Harbor 拉取密钥
# 亲和性配置(可选):尽量分散到不同节点
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- ljwx-boot
topologyKey: kubernetes.io/hostname
containers:
- name: ljwx-boot
image: harbor.omniverseai.net/ljwx-health/ljwx-boot:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 9998
protocol: TCP
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod" # 生产环境配置
- name: TZ
value: "Asia/Shanghai"
# 数据库配置(从ConfigMap/Secret读取)
- name: SPRING_DATASOURCE_URL
valueFrom:
configMapKeyRef:
name: ljwx-boot-config
key: database.url
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: ljwx-boot-secret
key: database.username
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: ljwx-boot-secret
key: database.password
# Redis配置
- name: SPRING_REDIS_HOST
valueFrom:
configMapKeyRef:
name: ljwx-boot-config
key: redis.host
- name: SPRING_REDIS_PORT
valueFrom:
configMapKeyRef:
name: ljwx-boot-config
key: redis.port
- name: SPRING_REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: ljwx-boot-secret
key: redis.password
- name: SPRING_REDIS_DATABASE
value: "1"
# JVM参数
- name: JAVA_OPTS
value: "-Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof"
# 资源限制
resources:
requests:
cpu: "500m" # 请求0.5核
memory: "1Gi" # 请求1GB内存
limits:
cpu: "2000m" # 限制2核
memory: "2Gi" # 限制2GB内存
# 健康检查
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 9998
initialDelaySeconds: 120 # 应用启动需要时间
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 9998
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
# 启动探针(防止慢启动应用被杀死)
startupProbe:
httpGet:
path: /actuator/health
port: 9998
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30 # 最多等待300秒
# 挂载卷
volumeMounts:
- name: logs
mountPath: /app/logs
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: logs
emptyDir: {} # 临时日志存储(建议改用持久化卷或日志收集器)
- name: config
configMap:
name: ljwx-boot-config
items:
- key: application-prod.yml
path: application-prod.yml
# 优雅终止配置
terminationGracePeriodSeconds: 30
# 安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
+86
View File
@@ -0,0 +1,86 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ljwx-boot-ingress
namespace: ljwx-health
labels:
app: ljwx-boot
annotations:
# Nginx Ingress配置
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
# 请求大小限制
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# 超时配置
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
# WebSocket支持(如果需要)
nginx.ingress.kubernetes.io/websocket-services: "ljwx-boot"
# 健康检查
nginx.ingress.kubernetes.io/health-check-path: "/actuator/health"
# CORS配置(根据需要启用)
# nginx.ingress.kubernetes.io/enable-cors: "true"
# nginx.ingress.kubernetes.io/cors-allow-origin: "*"
# 速率限制(可选)
# nginx.ingress.kubernetes.io/limit-rps: "100"
# SSL证书(如果使用cert-manager)
# cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx # 使用Nginx Ingress Controller
# TLS配置(可选)
# tls:
# - hosts:
# - api.ljwx.example.com
# secretName: ljwx-boot-tls-secret
rules:
- host: api.ljwx.local # 修改为您的域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ljwx-boot
port:
number: 9998
# 可以添加多个域名规则
# - host: api.ljwx.example.com
# http:
# paths:
# - path: /
# pathType: Prefix
# backend:
# service:
# name: ljwx-boot
# port:
# number: 9998
---
# 如果需要在集群外部直接访问(不推荐生产环境)
# 可以使用NodePort或LoadBalancer类型的Service
# apiVersion: v1
# kind: Service
# metadata:
# name: ljwx-boot-nodeport
# namespace: ljwx-health
# spec:
# type: NodePort
# selector:
# app: ljwx-boot
# ports:
# - port: 9998
# targetPort: 9998
# nodePort: 30998 # 30000-32767范围内
+43
View File
@@ -0,0 +1,43 @@
# ============================================================================
# Kubernetes Secret 配置模板
#
# 使用方法:
# 1. 复制此文件为 secret.yml
# 2. 将占位符替换为实际的base64编码值
# 3. 不要将 secret.yml 提交到Git(已在.gitignore中)
#
# Base64编码命令:
# echo -n "your-password" | base64
# ============================================================================
apiVersion: v1
kind: Secret
metadata:
name: ljwx-boot-secret
namespace: ljwx-health
labels:
app: ljwx-boot
type: Opaque
data:
# 数据库密码(base64编码)
# 示例: echo -n "123456" | base64 => MTIzNDU2
database.username: <BASE64_ENCODED_DB_USERNAME> # 例如: cm9vdA==
database.password: <BASE64_ENCODED_DB_PASSWORD> # 例如: MTIzNDU2
# Redis密码(base64编码)
# 如果Redis没有密码,可以设置为空字符串的base64: ""
redis.password: <BASE64_ENCODED_REDIS_PASSWORD> # 例如: MTIzNDU2
# JWT密钥(base64编码)
jwt.secret: <BASE64_ENCODED_JWT_SECRET>
# 其他敏感配置...
---
# Harbor镜像拉取密钥
# 由Gitea workflow自动创建,无需手动创建
# kubectl create secret docker-registry harbor-secret \
# --docker-server=harbor.omniverseai.net \
# --docker-username=<your-username> \
# --docker-password=<your-password> \
# --namespace=ljwx-health
+44
View File
@@ -0,0 +1,44 @@
apiVersion: v1
kind: Service
metadata:
name: ljwx-boot
namespace: ljwx-health
labels:
app: ljwx-boot
component: backend
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-health-check-flag: "on"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-health-check-uri: "/actuator/health"
spec:
type: ClusterIP # 使用ClusterIP,通过Ingress暴露服务
selector:
app: ljwx-boot
ports:
- name: http
port: 9998 # 服务端口
targetPort: 9998 # 容器端口
protocol: TCP
sessionAffinity: ClientIP # 会话亲和性(可选)
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3小时
---
apiVersion: v1
kind: Service
metadata:
name: ljwx-boot-headless
namespace: ljwx-health
labels:
app: ljwx-boot
component: backend
spec:
type: ClusterIP
clusterIP: None # Headless Service,用于StatefulSet或服务发现
selector:
app: ljwx-boot
ports:
- name: http
port: 9998
targetPort: 9998
protocol: TCP