【Node.js】摆设与运维

打印 上一主题 下一主题

主题 1999|帖子 1999|积分 5997


   个人主页:Guiat
归属专栏:node.js

  


  
正文
1. Node.js 摆设概述

Node.js 应用的摆设与运维是将开辟完成的应用程序安全、稳固地运行在生产情况中的关键环节。良好的摆设计谋和运维实践能够确保应用的高可用性、可扩展性和安全性。
1.1 摆设的核心要素



  • 情况一致性包管
  • 应用程序的可靠性和稳固性
  • 性能优化和资源管理
  • 安全性和访问控制
  • 监控和日志管理
  • 自动化摆设流程
1.2 Node.js 摆设架构全景

     2. 传统服务器摆设

2.1 Linux 服务器情况预备

系统更新与基础软件安装

  1. # Ubuntu/Debian 系统
  2. sudo apt update && sudo apt upgrade -y
  3. # 安装必要的软件包
  4. sudo apt install -y curl wget git build-essential
  5. # CentOS/RHEL 系统
  6. sudo yum update -y
  7. sudo yum install -y curl wget git gcc-c++ make
  8. # 安装 Node.js (使用 NodeSource 仓库)
  9. curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
  10. sudo apt-get install -y nodejs
  11. # 验证安装
  12. node --version
  13. npm --version
复制代码
创建应用用户

  1. # 创建专用的应用用户
  2. sudo useradd -m -s /bin/bash nodeapp
  3. sudo usermod -aG sudo nodeapp
  4. # 切换到应用用户
  5. sudo su - nodeapp
  6. # 创建应用目录
  7. mkdir -p ~/apps/myapp
  8. cd ~/apps/myapp
复制代码
2.2 应用摆设脚本

  1. #!/bin/bash
  2. # deploy.sh - 应用部署脚本
  3. set -e
  4. APP_NAME="myapp"
  5. APP_DIR="/home/nodeapp/apps/$APP_NAME"
  6. REPO_URL="https://github.com/username/myapp.git"
  7. BRANCH="main"
  8. NODE_ENV="production"
  9. echo "开始部署 $APP_NAME..."
  10. # 创建应用目录
  11. mkdir -p $APP_DIR
  12. cd $APP_DIR
  13. # 克隆或更新代码
  14. if [ -d ".git" ]; then
  15.     echo "更新代码..."
  16.     git fetch origin
  17.     git reset --hard origin/$BRANCH
  18. else
  19.     echo "克隆代码..."
  20.     git clone -b $BRANCH $REPO_URL .
  21. fi
  22. # 安装依赖
  23. echo "安装依赖..."
  24. npm ci --production
  25. # 构建应用
  26. echo "构建应用..."
  27. npm run build
  28. # 设置环境变量
  29. echo "设置环境变量..."
  30. cp .env.example .env
  31. # 根据需要修改 .env 文件
  32. # 重启应用
  33. echo "重启应用..."
  34. pm2 restart $APP_NAME || pm2 start ecosystem.config.js
  35. echo "部署完成!"
复制代码
2.3 情况变量管理

  1. # .env.production
  2. NODE_ENV=production
  3. PORT=3000
  4. HOST=0.0.0.0
  5. # 数据库配置
  6. MONGODB_URI=mongodb://localhost:27017/myapp_prod
  7. REDIS_URL=redis://localhost:6379
  8. # 安全配置
  9. JWT_SECRET=your_super_secret_jwt_key_here
  10. SESSION_SECRET=your_session_secret_here
  11. # 第三方服务
  12. SMTP_HOST=smtp.gmail.com
  13. SMTP_PORT=587
  14. SMTP_USER=your_email@gmail.com
  15. SMTP_PASS=your_app_password
  16. # 日志配置
  17. LOG_LEVEL=info
  18. LOG_FILE=/var/log/myapp/app.log
  19. # 性能配置
  20. MAX_MEMORY=512M
  21. CLUSTER_WORKERS=auto
复制代码
2.4 Nginx 反向代理配置

  1. # /etc/nginx/sites-available/myapp
  2. server {
  3.     listen 80;
  4.     server_name yourdomain.com www.yourdomain.com;
  5.    
  6.     # 重定向到 HTTPS
  7.     return 301 https://$server_name$request_uri;
  8. }
  9. server {
  10.     listen 443 ssl http2;
  11.     server_name yourdomain.com www.yourdomain.com;
  12.    
  13.     # SSL 证书配置
  14.     ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
  15.     ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
  16.    
  17.     # SSL 安全配置
  18.     ssl_protocols TLSv1.2 TLSv1.3;
  19.     ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
  20.     ssl_prefer_server_ciphers off;
  21.     ssl_session_cache shared:SSL:10m;
  22.     ssl_session_timeout 10m;
  23.    
  24.     # 安全头
  25.     add_header X-Frame-Options DENY;
  26.     add_header X-Content-Type-Options nosniff;
  27.     add_header X-XSS-Protection "1; mode=block";
  28.     add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  29.    
  30.     # 静态文件处理
  31.     location /static/ {
  32.         alias /home/nodeapp/apps/myapp/public/;
  33.         expires 1y;
  34.         add_header Cache-Control "public, immutable";
  35.     }
  36.    
  37.     # API 代理
  38.     location /api/ {
  39.         proxy_pass http://127.0.0.1:3000;
  40.         proxy_http_version 1.1;
  41.         proxy_set_header Upgrade $http_upgrade;
  42.         proxy_set_header Connection 'upgrade';
  43.         proxy_set_header Host $host;
  44.         proxy_set_header X-Real-IP $remote_addr;
  45.         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  46.         proxy_set_header X-Forwarded-Proto $scheme;
  47.         proxy_cache_bypass $http_upgrade;
  48.         
  49.         # 超时设置
  50.         proxy_connect_timeout 60s;
  51.         proxy_send_timeout 60s;
  52.         proxy_read_timeout 60s;
  53.     }
  54.    
  55.     # 主应用代理
  56.     location / {
  57.         proxy_pass http://127.0.0.1:3000;
  58.         proxy_http_version 1.1;
  59.         proxy_set_header Upgrade $http_upgrade;
  60.         proxy_set_header Connection 'upgrade';
  61.         proxy_set_header Host $host;
  62.         proxy_set_header X-Real-IP $remote_addr;
  63.         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  64.         proxy_set_header X-Forwarded-Proto $scheme;
  65.         proxy_cache_bypass $http_upgrade;
  66.     }
  67.    
  68.     # 健康检查
  69.     location /health {
  70.         access_log off;
  71.         proxy_pass http://127.0.0.1:3000/health;
  72.     }
  73. }
复制代码
2.5 SSL 证书配置

  1. # 安装 Certbot
  2. sudo apt install certbot python3-certbot-nginx
  3. # 获取 SSL 证书
  4. sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
  5. # 设置自动续期
  6. sudo crontab -e
  7. # 添加以下行
  8. 0 12 * * * /usr/bin/certbot renew --quiet
复制代码
3. PM2 进程管理

3.1 PM2 安装与根本使用

  1. # 全局安装 PM2
  2. npm install -g pm2
  3. # 启动应用
  4. pm2 start app.js --name "myapp"
  5. # 查看运行状态
  6. pm2 status
  7. # 查看日志
  8. pm2 logs myapp
  9. # 重启应用
  10. pm2 restart myapp
  11. # 停止应用
  12. pm2 stop myapp
  13. # 删除应用
  14. pm2 delete myapp
复制代码
3.2 PM2 配置文件

  1. // ecosystem.config.js
  2. module.exports = {
  3.   apps: [
  4.     {
  5.       name: 'myapp',
  6.       script: './src/index.js',
  7.       instances: 'max', // 或者指定数字,如 4
  8.       exec_mode: 'cluster',
  9.       env: {
  10.         NODE_ENV: 'development',
  11.         PORT: 3000
  12.       },
  13.       env_production: {
  14.         NODE_ENV: 'production',
  15.         PORT: 3000,
  16.         MONGODB_URI: 'mongodb://localhost:27017/myapp_prod'
  17.       },
  18.       // 日志配置
  19.       log_file: './logs/combined.log',
  20.       out_file: './logs/out.log',
  21.       error_file: './logs/error.log',
  22.       log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
  23.       
  24.       // 自动重启配置
  25.       watch: false,
  26.       ignore_watch: ['node_modules', 'logs'],
  27.       max_memory_restart: '1G',
  28.       
  29.       // 实例配置
  30.       min_uptime: '10s',
  31.       max_restarts: 10,
  32.       
  33.       // 健康检查
  34.       health_check_grace_period: 3000,
  35.       
  36.       // 环境变量
  37.       env_file: '.env'
  38.     }
  39.   ],
  40.   
  41.   deploy: {
  42.     production: {
  43.       user: 'nodeapp',
  44.       host: 'your-server.com',
  45.       ref: 'origin/main',
  46.       repo: 'https://github.com/username/myapp.git',
  47.       path: '/home/nodeapp/apps/myapp',
  48.       'post-deploy': 'npm install && npm run build && pm2 reload ecosystem.config.js --env production'
  49.     }
  50.   }
  51. };
复制代码
3.3 PM2 集群模式

  1. // cluster.config.js
  2. module.exports = {
  3.   apps: [
  4.     {
  5.       name: 'myapp-cluster',
  6.       script: './src/index.js',
  7.       instances: 4, // 4个工作进程
  8.       exec_mode: 'cluster',
  9.       
  10.       // 负载均衡策略
  11.       instance_var: 'INSTANCE_ID',
  12.       
  13.       // 集群配置
  14.       kill_timeout: 5000,
  15.       listen_timeout: 3000,
  16.       
  17.       // 内存和CPU限制
  18.       max_memory_restart: '500M',
  19.       node_args: '--max-old-space-size=512',
  20.       
  21.       env_production: {
  22.         NODE_ENV: 'production',
  23.         PORT: 3000
  24.       }
  25.     }
  26.   ]
  27. };
复制代码
3.4 PM2 监控与管理

  1. # 实时监控
  2. pm2 monit
  3. # 查看详细信息
  4. pm2 show myapp
  5. # 重载所有应用(零停机时间)
  6. pm2 reload all
  7. # 保存当前进程列表
  8. pm2 save
  9. # 设置开机自启动
  10. pm2 startup
  11. pm2 save
  12. # 更新 PM2
  13. pm2 update
复制代码
3.5 PM2 日志管理

  1. # 查看实时日志
  2. pm2 logs
  3. # 查看特定应用日志
  4. pm2 logs myapp
  5. # 清空日志
  6. pm2 flush
  7. # 日志轮转
  8. pm2 install pm2-logrotate
  9. # 配置日志轮转
  10. pm2 set pm2-logrotate:max_size 10M
  11. pm2 set pm2-logrotate:retain 30
  12. pm2 set pm2-logrotate:compress true
复制代码
4. Docker 容器化摆设

4.1 Dockerfile 最佳实践

  1. # 多阶段构建 Dockerfile
  2. FROM node:18-alpine AS builder
  3. # 设置工作目录
  4. WORKDIR /app
  5. # 复制 package 文件
  6. COPY package*.json ./
  7. # 安装依赖
  8. RUN npm ci --only=production && npm cache clean --force
  9. # 复制源代码
  10. COPY . .
  11. # 构建应用
  12. RUN npm run build
  13. # 生产阶段
  14. FROM node:18-alpine AS production
  15. # 创建非 root 用户
  16. RUN addgroup -g 1001 -S nodejs
  17. RUN adduser -S nodeapp -u 1001
  18. # 设置工作目录
  19. WORKDIR /app
  20. # 复制构建产物和依赖
  21. COPY --from=builder --chown=nodeapp:nodejs /app/dist ./dist
  22. COPY --from=builder --chown=nodeapp:nodejs /app/node_modules ./node_modules
  23. COPY --from=builder --chown=nodeapp:nodejs /app/package*.json ./
  24. # 安装 dumb-init
  25. RUN apk add --no-cache dumb-init
  26. # 切换到非 root 用户
  27. USER nodeapp
  28. # 暴露端口
  29. EXPOSE 3000
  30. # 健康检查
  31. HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  32.   CMD node healthcheck.js
  33. # 启动应用
  34. ENTRYPOINT ["dumb-init", "--"]
  35. CMD ["node", "dist/index.js"]
复制代码
4.2 Docker Compose 配置

  1. # docker-compose.yml
  2. version: '3.8'
  3. services:
  4.   app:
  5.     build:
  6.       context: .
  7.       dockerfile: Dockerfile
  8.       target: production
  9.     image: myapp:latest
  10.     container_name: myapp
  11.     restart: unless-stopped
  12.     ports:
  13.       - "3000:3000"
  14.     environment:
  15.       - NODE_ENV=production
  16.       - MONGODB_URI=mongodb://mongo:27017/myapp
  17.       - REDIS_URL=redis://redis:6379
  18.     depends_on:
  19.       mongo:
  20.         condition: service_healthy
  21.       redis:
  22.         condition: service_healthy
  23.     volumes:
  24.       - app_logs:/app/logs
  25.       - app_uploads:/app/uploads
  26.     networks:
  27.       - app_network
  28.     healthcheck:
  29.       test: ["CMD", "node", "healthcheck.js"]
  30.       interval: 30s
  31.       timeout: 10s
  32.       retries: 3
  33.       start_period: 40s
  34.   mongo:
  35.     image: mongo:6
  36.     container_name: myapp_mongo
  37.     restart: unless-stopped
  38.     ports:
  39.       - "27017:27017"
  40.     environment:
  41.       MONGO_INITDB_ROOT_USERNAME: admin
  42.       MONGO_INITDB_ROOT_PASSWORD: password
  43.       MONGO_INITDB_DATABASE: myapp
  44.     volumes:
  45.       - mongo_data:/data/db
  46.       - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
  47.     networks:
  48.       - app_network
  49.     healthcheck:
  50.       test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
  51.       interval: 30s
  52.       timeout: 10s
  53.       retries: 3
  54.       start_period: 40s
  55.   redis:
  56.     image: redis:7-alpine
  57.     container_name: myapp_redis
  58.     restart: unless-stopped
  59.     ports:
  60.       - "6379:6379"
  61.     command: redis-server --appendonly yes --requirepass password
  62.     volumes:
  63.       - redis_data:/data
  64.     networks:
  65.       - app_network
  66.     healthcheck:
  67.       test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
  68.       interval: 30s
  69.       timeout: 10s
  70.       retries: 3
  71.       start_period: 40s
  72.   nginx:
  73.     image: nginx:alpine
  74.     container_name: myapp_nginx
  75.     restart: unless-stopped
  76.     ports:
  77.       - "80:80"
  78.       - "443:443"
  79.     volumes:
  80.       - ./nginx.conf:/etc/nginx/nginx.conf:ro
  81.       - ./ssl:/etc/nginx/ssl:ro
  82.       - app_static:/var/www/static:ro
  83.     depends_on:
  84.       - app
  85.     networks:
  86.       - app_network
  87. volumes:
  88.   mongo_data:
  89.   redis_data:
  90.   app_logs:
  91.   app_uploads:
  92.   app_static:
  93. networks:
  94.   app_network:
  95.     driver: bridge
复制代码
4.3 康健检查脚本

  1. // healthcheck.js
  2. const http = require('http');
  3. const options = {
  4.   hostname: 'localhost',
  5.   port: process.env.PORT || 3000,
  6.   path: '/health',
  7.   method: 'GET',
  8.   timeout: 2000
  9. };
  10. const request = http.request(options, (res) => {
  11.   if (res.statusCode === 200) {
  12.     process.exit(0);
  13.   } else {
  14.     process.exit(1);
  15.   }
  16. });
  17. request.on('error', () => {
  18.   process.exit(1);
  19. });
  20. request.on('timeout', () => {
  21.   request.destroy();
  22.   process.exit(1);
  23. });
  24. request.end();
复制代码
4.4 Docker 摆设脚本

  1. #!/bin/bash
  2. # docker-deploy.sh
  3. set -e
  4. APP_NAME="myapp"
  5. IMAGE_NAME="myapp:latest"
  6. CONTAINER_NAME="myapp_container"
  7. echo "开始 Docker 部署..."
  8. # 构建镜像
  9. echo "构建 Docker 镜像..."
  10. docker build -t $IMAGE_NAME .
  11. # 停止并删除旧容器
  12. echo "停止旧容器..."
  13. docker stop $CONTAINER_NAME 2>/dev/null || true
  14. docker rm $CONTAINER_NAME 2>/dev/null || true
  15. # 启动新容器
  16. echo "启动新容器..."
  17. docker run -d \
  18.   --name $CONTAINER_NAME \
  19.   --restart unless-stopped \
  20.   -p 3000:3000 \
  21.   -e NODE_ENV=production \
  22.   -v $(pwd)/logs:/app/logs \
  23.   $IMAGE_NAME
  24. # 等待容器启动
  25. echo "等待容器启动..."
  26. sleep 10
  27. # 检查容器状态
  28. if docker ps | grep -q $CONTAINER_NAME; then
  29.     echo "部署成功! 容器正在运行."
  30.     docker logs --tail 20 $CONTAINER_NAME
  31. else
  32.     echo "部署失败! 容器未能启动."
  33.     docker logs $CONTAINER_NAME
  34.     exit 1
  35. fi
复制代码
4.5 Docker 镜像优化

  1. # 优化后的 Dockerfile
  2. FROM node:18-alpine AS base
  3. RUN apk add --no-cache dumb-init
  4. WORKDIR /app
  5. COPY package*.json ./
  6. RUN npm ci --only=production && npm cache clean --force
  7. FROM base AS build
  8. RUN npm ci
  9. COPY . .
  10. RUN npm run build
  11. FROM base AS runtime
  12. COPY --from=build /app/dist ./dist
  13. RUN addgroup -g 1001 -S nodejs && adduser -S nodeapp -u 1001
  14. USER nodeapp
  15. EXPOSE 3000
  16. HEALTHCHECK --interval=30s CMD node healthcheck.js
  17. ENTRYPOINT ["dumb-init", "--"]
  18. CMD ["node", "dist/index.js"]
复制代码
5. Kubernetes 摆设

5.1 Kubernetes 基础配置

  1. # namespace.yaml
  2. apiVersion: v1
  3. kind: Namespace
  4. metadata:
  5.   name: myapp
  6.   labels:
  7.     name: myapp
复制代码
  1. # configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5.   name: myapp-config
  6.   namespace: myapp
  7. data:
  8.   NODE_ENV: "production"
  9.   PORT: "3000"
  10.   LOG_LEVEL: "info"
复制代码
  1. # secret.yaml
  2. apiVersion: v1
  3. kind: Secret
  4. metadata:
  5.   name: myapp-secrets
  6.   namespace: myapp
  7. type: Opaque
  8. data:
  9.   # base64 编码的值
  10.   MONGODB_URI: bW9uZ29kYjovL21vbmdvOjI3MDE3L215YXBw
  11.   JWT_SECRET: eW91cl9qd3Rfc2VjcmV0X2hlcmU=
  12.   REDIS_URL: cmVkaXM6Ly9yZWRpczozNjM3OQ==
复制代码
5.2 Deployment 配置

  1. # deployment.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   name: myapp-deployment
  6.   namespace: myapp
  7.   labels:
  8.     app: myapp
  9. spec:
  10.   replicas: 3
  11.   selector:
  12.     matchLabels:
  13.       app: myapp
  14.   template:
  15.     metadata:
  16.       labels:
  17.         app: myapp
  18.     spec:
  19.       containers:
  20.       - name: myapp
  21.         image: myapp:latest
  22.         ports:
  23.         - containerPort: 3000
  24.         env:
  25.         - name: NODE_ENV
  26.           valueFrom:
  27.             configMapKeyRef:
  28.               name: myapp-config
  29.               key: NODE_ENV
  30.         - name: PORT
  31.           valueFrom:
  32.             configMapKeyRef:
  33.               name: myapp-config
  34.               key: PORT
  35.         - name: MONGODB_URI
  36.           valueFrom:
  37.             secretKeyRef:
  38.               name: myapp-secrets
  39.               key: MONGODB_URI
  40.         - name: JWT_SECRET
  41.           valueFrom:
  42.             secretKeyRef:
  43.               name: myapp-secrets
  44.               key: JWT_SECRET
  45.         resources:
  46.           requests:
  47.             memory: "256Mi"
  48.             cpu: "250m"
  49.           limits:
  50.             memory: "512Mi"
  51.             cpu: "500m"
  52.         livenessProbe:
  53.           httpGet:
  54.             path: /health
  55.             port: 3000
  56.           initialDelaySeconds: 30
  57.           periodSeconds: 10
  58.           timeoutSeconds: 5
  59.           failureThreshold: 3
  60.         readinessProbe:
  61.           httpGet:
  62.             path: /ready
  63.             port: 3000
  64.           initialDelaySeconds: 5
  65.           periodSeconds: 5
  66.           timeoutSeconds: 3
  67.           failureThreshold: 3
  68.         volumeMounts:
  69.         - name: app-logs
  70.           mountPath: /app/logs
  71.       volumes:
  72.       - name: app-logs
  73.         emptyDir: {}
  74.       imagePullSecrets:
  75.       - name: regcred
复制代码
5.3 Service 配置

  1. # service.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5.   name: myapp-service
  6.   namespace: myapp
  7.   labels:
  8.     app: myapp
  9. spec:
  10.   selector:
  11.     app: myapp
  12.   ports:
  13.   - protocol: TCP
  14.     port: 80
  15.     targetPort: 3000
  16.   type: ClusterIP
复制代码
5.4 Ingress 配置

  1. # ingress.yaml
  2. apiVersion: networking.k8s.io/v1
  3. kind: Ingress
  4. metadata:
  5.   name: myapp-ingress
  6.   namespace: myapp
  7.   annotations:
  8.     kubernetes.io/ingress.class: nginx
  9.     cert-manager.io/cluster-issuer: letsencrypt-prod
  10.     nginx.ingress.kubernetes.io/rate-limit: "100"
  11.     nginx.ingress.kubernetes.io/rate-limit-window: "1m"
  12. spec:
  13.   tls:
  14.   - hosts:
  15.     - yourdomain.com
  16.     secretName: myapp-tls
  17.   rules:
  18.   - host: yourdomain.com
  19.     http:
  20.       paths:
  21.       - path: /
  22.         pathType: Prefix
  23.         backend:
  24.           service:
  25.             name: myapp-service
  26.             port:
  27.               number: 80
复制代码
5.5 HorizontalPodAutoscaler

  1. # hpa.yaml
  2. apiVersion: autoscaling/v2
  3. kind: HorizontalPodAutoscaler
  4. metadata:
  5.   name: myapp-hpa
  6.   namespace: myapp
  7. spec:
  8.   scaleTargetRef:
  9.     apiVersion: apps/v1
  10.     kind: Deployment
  11.     name: myapp-deployment
  12.   minReplicas: 2
  13.   maxReplicas: 10
  14.   metrics:
  15.   - type: Resource
  16.     resource:
  17.       name: cpu
  18.       target:
  19.         type: Utilization
  20.         averageUtilization: 70
  21.   - type: Resource
  22.     resource:
  23.       name: memory
  24.       target:
  25.         type: Utilization
  26.         averageUtilization: 80
  27.   behavior:
  28.     scaleDown:
  29.       stabilizationWindowSeconds: 300
  30.       policies:
  31.       - type: Percent
  32.         value: 10
  33.         periodSeconds: 60
  34.     scaleUp:
  35.       stabilizationWindowSeconds: 0
  36.       policies:
  37.       - type: Percent
  38.         value: 100
  39.         periodSeconds: 15
  40.       - type: Pods
  41.         value: 4
  42.         periodSeconds: 15
  43.       selectPolicy: Max
复制代码
5.6 Kubernetes 摆设流程

     6. 监控与日志

6.1 应用监控

Prometheus 配置

  1. # prometheus.yml
  2. global:
  3.   scrape_interval: 15s
  4.   evaluation_interval: 15s
  5. rule_files:
  6.   - "rules/*.yml"
  7. scrape_configs:
  8.   - job_name: 'myapp'
  9.     static_configs:
  10.       - targets: ['localhost:3000']
  11.     metrics_path: '/metrics'
  12.     scrape_interval: 5s
  13.   - job_name: 'node-exporter'
  14.     static_configs:
  15.       - targets: ['localhost:9100']
  16. alerting:
  17.   alertmanagers:
  18.     - static_configs:
  19.         - targets:
  20.           - alertmanager:9093
复制代码
Node.js 应用集成 Prometheus

  1. // metrics.js
  2. const promClient = require('prom-client');
  3. // 创建注册表
  4. const register = new promClient.Registry();
  5. // 添加默认指标
  6. promClient.collectDefaultMetrics({
  7.   register,
  8.   prefix: 'myapp_'
  9. });
  10. // 自定义指标
  11. const httpRequestDuration = new promClient.Histogram({
  12.   name: 'myapp_http_request_duration_seconds',
  13.   help: 'HTTP request duration in seconds',
  14.   labelNames: ['method', 'route', 'status_code'],
  15.   buckets: [0.1, 0.5, 1, 2, 5]
  16. });
  17. const httpRequestTotal = new promClient.Counter({
  18.   name: 'myapp_http_requests_total',
  19.   help: 'Total number of HTTP requests',
  20.   labelNames: ['method', 'route', 'status_code']
  21. });
  22. const activeConnections = new promClient.Gauge({
  23.   name: 'myapp_active_connections',
  24.   help: 'Number of active connections'
  25. });
  26. // 注册指标
  27. register.registerMetric(httpRequestDuration);
  28. register.registerMetric(httpRequestTotal);
  29. register.registerMetric(activeConnections);
  30. // 中间件
  31. const metricsMiddleware = (req, res, next) => {
  32.   const start = Date.now();
  33.   
  34.   res.on('finish', () => {
  35.     const duration = (Date.now() - start) / 1000;
  36.     const route = req.route ? req.route.path : req.path;
  37.    
  38.     httpRequestDuration
  39.       .labels(req.method, route, res.statusCode)
  40.       .observe(duration);
  41.       
  42.     httpRequestTotal
  43.       .labels(req.method, route, res.statusCode)
  44.       .inc();
  45.   });
  46.   
  47.   next();
  48. };
  49. module.exports = {
  50.   register,
  51.   metricsMiddleware,
  52.   activeConnections
  53. };
复制代码
6.2 日志管理

Winston 日志配置

  1. // logger.js
  2. const winston = require('winston');
  3. const path = require('path');
  4. // 自定义日志格式
  5. const logFormat = winston.format.combine(
  6.   winston.format.timestamp({
  7.     format: 'YYYY-MM-DD HH:mm:ss'
  8.   }),
  9.   winston.format.errors({ stack: true }),
  10.   winston.format.json()
  11. );
  12. // 创建 logger
  13. const logger = winston.createLogger({
  14.   level: process.env.LOG_LEVEL || 'info',
  15.   format: logFormat,
  16.   defaultMeta: {
  17.     service: 'myapp',
  18.     version: process.env.APP_VERSION || '1.0.0'
  19.   },
  20.   transports: [
  21.     // 错误日志
  22.     new winston.transports.File({
  23.       filename: path.join(__dirname, '../logs/error.log'),
  24.       level: 'error',
  25.       maxsize: 10485760, // 10MB
  26.       maxFiles: 5,
  27.       tailable: true
  28.     }),
  29.    
  30.     // 组合日志
  31.     new winston.transports.File({
  32.       filename: path.join(__dirname, '../logs/combined.log'),
  33.       maxsize: 10485760, // 10MB
  34.       maxFiles: 5,
  35.       tailable: true
  36.     }),
  37.    
  38.     // 控制台输出
  39.     new winston.transports.Console({
  40.       format: winston.format.combine(
  41.         winston.format.colorize(),
  42.         winston.format.simple()
  43.       )
  44.     })
  45.   ]
  46. });
  47. // 请求日志中间件
  48. const requestLogger = (req, res, next) => {
  49.   const start = Date.now();
  50.   
  51.   res.on('finish', () => {
  52.     const duration = Date.now() - start;
  53.    
  54.     logger.info('HTTP Request', {
  55.       method: req.method,
  56.       url: req.url,
  57.       statusCode: res.statusCode,
  58.       duration: `${duration}ms`,
  59.       userAgent: req.get('User-Agent'),
  60.       ip: req.ip,
  61.       userId: req.user?.id
  62.     });
  63.   });
  64.   
  65.   next();
  66. };
  67. module.exports = { logger, requestLogger };
复制代码
ELK Stack 配置

  1. # docker-compose.elk.yml
  2. version: '3.8'
  3. services:
  4.   elasticsearch:
  5.     image: docker.elastic.co/elasticsearch/elasticsearch:8.8.0
  6.     container_name: elasticsearch
  7.     environment:
  8.       - discovery.type=single-node
  9.       - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
  10.       - xpack.security.enabled=false
  11.     ports:
  12.       - "9200:9200"
  13.     volumes:
  14.       - elasticsearch_data:/usr/share/elasticsearch/data
  15.   logstash:
  16.     image: docker.elastic.co/logstash/logstash:8.8.0
  17.     container_name: logstash
  18.     ports:
  19.       - "5044:5044"
  20.     volumes:
  21.       - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
  22.     depends_on:
  23.       - elasticsearch
  24.   kibana:
  25.     image: docker.elastic.co/kibana/kibana:8.8.0
  26.     container_name: kibana
  27.     ports:
  28.       - "5601:5601"
  29.     environment:
  30.       - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
  31.     depends_on:
  32.       - elasticsearch
  33. volumes:
  34.   elasticsearch_data:
复制代码
  1. # logstash.conf
  2. input {
  3.   beats {
  4.     port => 5044
  5.   }
  6. }
  7. filter {
  8.   if [fields][service] == "myapp" {
  9.     json {
  10.       source => "message"
  11.     }
  12.    
  13.     date {
  14.       match => [ "timestamp", "yyyy-MM-dd HH:mm:ss" ]
  15.     }
  16.    
  17.     mutate {
  18.       remove_field => [ "message", "host", "agent", "ecs", "log", "input" ]
  19.     }
  20.   }
  21. }
  22. output {
  23.   elasticsearch {
  24.     hosts => ["elasticsearch:9200"]
  25.     index => "myapp-logs-%{+YYYY.MM.dd}"
  26.   }
  27. }
复制代码
6.3 APM 监控

New Relic 集成

  1. // newrelic.js
  2. 'use strict';
  3. exports.config = {
  4.   app_name: ['MyApp'],
  5.   license_key: process.env.NEW_RELIC_LICENSE_KEY,
  6.   logging: {
  7.     level: 'info'
  8.   },
  9.   allow_all_headers: true,
  10.   attributes: {
  11.     exclude: [
  12.       'request.headers.cookie',
  13.       'request.headers.authorization',
  14.       'request.headers.proxyAuthorization',
  15.       'request.headers.setCookie*',
  16.       'request.headers.x*',
  17.       'response.headers.cookie',
  18.       'response.headers.authorization',
  19.       'response.headers.proxyAuthorization',
  20.       'response.headers.setCookie*',
  21.       'response.headers.x*'
  22.     ]
  23.   }
  24. };
复制代码
Datadog 集成

  1. // app.js
  2. const tracer = require('dd-trace').init({
  3.   service: 'myapp',
  4.   env: process.env.NODE_ENV,
  5.   version: process.env.APP_VERSION
  6. });
  7. const express = require('express');
  8. const app = express();
  9. // Datadog 中间件
  10. app.use((req, res, next) => {
  11.   const span = tracer.scope().active();
  12.   if (span) {
  13.     span.setTag('user.id', req.user?.id);
  14.     span.setTag('http.route', req.route?.path);
  15.   }
  16.   next();
  17. });
复制代码
6.4 康健检查端点

  1. // health.js
  2. const mongoose = require('mongoose');
  3. const redis = require('redis');
  4. const healthCheck = async (req, res) => {
  5.   const health = {
  6.     status: 'ok',
  7.     timestamp: new Date().toISOString(),
  8.     uptime: process.uptime(),
  9.     version: process.env.APP_VERSION || '1.0.0',
  10.     checks: {}
  11.   };
  12.   try {
  13.     // 数据库连接检查
  14.     if (mongoose.connection.readyState === 1) {
  15.       health.checks.database = { status: 'ok' };
  16.     } else {
  17.       health.checks.database = { status: 'error', message: 'Database not connected' };
  18.       health.status = 'error';
  19.     }
  20.     // Redis 连接检查
  21.     const redisClient = redis.createClient();
  22.     try {
  23.       await redisClient.ping();
  24.       health.checks.redis = { status: 'ok' };
  25.       await redisClient.quit();
  26.     } catch (error) {
  27.       health.checks.redis = { status: 'error', message: error.message };
  28.       health.status = 'error';
  29.     }
  30.     // 内存使用检查
  31.     const memUsage = process.memoryUsage();
  32.     health.checks.memory = {
  33.       status: 'ok',
  34.       usage: {
  35.         rss: `${Math.round(memUsage.rss / 1024 / 1024)}MB`,
  36.         heapTotal: `${Math.round(memUsage.heapTotal / 1024 / 1024)}MB`,
  37.         heapUsed: `${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`
  38.       }
  39.     };
  40.     const statusCode = health.status === 'ok' ? 200 : 503;
  41.     res.status(statusCode).json(health);
  42.   } catch (error) {
  43.     res.status(503).json({
  44.       status: 'error',
  45.       message: error.message,
  46.       timestamp: new Date().toISOString()
  47.     });
  48.   }
  49. };
  50. module.exports = { healthCheck };
复制代码
7. 性能优化与扩展

7.1 负载均衡配置

Nginx 负载均衡

  1. # nginx.conf
  2. upstream myapp_backend {
  3.     least_conn;
  4.     server 127.0.0.1:3001 weight=3 max_fails=3 fail_timeout=30s;
  5.     server 127.0.0.1:3002 weight=3 max_fails=3 fail_timeout=30s;
  6.     server 127.0.0.1:3003 weight=2 max_fails=3 fail_timeout=30s;
  7.     server 127.0.0.1:3004 backup;
  8. }
  9. server {
  10.     listen 80;
  11.     server_name yourdomain.com;
  12.    
  13.     # 连接限制
  14.     limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
  15.     limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
  16.    
  17.     location / {
  18.         limit_conn conn_limit_per_ip 10;
  19.         limit_req zone=req_limit_per_ip burst=10 nodelay;
  20.         
  21.         proxy_pass http://myapp_backend;
  22.         proxy_http_version 1.1;
  23.         proxy_set_header Upgrade $http_upgrade;
  24.         proxy_set_header Connection 'upgrade';
  25.         proxy_set_header Host $host;
  26.         proxy_set_header X-Real-IP $remote_addr;
  27.         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  28.         proxy_set_header X-Forwarded-Proto $scheme;
  29.         proxy_cache_bypass $http_upgrade;
  30.         
  31.         # 超时设置
  32.         proxy_connect_timeout 5s;
  33.         proxy_send_timeout 60s;
  34.         proxy_read_timeout 60s;
  35.         
  36.         # 缓存设置
  37.         proxy_cache my_cache;
  38.         proxy_cache_valid 200 302 10m;
  39.         proxy_cache_valid 404 1m;
  40.     }
  41. }
复制代码
HAProxy 配置

  1. # haproxy.cfg
  2. global
  3.     daemon
  4.     maxconn 4096
  5.     log stdout local0
  6. defaults
  7.     mode http
  8.     timeout connect 5000ms
  9.     timeout client 50000ms
  10.     timeout server 50000ms
  11.     option httplog
  12. frontend myapp_frontend
  13.     bind *:80
  14.     default_backend myapp_backend
  15. backend myapp_backend
  16.     balance roundrobin
  17.     option httpchk GET /health
  18.     server app1 127.0.0.1:3001 check
  19.     server app2 127.0.0.1:3002 check
  20.     server app3 127.0.0.1:3003 check
  21.     server app4 127.0.0.1:3004 check backup
复制代码
7.2 缓存计谋

Redis 缓存实现

  1. // cache.js
  2. const redis = require('redis');
  3. const client = redis.createClient({
  4.   url: process.env.REDIS_URL
  5. });
  6. client.on('error', (err) => {
  7.   console.error('Redis Client Error', err);
  8. });
  9. client.connect();
  10. // 缓存中间件
  11. const cacheMiddleware = (duration = 300) => {
  12.   return async (req, res, next) => {
  13.     if (req.method !== 'GET') {
  14.       return next();
  15.     }
  16.     const key = `cache:${req.originalUrl}`;
  17.    
  18.     try {
  19.       const cached = await client.get(key);
  20.       
  21.       if (cached) {
  22.         return res.json(JSON.parse(cached));
  23.       }
  24.       // 重写 res.json 方法
  25.       const originalJson = res.json;
  26.       res.json = function(data) {
  27.         // 缓存响应数据
  28.         client.setEx(key, duration, JSON.stringify(data));
  29.         return originalJson.call(this, data);
  30.       };
  31.       next();
  32.     } catch (error) {
  33.       console.error('Cache error:', error);
  34.       next();
  35.     }
  36.   };
  37. };
  38. // 缓存失效
  39. const invalidateCache = async (pattern) => {
  40.   try {
  41.     const keys = await client.keys(pattern);
  42.     if (keys.length > 0) {
  43.       await client.del(keys);
  44.     }
  45.   } catch (error) {
  46.     console.error('Cache invalidation error:', error);
  47.   }
  48. };
  49. module.exports = {
  50.   client,
  51.   cacheMiddleware,
  52.   invalidateCache
  53. };
复制代码
7.3 数据库优化

MongoDB 优化

  1. // database.js
  2. const mongoose = require('mongoose');
  3. // 连接配置
  4. const connectDB = async () => {
  5.   try {
  6.     const conn = await mongoose.connect(process.env.MONGODB_URI, {
  7.       useNewUrlParser: true,
  8.       useUnifiedTopology: true,
  9.       maxPoolSize: 10, // 连接池大小
  10.       serverSelectionTimeoutMS: 5000,
  11.       socketTimeoutMS: 45000,
  12.       bufferCommands: false,
  13.       bufferMaxEntries: 0
  14.     });
  15.     console.log(`MongoDB Connected: ${conn.connection.host}`);
  16.   } catch (error) {
  17.     console.error('Database connection error:', error);
  18.     process.exit(1);
  19.   }
  20. };
  21. // 索引创建
  22. const createIndexes = async () => {
  23.   try {
  24.     // 用户集合索引
  25.     await mongoose.connection.db.collection('users').createIndexes([
  26.       { key: { email: 1 }, unique: true },
  27.       { key: { username: 1 }, unique: true },
  28.       { key: { createdAt: -1 } }
  29.     ]);
  30.     // 产品集合索引
  31.     await mongoose.connection.db.collection('products').createIndexes([
  32.       { key: { name: 'text', description: 'text' } },
  33.       { key: { category: 1, price: 1 } },
  34.       { key: { createdAt: -1 } }
  35.     ]);
  36.     console.log('Database indexes created');
  37.   } catch (error) {
  38.     console.error('Index creation error:', error);
  39.   }
  40. };
  41. module.exports = { connectDB, createIndexes };
复制代码
7.4 性能监控

     8. 安全与备份

8.1 安全配置

应用安全中心件

  1. // security.js
  2. const helmet = require('helmet');
  3. const rateLimit = require('express-rate-limit');
  4. const mongoSanitize = require('express-mongo-sanitize');
  5. const xss = require('xss-clean');
  6. // 安全配置
  7. const configureSecurity = (app) => {
  8.   // 设置安全头
  9.   app.use(helmet({
  10.     contentSecurityPolicy: {
  11.       directives: {
  12.         defaultSrc: ["'self'"],
  13.         styleSrc: ["'self'", "'unsafe-inline'"],
  14.         scriptSrc: ["'self'"],
  15.         imgSrc: ["'self'", "data:", "https:"],
  16.       },
  17.     },
  18.     hsts: {
  19.       maxAge: 31536000,
  20.       includeSubDomains: true,
  21.       preload: true
  22.     }
  23.   }));
  24.   // 速率限制
  25.   const limiter = rateLimit({
  26.     windowMs: 15 * 60 * 1000, // 15分钟
  27.     max: 100, // 限制每个IP 100个请求
  28.     message: {
  29.       error: 'Too many requests, please try again later.'
  30.     },
  31.     standardHeaders: true,
  32.     legacyHeaders: false,
  33.   });
  34.   app.use('/api/', limiter);
  35.   // 严格的认证限制
  36.   const authLimiter = rateLimit({
  37.     windowMs: 60 * 60 * 1000, // 1小时
  38.     max: 5, // 限制每个IP 5次尝试
  39.     skipSuccessfulRequests: true,
  40.   });
  41.   app.use('/api/auth/login', authLimiter);
  42.   // 防止 NoSQL 注入
  43.   app.use(mongoSanitize());
  44.   // 防止 XSS 攻击
  45.   app.use(xss());
  46. };
  47. module.exports = { configureSecurity };
复制代码
SSL/TLS 配置

  1. // ssl-server.js
  2. const https = require('https');
  3. const fs = require('fs');
  4. const express = require('express');
  5. const app = express();
  6. // SSL 证书配置
  7. const sslOptions = {
  8.   key: fs.readFileSync('/path/to/private-key.pem'),
  9.   cert: fs.readFileSync('/path/to/certificate.pem'),
  10.   ca: fs.readFileSync('/path/to/ca-certificate.pem'), // 可选
  11.   
  12.   // 安全配置
  13.   secureProtocol: 'TLSv1_2_method',
  14.   ciphers: [
  15.     'ECDHE-RSA-AES256-GCM-SHA512',
  16.     'DHE-RSA-AES256-GCM-SHA512',
  17.     'ECDHE-RSA-AES256-GCM-SHA384',
  18.     'DHE-RSA-AES256-GCM-SHA384',
  19.     'ECDHE-RSA-AES256-SHA384'
  20.   ].join(':'),
  21.   honorCipherOrder: true
  22. };
  23. // 创建 HTTPS 服务器
  24. const server = https.createServer(sslOptions, app);
  25. server.listen(443, () => {
  26.   console.log('HTTPS Server running on port 443');
  27. });
复制代码
8.2 数据备份计谋

MongoDB 备份脚本

  1. #!/bin/bash
  2. # mongodb-backup.sh
  3. set -e
  4. # 配置
  5. DB_NAME="myapp"
  6. BACKUP_DIR="/backup/mongodb"
  7. DATE=$(date +%Y%m%d_%H%M%S)
  8. BACKUP_NAME="mongodb_backup_${DATE}"
  9. RETENTION_DAYS=7
  10. # 创建备份目录
  11. mkdir -p $BACKUP_DIR
  12. # 执行备份
  13. echo "开始备份 MongoDB 数据库: $DB_NAME"
  14. mongodump --db $DB_NAME --out $BACKUP_DIR/$BACKUP_NAME
  15. # 压缩备份
  16. echo "压缩备份文件..."
  17. tar -czf $BACKUP_DIR/${BACKUP_NAME}.tar.gz -C $BACKUP_DIR $BACKUP_NAME
  18. rm -rf $BACKUP_DIR/$BACKUP_NAME
  19. # 清理旧备份
  20. echo "清理 $RETENTION_DAYS 天前的备份..."
  21. find $BACKUP_DIR -name "mongodb_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
  22. # 上传到云存储 (可选)
  23. if [ ! -z "$AWS_S3_BUCKET" ]; then
  24.     echo "上传备份到 S3..."
  25.     aws s3 cp $BACKUP_DIR/${BACKUP_NAME}.tar.gz s3://$AWS_S3_BUCKET/mongodb-backups/
  26. fi
  27. echo "备份完成: ${BACKUP_NAME}.tar.gz"
复制代码
自动化备份配置

  1. # 添加到 crontab
  2. # 每天凌晨 2 点执行备份
  3. 0 2 * * * /path/to/mongodb-backup.sh >> /var/log/mongodb-backup.log 2>&1
  4. # 每周日凌晨 3 点执行完整备份
  5. 0 3 * * 0 /path/to/full-backup.sh >> /var/log/full-backup.log 2>&1
复制代码
8.3 灾难规复

规复脚本

  1. #!/bin/bash
  2. # mongodb-restore.sh
  3. set -e
  4. BACKUP_FILE=$1
  5. DB_NAME="myapp"
  6. TEMP_DIR="/tmp/mongodb_restore"
  7. if [ -z "$BACKUP_FILE" ]; then
  8.     echo "用法: $0 <backup_file.tar.gz>"
  9.     exit 1
  10. fi
  11. echo "开始恢复 MongoDB 数据库..."
  12. # 创建临时目录
  13. mkdir -p $TEMP_DIR
  14. # 解压备份文件
  15. echo "解压备份文件..."
  16. tar -xzf $BACKUP_FILE -C $TEMP_DIR
  17. # 获取备份目录名
  18. BACKUP_DIR=$(ls $TEMP_DIR)
  19. # 停止应用服务
  20. echo "停止应用服务..."
  21. pm2 stop myapp
  22. # 恢复数据库
  23. echo "恢复数据库..."
  24. mongorestore --db $DB_NAME --drop $TEMP_DIR/$BACKUP_DIR/$DB_NAME
  25. # 启动应用服务
  26. echo "启动应用服务..."
  27. pm2 start myapp
  28. # 清理临时文件
  29. rm -rf $TEMP_DIR
  30. echo "数据库恢复完成!"
复制代码
9. 故障排查与调试

9.1 常见题目诊断

内存走漏检测

  1. // memory-monitor.js
  2. const v8 = require('v8');
  3. const fs = require('fs');
  4. // 内存使用监控
  5. const monitorMemory = () => {
  6.   const usage = process.memoryUsage();
  7.   const heapStats = v8.getHeapStatistics();
  8.   
  9.   console.log('Memory Usage:', {
  10.     rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
  11.     heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
  12.     heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
  13.     external: `${Math.round(usage.external / 1024 / 1024)}MB`,
  14.     heapLimit: `${Math.round(heapStats.heap_size_limit / 1024 / 1024)}MB`
  15.   });
  16.   
  17.   // 如果内存使用超过阈值,生成堆快照
  18.   if (usage.heapUsed > 500 * 1024 * 1024) { // 500MB
  19.     const snapshot = v8.writeHeapSnapshot();
  20.     console.log('Heap snapshot written to:', snapshot);
  21.   }
  22. };
  23. // 每分钟检查一次内存使用
  24. setInterval(monitorMemory, 60000);
  25. module.exports = { monitorMemory };
复制代码
性能分析

  1. // profiler.js
  2. const inspector = require('inspector');
  3. const fs = require('fs');
  4. const path = require('path');
  5. class Profiler {
  6.   constructor() {
  7.     this.session = null;
  8.   }
  9.   start() {
  10.     this.session = new inspector.Session();
  11.     this.session.connect();
  12.    
  13.     // 启用 CPU 分析器
  14.     this.session.post('Profiler.enable');
  15.     this.session.post('Profiler.start');
  16.    
  17.     console.log('CPU profiler started');
  18.   }
  19.   async stop() {
  20.     if (!this.session) return;
  21.     return new Promise((resolve, reject) => {
  22.       this.session.post('Profiler.stop', (err, { profile }) => {
  23.         if (err) {
  24.           reject(err);
  25.           return;
  26.         }
  27.         const filename = `cpu-profile-${Date.now()}.cpuprofile`;
  28.         const filepath = path.join(__dirname, '../profiles', filename);
  29.         
  30.         fs.writeFileSync(filepath, JSON.stringify(profile));
  31.         console.log('CPU profile saved to:', filepath);
  32.         
  33.         this.session.disconnect();
  34.         resolve(filepath);
  35.       });
  36.     });
  37.   }
  38. }
  39. module.exports = Profiler;
复制代码
9.2 日志分析

错误追踪

  1. // error-tracker.js
  2. const Sentry = require('@sentry/node');
  3. // 初始化 Sentry
  4. Sentry.init({
  5.   dsn: process.env.SENTRY_DSN,
  6.   environment: process.env.NODE_ENV,
  7.   tracesSampleRate: 1.0,
  8. });
  9. // 错误处理中间件
  10. const errorHandler = (err, req, res, next) => {
  11.   // 记录错误到 Sentry
  12.   Sentry.captureException(err, {
  13.     user: {
  14.       id: req.user?.id,
  15.       email: req.user?.email
  16.     },
  17.     request: {
  18.       method: req.method,
  19.       url: req.url,
  20.       headers: req.headers
  21.     }
  22.   });
  23.   // 记录到本地日志
  24.   console.error('Error:', {
  25.     message: err.message,
  26.     stack: err.stack,
  27.     url: req.url,
  28.     method: req.method,
  29.     userId: req.user?.id,
  30.     timestamp: new Date().toISOString()
  31.   });
  32.   // 返回错误响应
  33.   const statusCode = err.statusCode || 500;
  34.   res.status(statusCode).json({
  35.     error: {
  36.       message: process.env.NODE_ENV === 'production'
  37.         ? 'Internal Server Error'
  38.         : err.message,
  39.       status: statusCode
  40.     }
  41.   });
  42. };
  43. module.exports = { errorHandler };
复制代码
9.3 调试工具

长途调试配置

  1. # 启动应用时启用调试模式
  2. node --inspect=0.0.0.0:9229 src/index.js
  3. # 或者使用 PM2
  4. pm2 start src/index.js --node-args="--inspect=0.0.0.0:9229"
复制代码
调试脚本

  1. // debug-utils.js
  2. const util = require('util');
  3. const fs = require('fs');
  4. // 深度日志记录
  5. const deepLog = (obj, label = 'Debug') => {
  6.   console.log(`\n=== ${label} ===`);
  7.   console.log(util.inspect(obj, {
  8.     depth: null,
  9.     colors: true,
  10.     showHidden: false
  11.   }));
  12.   console.log('='.repeat(label.length + 8));
  13. };
  14. // 性能计时器
  15. class Timer {
  16.   constructor(label) {
  17.     this.label = label;
  18.     this.start = process.hrtime.bigint();
  19.   }
  20.   end() {
  21.     const end = process.hrtime.bigint();
  22.     const duration = Number(end - this.start) / 1000000; // 转换为毫秒
  23.     console.log(`${this.label}: ${duration.toFixed(2)}ms`);
  24.     return duration;
  25.   }
  26. }
  27. // 内存快照
  28. const takeMemorySnapshot = () => {
  29.   const usage = process.memoryUsage();
  30.   const snapshot = {
  31.     timestamp: new Date().toISOString(),
  32.     pid: process.pid,
  33.     memory: {
  34.       rss: usage.rss,
  35.       heapTotal: usage.heapTotal,
  36.       heapUsed: usage.heapUsed,
  37.       external: usage.external
  38.     }
  39.   };
  40.   
  41.   const filename = `memory-snapshot-${Date.now()}.json`;
  42.   fs.writeFileSync(filename, JSON.stringify(snapshot, null, 2));
  43.   console.log('Memory snapshot saved to:', filename);
  44.   
  45.   return snapshot;
  46. };
  47. module.exports = {
  48.   deepLog,
  49.   Timer,
  50.   takeMemorySnapshot
  51. };
复制代码
10. 最佳实践与总结

10.1 摆设最佳实践

摆设检查清单

     情况管理计谋

  1. // config/environments.js
  2. const environments = {
  3.   development: {
  4.     port: 3000,
  5.     database: {
  6.       uri: 'mongodb://localhost:27017/myapp_dev',
  7.       options: {
  8.         useNewUrlParser: true,
  9.         useUnifiedTopology: true
  10.       }
  11.     },
  12.     redis: {
  13.       url: 'redis://localhost:6379'
  14.     },
  15.     logging: {
  16.       level: 'debug',
  17.       console: true
  18.     },
  19.     security: {
  20.       cors: {
  21.         origin: '*'
  22.       }
  23.     }
  24.   },
  25.   
  26.   staging: {
  27.     port: process.env.PORT || 3000,
  28.     database: {
  29.       uri: process.env.MONGODB_URI,
  30.       options: {
  31.         useNewUrlParser: true,
  32.         useUnifiedTopology: true,
  33.         maxPoolSize: 5
  34.       }
  35.     },
  36.     redis: {
  37.       url: process.env.REDIS_URL
  38.     },
  39.     logging: {
  40.       level: 'info',
  41.       console: false,
  42.       file: true
  43.     },
  44.     security: {
  45.       cors: {
  46.         origin: process.env.ALLOWED_ORIGINS?.split(',') || []
  47.       }
  48.     }
  49.   },
  50.   
  51.   production: {
  52.     port: process.env.PORT || 3000,
  53.     database: {
  54.       uri: process.env.MONGODB_URI,
  55.       options: {
  56.         useNewUrlParser: true,
  57.         useUnifiedTopology: true,
  58.         maxPoolSize: 10,
  59.         serverSelectionTimeoutMS: 5000,
  60.         socketTimeoutMS: 45000
  61.       }
  62.     },
  63.     redis: {
  64.       url: process.env.REDIS_URL,
  65.       options: {
  66.         retryDelayOnFailover: 100,
  67.         maxRetriesPerRequest: 3
  68.       }
  69.     },
  70.     logging: {
  71.       level: 'warn',
  72.       console: false,
  73.       file: true,
  74.       sentry: true
  75.     },
  76.     security: {
  77.       cors: {
  78.         origin: process.env.ALLOWED_ORIGINS?.split(',') || []
  79.       },
  80.       rateLimit: {
  81.         windowMs: 15 * 60 * 1000,
  82.         max: 100
  83.       }
  84.     }
  85.   }
  86. };
  87. module.exports = environments[process.env.NODE_ENV || 'development'];
复制代码
10.2 运维自动化

自动化摆设脚本

  1. #!/bin/bash
  2. # auto-deploy.sh
  3. set -e
  4. # 配置
  5. APP_NAME="myapp"
  6. DEPLOY_USER="deploy"
  7. DEPLOY_HOST="your-server.com"
  8. DEPLOY_PATH="/opt/apps/$APP_NAME"
  9. BACKUP_PATH="/opt/backups/$APP_NAME"
  10. HEALTH_CHECK_URL="http://localhost:3000/health"
  11. # 颜色输出
  12. RED='\033[0;31m'
  13. GREEN='\033[0;32m'
  14. YELLOW='\033[1;33m'
  15. NC='\033[0m' # No Color
  16. log() {
  17.     echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
  18. }
  19. warn() {
  20.     echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
  21. }
  22. error() {
  23.     echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
  24.     exit 1
  25. }
  26. # 预检查
  27. pre_deploy_check() {
  28.     log "执行部署前检查..."
  29.    
  30.     # 检查 Git 状态
  31.     if [[ -n $(git status --porcelain) ]]; then
  32.         error "工作目录不干净,请提交或暂存更改"
  33.     fi
  34.    
  35.     # 检查测试
  36.     log "运行测试..."
  37.     npm test || error "测试失败"
  38.    
  39.     # 检查构建
  40.     log "检查构建..."
  41.     npm run build || error "构建失败"
  42.    
  43.     log "预检查完成"
  44. }
  45. # 备份当前版本
  46. backup_current() {
  47.     log "备份当前版本..."
  48.    
  49.     ssh $DEPLOY_USER@$DEPLOY_HOST "
  50.         if [ -d $DEPLOY_PATH ]; then
  51.             sudo mkdir -p $BACKUP_PATH
  52.             sudo cp -r $DEPLOY_PATH $BACKUP_PATH/backup-$(date +%Y%m%d_%H%M%S)
  53.             # 保留最近5个备份
  54.             sudo ls -t $BACKUP_PATH | tail -n +6 | xargs -r sudo rm -rf
  55.         fi
  56.     "
  57.    
  58.     log "备份完成"
  59. }
  60. # 部署新版本
  61. deploy() {
  62.     log "开始部署..."
  63.    
  64.     # 上传代码
  65.     rsync -avz --delete \
  66.         --exclude 'node_modules' \
  67.         --exclude '.git' \
  68.         --exclude 'logs' \
  69.         ./ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/
  70.    
  71.     # 远程部署命令
  72.     ssh $DEPLOY_USER@$DEPLOY_HOST "
  73.         cd $DEPLOY_PATH
  74.         npm ci --production
  75.         npm run build
  76.         sudo systemctl reload nginx
  77.         pm2 reload ecosystem.config.js --env production
  78.     "
  79.    
  80.     log "部署完成"
  81. }
  82. # 健康检查
  83. health_check() {
  84.     log "执行健康检查..."
  85.    
  86.     local max_attempts=30
  87.     local attempt=1
  88.    
  89.     while [ $attempt -le $max_attempts ]; do
  90.         if ssh $DEPLOY_USER@$DEPLOY_HOST "curl -f $HEALTH_CHECK_URL > /dev/null 2>&1"; then
  91.             log "健康检查通过"
  92.             return 0
  93.         fi
  94.         
  95.         warn "健康检查失败 (尝试 $attempt/$max_attempts)"
  96.         sleep 10
  97.         ((attempt++))
  98.     done
  99.    
  100.     error "健康检查失败,部署可能有问题"
  101. }
  102. # 回滚
  103. rollback() {
  104.     warn "开始回滚..."
  105.    
  106.     ssh $DEPLOY_USER@$DEPLOY_HOST "
  107.         latest_backup=\$(ls -t $BACKUP_PATH | head -n 1)
  108.         if [ -n "\$latest_backup" ]; then
  109.             sudo rm -rf $DEPLOY_PATH
  110.             sudo cp -r $BACKUP_PATH/\$latest_backup $DEPLOY_PATH
  111.             pm2 reload ecosystem.config.js --env production
  112.         else
  113.             echo 'No backup found for rollback'
  114.             exit 1
  115.         fi
  116.     "
  117.    
  118.     warn "回滚完成"
  119. }
  120. # 主流程
  121. main() {
  122.     log "开始自动化部署流程..."
  123.    
  124.     pre_deploy_check
  125.     backup_current
  126.     deploy
  127.    
  128.     if health_check; then
  129.         log "部署成功完成!"
  130.     else
  131.         error "部署失败,开始回滚..."
  132.         rollback
  133.         health_check || error "回滚后健康检查仍然失败"
  134.         warn "已回滚到之前版本"
  135.     fi
  136. }
  137. # 捕获错误并回滚
  138. trap 'error "部署过程中发生错误"' ERR
  139. # 执行主流程
  140. main "$@"
复制代码
10.3 监控告警配置

Prometheus 告警规则

  1. # alerts.yml
  2. groups:
  3.   - name: myapp.rules
  4.     rules:
  5.       - alert: HighErrorRate
  6.         expr: rate(myapp_http_requests_total{status_code=~"5.."}[5m]) > 0.1
  7.         for: 5m
  8.         labels:
  9.           severity: critical
  10.         annotations:
  11.           summary: "High error rate detected"
  12.           description: "Error rate is {{ $value }} errors per second"
  13.       - alert: HighResponseTime
  14.         expr: histogram_quantile(0.95, rate(myapp_http_request_duration_seconds_bucket[5m])) > 1
  15.         for: 5m
  16.         labels:
  17.           severity: warning
  18.         annotations:
  19.           summary: "High response time detected"
  20.           description: "95th percentile response time is {{ $value }} seconds"
  21.       - alert: HighMemoryUsage
  22.         expr: (myapp_process_resident_memory_bytes / 1024 / 1024) > 512
  23.         for: 10m
  24.         labels:
  25.           severity: warning
  26.         annotations:
  27.           summary: "High memory usage"
  28.           description: "Memory usage is {{ $value }}MB"
  29.       - alert: ServiceDown
  30.         expr: up{job="myapp"} == 0
  31.         for: 1m
  32.         labels:
  33.           severity: critical
  34.         annotations:
  35.           summary: "Service is down"
  36.           description: "MyApp service is not responding"
复制代码
告警通知配置

  1. # alertmanager.yml
  2. global:
  3.   smtp_smarthost: 'smtp.gmail.com:587'
  4.   smtp_from: 'alerts@yourdomain.com'
  5. route:
  6.   group_by: ['alertname']
  7.   group_wait: 10s
  8.   group_interval: 10s
  9.   repeat_interval: 1h
  10.   receiver: 'web.hook'
  11. receivers:
  12.   - name: 'web.hook'
  13.     email_configs:
  14.       - to: 'admin@yourdomain.com'
  15.         subject: 'Alert: {{ .GroupLabels.alertname }}'
  16.         body: |
  17.           {{ range .Alerts }}
  18.           Alert: {{ .Annotations.summary }}
  19.           Description: {{ .Annotations.description }}
  20.           {{ end }}
  21.    
  22.     slack_configs:
  23.       - api_url: 'YOUR_SLACK_WEBHOOK_URL'
  24.         channel: '#alerts'
  25.         title: 'Alert: {{ .GroupLabels.alertname }}'
  26.         text: |
  27.           {{ range .Alerts }}
  28.           {{ .Annotations.summary }}
  29.           {{ .Annotations.description }}
  30.           {{ end }}
复制代码
10.4 总结

Node.js 应用的摆设与运维是一个复杂的系统工程,需要思量多个方面:
关键要点


  • 情况一致性:使用容器化技能确保开辟、测试、生产情况的一致性
  • 自动化流程:建立完备的 CI/CD 流水线,淘汰人工操作错误
  • 监控体系:建立全面的监控和告警系统,及时发现息争决题目
  • 安全防护:实施多层安全防护措施,保护应用和数据安全
  • 性能优化:通过负载均衡、缓存、数据库优化等手段提拔性能
  • 故障规复:建立完善的备份和规复机制,确保业务连续性
技能选型建议



  • 小型应用:PM2 + Nginx + 传统服务器摆设
  • 中型应用:Docker + Docker Compose + 云服务器
  • 大型应用:Kubernetes + 微服务架构 + 云原生技能栈
运维成熟度模型

     通过逐步实施这些最佳实践,可以构建一个稳固、可靠、高性能的 Node.js 应用运行情况,为业务发展提供坚固的技能保障。
结语
感谢您的阅读!等待您的一键三连!欢迎指正!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表