This commit is contained in:
DullJZ
2025-09-13 22:30:40 +08:00
parent 4268f6238d
commit f214b003fd
34 changed files with 2217 additions and 10 deletions

231
deploy/README.md Normal file
View File

@@ -0,0 +1,231 @@
# S3 Balance 部署指南
本目录包含 S3 Balance 服务的各种部署方案,从简单的 Docker Compose 到生产级的 Kubernetes 部署。
## 目录结构
```
deploy/
├── docker/ # Docker 和 Docker Compose 部署
│ ├── docker-compose.yml # Docker Compose 配置
│ ├── Dockerfile # Docker 镜像构建文件
│ ├── start-monitoring.sh # 一键启动脚本
│ └── config.docker.yaml # Docker 环境配置
├── kubernetes/ # Kubernetes 原生部署
│ ├── namespace.yaml # 命名空间
│ ├── configmap.yaml # 配置映射
│ ├── deployment.yaml # 部署配置
│ ├── service.yaml # 服务暴露
│ └── ingress.yaml # 入口配置
├── helm/ # HELM Chart 部署
│ └── s3-balance/ # HELM Chart 目录
└── monitoring/ # 监控系统配置
├── prometheus.yml # Prometheus 配置
├── s3_balance_alerts.yml # 告警规则
└── grafana/ # Grafana 配置和仪表板
```
## 🐳 Docker Compose 部署(推荐开发环境)
### 快速启动(含完整监控栈)
```bash
cd deploy/docker
./start-monitoring.sh
```
### 手动启动
```bash
cd deploy/docker
docker-compose up -d
```
### 访问地址
- S3 Balance API: http://localhost:8080
- Grafana 面板: http://localhost:3000 (admin/admin123)
- Prometheus: http://localhost:9090
- 监控指标: http://localhost:8080/metrics
### 配置文件
编辑 `config.docker.yaml` 来自定义:
- 存储桶配置
- 负载均衡策略
- 监控指标设置
## ☸️ Kubernetes 部署(推荐生产环境)
### 基础部署
```bash
cd deploy/kubernetes
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
```
### 高可用部署
```bash
kubectl apply -f hpa.yaml # 水平自动扩缩容
kubectl apply -f pdb.yaml # Pod 中断预算
```
### 外部访问
```bash
kubectl apply -f ingress.yaml
```
### 配置说明
- 使用 ConfigMap 管理配置文件
- 支持 HPA 自动扩缩容
- 集成集群 DNS 服务发现
- 支持多环境配置dev/test/prod
## 📊 HELM Chart 部署(推荐企业环境)
### 安装 Chart
```bash
cd deploy/helm
helm install s3-balance ./s3-balance
```
### 自定义参数
```bash
helm install s3-balance ./s3-balance -f custom-values.yaml
```
### 升级版本
```bash
helm upgrade s3-balance ./s3-balance
```
### HELM 优势
- 参数化配置
- 多环境管理
- 版本控制
- 依赖管理
## 🔧 监控集成
所有的部署方案都集成了完整的监控系统:
### Prometheus 指标
- S3 Balance 业务指标
- 系统资源指标
- 自定义应用指标
### Grafana 仪表板
- 服务概览面板
- 性能分析面板
- 容量监控面板
- 错误率跟踪面板
### 告警规则
- 存储桶健康状态告警
- 高错误率告警
- 高延迟告警
- 容量使用率告警
## 🚨 部署要求
### 系统要求
- Docker & Docker Compose开发环境
- Kubernetes 1.19+(生产环境)
- HELM 3.0+(企业环境)
### 资源要求
- CPU: 0.5-2 core
- Memory: 512MB-2GB
- Storage: 1GB-100GB根据使用情况
- Network: 内网访问存储桶
### 网络要求
- 访问后端 S3 存储桶
- Prometheus Pushgateway可选
- 外部监控系统(可选)
## 📋 环境变量
### S3 Balance
- `CONFIG_FILE`: 配置文件路径
- `LOG_LEVEL`: 日志级别 (debug/info/warn/error)
- `TZ`: 时区设置
### Prometheus
- `STORAGE_TSDB_RETENTION_TIME`: 数据保留时间
- `WEB_ENABLE_LIFECYCLE`: 启用生命周期管理
### Grafana
- `GF_SECURITY_ADMIN_USER`: 管理员用户名
- `GF_SECURITY_ADMIN_PASSWORD`: 管理员密码
- `GF_USERS_ALLOW_SIGN_UP`: 是否允许注册
## 🎯 最佳实践
### 生产环境建议
1. **资源限制**: 设置合理的CPU/内存限制
2. **健康检查**: 配置完整的探针检查
3. **持久化**: 重要数据使用持久化存储
4. **备份**: 定期备份配置和数据库
5. **监控**: 集成现有监控系统
### 高可用建议
1. **多副本**: 部署多个实例
2. **负载均衡**: 集群内负载均衡
3. **自动扩缩容**: 基于负载自动扩缩容
4. **多区域**: 跨可用区部署
### 安全建议
1. **镜像安全**: 使用官方基础镜像
2. **访问控制**: 配置RBAC权限
3. **网络隔离**: 使用网络策略
4. **敏感信息**: 使用Secret管理凭证
## 🔧 故障排除
### 常见问题
**Pod 启动失败**
```bash
kubectl describe pod <pod-name>
kubectl logs <pod-name>
```
**配置错误**
```bash
kubectl get configmap s3-balance-config -o yaml
```
**服务无法访问**
```bash
kubectl get service s3-balance-service
kubectl get ingress s3-balance-ingress
```
### 性能调优
- 调整负载均衡策略
- 优化数据库连接池
- 配置合理的缓存策略
- 监控资源使用情况
## 🔗 集成外部系统
### 已有 Prometheus
在外部 Prometheus 配置中添加:
```yaml
scrape_configs:
- job_name: 's3-balance'
static_configs:
- targets: ['s3-balance-service.default.svc.cluster.local:8080']
```
### 现有监控系统
- 通过 exporters 暴露指标
- 使用统一的日志格式
- 集成告警通知渠道
## 📖 参考文献
- [Docker Compose 文档](https://docs.docker.com/compose/)
- [Kubernetes 官方文档](https://kubernetes.io/docs/)
- [HELM 官方文档](https://helm.sh/docs/)
- [Prometheus 最佳实践](https://prometheus.io/docs/practices/)
- [Grafana 文档](https://grafana.com/docs/)

45
deploy/docker/Dockerfile Normal file
View File

@@ -0,0 +1,45 @@
# 多阶段构建用于减小镜像大小
FROM golang:1.24-alpine AS builder
# 安装构建依赖包括CGO支持的sqlite3
RUN apk add --no-cache gcc musl-dev sqlite-dev git ca-certificates tzdata
# 设置工作目录
WORKDIR /build
# 复制 Go 模块文件
COPY go.mod go.sum ./
RUN go mod download && go mod tidy
# 复制源码
COPY . .
# 构建应用启用CGO
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build \
-ldflags='-w -s -X main.version=$(git describe --tags --always)' \
-o s3-balance \
cmd/s3-balance/main.go
# 运行时镜像
FROM alpine:latest
# 安装运行时依赖包括sqlite库
RUN apk --no-cache add ca-certificates tzdata sqlite-libs
# 设置时区
ENV TZ=Asia/Shanghai
WORKDIR /app
# 复制二进制文件
COPY --from=builder /build/s3-balance /app/s3-balance
# 创建配置和数据目录
RUN mkdir -p /app/config /app/data
# 暴露端口
EXPOSE 8080
# 运行应用
ENTRYPOINT ["/app/s3-balance"]
CMD ["-config", "/app/config/config.yaml"]

View File

@@ -0,0 +1,62 @@
# Docker 配置 - 启用监控
server:
host: "0.0.0.0"
port: 8080
read_timeout: 30s
write_timeout: 30s
idle_timeout: 60s
database:
type: "sqlite"
dsn: "data/s3-balance.db"
max_open_conns: 25
max_idle_conns: 5
conn_max_lifetime: 300
log_level: "info"
auto_migrate: true
# 示例存储桶配置 - 请根据实际情况修改
buckets:
- name: "minio-bucket"
endpoint: "http://minio:9000"
region: "us-east-1"
access_key_id: "minioadmin"
secret_access_key: "minioadmin"
max_size: "1GB"
weight: 10
enabled: true
use_ssl: false
path_style: true
virtual: false
- name: "user-data"
endpoint: ""
region: "us-east-1"
access_key_id: ""
secret_access_key: ""
max_size: "1GB"
weight: 10
enabled: true
use_ssl: true
path_style: false
virtual: true
balancer:
strategy: "least-space"
health_check_period: 30s
update_stats_period: 60s
retry_attempts: 3
retry_delay: 1s
# 监控指标 - 启用状态
metrics:
enabled: true
path: "/metrics"
port: 9090
s3api:
access_key: "AKIAIOSFODNN7EXAMPLE"
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
virtual_host: false
proxy_mode: false
auth_required: false

View File

@@ -0,0 +1,90 @@
version: '3.8'
services:
# S3 Balance 服务
s3-balance:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile
container_name: s3-balance
ports:
- "8080:8080"
volumes:
- ../docker/config.docker.yaml:/app/config/config.yaml
- ../docker/data:/app/data
environment:
- TZ=Asia/Shanghai
networks:
- monitoring
restart: unless-stopped
command: ["/app/s3-balance", "-config", "/app/config/config.yaml"]
# Prometheus 监控
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ../monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- ../monitoring/s3_balance_alerts.yml:/etc/prometheus/s3_balance_alerts.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
networks:
- monitoring
restart: unless-stopped
# Grafana 可视化
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ../monitoring/grafana/provisioning:/etc/grafana/provisioning
- ../monitoring/grafana/dashboards:/var/lib/grafana/dashboards
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_USERS_ALLOW_SIGN_UP=false
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
networks:
- monitoring
restart: unless-stopped
depends_on:
- prometheus
# Node Exporter (可选,监控宿主机)
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
networks:
- monitoring
restart: unless-stopped
volumes:
prometheus_data:
driver: local
grafana_data:
driver: local
networks:
monitoring:
driver: bridge

View File

@@ -0,0 +1,72 @@
#!/bin/bash
# S3 Balance + 监控栈 一键启动(修复版)
set -e
echo "🚀 正在启动 S3 Balance + 监控栈修复CGO版本..."
# 检查 Docker 和 Docker Compose
if ! command -v docker &> /dev/null; then
echo "❌ Docker 未安装,请先安装 Docker"
exit 1
fi
if ! command -v docker-compose &> /dev/null; then
echo "❌ Docker Compose 未安装,请先安装 Docker Compose"
exit 1
fi
# 创建必要目录
echo "📁 创建必要目录..."
mkdir -p data grafana/provisioning/datasources grafana/provisioning/dashboards
# 构建 S3 Balance 镜像(使用固定 Go 版本)
echo "🔨 构建 S3 Balance 镜像..."
docker build -t s3-balance:latest -f ../docker/Dockerfile ../..
# 停止已有容器(如果存在)
echo "🛑 清理已有容器..."
docker-compose down 2>/dev/null || true
# 启动服务
echo "🐳 启动 Docker 容器..."
docker-compose up -d
# 等待服务启动
echo "⏳ 等待服务启动..."
sleep 25
# 检查服务状态
echo "🔍 检查服务状态..."
if docker-compose ps | grep -q "Up"; then
echo ""
echo "✅ 服务启动完成!"
else
echo ""
echo "❌ 服务启动可能失败,检查日志:"
echo "docker-compose logs"
exit 1
fi
# 输出访问信息
echo ""
echo "🔗 访问地址:"
echo " 📊 Grafana 面板: http://localhost:3000 (用户名: admin, 密码: admin123)"
echo " 🔥 Prometheus: http://localhost:9090"
echo " 📈 指标端点: http://localhost:8080/metrics"
echo " 🐳 Node 指标: http://localhost:9100/metrics"
echo ""
echo "🔧 管理命令:"
echo " docker-compose logs -f s3-balance # 查看 S3 Balance 日志"
echo " docker-compose logs -f prometheus # 查看 Prometheus 日志"
echo " docker-compose logs -f grafana # 查看 Grafana 日志"
echo " docker-compose down # 停止所有服务"
echo " docker-compose restart s3-balance # 重启 S3 Balance"
echo ""
echo "📊 指标查询示例:"
echo " - 存储桶健康: s3_balance_bucket_healthy"
echo " - QPS: rate(s3_balance_s3_operations_total[1m])"
echo " - 延迟: histogram_quantile(0.95, s3_balance_s3_operation_duration_seconds_bucket)"
echo ""
echo "🎉 享受完整的 S3 Balance 监控体验!"

View File

@@ -0,0 +1,9 @@
apiVersion: v2
name: s3-balance-servicemonitor
description: A Helm chart for ServiceMonitor to monitor S3 Balance
type: application
version: 0.1.0
appVersion: "1.0.0"
# Usage:
# helm install s3-balance-monitoring ./s3-balance-servicemonitor -n monitoring

View File

@@ -0,0 +1,20 @@
apiVersion: v2
name: s3-balance
description: A Helm chart for S3 Balance load balancing service
type: application
version: 0.1.0
appVersion: "1.0.0"
icon: https://helm.sh/img/helm.svg
keywords:
- s3
- storage
- load-balancer
- object-storage
- minio
- aws-s3
home: https://github.com/example/s3-balance
sources:
- https://github.com/example/s3-balance
maintainers:
- name: S3 Balance Team
email: admin@example.com

View File

@@ -0,0 +1,155 @@
# HELM Values 生产环境配置
replicaCount: 3
image:
repository: your-registry.com/s3-balance
tag: v1.0.0
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 8080
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: s3-balance.your-domain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: s3-balance-tls
hosts:
- s3-balance.your-domain.com
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 200m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
config:
configYaml: |
# 生产环境配置
server:
host: "0.0.0.0"
port: 8080
read_timeout: 30s
write_timeout: 30s
idle_timeout: 60s
database:
type: "sqlite"
dsn: "/app/data/s3-balance.db"
max_open_conns: 100
max_idle_conns: 20
conn_max_lifetime: 600
log_level: "warn"
auto_migrate: true
buckets:
# 生产存储桶配置
- name: "prod-minio"
endpoint: "https://your-minio-endpoint.com"
region: "us-east-1"
access_key_id: "CHANGEME"
secret_access_key: "CHANGEME"
max_size: "500GB"
weight: 10
enabled: true
use_ssl: true
path_style: true
virtual: false
balancer:
strategy: "least-space"
health_check_period: 30s
update_stats_period: 60s
metrics:
enabled: true
path: "/metrics"
port: 8080
s3api:
access_key: "your-access-key"
secret_key: "your-secret-key"
virtual_host: false
proxy_mode: false
auth_required: true
persistence:
enabled: true
storageClass: "fast-ssd"
accessMode: ReadWriteOnce
size: 20Gi
monitoring:
enabled: true
serviceMonitor:
enabled: true
namespace: monitoring
interval: 30s
scrapeTimeout: 10s
labels:
release: prometheus
prometheusRule:
enabled: true
namespace: monitoring
labels:
release: prometheus
rules:
- alert: BucketDown
expr: up{job="s3-balance"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "S3 Balance 服务不可用"
description: "S3 Balance 服务已经连续5分钟不可用"
- alert: HighErrorRate
expr: rate(s3_balance_s3_operations_total{status="error"}[5m]) > 0.05
for: 2m
labels:
severity: warning
annotations:
summary: "高错误率告警"
description: "错误率超过5%,操作类型: {{ $labels.operation }}"
nodeSelector:
disktype: ssd
tolerations:
- key: "highcpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- s3-balance
topologyKey: kubernetes.io/hostname

View File

@@ -0,0 +1,34 @@
{{/*
Return the proper s3-balance image name
*/}}
{{- define "s3-balance.image" -}}
{{- $registryName := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
{{- printf "%s:%s" $registryName $tag -}}
{{- end -}}
{{/*
Return the chart name
*/}}
{{- define "s3-balance.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Return the full name
*/}}
{{- define "s3-balance.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{/*
Return the service account name
*/}}
{{- define "s3-balance.serviceAccountName" -}}
{{- default (include "s3-balance.fullname" .) .Values.serviceAccount.name -}}
{{- end -}}

View File

@@ -0,0 +1,26 @@
{{/*
Labels
*/}}
{{- define "s3-balance.labels" -}}
helm.sh/chart: {{ include "s3-balance.chart" . }}
{{ include "s3-balance.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Selector labels
*/}}
{{- define "s3-balance.selectorLabels" -}}
app.kubernetes.io/name: {{ include "s3-balance.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{/*
Chart name and version as used by the chart label.
*/}}
{{- define "s3-balance.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end -}}

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "s3-balance.fullname" . }}-config
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
data:
config.yaml: |{{ .Values.config.configYaml | nindent 4 }}

View File

@@ -0,0 +1,93 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "s3-balance.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
{{- include "s3-balance.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "s3-balance.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "s3-balance.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
image: {{ include "s3-balance.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: TZ
value: "Asia/Shanghai"
livenessProbe:
httpGet:
path: /metrics
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /metrics
port: http
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
{{- toYaml .Values.resources | nindent 10 }}
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
- name: data
mountPath: /app/data
volumes:
- name: config
configMap:
name: {{ include "s3-balance.fullname" . }}-config
- name: data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "s3-balance.fullname" . }}-data
{{- else }}
emptyDir: {}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,33 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "s3-balance.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "s3-balance.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,32 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "s3-balance.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "s3-balance.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,21 @@
{{- if .Values.monitoring.enabled }}
{{- if .Values.prometheusRule.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: {{ include "s3-balance.fullname" . }}
namespace: {{ .Values.prometheusRule.namespace | default .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
{{- with .Values.prometheusRule.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
groups:
- name: {{ include "s3-balance.name" . }}
rules:
{{- with .Values.prometheusRule.rules }}
{{ toYaml . | indent 6 }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,21 @@
{{/*
Persistent Volume Claim template
*/}}
{{- if .Values.persistence.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "s3-balance.fullname" . }}-data
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
spec:
accessModes:
- {{ .Values.persistence.accessMode }}
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,41 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "s3-balance.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "s3-balance.selectorLabels" . | nindent 4 }}- name: metrics
port: 8080
targetPort: http
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "s3-balance.fullname" . }}-metrics
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
service-type: metrics
spec:
type: ClusterIP
ports:
- name: metrics
port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
selector:
{{- include "s3-balance.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "s3-balance.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}

View File

@@ -0,0 +1,24 @@
{{- if .Values.monitoring.enabled }}
{{- if .Values.monitoring.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "s3-balance.fullname" . }}
namespace: {{ .Values.monitoring.serviceMonitor.namespace | default .Release.Namespace }}
labels:
{{- include "s3-balance.labels" . | nindent 4 }}
{{- with .Values.monitoring.serviceMonitor.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
selector:
matchLabels:
{{- include "s3-balance.selectorLabels" . | nindent 6 }}
service-type: metrics
endpoints:
- port: metrics
interval: {{ .Values.monitoring.serviceMonitor.interval }}
scrapeTimeout: {{ .Values.monitoring.serviceMonitor.scrapeTimeout }}
path: /metrics
{{- end }}
{{- end }}

View File

@@ -0,0 +1,174 @@
# 默认值
replicaCount: 3
image:
repository: s3-balance
tag: latest
pullPolicy: IfNotPresent
imagePullSecrets: [ ]
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: { }
name: ""
podAnnotations: { }
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
service:
type: ClusterIP
port: 8080
targetPort: 8080
annotations: { }
ingress:
enabled: false
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/rewrite-target: /
hosts:
- host: s3-balance.local
paths:
- path: /
pathType: Prefix
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
nodeSelector: { }
tolerations: [ ]
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- s3-balance
topologyKey: kubernetes.io/hostname
config:
# S3 Balance 配置
configYaml: |
# HELM 配置的 S3 Balance
server:
host: "0.0.0.0"
port: 8080
read_timeout: 30s
write_timeout: 30s
idle_timeout: 60s
database:
type: "sqlite"
dsn: "/app/data/s3-balance.db"
max_open_conns: 50
max_idle_conns: 10
conn_max_lifetime: 600
log_level: "info"
auto_migrate: true
buckets:
# 示例配置 - 请根据实际情况修改
- name: "minio-bucket"
endpoint: "http://minio:9000"
region: "us-east-1"
access_key_id: "minioadmin"
secret_access_key: "minioadmin"
max_size: "50GB"
weight: 10
enabled: true
use_ssl: false
path_style: true
virtual: false
balancer:
strategy: "least-space"
health_check_period: 30s
update_stats_period: 60s
metrics:
enabled: true
path: "/metrics"
port: 8080
s3api:
access_key: "AKIAIOSFODNN7EXAMPLE"
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
virtual_host: false
proxy_mode: false
auth_required: false
# 监控配置
monitoring:
enabled: true
serviceMonitor:
enabled: true
namespace: ""
interval: 30s
scrapeTimeout: 10s
labels: { }
# PrometheusRule 配置
prometheusRule:
enabled: true
namespace: ""
labels: { }
rules:
- alert: HighErrorRate
expr: rate(s3_balance_s3_operations_total{status="error"}[5m]) > 0.1
for: 2m
labels:
severity: critical
annotations:
summary: "S3 Balance 错误率过高"
description: "S3 Balance 错误率超过10%: {{ $labels.operation }}"
- alert: HighLatency
expr: histogram_quantile(0.95, rate(s3_balance_s3_operation_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "操作延迟过高"
description: "操作延迟 P95 超过1秒"
# 持久化存储
persistence:
enabled: true
storageClass: ""
accessMode: ReadWriteOnce
size: 5Gi

View File

@@ -0,0 +1,56 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: s3-balance-config
namespace: s3-balance
labels:
app: s3-balance
data:
config.yaml: |
# Kubernetes 生产环境配置
server:
host: "0.0.0.0"
port: 8080
read_timeout: 30s
write_timeout: 30s
idle_timeout: 60s
database:
type: "sqlite"
dsn: "/app/data/s3-balance.db"
max_open_conns: 50
max_idle_conns: 10
conn_max_lifetime: 600
log_level: "info"
auto_migrate: true
buckets:
# 示例配置 - 请替换为实际存储桶
- name: "prod-bucket-1"
endpoint: "http://minio-svc:9000"
region: "us-east-1"
access_key_id: "minioadmin"
secret_access_key: "minioadmin"
max_size: "100GB"
weight: 10
enabled: true
use_ssl: false
path_style: true
virtual: false
balancer:
strategy: "least-space"
health_check_period: 30s
update_stats_period: 60s
metrics:
enabled: true
path: "/metrics"
port: 8080
s3api:
access_key: "AKIAIOSFODNN7EXAMPLE"
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
virtual_host: false
proxy_mode: false
auth_required: false

View File

@@ -0,0 +1,92 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: s3-balance-deployment
namespace: s3-balance
labels:
app: s3-balance
tier: backend
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: s3-balance
template:
metadata:
labels:
app: s3-balance
tier: backend
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- s3-balance
topologyKey: kubernetes.io/hostname
containers:
- name: s3-balance
image: s3-balance:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
env:
- name: TZ
value: "Asia/Shanghai"
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
- name: data
mountPath: /app/data
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumes:
- name: config
configMap:
name: s3-balance-config
- name: data
emptyDir: {}
restartPolicy: Always
terminationGracePeriodSeconds: 30

View File

@@ -0,0 +1,40 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: s3-balance-hpa
namespace: s3-balance
labels:
app: s3-balance
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: s3-balance-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60

View File

@@ -0,0 +1,22 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: s3-balance-ingress
namespace: s3-balance
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
rules:
- host: s3-balance.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: s3-balance-service
port:
number: 8080

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: s3-balance
labels:
app: s3-balance
tier: backend

View File

@@ -0,0 +1,34 @@
apiVersion: v1
kind: Service
metadata:
name: s3-balance-service
namespace: s3-balance
labels:
app: s3-balance
spec:
selector:
app: s3-balance
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: http
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: s3-balance-metrics-service
namespace: s3-balance
labels:
app: s3-balance
service-type: metrics
spec:
selector:
app: s3-balance
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: metrics
type: ClusterIP

View File

@@ -0,0 +1,279 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"description": "S3 Balance 负载均衡服务监控 Dashboard",
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 1,
"links": [],
"panels": [
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 10,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"expr": "rate(s3_balance_s3_operations_total[5m])",
"legendFormat": "{{operation}} - {{bucket}} - {{status}}",
"refId": "A"
}
],
"title": "QPS (操作/秒)",
"type": "timeseries"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [
{
"options": {
"0": {
"color": "red",
"index": 0,
"text": "Unhealthy"
},
"1": {
"color": "green",
"index": 1,
"text": "Healthy"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"colorMode": "background",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"text": {},
"textMode": "auto"
},
"pluginVersion": "8.0.0",
"targets": [
{
"expr": "s3_balance_bucket_healthy",
"legendFormat": "{{bucket}}",
"refId": "A"
}
],
"title": "存储桶健康状态",
"type": "stat"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"calcs": ["avg"],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"expr": "histogram_quantile(0.50, s3_balance_s3_operation_duration_seconds_bucket)",
"legendFormat": "P50 - {{operation}}",
"refId": "A"
},
{
"expr": "histogram_quantile(0.95, s3_balance_s3_operation_duration_seconds_bucket)",
"legendFormat": "P95 - {{operation}}",
"refId": "B"
},
{
"expr": "histogram_quantile(0.99, s3_balance_s3_operation_duration_seconds_bucket)",
"legendFormat": "P99 - {{operation}}",
"refId": "C"
}
],
"title": "操作延迟 (秒)",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 30,
"style": "dark",
"tags": ["s3-balance", "monitoring"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "S3 Balance 监控面板",
"uid": "s3-balance-monitoring",
"version": 1
}

View File

@@ -0,0 +1,13 @@
# Grafana 仪表板提供者配置
apiVersion: 1
providers:
- name: 'S3 Balance'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
allowUiUpdates: true
options:
path: /var/lib/grafana/dashboards

View File

@@ -0,0 +1,11 @@
# Grafana 数据源配置
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
jsonData:
timeInterval: 5s

View File

@@ -0,0 +1,30 @@
# Prometheus 配置 - 抓取 S3 Balance 指标
global:
scrape_interval: 15s # 抓取间隔
evaluation_interval: 15s # 评估间隔
# 告警规则文件
rule_files:
- "s3_balance_alerts.yml"
# 抓取配置
scrape_configs:
# S3 Balance 服务指标
- job_name: 's3-balance'
static_configs:
- targets: ['s3-balance:8080']
metrics_path: '/metrics'
scrape_interval: 15s
scrape_timeout: 10s
# Node Exporter (系统指标)
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
scrape_interval: 15s
# 告警配置
alerting:
alertmanagers:
- static_configs:
- targets: [] # 可以添加 AlertManager 地址

View File

@@ -0,0 +1,73 @@
# S3 Balance 告警规则
groups:
- name: s3_balance_alerts
rules:
# 存储桶不健康告警
- alert: BucketUnhealthy
expr: s3_balance_bucket_healthy == 0
for: 5m
labels:
severity: warning
annotations:
summary: "存储桶 {{ $labels.bucket }} 不健康"
description: "存储桶 {{ $labels.bucket }} ({{ $labels.endpoint }}) 已经连续5分钟不可用"
# 错误率过高告警
- alert: HighErrorRate
expr: rate(s3_balance_s3_operations_total{status="error"}[5m]) > 0.1
for: 2m
labels:
severity: critical
annotations:
summary: "存储桶 {{ $labels.bucket }} 错误率过高"
description: "存储桶 {{ $labels.bucket }} 错误率超过10%,操作: {{ $labels.operation }}"
# 操作延迟过高告警
- alert: HighLatency
expr: histogram_quantile(0.95, rate(s3_balance_s3_operation_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.operation }} 操作延迟过高"
description: "{{ $labels.operation }} 操作95%分位数延迟超过1秒"
# 存储桶使用率告警
- alert: BucketUsageHigh
expr: s3_balance_bucket_usage_bytes / s3_balance_bucket_capacity_bytes > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "存储桶 {{ $labels.bucket }} 使用率过高"
description: "存储桶 {{ $labels.bucket }} 使用率超过90%"
# 存储桶满告警
- alert: BucketFull
expr: s3_balance_bucket_usage_bytes / s3_balance_bucket_capacity_bytes >= 0.95
for: 1m
labels:
severity: critical
annotations:
summary: "存储桶 {{ $labels.bucket }} 即将满了"
description: "存储桶 {{ $labels.bucket }} 使用率达到95%"
# QPS过低告警 (可能服务异常)
- alert: LowQPS
expr: rate(s3_balance_s3_operations_total[5m]) == 0
for: 10m
labels:
severity: warning
annotations:
summary: "QPS异常低"
description: "系统QPS为0可能服务异常无请求"
# 负载均衡失败告警
- alert: BalancerDecisionFailure
expr: increase(s3_balance_balancer_decisions_total[5m]) == 0
for: 5m
labels:
severity: warning
annotations:
summary: "负载均衡决策异常"
description: "5分钟内没有负载均衡决策可能存储桶全部不可用"

309
deploy/start.sh Executable file
View File

@@ -0,0 +1,309 @@
#!/bin/bash
# S3 Balance 快速部署脚本
# 支持 Docker、Docker Compose、Kubernetes、HELM 四种部署方式
set -e
# 显示帮助信息
show_help() {
echo "S3 Balance 快速部署脚本"
echo ""
echo "用法: ./start.sh [选项]"
echo ""
echo "选项:"
echo " -m, --mode MODE 部署模式: docker|compose|kubernetes|helm"
echo " -e, --env ENV 环境: dev|test|prod (默认: dev)"
echo " -b, --build 构建镜像"
echo " -p, --port PORT 服务端口 (默认: 8080)"
echo " -h, --help 显示帮助信息"
echo ""
echo "示例:"
echo " ./start.sh -m compose -e prod"
echo " ./start.sh -m kubernetes --build"
echo " ./start.sh -m helm -e test"
}
# 默认参数
MODE="compose"
ENV="dev"
BUILD=false
PORT="8080"
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-m|--mode)
MODE="$2"
shift 2
;;
-e|--env)
ENV="$2"
shift 2
;;
-b|--build)
BUILD=true
shift
;;
-p|--port)
PORT="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知选项: $1"
show_help
exit 1
;;
esac
done
# 输出配置信息
echo "🚀 S3 Balance 快速部署开始..."
echo "📋 配置信息:"
echo " 部署模式: $MODE"
echo " 环境: $ENV"
echo " 构建镜像: $BUILD"
echo " 服务端口: $PORT"
echo ""
# 检查必要工具
check_requirements() {
local tool=$1
local tool_name=$2
if ! command -v $tool &> /dev/null; then
echo "$tool_name 未安装"
return 1
fi
echo "$tool_name 已安装"
}
# Docker 模式部署
deploy_docker() {
echo "📦 Docker 单容器部署..."
check_requirements docker "Docker" || exit 1
if [ "$BUILD" = true ]; then
echo "🔨 构建 Docker 镜像..."
docker build -t s3-balance:$MODE-$ENV -f deploy/docker/Dockerfile .
fi
echo "🚀 启动容器..."
docker run -d \
--name s3-balance-$ENV \
-p $PORT:8080 \
-v $(pwd)/deploy/docker/config.docker.yaml:/app/config/config.yaml \
-v s3-balance-data:/app/data \
-e TZ=Asia/Shanghai \
--restart unless-stopped \
s3-balance:$MODE-$ENV
echo "✅ Docker 容器已启动"
}
# Docker Compose 模式部署
deploy_compose() {
echo "🐳 Docker Compose 部署..."
check_requirements docker "Docker" || exit 1
check_requirements docker-compose "Docker Compose" || exit 1
if [ "$BUILD" = true ]; then
echo "🔨 构建 Docker 镜像..."
cd deploy/docker
docker build -t s3-balance:$MODE-$ENV -f Dockerfile ../..
fi
echo "🚀 启动服务栈..."
cd deploy/docker
# 修改 docker-compose.yml 中的端口
if [ "$PORT" != "8080" ]; then
sed -i.bak "s/8080:8080/$PORT:8080/" docker-compose.yml
fi
docker-compose up -d
# 恢复配置文件
if [ "$PORT" != "8080" ]; then
mv docker-compose.yml.bak docker-compose.yml
fi
cd ../..
echo "✅ Docker Compose 服务已启动"
}
# Kubernetes 模式部署
deploy_kubernetes() {
echo "☸️ Kubernetes 部署..."
check_requirements kubectl "kubectl" || exit 1
check_requirements kubectl "Kubernetes 集群连接" || exit 1
echo "🚀 应用 Kubernetes 配置..."
cd deploy/kubernetes
# 替换环境变量
if [ "$ENV" != "dev" ]; then
# 根据环境替换镜像标签
sed -i.bak 's/image: s3-balance:latest/image: s3-balance:'"$ENV"'/' deployment.yaml
fi
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
# 如果是生产环境,应用 HPA
if [ "$ENV" = "prod" ]; then
kubectl apply -f hpa.yaml
echo "📊 已启用水平自动扩缩容"
fi
# 恢复配置文件
if [ -f deployment.yaml.bak ]; then
mv deployment.yaml.bak deployment.yaml
fi
cd ../..
echo "✅ Kubernetes 部署完成"
}
# HELM 模式部署
deploy_helm() {
echo "🔧 HELM Chart 部署..."
check_requirements helm "HELM" || exit 1
check_requirements kubectl "kubectl" || exit 1
echo "🚀 部署 HELM Chart..."
cd deploy/helm
# 根据环境选择 values 文件
VALUES_FILE="values.yaml"
if [ "$ENV" = "prod" ] && [ -f "production-values.yaml" ]; then
VALUES_FILE="production-values.yaml"
fi
# 安装或升级 Chart
if helm status s3-balance-$ENV 2>/dev/null; then
echo "⬆️ 升级现有 HELM 发布..."
helm upgrade s3-balance-$ENV s3-balance -f $VALUES_FILE \
--namespace s3-balance-$ENV \
--create-namespace \
--wait
else
echo "📦 安装新的 HELM Chart..."
helm install s3-balance-$ENV s3-balance -f $VALUES_FILE \
--namespace s3-balance-$ENV \
--create-namespace \
--wait
fi
cd ../..
echo "✅ HELM Chart 部署完成"
}
# 通用后处理
post_deploy() {
echo ""
echo "⏳ 等待服务启动..."
sleep 10
case $MODE in
docker)
echo ""
echo "✅ Docker 部署完成!"
echo "🔗 访问地址:"
echo " 📊 指标端点: http://localhost:$PORT/metrics"
echo ""
echo "📝 Docker 命令:"
echo " docker logs -f s3-balance-$ENV # 查看日志"
echo " docker stop s3-balance-$ENV # 停止服务"
echo " docker rm s3-balance-$ENV # 删除容器"
;;
compose)
echo ""
echo "✅ Docker Compose 部署完成!"
echo "🔗 访问地址:"
echo " 📊 Grafana 面板: http://localhost:3000 (admin/admin123)"
echo " 🔥 Prometheus: http://localhost:9090"
echo " 📈 指标端点: http://localhost:$PORT/metrics"
echo ""
echo "📝 Compose 命令:"
echo " cd deploy/docker && docker-compose logs -f # 查看日志"
echo " cd deploy/docker && docker-compose down # 停止服务"
;;
kubernetes)
echo ""
echo "✅ Kubernetes 部署完成!"
echo "🔗 检查状态:"
echo " kubectl get pods -n s3-balance"
echo " kubectl get svc -n s3-balance"
echo " kubectl get ingress -n s3-balance"
echo ""
echo "📋 常用命令:"
echo " kubectl logs -f deployment/s3-balance-deployment -n s3-balance"
echo " kubectl scale deployment s3-balance-deployment --replicas=5 -n s3-balance"
;;
helm)
echo ""
echo "✅ HELM Chart 部署完成!"
echo "🔗 检查状态:"
echo " helm status s3-balance-$ENV -n s3-balance-$ENV"
echo " kubectl get pods -n s3-balance-$ENV"
echo " kubectl get svc -n s3-balance-$ENV"
echo ""
echo "🔧 HELM 命令:"
echo " helm uninstall s3-balance-$ENV -n s3-balance-$ENV"
echo " helm list -A"
;;
esac
echo ""
echo "📊 监控指标查询:"
echo " - 存储桶健康: s3_balance_bucket_healthy"
echo " - QPS: rate(s3_balance_s3_operations_total[1m])"
echo " - 延迟: histogram_quantile(0.95, s3_balance_s3_operation_duration_seconds_bucket)"
echo ""
echo "🎉 部署完成!享受 S3 Balance 服务!"
}
# 主执行逻辑
main() {
echo "🚀 开始部署 S3 Balance..."
# 根据模式执行部署
case $MODE in
docker)
deploy_docker
;;
compose)
deploy_compose
;;
kubernetes)
deploy_kubernetes
;;
helm)
deploy_helm
;;
*)
echo "❌ 未知部署模式: $MODE"
show_help
exit 1
;;
esac
post_deploy
}
# 执行主函数
main

14
go.mod
View File

@@ -8,6 +8,7 @@ require (
github.com/aws/aws-sdk-go-v2/credentials v1.18.5
github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0
github.com/gorilla/mux v1.8.1
github.com/prometheus/client_golang v1.23.2
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.6.0
gorm.io/driver/postgres v1.6.0
@@ -31,6 +32,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.37.1 // indirect
github.com/aws/smithy-go v1.22.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -40,8 +43,15 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
)

44
go.sum
View File

@@ -36,12 +36,18 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.37.1 h1:ssCHKyNJqTnqRH4Vlf+jI0brtGQY
github.com/aws/aws-sdk-go-v2/service/sts v1.37.1/go.mod h1:JdeBDPgpJfuS6rU/hNglmOigKhyEZtBmbraLE4GK1J8=
github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw=
github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@@ -56,27 +62,49 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=