首次提交:初始化项目

This commit is contained in:
fei
2026-02-05 00:11:05 +08:00
commit 26eaf8110b
171 changed files with 17105 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
FROM python:3.11-alpine
# 安装 nginx
RUN apk add --no-cache nginx
# 创建工作目录
WORKDIR /app
# 复制生成器脚本
COPY generator.py /app/
COPY index.html /usr/share/nginx/html/
# 创建 nginx 配置
RUN mkdir -p /run/nginx && \
echo 'server {' > /etc/nginx/http.d/default.conf && \
echo ' listen 80;' >> /etc/nginx/http.d/default.conf && \
echo ' root /usr/share/nginx/html;' >> /etc/nginx/http.d/default.conf && \
echo ' index index.html;' >> /etc/nginx/http.d/default.conf && \
echo ' location / {' >> /etc/nginx/http.d/default.conf && \
echo ' try_files $uri $uri/ =404;' >> /etc/nginx/http.d/default.conf && \
echo ' }' >> /etc/nginx/http.d/default.conf && \
echo '}' >> /etc/nginx/http.d/default.conf
# 启动脚本
RUN echo '#!/bin/sh' > /app/start.sh && \
echo 'nginx' >> /app/start.sh && \
echo 'python3 /app/generator.py' >> /app/start.sh && \
chmod +x /app/start.sh
EXPOSE 80
CMD ["/app/start.sh"]

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# 部署导航服务
echo "部署导航服务..."
kubectl apply -f deployment.yaml
# 等待部署完成
echo "等待导航服务启动..."
kubectl wait --for=condition=ready pod -l app=navigation -n navigation --timeout=300s
# 显示状态
echo ""
echo "导航服务部署完成!"
kubectl get pods -n navigation
kubectl get svc -n navigation
kubectl get ingress -n navigation
echo ""
echo "访问地址: https://dh.u6.net3w.com"

View File

@@ -0,0 +1,179 @@
apiVersion: v1
kind: Namespace
metadata:
name: navigation
---
apiVersion: v1
kind: ConfigMap
metadata:
name: navigation-html
namespace: navigation
data:
index.html: |
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>K3s 服务导航</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container { max-width: 1200px; margin: 0 auto; }
header { text-align: center; color: white; margin-bottom: 40px; padding: 40px 20px; }
h1 { font-size: 3em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); }
.subtitle { font-size: 1.2em; opacity: 0.9; }
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.service-card {
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-decoration: none;
color: inherit;
display: block;
}
.service-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
}
.service-icon { font-size: 3em; margin-bottom: 15px; }
.service-name { font-size: 1.5em; font-weight: bold; margin-bottom: 10px; color: #333; }
.service-url { color: #667eea; font-size: 0.9em; word-break: break-all; }
.service-description { color: #666; margin-top: 10px; font-size: 0.9em; }
footer { text-align: center; color: white; padding: 20px; opacity: 0.8; }
.update-time {
background: rgba(255,255,255,0.2);
padding: 10px 20px;
border-radius: 20px;
display: inline-block;
margin-top: 20px;
}
.stats { background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin-top: 20px; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>🚀 K3s 服务导航</h1>
<p class="subtitle">快速访问您的所有服务</p>
</header>
<div class="services-grid" id="services"></div>
<footer>
<div class="stats" id="stats">加载中...</div>
<div class="update-time">最后更新: <span id="updateTime">-</span></div>
</footer>
</div>
<script>
const services = [
{ name: 'Longhorn', icon: '💾', url: 'https://longhorn.u6.net3w.com', description: '分布式块存储管理' },
{ name: 'Grafana', icon: '📊', url: 'https://grafana.u6.net3w.com', description: '监控数据可视化' },
{ name: 'Prometheus', icon: '📈', url: 'https://prometheus.u6.net3w.com', description: '指标监控系统' },
{ name: 'Alertmanager', icon: '🔔', url: 'https://alertmanager.u6.net3w.com', description: '告警管理系统' },
{ name: 'MinIO S3', icon: '🗄️', url: 'https://s3.u6.net3w.com', description: '对象存储 API' },
{ name: 'MinIO Console', icon: '🎛️', url: 'https://console.s3.u6.net3w.com', description: 'MinIO 管理控制台' },
{ name: '测试页面', icon: '🧪', url: 'https://test.u6.net3w.com', description: '测试服务' }
];
function renderServices() {
const container = document.getElementById('services');
container.innerHTML = services.map(service => `
<a href="${service.url}" class="service-card" target="_blank">
<div class="service-icon">${service.icon}</div>
<div class="service-name">${service.name}</div>
<div class="service-url">${service.url}</div>
<div class="service-description">${service.description}</div>
</a>
`).join('');
document.getElementById('stats').textContent = `共 ${services.length} 个服务`;
}
function updateTime() {
document.getElementById('updateTime').textContent = new Date().toLocaleString('zh-CN');
}
renderServices();
updateTime();
setInterval(updateTime, 60000);
</script>
</body>
</html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: navigation
namespace: navigation
spec:
# replicas 由 KEDA 管理,不设置固定值
selector:
matchLabels:
app: navigation
template:
metadata:
labels:
app: navigation
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: navigation-html
---
apiVersion: v1
kind: Service
metadata:
name: navigation
namespace: navigation
spec:
selector:
app: navigation
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: navigation-ingress
namespace: navigation
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: dh.u6.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: navigation
port:
number: 80

View File

@@ -0,0 +1,265 @@
#!/usr/bin/env python3
import re
import json
import time
from datetime import datetime
from pathlib import Path
# 服务图标映射
SERVICE_ICONS = {
'longhorn': '💾',
'grafana': '📊',
'prometheus': '📈',
'alertmanager': '🔔',
's3': '🗄️',
'console': '🎛️',
'minio': '🗄️',
'dh': '🏠',
'test': '🧪',
'default': '🌐'
}
# 服务描述映射
SERVICE_DESCRIPTIONS = {
'longhorn': '分布式块存储管理',
'grafana': '监控数据可视化',
'prometheus': '指标监控系统',
'alertmanager': '告警管理系统',
's3': '对象存储 API',
'console': 'MinIO 管理控制台',
'minio': 'MinIO 对象存储',
'dh': '服务导航页面',
'test': '测试服务',
'default': 'K3s 服务'
}
def parse_caddyfile(caddyfile_path):
"""解析 Caddyfile 并提取域名"""
services = []
try:
with open(caddyfile_path, 'r', encoding='utf-8') as f:
content = f.read()
# 匹配域名配置块 (domain.com {)
pattern = r'([a-zA-Z0-9.-]+\.u6\.net3w\.com)\s*\{'
matches = re.findall(pattern, content)
for domain in matches:
# 提取子域名前缀
subdomain = domain.split('.')[0]
# 获取图标和描述
icon = SERVICE_ICONS.get(subdomain, SERVICE_ICONS['default'])
description = SERVICE_DESCRIPTIONS.get(subdomain, SERVICE_DESCRIPTIONS['default'])
# 生成服务名称
name = subdomain.capitalize()
if subdomain == 's3':
name = 'MinIO S3'
elif subdomain == 'console':
name = 'MinIO Console'
elif subdomain == 'dh':
name = '导航页面'
services.append({
'name': name,
'icon': icon,
'url': f'https://{domain}',
'description': description
})
# 排序:导航页面放最后
services.sort(key=lambda x: (x['name'] == '导航页面', x['name']))
except Exception as e:
print(f"Error parsing Caddyfile: {e}")
return services
def generate_html(services):
"""生成 HTML 页面"""
services_html = '\n'.join([
f'''
<a href="{service['url']}" class="service-card" target="_blank">
<div class="service-icon">{service['icon']}</div>
<div class="service-name">{service['name']}</div>
<div class="service-url">{service['url']}</div>
<div class="service-description">{service['description']}</div>
</a>
'''
for service in services
])
html_template = f'''<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>K3s 服务导航</title>
<style>
* {{
margin: 0;
padding: 0;
box-sizing: border-box;
}}
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
}}
header {{
text-align: center;
color: white;
margin-bottom: 40px;
padding: 40px 20px;
}}
h1 {{
font-size: 3em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}}
.subtitle {{
font-size: 1.2em;
opacity: 0.9;
}}
.services-grid {{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 40px;
}}
.service-card {{
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-decoration: none;
color: inherit;
display: block;
}}
.service-card:hover {{
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
}}
.service-icon {{
font-size: 3em;
margin-bottom: 15px;
}}
.service-name {{
font-size: 1.5em;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}}
.service-url {{
color: #667eea;
font-size: 0.9em;
word-break: break-all;
}}
.service-description {{
color: #666;
margin-top: 10px;
font-size: 0.9em;
}}
footer {{
text-align: center;
color: white;
padding: 20px;
opacity: 0.8;
}}
.update-time {{
background: rgba(255,255,255,0.2);
padding: 10px 20px;
border-radius: 20px;
display: inline-block;
margin-top: 20px;
}}
.stats {{
background: rgba(255,255,255,0.1);
padding: 15px;
border-radius: 10px;
margin-top: 20px;
}}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🚀 K3s 服务导航</h1>
<p class="subtitle">快速访问您的所有服务</p>
</header>
<div class="services-grid">
{services_html}
</div>
<footer>
<div class="stats">
{len(services)} 个服务
</div>
<div class="update-time">
最后更新: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
</div>
</footer>
</div>
</body>
</html>
'''
return html_template
def main():
caddyfile_path = '/etc/caddy/Caddyfile'
output_path = '/usr/share/nginx/html/index.html'
print(f"Navigation page generator started at {datetime.now()}")
print(f"Caddyfile path: {caddyfile_path}")
print(f"Output path: {output_path}")
while True:
try:
# 解析 Caddyfile
services = parse_caddyfile(caddyfile_path)
print(f"Found {len(services)} services")
# 生成 HTML
html = generate_html(services)
# 写入文件
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
f.write(html)
print(f"Updated navigation page at {datetime.now()}")
except Exception as e:
print(f"Error: {e}")
# 每 5 分钟更新一次
time.sleep(300)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,193 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>K3s 服务导航</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
color: white;
margin-bottom: 40px;
padding: 40px 20px;
}
h1 {
font-size: 3em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.subtitle {
font-size: 1.2em;
opacity: 0.9;
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.service-card {
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
text-decoration: none;
color: inherit;
display: block;
}
.service-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
}
.service-icon {
font-size: 3em;
margin-bottom: 15px;
}
.service-name {
font-size: 1.5em;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.service-url {
color: #667eea;
font-size: 0.9em;
word-break: break-all;
}
.service-description {
color: #666;
margin-top: 10px;
font-size: 0.9em;
}
footer {
text-align: center;
color: white;
padding: 20px;
opacity: 0.8;
}
.update-time {
background: rgba(255,255,255,0.2);
padding: 10px 20px;
border-radius: 20px;
display: inline-block;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🚀 K3s 服务导航</h1>
<p class="subtitle">快速访问您的所有服务</p>
</header>
<div class="services-grid" id="services">
<!-- 服务卡片将由 JavaScript 动态生成 -->
</div>
<footer>
<div class="update-time">
最后更新: <span id="updateTime">加载中...</span>
</div>
</footer>
</div>
<script>
// 服务配置
const services = [
{
name: 'Longhorn',
icon: '💾',
url: 'https://longhorn.u6.net3w.com',
description: '分布式块存储管理'
},
{
name: 'Grafana',
icon: '📊',
url: 'https://grafana.u6.net3w.com',
description: '监控数据可视化'
},
{
name: 'Prometheus',
icon: '📈',
url: 'https://prometheus.u6.net3w.com',
description: '指标监控系统1'
},
{
name: 'Alertmanager',
icon: '🔔',
url: 'https://alertmanager.u6.net3w.com',
description: '告警管理系统'
},
{
name: 'MinIO S3',
icon: '🗄️',
url: 'https://s3.u6.net3w.com',
description: '对象存储 API'
},
{
name: 'MinIO Console',
icon: '🎛️',
url: 'https://console.s3.u6.net3w.com',
description: 'MinIO 管理控制台'
}
];
// 渲染服务卡片
function renderServices() {
const container = document.getElementById('services');
container.innerHTML = services.map(service => `
<a href="${service.url}" class="service-card" target="_blank">
<div class="service-icon">${service.icon}</div>
<div class="service-name">${service.name}</div>
<div class="service-url">${service.url}</div>
<div class="service-description">${service.description}</div>
</a>
`).join('');
}
// 更新时间
function updateTime() {
const now = new Date();
document.getElementById('updateTime').textContent = now.toLocaleString('zh-CN');
}
// 初始化
renderServices();
updateTime();
// 每分钟更新一次时间
setInterval(updateTime, 60000);
</script>
</body>
</html>

View File

@@ -0,0 +1,199 @@
# 导航页面服务
## 功能说明
自动生成的服务导航页面,定时读取 Caddy 配置文件中的域名,动态生成美观的导航网页。
## 访问地址
https://dh.u6.net3w.com
## 特性
- 🔄 **自动更新**: 每 5 分钟自动读取 Caddyfile 并更新导航页面
- 🎨 **美观界面**: 渐变背景、卡片式布局、悬停动画
- 📱 **响应式设计**: 自适应各种屏幕尺寸
- 🚀 **快速访问**: 一键跳转到所有服务
## 架构
```
┌─────────────────────────────────────┐
│ Caddy ConfigMap │
│ (包含所有域名配置) │
└──────────────┬──────────────────────┘
│ 挂载为只读卷
┌─────────────────────────────────────┐
│ Navigation Pod │
│ ┌───────────────────────────────┐ │
│ │ Generator Container │ │
│ │ - 读取 Caddyfile │ │
│ │ - 解析域名 │ │
│ │ - 生成 HTML │ │
│ │ - 每 5 分钟更新 │ │
│ └───────────┬───────────────────┘ │
│ │ 共享卷 │
│ ↓ │
│ ┌───────────────────────────────┐ │
│ │ Nginx Container │ │
│ │ - 提供静态文件服务 │ │
│ │ - 端口 80 │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Traefik Ingress │
│ dh.u6.net3w.com │
└─────────────────────────────────────┘
```
## 部署方式
```bash
bash deploy.sh
```
## 工作原理
1. **Generator 容器**:
- 从 Caddy ConfigMap 挂载 Caddyfile
- Python 脚本解析 Caddyfile提取所有 `*.u6.net3w.com` 域名
- 根据子域名前缀匹配图标和描述
- 生成完整的 HTML 页面
- 写入共享卷 `/usr/share/nginx/html/index.html`
- 每 5 分钟重复一次
2. **Nginx 容器**:
- 从共享卷读取 HTML 文件
- 提供 HTTP 服务(端口 80
3. **Ingress**:
-`dh.u6.net3w.com` 路由到 navigation 服务
## 服务图标映射
| 子域名 | 图标 | 描述 |
|--------|------|------|
| longhorn | 💾 | 分布式块存储管理 |
| grafana | 📊 | 监控数据可视化 |
| prometheus | 📈 | 指标监控系统 |
| alertmanager | 🔔 | 告警管理系统 |
| s3 | 🗄️ | 对象存储 API |
| console | 🎛️ | MinIO 管理控制台 |
| dh | 🏠 | 服务导航页面 |
| test | 🧪 | 测试服务 |
| 其他 | 🌐 | K3s 服务 |
## 添加新服务
当你在 Caddyfile 中添加新的域名配置时,导航页面会在 5 分钟内自动更新。
### 自定义图标和描述
编辑 ConfigMap 中的 `generator.py`,修改以下字典:
```python
SERVICE_ICONS = {
'newservice': '🎯', # 添加新服务的图标
# ...
}
SERVICE_DESCRIPTIONS = {
'newservice': '新服务的描述', # 添加新服务的描述
# ...
}
```
然后重新部署:
```bash
kubectl apply -f deployment.yaml
kubectl rollout restart deployment navigation -n navigation
```
## 手动触发更新
如果需要立即更新导航页面:
```bash
# 重启 generator 容器
kubectl rollout restart deployment navigation -n navigation
```
## 查看日志
```bash
# 查看 generator 日志
kubectl logs -n navigation -l app=navigation -c generator -f
# 查看 nginx 日志
kubectl logs -n navigation -l app=navigation -c nginx -f
```
## 自定义样式
如果需要修改页面样式,编辑 ConfigMap 中的 HTML 模板部分,然后重新部署。
## 故障排查
### 页面无法访问
```bash
# 检查 Pod 状态
kubectl get pods -n navigation
# 检查 Ingress
kubectl get ingress -n navigation
# 检查服务
kubectl get svc -n navigation
```
### 页面未更新
```bash
# 查看 generator 日志
kubectl logs -n navigation -l app=navigation -c generator
# 检查 Caddy ConfigMap
kubectl get configmap caddy-config -n default -o yaml
# 手动触发更新
kubectl rollout restart deployment navigation -n navigation
```
### Caddyfile 无法读取
确保 navigation 命名空间可以访问 default 命名空间的 ConfigMap
```bash
# 检查 ConfigMap 挂载
kubectl describe pod -n navigation -l app=navigation
```
## 性能优化
- **更新频率**: 默认 5 分钟,可在 `generator.py` 中修改 `time.sleep(300)`
- **缓存**: Nginx 自动缓存静态文件
- **资源限制**: 可根据需要添加资源限制
## 卸载
```bash
kubectl delete namespace navigation
```
## 未来改进
- [ ] 添加服务健康状态检查
- [ ] 支持服务分组
- [ ] 添加搜索功能
- [ ] 支持深色模式
- [ ] 添加访问统计
---
**导航页面让您的服务一目了然!** 🚀