Some checks failed
Deploy to Production / Run Tests (push) Failing after 16m35s
Deploy to Production / Security Scan (push) Has been skipped
Deploy to Production / Build Docker Image (push) Has been skipped
Deploy to Production / Deploy to Staging (push) Has been skipped
Deploy to Production / Deploy to Production (push) Has been skipped
Deploy to Production / Notify Results (push) Successful in 31s
✨ 新功能: - SQLite数据库集成和持久化存储 - 数据库迁移系统和版本管理 - API分页功能和高效查询 - 用户搜索和过滤机制 - 完整的RBAC角色权限系统 - 结构化日志记录和系统监控 - API限流和多层安全防护 - Docker容器化和生产部署配置 🔒 安全特性: - JWT认证和授权 - 限流和防暴力破解 - 安全头和CORS配置 - 输入验证和XSS防护 - 审计日志和安全监控 📊 监控和运维: - Prometheus指标收集 - 健康检查和系统监控 - 自动化备份和恢复 - 完整的运维文档和脚本 - CI/CD流水线配置 🚀 部署支持: - 多环境Docker配置 - 生产环境部署指南 - 性能优化和安全加固 - 故障排除和应急响应 - 自动化运维脚本 📚 文档完善: - API使用文档 - 部署检查清单 - 运维操作手册 - 性能和安全指南 - 故障排除指南
470 lines
12 KiB
Bash
Executable File
470 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# 生产环境设置脚本
|
||
# 用于初始化生产环境配置和部署
|
||
|
||
set -e
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 日志函数
|
||
log_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# 检查是否为root用户
|
||
check_root() {
|
||
if [[ $EUID -eq 0 ]]; then
|
||
log_error "请不要以root用户运行此脚本"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 检查系统要求
|
||
check_system_requirements() {
|
||
log_info "检查系统要求..."
|
||
|
||
# 检查Docker
|
||
if ! command -v docker &> /dev/null; then
|
||
log_error "Docker未安装,请先安装Docker"
|
||
exit 1
|
||
fi
|
||
|
||
# 检查Docker Compose
|
||
if ! command -v docker-compose &> /dev/null; then
|
||
log_error "Docker Compose未安装,请先安装Docker Compose"
|
||
exit 1
|
||
fi
|
||
|
||
# 检查系统资源
|
||
TOTAL_MEM=$(free -m | awk 'NR==2{printf "%.0f", $2}')
|
||
if [ "$TOTAL_MEM" -lt 1024 ]; then
|
||
log_warning "系统内存少于1GB,可能影响性能"
|
||
fi
|
||
|
||
DISK_SPACE=$(df -BG . | awk 'NR==2{print $4}' | sed 's/G//')
|
||
if [ "$DISK_SPACE" -lt 10 ]; then
|
||
log_warning "可用磁盘空间少于10GB,可能不足"
|
||
fi
|
||
|
||
log_success "系统要求检查完成"
|
||
}
|
||
|
||
# 创建目录结构
|
||
create_directories() {
|
||
log_info "创建目录结构..."
|
||
|
||
mkdir -p /opt/rust-api/{data,logs,backups,ssl,config}
|
||
mkdir -p /opt/rust-api/nginx/{conf.d,ssl}
|
||
mkdir -p /opt/rust-api/monitoring/{prometheus,grafana}
|
||
|
||
log_success "目录结构创建完成"
|
||
}
|
||
|
||
# 设置文件权限
|
||
setup_permissions() {
|
||
log_info "设置文件权限..."
|
||
|
||
# 创建专用用户(如果不存在)
|
||
if ! id "apiuser" &>/dev/null; then
|
||
sudo useradd -r -s /bin/false -m -d /opt/rust-api apiuser
|
||
log_success "创建用户 apiuser"
|
||
fi
|
||
|
||
# 设置目录权限
|
||
sudo chown -R apiuser:apiuser /opt/rust-api
|
||
sudo chmod 750 /opt/rust-api
|
||
sudo chmod 755 /opt/rust-api/{data,logs,backups}
|
||
sudo chmod 700 /opt/rust-api/{ssl,config}
|
||
|
||
log_success "文件权限设置完成"
|
||
}
|
||
|
||
# 生成SSL证书
|
||
generate_ssl_certificate() {
|
||
log_info "生成SSL证书..."
|
||
|
||
read -p "请输入域名 (例: example.com): " DOMAIN
|
||
|
||
if [ -z "$DOMAIN" ]; then
|
||
log_warning "未输入域名,跳过SSL证书生成"
|
||
return
|
||
fi
|
||
|
||
# 检查是否已存在证书
|
||
if [ -f "/opt/rust-api/ssl/cert.pem" ]; then
|
||
log_warning "SSL证书已存在,跳过生成"
|
||
return
|
||
fi
|
||
|
||
# 生成自签名证书(生产环境建议使用Let's Encrypt)
|
||
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||
-keyout /opt/rust-api/ssl/key.pem \
|
||
-out /opt/rust-api/ssl/cert.pem \
|
||
-subj "/C=CN/ST=State/L=City/O=Organization/CN=$DOMAIN"
|
||
|
||
sudo chown apiuser:apiuser /opt/rust-api/ssl/*.pem
|
||
sudo chmod 600 /opt/rust-api/ssl/*.pem
|
||
|
||
log_success "SSL证书生成完成"
|
||
log_info "生产环境建议使用Let's Encrypt证书"
|
||
}
|
||
|
||
# 创建环境配置文件
|
||
create_env_file() {
|
||
log_info "创建环境配置文件..."
|
||
|
||
if [ -f ".env.production" ]; then
|
||
log_warning ".env.production 已存在,跳过创建"
|
||
return
|
||
fi
|
||
|
||
# 复制模板文件
|
||
cp config/production.env.template .env.production
|
||
|
||
# 生成随机JWT密钥
|
||
JWT_SECRET=$(openssl rand -base64 32)
|
||
sed -i "s/CHANGE_THIS_TO_A_SECURE_SECRET_KEY_AT_LEAST_32_CHARACTERS_LONG/$JWT_SECRET/" .env.production
|
||
|
||
log_success "环境配置文件创建完成"
|
||
log_warning "请编辑 .env.production 文件,配置实际的生产环境参数"
|
||
}
|
||
|
||
# 创建Nginx配置
|
||
create_nginx_config() {
|
||
log_info "创建Nginx配置..."
|
||
|
||
cat > /opt/rust-api/nginx/nginx.conf << 'EOF'
|
||
events {
|
||
worker_connections 1024;
|
||
}
|
||
|
||
http {
|
||
upstream rust_api {
|
||
server rust-user-api:3000;
|
||
}
|
||
|
||
# 限流配置
|
||
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
||
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
|
||
|
||
# 日志格式
|
||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||
'$status $body_bytes_sent "$http_referer" '
|
||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||
|
||
server {
|
||
listen 80;
|
||
server_name _;
|
||
|
||
# 重定向到HTTPS
|
||
return 301 https://$server_name$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name _;
|
||
|
||
# SSL配置
|
||
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
|
||
ssl_prefer_server_ciphers off;
|
||
|
||
# 安全头
|
||
add_header X-Frame-Options DENY;
|
||
add_header X-Content-Type-Options nosniff;
|
||
add_header X-XSS-Protection "1; mode=block";
|
||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
|
||
|
||
# 限流
|
||
limit_req zone=api burst=20 nodelay;
|
||
limit_conn conn_limit_per_ip 10;
|
||
|
||
# API代理
|
||
location /api/ {
|
||
proxy_pass http://rust_api;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
||
# 超时配置
|
||
proxy_connect_timeout 30s;
|
||
proxy_send_timeout 30s;
|
||
proxy_read_timeout 30s;
|
||
}
|
||
|
||
# 健康检查
|
||
location /health {
|
||
proxy_pass http://rust_api;
|
||
access_log off;
|
||
}
|
||
|
||
# 监控端点(限制访问)
|
||
location /monitoring/ {
|
||
allow 10.0.0.0/8;
|
||
allow 172.16.0.0/12;
|
||
allow 192.168.0.0/16;
|
||
deny all;
|
||
|
||
proxy_pass http://rust_api;
|
||
}
|
||
}
|
||
}
|
||
EOF
|
||
|
||
sudo chown apiuser:apiuser /opt/rust-api/nginx/nginx.conf
|
||
log_success "Nginx配置创建完成"
|
||
}
|
||
|
||
# 创建监控配置
|
||
create_monitoring_config() {
|
||
log_info "创建监控配置..."
|
||
|
||
# Prometheus配置
|
||
cat > /opt/rust-api/monitoring/prometheus/prometheus.yml << 'EOF'
|
||
global:
|
||
scrape_interval: 15s
|
||
|
||
scrape_configs:
|
||
- job_name: 'rust-user-api'
|
||
static_configs:
|
||
- targets: ['rust-user-api:3000']
|
||
metrics_path: '/monitoring/metrics/prometheus'
|
||
scrape_interval: 30s
|
||
|
||
- job_name: 'nginx'
|
||
static_configs:
|
||
- targets: ['nginx:9113']
|
||
EOF
|
||
|
||
# Grafana配置
|
||
mkdir -p /opt/rust-api/monitoring/grafana/dashboards
|
||
cat > /opt/rust-api/monitoring/grafana/dashboard.json << 'EOF'
|
||
{
|
||
"dashboard": {
|
||
"id": null,
|
||
"title": "Rust User API Dashboard",
|
||
"tags": ["rust", "api"],
|
||
"timezone": "browser",
|
||
"panels": [
|
||
{
|
||
"id": 1,
|
||
"title": "Request Rate",
|
||
"type": "graph",
|
||
"targets": [
|
||
{
|
||
"expr": "rate(http_requests_total[5m])",
|
||
"legendFormat": "Requests/sec"
|
||
}
|
||
],
|
||
"yAxes": [
|
||
{
|
||
"label": "requests/sec"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": 2,
|
||
"title": "Response Time",
|
||
"type": "graph",
|
||
"targets": [
|
||
{
|
||
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
|
||
"legendFormat": "95th percentile"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"time": {
|
||
"from": "now-1h",
|
||
"to": "now"
|
||
},
|
||
"refresh": "30s"
|
||
}
|
||
}
|
||
EOF
|
||
|
||
sudo chown -R apiuser:apiuser /opt/rust-api/monitoring
|
||
log_success "监控配置创建完成"
|
||
}
|
||
|
||
# 创建备份脚本
|
||
create_backup_script() {
|
||
log_info "创建备份脚本..."
|
||
|
||
cat > /opt/rust-api/backup.sh << 'EOF'
|
||
#!/bin/bash
|
||
|
||
# 数据库备份脚本
|
||
|
||
BACKUP_DIR="/opt/rust-api/backups"
|
||
DATE=$(date +%Y%m%d-%H%M%S)
|
||
RETENTION_DAYS=30
|
||
|
||
# 创建备份目录
|
||
mkdir -p $BACKUP_DIR
|
||
|
||
# 备份数据库
|
||
docker-compose exec -T rust-user-api \
|
||
sqlite3 /app/data/production.db ".backup /app/data/backup-$DATE.db"
|
||
|
||
# 复制备份文件到主机
|
||
docker cp rust-user-api-prod:/app/data/backup-$DATE.db $BACKUP_DIR/
|
||
|
||
# 压缩备份
|
||
tar -czf "$BACKUP_DIR/api-backup-$DATE.tar.gz" \
|
||
-C $BACKUP_DIR backup-$DATE.db
|
||
|
||
# 清理临时文件
|
||
rm -f "$BACKUP_DIR/backup-$DATE.db"
|
||
|
||
# 清理旧备份
|
||
find $BACKUP_DIR -name "api-backup-*.tar.gz" -mtime +$RETENTION_DAYS -delete
|
||
|
||
echo "备份完成: api-backup-$DATE.tar.gz"
|
||
EOF
|
||
|
||
chmod +x /opt/rust-api/backup.sh
|
||
sudo chown apiuser:apiuser /opt/rust-api/backup.sh
|
||
|
||
log_success "备份脚本创建完成"
|
||
}
|
||
|
||
# 设置防火墙
|
||
setup_firewall() {
|
||
log_info "设置防火墙..."
|
||
|
||
if ! command -v ufw &> /dev/null; then
|
||
log_warning "UFW未安装,跳过防火墙配置"
|
||
return
|
||
fi
|
||
|
||
read -p "是否配置防火墙? (y/N): " SETUP_FIREWALL
|
||
if [[ ! "$SETUP_FIREWALL" =~ ^[Yy]$ ]]; then
|
||
log_info "跳过防火墙配置"
|
||
return
|
||
fi
|
||
|
||
sudo ufw default deny incoming
|
||
sudo ufw default allow outgoing
|
||
sudo ufw allow ssh
|
||
sudo ufw allow 80/tcp
|
||
sudo ufw allow 443/tcp
|
||
|
||
read -p "是否启用防火墙? (y/N): " ENABLE_FIREWALL
|
||
if [[ "$ENABLE_FIREWALL" =~ ^[Yy]$ ]]; then
|
||
sudo ufw --force enable
|
||
log_success "防火墙配置完成并已启用"
|
||
else
|
||
log_info "防火墙配置完成但未启用"
|
||
fi
|
||
}
|
||
|
||
# 创建systemd服务
|
||
create_systemd_service() {
|
||
log_info "创建systemd服务..."
|
||
|
||
cat > /tmp/rust-api.service << EOF
|
||
[Unit]
|
||
Description=Rust User API
|
||
Requires=docker.service
|
||
After=docker.service
|
||
|
||
[Service]
|
||
Type=oneshot
|
||
RemainAfterExit=yes
|
||
WorkingDirectory=/opt/rust-api
|
||
ExecStart=/usr/bin/docker-compose -f docker-compose.prod.yml up -d
|
||
ExecStop=/usr/bin/docker-compose -f docker-compose.prod.yml down
|
||
TimeoutStartSec=0
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
sudo mv /tmp/rust-api.service /etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
|
||
log_success "systemd服务创建完成"
|
||
log_info "使用 'sudo systemctl enable rust-api' 启用自动启动"
|
||
}
|
||
|
||
# 运行安全检查
|
||
run_security_check() {
|
||
log_info "运行安全检查..."
|
||
|
||
# 检查文件权限
|
||
if [ "$(stat -c %a /opt/rust-api)" != "750" ]; then
|
||
log_warning "目录权限不正确"
|
||
fi
|
||
|
||
# 检查SSL证书
|
||
if [ ! -f "/opt/rust-api/ssl/cert.pem" ]; then
|
||
log_warning "SSL证书不存在"
|
||
fi
|
||
|
||
# 检查环境文件权限
|
||
if [ -f ".env.production" ] && [ "$(stat -c %a .env.production)" != "600" ]; then
|
||
chmod 600 .env.production
|
||
log_info "修正环境文件权限"
|
||
fi
|
||
|
||
log_success "安全检查完成"
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
echo "=========================================="
|
||
echo " Rust User API 生产环境设置脚本"
|
||
echo "=========================================="
|
||
echo
|
||
|
||
check_root
|
||
check_system_requirements
|
||
create_directories
|
||
setup_permissions
|
||
generate_ssl_certificate
|
||
create_env_file
|
||
create_nginx_config
|
||
create_monitoring_config
|
||
create_backup_script
|
||
setup_firewall
|
||
create_systemd_service
|
||
run_security_check
|
||
|
||
echo
|
||
log_success "生产环境设置完成!"
|
||
echo
|
||
echo "下一步操作:"
|
||
echo "1. 编辑 .env.production 文件,配置生产环境参数"
|
||
echo "2. 复制项目文件到 /opt/rust-api/"
|
||
echo "3. 运行 'docker-compose -f docker-compose.prod.yml up -d' 启动服务"
|
||
echo "4. 使用 'sudo systemctl enable rust-api' 启用自动启动"
|
||
echo "5. 配置域名DNS指向服务器IP"
|
||
echo "6. 使用Let's Encrypt获取正式SSL证书"
|
||
echo
|
||
log_info "详细文档请参考 docs/production-deployment.md"
|
||
}
|
||
|
||
# 运行主函数
|
||
main "$@" |