diff --git a/.argocdignore b/.argocdignore new file mode 100644 index 00000000..96254237 --- /dev/null +++ b/.argocdignore @@ -0,0 +1 @@ +client/data/mysql/mysql.sock diff --git a/.env.local-registry b/.env.local-registry new file mode 100644 index 00000000..cdd1e059 --- /dev/null +++ b/.env.local-registry @@ -0,0 +1,21 @@ +# 本地 Docker Registry 配置(用于 CI/CD) +# 此文件配置使用本地 Registry 进行镜像构建和部署 + +# Registry 配置(使用宿主机 IP) +REGISTRY=173.212.254.252:5000 +HARBOR_HOST=173.212.254.252:5000 +HARBOR_PROJECT=ljwx-health + +# 版本配置 +LJWX_VERSION=2.0.4 + +# 镜像版本(与 LJWX_VERSION 保持一致) +LJWX_MYSQL_VERSION=2.0.4 +LJWX_REDIS_VERSION=2.0.4 +LJWX_BOOT_VERSION=2.0.4 +LJWX_BIGSCREEN_VERSION=2.0.4 +LJWX_ADMIN_VERSION=2.0.4 + +# Registry 凭据(用于本地开发) +REGISTRY_USERNAME=admin +REGISTRY_PASSWORD=admin123 diff --git a/.github/CICD_SETUP.md b/.github/CICD_SETUP.md new file mode 100644 index 00000000..56b77b37 --- /dev/null +++ b/.github/CICD_SETUP.md @@ -0,0 +1,440 @@ +# GitHub Actions CI/CD 配置指南 + +本文档说明如何配置 GitHub Actions 实现从构建、推送到自动部署 k3s 的完整 CI/CD 流程。 + +## 架构概述 + +``` +GitHub Actions Workflow +├── 1. Build & Push (ubuntu-latest) +│ ├── 构建 Docker 镜像(多架构支持) +│ └── 推送到 Harbor 仓库 +└── 2. Deploy to K3s (self-hosted runner) + ├── 创建 namespace 和 secrets + ├── 更新镜像标签 + ├── 部署 ljwx-boot + ├── 部署 ljwx-bigscreen + └── 验证部署状态 +``` + +## 前置条件 + +### 1. Harbor 仓库配置 + +确保您的 Harbor 仓库已正确配置: + +- **Registry 地址**: `harbor.omniverseai.net` +- **项目名称**: `ljwx-health` +- **访问凭据**: `admin/admin123`(生产环境请修改) + +### 2. K3s 集群配置 + +确保 k3s 集群正常运行,并且 GitHub Actions runner 可以访问: + +```bash +# 验证 k3s 状态 +kubectl cluster-info +kubectl get nodes + +# 验证 namespace +kubectl get ns ljwx-health +# 如果不存在,workflow 会自动创建 +``` + +### 3. GitHub Actions Self-Hosted Runner + +Runner 必须安装在 k3s 集群内部或可以访问 k3s 的环境中。 + +**验证 Runner 配置**: + +```bash +# 检查 kubectl 配置 +kubectl version --client +kubectl cluster-info + +# 检查 kubeconfig +echo $KUBECONFIG +# 或者检查默认配置 +cat ~/.kube/config +``` + +## GitHub Secrets 配置 + +在 GitHub 仓库中配置以下 Secrets(Settings → Secrets and variables → Actions): + +### 必需的 Secrets + +| Secret 名称 | 说明 | 示例值 | +|------------|------|--------| +| `HARBOR_HOST` | Harbor 仓库地址 | `harbor.omniverseai.net` | +| `HARBOR_USERNAME` | Harbor 用户名 | `admin` | +| `HARBOR_PASSWORD` | Harbor 密码 | `admin123` | + +### 可选的 Secrets(使用默认值) + +| Secret 名称 | 说明 | 默认值 | +|------------|------|--------| +| `MYSQL_USERNAME` | MySQL 用户名 | `root` | +| `MYSQL_PASSWORD` | MySQL 密码 | `123456` | +| `REDIS_PASSWORD` | Redis 密码 | `123456` | +| `JWT_SECRET` | JWT 密钥 | `ljwx-default-jwt-secret-change-me` | + +**配置步骤**: + +1. 进入 GitHub 仓库页面 +2. 点击 `Settings` → `Secrets and variables` → `Actions` +3. 点击 `New repository secret` +4. 添加上述 Secrets + +## Workflow 配置 + +### 触发条件 + +Workflow 在以下情况下自动触发: + +1. **推送到 main/develop 分支** - 完整的构建、推送和部署 +2. **创建版本标签 (v*)** - 构建特定版本 +3. **Pull Request** - 仅构建和推送,不部署 +4. **手动触发** - 通过 GitHub UI 手动运行 + +### 部署条件 + +部署到 k3s 仅在以下情况执行: + +- ✅ 构建和推送成功 +- ✅ 分支为 `main` 或 `develop` +- ✅ Runner 为 `self-hosted` + +### 镜像版本策略 + +1. **从 .env 文件读取** - 使用 `LJWX_VERSION` 变量 +2. **从 Git 标签读取** - 推送 `v*` 标签时使用标签名 +3. **手动指定** - 通过 workflow_dispatch 输入 +4. **默认值** - `latest` + +## 部署流程详解 + +### 1. 构建和推送镜像 + +```yaml +# 支持的组件 +- ljwx-boot # Spring Boot 后端 +- ljwx-admin # Vue3 前端 +- ljwx-bigscreen # Flask 数据大屏 +- ljwx-mysql # MySQL 数据库 +- ljwx-redis # Redis 缓存 +``` + +**多架构支持**: +- linux/amd64 +- linux/arm64 + +### 2. 部署到 K3s + +**部署步骤**: + +1. **创建 namespace** + ```bash + kubectl create namespace ljwx-health + ``` + +2. **创建 Harbor 镜像拉取密钥** + ```bash + kubectl create secret docker-registry harbor-secret \ + --docker-server=harbor.omniverseai.net \ + --docker-username=admin \ + --docker-password=admin123 \ + --namespace=ljwx-health + ``` + +3. **更新镜像标签** + - 自动替换 deployment 文件中的镜像版本 + - 修正 namespace 为 `ljwx-health` + +4. **部署 ljwx-boot** + - 应用 ConfigMap(数据库、Redis 配置) + - 创建 Secret(密码、JWT 密钥) + - 部署 Service 和 Deployment + - 等待 Pod 就绪(最多 5 分钟) + +5. **部署 ljwx-bigscreen** + - 应用 ConfigMap + - 部署 Service 和 Deployment + - 等待 Pod 就绪(最多 5 分钟) + +6. **验证部署** + - 检查 Pod 状态 + - 检查 Service 状态 + - 输出部署摘要到 GitHub Actions Summary + +## 手动触发部署 + +### 通过 GitHub UI + +1. 进入仓库的 `Actions` 标签页 +2. 选择 `Build and Push Docker Images` workflow +3. 点击 `Run workflow` +4. 选择参数: + - **Branch**: 选择分支(main/develop) + - **Component**: 选择要构建的组件(all 或特定组件) + - **Version**: 可选,指定版本标签 + +### 通过命令行(使用 GitHub CLI) + +```bash +# 安装 GitHub CLI +brew install gh # macOS +# 或访问 https://cli.github.com/ + +# 触发 workflow +gh workflow run build-and-push.yml \ + --ref main \ + -f component=all \ + -f version=v2.0.4 +``` + +## 验证部署 + +### 检查 Workflow 状态 + +在 GitHub Actions 页面查看: + +1. ✅ Build and Push - 所有组件构建成功 +2. ✅ Deploy to K3s - 部署完成 +3. ✅ Summary - 汇总报告 + +### 检查 K3s 部署 + +```bash +# 查看 namespace +kubectl get ns ljwx-health + +# 查看所有资源 +kubectl get all -n ljwx-health + +# 查看 Pods 状态 +kubectl get pods -n ljwx-health -o wide + +# 查看 Services +kubectl get svc -n ljwx-health + +# 查看 Deployments +kubectl get deployments -n ljwx-health + +# 查看最近的事件 +kubectl get events -n ljwx-health --sort-by='.lastTimestamp' | tail -20 + +# 查看 Pod 日志 +kubectl logs -f deployment/ljwx-boot -n ljwx-health +kubectl logs -f deployment/ljwx-bigscreen -n ljwx-health +``` + +### 检查应用健康状态 + +```bash +# ljwx-boot 健康检查 +kubectl exec -n ljwx-health deployment/ljwx-boot -- curl -s http://localhost:9998/actuator/health + +# ljwx-bigscreen 健康检查 +kubectl exec -n ljwx-health deployment/ljwx-bigscreen -- curl -s http://localhost:5001/api/health +``` + +## 故障排查 + +### 1. 构建失败 + +**常见原因**: +- Harbor 凭据错误 +- Dockerfile 路径错误 +- 构建上下文问题 + +**解决方法**: +```bash +# 检查 Secrets 配置 +# 查看 workflow 日志中的详细错误信息 +# 本地测试构建 +docker build -f ljwx-boot/ljwx-boot-admin/Dockerfile.prod . +``` + +### 2. 推送到 Harbor 失败 + +**常见原因**: +- Harbor 凭据错误 +- 网络连接问题 +- Harbor 项目不存在 + +**解决方法**: +```bash +# 测试 Harbor 登录 +docker login harbor.omniverseai.net -u admin -p admin123 + +# 检查项目是否存在 +# 访问 https://harbor.omniverseai.net/harbor/projects +``` + +### 3. 部署到 K3s 失败 + +**常见原因**: +- kubectl 未配置或无权限 +- namespace 不存在 +- Secret 配置错误 +- 镜像拉取失败 + +**解决方法**: +```bash +# 检查 kubectl 配置 +kubectl config current-context +kubectl auth can-i create deployments -n ljwx-health + +# 检查 Harbor Secret +kubectl get secret harbor-secret -n ljwx-health -o yaml + +# 检查镜像是否可以拉取 +kubectl run test-pod --image=harbor.omniverseai.net/ljwx-health/ljwx-boot:latest \ + --image-pull-policy=Always \ + --dry-run=client -n ljwx-health + +# 查看 Pod 拉取镜像失败的详细信息 +kubectl describe pod -n ljwx-health +``` + +### 4. Pod 启动失败 + +**常见原因**: +- ConfigMap/Secret 配置错误 +- 数据库连接失败 +- 资源不足 + +**解决方法**: +```bash +# 查看 Pod 详细信息 +kubectl describe pod -n ljwx-health + +# 查看 Pod 日志 +kubectl logs -n ljwx-health --tail=100 + +# 查看所有容器日志(包括 init 容器) +kubectl logs -n ljwx-health --all-containers=true + +# 检查节点资源 +kubectl top nodes +kubectl describe node +``` + +### 5. 数据库连接失败 + +**检查配置**: +```bash +# 查看 ConfigMap +kubectl get configmap ljwx-boot-config -n ljwx-health -o yaml + +# 查看 Secret(base64 解码) +kubectl get secret ljwx-boot-secret -n ljwx-health -o jsonpath='{.data.database\.password}' | base64 -d + +# 测试数据库连接 +kubectl run mysql-test --image=mysql:8.0 --rm -it --restart=Never -n ljwx-health -- \ + mysql -h mysql-service.ljwx-health.svc.cluster.local -u root -p123456 -e "SELECT 1" +``` + +## 回滚部署 + +如果部署出现问题,可以快速回滚: + +```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 +``` + +## 性能优化 + +### 1. 加速镜像拉取 + +```bash +# 在 k3s 节点上预拉取镜像 +docker pull harbor.omniverseai.net/ljwx-health/ljwx-boot:latest +docker pull harbor.omniverseai.net/ljwx-health/ljwx-bigscreen:latest +``` + +### 2. 优化 Workflow 缓存 + +Workflow 已配置 GitHub Actions 缓存: + +```yaml +cache-from: type=gha +cache-to: type=gha,mode=max +``` + +### 3. 并行构建 + +可以同时构建多个组件,利用 matrix 策略加速构建。 + +## 安全建议 + +### 1. 生产环境配置 + +- ❌ 不要使用默认密码 +- ✅ 为 MySQL、Redis 设置强密码 +- ✅ 定期轮换 JWT 密钥 +- ✅ 限制 Harbor 用户权限(使用专用的 CI/CD 账户) + +### 2. Secret 管理 + +- ✅ 使用 GitHub Secrets 存储敏感信息 +- ✅ 不要在代码中硬编码密码 +- ✅ 定期审计 Secrets 访问日志 + +### 3. 镜像安全 + +- ✅ 启用 Harbor 镜像扫描 +- ✅ 使用非 root 用户运行容器 +- ✅ 定期更新基础镜像 + +## 监控和日志 + +### 查看实时日志 + +```bash +# 实时查看 ljwx-boot 日志 +kubectl logs -f deployment/ljwx-boot -n ljwx-health + +# 查看最近 100 行日志 +kubectl logs deployment/ljwx-boot -n ljwx-health --tail=100 + +# 查看前一个容器的日志(崩溃后) +kubectl logs -n ljwx-health --previous +``` + +### 查看 Metrics + +```bash +# 查看 Pod 资源使用 +kubectl top pods -n ljwx-health + +# 查看节点资源使用 +kubectl top nodes +``` + +## 相关文档 + +- [K8s 部署指南](../ljwx-boot/k8s/README.md) +- [Docker 构建指南](../DOCKER_README.md) +- [GitHub Actions 文档](https://docs.github.com/actions) +- [K3s 官方文档](https://docs.k3s.io/) + +## 联系支持 + +如遇到问题,请: + +1. 查看 GitHub Actions 日志 +2. 查看 K8s Events 和 Pod 日志 +3. 参考本文档的故障排查章节 +4. 提交 Issue 到项目仓库 diff --git a/.github/LOCAL_REGISTRY_SETUP.md b/.github/LOCAL_REGISTRY_SETUP.md new file mode 100644 index 00000000..e71d0938 --- /dev/null +++ b/.github/LOCAL_REGISTRY_SETUP.md @@ -0,0 +1,302 @@ +# 本地 Docker Registry + GitHub Actions Self-Hosted Runner 完整配置指南 + +本文档记录了如何配置本地 Docker Registry 并与 K3s 中的 GitHub Actions Self-Hosted Runner 集成。 + +## 架构概览 + +``` +GitHub Push → Self-Hosted Runner (K3s Pod) + ↓ + Docker Build (DinD) + ↓ + Push to 173.212.254.252:5000 + ↓ + Deploy to K3s (ljwx-health namespace) +``` + +## 关键组件 + +### 1. 本地 Docker Registry + +**部署位置**: 宿主机 +**地址**: `173.212.254.252:5000` +**Web UI**: `http://localhost:8888` +**凭据**: `admin/admin123` + +**配置文件**: +- `harbor/docker-compose.yml` - Registry 服务定义 +- `harbor/setup.sh` - 部署脚本 + +**启动**: +```bash +cd /root/codes/ljwx-health/harbor +./setup.sh +``` + +**验证**: +```bash +curl -u admin:admin123 http://localhost:5000/v2/_catalog +docker login 173.212.254.252:5000 -u admin -p admin123 +``` + +### 2. GitHub Actions Runner (K3s) + +**Namespace**: `github-runner` +**类型**: RunnerDeployment (summerwind/actions-runner-controller) +**组织**: `BrunoGaoSZ` +**副本数**: 1 + +**关键配置**: +- **Docker-in-Docker (DinD)**: 每个 runner pod 包含两个容器 (`runner` + `docker`) +- **Docker Daemon 配置**: 通过 ConfigMap 持久化 +- **资源限制**: 2 CPU, 4Gi Memory + +### 3. 配置持久化 + +#### ConfigMap: docker-daemon-config + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: docker-daemon-config + namespace: github-runner +data: + daemon.json: | + { + "insecure-registries": ["173.212.254.252:5000", "localhost:5000"] + } +``` + +**作用**: +- 允许 Runner 的 DinD 容器访问 HTTP Registry(无 TLS) +- 配置会自动挂载到每个新 runner pod +- Pod 重建后配置依然生效 + +#### RunnerDeployment 配置 + +```yaml +apiVersion: actions.summerwind.dev/v1alpha1 +kind: RunnerDeployment +metadata: + name: brunogao-runner + namespace: github-runner +spec: + replicas: 1 + template: + spec: + dockerEnabled: true + organization: BrunoGaoSZ + labels: ["k3s", "self-hosted", "linux", "x64"] + + # 挂载 Docker daemon 配置到 DinD 容器 + dockerVolumeMounts: + - mountPath: /etc/docker/daemon.json + name: docker-daemon-config + subPath: daemon.json + + volumes: + - name: docker-daemon-config + configMap: + name: docker-daemon-config +``` + +## 配置步骤总结 + +### 步骤 1: 部署本地 Registry + +```bash +cd /root/codes/ljwx-health/harbor +./setup.sh +``` + +### 步骤 2: 配置宿主机 Docker + +```bash +# 允许不安全的 Registry +cat > /etc/docker/daemon.json < /etc/rancher/k3s/registries.yaml <> $GITHUB_OUTPUT echo "harbor_host=${HARBOR_HOST}" >> $GITHUB_OUTPUT echo "harbor_project=${HARBOR_PROJECT}" >> $GITHUB_OUTPUT + elif [ -f .env ]; then + source .env + echo "registry=${REGISTRY:-173.212.254.252:5000}" >> $GITHUB_OUTPUT + echo "harbor_host=${HARBOR_HOST:-173.212.254.252:5000}" >> $GITHUB_OUTPUT + echo "harbor_project=${HARBOR_PROJECT:-ljwx-health}" >> $GITHUB_OUTPUT else - echo "registry=harbor.omniverseai.net/health" >> $GITHUB_OUTPUT + echo "registry=173.212.254.252:5000" >> $GITHUB_OUTPUT fi - name: Determine version @@ -80,7 +86,7 @@ jobs: build-and-push: name: Build and Push ${{ matrix.component }} needs: prepare - runs-on: ubuntu-latest + runs-on: [self-hosted, Linux, X64, k3s] strategy: fail-fast: false matrix: @@ -89,20 +95,79 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 2 # 获取最近两次提交以检测变化 + + - name: Detect changes for ${{ matrix.component }} + id: changes + run: | + COMPONENT="${{ matrix.component }}" + + # 手动触发或标签推送时,强制构建所有组件 + if [ "${{ github.event_name }}" = "workflow_dispatch" ] || [[ "${{ github.ref }}" == refs/tags/v* ]]; then + echo "should_build=true" >> $GITHUB_OUTPUT + echo "reason=Manual trigger or tag push" >> $GITHUB_OUTPUT + exit 0 + fi + + # 定义每个组件相关的文件路径 + case "$COMPONENT" in + ljwx-boot) + PATHS="ljwx-boot/ docker/mysql/Dockerfile docker/redis/Dockerfile" + ;; + ljwx-admin) + PATHS="ljwx-admin/" + ;; + ljwx-bigscreen) + PATHS="ljwx-bigscreen/" + ;; + ljwx-mysql) + PATHS="docker/mysql/" + ;; + ljwx-redis) + PATHS="docker/redis/" + ;; + *) + PATHS="." + ;; + esac + + # 检测文件变化 + CHANGED=false + for path in $PATHS; do + if git diff --name-only HEAD~1 HEAD | grep -q "^$path"; then + CHANGED=true + echo "✅ Detected changes in: $path" + break + fi + done + + if [ "$CHANGED" = "true" ]; then + echo "should_build=true" >> $GITHUB_OUTPUT + echo "reason=Code changes detected" >> $GITHUB_OUTPUT + else + echo "should_build=false" >> $GITHUB_OUTPUT + echo "reason=No code changes" >> $GITHUB_OUTPUT + echo "⏭️ No changes detected for $COMPONENT, skipping build" + fi - name: Set up Docker Buildx + if: steps.changes.outputs.should_build == 'true' uses: docker/setup-buildx-action@v3 with: - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 + config-inline: | + [registry."173.212.254.252:5000"] + http = true + insecure = true - - name: Log in to Harbor - uses: docker/login-action@v3 - with: - registry: ${{ secrets.HARBOR_HOST || 'harbor.omniverseai.net' }} - username: ${{ secrets.HARBOR_USERNAME }} - password: ${{ secrets.HARBOR_PASSWORD }} + - name: Log in to Local Registry + if: steps.changes.outputs.should_build == 'true' + run: | + echo "admin123" | docker login 173.212.254.252:5000 -u admin --password-stdin - name: Prepare build context + if: steps.changes.outputs.should_build == 'true' id: context run: | COMPONENT="${{ matrix.component }}" @@ -127,7 +192,7 @@ jobs: CONTEXT="ljwx-bigscreen/bigscreen" ;; ljwx-admin) - DOCKERFILE="ljwx-admin/Dockerfile" + DOCKERFILE="ljwx-admin/Dockerfile.prod" CONTEXT="ljwx-admin" ;; *) @@ -138,10 +203,11 @@ jobs: echo "dockerfile=${DOCKERFILE}" >> $GITHUB_OUTPUT echo "context=${CONTEXT}" >> $GITHUB_OUTPUT - echo "image_name=${REGISTRY}/${COMPONENT}" >> $GITHUB_OUTPUT + echo "image_name=${REGISTRY}/ljwx-health/${COMPONENT}" >> $GITHUB_OUTPUT echo "version=${VERSION}" >> $GITHUB_OUTPUT - name: Extract metadata + if: steps.changes.outputs.should_build == 'true' id: meta uses: docker/metadata-action@v5 with: @@ -152,11 +218,12 @@ jobs: type=sha,prefix={{branch}}- - name: Build and push Docker image + if: steps.changes.outputs.should_build == 'true' uses: docker/build-push-action@v5 with: context: ${{ steps.context.outputs.context }} file: ${{ steps.context.outputs.dockerfile }} - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -166,13 +233,139 @@ jobs: VERSION=${{ steps.context.outputs.version }} cache-from: type=gha cache-to: type=gha,mode=max + network: host - - name: Image digest - run: echo "Image pushed successfully" + - name: Build summary + run: | + if [ "${{ steps.changes.outputs.should_build }}" = "true" ]; then + echo "✅ ${{ matrix.component }} - Built and pushed successfully" + echo "📦 Reason: ${{ steps.changes.outputs.reason }}" + else + echo "⏭️ ${{ matrix.component }} - Skipped (no changes)" + echo "📦 Reason: ${{ steps.changes.outputs.reason }}" + fi + + deploy-to-k3s: + name: Deploy to K3s + needs: [prepare, build-and-push] + runs-on: self-hosted + # 暂时禁用自动部署 - 等本地测试完成后再启用 + if: false + # if: success() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') + env: + PATH: /custom-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up kubectl context + run: | + # Verify kubectl is available + kubectl version --client + + # Verify cluster connection + kubectl cluster-info + + - name: Create namespace if not exists + run: | + kubectl create namespace ljwx-health --dry-run=client -o yaml | kubectl apply -f - + + - name: Create Local Registry image pull secret + run: | + kubectl create secret docker-registry harbor-secret \ + --docker-server=173.212.254.252:5000 \ + --docker-username=admin \ + --docker-password=admin123 \ + --namespace=ljwx-health \ + --dry-run=client -o yaml | kubectl apply -f - + + - name: Update image tags in deployments + env: + VERSION: ${{ needs.prepare.outputs.version }} + REGISTRY: ${{ needs.prepare.outputs.registry }} + run: | + # Update ljwx-boot deployment image tag + if [ -f ljwx-boot/k8s/deployment.yml ]; then + sed -i "s|image:.*ljwx-boot:.*|image: ${REGISTRY}/ljwx-boot:${VERSION}|g" ljwx-boot/k8s/deployment.yml + fi + + # Update ljwx-bigscreen deployment image tag + if [ -f ljwx-bigscreen/k8s/prod/deployment.yaml ]; then + # Fix namespace to ljwx-health + sed -i 's|namespace: ljwx-system|namespace: ljwx-health|g' ljwx-bigscreen/k8s/prod/deployment.yaml + # Update image reference + sed -i "s|\${REGISTRY}/\${IMAGE_NAME}:\${IMAGE_TAG}|${REGISTRY}/ljwx-bigscreen:${VERSION}|g" ljwx-bigscreen/k8s/prod/deployment.yaml + fi + + - name: Deploy ljwx-boot + env: + DB_USERNAME: ${{ secrets.MYSQL_USERNAME || 'root' }} + DB_PASSWORD: ${{ secrets.MYSQL_PASSWORD || '123456' }} + REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD || '123456' }} + JWT_SECRET: ${{ secrets.JWT_SECRET || 'ljwx-default-jwt-secret-change-me' }} + run: | + # Apply ConfigMap first + kubectl apply -f ljwx-boot/k8s/configmap.yml -n ljwx-health || true + + # Create secret from template + if [ -f ljwx-boot/k8s/secret.yml.template ]; then + sed "s||$(echo -n "${DB_USERNAME}" | base64)|g" ljwx-boot/k8s/secret.yml.template | \ + sed "s||$(echo -n "${DB_PASSWORD}" | base64)|g" | \ + sed "s||$(echo -n "${REDIS_PASSWORD}" | base64)|g" | \ + sed "s||$(echo -n "${JWT_SECRET}" | base64)|g" | \ + kubectl apply -f - -n ljwx-health + fi + + # Apply Service + kubectl apply -f ljwx-boot/k8s/service.yml -n ljwx-health || true + + # Apply Deployment + kubectl apply -f ljwx-boot/k8s/deployment.yml -n ljwx-health + + # Wait for rollout + kubectl rollout status deployment/ljwx-boot -n ljwx-health --timeout=5m + + - name: Deploy ljwx-bigscreen + run: | + # Apply ConfigMap + kubectl apply -f ljwx-bigscreen/k8s/base/configmap.yaml -n ljwx-health || true + + # Apply Service + kubectl apply -f ljwx-bigscreen/k8s/prod/service.yaml -n ljwx-health || true + + # Apply Deployment + kubectl apply -f ljwx-bigscreen/k8s/prod/deployment.yaml -n ljwx-health + + # Wait for rollout + kubectl rollout status deployment/ljwx-bigscreen -n ljwx-health --timeout=5m + + - name: Verify deployment + run: | + echo "## Deployment Status" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Get pod status + kubectl get pods -n ljwx-health -o wide >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Get service status + echo "### Services" >> $GITHUB_STEP_SUMMARY + kubectl get svc -n ljwx-health >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Check deployment health + BOOT_READY=$(kubectl get deployment ljwx-boot -n ljwx-health -o jsonpath='{.status.readyReplicas}') + BOOT_DESIRED=$(kubectl get deployment ljwx-boot -n ljwx-health -o jsonpath='{.status.replicas}') + + if [ "$BOOT_READY" == "$BOOT_DESIRED" ]; then + echo "✅ ljwx-boot: $BOOT_READY/$BOOT_DESIRED replicas ready" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ ljwx-boot: $BOOT_READY/$BOOT_DESIRED replicas ready" >> $GITHUB_STEP_SUMMARY + fi summary: name: Build Summary - needs: [prepare, build-and-push] + needs: [prepare, build-and-push, deploy-to-k3s] runs-on: ubuntu-latest if: always() steps: @@ -190,3 +383,11 @@ jobs: else echo "❌ Some builds failed. Check the logs above." >> $GITHUB_STEP_SUMMARY fi + + if [ "${{ needs.deploy-to-k3s.result }}" == "success" ]; then + echo "✅ Deployment to K3s successful!" >> $GITHUB_STEP_SUMMARY + elif [ "${{ needs.deploy-to-k3s.result }}" == "skipped" ]; then + echo "⏭️ Deployment skipped (not main/develop branch)" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Deployment to K3s failed. Check the logs above." >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/build-push-update-deploy.yml b/.github/workflows/build-push-update-deploy.yml new file mode 100644 index 00000000..c1076bb9 --- /dev/null +++ b/.github/workflows/build-push-update-deploy.yml @@ -0,0 +1,71 @@ +name: Build -> Push Harbor -> Update Deploy Repo + +on: + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: ljwx-health-dev + cancel-in-progress: false + +jobs: + build_push_and_bump_deploy: + runs-on: [self-hosted, linux, x64] + + env: + IMAGE: harbor.eu.lingjingwanxiang.cn:8080/ljwx/ljwx-health + DEPLOY_REPO: BrunoGaoSZ/ljwx-deploy + DEPLOY_BRANCH: main + DEPLOY_FILE: apps/nginx/overlays/dev/kustomization.yaml + + steps: + - name: Checkout app repo + uses: actions/checkout@v4 + + - name: Compute tag (dev-${sha7}) + id: meta + shell: bash + run: | + set -euo pipefail + SHA7="${GITHUB_SHA:0:7}" + echo "tag=dev-${SHA7}" >> "$GITHUB_OUTPUT" + + - name: Docker login to Harbor + shell: bash + run: | + set -euo pipefail + echo "${{ secrets.HARBOR_PASSWORD }}" | docker login harbor.eu.lingjingwanxiang.cn:8080 \ + -u "${{ secrets.HARBOR_USERNAME }}" --password-stdin + + - name: Build & push image + shell: bash + run: | + set -euo pipefail + docker build -t "${IMAGE}:${{ steps.meta.outputs.tag }}" . + docker push "${IMAGE}:${{ steps.meta.outputs.tag }}" + + - name: Checkout deploy repo + uses: actions/checkout@v4 + with: + repository: BrunoGaoSZ/ljwx-deploy + token: ${{ secrets.DEPLOY_REPO_TOKEN }} + path: deploy + ref: main + + - name: Update newTag and push + shell: bash + run: | + set -euo pipefail + cd deploy + + perl -0777 -i -pe "s/(\\bnewTag:\\s*).*/\$1${{ steps.meta.outputs.tag }}/m" \ + apps/nginx/overlays/dev/kustomization.yaml + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add apps/nginx/overlays/dev/kustomization.yaml + git commit -m "deploy(dev): bump ljwx-health to ${{ steps.meta.outputs.tag }}" || exit 0 + git push origin main diff --git a/.gitignore b/.gitignore index dbe2c79a..16ce3840 100644 --- a/.gitignore +++ b/.gitignore @@ -199,4 +199,5 @@ performance-test/results/*.jtl # ================================ # 网络和其他文件 # ================================ -.nfs* \ No newline at end of file +.nfs* +harbor/data/ diff --git a/.trigger-kubectl-test b/.trigger-kubectl-test new file mode 100644 index 00000000..f798c2cf --- /dev/null +++ b/.trigger-kubectl-test @@ -0,0 +1 @@ +Tue Dec 16 15:31:18 CET 2025 diff --git a/.trigger-test b/.trigger-test index d6066560..18d49f7a 100644 --- a/.trigger-test +++ b/.trigger-test @@ -2,6 +2,7 @@ This file is created to trigger the ci-cd.yml workflow after fixing the YAML 'on:' keyword issue. +Tue Dec 16 08:13:12 CET 2025 Tue Dec 16 12:36:42 CST 2025 Tue Dec 16 12:41:44 CST 2025 Tue Dec 16 13:10:39 CST 2025 diff --git a/apps/nginx/base/deployment.yaml b/apps/nginx/base/deployment.yaml new file mode 100644 index 00000000..43ca4a65 --- /dev/null +++ b/apps/nginx/base/deployment.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + replicas: 1 + selector: + matchLabels: { app: nginx } + template: + metadata: + labels: { app: nginx } + spec: + containers: + - name: nginx + image: nginx:1.27 + ports: + - containerPort: 80 diff --git a/apps/nginx/base/kustomization.yaml b/apps/nginx/base/kustomization.yaml new file mode 100644 index 00000000..6d1374a1 --- /dev/null +++ b/apps/nginx/base/kustomization.yaml @@ -0,0 +1,3 @@ +resources: + - deployment.yaml + - service.yaml diff --git a/apps/nginx/base/service.yaml b/apps/nginx/base/service.yaml new file mode 100644 index 00000000..ebf8d5b7 --- /dev/null +++ b/apps/nginx/base/service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: nginx +spec: + selector: + app: nginx + ports: + - port: 80 + targetPort: 80 diff --git a/apps/nginx/overlays/dev/kustomization.yaml b/apps/nginx/overlays/dev/kustomization.yaml new file mode 100644 index 00000000..6441a150 --- /dev/null +++ b/apps/nginx/overlays/dev/kustomization.yaml @@ -0,0 +1,5 @@ +resources: + - ../../base +images: + - name: nginx + newTag: "1.27" diff --git a/argocd-apps/00-cluster-bootstrap.yaml b/argocd-apps/00-cluster-bootstrap.yaml new file mode 100644 index 00000000..4b3f1511 --- /dev/null +++ b/argocd-apps/00-cluster-bootstrap.yaml @@ -0,0 +1,20 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: cluster-bootstrap + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/BrunoGaoSZ/ljwx-health.git + targetRevision: main + path: cluster + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/argocd-apps/10-nginx-dev.yaml b/argocd-apps/10-nginx-dev.yaml new file mode 100644 index 00000000..e4b6e569 --- /dev/null +++ b/argocd-apps/10-nginx-dev.yaml @@ -0,0 +1,18 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: nginx-dev + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/BrunoGaoSZ/ljwx-health.git + targetRevision: main + path: apps/nginx/overlays/dev + destination: + server: https://kubernetes.default.svc + namespace: dev + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/check-registry.sh b/check-registry.sh new file mode 100755 index 00000000..cfa81a80 --- /dev/null +++ b/check-registry.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +echo "=========================================" +echo "📦 本地 Registry 镜像检查" +echo "=========================================" +echo "" + +echo "🔍 Registry 容器状态:" +docker ps | grep -E "NAME|registry" | grep -v grep +echo "" + +echo "📋 Registry 中的所有仓库:" +curl -s -u admin:admin123 http://localhost:5000/v2/_catalog | jq '.' +echo "" + +echo "🏷️ ljwx-health 项目的镜像:" +REPOS=$(curl -s -u admin:admin123 http://localhost:5000/v2/_catalog | jq -r '.repositories[]' 2>/dev/null) + +if [ -z "$REPOS" ]; then + echo "暂无镜像(CI/CD 构建中...)" +else + for repo in $REPOS; do + if [[ $repo == ljwx-health/* ]]; then + echo "" + echo "📦 $repo:" + TAGS=$(curl -s -u admin:admin123 "http://localhost:5000/v2/$repo/tags/list" | jq -r '.tags[]?' 2>/dev/null) + if [ -z "$TAGS" ]; then + echo " └─ 暂无标签" + else + for tag in $TAGS; do + echo " └─ $tag" + done + fi + fi + done +fi + +echo "" +echo "=========================================" +echo "💡 实用命令" +echo "=========================================" +echo "访问 Web UI: http://localhost:8888" +echo "重新运行检查: ./check-registry.sh" +echo "查看 K3s 部署: ./monitor-deployment.sh" +echo "" diff --git a/client/data/mysql/mysql.sock b/client/data/mysql/mysql.sock deleted file mode 120000 index dd20ce8d..00000000 --- a/client/data/mysql/mysql.sock +++ /dev/null @@ -1 +0,0 @@ -/var/run/mysqld/mysqld.sock \ No newline at end of file diff --git a/cluster/default-sa.yaml b/cluster/default-sa.yaml new file mode 100644 index 00000000..74aa5934 --- /dev/null +++ b/cluster/default-sa.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: default + namespace: dev +imagePullSecrets: + - name: regcred diff --git a/cluster/kustomization.yaml b/cluster/kustomization.yaml new file mode 100644 index 00000000..42aa7f49 --- /dev/null +++ b/cluster/kustomization.yaml @@ -0,0 +1,4 @@ +resources: + - namespace-dev.yaml + - regcred.yaml + - default-sa.yaml diff --git a/cluster/namespace-dev.yaml b/cluster/namespace-dev.yaml new file mode 100644 index 00000000..8cab2978 --- /dev/null +++ b/cluster/namespace-dev.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: dev diff --git a/cluster/regcred.yaml b/cluster/regcred.yaml new file mode 100644 index 00000000..2dd48afd --- /dev/null +++ b/cluster/regcred.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +data: + .dockerconfigjson: eyJhdXRocyI6eyJoYXJib3IuZXUubGluZ2ppbmd3YW54aWFuZy5jbjo4MDgwIjp7InVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IkhhcmJvcjEyMzQ1IiwiYXV0aCI6IllXUnRhVzQ2U0dGeVltOXlNVEl6TkRVPSJ9fX0= +kind: Secret +metadata: + name: regcred + namespace: dev +type: kubernetes.io/dockerconfigjson diff --git a/harbor/.trigger-test b/harbor/.trigger-test new file mode 100644 index 00000000..7d051092 --- /dev/null +++ b/harbor/.trigger-test @@ -0,0 +1 @@ +Tue Dec 16 09:18:06 CET 2025 diff --git a/harbor/auth/htpasswd b/harbor/auth/htpasswd new file mode 100644 index 00000000..0c2b5a3c --- /dev/null +++ b/harbor/auth/htpasswd @@ -0,0 +1,2 @@ +admin:$2y$05$d3UA3lnupxLKfSoVhqHoG.QQriPbPTkdrCKJbVxmxvtXXCGDhwcaW + diff --git a/harbor/docker-compose.yml b/harbor/docker-compose.yml new file mode 100644 index 00000000..ab8c7356 --- /dev/null +++ b/harbor/docker-compose.yml @@ -0,0 +1,46 @@ +version: '3.8' + +services: + registry: + image: registry:2 + container_name: local-harbor-registry + restart: always + ports: + - "5000:5000" + environment: + REGISTRY_AUTH: htpasswd + REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm + REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd + REGISTRY_STORAGE_DELETE_ENABLED: "true" + volumes: + - ./data/registry:/var/lib/registry + - ./auth:/auth + networks: + - harbor + + registry-ui: + image: joxit/docker-registry-ui:latest + container_name: local-harbor-ui + restart: always + ports: + - "8888:80" + environment: + - SINGLE_REGISTRY=true + - REGISTRY_TITLE=LJWX Health Registry + - DELETE_IMAGES=true + - SHOW_CONTENT_DIGEST=true + - NGINX_PROXY_PASS_URL=http://registry:5000 + - SHOW_CATALOG_NB_TAGS=true + - CATALOG_MIN_BRANCHES=1 + - CATALOG_MAX_BRANCHES=1 + - TAGLIST_PAGE_SIZE=100 + - REGISTRY_SECURED=false + - CATALOG_ELEMENTS_LIMIT=1000 + networks: + - harbor + depends_on: + - registry + +networks: + harbor: + driver: bridge diff --git a/harbor/setup.sh b/harbor/setup.sh new file mode 100755 index 00000000..06efb5a7 --- /dev/null +++ b/harbor/setup.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -e + +echo "=========================================" +echo "🚀 本地 Docker Registry 部署脚本" +echo "=========================================" +echo "" + +HARBOR_DIR="/root/codes/ljwx-health/harbor" +cd "$HARBOR_DIR" + +# 创建必要的目录 +echo "📁 创建数据目录..." +mkdir -p data/registry +mkdir -p auth + +# 生成 htpasswd 文件(admin/admin123) +echo "🔐 创建认证文件..." +if command -v htpasswd &> /dev/null; then + htpasswd -Bbn admin admin123 > auth/htpasswd +else + # 如果没有 htpasswd 命令,使用 docker 生成 + docker run --rm --entrypoint htpasswd httpd:2 -Bbn admin admin123 > auth/htpasswd +fi + +echo "✅ 认证文件已创建" +cat auth/htpasswd +echo "" + +# 停止并删除旧容器 +echo "🧹 清理旧容器..." +docker compose down -v 2>/dev/null || true + +echo "" +echo "🐳 启动 Docker Registry..." +docker compose up -d + +echo "" +echo "⏳ 等待 Registry 启动 (10秒)..." +sleep 10 + +# 测试连接 +echo "" +echo "🔍 测试 Registry 连接..." +if curl -s -f http://localhost:5000/v2/ > /dev/null; then + echo "✅ Registry API 可访问" +else + echo "⚠️ Registry API 尚未就绪,请稍等片刻" +fi + +echo "" +echo "=========================================" +echo "✅ Docker Registry 部署完成!" +echo "=========================================" +echo "" +echo "📦 Registry 信息:" +echo " - Registry API: http://localhost:5000" +echo " - Web UI: http://localhost:8888" +echo " - 用户名: admin" +echo " - 密码: admin123" +echo "" +echo "🔧 Docker 登录:" +echo " docker login localhost:5000 -u admin -p admin123" +echo "" +echo "📝 推送镜像示例:" +echo " docker tag myimage:latest localhost:5000/ljwx-health/myimage:latest" +echo " docker push localhost:5000/ljwx-health/myimage:latest" +echo "" +echo "🌐 查看所有仓库:" +echo " curl -u admin:admin123 http://localhost:5000/v2/_catalog" +echo "" diff --git a/ljwx-admin/Dockerfile.prod b/ljwx-admin/Dockerfile.prod index bbcdaf35..b1c9eb1a 100644 --- a/ljwx-admin/Dockerfile.prod +++ b/ljwx-admin/Dockerfile.prod @@ -1,23 +1,15 @@ -# 多阶段构建:第一阶段 - 前端构建环境 -FROM harbor.omniverseai.net/proxy-dockerhub/node:18-alpine AS builder +# 多阶段构建:第一阶段 - 前端构建环境 +FROM node:18-alpine AS builder # 设置工作目录 WORKDIR /app -# 设置阿里云镜像加速环境变量 -ENV SASS_BINARY_SITE=https://npmmirror.com/mirrors/node-sass/ \ - ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ \ - PUPPETEER_DOWNLOAD_HOST=https://npmmirror.com/mirrors \ - CHROMEDRIVER_CDNURL=https://npmmirror.com/mirrors/chromedriver \ - OPERADRIVER_CDNURL=https://npmmirror.com/mirrors/operadriver \ - PHANTOMJS_CDNURL=https://npmmirror.com/mirrors/phantomjs \ - SELENIUM_CDNURL=https://npmmirror.com/mirrors/selenium \ - SQLITE3_BINARY_SITE=https://npmmirror.com/mirrors - -# 安装 pnpm 并设置阿里云 npm 镜像 -RUN npm config set registry https://registry.npmmirror.com && \ +# 安装 pnpm 并配置超时重试(使用官方 registry) +RUN npm config set fetch-timeout 600000 && \ + npm config set fetch-retries 5 && \ npm install -g pnpm && \ - pnpm config set registry https://registry.npmmirror.com + pnpm config set network-timeout 600000 && \ + pnpm config set fetch-retries 5 # 复制 package 相关文件(利用Docker层缓存) COPY package*.json pnpm-*.yaml ./ @@ -47,7 +39,7 @@ ENV VITE_SERVICE_EXPIRED_TOKEN_CODES=40101 RUN pnpm run build # 多阶段构建:第二阶段 - 生产运行环境 -FROM harbor.omniverseai.net/proxy-dockerhub/nginx:alpine +FROM nginx:alpine # 设置北京时区 ENV TZ=Asia/Shanghai @@ -55,8 +47,6 @@ RUN apk add --no-cache tzdata && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone -RUN apk add --no-cache findutils # 添加find命令支持 - # 从构建阶段复制构建结果 COPY --from=builder /app/dist /usr/share/nginx/html diff --git a/ljwx-bigscreen/bigscreen/Dockerfile.prod b/ljwx-bigscreen/bigscreen/Dockerfile.prod index a86b0de6..ecee8438 100644 --- a/ljwx-bigscreen/bigscreen/Dockerfile.prod +++ b/ljwx-bigscreen/bigscreen/Dockerfile.prod @@ -7,8 +7,13 @@ # 4. 分层构建:系统依赖 + Python 依赖 + 应用代码 # ============================================ +# 动态镜像源配置 +# 默认使用 Docker Hub 官方源(本机/GitHub Actions) +# 开发服务器可通过 --build-arg REGISTRY_PREFIX=harbor.omniverseai.net/proxy-dockerhub/ 使用 Harbor 代理 +ARG REGISTRY_PREFIX="" + # ====== 构建阶段 ====== -FROM harbor.omniverseai.net/proxy-dockerhub/python:3.12-slim AS builder +FROM ${REGISTRY_PREFIX}python:3.12-slim AS builder # 安装编译工具和运行时依赖 RUN apt-get update --fix-missing && \ @@ -37,7 +42,9 @@ RUN --mount=type=cache,target=/root/.cache/pip \ pip install --user --no-cache-dir -r requirements-docker.txt # ====== 运行阶段(最小化镜像)====== -FROM harbor.omniverseai.net/proxy-dockerhub/python:3.12-slim +# 复用 ARG 以在运行阶段使用相同的镜像源 +ARG REGISTRY_PREFIX="" +FROM ${REGISTRY_PREFIX}python:3.12-slim # 安装运行时依赖 RUN apt-get update --fix-missing && \ diff --git a/ljwx-bigscreen/k8s/base/configmap.yaml b/ljwx-bigscreen/k8s/base/configmap.yaml index 603d87c4..04150c77 100644 --- a/ljwx-bigscreen/k8s/base/configmap.yaml +++ b/ljwx-bigscreen/k8s/base/configmap.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ConfigMap metadata: name: ljwx-config - namespace: ljwx-system + namespace: ljwx-health data: - redis.host: "redis-service.ljwx-system.svc.cluster.local" + redis.host: "redis-service.ljwx-health.svc.cluster.local" redis.port: "6379" app.log.level: "INFO" app.debug: "false" @@ -14,10 +14,10 @@ apiVersion: v1 kind: Secret metadata: name: ljwx-db-secret - namespace: ljwx-system + namespace: ljwx-health type: Opaque data: - host: bXlzcWwtc2VydmljZS5sand4LXN5c3RlbS5zdmMuY2x1c3Rlci5sb2NhbA== # mysql-service.ljwx-system.svc.cluster.local - username: bGp3eA== # ljwx - password: bGp3eDEyMw== # ljwx123 - database: bGp3eA== # ljwx \ No newline at end of file + host: bXlzcWwtc2VydmljZS5sand4LWhlYWx0aC5zdmMuY2x1c3Rlci5sb2NhbA== # mysql-service.ljwx-health.svc.cluster.local + username: cm9vdA== # root + password: MTIzNDU2 # 123456 + database: dGVzdA== # test \ No newline at end of file diff --git a/ljwx-bigscreen/k8s/prod/deployment.yaml b/ljwx-bigscreen/k8s/prod/deployment.yaml index bc314ccb..a5e281a6 100644 --- a/ljwx-bigscreen/k8s/prod/deployment.yaml +++ b/ljwx-bigscreen/k8s/prod/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: ljwx-bigscreen - namespace: ljwx-system + namespace: ljwx-health labels: app: ljwx-bigscreen env: prod @@ -41,7 +41,7 @@ spec: topologyKey: kubernetes.io/hostname containers: - name: ljwx-bigscreen - image: ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} + image: 173.212.254.252:5000/ljwx-health/ljwx-bigscreen:latest ports: - containerPort: 5001 name: http @@ -63,6 +63,10 @@ spec: secretKeyRef: name: ljwx-db-secret key: password + - name: MYSQL_DATABASE + value: "test" + - name: APP_PORT + value: "5001" - name: REDIS_HOST valueFrom: configMapKeyRef: @@ -80,24 +84,21 @@ spec: memory: "1Gi" cpu: "1000m" livenessProbe: - httpGet: - path: /api/health + tcpSocket: port: 5001 initialDelaySeconds: 60 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: - httpGet: - path: /api/ready + tcpSocket: port: 5001 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 3 startupProbe: # 启动探针,给应用更多启动时间 - httpGet: - path: /api/health + tcpSocket: port: 5001 initialDelaySeconds: 30 periodSeconds: 10 @@ -108,7 +109,7 @@ apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: ljwx-bigscreen-hpa - namespace: ljwx-system + namespace: ljwx-health spec: scaleTargetRef: apiVersion: apps/v1 diff --git a/ljwx-bigscreen/k8s/prod/service.yaml b/ljwx-bigscreen/k8s/prod/service.yaml index 0b9ddc50..75b97088 100644 --- a/ljwx-bigscreen/k8s/prod/service.yaml +++ b/ljwx-bigscreen/k8s/prod/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: ljwx-bigscreen-service - namespace: ljwx-system + namespace: ljwx-health labels: app: ljwx-bigscreen env: prod diff --git a/ljwx-boot/k8s/configmap.yml b/ljwx-boot/k8s/configmap.yml index 7ad72052..c524cffe 100644 --- a/ljwx-boot/k8s/configmap.yml +++ b/ljwx-boot/k8s/configmap.yml @@ -7,7 +7,7 @@ metadata: 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.url: "jdbc:mysql://mysql-service.ljwx-health.svc.cluster.local:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true" database.driver: "com.mysql.cj.jdbc.Driver" # Redis配置 @@ -30,6 +30,7 @@ data: name: ljwx-boot datasource: + driver-class-name: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource hikari: minimum-idle: 10 @@ -39,6 +40,9 @@ data: max-lifetime: 1800000 redis: + # Redis Service DNS 名称(K8s 集群内部) + host: redis-service.ljwx-health.svc.cluster.local + port: 6379 database: 1 timeout: 10000 lettuce: diff --git a/ljwx-boot/k8s/deployment.yml b/ljwx-boot/k8s/deployment.yml index 013d7d4c..13ace0de 100644 --- a/ljwx-boot/k8s/deployment.yml +++ b/ljwx-boot/k8s/deployment.yml @@ -46,7 +46,7 @@ spec: containers: - name: ljwx-boot - image: harbor.omniverseai.net/ljwx-health/ljwx-boot:latest + image: 173.212.254.252:5000/ljwx-health/ljwx-boot:latest imagePullPolicy: Always ports: @@ -80,18 +80,18 @@ spec: name: ljwx-boot-secret key: database.password - # Redis配置 + # Redis配置 - 直接指定值,覆盖配置文件 + - name: SPRING_DATA_REDIS_HOST + value: "redis-service.ljwx-health.svc.cluster.local" + + - name: SPRING_DATA_REDIS_PORT + value: "6379" + - name: SPRING_REDIS_HOST - valueFrom: - configMapKeyRef: - name: ljwx-boot-config - key: redis.host + value: "redis-service.ljwx-health.svc.cluster.local" - name: SPRING_REDIS_PORT - valueFrom: - configMapKeyRef: - name: ljwx-boot-config - key: redis.port + value: "6379" - name: SPRING_REDIS_PASSWORD valueFrom: @@ -102,9 +102,25 @@ spec: - name: SPRING_REDIS_DATABASE value: "1" + # Redis URL 连接配置(application-common.yml 使用) + - name: REDIS_HOST + value: "redis-service.ljwx-health.svc.cluster.local" + + - name: REDIS_PORT + value: "6379" + + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: ljwx-boot-secret + key: redis.password + + - name: REDIS_DB + value: "1" + # JVM参数 - name: JAVA_OPTS - value: "-Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof" + value: "-Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -Dspring.data.redis.host=redis-service.ljwx-health.svc.cluster.local -Dspring.data.redis.port=6379 -Dspring.redis.host=redis-service.ljwx-health.svc.cluster.local -Dspring.redis.port=6379" # 资源限制 resources: @@ -147,7 +163,7 @@ spec: # 挂载卷 volumeMounts: - name: logs - mountPath: /app/logs + mountPath: /app/ljwx-boot-logs - name: config mountPath: /app/config readOnly: true diff --git a/ljwx-boot/k8s/mysql-deployment.yml b/ljwx-boot/k8s/mysql-deployment.yml new file mode 100644 index 00000000..39e11aa2 --- /dev/null +++ b/ljwx-boot/k8s/mysql-deployment.yml @@ -0,0 +1,97 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql + namespace: ljwx-health + labels: + app: mysql + component: database +spec: + replicas: 1 + selector: + matchLabels: + app: mysql + strategy: + type: Recreate # MySQL 使用 Recreate 策略,避免多实例同时访问数据 + template: + metadata: + labels: + app: mysql + spec: + imagePullSecrets: + - name: harbor-secret + containers: + - name: mysql + image: 173.212.254.252:5000/ljwx-health/ljwx-mysql:latest + imagePullPolicy: Always + ports: + - name: mysql + containerPort: 3306 + protocol: TCP + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: ljwx-boot-secret + key: database.password + - name: MYSQL_DATABASE + value: "test" + - name: TZ + value: "Asia/Shanghai" + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "2Gi" + livenessProbe: + exec: + command: + - mysqladmin + - ping + - -h + - localhost + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + exec: + command: + - mysqladmin + - ping + - -h + - localhost + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - name: mysql-data + mountPath: /var/lib/mysql + - name: mysql-config + mountPath: /etc/mysql/conf.d + readOnly: true + volumes: + - name: mysql-data + persistentVolumeClaim: + claimName: mysql-pvc + - name: mysql-config + configMap: + name: mysql-config +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql-config + namespace: ljwx-health +data: + custom.cnf: | + [mysqld] + character-set-server=utf8mb4 + collation-server=utf8mb4_unicode_ci + default-time-zone='+08:00' + max_connections=200 + innodb_buffer_pool_size=1G + innodb_log_file_size=256M diff --git a/ljwx-boot/k8s/mysql-pvc.yml b/ljwx-boot/k8s/mysql-pvc.yml new file mode 100644 index 00000000..29f9d299 --- /dev/null +++ b/ljwx-boot/k8s/mysql-pvc.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql-pvc + namespace: ljwx-health + labels: + app: mysql +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + storageClassName: local-path # k3s 默认使用 local-path diff --git a/ljwx-boot/k8s/mysql-service.yml b/ljwx-boot/k8s/mysql-service.yml new file mode 100644 index 00000000..696afba1 --- /dev/null +++ b/ljwx-boot/k8s/mysql-service.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: mysql-service + namespace: ljwx-health + labels: + app: mysql + component: database +spec: + type: ClusterIP + selector: + app: mysql + ports: + - name: mysql + port: 3306 + targetPort: 3306 + protocol: TCP diff --git a/ljwx-boot/k8s/redis-deployment.yml b/ljwx-boot/k8s/redis-deployment.yml new file mode 100644 index 00000000..7ce7f399 --- /dev/null +++ b/ljwx-boot/k8s/redis-deployment.yml @@ -0,0 +1,109 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + namespace: ljwx-health + labels: + app: redis + component: cache +spec: + replicas: 1 + selector: + matchLabels: + app: redis + strategy: + type: Recreate # Redis 使用 Recreate 策略 + template: + metadata: + labels: + app: redis + spec: + imagePullSecrets: + - name: harbor-secret + containers: + - name: redis + image: 173.212.254.252:5000/ljwx-health/ljwx-redis:latest + imagePullPolicy: Always + ports: + - name: redis + containerPort: 6379 + protocol: TCP + env: + - name: TZ + value: "Asia/Shanghai" + command: + - redis-server + - /usr/local/etc/redis/redis.conf + - --requirepass + - $(REDIS_PASSWORD) + env: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: ljwx-boot-secret + key: redis.password + resources: + requests: + cpu: "200m" + memory: "512Mi" + limits: + cpu: "1000m" + memory: "1Gi" + livenessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - name: redis-data + mountPath: /data + - name: redis-config + mountPath: /usr/local/etc/redis + readOnly: true + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: redis-pvc + - name: redis-config + configMap: + name: redis-config +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: redis-config + namespace: ljwx-health +data: + redis.conf: | + bind 0.0.0.0 + port 6379 + protected-mode yes + timeout 0 + tcp-keepalive 300 + databases 16 + save 900 1 + save 300 10 + save 60 10000 + stop-writes-on-bgsave-error yes + rdbcompression yes + rdbchecksum yes + dbfilename dump.rdb + dir /data + maxmemory 512mb + maxmemory-policy allkeys-lru + appendonly yes + appendfilename "appendonly.aof" + appendfsync everysec diff --git a/ljwx-boot/k8s/redis-pvc.yml b/ljwx-boot/k8s/redis-pvc.yml new file mode 100644 index 00000000..49600011 --- /dev/null +++ b/ljwx-boot/k8s/redis-pvc.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: redis-pvc + namespace: ljwx-health + labels: + app: redis +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: local-path # k3s 默认使用 local-path diff --git a/ljwx-boot/k8s/redis-service.yml b/ljwx-boot/k8s/redis-service.yml new file mode 100644 index 00000000..765950ab --- /dev/null +++ b/ljwx-boot/k8s/redis-service.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: redis-service + namespace: ljwx-health + labels: + app: redis + component: cache +spec: + type: ClusterIP + selector: + app: redis + ports: + - name: redis + port: 6379 + targetPort: 6379 + protocol: TCP diff --git a/monitor-deployment.sh b/monitor-deployment.sh new file mode 100755 index 00000000..287a4b4b --- /dev/null +++ b/monitor-deployment.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# 监控 ljwx-health 部署状态 + +echo "=========================================" +echo "📊 LJWX Health 部署状态监控" +echo "=========================================" +echo "" + +# 设置颜色 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# 检查 namespace +echo -e "${YELLOW}[1/5] 检查 Namespace...${NC}" +kubectl get ns ljwx-health -o wide +echo "" + +# 检查 Secrets +echo -e "${YELLOW}[2/5] 检查 Secrets...${NC}" +kubectl get secrets -n ljwx-health | grep -E "NAME|harbor-secret|ljwx-boot-secret" +echo "" + +# 检查 Deployments +echo -e "${YELLOW}[3/5] 检查 Deployments...${NC}" +kubectl get deployments -n ljwx-health -o wide +echo "" + +# 检查 Pods +echo -e "${YELLOW}[4/5] 检查 Pods...${NC}" +kubectl get pods -n ljwx-health -o wide +echo "" + +# 检查最近的 Events +echo -e "${YELLOW}[5/5] 最近的 Events (最后 10 条)...${NC}" +kubectl get events -n ljwx-health --sort-by='.lastTimestamp' | tail -10 +echo "" + +# 健康状态摘要 +echo "=========================================" +echo "📈 健康状态摘要" +echo "=========================================" + +TOTAL_PODS=$(kubectl get pods -n ljwx-health --no-headers 2>/dev/null | wc -l | tr -d ' ') +RUNNING_PODS=$(kubectl get pods -n ljwx-health --field-selector=status.phase=Running --no-headers 2>/dev/null | wc -l | tr -d ' ') +READY_PODS=$(kubectl get pods -n ljwx-health -o json 2>/dev/null | jq '[.items[] | select(.status.conditions[] | select(.type=="Ready" and .status=="True"))] | length' 2>/dev/null || echo "0") + +echo "总 Pods: $TOTAL_PODS" +echo "运行中: $RUNNING_PODS" +echo "就绪: $READY_PODS" + +if [ "$READY_PODS" = "$TOTAL_PODS" ] && [ "$TOTAL_PODS" -gt 0 ]; then + echo -e "${GREEN}✅ 所有 Pods 已就绪!${NC}" +else + echo -e "${YELLOW}⚠️ 部分 Pods 未就绪,请检查详细信息${NC}" +fi + +echo "" +echo "=========================================" +echo "💡 实用命令" +echo "=========================================" +echo "查看 ljwx-boot 日志: kubectl logs -f deployment/ljwx-boot -n ljwx-health" +echo "查看 ljwx-bigscreen 日志: kubectl logs -f deployment/ljwx-bigscreen -n ljwx-health" +echo "查看所有资源: kubectl get all -n ljwx-health" +echo "重新运行此脚本: ./monitor-deployment.sh" +echo "" diff --git a/scripts/deploy-local-test.sh b/scripts/deploy-local-test.sh new file mode 100755 index 00000000..5f17f5b9 --- /dev/null +++ b/scripts/deploy-local-test.sh @@ -0,0 +1,189 @@ +#!/bin/bash +set -e + +echo "=========================================" +echo "🧪 K3s 本地部署测试脚本" +echo "=========================================" +echo "" + +NAMESPACE="ljwx-health" +REGISTRY="173.212.254.252:5000" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +function log_info() { + echo -e "${GREEN}✅ $1${NC}" +} + +function log_warn() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +function log_error() { + echo -e "${RED}❌ $1${NC}" +} + +# 步骤 1: 验证 YAML 文件语法 +echo "=========================================" +echo "📝 步骤 1: 验证 K8s YAML 文件语法" +echo "=========================================" + +YAML_FILES=( + "ljwx-boot/k8s/configmap.yml" + "ljwx-boot/k8s/secret.yml.template" + "ljwx-boot/k8s/service.yml" + "ljwx-boot/k8s/deployment.yml" + "ljwx-bigscreen/k8s/base/configmap.yaml" + "ljwx-bigscreen/k8s/prod/service.yaml" + "ljwx-bigscreen/k8s/prod/deployment.yaml" +) + +for file in "${YAML_FILES[@]}"; do + if [ -f "$file" ]; then + echo "检查: $file" + if kubectl apply --dry-run=client -f "$file" &>/dev/null; then + log_info "$file - 语法正确" + else + log_error "$file - 语法错误,请检查!" + kubectl apply --dry-run=client -f "$file" + exit 1 + fi + else + log_warn "$file - 文件不存在" + fi +done + +echo "" + +# 步骤 2: 创建 namespace +echo "=========================================" +echo "📦 步骤 2: 创建 Namespace" +echo "=========================================" +kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - +log_info "Namespace $NAMESPACE 已创建" +echo "" + +# 步骤 3: 创建镜像拉取密钥 +echo "=========================================" +echo "🔐 步骤 3: 创建镜像拉取密钥" +echo "=========================================" +kubectl create secret docker-registry harbor-secret \ + --docker-server=$REGISTRY \ + --docker-username=admin \ + --docker-password=admin123 \ + --namespace=$NAMESPACE \ + --dry-run=client -o yaml | kubectl apply -f - +log_info "镜像拉取密钥已创建" +echo "" + +# 步骤 4: 部署 ljwx-boot +echo "=========================================" +echo "🚀 步骤 4: 部署 ljwx-boot" +echo "=========================================" + +# 4.1 ConfigMap +echo "4.1 部署 ConfigMap..." +kubectl apply -f ljwx-boot/k8s/configmap.yml -n $NAMESPACE +log_info "ConfigMap 已创建" + +# 4.2 Secret (从模板创建) +echo "4.2 部署 Secret..." +if [ -f ljwx-boot/k8s/secret.yml.template ]; then + DB_USERNAME="${DB_USERNAME:-root}" + DB_PASSWORD="${DB_PASSWORD:-123456}" + REDIS_PASSWORD="${REDIS_PASSWORD:-123456}" + JWT_SECRET="${JWT_SECRET:-ljwx-default-jwt-secret-change-me}" + + sed "s||$(echo -n "$DB_USERNAME" | base64)|g" ljwx-boot/k8s/secret.yml.template | \ + sed "s||$(echo -n "$DB_PASSWORD" | base64)|g" | \ + sed "s||$(echo -n "$REDIS_PASSWORD" | base64)|g" | \ + sed "s||$(echo -n "$JWT_SECRET" | base64)|g" | \ + kubectl apply -f - -n $NAMESPACE + log_info "Secret 已创建" +else + log_warn "Secret 模板文件不存在,跳过" +fi + +# 4.3 Service +echo "4.3 部署 Service..." +kubectl apply -f ljwx-boot/k8s/service.yml -n $NAMESPACE +log_info "Service 已创建" + +# 4.4 Deployment +echo "4.4 部署 Deployment..." +kubectl apply -f ljwx-boot/k8s/deployment.yml -n $NAMESPACE +log_info "Deployment 已创建" + +# 4.5 等待就绪 +echo "4.5 等待 ljwx-boot 就绪..." +kubectl rollout status deployment/ljwx-boot -n $NAMESPACE --timeout=5m || { + log_error "ljwx-boot 部署超时" + kubectl get pods -n $NAMESPACE + exit 1 +} +log_info "ljwx-boot 部署成功!" + +echo "" + +# 步骤 5: 部署 ljwx-bigscreen +echo "=========================================" +echo "🚀 步骤 5: 部署 ljwx-bigscreen" +echo "=========================================" + +# 5.1 ConfigMap +echo "5.1 部署 ConfigMap..." +if [ -f ljwx-bigscreen/k8s/base/configmap.yaml ]; then + kubectl apply -f ljwx-bigscreen/k8s/base/configmap.yaml -n $NAMESPACE + log_info "ConfigMap 已创建" +fi + +# 5.2 Service +echo "5.2 部署 Service..." +if [ -f ljwx-bigscreen/k8s/prod/service.yaml ]; then + kubectl apply -f ljwx-bigscreen/k8s/prod/service.yaml -n $NAMESPACE + log_info "Service 已创建" +fi + +# 5.3 Deployment +echo "5.3 部署 Deployment..." +if [ -f ljwx-bigscreen/k8s/prod/deployment.yaml ]; then + kubectl apply -f ljwx-bigscreen/k8s/prod/deployment.yaml -n $NAMESPACE + log_info "Deployment 已创建" + + # 5.4 等待就绪 + echo "5.4 等待 ljwx-bigscreen 就绪..." + kubectl rollout status deployment/ljwx-bigscreen -n $NAMESPACE --timeout=5m || { + log_error "ljwx-bigscreen 部署超时" + kubectl get pods -n $NAMESPACE + exit 1 + } + log_info "ljwx-bigscreen 部署成功!" +fi + +echo "" + +# 步骤 6: 验证部署 +echo "=========================================" +echo "✅ 步骤 6: 验证部署状态" +echo "=========================================" + +echo "" +echo "📊 Pods 状态:" +kubectl get pods -n $NAMESPACE -o wide + +echo "" +echo "🌐 Services 状态:" +kubectl get svc -n $NAMESPACE + +echo "" +echo "📦 Deployments 状态:" +kubectl get deployments -n $NAMESPACE + +echo "" +echo "=========================================" +echo "🎉 部署测试完成!" +echo "=========================================" diff --git a/watch-cicd.sh b/watch-cicd.sh new file mode 100755 index 00000000..e6cb0cf8 --- /dev/null +++ b/watch-cicd.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +echo "=========================================" +echo "🔍 实时监控 CI/CD 执行 (每 10 秒刷新)" +echo "=========================================" +echo "按 Ctrl+C 停止监控" +echo "" +sleep 2 + +while true; do + clear + echo "=========================================" + echo "🚀 CI/CD 执行状态监控" + echo "=========================================" + date "+%Y-%m-%d %H:%M:%S" + echo "" + + echo "📦 GitHub Actions Runner:" + kubectl get pods -n github-runner -o wide | grep -E "NAME|runner" + echo "" + + echo "🔨 当前正在执行的任务:" + RUNNER_POD=$(kubectl get pods -n github-runner -o name | grep runner | head -1 | cut -d'/' -f2) + if [ -n "$RUNNER_POD" ]; then + kubectl logs -n github-runner "$RUNNER_POD" -c runner --tail=5 2>&1 | grep -E "Running job|Finished|Starting" || echo " 等待任务分配..." + else + echo " 无 runner pod 运行" + fi + echo "" + + echo "🐳 Registry 镜像数量:" + REPO_COUNT=$(curl -s -u admin:admin123 http://localhost:5000/v2/_catalog 2>/dev/null | jq -r '.repositories | length' 2>/dev/null || echo "0") + echo " 已推送镜像仓库数: $REPO_COUNT" + + if [ "$REPO_COUNT" -gt 0 ]; then + echo "" + echo "📋 Registry 中的镜像:" + curl -s -u admin:admin123 http://localhost:5000/v2/_catalog 2>/dev/null | jq -r '.repositories[]' | while read repo; do + TAGS=$(curl -s -u admin:admin123 "http://localhost:5000/v2/$repo/tags/list" 2>/dev/null | jq -r '.tags[]?' | head -3 | tr '\n' ', ' | sed 's/,$//') + echo " └─ $repo: $TAGS" + done + fi + echo "" + + echo "📊 K3s 部署状态 (ljwx-health namespace):" + kubectl get deployments -n ljwx-health 2>/dev/null | grep -E "NAME|ljwx" || echo " 部署尚未更新" + echo "" + + echo "=========================================" + echo "⏰ 每 10 秒自动刷新 | 按 Ctrl+C 退出" + echo "=========================================" + + sleep 10 +done diff --git a/watch-deployment.sh b/watch-deployment.sh new file mode 100755 index 00000000..ebbbe6d6 --- /dev/null +++ b/watch-deployment.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# 实时监控 ljwx-health 部署变化 + +echo "=========================================" +echo "🔍 实时监控 ljwx-health 部署 (每 5 秒刷新)" +echo "=========================================" +echo "按 Ctrl+C 停止监控" +echo "" +sleep 2 + +# 使用 watch 命令实时监控 +watch -n 5 -c ' +echo "📊 Deployments:" +kubectl get deployments -n ljwx-health -o wide +echo "" +echo "🐳 Pods:" +kubectl get pods -n ljwx-health -o wide +echo "" +echo "🔑 Secrets:" +kubectl get secrets -n ljwx-health | grep -E "NAME|harbor|ljwx" +echo "" +echo "⏰ 更新时间: $(date +"%Y-%m-%d %H:%M:%S")" +'