Appearance
【02】ERPNext 基础安装指南:微型服务器与 1Panel 编排
NOTE
前面讲过如何 5 分钟极速跑起 ERPNext。本篇则重点聚焦:小微团队如何利用高性价比的微型主板(或家用服务器),通过 1Panel 的“容器编排 (Docker Compose)”进行可视化、支持多租户的稳定部署。
我们的折腾解法
1Panel 提供了友好的 Web 界面,我们只需配置一份标准的 docker-compose.yml,即可一键启动整个集群,并在面板中随时查看日志。
在这里,我们选择了 ERPNext v16,因为它是目前引入了最新 UI 体验的大版本。同时,对底层依赖进行了现代化升级:
- MariaDB 升级至
10.11 LTS版本,比陈旧的 10.6 更稳定兼容。 - Redis 升级至
7-alpine,性能更加卓越。
必须要懂的“多租户(Multi-tenant)”概念
在安装前,我们需要决定以什么地址来访问 ERPNext。Frappe 框架原生支持多租户架构,这意味着你可以运行同一套容器服务,但根据你访问的地址不同,系统会自动指引你进入完全物理隔离的不同数据库账套!
强烈推荐的“内外网双账套”玩法:
- 内部生产核心(内网 IP 或内网域名):极其安全,只在公司内部或 VPN 下允许访问,里面是真实的业务和财务数据。
- 外部公开试用(公网穿透域名):外网随便访客访问,数据随时可以销毁重置,完全不会污染你的内部账套。
开始安装编排
1. 创建 1Panel 容器编排
登录 1Panel 面板,进入 容器 -> 编排 (Compose) -> 点击 创建编排。 将名称设为 erpnext,然后将下方代码粘贴到文本框中:
yaml
services:
backend:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
configurator:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: none
entrypoint:
- bash
- -c
command:
- >
ls -1 apps > sites/apps.txt;
bench set-config -g db_host db;
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-socketio:6379";
bench set-config -p socketio_port 9000;
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
db:
image: mariadb:10.11
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
retries: 15
deploy:
restart_policy:
condition: on-failure
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
MYSQL_ROOT_PASSWORD: admin
volumes:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
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
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
ports:
- "8080:8080"
queue-default:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
command: [bench, worker, --queue, default]
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
queue-long:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
command: [bench, worker, --queue, long]
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
queue-short:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
command: [bench, worker, --queue, short]
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
redis-queue:
image: redis:7-alpine
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-queue-data:/data
redis-cache:
image: redis:7-alpine
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-cache-data:/data
redis-socketio:
image: redis:7-alpine
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-socketio-data:/data
scheduler:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
command: [bench, schedule]
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
websocket:
image: frappe/erpnext:v16
deploy:
restart_policy:
condition: on-failure
command: [node, /home/frappe/frappe-bench/apps/frappe/socketio.js]
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- apps:/home/frappe/frappe-bench/apps
volumes:
db-data:
redis-queue-data:
redis-cache-data:
redis-socketio-data:
sites:
logs:
assets:
apps:2. 初始化双账套架构
当 1Panel 提示容器全部启动后,从终端进入 backend 容器来分配账套:
bash
# 假设容器名为 erpnext-backend-1
docker exec -it erpnext-backend-1 bash
# --- 账套 A:创建【内部生产账套】 ---
# 使用内网 IP(例如:10.0.0.2),数据库密码 admin
bench new-site 10.0.0.2 --admin-password admin --db-root-password admin
bench --site 10.0.0.2 install-app erpnext
# 将其设为系统兜底默认站
bench use 10.0.0.2
# --- 账套 B:创建【外部公开试用账套】 ---
# 使用已解析的公网子域名(例如:demo.qingpy.cn)
bench new-site demo.qingpy.cn --admin-password admin --db-root-password admin
bench --site demo.qingpy.cn install-app erpnext
exit名字严格匹配机制
因为去除了请求头的拦截,Frappe 会严格根据你在浏览器地址栏敲的地址去寻找匹配的 Site 名。如果你建站用的是内部 IP 10.0.0.2,但你从外部用一个公网 IP 直接访问 8080 端口,Frappe 会报 "Sorry! We will be back soon" 的错误,因为它查不到这个公网 IP 的文件夹信息!这也天然成为了一层隐私屏障。
避坑指南总结
在日常的微型主机部署中,你极大概率会遇到下面的坑:
Docker Hub 极不稳定的拉取超时
解决方案:强烈建议开启 1Panel 的镜像加速设置,填入阿里云或腾讯云专属于你账号的容器服务镜像加速器地址。如果是极度封闭的网络,可以采用一台能访问外网的电脑通过 docker save 导出离线包,再通过 docker load 导入。
云主机 `docker exec` 报错无权限 `permission denied`
解决方案:默认的 Ubuntu 账户不是 root 导致的。执行 sudo usermod -aG docker $USER 和 newgrp docker 将非 root 的你自己加入群组即可。
前端打包失败 `socketio_port is not exported by common_site_config.json`
解决方案:由于安装含有前端框架(如 HRMS 大应用)时的配置加载时序问题。进入后端容器,手动补变量 bench set-config -g socketio_port 9000 然后重试 bench build 即可。
开始你的定制之路
无论是增加财务组件,或是调整进销存模块,使用 Frappe 的这套多租户体系,你在页面上所作的配置都是完全写入专属租户的 MariaDB 的!这也意味着你在“外部试用账套”页面上随便改,绝不会污染你的生产环境数据库,而如果有新的 python 代码功能加入了底层架构,两个账套又总是能同步享受到升级红利!
尽情折腾吧。