Site Tools


Hotfix release available: 2025-05-14b "Librarian". upgrade now! [56.2] (what's this?)
project:deployment:start

🚀 DV IT Infrastructure Platform - 生产环境部署指南

更新日期: 2025年1月 项目: DV IT Infrastructure Platform 部署方式: 生产环境实际部署 域名: oasisvape.co.nz + 二级域名


🎯 部署概述

生产环境信息

  • VPS IP: 156.67.214.225
  • 操作系统: Ubuntu (Linux 6.11.0-29-generic)
  • Docker Compose: v2 (docker compose)
  • 时区: Pacific/Auckland
  • 部署路径: /opt/discountvapor-it/

核心原则

  • 容器化部署: 所有服务使用Docker Compose部署
  • 自动化SSL: 通过Traefik自动管理Let's Encrypt证书
  • 网络安全: 仅开放必要端口,通过反向代理访问
  • 监控集成: 完整的监控栈 (cAdvisor + Prometheus + Grafana)

📋 服务版本清单

核心服务

数据库服务

服务 版本 用途 架构
MariaDB 10.6 ERPNext专用数据库 独立实例
PostgreSQL 15-alpine Strapi + n8n共享数据库 共享实例,Schema隔离

🏗️ 生产环境部署

1. 环境准备

# 创建项目目录
mkdir -p /opt/discountvapor-it
cd /opt/discountvapor-it
 
# 创建必要目录
mkdir -p data logs traefik config_backup scripts wiki

2. 网络配置

# 创建Traefik网络
docker network create traefik-network
 
# 创建Strapi网络
docker network create strapi_network
 
# 创建ERPNext网络
docker network create discountvapor-it_erpnext-network
 
# 创建监控网络
docker network create monitoring

3. 环境变量配置

# .env 文件
ERPNEXT_VERSION=v15.67.5
DB_PASSWORD=35@Riccarton
LETSENCRYPT_EMAIL=discountvapor2025@gmail.com
SITES=erp.oasisvape.co.nz

🐳 服务部署配置

1. Traefik 反向代理

# docker-compose-traefik.yml
version: '3.8'
services:
  traefik:
    image: traefik:v2.10
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
      - traefik_certs:/letsencrypt
    networks:
      - traefik-network
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.rule=Host(`traefik.oasisvape.co.nz`)
      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.tls.certresolver=letsencrypt
      - traefik.http.services.traefik.loadbalancer.server.port=8080

volumes:
  traefik_certs:
    driver: local

networks:
  traefik-network:

2. Traefik 配置文件

# traefik/traefik.yml
api:
  dashboard: true
  insecure: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: traefik-network

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@discountvapor.co.nz
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

log:
  level: INFO

3. ERPNext 部署

# docker-compose-erpnext.yml
version: '3.8'
services:
  database:
    image: mariadb:10.6
    restart: unless-stopped
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
      - --skip-innodb-read-only-compressed
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - erpnext-network
    healthcheck:
      test: mysqladmin ping -h localhost --password=${DB_PASSWORD}
      interval: 1s
      retries: 15

  redis-cache:
    image: redis:6.2-alpine
    restart: unless-stopped
    volumes:
      - redis-cache-data:/data
    networks:
      - erpnext-network

  redis-queue:
    image: redis:6.2-alpine
    restart: unless-stopped
    volumes:
      - redis-queue-data:/data
    networks:
      - erpnext-network

  configurator:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: "no"
    entrypoint:
      - bash
      - -c
    command:
      - >
        ls -1 apps > sites/apps.txt;
        bench set-config -g db_host database;
        bench set-config -gp db_port 3306;
        bench set-config -g redis_cache "redis://redis-cache:6379";
        bench set-config -g redis_queue "redis://redis-queue:6379";
        bench set-config -g redis_socketio "redis://redis-queue:6379";
        bench set-config -gp socketio_port 9000;
    depends_on:
      database:
        condition: service_healthy
      redis-cache:
        condition: service_started
      redis-queue:
        condition: service_started
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

  backend:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    depends_on:
      configurator:
        condition: service_completed_successfully
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

  frontend:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    command:
      - nginx-entrypoint.sh
    environment:
      BACKEND: backend:8000
      FRAPPE_SITE_NAME_HEADER: ${SITES}
      SOCKETIO: websocket:9000
      UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
      UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
      UPSTREAM_REAL_IP_RECURSIVE: "off"
      PROXY_READ_TIMEOUT: 120
      CLIENT_MAX_BODY_SIZE: 50m
    depends_on:
      backend:
        condition: service_started
      websocket:
        condition: service_started
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    ports:
      - "8080:8080"
    labels:
      - traefik.enable=true
      - traefik.http.routers.erpnext.rule=Host(`erp.oasisvape.co.nz`)
      - traefik.http.routers.erpnext.entrypoints=websecure
      - traefik.http.routers.erpnext.tls.certresolver=letsencrypt
      - traefik.http.services.erpnext.loadbalancer.server.port=8080
      - traefik.docker.network=traefik-network
    networks:
      - erpnext-network
      - traefik-network
 
  # 其他ERPNext服务...
  queue-default:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    command:
      - bench
      - worker
      - --queue
      - default
    depends_on:
      configurator:
        condition: service_completed_successfully
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

  queue-short:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    command:
      - bench
      - worker
      - --queue
      - short
    depends_on:
      configurator:
        condition: service_completed_successfully
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

  queue-long:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    command:
      - bench
      - worker
      - --queue
      - long
    depends_on:
      configurator:
        condition: service_completed_successfully
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

  scheduler:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    command:
      - bench
      - schedule
    depends_on:
      configurator:
        condition: service_completed_successfully
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

  websocket:
    image: frappe/erpnext:${ERPNEXT_VERSION}
    restart: unless-stopped
    command:
      - node
      - /home/frappe/frappe-bench/apps/frappe/socketio.js
    depends_on:
      configurator:
        condition: service_completed_successfully
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    networks:
      - erpnext-network

volumes:
  sites:
  db-data:
  redis-cache-data:
  redis-queue-data:

networks:
  erpnext-network:
    driver: bridge
  traefik-network:
    external: true

4. Strapi CMS 部署

# docker-compose-strapi.yml
version: '3.8'
services:
  strapi:
    image: strapi_infra-strapi:latest
    container_name: strapi_cms
    restart: unless-stopped
    environment:
      DATABASE_CLIENT: postgres
      DATABASE_HOST: strapi_postgres
      DATABASE_PORT: 5432
      DATABASE_NAME: strapi_db
      DATABASE_USERNAME: strapi_user
      DATABASE_PASSWORD: 35@Riccarton
      DATABASE_SSL: false
      DATABASE_SCHEMA: strapi_schema
      NODE_ENV: production
      HOST: 0.0.0.0
      PORT: 1337
      TZ: Pacific/Auckland
    volumes:
      - strapi_app_data:/srv/app
    labels:
      - traefik.enable=true
      - traefik.http.routers.strapi.rule=Host(`cms.oasisvape.co.nz`)
      - traefik.http.routers.strapi.entrypoints=websecure
      - traefik.http.routers.strapi.tls.certresolver=letsencrypt
      - traefik.http.services.strapi.loadbalancer.server.port=1337
    networks:
      - strapi_network
      - traefik-network

volumes:
  strapi_app_data:
    driver: local

networks:
  strapi_network:
    external: true
  traefik-network:

5. n8n 工作流自动化

# docker-compose-n8n.yml
version: '3.8'
services:
  n8n:
    image: n8nio/n8n:1.100.1
    restart: unless-stopped
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=strapi_postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=strapi_db
      - DB_POSTGRESDB_SCHEMA=n8n_schema
      - DB_POSTGRESDB_USER=strapi_user
      - DB_POSTGRESDB_PASSWORD=35@Riccarton
      - GENERIC_TIMEZONE=Pacific/Auckland
      - TZ=Pacific/Auckland
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=35@Riccarton
      - N8N_HOST=n8n.oasisvape.co.nz
      - N8N_PORT=5678
      - N8N_SECURE_COOKIE=false
      - WEBHOOK_TUNNEL_URL=https://n8n.oasisvape.co.nz
    ports:
      - "5678:5678"
    volumes:
      - ./data:/home/node/.n8n
      - ./logs:/var/log/n8n
    networks:
      - strapi_network
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.n8n.rule=Host(`n8n.oasisvape.co.nz`)"
      - "traefik.http.routers.n8n.entrypoints=websecure"
      - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
      - "traefik.http.services.n8n.loadbalancer.server.port=5678"

networks:
  strapi_network:
    external: true
  traefik-network:

6. DokuWiki 文档系统

# docker-compose-dokuwiki.yml
version: '3.8'
services:
  dokuwiki:
    image: lscr.io/linuxserver/dokuwiki:latest
    container_name: dokuwiki
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Pacific/Auckland
    volumes:
      - ./data:/config
      - ./logs:/var/log/dokuwiki
    ports:
      - "8081:80"
    networks:
      - strapi_network
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dokuwiki.rule=Host(`wiki.oasisvape.co.nz`)"
      - "traefik.http.routers.dokuwiki.entrypoints=websecure"
      - "traefik.http.routers.dokuwiki.tls.certresolver=letsencrypt"
      - "traefik.http.services.dokuwiki.loadbalancer.server.port=80"

networks:
  strapi_network:
    external: true
  traefik-network:

7. 监控系统

# docker-compose.yml (监控系统)
version: '3.8'
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - ./data/prometheus:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
      - '--web.enable-admin-api'
    environment:
      - TZ=Pacific/Auckland
    networks:
      - monitoring
      - traefik-network

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    restart: unless-stopped
    privileged: true
    devices:
      - /dev/kmsg:/dev/kmsg
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    ports:
      - "8180:8080"
    environment:
      - TZ=Pacific/Auckland
    networks:
      - monitoring
      - traefik-network

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - ./data/grafana:/var/lib/grafana
      - ./data/grafana/logs:/var/log/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=35@Riccarton
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-worldmap-panel
      - TZ=Pacific/Auckland
    networks:
      - monitoring
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=Host(`grafana.oasisvape.co.nz`)"
      - "traefik.http.routers.grafana.entrypoints=websecure"
      - "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
      - "traefik.http.services.grafana.loadbalancer.server.port=3000"

networks:
  monitoring:
    driver: bridge
    name: monitoring
  traefik-network:
    external: true

volumes:
  prometheus_data:

🔧 网络安全配置

防火墙设置

# 启用UFW
ufw enable
 
# 开放必要端口
ufw allow 22/tcp    # SSH
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS
 
# 关闭其他端口
ufw deny 3000       # Grafana
ufw deny 5678       # n8n
ufw deny 8080       # ERPNext
ufw deny 8081       # DokuWiki
ufw deny 8180       # cAdvisor
ufw deny 9090       # Prometheus
 
# 查看状态
ufw status

SSL证书管理

  • 证书类型: Let's Encrypt
  • 挑战方式: HTTP Challenge
  • 存储位置: /letsencrypt/acme.json
  • 自动续期: Traefik自动处理

📊 监控配置

Prometheus 配置

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: 'traefik'
    static_configs:
      - targets: ['traefik:8080']

Grafana 配置

  • 默认用户: admin
  • 默认密码: 35@Riccarton
  • 时区: Pacific/Auckland
  • 插件: grafana-piechart-panel, grafana-worldmap-panel

🚀 部署流程

1. 初始化部署

# 进入项目目录
cd /opt/discountvapor-it
 
# 创建网络
docker network create traefik-network
docker network create strapi_network
docker network create discountvapor-it_erpnext-network
docker network create monitoring
 
# 启动Traefik
docker compose -f docker-compose-traefik.yml up -d
 
# 启动PostgreSQL
docker compose -f docker-compose-postgres.yml up -d
 
# 启动ERPNext
docker compose -f docker-compose-erpnext.yml up -d
 
# 启动Strapi
docker compose -f docker-compose-strapi.yml up -d
 
# 启动n8n
docker compose -f docker-compose-n8n.yml up -d
 
# 启动DokuWiki
docker compose -f docker-compose-dokuwiki.yml up -d
 
# 启动监控系统
docker compose up -d

2. 服务验证

# 检查所有容器状态
docker ps
 
# 检查网络连接
docker network ls
 
# 检查Traefik路由
docker logs traefik
 
# 验证SSL证书
curl -I https://erp.oasisvape.co.nz
curl -I https://cms.oasisvape.co.nz
curl -I https://n8n.oasisvape.co.nz
curl -I https://wiki.oasisvape.co.nz
curl -I https://grafana.oasisvape.co.nz

3. 防火墙配置

# 配置防火墙
ufw enable
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw deny 3000
ufw deny 5678
ufw deny 8080
ufw deny 8081
ufw deny 8180
ufw deny 9090

🔍 故障排查

常见问题

1. SSL证书问题

# 检查acme.json权限
ls -la traefik/acme.json
 
# 重启Traefik
docker compose -f docker-compose-traefik.yml restart

2. 服务无法访问

# 检查容器状态
docker ps -a
 
# 检查网络
docker network inspect traefik-network
 
# 查看服务日志
docker logs <container_name>

3. 数据库连接问题

# 检查数据库容器
docker logs strapi_postgres
 
# 检查网络连接
docker exec -it strapi_cms ping strapi_postgres

日志查看

# 查看所有服务日志
docker logs traefik
docker logs erpnext-frontend
docker logs strapi_cms
docker logs n8n-automation
docker logs dokuwiki
docker logs grafana

📋 访问信息汇总

服务 域名 登录信息 状态
ERPNext https://erp.oasisvape.co.nz 管理员账号 ✅ 正常
Strapi CMS https://cms.oasisvape.co.nz 管理员账号 ✅ 正常
n8n https://n8n.oasisvape.co.nz admin / 35@Riccarton ✅ 正常
DokuWiki https://wiki.oasisvape.co.nz 管理员账号 ✅ 正常
Grafana https://grafana.oasisvape.co.nz admin / 35@Riccarton ✅ 正常
Traefik Dashboard https://traefik.oasisvape.co.nz - ✅ 正常

🔄 维护和更新

服务更新

# 更新特定服务
docker compose -f docker-compose-xxx.yml pull
docker compose -f docker-compose-xxx.yml up -d
 
# 更新所有服务
docker compose -f docker-compose-traefik.yml pull
docker compose -f docker-compose-erpnext.yml pull
docker compose -f docker-compose-strapi.yml pull
docker compose -f docker-compose-n8n.yml pull
docker compose -f docker-compose-dokuwiki.yml pull
docker compose pull

备份策略

# 备份配置文件
cp docker-compose-*.yml config_backup/
cp .env config_backup/
cp traefik/traefik.yml config_backup/
 
# 备份数据
tar -czf backup-$(date +%Y%m%d).tar.gz data/

📞 技术支持

project/deployment/start.txt · Last modified: by 127.0.0.1