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:
@@ -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 "⚠️ 已回滚到上一个版本,请检查错误日志"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
# Kubernetes Secrets(不要提交到Git)
|
||||
secret.yml
|
||||
|
||||
# Backup files
|
||||
*.backup
|
||||
*.bak
|
||||
|
||||
# Temporary kubectl files
|
||||
*.tmp
|
||||
@@ -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
|
||||
@@ -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
|
||||
Executable
+271
@@ -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 "$@"
|
||||
@@ -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
|
||||
@@ -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范围内
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user