Merge branch 'main' of github.com:BrunoGaoSZ/ljwx-health

Merging two diverged development lines:
- Local (Gitea): Gitea Actions CI/CD, test suite, tenant optimization
- GitHub: Harbor registry, ArgoCD, K8s configurations

Resolved conflicts:
- .trigger-test: Merged timestamps from both branches

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

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
This commit is contained in:
2025-12-19 00:59:29 +08:00
co-authored by Claude Sonnet 4.5
44 changed files with 2113 additions and 69 deletions
+1
View File
@@ -0,0 +1 @@
client/data/mysql/mysql.sock
+21
View File
@@ -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
+440
View File
@@ -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 <pod-name> -n ljwx-health
```
### 4. Pod 启动失败
**常见原因**:
- ConfigMap/Secret 配置错误
- 数据库连接失败
- 资源不足
**解决方法**:
```bash
# 查看 Pod 详细信息
kubectl describe pod <pod-name> -n ljwx-health
# 查看 Pod 日志
kubectl logs <pod-name> -n ljwx-health --tail=100
# 查看所有容器日志(包括 init 容器)
kubectl logs <pod-name> -n ljwx-health --all-containers=true
# 检查节点资源
kubectl top nodes
kubectl describe node <node-name>
```
### 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 <pod-name> -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 到项目仓库
+302
View File
@@ -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 <<EOF
{
"insecure-registries": ["173.212.254.252:5000", "localhost:5000"]
}
EOF
systemctl restart docker
```
### 步骤 3: 配置 K3s
```bash
# 配置 K3s Registry
mkdir -p /etc/rancher/k3s
cat > /etc/rancher/k3s/registries.yaml <<EOF
mirrors:
"173.212.254.252:5000":
endpoint:
- "http://173.212.254.252:5000"
configs:
"173.212.254.252:5000":
auth:
username: admin
password: admin123
tls:
insecure_skip_verify: true
EOF
systemctl restart k3s
```
### 步骤 4: 创建 Docker Daemon ConfigMap
```bash
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-daemon-config
namespace: github-runner
data:
daemon.json: |
{
"insecure-registries": ["173.212.254.252:5000", "localhost:5000"]
}
EOF
```
### 步骤 5: 更新 RunnerDeployment
```bash
kubectl apply -f /tmp/runner-deployment-updated.yaml
```
### 步骤 6: 验证配置
```bash
# 等待新 runner pod 启动
kubectl get pods -n github-runner -w
# 验证 Docker daemon 配置
NEW_POD=$(kubectl get pods -n github-runner -o name | grep runner | tail -1 | sed 's|pod/||')
kubectl exec -n github-runner $NEW_POD -c docker -- cat /etc/docker/daemon.json
# 测试登录
kubectl exec -n github-runner $NEW_POD -c runner -- \
sh -c 'echo "admin123" | docker login 173.212.254.252:5000 -u admin --password-stdin'
```
## 故障排查
### 问题 1: Pod 无法访问 localhost:5000
**原因**: 在 Pod 内部,localhost 指向 Pod 自己,而不是宿主机。
**解决**: 使用宿主机 IP 地址 `173.212.254.252:5000`
### 问题 2: HTTPS 错误
```
Error: http: server gave HTTP response to HTTPS client
```
**原因**: Docker 默认尝试使用 HTTPS 连接 Registry。
**解决**: 配置 `insecure-registries` 允许 HTTP。
### 问题 3: Runner Pod 重建后配置丢失
**原因**: DinD 容器的配置未持久化。
**解决**:
1. 创建 ConfigMap 包含 daemon.json
2. 在 RunnerDeployment 中使用 `dockerVolumeMounts` 挂载配置
3. 每次 pod 重建时自动应用配置
### 问题 4: 认证失败
```
Error: unauthorized: authentication required
```
**解决**:
```bash
# 在 K3s 中创建镜像拉取密钥
kubectl create secret docker-registry harbor-secret \
--docker-server=173.212.254.252:5000 \
--docker-username=admin \
--docker-password=admin123 \
--namespace=ljwx-health
```
## 监控和维护
### 查看 Registry 镜像
```bash
# 命令行
curl -u admin:admin123 http://173.212.254.252:5000/v2/_catalog
# Web UI
open http://localhost:8888
# 使用脚本
./check-registry.sh
```
### 查看 Runner 状态
```bash
# Runner Deployment
kubectl get runnerdeployment -n github-runner
# Runner Pods
kubectl get pods -n github-runner
# Runner 日志
kubectl logs -n github-runner -l app.kubernetes.io/component=runner -c runner --tail=50 -f
```
### 实时监控 CI/CD
```bash
./watch-cicd.sh
```
## 文件清单
| 文件路径 | 说明 |
|---------|------|
| `harbor/docker-compose.yml` | Registry 服务定义 |
| `harbor/setup.sh` | Registry 部署脚本 |
| `.env.local-registry` | Registry 配置变量 |
| `.github/workflows/build-and-push.yml` | CI/CD workflow |
| `/etc/docker/daemon.json` | 宿主机 Docker 配置 |
| `/etc/rancher/k3s/registries.yaml` | K3s Registry 配置 |
| `docker-daemon-config` ConfigMap | Runner DinD 配置 |
| `brunogao-runner` RunnerDeployment | Runner 部署配置 |
## 安全注意事项
### 生产环境建议
1. **使用 TLS**: 配置 HTTPS Registry
2. **强密码**: 更改默认的 admin/admin123
3. **网络隔离**: 限制 Registry 访问范围
4. **定期备份**: Registry 数据目录 `harbor/data/registry`
### 当前配置(开发环境)
- ⚠️ HTTP Registry(无加密)
- ⚠️ 默认密码
- ✅ 基本认证
- ✅ 配置持久化
## 参考资料
- [Docker Registry Documentation](https://docs.docker.com/registry/)
- [Actions Runner Controller](https://github.com/actions/actions-runner-controller)
- [K3s Private Registry Configuration](https://docs.k3s.io/installation/private-registry)
---
**最后更新**: 2025-12-16
**维护者**: Claude Code with BrunoGaoSZ
+75
View File
@@ -0,0 +1,75 @@
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
name: brunogao-runner
namespace: github-runner
spec:
replicas: 1
template:
spec:
serviceAccountName: github-runner-sa
dockerEnabled: true
# 初始化容器:安装 kubectl
initContainers:
- name: install-kubectl
image: alpine:latest
command:
- sh
- -c
- |
apk add --no-cache curl
cd /custom-bin
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
echo "kubectl installed successfully"
ls -lh /custom-bin/kubectl
volumeMounts:
- name: custom-bin
mountPath: /custom-bin
# Runner 容器的环境变量
env:
- name: DISABLE_RUNNER_UPDATE
value: "true"
- name: PATH
value: "/custom-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
labels:
- k3s
- self-hosted
- linux
- x64
organization: BrunoGaoSZ
resources:
limits:
cpu: "2"
memory: 4Gi
requests:
cpu: "1"
memory: 2Gi
# Docker daemon 配置挂载
dockerVolumeMounts:
- mountPath: /etc/docker/daemon.json
name: docker-daemon-config
subPath: daemon.json
# Runner 容器的挂载
volumeMounts:
- mountPath: /runner/_work
name: work
- mountPath: /custom-bin
name: custom-bin
# 卷定义
volumes:
- name: work
emptyDir: {}
- name: docker-daemon-config
configMap:
name: docker-daemon-config
- name: custom-bin
emptyDir: {}
+51
View File
@@ -0,0 +1,51 @@
# ServiceAccount for runner
apiVersion: v1
kind: ServiceAccount
metadata:
name: github-runner-sa
namespace: github-runner
---
# ClusterRole with necessary permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: github-runner-role
rules:
# Deployment permissions
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Pod permissions
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Service permissions
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# ConfigMap and Secret permissions
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Namespace permissions - 添加 update 和 patch
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list", "create", "update", "patch"]
# Rollout status
- apiGroups: ["apps"]
resources: ["deployments/status"]
verbs: ["get", "watch"]
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: github-runner-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: github-runner-role
subjects:
- kind: ServiceAccount
name: github-runner-sa
namespace: github-runner
+218 -17
View File
@@ -40,13 +40,19 @@ jobs:
- name: Load .env configuration
id: config
run: |
if [ -f .env ]; then
source .env
# 优先使用本地 Registry 配置
if [ -f .env.local-registry ]; then
source .env.local-registry
echo "registry=${REGISTRY}" >> $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|<BASE64_ENCODED_DB_USERNAME>|$(echo -n "${DB_USERNAME}" | base64)|g" ljwx-boot/k8s/secret.yml.template | \
sed "s|<BASE64_ENCODED_DB_PASSWORD>|$(echo -n "${DB_PASSWORD}" | base64)|g" | \
sed "s|<BASE64_ENCODED_REDIS_PASSWORD>|$(echo -n "${REDIS_PASSWORD}" | base64)|g" | \
sed "s|<BASE64_ENCODED_JWT_SECRET>|$(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
@@ -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
+2 -1
View File
@@ -199,4 +199,5 @@ performance-test/results/*.jtl
# ================================
# 网络和其他文件
# ================================
.nfs*
.nfs*
harbor/data/
+1
View File
@@ -0,0 +1 @@
Tue Dec 16 15:31:18 CET 2025
+1
View File
@@ -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
+17
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
resources:
- deployment.yaml
- service.yaml
+10
View File
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
@@ -0,0 +1,5 @@
resources:
- ../../base
images:
- name: nginx
newTag: "1.27"
+20
View File
@@ -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
+18
View File
@@ -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
+45
View File
@@ -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 ""
-1
View File
@@ -1 +0,0 @@
/var/run/mysqld/mysqld.sock
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: dev
imagePullSecrets:
- name: regcred
+4
View File
@@ -0,0 +1,4 @@
resources:
- namespace-dev.yaml
- regcred.yaml
- default-sa.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: dev
+8
View File
@@ -0,0 +1,8 @@
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJoYXJib3IuZXUubGluZ2ppbmd3YW54aWFuZy5jbjo4MDgwIjp7InVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IkhhcmJvcjEyMzQ1IiwiYXV0aCI6IllXUnRhVzQ2U0dGeVltOXlNVEl6TkRVPSJ9fX0=
kind: Secret
metadata:
name: regcred
namespace: dev
type: kubernetes.io/dockerconfigjson
+1
View File
@@ -0,0 +1 @@
Tue Dec 16 09:18:06 CET 2025
+2
View File
@@ -0,0 +1,2 @@
admin:$2y$05$d3UA3lnupxLKfSoVhqHoG.QQriPbPTkdrCKJbVxmxvtXXCGDhwcaW
+46
View File
@@ -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
+71
View File
@@ -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 ""
+8 -18
View File
@@ -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
+9 -2
View File
@@ -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 && \
+7 -7
View File
@@ -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
host: bXlzcWwtc2VydmljZS5sand4LWhlYWx0aC5zdmMuY2x1c3Rlci5sb2NhbA== # mysql-service.ljwx-health.svc.cluster.local
username: cm9vdA== # root
password: MTIzNDU2 # 123456
database: dGVzdA== # test
+10 -9
View File
@@ -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
+1 -1
View File
@@ -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
+5 -1
View File
@@ -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:
+28 -12
View File
@@ -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
+97
View File
@@ -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
+14
View File
@@ -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
+17
View File
@@ -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
+109
View File
@@ -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
+14
View File
@@ -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
+17
View File
@@ -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
+67
View File
@@ -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 ""
+189
View File
@@ -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|<BASE64_ENCODED_DB_USERNAME>|$(echo -n "$DB_USERNAME" | base64)|g" ljwx-boot/k8s/secret.yml.template | \
sed "s|<BASE64_ENCODED_DB_PASSWORD>|$(echo -n "$DB_PASSWORD" | base64)|g" | \
sed "s|<BASE64_ENCODED_REDIS_PASSWORD>|$(echo -n "$REDIS_PASSWORD" | base64)|g" | \
sed "s|<BASE64_ENCODED_JWT_SECRET>|$(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 "========================================="
Executable
+54
View File
@@ -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
+23
View File
@@ -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")"
'