更新日期: 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证书管理
📊 监控配置
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 配置
🚀 部署流程
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
📋 访问信息汇总
🔄 维护和更新
服务更新
# 更新特定服务
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/
📞 技术支持