添加docker文档

This commit is contained in:
jerron
2026-10-18 03:39:07 +08:00
parent c316c8352d
commit 1e9025ca27
68 changed files with 7307 additions and 38 deletions
+4 -1
View File
@@ -22,4 +22,7 @@ node_modules/
# Banking SPA
yierp/public/banking
yierp/www/banking.html
yierp/www/banking.html
# Qoder configuration
.qoder.json
+18
View File
@@ -0,0 +1,18 @@
# Qoder 配置
## 模型配置
本项目使用自定义的 OpenAI 兼容 API 模型。
### 模型设置
- **提供商**: OpenAI 兼容 API
- **端点**: `https://api.openai.com/v1`
- **模型**: `Qwen-3.8-Flash`
- **API 密钥**: 请在环境变量或本地配置中设置
### 使用说明
要激活此配置,请确保:
1. 在环境变量中设置 `OPENAI_API_KEY`
2. 或在 `/root/.qoder-cn/` 中配置相应的凭据
+43 -7
View File
@@ -8,7 +8,33 @@
"shutdownAction": "stopCompose",
"remoteUser": "frappe",
"overrideCommand": false,
"forwardPorts": [8000, 9000, 3306, 6379],
"forwardPorts": [8000, 9000, 3306, 16379, 16380, 8025],
"portsAttributes": {
"8000": {
"label": "Frappe Web Server",
"onAutoForward": "notify"
},
"9000": {
"label": "Socket.IO",
"onAutoForward": "ignore"
},
"3306": {
"label": "MariaDB",
"onAutoForward": "silent"
},
"16379": {
"label": "Redis Cache",
"onAutoForward": "silent"
},
"16380": {
"label": "Redis Queue",
"onAutoForward": "silent"
},
"8025": {
"label": "Mailpit UI",
"onAutoForward": "notify"
}
},
"customizations": {
"vscode": {
"extensions": [
@@ -16,22 +42,32 @@
"ms-python.vscode-pylance",
"ms-azuretools.vscode-docker",
"tamasfe.even-better-toml",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
"visualstudioexptteam.vscodeintellicode",
"ritwickdew.sshfs"
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.defaultInterpreterPath": "/home/frappe/.bench-root/frappe-bench/env/bin/python",
"python.analysis.extraPaths": [
"/workspace",
"/home/frappe/frappe-bench/apps/frappe",
"/home/frappe/frappe-bench/apps/yierp"
"/home/frappe/.bench-root/frappe-bench/apps/frappe",
"/home/frappe/.bench-root/frappe-bench/apps/yierp"
],
"python.terminal.activateEnvironment": true,
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/**": true,
"**/.venv/**": true
"**/.venv/**": true,
"**/__pycache__/**": true
},
"search.exclude": {
"**/node_modules": true,
"**/__pycache__": true,
"**/.bench-root": false
}
}
}
}
},
"postCreateCommand": "echo 'Development environment ready. Access YiERP at http://localhost:8000'"
}
+6 -3
View File
@@ -10,12 +10,13 @@
- `sites/`: Frappe site 数据目录
- `logs/`: 日志目录
- `config/`: Frappe/bench 配置目录
- bench 实际数据目录位于容器内 `/home/frappe/.bench-root/frappe-bench`,并由 Docker volume 持久化,避免挂载冲突导致重复初始化
## 特性
- 使用官方 `frappe/bench` 镜像作为开发容器基座
- 使用 MariaDB 11.8
- 使用 Redis 7
- 使用 Redis 6.2
- 自动执行:
- `bench init`
- `bench new-site`
@@ -33,7 +34,7 @@
5. 进入 bench 目录后执行:
```bash
cd /home/frappe/frappe-bench
cd /home/frappe/.bench-root/frappe-bench
bench start
```
@@ -48,6 +49,8 @@ docker compose -f docker/docker-compose.yml up -d
启动后访问:
- 前端: http://localhost:8000
- MariaDB: localhost:3306
- Redis: localhost:6379
- 默认站点: `yierp.localhost`
- 默认账号: `Administrator`
- 默认密码: `admin`
@@ -69,7 +72,7 @@ docker compose -f docker/docker-compose.yml up -d
宿主机代码目录会挂载到容器中的 `/workspace`,而此目录又被链接到:
```bash
/home/frappe/frappe-bench/apps/yierp
/home/frappe/.bench-root/frappe-bench/apps/yierp
```
因此,修改宿主机中的代码,容器内对应代码会同步更新,开发时无需重复复制或安装。
View File
+49 -18
View File
@@ -2,16 +2,15 @@ services:
frappe:
image: frappe/bench:latest
container_name: yierp-frappe-dev
user: frappe
user: root
restart: unless-stopped
working_dir: /home/frappe/frappe-bench
working_dir: /home/frappe
command: ["/bin/bash", "/home/frappe/entrypoint.sh"]
volumes:
- ../:/workspace:cached
- frappe-bench:/home/frappe/.bench-root
- ./entrypoint.sh:/home/frappe/entrypoint.sh:ro
- ./sites:/home/frappe/frappe-bench/sites:cached
- ./logs:/home/frappe/frappe-bench/logs:cached
- ./config:/home/frappe/frappe-bench/config:cached
- ~/.ssh:/home/frappe/.ssh:ro
environment:
FRAPPE_SITE_NAME: yierp.localhost
FRAPPE_APP_NAME: yierp
@@ -19,23 +18,28 @@ services:
FRAPPE_DB_NAME: frappe
FRAPPE_DB_USER: root
FRAPPE_DB_PASSWORD: admin
BENCH_DIR: /home/frappe/frappe-bench
REDIS_CACHE: redis://redis:6379
REDIS_QUEUE: redis://redis:6379
REDIS_SOCKETIO: redis://redis:6379
BENCH_DIR: /home/frappe/.bench-root/frappe-bench
REDIS_CACHE: redis://redis-cache:6379
REDIS_QUEUE: redis://redis-queue:6379
REDIS_SOCKETIO: redis://redis-queue:6379
MARIADB_ROOT_PASSWORD: admin
MARIADB_ROOT_HOST: "%"
ADMIN_PASSWORD: admin
MARIADB_DATABASE: frappe
depends_on:
mariadb:
condition: service_healthy
redis:
redis-cache:
condition: service_started
redis-queue:
condition: service_started
ports:
- "8000:8000"
- "9000:9000"
- "3306:3306"
- "6379:6379"
- "8080:8080"
- "6787:6787"
extra_hosts:
- "host.docker.internal:host-gateway"
mariadb:
image: mariadb:11.8
@@ -43,7 +47,10 @@ services:
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: admin
MARIADB_ROOT_HOST: "%"
MARIADB_DATABASE: frappe
ports:
- "3306:3306"
volumes:
- mariadb-data:/var/lib/mysql
healthcheck:
@@ -53,14 +60,38 @@ services:
retries: 10
start_period: 20s
redis:
image: redis:7-alpine
container_name: yierp-redis
redis-cache:
image: redis:6.2-alpine
container_name: yierp-redis-cache
restart: unless-stopped
command: ["redis-server", "--appendonly", "no"]
command: ["redis-server", "--maxmemory-policy", "allkeys-lru"]
ports:
- "16379:6379"
volumes:
- redis-data:/data
- redis-cache-data:/data
redis-queue:
image: redis:6.2-alpine
container_name: yierp-redis-queue
restart: unless-stopped
command: ["redis-server"]
ports:
- "16380:6379"
volumes:
- redis-queue-data:/data
mailpit:
image: axllent/mailpit:latest
container_name: yierp-mailpit
restart: unless-stopped
ports:
- "8025:8025"
- "1025:1025"
environment:
MP_MAX_MESSAGES: 5000
volumes:
frappe-bench:
mariadb-data:
redis-data:
redis-cache-data:
redis-queue-data:
@@ -0,0 +1,92 @@
---
title: 简介
---
# Frappe Docker 简介
本文档是 Frappe Docker 仓库的文档,包含了如何使用 Docker 容器开发、部署和分享 Frappe 应用的所有信息。
## 仓库架构
Frappe Docker 为开发和部署 Frappe/ERPNext 应用提供了全面的容器化环境。它采用**多服务架构**,涵盖从 Web 服务到后台任务处理的所有环节。
### 核心服务
基础 Compose 文件包含以下核心服务:
- **configurator** - 初始化服务,配置数据库和 Redis 连接;启动时运行后退出
- **backend** - Werkzeug 开发服务器,处理动态内容
- **frontend** - Nginx 反向代理,提供静态资源并路由请求
- **websocket** - 运行 Socket.IO 的 Node.js 服务器,提供实时通信
- **queue-short/long** - 使用 RQ(Redis Queue)的 Python Worker,处理异步后台任务
- **scheduler** - 使用 schedule 库运行定时任务的 Python 服务
通过 Compose 覆盖文件可添加额外服务:
- **db** - MariaDB 或 PostgreSQL 数据库服务(通过 `compose.mariadb.yaml` 或 `compose.postgres.yaml`)
- **redis-cache/queue** - 用于缓存和任务队列的 Redis 实例(通过 `compose.redis.yaml`)
### 服务协作方式
```
用户请求
↓
[frontend (Nginx)] → 直接提供静态文件
↓
[backend (Werkzeug)] → 处理动态内容
↓ ↓
[db (MariaDB)] [redis-cache]
后台任务:
[scheduler] → [redis-queue] → [queue-short/long workers]
实时通信:
[websocket (Socket.IO)] ←→ [redis-cache]
```
## 仓库结构
### `/` 根目录:核心配置文件
- **compose.yaml** - 定义所有服务的主 Docker Compose 文件
- **example.env** - 环境变量模板(复制为 `.env`)
- **pwd.yml** - "Play with Docker" - 用于快速测试的简化单文件配置
- **docker-bake.hcl** - 用于多架构构建的高级 Docker Buildx 配置
- **docs/container-setup/env-variables.md** - 环境配置逻辑和默认值的集中参考
### `images/`:Docker 镜像定义
提供四个预定义的 Dockerfile,各有不同用途:
- **images/bench/** - 仅安装 Bench CLI,用于开发或调试;不包含运行时服务
- **images/custom/** - 基于纯 Python 基础镜像的多用途 Python 后端;从 `apps.json` 安装应用;适用于**生产**和测试;当你需要控制 Python/Node 版本时最为理想
- **images/layered/** - 最终内容与 `custom` 相同,但基于 Docker Hub 的预构建镜像;当你使用 Frappe 管理的依赖版本时,构建速度更快
- **images/production/** - 仅安装 Frappe 和 ERPNext(不可通过 `apps.json` 自定义);最适合**快速启动或探索**;实际部署请使用 `custom` 或 `layered`
> **注意:** 有关详细构建参数和高级配置选项,请参阅[配置总览](../02-setup/01-overview.md)。
### `overrides/`:Compose 文件扩展
Docker Compose "覆盖文件",用于扩展基础 compose.yaml 以适应不同场景:
- **compose.mariadb.yaml** - 添加 MariaDB 数据库服务
- **compose.redis.yaml** - 添加 Redis 缓存服务
- **compose.proxy.yaml** - 添加 Traefik 反向代理用于多站点托管(基于标签的路由)
- **compose.https.yaml** - 添加 Traefik HTTPS + 自动证书(使用 `SITES_RULE`)
- **compose.nginxproxy.yaml** - 添加 nginx-proxy 反向代理(HTTP,基于环境变量的 `VIRTUAL_HOST`)
- **compose.nginxproxy-ssl.yaml** - 添加 nginx-proxy + acme-companion(HTTPS,基于环境变量的 `LETSENCRYPT_HOST`)
**代理选择:**
- Traefik 在高级路由和多 bench 场景中更灵活
- nginx-proxy 在单 bench 基于主机路由的场景中更简单
### `development/`:开发环境
- **development/installer.py** - 自动化的 bench/site 创建和配置脚本
- 包含本地开发文件(已被 git 忽略以防止意外提交)
### `resources/`:运行时模板
- **core/nginx/nginx-entrypoint.sh** - 动态 Nginx 配置生成脚本
- **core/nginx/nginx-template.conf** - 带变量替换的 Nginx 配置模板
@@ -0,0 +1,126 @@
---
title: 选择部署方式
---
# 选择部署或开发方式
本仓库(`frappe_docker`)支持**多种使用 Docker 运行 Frappe 的方式**。
每种方式都有**特定用途**,它们**不可互换**。
本文档说明:
- 本仓库所有支持的使用方式
- 根据你的目标选择哪种方式
- 常见误解,尤其是关于持久化和应用安装方面
强烈建议**在按照任何配置指南操作之前**先阅读本文档。
## 概览
| 目标 | 推荐方式 | 生产就绪 |
| ---------------- | ------------------------- | -------- |
| 快速体验 | `pwd.yml` | ❌ |
| 本地开发 | VS Code Devcontainers | ❌ |
| 自动化生产安装 | Easy Install 脚本 | ✅ |
| 手动生产部署 | `compose.yml` + 覆盖文件 | ✅ |
## 1. `pwd.yml` - 快速测试/体验配置
`pwd.yml` 文件是一个**独立的 Docker Compose 单文件**,用于:
- 试用 Frappe 和 ERPNext
- 演示和短期测试环境
- 无需复杂配置即可学习基础知识
### 特点
- 单个 Compose 文件
- 最少配置
- 快速启动
- 设计上即为一次性使用
### 局限性
- ❌ **不适用于生产环境**
- ❌ **不适用于开发**
- ❌ **不适合作为迁移起点**
如果你从 `pwd.yml` 开始,应该预期**丢弃该环境**。
## 2. VS Code Devcontainers - 本地开发配置
[/docs/05-development/development.md](../05-development/01-development.md) 中描述的开发配置
使用 **VS Code Devcontainers** 提供**本地 Frappe 开发环境**。
### 适用场景
- 开发 Frappe 或自定义应用
- 使用源代码进行开发
- 在本地调试和测试变更
### 与其他配置的关键区别
- 为**交互式开发**优化
- 代码可实时编辑
- 容器为开发者工作流量身定制
- 不用于模拟生产环境
### 重要说明
- ❌ **不是部署方式**
- ❌ **不适用于生产环境**
- ✔ 是本仓库进行本地开发的**正确方式**
强烈不建议使用面向生产的配置(`pwd.yml` 或 `compose.yml`)进行开发。
## 3. Easy Install 脚本(来自 `frappe/bench`)
[ rappe/bench](https://github.com/frappe/bench) 仓库中提供的 Easy Install 脚本内部使用了 `frappe_docker`,并自动化了完整的部署流程。
它类似于**部署流水线**所执行的操作。
### 功能
- 安装 Docker 和前置依赖
- 拉取并配置 `frappe_docker`
- 使用生产级镜像和服务
- 减少手动配置
### 适用场景
- 生产环境
- 希望获得引导式自动化安装的用户
- 需要最少手动步骤的服务器部署
### 生产就绪
✔ **是** - 适用于实际生产系统
✔ 使用与手动生产部署相同的组件
## 4. `compose.yml` + 覆盖文件 - 推荐的生产配置
这是 `frappe_docker` 的**标准生产部署方式**。
它使用:
- 主 `compose.yml`
- `overrides/` 目录中的覆盖文件
详细说明请参阅 [/docs/02-setup](../02-setup/01-overview.md)
### 特点
- 明确的服务定义
- 灵活可配置
- 为长期运行的生产环境设计
- 适用于高级和自定义部署
**这是管理自有基础设施的团队的首选方案。**
## 总结
- 每种配置都有**独特用途**
- 开发、测试和生产是**独立的工作流**
- 不要期望将一次性配置升级为生产环境
- 应用必须在**构建时**包含,不能事后安装([Docker 不可变性](02-docker-immutability.md))
@@ -0,0 +1,52 @@
---
title: Docker 不可变性
---
# 重要概念:不可变性与持久化
一个常见的困惑来源是**基于 Docker 的 Frappe 部署如何处理持久化**。
### 容器是不可变的
Docker 容器**在构建后不应被修改**。
你只应更改:
- 环境变量
- 挂载的卷
- Docker 镜像本身(通过重新构建)
### 什么是持久化的
通常只有以下路径会被持久化:
- 站点数据(`/sites`)
- 数据库存储
这允许你:
- 创建新站点
- 运行迁移
- 执行备份和恢复
- 安全地重新创建容器
## 部署后安装应用
### ❌ 不支持
在运行中的容器内安装应用是**不被支持的**。
`bench get-app` 和 `bench build` 是常见但不被支持的操作示例。
### 为什么?
- 应用和资源是 **Docker 镜像**的一部分
- 运行时变更在容器重建时会丢失
- 这确保了可复现性和稳定性
### 正确的工作流
1. 将应用添加到镜像构建配置中
2. 重新构建 Docker 镜像
3. 重新部署整个栈
这适用于**所有面向生产的配置**。
+235
View File
@@ -0,0 +1,235 @@
---
title: Linux 和 Mac 快速上手
---
# 如何在 Linux/Mac 上使用 Frappe_docker 安装 ERPNext?
## 克隆仓库
```sh
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
## buildx 平台
运行以下命令来构建专门用于 ARM64 的多架构镜像:
```sh
docker buildx bake --no-cache --set "*.platform=linux/arm64"
```
## 为所有服务添加平台
- 在 `pwd.yml` 中为所有服务添加 `platform: linux/arm64`
- (将 `pwd.yml` 中当前指定的 erpnext 镜像版本替换为 `:latest`)
以下是示例 pwd.yml 文件:
```yml
services:
backend:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
configurator:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: none
entrypoint:
- bash
- -c
# 添加 redis_socketio 以向后兼容
command:
- >
ls -1 apps > sites/apps.txt;
bench set-config -g db_host $;
bench set-config -gp db_port $;
bench set-config -g redis_cache "redis://$";
bench set-config -g redis_queue "redis://$";
bench set-config -g redis_socketio "redis://$";
bench set-config -gp socketio_port $;
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
create-site:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: none
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
entrypoint:
- bash
- -c
command:
- >
wait-for-it -t 120 db:3306;
wait-for-it -t 120 redis-cache:6379;
wait-for-it -t 120 redis-queue:6379;
export start=`date +%s`;
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( `date +%s`-start > 120 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
bench new-site --mariadb-user-host-login-scope=% --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend;
db:
image: mariadb:11.8
platform: linux/arm64
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
retries: 20
deploy:
restart_policy:
condition: on-failure
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
environment:
MYSQL_ROOT_PASSWORD: admin
volumes:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
depends_on:
- websocket
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: frontend
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
ports:
- "8080:8080"
queue-long:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- long,default,short
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
queue-short:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- short,default
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
redis-queue:
image: redis:6.2-alpine
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-queue-data:/data
redis-cache:
image: redis:6.2-alpine
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
scheduler:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
command:
- bench
- schedule
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
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
volumes:
db-data:
redis-queue-data:
sites:
logs:
```
## 运行 Compose 文件
```sh
docker compose -f pwd.yml up
```
---
等待几分钟。
打开 localhost:8080
@@ -0,0 +1,42 @@
---
title: 单文件 Compose 配置
---
# 单文件 Compose 配置
此配置是一个简单的单文件 Compose,用于启动所有必需的服务和 frappe-bench。它用于启动 Play with Docker 实例并创建一个站点。该文件位于仓库根目录,名为 `pwd.yml`。
## 服务
### frappe-bench 组件
- backend,提供 gunicorn 后端服务
- frontend,通过 nginx 提供静态资源,并反向代理 websocket 和 gunicorn
- queue-long,处理 long、default 和 short 队列的 rq worker
- queue-short,处理 default 和 short 队列的 rq worker
- schedule,事件调度器
- websocket,用于实时通信的 socketio websocket
### 一次性配置
- configurator,配置 `common_site_config.json` 以设置 db 和 redis 主机
- create-site,创建一个站点作为 frappe-bench 的默认站点
### 服务依赖
- db,mariadb,带有 frappe 特定配置的容器
- redis-cache,用于缓存数据的 redis
- redis-queue,用于 rq 数据和发布/订阅的 redis
## 卷
- sites:bench 数据卷。公共配置、所有站点、所有站点配置和站点文件都将存储在此处。
- logs:bench 日志卷。所有进程日志都转储在此处。无需挂载。如果未指定,每个容器将为日志创建一个临时卷。
## 适配
如果你了解容器,可以使用 `pwd.yml` 作为参考来构建更复杂的配置,例如单服务器示例、Docker Swarm 栈、Kubernetes Helm chart 等。
此配置仅通过 nginx 提供名为 `frontend` 的站点。`FRAPPE_SITE_NAME_HEADER` 设置为 `frontend`,并创建一个名为 `frontend` 的默认站点。
更改 `host` 将允许容器接受任何主机头并提供对应站点的服务。要在 compose yaml 中转义 `$`,请使用 ``。要取消默认站点,请从 `sites` 目录中删除 `currentsite.txt` 文件。
+3
View File
@@ -0,0 +1,3 @@
---
title: 快速上手
---
+53
View File
@@ -0,0 +1,53 @@
---
title: 配置总览
---
本文档的目的是让你了解 Frappe Docker 容器的结构。
# 🐳 镜像
`/images` 目录中提供了**四个预定义的 Dockerfile**。
| Dockerfile | 内容 | 用途与使用场景 |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **bench** | 仅安装 Bench CLI。 | 用于**开发**或调试。提供命令行工具但不包含运行时服务。 |
| **custom** | 基于纯 Python 镜像构建的多用途 Python 后端。包含通过 Compose 配置运行 Frappe 实例所需的一切。安装 `apps.json` 中定义的应用。 | 适用于**生产**和**测试**。当你需要控制依赖(例如尝试新的 Python 或 Node 版本)时最为理想。 |
| **layered** | 最终内容与 `custom` 相同,但基于 **[Docker Hub](https://hub.docker.com/u/frappe) 的预构建镜像**。 | 当你接受 Frappe 管理的依赖版本时,非常适合**生产构建**。由于基础层已准备好,构建速度更快。 |
| **production** | 与 `custom` 类似(基于 Python 基础镜像构建),但**仅安装 Frappe 和 ERPNext**。不可通过 `apps.json` 自定义。 | 最适合**快速启动**或探索。对于实际部署或 CI/CD 流水线,`custom` 或 `layered` 更受青睐,因为它们提供更高的灵活性。 |
---
这些镜像包含了运行 Frappe 框架所需的所有进程的一切
(参见 [Bench Procfile 参考](https://frappeframework.com/docs/v14/user/en/bench/resources/bench-procfile))。
- `bench` 镜像仅安装 CLI 工具。
- 其他镜像(`custom`、`layered` 和 `production`)更进一步 - 为 ERPNext 和自定义应用提供近乎**即插即用**的配置。
> 我们使用[多阶段构建](https://docs.docker.com/develop/develop-images/multistage-build/)和 [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) 来最大化层复用并提高构建效率。
# 🏗️ Compose
镜像构建完成后,容器使用 [compose 文件](https://docs.docker.com/compose/compose-file/)进行编排。主 compose.yaml 为任何 Frappe 配置提供核心服务、网络和卷。
## 🛠️ 服务
| 服务 | 角色 | 用途 |
| ---------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **configurator** | 初始化 | 更新 `common_site_config.json` 以便 Frappe 知道如何访问 db 和 redis。它在每次 `docker-compose up` 时执行(并立即退出)。其他服务在此容器成功退出后启动 |
| **backend** | 运行时 | [Werkzeug 服务器](https://werkzeug.palletsprojects.com/en/2.0.x/) |
| **frontend** | 代理 | [nginx](https://www.nginx.com) 服务器,提供 JS/CSS 资源并路由传入请求 |
| **websocket** | 实时通信 | 运行 [Socket.IO](https://socket.io) 的 Node 服务器 |
| **queue-\_** | 后台任务 | 使用 [rq](https://python-rq.org) 运行任务队列的 Python 服务器 |
| **scheduler** | 任务自动化 | 使用 [schedule](https://schedule.readthedocs.io/en/stable/) 按计划运行任务的 Python 服务器 |
## 🧩 覆盖文件
可以使用[覆盖文件](https://docs.docker.com/compose/extends/)添加额外功能。这些文件修改现有服务或添加新服务,而不更改主 `compose.yaml`。
示例:主 Compose 文件没有数据库服务,但 `compose.mariadb.yaml` 添加了 MariaDB。参见 [overrides.md](05-overrides.md) 获取可用覆盖文件的完整列表及其使用方法。
---
**下一步:** [构建配置 →](02-build-setup.md)
**另见:** [配置示例](06-setup-examples.md) 了解实际部署场景。
+153
View File
@@ -0,0 +1,153 @@
---
title: 构建配置
---
本指南将引导你从仓库资源构建 Frappe 镜像。
# 前置条件
- git
- docker(Engine **v23.0+** 并带有 buildx)或 podman
- docker compose v2 或 podman compose
> 请根据官方维护者文档安装容器化软件。避免在不推荐的情况下使用包管理器,因为它们经常导致兼容性问题。
> **为什么需要 Docker Engine v23+?** 构建使用 [BuildKit secrets](https://docs.docker.com/build/building/secrets/)(`--secret`)来防止 `apps.json` 中的令牌进入镜像层。BuildKit 是 Docker Engine 23.0 开始的默认构建器 - 旧版本会失败或静默回退到不支持 secret 挂载的旧版构建器。
# 克隆本仓库
```bash
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
# 定义自定义应用
如果你不想在镜像中包含自定义应用,可以跳过本节。
要在镜像中包含自定义应用,请在仓库根目录创建 `apps.json` 文件:
```json
[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-16"
},
{
"url": "https://github.com/frappe/hrms",
"branch": "version-16"
},
{
"url": "https://github.com/frappe/helpdesk",
"branch": "main"
}
]
```
# 构建自定义镜像
## 手动构建
根据你的容器运行时和所需镜像类型选择合适的构建命令。此示例使用你创建的自定义 `apps.json` 构建 `layered` 镜像。
> **安全说明:** `apps.json` 文件作为 [BuildKit secret](https://docs.docker.com/build/building/secrets/) 传递,这样私有仓库令牌**永远不会**存储在镜像层元数据中。不要对 `apps.json` 使用 `--build-arg` - 构建参数可通过 `docker image history` 永久查看。这需要 **Docker Engine v23.0+**(BuildKit 是默认构建器)。
`Docker`:
```bash
docker build \
--no-cache \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
`Podman`:
```bash
podman build \
--no-cache \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
## 自动化构建
本仓库完全适合自动化构建,即使用 CI/CD 流水线。
更多信息请参见[自动化构建和部署](../03-production/06-automated-builds-and-deployment.md)。
## 构建参数、密钥和标志
| 变量 | 用途 |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **Frappe 框架** | |
| FRAPPE_PATH | Frappe 框架源代码的仓库 URL。默认为 <https://github.com/frappe/frappe> |
| FRAPPE_BRANCH | 用于 Frappe 框架的分支。默认为 version-16 |
| **自定义应用** | |
| CACHE_BUST | 可用于使缓存层失效。参见[构建缓存](../03-production/06-automated-builds-and-deployment.md#build-cache) |
| (secret) apps_json | 通过 `--secret=id=apps_json,src=apps.json` 传递。切勿对此文件使用 `--build-arg`。 |
| **依赖** | |
| PYTHON_VERSION | 基础镜像的 Python 版本 |
| NODE_VERSION | Node.js 版本 |
| WKHTMLTOPDF_VERSION | wkhtmltopdf 版本 |
| INSTALL_CHROMIUM | 配置 chromium 安装,默认为 `true` - Frappe Workbench 版本 >15 需要 |
| **仅 bench** | |
| DEBIAN_BASE | bench 镜像的 Debian 基础版本,默认为 `bookworm` |
| WKHTMLTOPDF_DISTRO | 使用指定的 debian 包发行版。默认为 `bookworm` |
# 部署栈
## env 文件
Compose 文件需要多个环境变量。你可以在系统中导出它们或创建 `.env` 文件。
```bash
cp example.env custom.env
```
编辑 `custom.env` 以自定义你的配置变量。模板包含常用变量,但你可以根据需要添加、修改或删除任何变量。有关所有可用变量的详细说明,请参见 [env-variables.md](04-env-variables.md)。
对于此配置,请确保**至少**将以下值添加到 `custom.env`:
```txt
CUSTOM_IMAGE=custom
CUSTOM_TAG=16
PULL_POLICY=missing
```
> `CUSTOM_*` 变量确保镜像引用指向最近构建的镜像。
> `PULL_POLICY` 确保 Docker 不会尝试拉取镜像,而是使用本地构建的镜像(默认拉取策略是 `always`)。
**⚠️ 这不是一个完整的 `.env` 配置指南。这些只是此示例所需的最小添加。
请查看 [env-variables.md](04-env-variables.md) 获取所有可用变量的完整描述并根据你的需求进行调整。**
## 创建最终 Compose 文件
将基础 Compose 文件与适合你使用场景的覆盖文件组合。此示例添加 MariaDB、Redis,并在 `:8080` 上暴露端口:
```bash
docker compose --env-file custom.env \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.noproxy.yaml \
config > compose.custom.yaml
```
这将生成 `compose.custom.yaml`,你将使用它来启动所有容器。根据你的需求自定义覆盖文件和环境变量。
> **注意**:podman compose 只是一个包装器,如果可用则使用 docker-compose,否则使用 podman-compose。podman-compose 在读取 .env 文件时存在问题([Issue](https://github.com/containers/podman-compose/issues/475)),可能会在运行容器时产生问题。
---
**下一步:** [启动配置 →](03-start-setup.md)
**返回:** [容器总览 ←](01-overview.md)
**另见:** [配置示例](06-setup-examples.md) 了解实际部署场景。
+66
View File
@@ -0,0 +1,66 @@
---
title: 启动容器
---
# 启动容器
当你的 Compose 文件准备好后,使用一个命令启动所有容器:
```bash
docker compose -p frappe -f compose.custom.yaml up -d
```
```bash
podman-compose --in-pod=1 --project-name frappe -f compose.custom.yaml up -d
```
`-p`(或 `--project-name`)标志将项目命名为 `frappe`,允许你轻松地一起引用和管理所有容器。
# 创建站点并安装应用
Frappe 现在正在运行,但尚未配置。你需要创建一个站点并安装你的应用。
## 基本站点创建
```bash
docker compose -p frappe exec backend bench new-site <sitename> --mariadb-user-host-login-scope='172.%.%.%'
docker compose -p frappe exec backend bench --site <sitename> install-app erpnext
```
```bash
podman exec -ti erpnext_backend_1 /bin/bash
bench new-site <sitename> --mariadb-user-host-login-scope='172.%.%.%'
bench --site <sitename> install-app erpnext
```
将 `<sitename>` 替换为你想要的站点名称。
## 创建站点并安装应用
你可以在创建站点时安装应用:
```bash
docker compose -p frappe exec backend bench new-site <sitename> \
--mariadb-user-host-login-scope='%' \
--db-root-password <db-password> \
--admin-password <admin-password> \
--install-app erpnext
```
> **注意:** 在尝试创建新站点之前,请等待 `db` 服务启动并且 `configurator` 退出。通常需要最多 10 秒。
更多站点操作,请参见[站点操作](../04-operations/01-site-operations.md)。
> ## 理解 MariaDB 用户作用域
>
> 标志 --mariadb-user-host-login-scope='172.%.%.%' 允许来自 172.0.0.0/8 范围内任何 IP 地址的数据库连接。这包括在你的机器上运行的所有容器和虚拟机。
>
> **为什么这是必要的?** Docker 和 Podman 为容器分配动态 IP 地址。如果你设置固定 IP 地址,当容器重启并获得新 IP 时,数据库连接将失败。通配符模式确保连接始终有效,无论 IP 如何变化。
>
> **安全说明:** 此作用域已足够,因为只有后端容器访问数据库。如果你需要外部数据库访问,请相应调整作用域,但要谨慎使用过于宽松的设置。
---
**返回:** [构建配置 →](02-build-setup.md)
**下一步:** [配置示例 →](06-setup-examples.md)
+160
View File
@@ -0,0 +1,160 @@
---
title: 环境变量
---
# 环境变量参考
环境变量用于配置你的 Frappe Docker 设置。它们可以直接在容器中设置,也可以定义在 Docker Compose 引用的 `.env` 文件中。
**快速开始:**
```bash
cp example.env .env
```
然后编辑 `.env` 并根据你的需求设置变量。
---
## 必需变量
| 变量 | 用途 | 示例 | 说明 |
| ----------------- | --------------------------------------------- | ------------------------------- | ----------------------------------------------------------------- |
| `FRAPPE_PATH` | Frappe 框架路径 | https://github.com/frappe/frappe | |
| `FRAPPE_BRANCH` | Frappe 分支 | `version-15` | 参见 [Frappe 发布版](https://github.com/frappe/frappe/releases) |
| `ERPNEXT_VERSION` | ERPNext 发布版本 | `v15.67.0` | 必需,尽管从未使用 |
| `DB_PASSWORD` | 数据库 root 密码(MariaDB 或 Postgres) | `secure_password_123` | 如果使用 `DB_PASSWORD_SECRETS_FILE` 则不需要 |
---
## 数据库配置
| 变量 | 用途 | 默认值 | 何时设置 |
| -------------------------- | -------------------------------------- | ---------------------------------- | ---------------------------------- |
| `DB_PASSWORD` | 数据库 root 用户密码 | 123 | 始终(除非使用密钥文件) |
| `DB_PASSWORD_SECRETS_FILE` | 包含数据库密码的文件路径 | — | 设置 mariadb-secrets 覆盖时 |
| `DB_HOST` | 数据库主机名或 IP | `db`(服务名) | 仅在使用外部数据库时 |
| `DB_PORT` | 数据库端口 | `3306`(MariaDB)/ `5432`(Postgres) | 仅在使用外部数据库时 |
---
## Redis 配置
| 变量 | 用途 | 默认值 | 何时设置 |
| --------------- | ------------------------------------------------ | ---------------------------- | ------------------------------------- |
| `REDIS_CACHE` | 用于缓存的 Redis 主机名 | `redis-cache`(服务名) | 仅在使用外部 Redis 实例时 |
| `REDIS_QUEUE` | 用于任务队列和实时更新的 Redis 主机名 | `redis-queue`(服务名) | 仅在使用外部 Redis 实例时 |
---
## 反向代理和 SSL(HTTPS)配置
### Traefik(compose.proxy.yaml / compose.https.yaml)
| 变量 | 用途 | 默认值 | 何时设置 |
| ------------------- | --------------------------------------------- | ------ | ----------------------------------------- |
| `LETSENCRYPT_EMAIL` | 用于 Let's Encrypt 证书注册的邮箱 | - | `compose.https.yaml` 必需 |
| `SITES_RULE` | 用于路由的域名(Traefik 规则表达式) | - | Traefik 路由/HTTPS 覆盖必需 |
**`SITES_RULE` 格式:**
```bash
# 单站点
SITES_RULE=Host(mysite.example.com)
# 多站点
SITES_RULE=Host(.example.com) || Host(.example.com)
```
> 注意:Traefik v3 迁移已完成。使用 `SITES_RULE` 作为完整的 v3 规则表达式;`SITES` 已弃用。
> 规则语法现在默认为 v3,因此不需要 `core.defaultRuleSyntax` 或每个路由的 `ruleSyntax` 设置。
### nginx-proxy + acme-companion(compose.nginxproxy*.yaml)
| 变量 | 用途 | 默认值 | 何时设置 |
| ------------------- | -------------------------------------- | ------ | --------------------------------------- |
| `LETSENCRYPT_EMAIL` | Let's Encrypt 证书邮箱 | - | `compose.nginxproxy-ssl.yaml` 必需 |
| `NGINX_PROXY_HOSTS` | nginx-proxy 的逗号分隔主机名列表 | - | `compose.nginxproxy*.yaml` 必需 |
**示例:**
```bash
NGINX_PROXY_HOSTS=example.com,www.example.com
```
> 注意:自动证书需要端口 80 可访问(HTTP-01)。
### 发布端口(Traefik 和 nginx-proxy)
| 变量 | 用途 | 默认值 | 何时设置 |
| -------------------- | ----------------- | ----------------------------- | ------------------------- |
| `HTTP_PUBLISH_PORT` | 发布的 HTTP 端口 | `80`(代理)/ `8080`(无代理) | 如果端口被占用则更改 |
| `HTTPS_PUBLISH_PORT` | 发布的 HTTPS 端口 | `443` | 如果端口 443 被占用则更改 |
---
## 站点配置
| 变量 | 用途 | 默认值 | 何时设置 |
| ------------------------- | ----------------------------- | --------------------------------------- | -------------------------------------------- |
| `FRAPPE_SITE_NAME_HEADER` | 多租户设置的站点名 | `System.Management.Automation.Internal.Host.InternalHost`(从请求主机名解析) | 通过 IP 访问或需要显式站点名时 |
**示例:**
如果你的站点名为 `mysite` 但你想通过 `127.0.0.1` 访问:
```bash
FRAPPE_SITE_NAME_HEADER=mysite
```
如果你的站点名为 `example.com` 并且你通过该域名访问,则无需设置(默认使用主机名)。
---
## 镜像配置
| 变量 | 用途 | 默认值 | 说明 |
| ---------------- | --------------------------- | -------------------- | ------------------------------------------------------ |
| `CUSTOM_IMAGE` | 自定义 Docker 镜像仓库 | Frappe 官方镜像 | 留空使用默认 |
| `CUSTOM_TAG` | 自定义 Docker 镜像标签 | 最新稳定版 | 对应 `FRAPPE_VERSION` |
| `PULL_POLICY` | 镜像拉取行为 | `always` | 选项:`always`、`never`、`if-not-present` |
| `RESTART_POLICY` | 容器重启行为 | `unless-stopped` | 选项:`no`、`always`、`unless-stopped`、`on-failure` |
---
## 后端(Gunicorn)配置
| 变量 | 用途 | 默认值 | 何时设置 / 允许值 |
| :----------------- | :---------------------------------------------------------- | :----- | :-------------------------------------------------------------------- |
| `GUNICORN_WORKERS` | 处理 Web 请求的 Worker 进程数 | `2` | 多核 CPU 时增加。公式:`(2 x 核心数) + 1` |
| `GUNICORN_THREADS` | 每个 Worker 进程的并发线程数 | `4` | 增加以处理更多同时的 I/O 密集型请求,内存开销不高 |
| `GUNICORN_TIMEOUT` | Worker 在重启前可以花在单个请求上的最长时间 | `120` | 如果长时间运行的报告或数据导入超时则增加 |
---
## 前端 Nginx 配置(在 frontend 容器内)
| 变量 | 用途 | 默认值 | 允许值 |
| ---------------------- | ------------------------------- | -------------- | ---------------------------------------- |
| `BACKEND` | 后端服务地址和端口 | `0.0.0.0:8000` | `{host}:{port}` |
| `SOCKETIO` | Socket.IO 服务地址和端口 | `0.0.0.0:9000` | `{host}:{port}` |
| `PROXY_READ_TIMEOUT` | 上游请求超时 | `120s` | 任何 nginx 超时值(例如 `300s`、`5m`) |
| `CLIENT_MAX_BODY_SIZE` | 最大上传文件大小 | `50m` | 任何 nginx 大小值(例如 `100m`、`1g`) |
### 真实 IP 配置(在代理后面)
在反向代理或负载均衡器后面运行时使用这些变量:
| 变量 | 用途 | 默认值 |
| -------------------------- | ---------------------------------------------- | ----------------- |
| `UPSTREAM_REAL_IP_ADDRESS` | 用于真实 IP 检测的可信上游 IP 地址 | `127.0.0.1` |
| `UPSTREAM_REAL_IP_HEADER` | 包含客户端 IP 的请求头 | `X-Forwarded-For` |
| `UPSTREAM_REAL_IP_RECURSIVE` | 启用递归 IP 搜索 | `off` |
---
## 迁移服务
| 变量 | 用途 | 默认值 | 允许值 |
| --------------- | ---------------------------- | ------------------------- | -------------- |
| `MIGRATE_SITES` | 开关自动迁移 | `true` - 自动迁移开启 | `true`、`false` |
+35
View File
@@ -0,0 +1,35 @@
---
title: 覆盖文件
---
覆盖文件通过额外服务扩展基础 compose.yaml 或修改现有行为。使用多个 -f 标志将它们包含在你的 compose 命令中。
```bash
docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml config > compose.custom.yaml
```
| 覆盖文件 | 用途 | 附加信息 |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **数据库** | | |
| compose.mariadb.yaml | 添加 MariaDB 数据库服务 | 设置 `DB_PASSWORD` 或使用默认密码 |
| compose.mariadb-secrets.yaml | 添加 MariaDB,密码从密钥文件读取而非环境变量 | 将 `DB_PASSWORD_SECRETS_FILE` 设置为密钥文件路径 |
| compose.mariadb-shared.yaml | 使 MariaDB 在共享网络(mariadb-network)上可供其他服务使用 | 设置 `DB_PASSWORD` |
| compose.postgres.yaml | 使用 PostgreSQL 而非 MariaDB 作为数据库 | 设置 `DB_PASSWORD` |
| **代理** | | |
| compose.noproxy.yaml | 直接在端口 `:8080` 上暴露应用,不使用反向代理 | |
| compose.proxy.yaml | 使用 Traefik 作为 HTTP 反向代理,端口 `:80` | 你可以通过设置 `HTTP_PUBLISH_PORT` 更改发布端口 |
| compose.https.yaml | 使用 Traefik 作为 HTTPS 反向代理,端口 `:443`,自动 HTTP 到 HTTPS 重定向 | 必须设置 `SITES_RULE` 和 `LETSENCRYPT_EMAIL`。可以设置 `HTTP_PUBLISH_PORT` 和 `HTTPS_PUBLISH_PORT` |
| compose.traefik.yaml | 在共享 `traefik-public` 网络上运行独立 Traefik 代理,带仪表板(HTTP) | 用于多栈设置。需要 `TRAEFIK_DOMAIN` 和 `HASHED_PASSWORD` |
| compose.traefik-ssl.yaml | 为 Traefik 仪表板添加 HTTPS 和 Let's Encrypt | 与 `compose.traefik.yaml` 一起使用。需要 `EMAIL` 和 `TRAEFIK_DOMAIN`。发布 `HTTPS_PUBLISH_PORT` |
| compose.nginxproxy.yaml | 使用 nginx-proxy 作为 HTTP 反向代理,端口 `:80` | 设置 `NGINX_PROXY_HOSTS`。与 `compose.nginxproxy-ssl.yaml` 一起使用以启用 HTTPS。可以通过设置 `HTTP_PUBLISH_PORT` 更改发布端口 |
| compose.nginxproxy-ssl.yaml | 添加 acme-companion 以在端口 `:443` 上启用 HTTPS 并自动获取证书 | 需要 `compose.nginxproxy.yaml`。设置 `NGINX_PROXY_HOSTS` 和 `LETSENCRYPT_EMAIL`。可以设置 `HTTP_PUBLISH_PORT` 和 `HTTPS_PUBLISH_PORT` |
| **Redis** | | |
| compose.redis.yaml | 添加 Redis 服务用于缓存和后台任务队列 | |
| **服务** | | |
| compose.migrator.yaml | 运行专用迁移容器,在每次启动时对所有站点执行 `bench --site all migrate` | 通过 `MIGRATE_SITES` 控制迁移意图 - 默认为 true |
| **待补充** | **以下覆盖文件可用但缺少文档。如果你使用它们并了解其用途,请考虑为本文档做出贡献。** | |
| compose.backup-cron.yaml | | |
| compose.custom-domain-ssl.yaml | | |
| compose.custom-domain.yaml | | |
| compose.multi-bench-ssl.yaml | | |
| compose.multi-bench.yaml | | |
+142
View File
@@ -0,0 +1,142 @@
---
title: 配置示例
---
# 配置示例
本指南提供常见配置场景的实际示例。这些示例建立在[容器配置指南](01-overview.md)之上,演示如何将基础 Compose 文件与覆盖文件组合使用。
> **注意:** 此配置不适用于开发。完整的开发环境请参见[此处](../05-development/01-development.md)。
## 前置条件
- [docker](https://docker.com/get-started)
- [docker compose v2](https://docs.docker.com/compose/cli-command)
- 已克隆的 `frappe_docker` 仓库
## 设置环境变量
将示例 docker 环境文件复制为 `.env`:
```sh
cp example.env .env
```
编辑 `.env` 并根据你的需求设置变量。有关所有可用变量的详细说明,请参见[环境变量](04-env-variables.md)。
## 存储生成的 YAML 文件
`docker compose config` 生成的 YAML 文件可以存储在目录中以便版本控制和管理:
```shell
mkdir ~/gitops
```
你可以将此目录设为私有 git 仓库以跟踪配置变更。这对于管理多个环境或项目特别有用。
或者,你可以直接使用 `docker compose up` 启动容器而不存储中间 YAML 文件。
## 示例 1:无代理的 Frappe(直接访问)
使用容器化的 MariaDB 和 Redis 设置 Frappe,直接在端口 `:8080` 上暴露应用,不使用反向代理。
**要求:**
- 在 `.env` 中设置 `DB_PASSWORD`(或使用默认值 `123`)
- 不需要外部数据库或 Redis
```sh
# 生成 YAML
docker compose -f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.noproxy.yaml \
config > ~/gitops/docker-compose.yml
# 启动容器
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
## 示例 2:使用外部数据库和 Redis 的 ERPNext
使用外部 MariaDB 和 Redis 实例以及 Traefik HTTP 代理设置 ERPNext。
**要求:**
- 在 `.env` 中设置 `DB_HOST`、`DB_PORT`、`REDIS_CACHE` 和 `REDIS_QUEUE`
- 外部数据库和 Redis 必须可访问
```sh
# 生成 YAML
docker compose -f compose.yaml \
-f overrides/compose.proxy.yaml \
config > ~/gitops/docker-compose.yml
# 启动容器
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
## 示例 3:带 HTTPS 的生产配置
使用容器化的 MariaDB 和 Redis 以及通过 Traefik 的 Let's Encrypt SSL 证书设置 Frappe/ERPNext。
**要求:**
- 设置 `LETSENCRYPT_EMAIL` 和 `SITES_RULE` 环境变量
- DNS 必须指向你的服务器 IP
```sh
# 生成 YAML
docker compose -f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.https.yaml \
config > ~/gitops/docker-compose.yml
# 启动容器
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
> **注意:** 确保你的 `SITES_RULE` 变量格式正确。有关正确格式,请参见[环境变量](04-env-variables.md)。
## 创建第一个站点
启动容器后,创建你的第一个站点。详细说明请参见[站点操作](../04-operations/01-site-operations.md#setup-new-site)。
## 更新镜像
要更新到更新版本的 Frappe 或 ERPNext:
```sh
# 1. 更新 .env 中的环境变量
nano .env
# 根据需要编辑 ERPNEXT_VERSION 和 FRAPPE_VERSION
# 2. 使用新版本重新生成 Compose 文件
docker compose --env-file .env \
-f compose.yaml \
# ... 你的其他覆盖文件
config > ~/gitops/docker-compose.yml
# 3. 拉取新镜像
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml pull
# 4. 停止容器
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml down
# 5. 重启容器
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
> **注意:**
>
> - 如果使用不可变镜像标签,可以跳过拉取和停止容器命令
> - 如果未找到新的不可变标签,`docker compose up -d` 将拉取它们
更新后迁移站点,请参见[站点操作](../04-operations/01-site-operations.md#migrate-site)。
---
**返回:** [启动配置 →](03-start-setup.md)
**下一步:** [单服务器示例 →](07-single-server-example.md)
@@ -0,0 +1,298 @@
---
title: 单服务器配置
---
# 单服务器示例
本指南演示如何在单台服务器上设置多个 Frappe/ERPNext bench(项目),并共享基础设施组件。
在此使用场景中,我们有一台具有静态 IP 的服务器。它适用于以下场景:一台强大的 VM 运行多个 bench 和应用,或者一台入门级 VM 运行单个站点。对于单 bench、单站点配置,只需按照操作到添加第一个 bench 和第一个站点即可。如果你选择此配置,则只能垂直扩展。如果需要水平扩展,则需要备份站点并将其恢复到集群配置。
我们将设置以下内容:
- 在 Linux 服务器上安装 docker 和 docker compose v2。
- 安装 traefik 服务用于内部负载均衡和 letsencrypt。
- 使用容器安装 MariaDB。
- 设置名为 `erpnext-one` 的项目,并在该项目中创建站点 `one.example.com` 和 `two.example.com`。
- 设置名为 `erpnext-two` 的项目,并在该项目中创建站点 `three.example.com` 和 `four.example.com`。
说明:
将安装一个 **Traefik** 实例作为服务器上托管的多个 bench 和站点的内部负载均衡器。它还可以与其他应用(如 wordpress、metabase 等)一起进行负载均衡。我们只使用此 traefik 实例暴露端口 `80` 和 `443` 一次。Traefik 还将负责服务器上安装的所有站点的 letsencrypt 自动化。_为什么选择 Traefik 而非 Nginx Proxy Manager?_ Traefik 不需要额外的 DB 服务,可以将证书存储在卷中的 json 文件中。
将安装一个 **MariaDB** 实例作为服务器上安装的所有 bench/项目的数据库服务。
每个 ERPNext 项目(bench)实例将拥有自己的 redis、socketio、gunicorn、nginx、worker 和 scheduler。它将通过连接到 MariaDB 网络连接到内部 MariaDB。它将通过连接到 Traefik 网络通过 Traefik 向公众暴露站点。
### 安装 Docker
安装 Docker 最简单的方法是使用[便捷脚本](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script)。
```shell
curl -fsSL https://get.docker.com | bash
```
注意:本文档假设使用 Ubuntu LTS 服务器。只要 docker 便捷脚本可用,可以使用任何发行版。如果便捷脚本不起作用,你需要手动安装 docker。
### 安装 Compose V2
参考[原始文档](https://docs.docker.com/compose/cli-command/#install-on-linux)获取更新版本。
```shell
DOCKER_CONFIG=
mkdir -p /cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o /cli-plugins/docker-compose
chmod +x /cli-plugins/docker-compose
```
### 准备
克隆 `frappe_docker` 仓库获取所需的 YAML 文件,并将 shell 的当前工作目录更改为克隆的仓库。
```shell
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
创建配置和资源目录
```shell
mkdir ~/gitops
```
`~/gitops` 目录将存储我们用于设置的所有资源。我们还将把环境文件保存在此目录中,因为将有多个具有不同环境变量的项目。你可以为此目录创建私有仓库并在那里跟踪变更。
### 安装 Traefik
使用 docker compose 进行基本 Traefik 设置。
在 `~/gitops` 中创建名为 `traefik.env` 的文件
```shell
echo 'TRAEFIK_DOMAIN=traefik.example.com' > ~/gitops/traefik.env
echo 'EMAIL=admin@example.com' >> ~/gitops/traefik.env
echo "HASHED_PASSWORD='$apr1$WMfx3Ic1$147TC1sAH9aN8MlTWdZfK/'" >> ~/gitops/traefik.env
```
注意:
- 将域名从 `traefik.example.com` 更改为生产中使用的域名。DNS 条目需要指向服务器 IP。
- 将 letsencrypt 通知邮箱从 `admin@example.com` 更改为正确的邮箱。
- 将密码从 `changeit` 更改为更安全的密码。
位于 `~/gitops/traefik.env` 的 env 文件将如下所示:
```env
TRAEFIK_DOMAIN=traefik.example.com
EMAIL=admin@example.com
HASHED_PASSWORD=.4gp7RT/
```
如果容器无法部署,请将 HASHED_PASSWORD 放在 '' 中。
使用 letsencrypt SSL 部署 traefik 容器
```shell
docker compose --project-name traefik \
--env-file ~/gitops/traefik.env \
-f overrides/compose.traefik.yaml \
-f overrides/compose.traefik-ssl.yaml up -d
```
这将使 traefik 仪表板在 `traefik.example.com` 上可用,所有证书将存放在 Docker 卷 `cert-data` 中。
对于 LAN 设置,部署 traefik 容器时不覆盖 `overrides/compose.traefik-ssl.yaml`。
### 安装 MariaDB
使用 docker compose 进行基本 MariaDB 设置。
在 `~/gitops` 中创建名为 `mariadb.env` 的文件
```shell
echo "DB_PASSWORD=changeit" > ~/gitops/mariadb.env
```
注意:
- 将密码从 `changeit` 更改为更安全的密码。
位于 `~/gitops/mariadb.env` 的 env 文件将如下所示:
```env
DB_PASSWORD=changeit
```
注意:将密码从 `changeit` 更改为更安全的密码。
部署 mariadb 容器
```shell
docker compose --project-name mariadb --env-file ~/gitops/mariadb.env -f overrides/compose.mariadb-shared.yaml up -d
```
这将使 `mariadb-database` 服务在 `mariadb-network` 下可用。数据将存放在 `/data/mariadb` 中。
### 安装 ERPNext
#### 创建第一个 bench
创建名为 `erpnext-one` 的第一个 bench,包含 `one.example.com` 和 `two.example.com`
在 `~/gitops` 中创建名为 `erpnext-one.env` 的文件
```shell
cp example.env ~/gitops/erpnext-one.env
sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-one.env
sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-one.env
sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-one.env
sed -i 's/SITES_RULE=Host(rp.example.com)/SITES_RULE=Host(one.example.com) || Host( wo.example.com)/g' ~/gitops/erpnext-one.env
echo 'ROUTER=erpnext-one' >> ~/gitops/erpnext-one.env
echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/erpnext-one.env
```
注意:
- 将密码从 `changeit` 更改为上一步中为 MariaDB compose 设置的密码。
env 文件生成在 `~/gitops/erpnext-one.env` 位置。
在 `~/gitops` 目录中创建名为 `erpnext-one.yaml` 的 yaml 文件:
```shell
docker compose --project-name erpnext-one \
--env-file ~/gitops/erpnext-one.env \
-f compose.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.multi-bench.yaml \
-f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-one.yaml
```
对于 LAN 设置,不覆盖 `compose.multi-bench-ssl.yaml`。
在对 `erpnext-one.env` 文件进行任何更改后,使用上述命令重新生成 `~/gitops/erpnext-one.yaml`。例如,在更改版本以迁移 bench 之后。
部署 `erpnext-one` 容器:
```shell
docker compose --project-name erpnext-one -f ~/gitops/erpnext-one.yaml up -d
```
创建站点 `one.example.com` 和 `two.example.com`:
```shell
# one.example.com
docker compose --project-name erpnext-one exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit one.example.com
```
你可以在此停止,完成单 bench 单站点配置。继续向当前 bench 添加另一个站点。
```shell
# two.example.com
docker compose --project-name erpnext-one exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit two.example.com
```
#### 创建第二个 bench
设置额外的 bench 是可选的。仅在需要多 bench 配置时继续。
创建名为 `erpnext-two` 的第二个 bench,包含 `three.example.com` 和 `four.example.com`
在 `~/gitops` 中创建名为 `erpnext-two.env` 的文件
```shell
curl -sL https://raw.githubusercontent.com/frappe/frappe_docker/main/example.env -o ~/gitops/erpnext-two.env
sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-two.env
sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-two.env
sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-two.env
echo "ROUTER=erpnext-two" >> ~/gitops/erpnext-two.env
echo 'SITES_RULE=Host( hree.example.com) || Host( our.example.com)' >> ~/gitops/erpnext-two.env
echo "BENCH_NETWORK=erpnext-two" >> ~/gitops/erpnext-two.env
```
注意:
- 将密码从 `changeit` 更改为上一步中为 MariaDB compose 设置的密码。
env 文件生成在 `~/gitops/erpnext-two.env` 位置。
在 `~/gitops` 目录中创建名为 `erpnext-two.yaml` 的 yaml 文件:
```shell
docker compose --project-name erpnext-two \
--env-file ~/gitops/erpnext-two.env \
-f compose.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.multi-bench.yaml \
-f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-two.yaml
```
在对 `erpnext-two.env` 文件进行任何更改后,使用上述命令重新生成 `~/gitops/erpnext-two.yaml`。例如,在更改版本以迁移 bench 之后。
部署 `erpnext-two` 容器:
```shell
docker compose --project-name erpnext-two -f ~/gitops/erpnext-two.yaml up -d
```
创建站点 `three.example.com` 和 `four.example.com`:
```shell
# three.example.com
docker compose --project-name erpnext-two exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit three.example.com
# four.example.com
docker compose --project-name erpnext-two exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit four.example.com
```
#### 为现有站点创建自定义域名
如果你需要将自定义域名指向现有站点,请按照以下步骤操作。
对于基于 LAN 的访问需要自定义域名时也很有用。
创建环境文件
```shell
echo "ROUTER=custom-one-example" > ~/gitops/custom-one-example.env
echo 'SITES_RULE=Host(custom-one.example.com)' >> ~/gitops/custom-one-example.env
echo "BASE_SITE=one.example.com" >> ~/gitops/custom-one-example.env
echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/custom-one-example.env
```
注意:
- 将文件名从 `custom-one-example.env` 更改为逻辑名称。
- 将 `ROUTER` 变量从 `custom-one.example.com` 更改为正在添加的域名。
- 将 `SITES_RULE` 变量更改为正在添加的域名。你可以使用 `Host(...) || Host(...)` 添加多个站点。
- 将 `BASE_SITE` 变量从 `one.example.com` 更改为正在指向的站点。
- 将 `BENCH_NETWORK` 变量从 `erpnext-one` 更改为创建 bench 时创建的网络。
env 文件生成在命令中提到的位置。
生成 yaml 以进行反向代理:
```shell
docker compose --project-name custom-one-example \
--env-file ~/gitops/custom-one-example.env \
-f overrides/compose.custom-domain.yaml \
-f overrides/compose.custom-domain-ssl.yaml config > ~/gitops/custom-one-example.yaml
```
对于 LAN 设置,不覆盖 `compose.custom-domain-ssl.yaml`。
部署 `erpnext-two` 容器:
```shell
docker compose --project-name custom-one-example -f ~/gitops/custom-one-example.yaml up -d
```
### 站点操作
参考:[站点操作](../04-operations/01-site-operations.md)
---
**返回:** [配置示例 →](06-setup-examples.md)
@@ -0,0 +1,173 @@
---
title: 单服务器示例
---
# 单服务器示例(nginx-proxy + acme-companion)
本指南演示如何使用 nginx-proxy 和 acme-companion 进行 HTTPS 的单服务器配置。它最适合少量主机名和单个 bench。如果你需要多个 bench 或高级路由,请使用基于 Traefik 的示例。
我们将设置以下内容:
- 在 Linux 服务器上安装 Docker 和 Docker Compose v2。
- 使用 nginx-proxy + acme-companion 进行 HTTPS(Let's Encrypt)。
- 通过容器安装 MariaDB 和 Redis。
- 设置一个名为 `erpnext` 的项目,包含站点 `erp.your-domain.com` 和 `crm.your-domain.com`。
## 要求
- 可以运行 Docker Engine **v23.0+** 的服务器(推荐:2 vCPU,4 GB RAM,50 GB SSD)。下面的自定义镜像构建使用 [BuildKit secrets](https://docs.docker.com/build/building/secrets/),需要 BuildKit 作为默认构建器(Docker Engine 23.0+)。
- 具有 DNS 控制权的公共域名。
- 两个指向服务器 IP 的子域名(A/AAAA 记录):
- `erp.your-domain.com`
- `crm.your-domain.com`
- 端口 80 和 443 可从互联网访问(Let's Encrypt HTTP-01 所需)。
### 安装 Docker
Docker 可以安装在多种系统上。最简单的方法是使用便捷脚本。
| 平台 | 便捷脚本 | 使用仓库 |
| ------ | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| CentOS | [链接](https://docs.docker.com/engine/install/centos/#install-using-the-convenience-script) | [链接](https://docs.docker.com/engine/install/centos/#install-using-the-repository) |
| Debian | [链接](https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script) | [链接](https://docs.docker.com/engine/install/debian/#install-using-the-repository) |
| Ubuntu | [链接](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script) | [链接](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository) |
| Fedora | [链接](https://docs.docker.com/engine/install/fedora/#install-using-the-convenience-script) | [链接](https://docs.docker.com/engine/install/fedora/#install-using-the-repository) |
然后执行安装后步骤。这将确保权限更易于使用,并且 Docker 将随系统启动。[安装后步骤](https://docs.docker.com/engine/install/linux-postinstall/)
### 准备
克隆 `frappe_docker` 并将当前工作目录更改为仓库。
```shell
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
创建配置目录:
```shell
mkdir ~/gitops
```
## 可选:构建自定义镜像
如果你需要额外的应用(超出 Frappe/ERPNext),请构建自定义镜像。否则,跳过本节并使用默认镜像。
创建 `apps.json`(每个条目是一个 Git 仓库 + 分支):
```shell
cat > ~/gitops/apps.json <<'EOF'
[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-16"
},
{
"url": "https://github.com/frappe/payments",
"branch": "version-16"
}
]
EOF
```
仅 CRM 的示例:
```shell
cat > ~/gitops/apps.json <<'EOF'
[
{
"url": "https://github.com/frappe/crm",
"branch": "main"
}
]
EOF
```
构建镜像,将 `apps.json` 作为 [BuildKit secret](https://docs.docker.com/build/building/secrets/) 传递,这样私有仓库令牌永远不会存储在镜像层中。这需要 **Docker Engine v23.0+**,其中 BuildKit 是默认构建器:
```shell
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=C:\Users\jerro/gitops/apps.json \
--tag=my-erpnext-prod-image:16.0.0 \
--file=images/layered/Containerfile .
```
### 配置环境
为 bench 创建环境文件:
```shell
cp example.env ~/gitops/erpnext.env
sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext.env
echo 'NGINX_PROXY_HOSTS=erp.your-domain.com,crm.your-domain.com' >> ~/gitops/erpnext.env
echo 'LETSENCRYPT_EMAIL=admin@your-domain.com' >> ~/gitops/erpnext.env
```
注意:
- 将 `changeit` 替换为强密码。
- 将域名和邮箱替换为你的生产值。
- `NGINX_PROXY_HOSTS` 是不带空格的逗号分隔列表。
- 如果你构建了自定义镜像,添加:
```shell
echo "CUSTOM_IMAGE=my-erpnext-prod-image" >> ~/gitops/erpnext.env
echo "CUSTOM_TAG=16.0.0" >> ~/gitops/erpnext.env
```
### 生成 Compose 配置
创建渲染后的 Compose 文件:
```shell
docker compose --project-name erpnext \
--env-file ~/gitops/erpnext.env \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.nginxproxy.yaml \
-f overrides/compose.nginxproxy-ssl.yaml config > ~/gitops/erpnext.yaml
```
启动栈:
```shell
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml up -d
```
这将启动 MariaDB 和 Redis 容器作为同一栈的一部分。
### 创建站点
```shell
# erp.your-domain.com
docker compose --project-name erpnext exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit erp.your-domain.com
# crm.your-domain.com
docker compose --project-name erpnext exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app crm --admin-password changeit crm.your-domain.com
```
### 注意
- Let's Encrypt 需要端口 80 和 443 可从互联网访问。
- 如果你无法暴露这些端口(仅限 LAN),请省略 `compose.nginxproxy-ssl.yaml` 并使用 HTTP 或本地 TLS 代理(如 Caddy)。
- 将 `changeit` 替换为强 DB root 密码,并为每个站点设置强管理员密码。
### 站点操作
参考:[站点操作](../04-operations/01-site-operations.md)
### 故障排除(ACME / 证书)
- **未颁发证书:** 验证 DNS 指向服务器 IP,并且端口 80/443 可从互联网访问。
- **日志中的 ACME 错误:** 检查 `acme-companion` 日志以获取确切的挑战错误。
- **错误的主机名:** 确保域名包含在 `NGINX_PROXY_HOSTS` 中,并且你在编辑后重新启动了栈。
---
**返回:** [单服务器示例(Traefik)](07-single-server-example.md)
+3
View File
@@ -0,0 +1,3 @@
---
title: 配置指南
---
@@ -0,0 +1,57 @@
---
title: TLS/SSL 配置总览
---
# TLS/SSL 配置总览
Frappe Docker 支持多种 TLS/SSL 方法。选择与你的路由需求和代理运行位置相匹配的方法。
## 选项
### Traefik(内置 HTTPS)
- 使用 `overrides/compose.https.yaml`
- 最适合多站点配置和高级路由规则
- 需要 `SITES_RULE` 和 `LETSENCRYPT_EMAIL`
- 参见[环境变量](../02-setup/04-env-variables.md)和[配置示例](../02-setup/06-setup-examples.md#example-3-production-setup-with-https)
#### Traefik 部署模型
- **单栈(Traefik 在栈内):**
- 使用 `compose.proxy.yaml`(HTTP)或 `compose.https.yaml`(HTTPS)
- Traefik 作为 `proxy` 在同一栈中运行
- **多栈的中央 Traefik:**
- 使用 `compose.traefik.yaml`(以及可选的 `compose.traefik-ssl.yaml` 用于仪表板)运行专用 Traefik 栈
- 每个 Frappe 栈使用 `compose.multi-bench.yaml`(以及可选的 `compose.multi-bench-ssl.yaml`)
- 这将栈连接到共享的 `traefik-public` 网络
### nginx-proxy + acme-companion
- 使用 `overrides/compose.nginxproxy.yaml` 加 `overrides/compose.nginxproxy-ssl.yaml`
- 适用于单 bench 或小型配置的简单基于主机的路由
- 需要 `NGINX_PROXY_HOSTS` 和 `LETSENCRYPT_EMAIL`
- 参见 [nginx-proxy + acme-companion](04-nginx-proxy-acme-companion.md)
## Traefik 与 nginx-proxy + acme-companion 对比
| 主题 | Traefik (compose.https.yaml) | nginx-proxy + acme-companion |
| ------------------ | --------------------------------------------- | ------------------------------------------------------------------------------ |
| 配置 | 带有 `SITES_RULE` 表达式的标签 | 环境变量(`NGINX_PROXY_HOSTS`) |
| 路由 | 灵活(规则、头、路径) | 仅基于主机 |
| 多站点 | 强 | 适用于简单主机列表 |
| TLS/ACME | 内置 | 单独的 companion 容器 |
| 证书存储 | `cert-data` 卷(`/letsencrypt/acme.json`)| `nginx-proxy-certs` + `acme-data` 卷(`/etc/nginx/certs`、`/etc/acme.sh`) |
| 复杂度 | 中等 | 低 |
| 可观测性 | 可选仪表板(此处未启用) | 无内置仪表板 |
### Caddy(外部反向代理)
- 在主机上运行 Caddy 并代理到 frontend 容器
- 适用于本地 HTTPS 或当你已经使用 Caddy 时
- 参见 [Caddy 反向代理](05-caddy-https.md)
## 常见要求
- DNS 必须指向服务器以获取公共 TLS 证书
- 端口 80 和 443 必须可访问以进行 HTTP-01 挑战
- 如果需要非默认端口,使用 `HTTP_PUBLISH_PORT` 和 `HTTPS_PUBLISH_PORT`
@@ -0,0 +1,62 @@
---
title: 备份策略
---
创建备份服务或栈。
```yaml
# backup-job.yml
version: "3.7"
services:
backup:
image: frappe/erpnext:
entrypoint: ["bash", "-c"]
command:
- |
bench --site all backup
## 取消注释以使用 restic 快照。
# restic snapshots || restic init
# restic backup sites
## 取消注释以仅保留最后 n=30 个快照。
# restic forget --group-by=paths --keep-last=30 --prune
environment:
# 为 restic 设置正确的环境变量
- RESTIC_REPOSITORY=s3:https://s3.endpoint.com/restic
- AWS_ACCESS_KEY_ID=access_key
- AWS_SECRET_ACCESS_KEY=secret_access_key
- RESTIC_PASSWORD=restic_password
volumes:
- "sites:/home/frappe/frappe-bench/sites"
networks:
- erpnext-network
networks:
erpnext-network:
external: true
name: _default
volumes:
sites:
external: true
name: _sites
```
在单 docker 主机配置中,添加 crontab 条目以每 6 小时备份一次。
```
0 */6 * * * /usr/local/bin/docker-compose -f /path/to/backup-job.yml up -d > /dev/null
```
或者
```
0 */6 * * * docker compose -p erpnext exec backend bench --site all backup --with-files > /dev/null
```
注意:
- 确保在执行时 `docker-compose` 或 `docker compose` 在路径中可用。
- 根据需要更改 cron 字符串。
- 在 `erpnext` 位置设置正确的项目名称。
- 对于 Docker Swarm,将其添加为 [swarm-cronjob](https://github.com/crazy-max/swarm-cronjob)
- 对于 Kubernetes 集群,将其添加为 `CronJob`。
@@ -0,0 +1,73 @@
---
title: 多租户
---
警告:如果站点将通过纯 http 提供服务,请勿在生产中使用此功能。
### 步骤 1
从 docker-compose.yml 中移除 traefik 服务
### 步骤 2
为每个需要暴露的端口添加服务。
例如 `port-site-1`、`port-site-2`、`port-site-3`。
```yaml
# ... 为简洁起见省略
services:
# ... 为简洁起见省略
port-site-1:
image: frappe/erpnext:v14.11.1
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: site1.local
SOCKETIO: websocket:9000
volumes:
- sites:/home/frappe/frappe-bench/sites
ports:
- "8080:8080"
port-site-2:
image: frappe/erpnext:v14.11.1
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: site2.local
SOCKETIO: websocket:9000
volumes:
- sites:/home/frappe/frappe-bench/sites
ports:
- "8081:8080"
port-site-3:
image: frappe/erpnext:v14.11.1
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: site3.local
SOCKETIO: websocket:9000
volumes:
- sites:/home/frappe/frappe-bench/sites
ports:
- "8082:8080"
```
注意:
- 上述配置将分别在端口 `8080`、`8081`、`8082` 上暴露 `site1.local`、`site2.local`、`site3.local`。
- 将 `site1.local` 更改为要从 bench 提供的站点名称。
- 根据你的服务名称更改 `BACKEND` 和 `SOCKETIO` 环境变量。
- 确保 `sites:` 卷作为 yaml 的一部分可用。
@@ -0,0 +1,86 @@
---
title: NGINX 和 ACME Companion
---
# nginx-proxy + acme-companion(HTTPS)
本指南说明如何使用 nginx-proxy 和 acme-companion 为 Frappe Docker 栈提供 HTTPS。
## 何时选择此方案
- 你想要一个简单的基于主机的反向代理
- 你运行单个 bench 或只有少量主机名
- 你更喜欢基于环境变量的配置
如果你需要高级路由或复杂的多站点配置,**Traefik** 通常是更好的选择。
## 前置条件
- 公共 DNS 将你的域名指向服务器
- 端口 80 和 443 可访问(HTTP-01 挑战)
- 已安装 Docker 和 Docker Compose v2
## 必需的环境变量
在 `.env` 中设置这些:
```bash
NGINX_PROXY_HOSTS=erp.your-domain.com
LETSENCRYPT_EMAIL=admin@your-domain.com
```
多个主机名(逗号分隔,无空格):
```bash
NGINX_PROXY_HOSTS=erp.your-domain.com,erp2.your-domain.com
LETSENCRYPT_EMAIL=admin@example.com
```
可选(非默认端口):
```bash
HTTP_PUBLISH_PORT=80
HTTPS_PUBLISH_PORT=443
```
## Compose 配置(HTTPS)
对于 HTTPS,你必须包含两个覆盖文件:
- `overrides/compose.nginxproxy.yaml`(nginx-proxy,VIRTUAL_HOST)
- `overrides/compose.nginxproxy-ssl.yaml`(acme-companion,LETSENCRYPT_HOST)
示例:
```sh
docker compose -f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.nginxproxy.yaml \
-f overrides/compose.nginxproxy-ssl.yaml \
config > ~/gitops/docker-compose.yml
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
> 如果你使用外部 MariaDB/Redis,请相应替换数据库和 Redis 覆盖文件。
## 主机名如何应用
`NGINX_PROXY_HOSTS` 是逗号分隔的主机名列表。覆盖文件将其应用为:
- `VIRTUAL_HOST` 用于 nginx-proxy 路由
- `LETSENCRYPT_HOST` 用于证书颁发
## 验证
检查日志以查看证书颁发和代理状态:
```sh
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml logs -f nginx-proxy
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml logs -f acme-companion
```
> 根据注册商的不同,分配可能需要一些时间,同时还必须确保 A 和 AAAA 记录正确指向服务器以颁发证书(如有必要)。
另见:[环境变量](../02-setup/04-env-variables.md)和 [TLS/SSL 配置总览](01-tls-ssl-setup.md)。
@@ -0,0 +1,48 @@
---
title: Caddy 与 HTTPS
---
# Caddy 反向代理(本地 HTTPS)
本指南说明如何使用 Caddy 作为 frontend 容器前的外部反向代理。它最适用于本地 HTTPS 或内部网络。
## 前置条件
- 在主机端口上暴露 frontend 容器(默认 8080)
- 将本地域名添加到 hosts 文件(或使用内部 DNS)
- 安装 Caddy
## 步骤 1:暴露 frontend 服务
包含 no-proxy 覆盖文件,使 frontend 在主机上可访问:
```sh
docker compose -f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.noproxy.yaml \
config > ~/gitops/docker-compose.yml
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
如果你更改了 HTTP 端口,请记下 `HTTP_PUBLISH_PORT` 的值以用于下一步。
## 步骤 2:配置 Caddy
将站点块添加到你的 Caddyfile(通常是 `/etc/caddy/Caddyfile`):
```caddy
erp.localdev.net {
tls internal
reverse_proxy localhost:8080
}
```
如果你更改了发布的 frontend 端口,请将 `8080` 替换为你的端口。
## 步骤 3:信任 Caddy 根证书
使用 `tls internal` 时,Caddy 从其内部 CA 颁发证书。在任何需要访问站点的客户端上导入并信任 Caddy 根证书。
另见:[TLS/SSL 配置总览](01-tls-ssl-setup.md)。
@@ -0,0 +1,145 @@
---
title: 自动化构建和部署
---
# 简介
这是关于自定义 Frappe 镜像自动化构建和部署的简要指南。
根据你的具体配置、环境和安全规则,以下信息可能需要根据你的需求进行调整。
# 要求
## 知识
需要具备 Docker 和构建流水线的基础知识。
请先参考配置章节,特别是[构建配置](../02-setup/02-build-setup.md),以获得基本理解。
## 额外文件
### 应用
在构建时可以提供 `apps.json` 文件。这指定了要包含在自定义镜像中的额外 Frappe 框架兼容应用。
### 构建
需要适用于你的 CI 平台和环境的工作流文件。
## 构建缓存
与手动构建不同,自动化构建命令通常不应使用 `--no-cache`。
重用缓存层可以大大减少构建时间、磁盘使用率和推送到镜像仓库时的带宽使用。
相反,可以使用 `CACHE_BUST` 来控制 Frappe 层的缓存失效(当需要重新构建时)。
这尤其相关,因为 `apps.json` 是作为 secret 提供的。Secret 内容不是 Docker 层缓存键的一部分,因此无法自动触发缓存失效。
因此,即使自定义应用定义已更改,Docker 也可能重用较旧的缓存层。
例外:Frappe 框架的较新版本可能仍会触发重建层。
### 使用 `CACHE_BUST` 进行缓存失效的可能技术:
1. 无覆盖:使用正常的 Docker 层缓存 - 在此使用场景中不推荐
2. 时间戳:在每次流水线运行时强制重建 - 因为值每次都会变化
3. 流水线运行 ID:每次 CI 运行重建一次
4. 提交 SHA:每次提交重建一次
5. apps.json 哈希:仅在自定义应用定义更改时重建 - 额外要求,见下文示例
### 示例:
#### 1. 无覆盖 - 不推荐
这将重用之前构建的层,除了 Frappe 框架外不会检查应用更新
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 2. 时间戳
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 3. 来自 GitHub 的流水线运行 ID
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 4. 来自 GitHub 的提交 SHA
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 5. apps.json 哈希
注意:在 `apps.json` 中使用分支引用时,哈希仅在文件内容更改时变化,而不是在上游应用分支收到更新时变化。此方法在固定特定提交或发布版时效果最佳。
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
## 自动化部署
### 自动化站点迁移
更新自定义镜像或部署新版本的应用后,必须使用 `bench migrate` 执行数据库迁移。
如果不运行迁移,站点可能变得不一致或无法正确启动。
对于自动化部署,不应手动执行此步骤。
考虑使用作为 Compose 覆盖文件提供的专用 `migrator` 服务。
它确保在栈启动时自动执行迁移。
这种方法在 CI/CD 流水线中特别有用,因为没有对后端容器的交互式访问。
参见 [Compose 覆盖文件](../../overrides/compose.migrator.yaml)
+3
View File
@@ -0,0 +1,3 @@
---
title: 生产环境
---
@@ -0,0 +1,85 @@
---
title: 站点操作
---
# 站点操作
> 💡 如果你有非标准项目名称,应在 `docker-compose` 命令中设置 `--project-name` 选项。
## 设置新站点
注意:
- 在尝试创建新站点之前,请等待 `db` 服务启动并且 `configurator` 退出。通常需要最多 10 秒。
- 如果之前传递了 `-p`,你还必须传递 `-p <project_name>`,例如 `docker-compose -p <project_name> exec (命令的其余部分)`。
```sh
docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --db-root-password <db-password> --admin-password <admin-password> <site-name>
```
如果你需要安装某个应用,请指定 `--install-app`。要查看所有选项,只需运行 `bench new-site --help`。
要创建 Postgres 站点(假设你已经使用 [Postgres Compose 覆盖文件](../02-setup/05-overrides.md)),`root_login` 和 `root_password` 现在由 `configurator` 自动设置。对于主要版本升级,请参考 [Postgres 迁移指南](../06-migration/03-postgres-major-version-upgrade.md)。
要创建新的 Postgres 站点:
```sh
docker-compose exec backend bench new-site --db-type postgres --admin-password <admin-password> <site-name>
```
## 将备份推送到 S3 存储
我们有脚本可以帮助将最新备份推送到 S3。
```sh
docker-compose exec backend push_backup.py --site-name <site-name> --bucket <bucket> --region-name <region> --endpoint-url <endpoint-url> --aws-access-key-id <access-key> --aws-secret-access-key <secret-key>
```
请注意,你只能手动恢复备份。
## 编辑配置
在某些情况下可能需要手动编辑配置,
一种这样的情况是使用 Amazon RDS(或任何其他 DBaaS)。
完整说明请参考 [wiki](<https://github.com/frappe/frappe/wiki/Using-Frappe-with-Amazon-RDS-(or-any-other-DBaaS)>)。常见问题可以在 Issues 和论坛中找到。
必须使用以下命令编辑 `sites` 卷中的 `common_site_config.json` 或 `site_config.json`:
```sh
docker run --rm -it \
-v <project-name>_sites:/sites \
alpine vi /sites/common_site_config.json
```
你可以使用任何你选择的镜像代替 `alpine`。
## 健康检查
对于 socketio 和 gunicorn 服务,ping 主机名:端口就足够了。对于 worker 和 scheduler,有一个需要执行的命令。
```shell
docker-compose exec backend healthcheck.sh --ping-service mongodb:27017
```
可以使用选项 `-p` 或 `--ping-service` 将额外服务作为健康检查的一部分进行 ping。
此检查确保给定服务应该与 common_site_config.json 中的服务一起连接。
如果连接到服务失败,命令将以退出代码 1 失败。
---
有关 `backup`、`drop-site` 或 `migrate` 等命令的参考,请查看[官方指南](https://frappeframework.com/docs/v13/user/en/bench/frappe-commands)或运行:
```sh
docker-compose exec backend bench --help
```
## 迁移站点
注意:
- 在尝试迁移站点之前,请等待 `db` 服务启动并且 `configurator` 退出。通常需要最多 10 秒。
```sh
docker-compose exec backend bench --site <site-name> migrate
```
+9
View File
@@ -0,0 +1,9 @@
---
title: Docker部署
index: false
icon: laptop-code
category:
- 使用指南
---
<Catalog />
+3
View File
@@ -0,0 +1,3 @@
---
title: 运维操作
---
@@ -0,0 +1,446 @@
---
title: 快速上手
---
# 快速上手
## 前置条件
为了开始开发,你需要满足以下前置条件:
- Docker
- docker-compose
- 已添加到 docker 组的用户
建议为 docker 分配至少 4GB 的 RAM:
- [Windows 说明](https://docs.docker.com/docker-for-windows/#resources)
- [macOS 说明](https://docs.docker.com/desktop/settings/mac/#advanced)
以下是显示帮助手册中相关设置的屏幕截图
![image](../images/Docker%20Manual%20Screenshot%20-%20Resources%20section.png)
以下是显示 Mac 上 Docker Desktop 设置的屏幕截图
![images](../images/Docker%20Desktop%20Screenshot%20-%20Resources%20section.png)
## 为开发引导容器
克隆并切换到 frappe_docker 目录
```shell
git clone https://github.com/frappe/frappe_docker.git
cd frappe_docker
```
从 `devcontainer-example` 复制示例 devcontainer 配置到 `.devcontainer`
```shell
cp -R devcontainer-example .devcontainer
```
从 `development/vscode-example` 复制示例 vscode 配置到 `development/.vscode`。这将设置基本的调试配置。
```shell
cp -R development/vscode-example development/.vscode
```
## 使用 VSCode Remote Containers 扩展
对于大多数开始 Frappe 开发的人来说,最好的解决方案是使用 [VSCode Dev Containers 扩展](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)。
在容器中打开文件夹之前,确定你要使用的数据库。默认是 MariaDB。
如果你想使用 PostgreSQL,请编辑 `.devcontainer/docker-compose.yml` 并取消注释 `postgresql` 服务的部分,你可能还想注释掉 `mariadb`。
VSCode 应该会自动询问你是否安装所需的扩展,也可以按如下方式手动安装:
- 为 VSCode 安装 Dev Containers
- 通过命令行 `code --install-extension ms-vscode-remote.remote-containers`
- 点击 Visual Studio Marketplace 中的安装按钮:[Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
- VSCode 中的查看:扩展命令(Windows:Ctrl+Shift+X;macOS:Cmd+Shift+X)然后搜索扩展 `ms-vscode-remote.remote-containers`
安装扩展后,你可以:
- 在 VS Code 中打开 frappe_docker 文件夹。
- `code .`
- 从命令面板(Ctrl + Shift + P)启动命令 `Dev Containers: Reopen in Container`。你也可以点击左下角访问远程容器菜单。
注意:
- `development` 目录被 git 忽略。它被挂载并在容器内可用。在此目录中创建你的所有 bench(bench 的安装,管理 frappe 的工具)。
- 安装了 Node v14 和 v10。使用 `nvm ls` 检查。默认使用 Node v14。
### 设置第一个 bench
> 跳转到[脚本](#setup-bench--new-site-using-script)部分以自动设置 bench。或者,你可以使用以下指南手动设置 bench。
在容器内的终端中运行以下命令。你可能需要在 VSCode 中创建新终端。
注意:在执行以下操作之前,确保用户是 **frappe**。
```shell
bench init --skip-redis-config-generation frappe-bench
cd frappe-bench
```
要设置 frappe 框架版本 14 bench,将 `PYENV_VERSION` 环境变量设置为 `3.10.5`(默认)并使用 NodeJS 版本 16(默认),
```shell
# 使用默认环境
bench init --skip-redis-config-generation --frappe-branch version-14 frappe-bench
# 或显式设置环境版本
nvm use v16
PYENV_VERSION=3.10.13 bench init --skip-redis-config-generation --frappe-branch version-14 frappe-bench
# 切换目录
cd frappe-bench
```
要设置 frappe 框架版本 13 bench,将 `PYENV_VERSION` 环境变量设置为 `3.9.17` 并使用 NodeJS 版本 14,
```shell
nvm use v14
PYENV_VERSION=3.9.17 bench init --skip-redis-config-generation --frappe-branch version-13 frappe-bench
cd frappe-bench
```
此时目录结构将非常接近以下内容(如果不是完全相同的话),
```
development/
├── frappe-bench/ # 你的实际 Frappe 安装
│ ├── apps/ # 所有已安装的 Frappe 应用
│ │ ├── frappe/ # 核心框架(不要直接修改)
│ │ ├── erpnext/ # ERPNext 应用(如果已安装)
│ │ └── my_custom_app/ # 你的自定义应用(随意编辑)
│ ├── sites/ # 多租户站点
│ │ ├── development.localhost/ # 默认开发站点
│ │ │ ├── site_config.json # 站点特定配置
│ │ │ └── private/files/ # 上传的文件
│ │ └── common_site_config.json # 共享配置
│ ├── env/ # Python 虚拟环境
│ ├── logs/ # 应用日志
│ └── config/ # Bench 级配置
└── .vscode/ # VSCode 工作区设置
```
### 设置主机
我们需要告诉 bench 使用正确的容器而不是 localhost。在容器内运行以下命令:
```shell
bench set-config -g db_host mariadb
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
```
如果上述命令因任何原因失败,请手动在 `common_site_config.json` 中设置值。
```json
{
"db_host": "mariadb",
"redis_cache": "redis://redis-cache:6379",
"redis_queue": "redis://redis-queue:6379",
"redis_socketio": "redis://redis-queue:6379"
}
```
### 编辑 Honcho 的 Procfile
注意:使用 `--skip-redis-config-generation` 选项在 bench init 期间,这些操作不再需要。但至少,看一下 ProcFile 以了解 bench 在启动命令时启动 honcho 的情况
Honcho 是 Bench 用来管理 Frappe 所需的所有进程的工具。通常,这些都在 localhost 上运行,但在这种情况下,我们有用于 Redis 的外部容器。因此,我们必须阻止 Honcho 尝试启动 Redis 进程。
Honcho 安装在全局 python 环境中,与 bench 一起。要使其在本地可用,你必须在创建的每个 `frappe-bench/env` 中安装它。使用命令 `./env/bin/pip install honcho` 安装它。如果你希望将其作为 VSCode 启动配置的一部分使用,则需要在本地安装它。
打开 Procfile 文件并移除包含 Redis 配置的三行,可以通过手动编辑文件:
```shell
code Procfile
```
或运行以下命令:
```shell
sed -i '/redis/d' ./Procfile
```
### 使用 bench 创建新站点
你可以使用以下命令创建新站点:
```shell
bench new-site --mariadb-user-host-login-scope=% sitename
```
sitename 必须以 .localhost 结尾以便在本地尝试部署。
例如:
```shell
bench new-site --mariadb-user-host-login-scope=% development.localhost
```
同样的命令也可以非交互式运行:
```shell
bench new-site --db-root-password 123 --admin-password admin --mariadb-user-host-login-scope=% development.localhost
```
该命令将询问 MariaDB root 密码。默认 root 密码是 `123`。
这将创建一个新站点和一个 `development.localhost` 目录在 `frappe-bench/sites` 下。
选项 `--mariadb-user-host-login-scope=%` 将配置站点的数据库凭据以与 docker 一起工作。
如果你在 Linux、Mac 上,可能需要配置系统的 /etc/hosts,或者其 Windows 等效文件。
要使用 PostgreSQL 作为数据库设置站点,请使用选项 `--db-type postgres` 和 `--db-host postgresql`。(仅从 v12 开始可用,目前不适用于 ERPNext)。
示例:
```shell
bench new-site --db-type postgres --db-host postgresql mypgsql.localhost
```
要避免输入 postgresql 用户名和 root 密码,请在 `common_site_config.json` 中设置,
```shell
bench config set-common-config -c root_login postgres
bench config set-common-config -c root_password '"123"'
```
注意:如果不需要 PostgreSQL,可以停止 postgresql 服务/容器。
### 在新站点上设置 bench 开发者模式
要开发新应用,最后一步是将站点设置为开发者模式。文档可在[此链接](https://frappe.io/docs/user/en/guides/app-development/how-enable-developer-mode-in-frappe)获得。
```shell
bench --site development.localhost set-config developer_mode 1
bench --site development.localhost clear-cache
```
### 安装应用
要安装应用,我们需要从适当的 git 仓库获取它,然后安装到适当的站点:
你可以查看 [VSCode 容器远程扩展文档](https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container) 关于 git 凭据共享。
安装自定义应用
```shell
# --branch 是可选的,使用它指向自定义应用仓库上的分支
bench get-app --branch version-12 https://github.com/myusername/myapp
bench --site development.localhost install-app myapp
```
在撰写本文时,Payments 应用已从版本 14 ERPNext 应用中分离出来,现在是一个独立的应用。ERPNext 不会安装它。
```shell
bench get-app --branch version-14 --resolve-deps erpnext
bench --site development.localhost install-app erpnext
```
要安装 ERPNext(从 version-13 分支):
```shell
bench get-app --branch version-13 erpnext
bench --site development.localhost install-app erpnext
```
注意:frappe 和 erpnext 必须在同名分支上。例如 version-14
如果你收到关于版本不匹配的错误,你可以使用 `switch-to-branch` 命令对齐版本。
```shell
bench switch-to-branch version-xx
```
### 在不调试的情况下启动 Frappe
从 `frappe-bench` 目录执行以下命令。
```shell
bench start
```
你现在可以使用用户 `Administrator` 和创建站点时选择的密码登录。
你的网站现在可以在 [development.localhost:8000](http://development.localhost:8000) 访问
注意:要使用调试器启动 bench,请参考调试部分。
### 使用脚本设置 bench / 新站点
大多数开发者与众多客户和版本一起工作。此外,可能需要团队中的每个人为处理客户的客户安装应用。
这通过使用脚本来简化,以自动化创建新 bench / 站点和安装所需应用的过程。创建的站点的 `Administrator` 密码是 `admin`。
默认使用示例 `apps-example.json`,它在当前稳定版本上安装 erpnext。要安装自定义应用,将 `apps-example.json` 复制到自定义 json 文件并更改应用列表。将此文件传递给 `installer.py` 脚本。
> 你可能在私有仓库中有应用,这可能需要 ssh 访问。你可以在 linux 上使用主目录的 SSH(可在 docker-compose.yml 中配置)。
```shell
python installer.py #传递 --db-type postgres 用于 postgresdb
```
命令帮助
```shell
python installer.py --help
usage: installer.py [-h] [-j APPS_JSON] [-b BENCH_NAME] [-s SITE_NAME] [-r FRAPPE_REPO] [-t FRAPPE_BRANCH] [-p PY_VERSION] [-n NODE_VERSION] [-v] [-a ADMIN_PASSWORD] [-d DB_TYPE]
options:
-h, --help show this help message and exit
-j APPS_JSON, --apps-json APPS_JSON
Path to apps.json, default: apps-example.json
-b BENCH_NAME, --bench-name BENCH_NAME
Bench directory name, default: frappe-bench
-s SITE_NAME, --site-name SITE_NAME
Site name, should end with .localhost, default: development.localhost
-r FRAPPE_REPO, --frappe-repo FRAPPE_REPO
frappe repo to use, default: https://github.com/frappe/frappe
-t FRAPPE_BRANCH, --frappe-branch FRAPPE_BRANCH
frappe repo to use, default: version-15
-p PY_VERSION, --py-version PY_VERSION
python version, default: Not Set
-n NODE_VERSION, --node-version NODE_VERSION
node version, default: Not Set
-v, --verbose verbose output
-a ADMIN_PASSWORD, --admin-password ADMIN_PASSWORD
admin password for site, default: admin
-d DB_TYPE, --db-type DB_TYPE
Database type to use (e.g., mariadb or postgres)
```
为客户创建新 bench 和/或站点,使用以下默认值。
- MariaDB root 密码:`123`
- 管理员密码:`admin`
> 要使用 Postegres DB,注释掉 mariabdb 服务并取消注释 postegres 服务。
### 使用 Visual Studio Code Python 调试启动 Frappe
要在 Visual Studio Code 中启用 Python 调试,你必须首先在容器内安装 `ms-python.python` 扩展。这应该已经自动发生,但根据你的 VSCode 配置,你可以强制它:
- 点击 VSCode 内的扩展图标
- 搜索 `ms-python.python`
- 点击 `Install on Dev Container: Frappe Bench`
- 点击 'Reload'
我们需要通过 VSCode 调试器单独启动 bench。因此,**不要**运行 `bench start`,你应该在 frappe-bench 目录中运行以下命令:
```shell
honcho start \
socketio \
watch \
schedule \
worker_short \
worker_long
```
或者你可以使用 VSCode 启动配置 "Honcho SocketIO Watch Schedule Worker",它启动与上面相同的命令。
此命令启动除 Redis(已在单独容器中运行)和 `web` 进程之外的所有进程。后者最终可以从 VSCode 的调试器选项卡通过点击 "play" 按钮启动。
你现在可以使用用户 `Administrator` 和创建站点时选择的密码登录,如果你遵循了本指南的无人值守安装,该密码将是 `admin`。
要调试 worker,跳过使用 honcho 启动 worker 并使用 VSCode 调试器启动它。
对于 devcontainer 中的高级 vscode 配置,更改 `development/.vscode` 中的配置文件。
## 使用交互式控制台进行开发
你可以在终端中使用以下命令启动简单的交互式 shell 控制台:
```shell
bench --site development.localhost console
```
更可能的是,你可能想启动基于 Jupyter 内核的 VSCode 交互式控制台。
启动 VSCode 命令面板(cmd+shift+p 或 ctrl+shift+p),运行命令 `Python: Select interpreter to start Jupyter server` 并选择 `/workspace/development/frappe-bench/env/bin/python`。
第一步是安装和更新所需的软件。通常 frappe 框架可能需要较旧版本的 Jupyter,而 VSCode 喜欢快速前进,这可能会[导致问题](https://github.com/jupyter/jupyter_console/issues/158)。因此我们需要运行以下命令。
```shell
/workspace/development/frappe-bench/env/bin/python -m pip install --upgrade jupyter ipykernel ipython
```
然后,从 VSCode 命令面板运行命令 `Python: Show Python interactive window`。
将 `development.localhost` 替换为你的站点并在 Jupyter 单元格中运行以下代码:
```python
import frappe
frappe.init(site='development.localhost', sites_path='/workspace/development/frappe-bench/sites')
frappe.connect()
frappe.local.lang = frappe.db.get_default('lang')
frappe.db.connect()
```
第一个命令可能需要几秒钟才能执行,这是预期的。
## 手动启动容器
示例配置通过 VS Code 转发开发端口,不通过 Docker 发布它们。如果你不使用 VS Code,请首先按照 [仅 CLI 的端口配置](04-alternate-setup.md#configure-ports-for-cli-only-use),然后手动启动容器:
### 运行容器
```shell
docker-compose -f .devcontainer/docker-compose.yml up -d
```
并使用以下命令进入开发容器的交互式 shell:
```shell
docker exec -e "TERM=xterm-256color" -w /workspace/development -it devcontainer-frappe-1 bash
```
## 在开发期间使用额外服务
在 `.devcontainer/docker-compose.yml` 中添加开发所需的任何服务,然后重建并在 devcontainer 中重新打开。
例如:
```yaml
...
services:
...
postgresql:
image: postgres:11.8
environment:
POSTGRES_PASSWORD: 123
volumes:
- postgresql-data:/var/lib/postgresql/data
ports:
- 5432:5432
volumes:
...
postgresql-data:
```
从 `frappe` 开发容器通过服务名称访问服务。上述服务将通过主机名 `postgresql` 访问。如果端口发布到主机,则通过 `localhost:5432` 访问。
## 使用 Cypress UI 测试
要在 docker 环境中运行基于 cypress 的 UI 测试,请按照以下步骤操作:
1. 使用脚本 `install_x11_deps.sh` 在 VM 上安装和设置 X11 工具
```shell
sudo bash ./install_x11_deps.sh
```
此脚本将安装所需的依赖项,启用 X11Forwarding 并重启 SSH 守护进程并导出 `DISPLAY` 变量。
2. 运行 X11 服务 `startx` 或 `xquartz`
3. 启动 docker compose 服务。
4. 使用 `docker exec..` 命令 SSH 到 ui-tester 服务
5. 导出 CYPRESS_baseUrl 和其他所需的环境变量
6. 通过发出 `cypress run command` 启动 Cypress UI 控制台
> 更多参考:[Cypress 官方文档](https://www.cypress.io/blog/2019/05/02/run-cypress-with-a-single-docker-command)
> 确保始终导出 DISPLAY 环境。
## 使用 Mailpit 测试邮件服务
要使用 Mailpit,只需取消注释 docker-compose.yml 文件中的服务。
然后界面可在端口 8025 下访问,smtp 服务可作为 mailpit:1025 使用。
@@ -0,0 +1,20 @@
---
title: 调试
---
将以下配置添加到 `launch.json` 的 `configurations` 数组中以启动 bench 控制台并使用调试器。将 `development.localhost` 替换为适当的站点。同时将 `frappe-bench` 替换为 bench 目录的名称。
```json
{
"name": "Bench Console",
"type": "python",
"request": "launch",
"program": "/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
"args": ["frappe", "--site", "development.localhost", "console"],
"pythonPath": "/frappe-bench/env/bin/python",
"cwd": "/frappe-bench/sites",
"env": {
"DEV_SERVER": "1"
}
}
```
@@ -0,0 +1,17 @@
---
title: 本地服务
---
在 `.devcontainer/docker-compose.yml` 中向 frappe 容器添加以下内容:
```yaml
...
frappe:
...
extra_hosts:
app1.localhost: 172.17.0.1
app2.localhost: 172.17.0.1
...
```
这使得域名 `app1.localhost` 和 `app2.localhost` 连接到 docker 主机并连接到在 `localhost` 上运行的服务。
@@ -0,0 +1,274 @@
---
title: Docker 开发配置
---
# Docker 开发配置
在 x86 和 ARM 架构的计算机上设置 Frappe 开发环境的完整指南,通过直接运行容器并在其中通过终端工作。无需 VS Code Dev Containers 扩展。
> [!IMPORTANT]
> Devcontainers 是 Frappe Framework 的预期开发配置,但如果你不想使用该方法,请按照以下说明直接使用 CLI
---
## 前置条件
- **Docker Desktop**(仅适用于 MacOS)— [在此下载](https://www.docker.com/products/docker-desktop/)
- **Git**
- 终端(iTerm2 或内置的 Terminal.app)
### Docker Desktop 资源分配(关键)
1. 打开 Docker Desktop → **设置** → **资源**
2. **内存**:至少 **6 GB**(推荐 8 GB)
3. **CPU**:至少 **4**
4. **磁盘镜像大小**:至少 **60 GB**
5. 点击 **应用并重启**
---
## 步骤 1 — 将 ARM64 设置为默认平台(仅适用于 ARM 架构系统)
```bash
export DOCKER_DEFAULT_PLATFORM=linux/arm64
```
使其永久生效:
```bash
echo 'export DOCKER_DEFAULT_PLATFORM=linux/arm64' >> ~/.zshrc
source ~/.zshrc
```
---
## 步骤 2 — 克隆仓库
```bash
git clone https://github.com/frappe/frappe_docker.git
cd frappe_docker
```
---
## 步骤 3 — 设置开发容器配置
`devcontainer-example/` 文件夹包含用于开发的现成 `docker-compose.yml`。将其复制到适当位置:
```bash
cp -R devcontainer-example .devcontainer
```
这将为你提供 `.devcontainer/docker-compose.yml`,其中定义了所有你需要的服务:
- `frappe` — 主开发容器(Debian、Python、Node、bench)
- `mariadb` — 数据库
- `redis-cache` — 缓存层
- `redis-queue` — 后台任务队列
### 为仅 CLI 使用配置端口
示例配置适用于 VS Code Dev Containers,并通过 VS Code 转发开发端口。由于此配置直接运行 Docker Compose,请将以下环境变量和仅环回端口映射添加到 `.devcontainer/docker-compose.yml` 中的 `frappe` 服务:
```yaml
services:
frappe:
# ... 现有配置
environment:
- SHELL=/bin/bash
- FRAPPE_BIND_ADDR=0.0.0.0
ports:
- "127.0.0.1:8000-8005:8000-8005"
- "127.0.0.1:9000-9005:9000-9005"
```
Frappe 的开发服务器默认绑定到 `127.0.0.1`。`FRAPPE_BIND_ADDR=0.0.0.0` 使其可通过 Docker 的端口映射访问,同时将发布的端口绑定到 `127.0.0.1` 使其仅可从本地机器访问。
---
## 步骤 4 — 为所有服务添加 ARM64 平台
在任何编辑器中打开 `.devcontainer/docker-compose.yml` 并为每个服务块添加 `platform: linux/arm64`。它应该如下所示:
```yaml
services:
frappe:
image: frappe/bench:latest
platform: linux/arm64
# ... 其余配置
mariadb:
image: mariadb:10.8
platform: linux/arm64
# ...
redis-cache:
image: redis:6.2-alpine
platform: linux/arm64
# ...
redis-queue:
image: redis:6.2-alpine
platform: linux/arm64
# ...
```
> 如果不这样做,Docker 可能会拉取 amd64 镜像并通过 Rosetta 模拟它们 - 可以工作但会明显变慢。
---
## 步骤 5 — 启动容器
```bash
docker compose -f .devcontainer/docker-compose.yml up -d
```
验证所有服务都在运行:
```bash
docker compose -f .devcontainer/docker-compose.yml ps
```
你应该看到所有服务的状态为 `Up`。
如果你收到类似以下的错误,
```log
Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint devcontainer-frappe-1 (44b337b68d100e914fab0ce446ed08d791cc73aaffb05cf47c347c00ff88f567): Bind for 0.0.0.0:9001 failed: port is already allocated
```
- 使用 `lsof -i :PORT` 检查端口是否被其他服务使用
> 通常在 MacOS 上,端口 8000 和 9000 通常保留给系统使用
- 更改 `frappe` 服务下的主机端端口范围
例如:
```yaml
ports:
- "127.0.0.1:8100-8105:8000-8005"
- "127.0.0.1:9100-9105:9000-9005"
```
使用此示例,在端口 `8100` 而不是 `8000` 上打开第一个站点。
---
## 步骤 6 — 进入开发容器
```bash
docker exec -e "TERM=xterm-256color" -w /workspace/development -it devcontainer-frappe-1 bash
```
> 容器名称通常是 `devcontainer-frappe-1`。如果不同,请使用 `docker ps` 检查并使用显示的实际名称。
你现在以 `frappe` 用户身份进入容器。本指南中的所有后续命令都在**容器内**运行,除非另有说明。
---
## 步骤 7 — 初始化 Bench
```bash
bench init --skip-redis-config-generation --frappe-branch version-16 frappe-bench
cd frappe-bench
```
使用 `version-16` 获取最新稳定版本。如果需要,可以替换为 `version-15`。
这将创建:
```
development/
└── frappe-bench/
├── apps/ ← 所有 Frappe 应用都在这里
├── sites/ ← 你的站点(数据库、上传的文件)
├── env/ ← Python 虚拟环境
├── logs/
└── Procfile
```
---
## 步骤 8 — 配置服务主机
告诉 bench 使用容器化的服务(而非 localhost):
```bash
bench set-config -g db_host mariadb
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
```
如果任何命令失败,请直接编辑文件:
```bash
nano sites/common_site_config.json
```
粘贴:
```json
{
"db_host": "mariadb",
"redis_cache": "redis://redis-cache:6379",
"redis_queue": "redis://redis-queue:6379",
"redis_socketio": "redis://redis-queue:6379"
}
```
---
## 步骤 9 — 修复 Procfile
Redis 在单独的容器中运行,因此从 Honcho 的 Procfile 中移除它以避免冲突:
```bash
sudo sed -i '/redis/d' ./Procfile
```
---
## 步骤 10 — 创建站点
```bash
bench new-site \
--db-root-password 123 \
--admin-password admin \
--mariadb-user-host-login-scope=% \
development.localhost
```
- MariaDB root 密码:`123`(在 docker-compose 默认值中设置)
- 管理员密码:`admin`(更改为你想要的任何密码)
- 站点名称**必须以 `.localhost` 结尾**
---
## 步骤 11 — 启用开发者模式
```bash
bench --site development.localhost set-config developer_mode 1
bench --site development.localhost clear-cache
```
---
## 步骤 12 — 将 development.localhost 添加到 /etc/hosts(在你的 Mac 上)
在**你的 Mac 上**运行此命令(不在容器内):
```bash
echo "127.0.0.1 development.localhost" | sudo tee -a /etc/hosts
```
---
## 步骤 13 — 启动开发服务器
```bash
bench build # (可选)
bench start
```
在浏览器中打开 **http://development.localhost:8000**
登录:`Administrator` / `admin`
+3
View File
@@ -0,0 +1,3 @@
---
title: 开发指南
---
@@ -0,0 +1,126 @@
---
title: 从多镜像配置迁移
---
## 从多镜像配置迁移
所有容器现在使用相同的镜像。使用 `frappe/erpnext` 代替 `frappe/frappe-worker`、`frappe/frappe-nginx`、`frappe/frappe-socketio`、`frappe/erpnext-worker` 和 `frappe/erpnext-nginx`。
现在你需要为以下容器指定命令和环境变量:
### Frontend
要使 `frontend` 服务作为静态资源前端和反向代理,你需要传递 `nginx-entrypoint.sh` 作为容器 `command`,以及指向 gunicorn 和 websocket 服务的 `{host}:{port}` 的 `BACKEND` 和 `SOCKETIO` 环境变量。查看[环境变量](../02-setup/04-env-variables.md)
现在你只需要在位置 `/home/frappe/frappe-bench/sites` 挂载 `sites` 卷。不需要 `assets` 卷和资源填充脚本或步骤。
示例更改:
```yaml
# ... 为简洁起见省略
frontend:
image: frappe/erpnext:
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
SOCKETIO: websocket:9000
volumes:
- sites:/home/frappe/frappe-bench/sites
# ... 为简洁起见省略
```
### Websocket
要使 `websocket` 服务作为 socketio 后端,你需要传递 `["node", "/home/frappe/frappe-bench/apps/frappe/socketio.js"]` 作为容器 `command`
示例更改:
```yaml
# ... 为简洁起见省略
websocket:
image: frappe/erpnext:
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
# ... 为简洁起见省略
```
### Configurator
要使 `configurator` 服务作为一次性配置作业,你需要传递 `["bash", "-c"]` 作为容器 `entrypoint`,并将 bash 脚本内联到 yaml 中。容器中现在没有 `configure.py`。
示例更改:
```yaml
# ... 为简洁起见省略
configurator:
image: frappe/erpnext:
restart: "no"
entrypoint:
- bash
- -c
command:
- >
bench set-config -g db_host $;
bench set-config -gp db_port $;
bench set-config -g redis_cache "redis://$";
bench set-config -g redis_queue "redis://$";
bench set-config -gp socketio_port $;
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
# ... 为简洁起见省略
```
### 站点创建
要使 `create-site` 服务作为一次性站点创建作业,你需要传递 `["bash", "-c"]` 作为容器 `entrypoint`,并将 bash 脚本内联到 yaml 中。确保使用 `--mariadb-user-host-login-scope=%`,因为上游 bench 安装在容器中。
`WORKDIR` 已更改为 `/home/frappe/frappe-bench`,就像我们习惯的 `bench` 设置一样。因此查找 `common_site_config.json` 的路径已更改为 `sites/common_site_config.json`。
示例更改:
```yaml
# ... 为简洁起见省略
create-site:
image: frappe/erpnext:
restart: "no"
entrypoint:
- bash
- -c
command:
- >
wait-for-it -t 120 db:3306;
wait-for-it -t 120 redis-cache:6379;
wait-for-it -t 120 redis-queue:6379;
export start=date +%s;
until [[ -n grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty" ]] && \
[[ -n grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty" ]] && \
[[ -n grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty" ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( date +%s-start > 120 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
bench new-site --mariadb-user-host-login-scope=% --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend;
# ... 为简洁起见省略
```
## 从具有嵌套 sites/assets 卷的镜像升级
以前的镜像单独声明了 `VOLUME /home/frappe/frappe-bench/sites/assets`。这在 `sites` 卷内创建了一个隐式嵌套挂载点,可能导致 Docker 在多容器配置中为每个容器附加不同的匿名卷。
该声明已被移除。`sites` 现在是唯一的共享挂载,与 compose 配置和文档一致。
**拉取更新镜像后:**
- 重新创建所有容器(`docker compose up --force-recreate`)。如果不这样做,Docker 可能会保留更改前附加的旧匿名 `sites/assets` 卷
- 不需要 `bench build` - 这仅修复挂载一致性,而不是资源工作流。
@@ -0,0 +1,83 @@
---
title: 从 Traefik v2 迁移到 v3
---
# 将现有 Traefik v2 实例迁移到 v3
如果你已经使用 `frappe_docker` 运行 Traefik v2 并想升级到 v3,请使用本指南。它侧重于镜像升级和影响现有配置的 v3 路由规则更改。
> 注意:Traefik v2 -> v3 迁移已完成。提供的覆盖文件不再设置 `core.defaultRuleSyntax` 或每个路由的 `ruleSyntax` 标签,因为 v3 是默认规则语法。
> 注意:如果你有一个系统必须继续在 v2 上运行(尽管已 EOL),你可以在 Traefik 服务中使用 `--core.defaultRuleSyntax=v2` 固定 v2 规则语法。
### 开始之前
在迁移任何内容之前,始终建议创建备份。安全第一。特别是,应备份 compose 和 .env。
### 快速升级摘要
1. 拉取更新的仓库
2. 更新环境变量,特别是将 `SITES` 更新为 `SITES_RULE`
3. 重新生成 Compose 配置并重启栈
#### 多个主机名
v2 允许在 `Host(...)` 中使用逗号分隔的主机列表。在 v3 中,Traefik 使用逻辑 OR。
**之前(v2):**
```
Host(.example.com,.example.com)
```
**之后(v3):**
```
Host(.example.com) || Host(.example.com)
```
### 步骤 1:将 `SITES` 替换为 `SITES_RULE`
HTTPS 和多 bench 配置的所有 Traefik 路由现在使用 `SITES_RULE`,这是一个完整的 v3 规则表达式。
**单站点:**
```
SITES_RULE=Host(rp.example.com)
```
**多站点:**
```
SITES_RULE=Host(.example.com) || Host(.example.com)
```
### 步骤 2:重新生成并启动你的 Compose 配置
HTTPS 示例:
```sh
docker compose --env-file .env \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.https.yaml \
config > ~/gitops/docker-compose.yml
```
```sh
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
参见[单服务器示例](../02-setup/07-single-server-example.md)
### 步骤 3:验证 Traefik
重启后,Traefik 将使用新的受支持版本 3.6,在进行调整时将使用相同的 URL 访问实例。之后,页面应该可以通过代理访问,如果使用 HTTPS,则通过 HTTPS 访问。
### 回滚
如果你需要回滚:
1. 将 Traefik 镜像恢复为 `v2.11`
2. 恢复旧的 `SITES` 变量格式和 v2 规则
3. 重新生成 Compose 配置并重启
@@ -0,0 +1,49 @@
---
title: PostgreSQL 主要版本升级
---
# PostgreSQL 主要版本升级(v13 到 v15)
将 PostgreSQL 从版本 13 升级到 15 是一个主要版本跳跃。由于 PostgreSQL 不支持就地数据目录升级,现有用户必须使用 `pg_dump` 手动迁移其数据。
### **迁移步骤**
1. **备份现有数据(版本 13):**
在更新 Compose 文件之前,确保你的容器正在运行并执行所有数据库的转储。
```bash
docker exec -it <project_name>-db-1 pg_dumpall -U postgres > full_dump.sql
```
2. **停止并移除容器:**
```bash
docker compose down
```
3. **删除旧数据卷:**
PostgreSQL 15 无法读取版本 13 创建的数据。你必须移除现有卷(警告:这将删除旧数据目录)。
```bash
docker volume rm <project_name>_db-data
```
4. **更新镜像并启动(版本 15):**
更新你的 `overrides/compose.postgres.yaml`(或拉取最新更改)并启动容器。
```bash
docker compose up -d
```
5. **恢复数据:**
将转储恢复到新的 PostgreSQL 15 实例。
```bash
cat full_dump.sql | docker exec -i <project_name>-db-1 psql -U postgres
```
6. **验证并清理:**
确保你的站点使用 `bench migrate` 正常工作,然后删除 `full_dump.sql` 文件。
```bash
docker exec -it <project_name>-backend-1 bench --site all migrate
```
+3
View File
@@ -0,0 +1,3 @@
---
title: 迁移指南
---
@@ -0,0 +1,84 @@
---
title: 故障排除
---
- [修复重建容器后的 MariaDB 问题](#fixing-mariadb-issues-after-rebuilding-the-container)
- [docker-compose 无法识别 .env 文件中的变量](#docker-compose-does-not-recognize-variables-from-env-file)
- [基于 Windows 的安装](#windows-based-installation)
- [重新安装](#redo-installation)
### 修复重建容器后的 MariaDB 问题
由于任何原因,在重建容器后,如果你无法使用之前的配置正确访问 MariaDB(即 `Access denied for user [...]`),请按照以下说明操作。
首先测试网络问题。通过 `backend` 容器手动连接到数据库:
```
docker exec -it frappe_docker-backend-1 bash
mysql -uroot -padmin -hdb
```
将 `root` 替换为数据库 root 用户名,`admin` 替换为 root 密码,`db` 替换为 docker-compose `.yml` 配置文件中指定的服务名称。如果数据库连接成功,则网络配置正确,你可以继续下一步。否则,修改 docker-compose `.yml` 配置文件,在 `configurator` 服务的 `environment` 部分,使用容器名称(`frappe_docker-db-1`、`frappe_docker-redis-cache-1`、`frappe_docker-redis-queue-1` 或使用 `docker ps` 显示的名称)而不是服务名称,并重建容器。
然后,需要在 MariaDB 中设置参数 `'db_name'@'%'` 并将站点数据库的权限适当地分配给用户。
此步骤必须对当前 bench 下所有可用站点重复执行。
示例显示为站点 `localhost` 执行的查询
打开 sites/localhost/site_config.json:
```shell
code sites/localhost/site_config.json
```
并记下参数 `db_name` 和 `db_password`。
进入 MariaDB 交互式 shell:
```shell
mysql -uroot -padmin -hdb
```
参数 `'db_name'@'%'` 不能重复。使用以下命令验证它是唯一的:
```
SELECT User, Host FROM mysql.user;
```
如果发现重复条目,使用以下命令删除:
```
DROP USER 'db_name'@'host';
```
通过执行以下查询修改权限,将 `db_name` 和 `db_password` 替换为在 site_config.json 中找到的值。
```sql
-- 如果尚未创建用户,首先尝试使用下一个命令创建它
-- CREATE USER 'db_name'@'%' IDENTIFIED BY 'your_password';
-- 如果你使用上面的 create 命令,跳过下面的 upgrade 命令
UPDATE mysql.global_priv SET Host = '%' where User = 'db_name'; FLUSH PRIVILEGES;
SET PASSWORD FOR 'db_name'@'%' = PASSWORD('db_password'); FLUSH PRIVILEGES;
GRANT ALL PRIVILEGES ON db_name.* TO 'db_name'@'%' IDENTIFIED BY 'db_password' WITH GRANT OPTION; FLUSH PRIVILEGES;
EXIT;
```
注意:对于 MariaDB 10.3 及更早版本,使用 `mysql.user` 而不是 `mysql.global_priv`。
### docker-compose 无法识别 .env 文件中的变量
如果你使用的是旧版本的 `docker-compose`,.env 文件需要位于执行 docker-compose 命令的目录中。官方 `docker-compose` 和发行版打包的版本之间也可能存在差异。如果可用,使用 `--env-file=.env` 显式指定文件路径。
### 基于 Windows 的安装
- 设置环境变量 `COMPOSE_CONVERT_WINDOWS_PATHS`,例如 `set COMPOSE_CONVERT_WINDOWS_PATHS=1`
- 使用 docker machine 时,将 VM 的端口转发到主机器的端口。(端口 8080/8000/9000)
- 所有站点名称以 `.localhost` 结尾。并在本地通过浏览器访问。例如 `http://site1.localhost`
### 重新安装
- 如果你进行了更改并只想重新开始(放弃所有更改),移除所有 docker
- 容器
- 镜像
- 卷
- 重新安装
@@ -0,0 +1,16 @@
---
title: Windows 中的 NGINX
---
# 解决 Windows 上 Docker `nginx-entrypoint.sh` 脚本未找到错误
如果你在 Windows 上的 Docker 容器中遇到错误 `exec /usr/local/bin/nginx-entrypoint.sh: no such file or directory`,请按照以下步骤解决问题。
## 1. 检查行尾
在 Windows 上,文件通常有 `CRLF` 行尾,而 Linux 系统期望 `LF`。这可能在 Linux 容器中执行 shell 脚本时导致问题。
- **使用 `dos2unix` 转换行尾:**
```bash
dos2unix resources/core/nginx/nginx-entrypoint.sh
```
@@ -0,0 +1,10 @@
---
title: ARM64 / Apple Silicon
---
## ARM64 和 Apple Silicon 注意事项
- 在 Apple Silicon 上使用仅 x86 镜像进行初始构建时,启用 Docker Desktop 的 Rosetta 模拟。
- 优先使用发布的多架构镜像(`frappe/bench`、`frappe/erpnext`)或使用 `docker buildx bake --set *.platform=linux/amd64,linux/arm64` 在本地构建,以在一次传递中覆盖两种架构。
- 使用 `pwd.yml` 时,导出 `DOCKER_DEFAULT_PLATFORM=linux/arm64`(或选择提供的 compose profile)以避免意外模拟。
- 将绑定挂载保持在用户主目录下,并应用 `:cached` 或 `:delegated` 一致性标志以在 macOS 上获得更好的性能。
+3
View File
@@ -0,0 +1,3 @@
---
title: 故障排除
---
@@ -0,0 +1,20 @@
---
title: 构建版本 10
---
克隆本仓库的 version-10 分支
```shell
git clone https://github.com/frappe/frappe_docker.git -b version-10 && cd frappe_docker
```
构建镜像
```shell
export DOCKER_REGISTRY_PREFIX=frappe
docker build -t /frappe-socketio:v10 -f build/frappe-socketio/Dockerfile .
docker build -t /frappe-nginx:v10 -f build/frappe-nginx/Dockerfile .
docker build -t /erpnext-nginx:v10 -f build/erpnext-nginx/Dockerfile .
docker build -t /frappe-worker:v10 -f build/frappe-worker/Dockerfile .
docker build -t /erpnext-worker:v10 -f build/erpnext-worker/Dockerfile .
```
@@ -0,0 +1,45 @@
---
title: 配置 VitePress
---
# 配置 VitePress
要修改任何 VitePress 相关设置,需要 JavaScript 开发环境。所有与 VitePress 相关的内容都包含在 `docs/` 文件夹中。
## 前置条件
1. 推荐 Node.js v24 或更高版本。要安装和管理 Node.js,[nvm](https://github.com/nvm-sh/nvm) 是 Linux 和 MacOS 的首选方式。对于 Windows,使用官方安装程序或 [fnm](https://github.com/Schniz/fnm)。
2. pnpm 包管理器,v10.28 或更高版本。安装 pnpm 最简单的方法是使用 [corepack](https://pnpm.io/installation#using-corepack),它是 Node.js 的一部分。
## 开发
要启动开发环境,
1. 在终端中导航到 `/docs` 目录
```sh
cd docs
```
2. 安装依赖
```sh
pnpm install
```
3. 启动开发服务器
```sh
pnpm run docs:dev
```
4. 在浏览器中打开 `http://localhost:5173/frappe_docker` 以查看开发版本,它会在你进行更改时更新预览。
## 配置
1. VitePress 站点的公共资源添加到 `docs/public` 文件夹中。此文件夹**不应**用于添加 `.md` 文件中添加的图片。
2. VitePress 使用 `index.md` 文件执行一些特殊操作。例如,主页使用 `docs/index.md` 文件配置。查看文件以获取更多详细信息。
3. VitePress 使用"基于文件的路由",这意味着 URL 路径模拟 `docs/` 目录内的目录和文件结构。
4. VitePress 特定配置在 `docs/.vitepress/config.mts`。
5. 要自动填充侧边栏,使用名为 'VitePress Sidebar' 的插件。`config.mts` 还包括此插件的配置。更多详细信息可以在[文档页面](https://vitepress-sidebar.cdget.com/guide/getting-started)找到。
6. 每个子文件夹都有一个 `index.md` 文件。这用于指定侧边栏中页面的组标题。
@@ -0,0 +1,161 @@
---
title: Fork 管理
---
# Fork 管理最佳实践
## 初始 Fork 设置
```bash
# 1. 在 GitHub 上 Fork(使用 Fork 按钮)
# 2. 克隆你的 fork
git clone https://github.com/YOUR_USERNAME/frappe_docker
cd frappe_docker
# 3. 添加 upstream 远程(原始仓库)
git remote add upstream https://github.com/frappe/frappe_docker.git
# 4. 验证远程
git remote -v
# origin https://github.com/YOUR_USERNAME/frappe_docker (你的 fork)
# upstream https://github.com/frappe/frappe_docker (原始)
# 5. 创建开发分支
git checkout -b my-custom-setup
```
## 安全的自定义区域
**✅ 安全(不会与 upstream 冲突):**
```
development/ # 你的整个开发环境
├── frappe-bench/ # 本地安装
└── .vscode/ # 你的编辑器设置
compose.my-*.yaml # 你的自定义 compose 覆盖
scripts/my-*.sh # 你的自定义脚本
docs/my-*.md # 你的自定义文档
.env.local # 本地环境覆盖
.gitignore.local # 额外的 gitignore 规则
```
**⚠️ 需要修改(可能冲突):**
```
compose.yaml # 核心 - 使用覆盖文件代替
docker-bake.hcl # 构建配置 - 使用自定义文件
images/*/Dockerfile # 核心镜像 - 扩展而非修改
```
**❌ 永远不要修改(会破坏 upstream 同步):**
```
.github/workflows/ # CI/CD 流水线
images/*/ # 核心镜像定义
resources/ # 核心模板
```
## 保持 Fork 更新
```bash
# 定期与 upstream 同步(建议每周)
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
# 更新你的开发分支
git checkout my-custom-setup
git rebase main # 或:git merge main
# 如果在 rebase 期间发生冲突:
# 1. 修复文件中的冲突
# 2. git add <fixed-files>
# 3. git rebase --continue
# 或:git rebase --abort (取消)
```
## 自定义环境模式
为你的自定义创建覆盖文件:
```yaml
# compose.my-env.yaml
version: "3.7"
services:
backend:
environment:
# 你的自定义环境变量
- DEVELOPER_MODE=true
- MY_API_KEY=
volumes:
# 你的自定义绑定挂载
- ./development/my-scripts:/home/frappe/my-scripts
- ./development/my-config:/home/frappe/config
# 你的额外服务
my-monitoring:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
# 使用它:
# docker compose -f compose.yaml -f compose.my-env.yaml up
```
## .gitignore 策略
添加到 `.gitignore`(或创建 `.gitignore.local`):
```gitignore
# 本地环境文件
.env.local
*.local.yaml
compose.my-*.yaml
# 开发产物
development/frappe-bench/sites/*
development/frappe-bench/apps/*
!development/frappe-bench/apps.json
development/frappe-bench/logs/
development/frappe-bench/env/
# 本地自定义
my-local-configs/
scripts/my-*.sh
docs/internal-*.md
# IDE
.vscode/settings.json.local
.idea/
# 临时文件
*.swp
*.swo
*~
.DS_Store
```
## 向上游贡献
```bash
# 1. 从 main 创建功能分支
git checkout main
git pull upstream main
git checkout -b feature/my-improvement
# 2. 进行更改并提交
git add .
git commit -m "feat: add awesome feature"
# 3. 推送到你的 fork
git push origin feature/my-improvement
# 4. 在 GitHub 上创建 Pull Request
# 前往:https://github.com/frappe/frappe_docker
# 点击 "Compare & pull request"
```
@@ -0,0 +1,163 @@
---
title: 框架对比
---
# 框架对比
> **注意:** 本节为熟悉其他框架的开发者提供与其他框架的对比。如果你对所有框架都是新手,可以跳过本节 - 本指南的其余部分是自包含的。
## Frappe 与 Django 概念对比
### 项目结构对比
**Django 项目:**
```python
myproject/
├── myproject/ # 项目设置
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── blog/ # Django 应用
│ ├── models.py
│ ├── views.py
│ └── urls.py
├── shop/ # Django 应用
└── users/ # Django 应用
```
**Frappe Bench:**
```
bench/
├── apps/
│ ├── frappe/ # 核心框架(相当于 Django 本身)
│ ├── erpnext/ # 完整的业务应用(相当于 Django + DRF + Celery + admin)
│ ├── hrms/ # 人力资源管理应用
│ └── my_custom_app/ # 你的自定义应用
└── sites/
└── mysite.com/ # 站点实例(相当于 Django 项目 + 数据库)
├── site_config.json
└── private/files/
```
### 概念映射
| Django | Frappe | 说明 |
| ------------------ | ----------------- | ---------------------------------------------- |
| Model | DocType | 但自动包含 UI、权限、API |
| View | Controller method | 需要的代码少得多 |
| Admin | Desk | 更强大,自动生成 |
| DRF Serializer | 内置 | 从 DocType 自动生成 |
| Celery task | Background job | 内置,无需单独设置 |
| signals | hooks.py | 更结构化 |
| Management command | bench command | 更易发现 |
### 关键架构差异
1. **多租户**
- Django:一个应用 = 一个数据库(通常)
- Frappe:一个安装 = 多个站点,每个站点有自己的数据库
2. **后台任务**
- Django:需要 Celery + Redis + worker 设置
- Frappe:内置队列系统,只需使用 `enqueue()`
3. **实时**
- Django:需要 Channels + Redis + ASGI 设置
- Frappe:内置 Socket.IO,DocType 更新自动处理
4. **管理/管理界面**
- Django:Admin 用于模型,基本 CRUD
- Frappe:功能齐全的 Desk,带有报告、仪表板、权限
5. **API**
- Django:手动 DRF 设置、序列化器、视图
- Frappe:从 DocType 定义自动生成 REST + RPC
### 代码对比示例
**创建"客户"模型:**
Django(需要约 50+ 行):
```python
# models.py
class Customer(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
# serializers.py
class CustomerSerializer(serializers.ModelSerializer):
# ...
# views.py
class CustomerViewSet(viewsets.ModelViewSet):
# ...
# urls.py
router.register(r'customers', CustomerViewSet)
# admin.py
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
# ...
```
Frappe(DocType JSON + 约 10 行 Python):
```json
// customer.json(通过 UI 或代码自动生成)
{
"name": "Customer",
"fields": [
{ "fieldname": "customer_name", "fieldtype": "Data" },
{ "fieldname": "email", "fieldtype": "Data", "unique": 1 }
]
}
```
```python
# customer.py(仅用于自定义业务逻辑)
import frappe
from frappe.model.document import Document
class Customer(Document):
def validate(self):
# 仅自定义验证逻辑
pass
```
✅ **自动包含:**
- REST API(`/api/resource/Customer`)
- 列表视图、表单视图
- 搜索、过滤器、排序
- 权限(创建、读取、更新、删除)
- 审计跟踪(created_by、modified_by、versions)
- 打印格式、邮件模板
### 何时选择 Frappe 与 Django
**选择 Frappe 当:**
- 构建业务应用(ERP、CRM、项目管理)
- 需要开箱即用的多租户
- 想要使用自动生成的 UI 进行快速开发
- 需要基于角色的权限和工作流
- 为需要自定义的非技术用户构建
**选择 Django 当:**
- 构建消费者 Web 应用(社交媒体、电子商务前端)
- 需要完全控制每个方面
- 有高度自定义的 UI 需求
- 团队已经是 Django 专家
- 构建仅 API 的服务
**混合方法:**
许多团队同时使用两者:Frappe 用于后台/管理工具,Django 用于面向客户的 Web 应用。
@@ -0,0 +1,18 @@
---
title: 外部链接
---
# 外部链接
## 官方文档
- [Frappe 框架文档](https://frappeframework.com/docs) - 核心框架文档
- [Frappe Docker 文档](https://github.com/frappe/frappe_docker/tree/main/docs) - 本仓库的文档
- [ERPNext 文档](https://docs.erpnext.com) - ERPNext 用户和开发者文档
- [Docker 文档](https://docs.docker.com) - Docker 基础知识
## 社区资源
- [Frappe 论坛](https://discuss.frappe.io) - 社区问答
- [Frappe 学校](https://frappe.school) - 视频教程
- [Frappe GitHub](https://github.com/frappe/frappe) - 框架源代码
@@ -0,0 +1,312 @@
---
title: GitHub Actions 镜像工作流
---
本文档描述了共享核心镜像和可重用下游应用镜像的当前工作流设置。
# 工作流角色
当前工作流布局是:
- `.github/workflows/core-build-develop.yml`
- `.github/workflows/core-build-stable.yml`
- `.github/workflows/core-build-test-images.yml`
- `.github/workflows/core-publish-images.yml`
- `.github/workflows/app-build-image.yml`
`core-build-develop.yml` 和 `core-build-stable.yml` 是编排工作流。
它们决定核心镜像流水线何时运行。
`core-build-test-images.yml` 是可重用工作流,它:
- 解析请求的发布行的镜像版本
- 将共享核心镜像构建到本地注册表
- 对这些镜像运行测试套件
`core-publish-images.yml` 是可重用工作流,它:
- 将测试过的镜像发布到 Docker Hub
- 将 `base` 和 `build` 发布到 GHCR
`app-build-image.yml` 是可重用工作流,下游仓库调用它来:
- 从调用者的应用仓库和 ref 创建 `apps.json` 文件
- 构建 `images/layered/Containerfile`
- 消费现有的 `base` 和 `build` 镜像
- 将请求的应用安装到最终镜像中
- 可选地将最终应用镜像推送到调用者的注册表
# 当前流程
当前结构是:
```text
核心编排
-> 核心构建和测试
-> 核心发布
下游应用工作流
-> 消费已发布的 base 和 build
-> 安装应用
-> 发布最终应用镜像
```
当前 Mermaid 概览:
```mermaid
flowchart TD
subgraph Core["核心镜像流程"]
A[core-build-develop.yml 或 core-build-stable.yml]
B[core-build-test-images.yml]
C[解析版本]
D[构建本地测试镜像]
E[运行 pytest]
F[core-publish-images.yml]
G[推送到 Docker Hub: erpnext, base, build]
H[推送到 GHCR: base, build]
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
F --> H
end
subgraph App["下游应用流程"]
I[下游仓库工作流]
J[app-build-image.yml]
K[创建 apps.json]
L[构建 images/layered/Containerfile]
M[安装应用]
N[推送最终应用镜像]
I --> J
J --> K
K --> L
L --> M
M --> N
end
G --> J
H --> J
```
更具体地说:
```text
core-build-test-images.yml
-> 解析 frappe 和 erpnext 标签
-> 将镜像构建到本地 CI 注册表
-> 运行测试
core-publish-images.yml
-> 推送到 Docker Hub: erpnext, base, build
-> 推送到 GHCR: base, build
app-build-image.yml
-> 拉取:
- <prefix>/base:<frappe_ref>
- <prefix>/build:<frappe_ref>
-> 从 app_repo + app_ref 安装应用
-> 推送最终 image_name:image_tag
```
# 命名约定
GitHub Actions 要求工作流文件直接位于 `.github/workflows` 内。
不支持工作流文件的子目录,因此结构应来自文件名和 `name:` 值。
推荐的文件命名约定:
```text
<area>-<action>-<subject>.yml
```
当前示例:
- `core-build-bench.yml`
- `core-build-develop.yml`
- `core-build-stable.yml`
- `core-build-test-images.yml`
- `core-publish-images.yml`
- `app-build-image.yml`
- `docs-publish-site.yml`
推荐的可见工作流名称:
- `Core / Build Bench`
- `Core / Build Develop`
- `Core / Build Stable`
- `Core / Build and Test Images`
- `Core / Publish Images`
- `App / Build Image`
- `Docs / Publish Site`
# 样式规则
为了保持工作流可预测,每个类别使用一个约定而不是混合样式。
工作流文件名应使用 kebab-case:
```text
core-build-test-images.yml
app-build-image.yml
```
工作流显示名称应使用短标题样式组:
```text
Core / Build and Test Images
App / Build Image
```
工作流输入应使用 snake_case:
```yaml
app_name:
frappe_ref:
image_name:
```
环境变量应使用大写 snake case:
```text
FRAPPE_IMAGE_PREFIX
PYTHON_VERSION
NODE_VERSION
```
推荐的规则集是:
- 工作流文件名:kebab-case
- 工作流 `name:` 值:分组标题大小写
- 工作流输入:snake_case
- 作业 id 和步骤 id:实际可行的 snake_case
- 环境变量:UPPER_SNAKE_CASE
这意味着 `-` 更适合文件名,而 `_` 仍然适合 YAML 键、输入和环境变量。
# `app-build-image.yml` 中的重要输入
可重用的应用工作流主要由以下输入控制:
- `app_name`
应用目录名称,例如 `crm`
- `app_repo`
要安装的 Git 仓库,例如 `frappe/crm`
- `app_ref`
要安装的分支或标签,例如 `develop`
- `frappe_ref`
现有 `base` 和 `build` 镜像的标签,例如 `version-16`
- `frappe_image_prefix`
共享 `base` 和 `build` 镜像的来源,例如 `frappe` 或 `ghcr.io/frappe`
- `image_name`
最终目标镜像名称,例如 `ghcr.io/acme/crm`
- `image_tag`
最终目标镜像标签,例如 `develop`
- `registry`
最终推送的注册表,例如 `ghcr.io` 或 `docker.io`
关键区别是:
```text
frappe_image_prefix = 共享 base/build 镜像的来源
image_name = 最终应用镜像的目标
```
# 示例:调用者仓库发布到 GHCR
此示例假设:
- 共享基础镜像存在于 `ghcr.io/frappe/base` 和 `ghcr.io/frappe/build`
- 调用者仓库希望将其自己的应用镜像发布到 `ghcr.io/acme/crm`
```yaml
name: App / Build CRM Image
on:
workflow_dispatch:
push:
branches:
- develop
permissions:
contents: read
packages: write
jobs:
build-image:
uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main
with:
app_name: crm
app_repo: acme/crm
app_ref: develop
frappe_ref: version-16
frappe_image_prefix: ghcr.io/frappe
image_name: ghcr.io/acme/crm
image_tag: develop
registry: ghcr.io
push: true
platforms: linux/amd64
```
发生的事情:
```text
1. 调用 app-build-image.yml
2. 从 acme/crm + develop 生成 apps.json
3. 工作流构建 images/layered/Containerfile
4. layered 使用:
- ghcr.io/frappe/build:version-16
- ghcr.io/frappe/base:version-16
5. 安装 CRM
6. 最终镜像推送到 ghcr.io/acme/crm:develop
```
对于 GHCR,调用者工作流应授予:
- `permissions: packages: write`
然后可重用工作流使用工作流令牌登录。
# 示例:调用者仓库发布到 Docker Hub
此示例假设:
- 共享基础镜像来自 Docker Hub 上的 `frappe`
- 调用者仓库希望将其应用镜像发布到 Docker Hub 作为 `acme/crm`
```yaml
name: App / Build CRM Image
on:
workflow_dispatch:
push:
branches:
- develop
jobs:
build-image:
uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main
with:
app_name: crm
app_repo: acme/crm
app_ref: develop
frappe_ref: version-16
frappe_image_prefix: frappe
image_name: acme/crm
image_tag: develop
registry: docker.io
push: true
platforms: linux/amd64
secrets:
REGISTRY_USERNAME:
REGISTRY_PASSWORD:
```
在这种情况下:
- 共享镜像从 `frappe/base:version-16` 和 `frappe/build:version-16` 拉取
- 最终镜像推送到 Docker Hub 作为 `acme/crm:develop`
@@ -0,0 +1,62 @@
---
title: 资源处理方式
---
# 资源参考
## 问题
`sites` 目录包含持久数据(站点配置、上传的文件等)和构建时产物(`sites/assets`)。将整个 `sites` 目录挂载为 Docker 卷会导致资源与配置一起持久化,从而导致:
- 镜像更新后资源仍然存在
- 重建后资源/清单不匹配
- 资源与卷生命周期绑定而非镜像生命周期
## 解决方案
在构建过程中将资源移出 `sites` 卷,然后替换为**符号链接**。这意味着资源始终从镜像层提供,而 `sites` 的其余部分保持持久化。
### 工作原理
在镜像构建期间(`Containerfile`),执行以下操作:
```dockerfile
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
```
这在 `VOLUME` 声明**之前**运行,因此 **`sites` 卷根本不包含任何资源**。
此外,向镜像添加了一个 `ENTRYPOINT`,它从 `assets` 到 `site\assets` 添加一个**符号链接**。
> 这是在入口点中实现的,而不是将符号链接直接烘焙到镜像中,这样它也可以与预先存在或已初始化的 `sites` 卷一起工作。
> 由于在 `/home/frappe/frappe-bench/sites` 上挂载卷会隐藏该路径处的镜像内容,在镜像构建期间创建的任何符号链接在挂载的卷内都不可见。入口点在容器启动时重新创建符号链接,确保它始终存在并自动修复可能尚未包含它的旧卷。
在运行时:
```
/home/frappe/frappe-bench/
├── assets/ ← 镜像层(临时的,始终与镜像匹配)
├── sites/
│ ├── assets -> /home/frappe/frappe-bench/assets ← 符号链接
│ ├── common_site_config.json ← 持久化在卷中
│ └── <site>/ ← 持久化在卷中
└── logs/ ← 持久化在卷中
```
### 卷行为
| 路径 | 持久化 | 来源 |
| -------------------------- | ---------------------- | --------------------- |
| `sites/`(除 assets 外) | ✅ 是 | 命名卷(`sites`) |
| `sites/assets`(符号链接)| ✅ 是(符号链接本身) | 命名卷(`sites`) |
| `assets/`(符号链接目标) | ❌ 否 | 镜像层 |
| `logs/` | ✅ 是 | 未命名卷 |
`sites/assets` 符号链接存储在持久的 `sites` 卷中,但其目标(`/home/frappe/frappe-bench/assets`)来自容器镜像。当容器重新创建或升级时,资源目录从新镜像重新创建,确保资源始终与运行版本保持同步。
## 重要提示:运行时的 `bench build`
在运行中的容器内运行 `bench build` 将写入新资源,并最终导致 `assets.json` 与实际资源之间的不匹配,从而破坏 UI。这可以通过重新创建容器来恢复
> 注意:重启容器是不够的 - 需要重新创建它们以丢弃可写层。
+3
View File
@@ -0,0 +1,3 @@
---
title: 参考资料
---
+64
View File
@@ -0,0 +1,64 @@
---
title: 自定义应用
---
# Frappe 自定义应用
## 什么是 Frappe 自定义应用?
自定义应用是自包含的模块化业务应用,用于扩展 Frappe 的功能。它们遵循约定优于配置的方法,其中框架自动提供大部分样板代码。
## 自定义应用结构
```
my_custom_app/
├── hooks.py # 应用配置和钩入 Frappe 生命周期
├── modules.txt # 此应用中的业务模块列表
├── my_custom_app/
│ ├── __init__.py
│ ├── config/
│ │ └── desktop.py # 桌面工作区图标和快捷方式
│ ├── my_module/ # 业务领域模块(例如,销售、库存)
│ │ ├── doctype/ # 文档类型(数据模型)
│ │ │ ├── customer/
│ │ │ │ ├── customer.py # Python 控制器(业务逻辑)
│ │ │ │ ├── customer.json # 模型定义(模式、验证)
│ │ │ │ └── customer.js # 前端逻辑(UI 交互)
│ │ └── page/ # 自定义页面(仪表板、报告)
│ ├── public/ # 静态资源(CSS、JS、图片)
│ ├── templates/ # 网页的 Jinja2 模板
│ └── www/ # 通过路由可访问的网页
└── requirements.txt # Python 包依赖
```
## 内置功能(自动生成)
每个 Frappe 应用自动包含:
- **REST API** - 从 DocType 定义自动 CRUD 端点
- **权限系统** - 行级和字段级访问控制
- **审计跟踪** - 自动版本跟踪和更改历史
- **自定义字段** - 运行时字段添加,无需代码更改
- **工作流** - 可配置的审批和状态管理
- **报告** - 查询构建器和报告设计器
- **打印格式** - 使用自定义模板生成 PDF
- **邮件集成** - 基于模板的邮件发送
- **文件附件** - 文档附件管理
## 创建自定义应用
```bash
# 进入开发容器
docker exec -it <container_name> bash
# 创建新应用(交互式提示将询问详细信息)
bench new-app my_custom_app
# 将应用安装到站点
bench --site mysite.com install-app my_custom_app
# 创建新的 DocType(数据模型)
bench --site mysite.com console
>>> bench.new_doc("DocType", {...})
# 或使用 Web UI:设置 → 自定义 → DocType → 新建
```
@@ -0,0 +1,62 @@
---
title: Docker 绑定挂载
---
# Docker 绑定挂载
## 什么是绑定挂载?
绑定挂载在主机上的目录和容器内的目录之间创建直接连接。任一位置的更改都会立即反映在另一个位置 - 非常适合开发,当你想在主机上编辑代码并在容器中查看更改时。
## 绑定挂载 vs 命名卷 vs 匿名卷
| 类型 | 语法 | 使用场景 | 持久化 |
| -------------------- | ---------------------------- | ------------------------ | -------------------------- |
| **绑定挂载** | `./local/path:/container/path` | 开发、配置文件 | 在主机文件系统上 |
| **命名卷** | `volume_name:/container/path` | 生产数据、数据库 | Docker 管理 |
| **匿名卷** | `/container/path` | 临时/缓存数据 | Docker 管理,自动删除 |
## 绑定挂载示例
```yaml
services:
backend:
volumes:
# 开发:在主机上编辑代码,在容器中运行
- ./my_custom_app:/home/frappe/frappe-bench/apps/my_custom_app
# 配置:用主机文件覆盖容器配置
- ./custom-config.json:/home/frappe/frappe-bench/sites/common_site_config.json:ro # :ro = 只读
# 日志:在主机上访问容器日志以进行调试
- ./logs:/home/frappe/frappe-bench/logs
# 数据库(不推荐用于生产)
- ./data/mysql:/var/lib/mysql
# 生产数据库的命名卷
db:
volumes:
- db_data:/var/lib/mysql # 由 Docker 管理,容器删除后仍然存在
volumes:
db_data: # 定义命名卷
```
## 性能优化(macOS/Windows)
macOS/Windows 上的 Docker 使用 VM,使绑定挂载变慢。使用这些标志:
```yaml
volumes:
# :cached - 主机写入被缓冲(适用于一般开发)
- ./development:/home/frappe/frappe-bench:cached
# :delegated - 容器写入被缓冲(当容器大量写入时最佳)
- ./development:/home/frappe/frappe-bench:delegated
# :consistent - 完全同步(最慢但最安全)
- ./development:/home/frappe/frappe-bench:consistent
```
**建议:** 在 macOS/Windows 上的大多数开发工作中使用 `:cached`。
+3
View File
@@ -0,0 +1,3 @@
---
title: 核心概念
---
+9
View File
@@ -0,0 +1,9 @@
---
title: Docker部署
index: false
icon: laptop-code
category:
- 使用指南
---
<Catalog />
+76
View File
@@ -0,0 +1,76 @@
# 参考: https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/04-env-variables.md
ERPNEXT_VERSION=v16.32.1
DB_PASSWORD=123
#仅当你使用 docker secrets 管理数据库密码时
DB_PASSWORD_SECRETS_FILE=
# 仅当你使用外部数据库时
DB_HOST=
DB_PORT=
# 仅当你使用外部 Redis 时
REDIS_CACHE=
REDIS_QUEUE=
# 每个 Gunicorn worker 进程处理并发请求的线程数。
GUNICORN_THREADS=4
# 处理请求的 worker 进程数。
# 典型公式为 (2 x CPU 核心数) + 1。
GUNICORN_WORKERS=2
# 超过此超时时间(秒)的 worker 将被终止并重启。
GUNICORN_TIMEOUT=120
# 仅在 HTTPS 覆盖时使用
LETSENCRYPT_EMAIL=mail@example.com
# 以下环境变量不是必需的。
# 默认值为 `host`,通过主机名解析站点。例如,如果你的主机是 `example.com`,
# 站点名称应为 `example.com`,或者如果主机是 `127.0.0.1`(本地调试),则应为 `127.0.0.1`。
# 此变量允许覆盖上述行为。假设你创建了一个名为 `mysite` 的站点
# 并且确实想通过 `127.0.0.1` 主机访问它。那么你需要将此变量设置为 `mysite`。
FRAPPE_SITE_NAME_HEADER=
# 默认值为 `8080`。
HTTP_PUBLISH_PORT=
# 默认值为 `443`。
HTTPS_PUBLISH_PORT=
# 默认值为 `127.0.0.1`。将 IP 地址设置为我们可信的上游地址。
UPSTREAM_REAL_IP_ADDRESS=
# 默认值为 `X-Forwarded-For`。设置请求头字段,其值将用于替换客户端地址。
UPSTREAM_REAL_IP_HEADER=
# 允许值为 on|off。默认值为 `off`。如果禁用递归搜索,
# 匹配可信地址之一的原始客户端地址
# 将被 real_ip_header 指令定义的请求头字段中发送的最后一个地址替换。
# 如果启用递归搜索,匹配可信地址之一的原始客户端地址将被请求头字段中发送的最后一个非可信地址替换。
UPSTREAM_REAL_IP_RECURSIVE=
# nginx proxy_read_timeout 允许的所有值均可用,默认值为 120s
# 如果你有长时间运行的打印格式或加载缓慢的站点,此设置很有用
PROXY_READ_TIMEOUT=
# nginx client_max_body_size 允许的所有值均可用,默认值为 50m
# 当 frappe 应用中的上传限制增加时,此设置是必需的
CLIENT_MAX_BODY_SIZE=
# 仅在 traefik 覆盖时使用
# 单站点: SITES_RULE=Host(`erp.example.com`)
# 多站点: SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`)
# 更多 https://doc.traefik.io/traefik/routing/routers/#rule
# 关于 acme https://doc.traefik.io/traefik/https/acme/#domain-definition
SITES_RULE=Host(rp.example.com)
# 仅在 nginxproxy 覆盖时使用
# 单站点: NGINX_PROXY_HOSTS=erp.example.com
# 多站点: NGINX_PROXY_HOSTS=erp.example.com,www.erp.example.com
NGINX_PROXY_HOSTS=erp.example.com
+978
View File
@@ -0,0 +1,978 @@
---
title: 快速上手
---
# Frappe Docker 快速上手
_面向开发者的 Frappe Docker 全面指南,包含与 Django 的对比,方便熟悉该框架的团队参考_
## 目录
- [如何使用本指南](#how-to-use-this-guide)
- [理解 Frappe Docker 架构](#understanding-frappe-docker-architecture)
- [仓库结构](#repository-structure)
- [自定义应用说明](#custom-apps-explained)
- [开发工作流](#development-workflow)
- [平台说明](#platform-notes)
- [文件位置和访问](#file-locations-and-access)
- [Docker 概念:绑定挂载](#docker-concepts-bind-mounts)
- [Fork 管理最佳实践](#fork-management-best-practices)
- [快速启动示例](#quick-start-examples)
- [框架对比](#framework-comparisons)
- [Frappe 与 Django](#frappe-vs-django-concepts)
- [资源和参考](#resources-and-references)
---
## 如何使用本指南
如果是从零开始,可以按顺序阅读各章节;也可以直接通过目录跳转到感兴趣的章节。
## 理解 Frappe Docker 架构
Frappe Docker 为开发和部署 Frappe/ERPNext 应用提供了全面的容器化环境。它采用**多服务架构**,涵盖从 Web 服务到后台任务处理的所有环节。
### 核心服务
基础 Compose 文件包含以下核心服务:
- **configurator** - 初始化服务,配置数据库和 Redis 连接;启动时运行后退出
- **backend** - Werkzeug 开发服务器,处理动态内容
- **frontend** - Nginx 反向代理,提供静态资源并路由请求
- **websocket** - 运行 Socket.IO 的 Node.js 服务器,提供实时通信
- **queue-short/long** - 使用 RQ(Redis Queue)的 Python Worker,处理异步后台任务
- **scheduler** - 使用 schedule 库运行定时任务的 Python 服务
通过 Compose 覆盖文件可添加额外服务:
- **db** - MariaDB 或 PostgreSQL 数据库服务(通过 `compose.mariadb.yaml` 或 `compose.postgres.yaml`)
- **redis-cache/queue** - 用于缓存和任务队列的 Redis 实例(通过 `compose.redis.yaml`)
### 服务协作方式
```
用户请求
↓
[frontend (Nginx)] → 直接提供静态文件
↓
[backend (Werkzeug)] → 处理动态内容
↓ ↓
[db (MariaDB)] [redis-cache]
后台任务:
[scheduler] → [redis-queue] → [queue-short/long workers]
实时通信:
[websocket (Socket.IO)] ←→ [redis-cache]
```
## 仓库结构
### 📁 核心配置文件
> ⚠️ 部署前,请阅读
> **[选择部署方式](01-getting-started/01-choosing-a-deployment-method.md)**
> 以了解 `pwd.yml`、开发配置、Easy Install 脚本和生产配置之间的差异。
- **compose.yaml** - 定义所有服务的主 Docker Compose 文件
- **example.env** - 环境变量模板(复制为 `.env`)
- **pwd.yml** - "Play with Docker" - 用于快速测试的简化单文件配置
- **docker-bake.hcl** - 用于多架构构建的高级 Docker Buildx 配置
- **docs/container-setup/env-variables.md** - 环境配置逻辑和默认值的集中参考
### 📁 images/ - Docker 镜像定义
提供四个预定义的 Dockerfile,各有不同用途:
- **images/bench/** - 仅安装 Bench CLI,用于开发或调试;不包含运行时服务
- **images/custom/** - 基于纯 Python 基础镜像的多用途 Python 后端;从 `apps.json` 安装应用;适用于**生产**和测试;当你需要控制 Python/Node 版本时最为理想
- **images/layered/** - 最终内容与 `custom` 相同,但基于 Docker Hub 的预构建镜像;当你使用 Frappe 管理的依赖版本时,构建速度更快
- **images/production/** - 仅安装 Frappe 和 ERPNext(不可通过 `apps.json` 自定义);最适合**快速启动或探索**;实际部署请使用 `custom` 或 `layered`
> **注意:** 有关详细构建参数和高级配置选项,参见 [docs/02-setup/01-overview.md](02-setup/01-overview.md)。
### 📁 overrides/ - Compose 文件扩展
Docker Compose "覆盖文件",用于扩展基础 compose.yaml 以适应不同场景:
- **compose.mariadb.yaml** - 添加 MariaDB 数据库服务
- **compose.redis.yaml** - 添加 Redis 缓存服务
- **compose.proxy.yaml** - 添加 Traefik 反向代理用于多站点托管(基于标签的路由)
- **compose.https.yaml** - 添加 Traefik HTTPS + 自动证书(使用 `SITES_RULE`)
- **compose.nginxproxy.yaml** - 添加 nginx-proxy 反向代理(HTTP,基于环境变量的 `VIRTUAL_HOST`)
- **compose.nginxproxy-ssl.yaml** - 添加 nginx-proxy + acme-companion(HTTPS,基于环境变量的 `LETSENCRYPT_HOST`)
**代理选择:**
- Traefik 在高级路由和多 bench 场景中更灵活
- nginx-proxy 在单 bench 基于主机路由的场景中更简单
### 📁 development/ - 开发环境
- **development/installer.py** - 自动化的 bench/site 创建和配置脚本
- 包含本地开发文件(已被 git 忽略以防止意外提交)
### 📁 resources/ - 运行时模板
- **core/nginx/nginx-entrypoint.sh** - 动态 Nginx 配置生成脚本
- **core/nginx/nginx-template.conf** - 带变量替换的 Nginx 配置模板
## 自定义应用说明
### 什么是 Frappe 自定义应用?
自定义应用是自包含的模块化业务应用,用于扩展 Frappe 的功能。它们遵循约定优于配置的方法,其中框架自动提供大部分样板代码。
### 自定义应用结构
```
my_custom_app/
├── hooks.py # 应用配置和钩入 Frappe 生命周期
├── modules.txt # 此应用中的业务模块列表
├── my_custom_app/
│ ├── __init__.py
│ ├── config/
│ │ └── desktop.py # 桌面工作区图标和快捷方式
│ ├── my_module/ # 业务领域模块(例如,销售、库存)
│ │ ├── doctype/ # 文档类型(数据模型)
│ │ │ ├── customer/
│ │ │ │ ├── customer.py # Python 控制器(业务逻辑)
│ │ │ │ ├── customer.json # 模型定义(模式、验证)
│ │ │ │ └── customer.js # 前端逻辑(UI 交互)
│ │ └── page/ # 自定义页面(仪表板、报告)
│ ├── public/ # 静态资源(CSS、JS、图片)
│ ├── templates/ # 网页的 Jinja2 模板
│ └── www/ # 通过路由可访问的网页
└── requirements.txt # Python 包依赖
```
### 内置功能(自动生成)
每个 Frappe 应用自动包含:
- **REST API** - 从 DocType 定义自动 CRUD 端点
- **权限系统** - 行级和字段级访问控制
- **审计跟踪** - 自动版本跟踪和更改历史
- **自定义字段** - 运行时字段添加,无需代码更改
- **工作流** - 可配置的审批和状态管理
- **报告** - 查询构建器和报告设计器
- **打印格式** - 使用自定义模板生成 PDF
- **邮件集成** - 基于模板的邮件发送
- **文件附件** - 文档附件管理
### 创建自定义应用
```bash
# 进入开发容器
docker exec -it <container_name> bash
# 创建新应用(交互式提示将询问详细信息)
bench new-app my_custom_app
# 将应用安装到站点
bench --site mysite.com install-app my_custom_app
# 创建新的 DocType(数据模型)
bench --site mysite.com console
>>> bench.new_doc("DocType", {...})
# 或使用 Web UI:设置 → 自定义 → DocType → 新建
```
## 开发工作流
### 快速测试配置(pwd.yml)
非常适合在无需任何本地设置的情况下评估 Frappe Docker:
```bash
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
docker compose -f pwd.yml up -d
# 监控站点创建(约 5 分钟)
docker compose -f pwd.yml logs -f create-site
# 当 "create-site" 容器成功退出后访问
# 访问 http://localhost:8080
# 登录:Administrator / admin
```
### 完整开发配置
用于带有热重载和调试的主动开发:
1. **复制 devcontainer 配置:**
```bash
cp -R devcontainer-example .devcontainer
```
2. **在 VSCode 中使用 Dev Containers 扩展打开**(Remote - Containers)
- VSCode 会检测到 `.devcontainer` 并提示在容器中重新打开
3. **运行自动化安装程序:**
```bash
cd /workspace/development
python installer.py
# 按照交互式提示输入站点名称、要安装的应用等
```
4. **访问开发文件:**
```
development/frappe-bench/ # 你的实时开发环境
```
### 开发文件位置
```
development/
├── frappe-bench/ # 你的实际 Frappe 安装
│ ├── apps/ # 所有已安装的 Frappe 应用
│ │ ├── frappe/ # 核心框架(不要直接修改)
│ │ ├── erpnext/ # ERPNext 应用(如果已安装)
│ │ └── my_custom_app/ # 你的自定义应用(随意编辑)
│ ├── sites/ # 多租户站点
│ │ ├── development.localhost/ # 默认开发站点
│ │ │ ├── site_config.json # 站点特定配置
│ │ │ └── private/files/ # 上传的文件
│ │ └── common_site_config.json # 共享配置
│ ├── env/ # Python 虚拟环境
│ ├── logs/ # 应用日志
│ └── config/ # Bench 级配置
└── .vscode/ # VSCode 工作区设置
```
### 常用开发命令
```bash
# 在容器内
bench start # 启动带热重载的开发服务器
# 数据库操作
bench migrate # 运行数据库迁移
bench --site mysite.com migrate # 站点特定迁移
# 前端构建
bench build # 构建所有应用资源
bench build --app my_custom_app # 构建特定应用
# 代码生成
bench new-app <app_name> # 创建新应用
bench new-site <site_name> # 创建新站点
# 应用管理
bench get-app <git_url> # 从 git 下载应用
bench install-app <app_name> # 将应用安装到当前站点
bench uninstall-app <app_name> # 从站点移除应用
# 调试
bench console # 带 Frappe 上下文的 Python REPL
bench mariadb # 数据库控制台
```
## 平台说明
### ARM64 和 Apple Silicon
- 在 Apple Silicon 上使用仅 x86 镜像进行初始构建时,启用 Docker Desktop 的 Rosetta 模拟。
- 优先使用发布的多架构镜像(`frappe/bench`、`frappe/erpnext`)或使用 `docker buildx bake --set *.platform=linux/amd64,linux/arm64` 在本地构建,以在一次传递中覆盖两种架构。
- 使用 `pwd.yml` 时,导出 `DOCKER_DEFAULT_PLATFORM=linux/arm64`(或选择提供的 compose profile)以避免意外模拟。
- 将绑定挂载保持在用户主目录下,并应用 `:cached` 或 `:delegated` 一致性标志以在 macOS 上获得更好的性能。
## 文件位置和访问
### 访问容器文件
```bash
# 进入后端容器 shell
docker compose -f pwd.yml exec backend bash
# 导航到 bench 目录
cd /home/frappe/frappe-bench/
# 关键目录:
/home/frappe/frappe-bench/apps/ # 所有 Frappe 应用
/home/frappe/frappe-bench/sites/ # 站点数据和配置
/home/frappe/frappe-bench/logs/ # 应用日志
/home/frappe/frappe-bench/env/ # Python 虚拟环境
```
### 从容器复制文件
```bash
# 将整个应用从容器复制到主机
docker compose -f pwd.yml cp backend:/home/frappe/frappe-bench/apps/my_app ./local-apps/
# 复制日志
docker compose -f pwd.yml cp backend:/home/frappe/frappe-bench/logs/ ./debug-logs/
# 复制站点文件
docker compose -f pwd.yml cp backend:/home/frappe/frappe-bench/sites/mysite.com ./backup/
```
### 有用的容器命令
```bash
# 列出所有站点
docker compose -f pwd.yml exec backend bench list-sites
# 列出站点已安装的应用
docker compose -f pwd.yml exec backend bench --site mysite.com list-apps
# 查看站点配置
docker compose -f pwd.yml exec backend cat /home/frappe/frappe-bench/sites/common_site_config.json
# 实时查看日志
docker compose -f pwd.yml logs -f backend
# 执行 bench 命令
docker compose -f pwd.yml exec backend bench --site mysite.com console
# 备份站点
docker compose -f pwd.yml exec backend bench --site mysite.com backup --with-files
```
## Docker 概念:绑定挂载
### 什么是绑定挂载?
绑定挂载在主机上的目录和容器内的目录之间创建直接连接。任一位置的更改都会立即反映在另一个位置 - 非常适合开发,当你想在主机上编辑代码并在容器中查看更改时。
### 绑定挂载 vs 命名卷 vs 匿名卷
| 类型 | 语法 | 使用场景 | 持久化 |
| -------------------- | ---------------------------- | ------------------------ | -------------------------- |
| **绑定挂载** | `./local/path:/container/path` | 开发、配置文件 | 在主机文件系统上 |
| **命名卷** | `volume_name:/container/path` | 生产数据、数据库 | Docker 管理 |
| **匿名卷** | `/container/path` | 临时/缓存数据 | Docker 管理,自动删除 |
### 绑定挂载示例
```yaml
services:
backend:
volumes:
# 开发:在主机上编辑代码,在容器中运行
- ./my_custom_app:/home/frappe/frappe-bench/apps/my_custom_app
# 配置:用主机文件覆盖容器配置
- ./custom-config.json:/home/frappe/frappe-bench/sites/common_site_config.json:ro # :ro = 只读
# 日志:在主机上访问容器日志以进行调试
- ./logs:/home/frappe/frappe-bench/logs
# 数据库(不推荐用于生产)
- ./data/mysql:/var/lib/mysql
# 生产数据库的命名卷
db:
volumes:
- db_data:/var/lib/mysql # 由 Docker 管理,容器删除后仍然存在
volumes:
db_data: # 定义命名卷
```
### 性能优化(macOS/Windows)
macOS/Windows 上的 Docker 使用 VM,使绑定挂载变慢。使用这些标志:
```yaml
volumes:
# :cached - 主机写入被缓冲(适用于一般开发)
- ./development:/home/frappe/frappe-bench:cached
# :delegated - 容器写入被缓冲(当容器大量写入时最佳)
- ./development:/home/frappe/frappe-bench:delegated
# :consistent - 完全同步(最慢但最安全)
- ./development:/home/frappe/frappe-bench:consistent
```
**建议:** 在 macOS/Windows 上的大多数开发工作中使用 `:cached`。
## Fork 管理最佳实践
### 初始 Fork 设置
```bash
# 1. 在 GitHub 上 Fork(使用 Fork 按钮)
# 2. 克隆你的 fork
git clone https://github.com/YOUR_USERNAME/frappe_docker
cd frappe_docker
# 3. 添加 upstream 远程(原始仓库)
git remote add upstream https://github.com/frappe/frappe_docker.git
# 4. 验证远程
git remote -v
# origin https://github.com/YOUR_USERNAME/frappe_docker (你的 fork)
# upstream https://github.com/frappe/frappe_docker (原始)
# 5. 创建开发分支
git checkout -b my-custom-setup
```
### 安全的自定义区域
**✅ 安全(不会与 upstream 冲突):**
```
development/ # 你的整个开发环境
├── frappe-bench/ # 本地安装
└── .vscode/ # 你的编辑器设置
compose.my-*.yaml # 你的自定义 compose 覆盖
scripts/my-*.sh # 你的自定义脚本
docs/my-*.md # 你的自定义文档
.env.local # 本地环境覆盖
.gitignore.local # 额外的 gitignore 规则
```
**⚠️ 需要修改(可能冲突):**
```
compose.yaml # 核心 - 使用覆盖文件代替
docker-bake.hcl # 构建配置 - 使用自定义文件
images/*/Dockerfile # 核心镜像 - 扩展而非修改
```
**❌ 永远不要修改(会破坏 upstream 同步):**
```
.github/workflows/ # CI/CD 流水线
images/*/ # 核心镜像定义
resources/ # 核心模板
```
### 保持 Fork 更新
```bash
# 定期与 upstream 同步(建议每周)
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
# 更新你的开发分支
git checkout my-custom-setup
git rebase main # 或:git merge main
# 如果在 rebase 期间发生冲突:
# 1. 修复文件中的冲突
# 2. git add <fixed-files>
# 3. git rebase --continue
# 或:git rebase --abort (取消)
```
### 自定义环境模式
为你的自定义创建覆盖文件:
```yaml
# compose.my-env.yaml
version: "3.7"
services:
backend:
environment:
# 你的自定义环境变量
- DEVELOPER_MODE=true
- MY_API_KEY=
volumes:
# 你的自定义绑定挂载
- ./development/my-scripts:/home/frappe/my-scripts
- ./development/my-config:/home/frappe/config
# 你的额外服务
my-monitoring:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
# 使用它:
# docker compose -f compose.yaml -f compose.my-env.yaml up
```
### .gitignore 策略
添加到 `.gitignore`(或创建 `.gitignore.local`):
```gitignore
# 本地环境文件
.env.local
*.local.yaml
compose.my-*.yaml
# 开发产物
development/frappe-bench/sites/*
development/frappe-bench/apps/*
!development/frappe-bench/apps.json
development/frappe-bench/logs/
development/frappe-bench/env/
# 本地自定义
my-local-configs/
scripts/my-*.sh
docs/internal-*.md
# IDE
.vscode/settings.json.local
.idea/
# 临时文件
*.swp
*.swo
*~
.DS_Store
```
### 向上游贡献
```bash
# 1. 从 main 创建功能分支
git checkout main
git pull upstream main
git checkout -b feature/my-improvement
# 2. 进行更改并提交
git add .
git commit -m "feat: add awesome feature"
# 3. 推送到你的 fork
git push origin feature/my-improvement
# 4. 在 GitHub 上创建 Pull Request
# 前往:https://github.com/frappe/frappe_docker
# 点击 "Compare & pull request"
```
## 快速启动示例
### 1. 快速测试(5 分钟)
**目标:** 在无需任何本地设置的情况下试用 Frappe/ERPNext
```bash
# 克隆并运行
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
docker compose -f pwd.yml up -d
# 监控设置进度(约 5 分钟)
docker compose -f pwd.yml logs -f create-site
# 完成后访问:
# URL: http://localhost:8080
# 用户名: Administrator
# 密码: admin
# 完成后清理
docker compose -f pwd.yml down -v
```
### 2. 开发环境(15 分钟)
**目标:** 设置带热重载的日常开发环境
```bash
# 复制 devcontainer 配置
cp -R devcontainer-example .devcontainer
# 在 VSCode 中打开
# 1. 安装 "Dev Containers" 扩展
# 2. 命令面板 (Ctrl+Shift+P) → "Reopen in Container"
# 3. 等待容器构建(首次约 5 分钟)
# 在容器终端内:
cd /workspace/development
python installer.py
# 按提示操作:
# - 站点名称: development.localhost
# - 安装 ERPNext: Yes
# - 版本: version-15
# 启动开发服务器
cd frappe-bench
bench start
# 访问: http://localhost:8000
# 编辑文件: development/frappe-bench/apps/
```
### 3. 自定义应用开发(30 分钟)
**目标:** 创建和开发自定义 Frappe 应用
```bash
# 前提:先完成示例 2
# 在开发容器内
cd /workspace/development/frappe-bench
# 创建新应用
bench new-app library_management
# 按提示操作(标题、描述、发布者等)
# 安装到站点
bench --site development.localhost install-app library_management
# 通过 Web UI 创建 DocType:
# 1. 前往: http://localhost:8000
# 2. 设置 → 自定义 → DocType → 新建
# 3. 创建: Book, Author, Borrower 等
# 或通过代码创建:
# 编辑: apps/library_management/library_management/library_management/doctype/
# 构建并重载
bench build --app library_management
# 服务器自动重载(bench start 监视更改)
```
### 4. 生产部署(1 小时)
**目标:** 使用 SSL 在生产环境中部署 Frappe
```bash
# 参考详细指南
# 参见: docs/single-server-example.md
# 快速概览:
# 1. 使用 Docker 设置服务器
# 2. 克隆 frappe_docker
# 3. 配置环境变量
# 4. 使用 compose.yaml + 生产覆盖文件
# 5. 使用 Traefik/Let's Encrypt 设置 SSL
# 6. 部署并监控
# 关键文件:
# - compose.yaml
# - compose.mariadb.yaml
# - compose.redis.yaml
# - compose.proxy.yaml
# - compose.https.yaml
# 部署命令:
docker compose \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.https.yaml \
up -d
```
### 5. 多站点托管
**目标:** 在一台服务器上托管多个 Frappe 站点
```bash
# 参见: docs/port-based-multi-tenancy.md
# 快速示例:
# 1. 在开发中创建多个站点
bench new-site site1.com
bench new-site site2.com
# 2. 配置 Nginx/Traefik 进行路由
# 3. 每个站点获得自己的数据库
# 4. 共享 Redis 和应用代码
```
---
## 框架对比
> **注意:** 本节为熟悉其他框架的开发者提供与其他框架的对比。如果你对所有框架都是新手,可以跳过本节 - 本指南的其余部分是自包含的。
### Frappe 与 Django 概念对比
#### 项目结构对比
**Django 项目:**
```python
myproject/
├── myproject/ # 项目设置
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── blog/ # Django 应用
│ ├── models.py
│ ├── views.py
│ └── urls.py
├── shop/ # Django 应用
└── users/ # Django 应用
```
**Frappe Bench:**
```
bench/
├── apps/
│ ├── frappe/ # 核心框架(相当于 Django 本身)
│ ├── erpnext/ # 完整的业务应用(相当于 Django + DRF + Celery + admin)
│ ├── hrms/ # 人力资源管理应用
│ └── my_custom_app/ # 你的自定义应用
└── sites/
└── mysite.com/ # 站点实例(相当于 Django 项目 + 数据库)
├── site_config.json
└── private/files/
```
#### 概念映射
| Django | Frappe | 说明 |
| ------------------ | ----------------- | ---------------------------------------------- |
| Model | DocType | 但自动包含 UI、权限、API |
| View | Controller method | 需要的代码少得多 |
| Admin | Desk | 更强大,自动生成 |
| DRF Serializer | 内置 | 从 DocType 自动生成 |
| Celery task | Background job | 内置,无需单独设置 |
| signals | hooks.py | 更结构化 |
| Management command | bench command | 更易发现 |
#### 关键架构差异
1. **多租户**
- Django:一个应用 = 一个数据库(通常)
- Frappe:一个安装 = 多个站点,每个站点有自己的数据库
2. **后台任务**
- Django:需要 Celery + Redis + worker 设置
- Frappe:内置队列系统,只需使用 `enqueue()`
3. **实时**
- Django:需要 Channels + Redis + ASGI 设置
- Frappe:内置 Socket.IO,DocType 更新自动处理
4. **管理/管理界面**
- Django:Admin 用于模型,基本 CRUD
- Frappe:功能齐全的 Desk,带有报告、仪表板、权限
5. **API**
- Django:手动 DRF 设置、序列化器、视图
- Frappe:从 DocType 定义自动生成 REST + RPC
#### 代码对比示例
**创建"客户"模型:**
Django(需要约 50+ 行):
```python
# models.py
class Customer(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
# serializers.py
class CustomerSerializer(serializers.ModelSerializer):
# ...
# views.py
class CustomerViewSet(viewsets.ModelViewSet):
# ...
# urls.py
router.register(r'customers', CustomerViewSet)
# admin.py
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
# ...
```
Frappe(DocType JSON + 约 10 行 Python):
```json
// customer.json(通过 UI 或代码自动生成)
{
"name": "Customer",
"fields": [
{ "fieldname": "customer_name", "fieldtype": "Data" },
{ "fieldname": "email", "fieldtype": "Data", "unique": 1 }
]
}
```
```python
# customer.py(仅用于自定义业务逻辑)
import frappe
from frappe.model.document import Document
class Customer(Document):
def validate(self):
# 仅自定义验证逻辑
pass
```
✅ **自动包含:**
- REST API(`/api/resource/Customer`)
- 列表视图、表单视图
- 搜索、过滤器、排序
- 权限(创建、读取、更新、删除)
- 审计跟踪(created_by、modified_by、versions)
- 打印格式、邮件模板
#### 何时选择 Frappe 与 Django
**选择 Frappe 当:**
- 构建业务应用(ERP、CRM、项目管理)
- 需要开箱即用的多租户
- 想要使用自动生成的 UI 进行快速开发
- 需要基于角色的权限和工作流
- 为需要自定义的非技术用户构建
**选择 Django 当:**
- 构建消费者 Web 应用(社交媒体、电子商务前端)
- 需要完全控制每个方面
- 有高度自定义的 UI 需求
- 团队已经是 Django 专家
- 构建仅 API 的服务
**混合方法:**
许多团队同时使用两者:Frappe 用于后台/管理工具,Django 用于面向客户的 Web 应用。
---
## 资源和参考
### 官方文档
- [Frappe 框架文档](https://frappeframework.com/docs) - 核心框架文档
- [Frappe Docker 文档](https://github.com/frappe/frappe_docker/tree/main/docs) - 本仓库的文档
- [ERPNext 文档](https://docs.erpnext.com) - ERPNext 用户和开发者文档
- [Docker 文档](https://docs.docker.com) - Docker 基础知识
### 本仓库中的关键文件
- [docs/05-development/01-development.md](05-development/01-development.md) - 详细的开发配置
- [docs/02-setup/04-env-variables.md](02-setup/04-env-variables.md) - 环境变量参考
- [docs/02-setup/07-single-server-example.md](02-setup/07-single-server-example.md) - 生产部署指南
- [docs/04-operations/01-site-operations.md](04-operations/01-site-operations.md) - 常见站点管理任务
- development/installer.py - 自动化设置脚本
- [pwd.yml](../pwd.yml) - 快速测试配置
- [compose.yaml](../compose.yaml) - 基础 Docker Compose 配置
### 社区资源
- [Frappe 论坛](https://discuss.frappe.io) - 社区问答
- [Frappe 学校](https://frappe.school) - 视频教程
- [Frappe GitHub](https://github.com/frappe/frappe) - 框架源代码
### 基本 Docker 命令参考
```bash
# 服务管理
docker compose up -d # 在后台启动所有服务
docker compose down # 停止并移除容器
docker compose down -v # 停止并移除卷(数据丢失!)
docker compose restart <service> # 重启特定服务
docker compose ps # 列出运行中的服务
docker compose logs -f <service> # 跟踪服务日志
# 容器访问
docker compose exec <service> bash # 在运行中的容器中打开 shell
docker compose exec <service> <cmd> # 在容器中运行命令
docker compose run <service> <cmd> # 运行一次性命令(创建新容器)
# 调试
docker compose logs --tail=100 <service> # 最近 100 行日志
docker compose top # 显示运行中的进程
docker inspect <container_name> # 详细容器信息
# 清理
docker system prune # 移除未使用的容器/网络
docker volume prune # 移除未使用的卷(小心!)
docker image prune # 移除未使用的镜像
```
### 基本 Bench 命令参考
```bash
# 站点操作
bench new-site <site_name> # 创建新站点
bench drop-site <site_name> # 删除站点(要求确认)
bench list-sites # 列出所有站点
bench use <site_name> # 设置默认站点
# 应用操作
bench get-app <git_url> # 从 git 下载应用
bench get-app <app_name> # 从 Frappe 注册表下载
bench install-app <app_name> # 安装到默认站点
bench install-app <app_name> --site <site> # 安装到特定站点
bench uninstall-app <app_name> # 从默认站点卸载
bench list-apps # 列出已安装的应用
# 开发
bench start # 启动开发服务器(热重载)
bench build # 构建前端资源
bench build --app <app_name> # 构建特定应用
bench migrate # 运行数据库迁移
bench clear-cache # 清除 Redis 缓存
bench clear-website-cache # 清除网站路由缓存
# 数据库
bench mariadb # 打开 MariaDB 控制台
bench backup # 备份默认站点
bench backup --with-files # 带上传文件备份
bench restore <path> # 恢复备份
# 代码生成
bench new-app <app_name> # 创建新应用
bench --site <site> console # 带 Frappe 上下文的 Python REPL
bench --site <site> execute "<python_code>" # 执行 Python 代码
# 部署
bench setup production <user> # 设置生产环境(supervisor、nginx)
bench restart # 重启 bench 进程
bench update # 更新框架和应用
```
### 故障排除快速参考
| 问题 | 解决方案 |
| ------------------------ | ----------------------------------------------------- |
| 端口 8080 已被使用 | 更改 `.env` 中的 `PWD_PORT` 或停止其他服务 |
| 容器无法启动 | 检查日志:`docker compose logs <service>` |
| 站点创建失败 | 检查 `create-site` 日志,确保 DB 已就绪 |
| 无法连接到站点 | 等待 5 分钟进行初始化,检查容器健康状态 |
| 权限错误 | 检查卷权限,可能需要 `chown` |
| 磁盘空间不足 | `docker system prune -a --volumes`(小心!) |
| 缺少 Python 包 | 在容器内 `bench pip install <package>` |
| 前端未构建 | `bench build --force`,检查 Node.js 错误 |
| 数据库连接失败 | 检查 `common_site_config.json`、Redis/MariaDB 状态 |
### 获取帮助
1. **查看现有文档** - 大多数问题在 [docs/07-troubleshooting/01-troubleshoot.md](07-troubleshooting/01-troubleshoot.md) 中有覆盖
2. **搜索 Frappe 论坛** - [discuss.frappe.io](https://discuss.frappe.io)
3. **GitHub Issues** - 先搜索现有问题
4. **Discord/Telegram** - 社区实时聊天(链接在主仓库中)
### 参与贡献
发现本指南的问题或改进建议?
- 创建 issue:[frappe_docker/issues](https://github.com/frappe/frappe_docker/issues)
- 提交聚焦的 PR:保持更新范围明确,将大型工作拆分为多个 pull request。
- 查看 CONTRIBUTING.md:了解编码标准和审查期望。
---
_本指南为各种背景的开发者提供了 Frappe Docker 的全面概述。有关特定用例或高级主题,请参阅链接的文档。_
_最后更新:2025 年 10 月_
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+26
View File
@@ -0,0 +1,26 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Frappe in Docker"
# text: "Frappe in Container"
tagline: "使用 Docker 部署 Frappe 框架的文档"
image:
src: /frappe-docker.png
actions:
- theme: brand
text: 快速上手
link: /getting-started
- theme: alt
text: 单文件 Compose 配置
link: /01-getting-started/04-single-compose-setup
features:
- title: 容器化
details: Frappe 的全部能力,运行在容器中
- title: 数分钟即可搭建
details: 几分钟内即可启动
- title: 生产就绪
details: 轻松部署生产应用
---
+19
View File
@@ -0,0 +1,19 @@
{
"devDependencies": {
"vitepress": "2.0.0-alpha.16",
"vitepress-sidebar": "1.33.1"
},
"scripts": {
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
},
"pnpm": {
"overrides": {
"vite": "7.3.2",
"minimatch": "10.2.5",
"picomatch": "4.0.4"
}
},
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
}
+1759
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

+69 -9
View File
@@ -3,35 +3,95 @@ set -euo pipefail
APP_NAME="${FRAPPE_APP_NAME:-yierp}"
SITE_NAME="${FRAPPE_SITE_NAME:-yierp.localhost}"
BENCH_DIR="${BENCH_DIR:-/home/frappe/frappe-bench}"
BENCH_DIR="${BENCH_DIR:-/home/frappe/.bench-root/frappe-bench}"
DB_HOST="${FRAPPE_DB_HOST:-mariadb}"
DB_ROOT_PASSWORD="${MARIADB_ROOT_PASSWORD:-admin}"
ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin}"
mkdir -p "${BENCH_DIR}/apps" "${BENCH_DIR}/sites" "${BENCH_DIR}/logs" "${BENCH_DIR}/config"
ln -sfn /workspace "${BENCH_DIR}/apps/${APP_NAME}"
export PATH="/home/frappe/.nvm/versions/node/v24.22.0/bin:/home/frappe/.pyenv/shims:/home/frappe/.local/bin:/home/frappe/.pyenv/bin:${PATH}"
export PYTHONPATH="/home/frappe/.bench:/home/frappe/.local/lib/python3.14/site-packages:${PYTHONPATH:-}"
if [ ! -f "${BENCH_DIR}/config/redis.conf" ] || [ ! -d "${BENCH_DIR}/apps/frappe" ]; then
echo "=== YiERP Development Environment Setup ==="
echo "App: ${APP_NAME}"
echo "Site: ${SITE_NAME}"
echo "Bench Dir: ${BENCH_DIR}"
echo ""
mkdir -p "$(dirname "${BENCH_DIR}")"
# Initialize bench if not exists
if [ ! -f "${BENCH_DIR}/config/procfile" ] || [ ! -d "${BENCH_DIR}/apps/frappe" ]; then
echo "Initializing bench at ${BENCH_DIR}..."
cd "$(dirname "${BENCH_DIR}")"
bench init \
--skip-redis-config-generation \
--frappe-branch version-16 \
--python /usr/local/bin/python \
"${BENCH_DIR}"
echo "Bench initialized successfully"
fi
mkdir -p "${BENCH_DIR}/sites" "${BENCH_DIR}/logs" "${BENCH_DIR}/config"
# Link app from workspace
APP_SOURCE="/workspace/${APP_NAME}"
if [ ! -d "${APP_SOURCE}" ]; then
echo "Warning: App source not found at ${APP_SOURCE}, using /workspace"
APP_SOURCE="/workspace"
fi
if [ -d "${BENCH_DIR}/apps/${APP_NAME}" ] && [ ! -L "${BENCH_DIR}/apps/${APP_NAME}" ]; then
echo "Removing existing app directory to create symlink..."
rm -rf "${BENCH_DIR}/apps/${APP_NAME}"
fi
ln -sfn "${APP_SOURCE}" "${BENCH_DIR}/apps/${APP_NAME}"
echo "App linked: ${APP_SOURCE} -> ${BENCH_DIR}/apps/${APP_NAME}"
# Create site if not exists
if [ ! -d "${BENCH_DIR}/sites/${SITE_NAME}" ]; then
echo "Creating site: ${SITE_NAME}..."
cd "${BENCH_DIR}"
# Set common config for Redis services
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 new-site "${SITE_NAME}" \
--mariadb-root-password "${DB_ROOT_PASSWORD}" \
--db-root-user root \
--db-root-password "${DB_ROOT_PASSWORD}" \
--admin-password "${ADMIN_PASSWORD}" \
--db-host "${DB_HOST}" \
--no-mariadb-socket
--no-mariadb-socket \
--install-app frappe
echo "Site created successfully"
else
echo "Site already exists: ${SITE_NAME}"
fi
if ! bench --site "${SITE_NAME}" list-apps | grep -qE "^${APP_NAME}$"; then
# Install app if not installed
if ! (cd "${BENCH_DIR}" && bench --site "${SITE_NAME}" list-apps 2>/dev/null | grep -qE "^${APP_NAME}$"); then
echo "Installing app: ${APP_NAME}..."
cd "${BENCH_DIR}"
bench --site "${SITE_NAME}" install-app "${APP_NAME}"
bench --site "${SITE_NAME}" install-app "${APP_NAME}" || {
echo "Warning: Failed to install app ${APP_NAME}, it may need dependencies first"
}
echo "App installation attempted"
else
echo "App already installed: ${APP_NAME}"
fi
# Enable developer mode
cd "${BENCH_DIR}"
bench --site "${SITE_NAME}" set-config developer_mode 1
bench --site "${SITE_NAME}" clear-cache
echo ""
echo "=== Setup Complete ==="
echo "Starting bench..."
echo "Access YiERP at: http://localhost:8000"
echo "Login: Administrator / ${ADMIN_PASSWORD}"
echo ""
cd "${BENCH_DIR}"
exec bench start
View File
View File