部署运维完全指南
快速部署方案对比
| 平台 | 难度 | 成本 | 性能 | 推荐场景 |
|---|---|---|---|---|
| Vercel | ⭐ | $0-20/月 | ⭐⭐⭐ | 静态网站+Serverless API |
| Render | ⭐⭐ | $7+/月 | ⭐⭐⭐⭐ | 全栈应用(推荐新手) |
| Fly.io | ⭐⭐ | $5+/月 | ⭐⭐⭐⭐ | 边缘计算 |
| Railway | ⭐⭐ | $5+/月 | ⭐⭐⭐ | 快速原型 |
| AWS/GCP | ⭐⭐⭐⭐⭐ | $50+/月 | ⭐⭐⭐⭐⭐ | 企业级应用 |
方案1: Render部署(推荐新手)
1分钟部署步骤
# 1. 准备代码
git init
git add .
git commit -m "Initial commit"
# 2. 推送到GitHub
gh repo create --public --push
# 3. 在Render.com创建新服务
# - 选择 "Web Service"
# - 连接GitHub仓库
# - 构建命令: npm install && npm run build
# - 启动命令: npm start
# - 完成!
render.yaml配置
services:
# Web服务
- type: web
name: realtime-app
env: node
buildCommand: npm install && npm run build
startCommand: npm start
healthCheckPath: /health
envVars:
- key: NODE_ENV
value: production
- key: DATABASE_URL
fromDatabase:
name: postgres-db
property: connectionString
- key: REDIS_URL
fromDatabase:
name: redis-db
property: connectionString
# PostgreSQL数据库
- type: pserv
name: postgres-db
env: postgresql
plan: starter
ipAllowList: []
# Redis
- type: redis
name: redis-db
plan: starter
ipAllowList: []
方案2: Fly.io部署(边缘计算)
部署步骤
# 1. 安装Fly CLI
curl -L https://fly.io/install.sh | sh
# 2. 登录
flyctl auth login
# 3. 初始化应用
flyctl launch
# 4. 部署
flyctl deploy
# 5. 打开应用
flyctl open
fly.toml配置
app = "my-realtime-app"
primary_region = "sjc"
[build]
builder = "heroku/buildpacks:20"
[env]
NODE_ENV = "production"
PORT = "8080"
[[services]]
internal_port = 8080
protocol = "tcp"
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[services.concurrency]
type = "connections"
hard_limit = 1000
soft_limit = 800
[[services.http_checks]]
interval = "10s"
timeout = "2s"
method = "GET"
path = "/health"
# 数据库
[[statics]]
guest_path = "/app/public"
url_prefix = "/static/"
添加PostgreSQL:
flyctl postgres create --name my-app-db
flyctl postgres attach my-app-db
添加Redis:
flyctl redis create
方案3: Docker + AWS/GCP
Dockerfile
# 多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产镜像
FROM node:18-alpine
WORKDIR /app
# 只复制必要文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
ENV NODE_ENV=production
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
CMD ["node", "dist/server.js"]
docker-compose.yml(本地开发)
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://postgres:password@db:5432/myapp
REDIS_URL: redis://redis:6379
NODE_ENV: development
volumes:
- ./src:/app/src
- /app/node_modules
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
AWS ECS部署
# 1. 创建ECR仓库
aws ecr create-repository --repository-name my-app
# 2. 构建并推送镜像
aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com
docker build -t my-app .
docker tag my-app:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
# 3. 创建ECS任务定义和服务(使用AWS Console或CLI)
方案4: Kubernetes(企业级)
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: realtime-app
spec:
replicas: 3
selector:
matchLabels:
app: realtime-app
template:
metadata:
labels:
app: realtime-app
spec:
containers:
- name: app
image: myregistry/realtime-app:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
- name: REDIS_URL
value: "redis://redis-service:6379"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: realtime-app-service
spec:
type: LoadBalancer
selector:
app: realtime-app
ports:
- port: 80
targetPort: 3000
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: realtime-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: realtime-app
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
CI/CD自动化
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Deploy to Render
env:
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
SERVICE_ID: ${{ secrets.RENDER_SERVICE_ID }}
run: |
curl -X POST \
"https://api.render.com/v1/services/$SERVICE_ID/deploys" \
-H "Authorization: Bearer $RENDER_API_KEY" \
-H "Content-Type: application/json"
notify:
needs: deploy
runs-on: ubuntu-latest
if: always()
steps:
- name: Notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deployment ${{ job.status }}'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
监控告警
1. Prometheus + Grafana
# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
volumes:
prometheus-data:
grafana-data:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['app:3000']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']
2. 日志聚合(ELK Stack)
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- "9200:9200"
logstash:
image: docker.elastic.co/logstash/logstash:8.5.0
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
kibana:
image: docker.elastic.co/kibana/kibana:8.5.0
ports:
- "5601:5601"
性能优化
1. 数据库优化
-- 创建索引
CREATE INDEX idx_messages_room_created
ON messages(room_id, created_at DESC);
-- 分区表(按时间)
CREATE TABLE messages_2024_01 PARTITION OF messages
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
-- 定期清理旧数据
DELETE FROM messages
WHERE created_at < NOW() - INTERVAL '90 days';
2. Redis优化
# redis.conf
maxmemory 256mb
maxmemory-policy allkeys-lru
save "" # 禁用RDB持久化(使用AOF)
appendonly yes
appendfsync everysec
3. Nginx优化
# nginx.conf
worker_processes auto;
worker_connections 4096;
http {
# Gzip压缩
gzip on;
gzip_types text/plain application/json application/javascript;
# 缓存
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;
# 限流
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
listen 80;
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# WebSocket
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
# API限流
location /api {
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://backend;
}
}
}
备份策略
1. 数据库备份
#!/bin/bash
# backup.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
DB_NAME="myapp"
# PostgreSQL备份
pg_dump $DB_NAME | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"
# 上传到S3
aws s3 cp "$BACKUP_DIR/db_$DATE.sql.gz" s3://my-backups/db/
# 保留最近30天
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +30 -delete
# 定时任务
# crontab -e
# 0 2 * * * /path/to/backup.sh
2. Redis备份
# 自动AOF重写
redis-cli BGREWRITEAOF
# 复制AOF文件
cp /var/lib/redis/appendonly.aof /backups/redis_$(date +%Y%m%d).aof
安全加固
1. 环境变量管理
# 不要将secrets提交到git
echo ".env" >> .gitignore
# 使用secrets管理工具
# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id prod/database/url
# 或使用dotenv-vault
npx dotenv-vault push
npx dotenv-vault pull production
2. SSL证书
# Let's Encrypt免费证书
certbot --nginx -d yourdomain.com -d www.yourdomain.com
# 自动续期
crontab -e
0 0 * * * certbot renew --quiet
3. 防火墙配置
# UFW(Ubuntu)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
应急响应
快速回滚
# Docker
docker ps # 找到旧版本的container ID
docker start <old-container-id>
docker stop <new-container-id>
# Kubernetes
kubectl rollout undo deployment/realtime-app
# Render
# 在Dashboard中选择之前的deploy并点击"Redeploy"
紧急扩容
# Kubernetes
kubectl scale deployment realtime-app --replicas=10
# Docker Swarm
docker service scale myapp=10
成本优化建议
- 使用Spot实例(节省70%)
- 设置自动伸缩(按需付费)
- 使用CDN(减少源站流量)
- 数据库连接池(减少数据库规格)
- Redis作为缓存(减少数据库查询)
- 定期清理日志(减少存储成本)
部署检查清单
部署前确认:
- 环境变量已配置
- 数据库迁移已完成
- SSL证书已配置
- 健康检查endpoint已实现
- 日志已配置
- 监控已设置
- 备份策略已实施
- 告警规则已配置
- 文档已更新
- 团队已知晓
部署后验证:
- 应用可以访问
- WebSocket连接正常
- 数据库连接正常
- Redis连接正常
- 日志正常输出
- 监控指标正常
- 负载测试通过
- 回滚计划已验证
推荐学习路径:
- 新手: Render一键部署
- 进阶: Docker + Fly.io
- 高级: Kubernetes + AWS
选择适合你的方案,从简单开始!