个人主页:Guiat
归属专栏:node.js
正文
1. Node.js 摆设概述
Node.js 应用的摆设与运维是将开辟完成的应用程序安全、稳固地运行在生产情况中的关键环节。良好的摆设计谋和运维实践能够确保应用的高可用性、可扩展性和安全性。
1.1 摆设的核心要素
- 情况一致性包管
- 应用程序的可靠性和稳固性
- 性能优化和资源管理
- 安全性和访问控制
- 监控和日志管理
- 自动化摆设流程
1.2 Node.js 摆设架构全景
2. 传统服务器摆设
2.1 Linux 服务器情况预备
系统更新与基础软件安装
- # Ubuntu/Debian 系统
- sudo apt update && sudo apt upgrade -y
- # 安装必要的软件包
- sudo apt install -y curl wget git build-essential
- # CentOS/RHEL 系统
- sudo yum update -y
- sudo yum install -y curl wget git gcc-c++ make
- # 安装 Node.js (使用 NodeSource 仓库)
- curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
- sudo apt-get install -y nodejs
- # 验证安装
- node --version
- npm --version
复制代码 创建应用用户
- # 创建专用的应用用户
- sudo useradd -m -s /bin/bash nodeapp
- sudo usermod -aG sudo nodeapp
- # 切换到应用用户
- sudo su - nodeapp
- # 创建应用目录
- mkdir -p ~/apps/myapp
- cd ~/apps/myapp
复制代码 2.2 应用摆设脚本
- #!/bin/bash
- # deploy.sh - 应用部署脚本
- set -e
- APP_NAME="myapp"
- APP_DIR="/home/nodeapp/apps/$APP_NAME"
- REPO_URL="https://github.com/username/myapp.git"
- BRANCH="main"
- NODE_ENV="production"
- echo "开始部署 $APP_NAME..."
- # 创建应用目录
- mkdir -p $APP_DIR
- cd $APP_DIR
- # 克隆或更新代码
- if [ -d ".git" ]; then
- echo "更新代码..."
- git fetch origin
- git reset --hard origin/$BRANCH
- else
- echo "克隆代码..."
- git clone -b $BRANCH $REPO_URL .
- fi
- # 安装依赖
- echo "安装依赖..."
- npm ci --production
- # 构建应用
- echo "构建应用..."
- npm run build
- # 设置环境变量
- echo "设置环境变量..."
- cp .env.example .env
- # 根据需要修改 .env 文件
- # 重启应用
- echo "重启应用..."
- pm2 restart $APP_NAME || pm2 start ecosystem.config.js
- echo "部署完成!"
复制代码 2.3 情况变量管理
- # .env.production
- NODE_ENV=production
- PORT=3000
- HOST=0.0.0.0
- # 数据库配置
- MONGODB_URI=mongodb://localhost:27017/myapp_prod
- REDIS_URL=redis://localhost:6379
- # 安全配置
- JWT_SECRET=your_super_secret_jwt_key_here
- SESSION_SECRET=your_session_secret_here
- # 第三方服务
- SMTP_HOST=smtp.gmail.com
- SMTP_PORT=587
- SMTP_USER=your_email@gmail.com
- SMTP_PASS=your_app_password
- # 日志配置
- LOG_LEVEL=info
- LOG_FILE=/var/log/myapp/app.log
- # 性能配置
- MAX_MEMORY=512M
- CLUSTER_WORKERS=auto
复制代码 2.4 Nginx 反向代理配置
- # /etc/nginx/sites-available/myapp
- server {
- listen 80;
- server_name yourdomain.com www.yourdomain.com;
-
- # 重定向到 HTTPS
- return 301 https://$server_name$request_uri;
- }
- server {
- listen 443 ssl http2;
- server_name yourdomain.com www.yourdomain.com;
-
- # SSL 证书配置
- ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
-
- # SSL 安全配置
- ssl_protocols TLSv1.2 TLSv1.3;
- ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
- ssl_prefer_server_ciphers off;
- ssl_session_cache shared:SSL:10m;
- ssl_session_timeout 10m;
-
- # 安全头
- 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=31536000; includeSubDomains" always;
-
- # 静态文件处理
- location /static/ {
- alias /home/nodeapp/apps/myapp/public/;
- expires 1y;
- add_header Cache-Control "public, immutable";
- }
-
- # API 代理
- location /api/ {
- proxy_pass http://127.0.0.1:3000;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection 'upgrade';
- 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_cache_bypass $http_upgrade;
-
- # 超时设置
- proxy_connect_timeout 60s;
- proxy_send_timeout 60s;
- proxy_read_timeout 60s;
- }
-
- # 主应用代理
- location / {
- proxy_pass http://127.0.0.1:3000;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection 'upgrade';
- 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_cache_bypass $http_upgrade;
- }
-
- # 健康检查
- location /health {
- access_log off;
- proxy_pass http://127.0.0.1:3000/health;
- }
- }
复制代码 2.5 SSL 证书配置
- # 安装 Certbot
- sudo apt install certbot python3-certbot-nginx
- # 获取 SSL 证书
- sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
- # 设置自动续期
- sudo crontab -e
- # 添加以下行
- 0 12 * * * /usr/bin/certbot renew --quiet
复制代码 3. PM2 进程管理
3.1 PM2 安装与根本使用
- # 全局安装 PM2
- npm install -g pm2
- # 启动应用
- pm2 start app.js --name "myapp"
- # 查看运行状态
- pm2 status
- # 查看日志
- pm2 logs myapp
- # 重启应用
- pm2 restart myapp
- # 停止应用
- pm2 stop myapp
- # 删除应用
- pm2 delete myapp
复制代码 3.2 PM2 配置文件
- // ecosystem.config.js
- module.exports = {
- apps: [
- {
- name: 'myapp',
- script: './src/index.js',
- instances: 'max', // 或者指定数字,如 4
- exec_mode: 'cluster',
- env: {
- NODE_ENV: 'development',
- PORT: 3000
- },
- env_production: {
- NODE_ENV: 'production',
- PORT: 3000,
- MONGODB_URI: 'mongodb://localhost:27017/myapp_prod'
- },
- // 日志配置
- log_file: './logs/combined.log',
- out_file: './logs/out.log',
- error_file: './logs/error.log',
- log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
-
- // 自动重启配置
- watch: false,
- ignore_watch: ['node_modules', 'logs'],
- max_memory_restart: '1G',
-
- // 实例配置
- min_uptime: '10s',
- max_restarts: 10,
-
- // 健康检查
- health_check_grace_period: 3000,
-
- // 环境变量
- env_file: '.env'
- }
- ],
-
- deploy: {
- production: {
- user: 'nodeapp',
- host: 'your-server.com',
- ref: 'origin/main',
- repo: 'https://github.com/username/myapp.git',
- path: '/home/nodeapp/apps/myapp',
- 'post-deploy': 'npm install && npm run build && pm2 reload ecosystem.config.js --env production'
- }
- }
- };
复制代码 3.3 PM2 集群模式
- // cluster.config.js
- module.exports = {
- apps: [
- {
- name: 'myapp-cluster',
- script: './src/index.js',
- instances: 4, // 4个工作进程
- exec_mode: 'cluster',
-
- // 负载均衡策略
- instance_var: 'INSTANCE_ID',
-
- // 集群配置
- kill_timeout: 5000,
- listen_timeout: 3000,
-
- // 内存和CPU限制
- max_memory_restart: '500M',
- node_args: '--max-old-space-size=512',
-
- env_production: {
- NODE_ENV: 'production',
- PORT: 3000
- }
- }
- ]
- };
复制代码 3.4 PM2 监控与管理
- # 实时监控
- pm2 monit
- # 查看详细信息
- pm2 show myapp
- # 重载所有应用(零停机时间)
- pm2 reload all
- # 保存当前进程列表
- pm2 save
- # 设置开机自启动
- pm2 startup
- pm2 save
- # 更新 PM2
- pm2 update
复制代码 3.5 PM2 日志管理
- # 查看实时日志
- pm2 logs
- # 查看特定应用日志
- pm2 logs myapp
- # 清空日志
- pm2 flush
- # 日志轮转
- pm2 install pm2-logrotate
- # 配置日志轮转
- pm2 set pm2-logrotate:max_size 10M
- pm2 set pm2-logrotate:retain 30
- pm2 set pm2-logrotate:compress true
复制代码 4. Docker 容器化摆设
4.1 Dockerfile 最佳实践
- # 多阶段构建 Dockerfile
- FROM node:18-alpine AS builder
- # 设置工作目录
- WORKDIR /app
- # 复制 package 文件
- COPY package*.json ./
- # 安装依赖
- RUN npm ci --only=production && npm cache clean --force
- # 复制源代码
- COPY . .
- # 构建应用
- RUN npm run build
- # 生产阶段
- FROM node:18-alpine AS production
- # 创建非 root 用户
- RUN addgroup -g 1001 -S nodejs
- RUN adduser -S nodeapp -u 1001
- # 设置工作目录
- WORKDIR /app
- # 复制构建产物和依赖
- COPY --from=builder --chown=nodeapp:nodejs /app/dist ./dist
- COPY --from=builder --chown=nodeapp:nodejs /app/node_modules ./node_modules
- COPY --from=builder --chown=nodeapp:nodejs /app/package*.json ./
- # 安装 dumb-init
- RUN apk add --no-cache dumb-init
- # 切换到非 root 用户
- USER nodeapp
- # 暴露端口
- EXPOSE 3000
- # 健康检查
- HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
- CMD node healthcheck.js
- # 启动应用
- ENTRYPOINT ["dumb-init", "--"]
- CMD ["node", "dist/index.js"]
复制代码 4.2 Docker Compose 配置
4.3 康健检查脚本
- // healthcheck.js
- const http = require('http');
- const options = {
- hostname: 'localhost',
- port: process.env.PORT || 3000,
- path: '/health',
- method: 'GET',
- timeout: 2000
- };
- const request = http.request(options, (res) => {
- if (res.statusCode === 200) {
- process.exit(0);
- } else {
- process.exit(1);
- }
- });
- request.on('error', () => {
- process.exit(1);
- });
- request.on('timeout', () => {
- request.destroy();
- process.exit(1);
- });
- request.end();
复制代码 4.4 Docker 摆设脚本
- #!/bin/bash
- # docker-deploy.sh
- set -e
- APP_NAME="myapp"
- IMAGE_NAME="myapp:latest"
- CONTAINER_NAME="myapp_container"
- echo "开始 Docker 部署..."
- # 构建镜像
- echo "构建 Docker 镜像..."
- docker build -t $IMAGE_NAME .
- # 停止并删除旧容器
- echo "停止旧容器..."
- docker stop $CONTAINER_NAME 2>/dev/null || true
- docker rm $CONTAINER_NAME 2>/dev/null || true
- # 启动新容器
- echo "启动新容器..."
- docker run -d \
- --name $CONTAINER_NAME \
- --restart unless-stopped \
- -p 3000:3000 \
- -e NODE_ENV=production \
- -v $(pwd)/logs:/app/logs \
- $IMAGE_NAME
- # 等待容器启动
- echo "等待容器启动..."
- sleep 10
- # 检查容器状态
- if docker ps | grep -q $CONTAINER_NAME; then
- echo "部署成功! 容器正在运行."
- docker logs --tail 20 $CONTAINER_NAME
- else
- echo "部署失败! 容器未能启动."
- docker logs $CONTAINER_NAME
- exit 1
- fi
复制代码 4.5 Docker 镜像优化
- # 优化后的 Dockerfile
- FROM node:18-alpine AS base
- RUN apk add --no-cache dumb-init
- WORKDIR /app
- COPY package*.json ./
- RUN npm ci --only=production && npm cache clean --force
- FROM base AS build
- RUN npm ci
- COPY . .
- RUN npm run build
- FROM base AS runtime
- COPY --from=build /app/dist ./dist
- RUN addgroup -g 1001 -S nodejs && adduser -S nodeapp -u 1001
- USER nodeapp
- EXPOSE 3000
- HEALTHCHECK --interval=30s CMD node healthcheck.js
- ENTRYPOINT ["dumb-init", "--"]
- CMD ["node", "dist/index.js"]
复制代码 5. Kubernetes 摆设
5.1 Kubernetes 基础配置
- # namespace.yaml
- apiVersion: v1
- kind: Namespace
- metadata:
- name: myapp
- labels:
- name: myapp
复制代码- # configmap.yaml
- apiVersion: v1
- kind: ConfigMap
- metadata:
- name: myapp-config
- namespace: myapp
- data:
- NODE_ENV: "production"
- PORT: "3000"
- LOG_LEVEL: "info"
复制代码- # secret.yaml
- apiVersion: v1
- kind: Secret
- metadata:
- name: myapp-secrets
- namespace: myapp
- type: Opaque
- data:
- # base64 编码的值
- MONGODB_URI: bW9uZ29kYjovL21vbmdvOjI3MDE3L215YXBw
- JWT_SECRET: eW91cl9qd3Rfc2VjcmV0X2hlcmU=
- REDIS_URL: cmVkaXM6Ly9yZWRpczozNjM3OQ==
复制代码 5.2 Deployment 配置
- # deployment.yaml
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: myapp-deployment
- namespace: myapp
- labels:
- app: myapp
- spec:
- replicas: 3
- selector:
- matchLabels:
- app: myapp
- template:
- metadata:
- labels:
- app: myapp
- spec:
- containers:
- - name: myapp
- image: myapp:latest
- ports:
- - containerPort: 3000
- env:
- - name: NODE_ENV
- valueFrom:
- configMapKeyRef:
- name: myapp-config
- key: NODE_ENV
- - name: PORT
- valueFrom:
- configMapKeyRef:
- name: myapp-config
- key: PORT
- - name: MONGODB_URI
- valueFrom:
- secretKeyRef:
- name: myapp-secrets
- key: MONGODB_URI
- - name: JWT_SECRET
- valueFrom:
- secretKeyRef:
- name: myapp-secrets
- key: JWT_SECRET
- resources:
- requests:
- memory: "256Mi"
- cpu: "250m"
- limits:
- memory: "512Mi"
- cpu: "500m"
- livenessProbe:
- httpGet:
- path: /health
- port: 3000
- initialDelaySeconds: 30
- periodSeconds: 10
- timeoutSeconds: 5
- failureThreshold: 3
- readinessProbe:
- httpGet:
- path: /ready
- port: 3000
- initialDelaySeconds: 5
- periodSeconds: 5
- timeoutSeconds: 3
- failureThreshold: 3
- volumeMounts:
- - name: app-logs
- mountPath: /app/logs
- volumes:
- - name: app-logs
- emptyDir: {}
- imagePullSecrets:
- - name: regcred
复制代码 5.3 Service 配置
- # service.yaml
- apiVersion: v1
- kind: Service
- metadata:
- name: myapp-service
- namespace: myapp
- labels:
- app: myapp
- spec:
- selector:
- app: myapp
- ports:
- - protocol: TCP
- port: 80
- targetPort: 3000
- type: ClusterIP
复制代码 5.4 Ingress 配置
- # ingress.yaml
- apiVersion: networking.k8s.io/v1
- kind: Ingress
- metadata:
- name: myapp-ingress
- namespace: myapp
- annotations:
- kubernetes.io/ingress.class: nginx
- cert-manager.io/cluster-issuer: letsencrypt-prod
- nginx.ingress.kubernetes.io/rate-limit: "100"
- nginx.ingress.kubernetes.io/rate-limit-window: "1m"
- spec:
- tls:
- - hosts:
- - yourdomain.com
- secretName: myapp-tls
- rules:
- - host: yourdomain.com
- http:
- paths:
- - path: /
- pathType: Prefix
- backend:
- service:
- name: myapp-service
- port:
- number: 80
复制代码 5.5 HorizontalPodAutoscaler
- # hpa.yaml
- apiVersion: autoscaling/v2
- kind: HorizontalPodAutoscaler
- metadata:
- name: myapp-hpa
- namespace: myapp
- spec:
- scaleTargetRef:
- apiVersion: apps/v1
- kind: Deployment
- name: myapp-deployment
- minReplicas: 2
- maxReplicas: 10
- metrics:
- - type: Resource
- resource:
- name: cpu
- target:
- type: Utilization
- averageUtilization: 70
- - type: Resource
- resource:
- name: memory
- target:
- type: Utilization
- averageUtilization: 80
- behavior:
- scaleDown:
- stabilizationWindowSeconds: 300
- policies:
- - type: Percent
- value: 10
- periodSeconds: 60
- scaleUp:
- stabilizationWindowSeconds: 0
- policies:
- - type: Percent
- value: 100
- periodSeconds: 15
- - type: Pods
- value: 4
- periodSeconds: 15
- selectPolicy: Max
复制代码 5.6 Kubernetes 摆设流程
6. 监控与日志
6.1 应用监控
Prometheus 配置
- # prometheus.yml
- global:
- scrape_interval: 15s
- evaluation_interval: 15s
- rule_files:
- - "rules/*.yml"
- scrape_configs:
- - job_name: 'myapp'
- static_configs:
- - targets: ['localhost:3000']
- metrics_path: '/metrics'
- scrape_interval: 5s
- - job_name: 'node-exporter'
- static_configs:
- - targets: ['localhost:9100']
- alerting:
- alertmanagers:
- - static_configs:
- - targets:
- - alertmanager:9093
复制代码 Node.js 应用集成 Prometheus
- // metrics.js
- const promClient = require('prom-client');
- // 创建注册表
- const register = new promClient.Registry();
- // 添加默认指标
- promClient.collectDefaultMetrics({
- register,
- prefix: 'myapp_'
- });
- // 自定义指标
- const httpRequestDuration = new promClient.Histogram({
- name: 'myapp_http_request_duration_seconds',
- help: 'HTTP request duration in seconds',
- labelNames: ['method', 'route', 'status_code'],
- buckets: [0.1, 0.5, 1, 2, 5]
- });
- const httpRequestTotal = new promClient.Counter({
- name: 'myapp_http_requests_total',
- help: 'Total number of HTTP requests',
- labelNames: ['method', 'route', 'status_code']
- });
- const activeConnections = new promClient.Gauge({
- name: 'myapp_active_connections',
- help: 'Number of active connections'
- });
- // 注册指标
- register.registerMetric(httpRequestDuration);
- register.registerMetric(httpRequestTotal);
- register.registerMetric(activeConnections);
- // 中间件
- const metricsMiddleware = (req, res, next) => {
- const start = Date.now();
-
- res.on('finish', () => {
- const duration = (Date.now() - start) / 1000;
- const route = req.route ? req.route.path : req.path;
-
- httpRequestDuration
- .labels(req.method, route, res.statusCode)
- .observe(duration);
-
- httpRequestTotal
- .labels(req.method, route, res.statusCode)
- .inc();
- });
-
- next();
- };
- module.exports = {
- register,
- metricsMiddleware,
- activeConnections
- };
复制代码 6.2 日志管理
Winston 日志配置
- // logger.js
- const winston = require('winston');
- const path = require('path');
- // 自定义日志格式
- const logFormat = winston.format.combine(
- winston.format.timestamp({
- format: 'YYYY-MM-DD HH:mm:ss'
- }),
- winston.format.errors({ stack: true }),
- winston.format.json()
- );
- // 创建 logger
- const logger = winston.createLogger({
- level: process.env.LOG_LEVEL || 'info',
- format: logFormat,
- defaultMeta: {
- service: 'myapp',
- version: process.env.APP_VERSION || '1.0.0'
- },
- transports: [
- // 错误日志
- new winston.transports.File({
- filename: path.join(__dirname, '../logs/error.log'),
- level: 'error',
- maxsize: 10485760, // 10MB
- maxFiles: 5,
- tailable: true
- }),
-
- // 组合日志
- new winston.transports.File({
- filename: path.join(__dirname, '../logs/combined.log'),
- maxsize: 10485760, // 10MB
- maxFiles: 5,
- tailable: true
- }),
-
- // 控制台输出
- new winston.transports.Console({
- format: winston.format.combine(
- winston.format.colorize(),
- winston.format.simple()
- )
- })
- ]
- });
- // 请求日志中间件
- const requestLogger = (req, res, next) => {
- const start = Date.now();
-
- res.on('finish', () => {
- const duration = Date.now() - start;
-
- logger.info('HTTP Request', {
- method: req.method,
- url: req.url,
- statusCode: res.statusCode,
- duration: `${duration}ms`,
- userAgent: req.get('User-Agent'),
- ip: req.ip,
- userId: req.user?.id
- });
- });
-
- next();
- };
- module.exports = { logger, requestLogger };
复制代码 ELK Stack 配置
- # docker-compose.elk.yml
- version: '3.8'
- services:
- elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:8.8.0
- container_name: elasticsearch
- environment:
- - discovery.type=single-node
- - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- - xpack.security.enabled=false
- ports:
- - "9200:9200"
- volumes:
- - elasticsearch_data:/usr/share/elasticsearch/data
- logstash:
- image: docker.elastic.co/logstash/logstash:8.8.0
- container_name: logstash
- ports:
- - "5044:5044"
- volumes:
- - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
- depends_on:
- - elasticsearch
- kibana:
- image: docker.elastic.co/kibana/kibana:8.8.0
- container_name: kibana
- ports:
- - "5601:5601"
- environment:
- - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- depends_on:
- - elasticsearch
- volumes:
- elasticsearch_data:
复制代码- # logstash.conf
- input {
- beats {
- port => 5044
- }
- }
- filter {
- if [fields][service] == "myapp" {
- json {
- source => "message"
- }
-
- date {
- match => [ "timestamp", "yyyy-MM-dd HH:mm:ss" ]
- }
-
- mutate {
- remove_field => [ "message", "host", "agent", "ecs", "log", "input" ]
- }
- }
- }
- output {
- elasticsearch {
- hosts => ["elasticsearch:9200"]
- index => "myapp-logs-%{+YYYY.MM.dd}"
- }
- }
复制代码 6.3 APM 监控
New Relic 集成
- // newrelic.js
- 'use strict';
- exports.config = {
- app_name: ['MyApp'],
- license_key: process.env.NEW_RELIC_LICENSE_KEY,
- logging: {
- level: 'info'
- },
- allow_all_headers: true,
- attributes: {
- exclude: [
- 'request.headers.cookie',
- 'request.headers.authorization',
- 'request.headers.proxyAuthorization',
- 'request.headers.setCookie*',
- 'request.headers.x*',
- 'response.headers.cookie',
- 'response.headers.authorization',
- 'response.headers.proxyAuthorization',
- 'response.headers.setCookie*',
- 'response.headers.x*'
- ]
- }
- };
复制代码 Datadog 集成
- // app.js
- const tracer = require('dd-trace').init({
- service: 'myapp',
- env: process.env.NODE_ENV,
- version: process.env.APP_VERSION
- });
- const express = require('express');
- const app = express();
- // Datadog 中间件
- app.use((req, res, next) => {
- const span = tracer.scope().active();
- if (span) {
- span.setTag('user.id', req.user?.id);
- span.setTag('http.route', req.route?.path);
- }
- next();
- });
复制代码 6.4 康健检查端点
- // health.js
- const mongoose = require('mongoose');
- const redis = require('redis');
- const healthCheck = async (req, res) => {
- const health = {
- status: 'ok',
- timestamp: new Date().toISOString(),
- uptime: process.uptime(),
- version: process.env.APP_VERSION || '1.0.0',
- checks: {}
- };
- try {
- // 数据库连接检查
- if (mongoose.connection.readyState === 1) {
- health.checks.database = { status: 'ok' };
- } else {
- health.checks.database = { status: 'error', message: 'Database not connected' };
- health.status = 'error';
- }
- // Redis 连接检查
- const redisClient = redis.createClient();
- try {
- await redisClient.ping();
- health.checks.redis = { status: 'ok' };
- await redisClient.quit();
- } catch (error) {
- health.checks.redis = { status: 'error', message: error.message };
- health.status = 'error';
- }
- // 内存使用检查
- const memUsage = process.memoryUsage();
- health.checks.memory = {
- status: 'ok',
- usage: {
- rss: `${Math.round(memUsage.rss / 1024 / 1024)}MB`,
- heapTotal: `${Math.round(memUsage.heapTotal / 1024 / 1024)}MB`,
- heapUsed: `${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`
- }
- };
- const statusCode = health.status === 'ok' ? 200 : 503;
- res.status(statusCode).json(health);
- } catch (error) {
- res.status(503).json({
- status: 'error',
- message: error.message,
- timestamp: new Date().toISOString()
- });
- }
- };
- module.exports = { healthCheck };
复制代码 7. 性能优化与扩展
7.1 负载均衡配置
Nginx 负载均衡
- # nginx.conf
- upstream myapp_backend {
- least_conn;
- server 127.0.0.1:3001 weight=3 max_fails=3 fail_timeout=30s;
- server 127.0.0.1:3002 weight=3 max_fails=3 fail_timeout=30s;
- server 127.0.0.1:3003 weight=2 max_fails=3 fail_timeout=30s;
- server 127.0.0.1:3004 backup;
- }
- server {
- listen 80;
- server_name yourdomain.com;
-
- # 连接限制
- limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
- limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
-
- location / {
- limit_conn conn_limit_per_ip 10;
- limit_req zone=req_limit_per_ip burst=10 nodelay;
-
- proxy_pass http://myapp_backend;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection 'upgrade';
- 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_cache_bypass $http_upgrade;
-
- # 超时设置
- proxy_connect_timeout 5s;
- proxy_send_timeout 60s;
- proxy_read_timeout 60s;
-
- # 缓存设置
- proxy_cache my_cache;
- proxy_cache_valid 200 302 10m;
- proxy_cache_valid 404 1m;
- }
- }
复制代码 HAProxy 配置
- # haproxy.cfg
- global
- daemon
- maxconn 4096
- log stdout local0
- defaults
- mode http
- timeout connect 5000ms
- timeout client 50000ms
- timeout server 50000ms
- option httplog
- frontend myapp_frontend
- bind *:80
- default_backend myapp_backend
- backend myapp_backend
- balance roundrobin
- option httpchk GET /health
- server app1 127.0.0.1:3001 check
- server app2 127.0.0.1:3002 check
- server app3 127.0.0.1:3003 check
- server app4 127.0.0.1:3004 check backup
复制代码 7.2 缓存计谋
Redis 缓存实现
- // cache.js
- const redis = require('redis');
- const client = redis.createClient({
- url: process.env.REDIS_URL
- });
- client.on('error', (err) => {
- console.error('Redis Client Error', err);
- });
- client.connect();
- // 缓存中间件
- const cacheMiddleware = (duration = 300) => {
- return async (req, res, next) => {
- if (req.method !== 'GET') {
- return next();
- }
- const key = `cache:${req.originalUrl}`;
-
- try {
- const cached = await client.get(key);
-
- if (cached) {
- return res.json(JSON.parse(cached));
- }
- // 重写 res.json 方法
- const originalJson = res.json;
- res.json = function(data) {
- // 缓存响应数据
- client.setEx(key, duration, JSON.stringify(data));
- return originalJson.call(this, data);
- };
- next();
- } catch (error) {
- console.error('Cache error:', error);
- next();
- }
- };
- };
- // 缓存失效
- const invalidateCache = async (pattern) => {
- try {
- const keys = await client.keys(pattern);
- if (keys.length > 0) {
- await client.del(keys);
- }
- } catch (error) {
- console.error('Cache invalidation error:', error);
- }
- };
- module.exports = {
- client,
- cacheMiddleware,
- invalidateCache
- };
复制代码 7.3 数据库优化
MongoDB 优化
- // database.js
- const mongoose = require('mongoose');
- // 连接配置
- const connectDB = async () => {
- try {
- const conn = await mongoose.connect(process.env.MONGODB_URI, {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- maxPoolSize: 10, // 连接池大小
- serverSelectionTimeoutMS: 5000,
- socketTimeoutMS: 45000,
- bufferCommands: false,
- bufferMaxEntries: 0
- });
- console.log(`MongoDB Connected: ${conn.connection.host}`);
- } catch (error) {
- console.error('Database connection error:', error);
- process.exit(1);
- }
- };
- // 索引创建
- const createIndexes = async () => {
- try {
- // 用户集合索引
- await mongoose.connection.db.collection('users').createIndexes([
- { key: { email: 1 }, unique: true },
- { key: { username: 1 }, unique: true },
- { key: { createdAt: -1 } }
- ]);
- // 产品集合索引
- await mongoose.connection.db.collection('products').createIndexes([
- { key: { name: 'text', description: 'text' } },
- { key: { category: 1, price: 1 } },
- { key: { createdAt: -1 } }
- ]);
- console.log('Database indexes created');
- } catch (error) {
- console.error('Index creation error:', error);
- }
- };
- module.exports = { connectDB, createIndexes };
复制代码 7.4 性能监控
8. 安全与备份
8.1 安全配置
应用安全中心件
- // security.js
- const helmet = require('helmet');
- const rateLimit = require('express-rate-limit');
- const mongoSanitize = require('express-mongo-sanitize');
- const xss = require('xss-clean');
- // 安全配置
- const configureSecurity = (app) => {
- // 设置安全头
- app.use(helmet({
- contentSecurityPolicy: {
- directives: {
- defaultSrc: ["'self'"],
- styleSrc: ["'self'", "'unsafe-inline'"],
- scriptSrc: ["'self'"],
- imgSrc: ["'self'", "data:", "https:"],
- },
- },
- hsts: {
- maxAge: 31536000,
- includeSubDomains: true,
- preload: true
- }
- }));
- // 速率限制
- const limiter = rateLimit({
- windowMs: 15 * 60 * 1000, // 15分钟
- max: 100, // 限制每个IP 100个请求
- message: {
- error: 'Too many requests, please try again later.'
- },
- standardHeaders: true,
- legacyHeaders: false,
- });
- app.use('/api/', limiter);
- // 严格的认证限制
- const authLimiter = rateLimit({
- windowMs: 60 * 60 * 1000, // 1小时
- max: 5, // 限制每个IP 5次尝试
- skipSuccessfulRequests: true,
- });
- app.use('/api/auth/login', authLimiter);
- // 防止 NoSQL 注入
- app.use(mongoSanitize());
- // 防止 XSS 攻击
- app.use(xss());
- };
- module.exports = { configureSecurity };
复制代码 SSL/TLS 配置
- // ssl-server.js
- const https = require('https');
- const fs = require('fs');
- const express = require('express');
- const app = express();
- // SSL 证书配置
- const sslOptions = {
- key: fs.readFileSync('/path/to/private-key.pem'),
- cert: fs.readFileSync('/path/to/certificate.pem'),
- ca: fs.readFileSync('/path/to/ca-certificate.pem'), // 可选
-
- // 安全配置
- secureProtocol: 'TLSv1_2_method',
- ciphers: [
- 'ECDHE-RSA-AES256-GCM-SHA512',
- 'DHE-RSA-AES256-GCM-SHA512',
- 'ECDHE-RSA-AES256-GCM-SHA384',
- 'DHE-RSA-AES256-GCM-SHA384',
- 'ECDHE-RSA-AES256-SHA384'
- ].join(':'),
- honorCipherOrder: true
- };
- // 创建 HTTPS 服务器
- const server = https.createServer(sslOptions, app);
- server.listen(443, () => {
- console.log('HTTPS Server running on port 443');
- });
复制代码 8.2 数据备份计谋
MongoDB 备份脚本
- #!/bin/bash
- # mongodb-backup.sh
- set -e
- # 配置
- DB_NAME="myapp"
- BACKUP_DIR="/backup/mongodb"
- DATE=$(date +%Y%m%d_%H%M%S)
- BACKUP_NAME="mongodb_backup_${DATE}"
- RETENTION_DAYS=7
- # 创建备份目录
- mkdir -p $BACKUP_DIR
- # 执行备份
- echo "开始备份 MongoDB 数据库: $DB_NAME"
- mongodump --db $DB_NAME --out $BACKUP_DIR/$BACKUP_NAME
- # 压缩备份
- echo "压缩备份文件..."
- tar -czf $BACKUP_DIR/${BACKUP_NAME}.tar.gz -C $BACKUP_DIR $BACKUP_NAME
- rm -rf $BACKUP_DIR/$BACKUP_NAME
- # 清理旧备份
- echo "清理 $RETENTION_DAYS 天前的备份..."
- find $BACKUP_DIR -name "mongodb_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
- # 上传到云存储 (可选)
- if [ ! -z "$AWS_S3_BUCKET" ]; then
- echo "上传备份到 S3..."
- aws s3 cp $BACKUP_DIR/${BACKUP_NAME}.tar.gz s3://$AWS_S3_BUCKET/mongodb-backups/
- fi
- echo "备份完成: ${BACKUP_NAME}.tar.gz"
复制代码 自动化备份配置
- # 添加到 crontab
- # 每天凌晨 2 点执行备份
- 0 2 * * * /path/to/mongodb-backup.sh >> /var/log/mongodb-backup.log 2>&1
- # 每周日凌晨 3 点执行完整备份
- 0 3 * * 0 /path/to/full-backup.sh >> /var/log/full-backup.log 2>&1
复制代码 8.3 灾难规复
规复脚本
- #!/bin/bash
- # mongodb-restore.sh
- set -e
- BACKUP_FILE=$1
- DB_NAME="myapp"
- TEMP_DIR="/tmp/mongodb_restore"
- if [ -z "$BACKUP_FILE" ]; then
- echo "用法: $0 <backup_file.tar.gz>"
- exit 1
- fi
- echo "开始恢复 MongoDB 数据库..."
- # 创建临时目录
- mkdir -p $TEMP_DIR
- # 解压备份文件
- echo "解压备份文件..."
- tar -xzf $BACKUP_FILE -C $TEMP_DIR
- # 获取备份目录名
- BACKUP_DIR=$(ls $TEMP_DIR)
- # 停止应用服务
- echo "停止应用服务..."
- pm2 stop myapp
- # 恢复数据库
- echo "恢复数据库..."
- mongorestore --db $DB_NAME --drop $TEMP_DIR/$BACKUP_DIR/$DB_NAME
- # 启动应用服务
- echo "启动应用服务..."
- pm2 start myapp
- # 清理临时文件
- rm -rf $TEMP_DIR
- echo "数据库恢复完成!"
复制代码 9. 故障排查与调试
9.1 常见题目诊断
内存走漏检测
- // memory-monitor.js
- const v8 = require('v8');
- const fs = require('fs');
- // 内存使用监控
- const monitorMemory = () => {
- const usage = process.memoryUsage();
- const heapStats = v8.getHeapStatistics();
-
- console.log('Memory Usage:', {
- rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
- heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
- heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
- external: `${Math.round(usage.external / 1024 / 1024)}MB`,
- heapLimit: `${Math.round(heapStats.heap_size_limit / 1024 / 1024)}MB`
- });
-
- // 如果内存使用超过阈值,生成堆快照
- if (usage.heapUsed > 500 * 1024 * 1024) { // 500MB
- const snapshot = v8.writeHeapSnapshot();
- console.log('Heap snapshot written to:', snapshot);
- }
- };
- // 每分钟检查一次内存使用
- setInterval(monitorMemory, 60000);
- module.exports = { monitorMemory };
复制代码 性能分析
- // profiler.js
- const inspector = require('inspector');
- const fs = require('fs');
- const path = require('path');
- class Profiler {
- constructor() {
- this.session = null;
- }
- start() {
- this.session = new inspector.Session();
- this.session.connect();
-
- // 启用 CPU 分析器
- this.session.post('Profiler.enable');
- this.session.post('Profiler.start');
-
- console.log('CPU profiler started');
- }
- async stop() {
- if (!this.session) return;
- return new Promise((resolve, reject) => {
- this.session.post('Profiler.stop', (err, { profile }) => {
- if (err) {
- reject(err);
- return;
- }
- const filename = `cpu-profile-${Date.now()}.cpuprofile`;
- const filepath = path.join(__dirname, '../profiles', filename);
-
- fs.writeFileSync(filepath, JSON.stringify(profile));
- console.log('CPU profile saved to:', filepath);
-
- this.session.disconnect();
- resolve(filepath);
- });
- });
- }
- }
- module.exports = Profiler;
复制代码 9.2 日志分析
错误追踪
- // error-tracker.js
- const Sentry = require('@sentry/node');
- // 初始化 Sentry
- Sentry.init({
- dsn: process.env.SENTRY_DSN,
- environment: process.env.NODE_ENV,
- tracesSampleRate: 1.0,
- });
- // 错误处理中间件
- const errorHandler = (err, req, res, next) => {
- // 记录错误到 Sentry
- Sentry.captureException(err, {
- user: {
- id: req.user?.id,
- email: req.user?.email
- },
- request: {
- method: req.method,
- url: req.url,
- headers: req.headers
- }
- });
- // 记录到本地日志
- console.error('Error:', {
- message: err.message,
- stack: err.stack,
- url: req.url,
- method: req.method,
- userId: req.user?.id,
- timestamp: new Date().toISOString()
- });
- // 返回错误响应
- const statusCode = err.statusCode || 500;
- res.status(statusCode).json({
- error: {
- message: process.env.NODE_ENV === 'production'
- ? 'Internal Server Error'
- : err.message,
- status: statusCode
- }
- });
- };
- module.exports = { errorHandler };
复制代码 9.3 调试工具
长途调试配置
- # 启动应用时启用调试模式
- node --inspect=0.0.0.0:9229 src/index.js
- # 或者使用 PM2
- pm2 start src/index.js --node-args="--inspect=0.0.0.0:9229"
复制代码 调试脚本
- // debug-utils.js
- const util = require('util');
- const fs = require('fs');
- // 深度日志记录
- const deepLog = (obj, label = 'Debug') => {
- console.log(`\n=== ${label} ===`);
- console.log(util.inspect(obj, {
- depth: null,
- colors: true,
- showHidden: false
- }));
- console.log('='.repeat(label.length + 8));
- };
- // 性能计时器
- class Timer {
- constructor(label) {
- this.label = label;
- this.start = process.hrtime.bigint();
- }
- end() {
- const end = process.hrtime.bigint();
- const duration = Number(end - this.start) / 1000000; // 转换为毫秒
- console.log(`${this.label}: ${duration.toFixed(2)}ms`);
- return duration;
- }
- }
- // 内存快照
- const takeMemorySnapshot = () => {
- const usage = process.memoryUsage();
- const snapshot = {
- timestamp: new Date().toISOString(),
- pid: process.pid,
- memory: {
- rss: usage.rss,
- heapTotal: usage.heapTotal,
- heapUsed: usage.heapUsed,
- external: usage.external
- }
- };
-
- const filename = `memory-snapshot-${Date.now()}.json`;
- fs.writeFileSync(filename, JSON.stringify(snapshot, null, 2));
- console.log('Memory snapshot saved to:', filename);
-
- return snapshot;
- };
- module.exports = {
- deepLog,
- Timer,
- takeMemorySnapshot
- };
复制代码 10. 最佳实践与总结
10.1 摆设最佳实践
摆设检查清单
情况管理计谋
- // config/environments.js
- const environments = {
- development: {
- port: 3000,
- database: {
- uri: 'mongodb://localhost:27017/myapp_dev',
- options: {
- useNewUrlParser: true,
- useUnifiedTopology: true
- }
- },
- redis: {
- url: 'redis://localhost:6379'
- },
- logging: {
- level: 'debug',
- console: true
- },
- security: {
- cors: {
- origin: '*'
- }
- }
- },
-
- staging: {
- port: process.env.PORT || 3000,
- database: {
- uri: process.env.MONGODB_URI,
- options: {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- maxPoolSize: 5
- }
- },
- redis: {
- url: process.env.REDIS_URL
- },
- logging: {
- level: 'info',
- console: false,
- file: true
- },
- security: {
- cors: {
- origin: process.env.ALLOWED_ORIGINS?.split(',') || []
- }
- }
- },
-
- production: {
- port: process.env.PORT || 3000,
- database: {
- uri: process.env.MONGODB_URI,
- options: {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- maxPoolSize: 10,
- serverSelectionTimeoutMS: 5000,
- socketTimeoutMS: 45000
- }
- },
- redis: {
- url: process.env.REDIS_URL,
- options: {
- retryDelayOnFailover: 100,
- maxRetriesPerRequest: 3
- }
- },
- logging: {
- level: 'warn',
- console: false,
- file: true,
- sentry: true
- },
- security: {
- cors: {
- origin: process.env.ALLOWED_ORIGINS?.split(',') || []
- },
- rateLimit: {
- windowMs: 15 * 60 * 1000,
- max: 100
- }
- }
- }
- };
- module.exports = environments[process.env.NODE_ENV || 'development'];
复制代码 10.2 运维自动化
自动化摆设脚本
10.3 监控告警配置
Prometheus 告警规则
- # alerts.yml
- groups:
- - name: myapp.rules
- rules:
- - alert: HighErrorRate
- expr: rate(myapp_http_requests_total{status_code=~"5.."}[5m]) > 0.1
- for: 5m
- labels:
- severity: critical
- annotations:
- summary: "High error rate detected"
- description: "Error rate is {{ $value }} errors per second"
- - alert: HighResponseTime
- expr: histogram_quantile(0.95, rate(myapp_http_request_duration_seconds_bucket[5m])) > 1
- for: 5m
- labels:
- severity: warning
- annotations:
- summary: "High response time detected"
- description: "95th percentile response time is {{ $value }} seconds"
- - alert: HighMemoryUsage
- expr: (myapp_process_resident_memory_bytes / 1024 / 1024) > 512
- for: 10m
- labels:
- severity: warning
- annotations:
- summary: "High memory usage"
- description: "Memory usage is {{ $value }}MB"
- - alert: ServiceDown
- expr: up{job="myapp"} == 0
- for: 1m
- labels:
- severity: critical
- annotations:
- summary: "Service is down"
- description: "MyApp service is not responding"
复制代码 告警通知配置
- # alertmanager.yml
- global:
- smtp_smarthost: 'smtp.gmail.com:587'
- smtp_from: 'alerts@yourdomain.com'
- route:
- group_by: ['alertname']
- group_wait: 10s
- group_interval: 10s
- repeat_interval: 1h
- receiver: 'web.hook'
- receivers:
- - name: 'web.hook'
- email_configs:
- - to: 'admin@yourdomain.com'
- subject: 'Alert: {{ .GroupLabels.alertname }}'
- body: |
- {{ range .Alerts }}
- Alert: {{ .Annotations.summary }}
- Description: {{ .Annotations.description }}
- {{ end }}
-
- slack_configs:
- - api_url: 'YOUR_SLACK_WEBHOOK_URL'
- channel: '#alerts'
- title: 'Alert: {{ .GroupLabels.alertname }}'
- text: |
- {{ range .Alerts }}
- {{ .Annotations.summary }}
- {{ .Annotations.description }}
- {{ end }}
复制代码 10.4 总结
Node.js 应用的摆设与运维是一个复杂的系统工程,需要思量多个方面:
关键要点
- 情况一致性:使用容器化技能确保开辟、测试、生产情况的一致性
- 自动化流程:建立完备的 CI/CD 流水线,淘汰人工操作错误
- 监控体系:建立全面的监控和告警系统,及时发现息争决题目
- 安全防护:实施多层安全防护措施,保护应用和数据安全
- 性能优化:通过负载均衡、缓存、数据库优化等手段提拔性能
- 故障规复:建立完善的备份和规复机制,确保业务连续性
技能选型建议
- 小型应用:PM2 + Nginx + 传统服务器摆设
- 中型应用:Docker + Docker Compose + 云服务器
- 大型应用:Kubernetes + 微服务架构 + 云原生技能栈
运维成熟度模型
通过逐步实施这些最佳实践,可以构建一个稳固、可靠、高性能的 Node.js 应用运行情况,为业务发展提供坚固的技能保障。
结语
感谢您的阅读!等待您的一键三连!欢迎指正!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |