Easy!Appointments 1.6.1 Docker Compose 部署指南
Docker Compose 部署 Easy!Appointments 1.6.1 + MariaDB 双服务栈:环境变量全集、数据持久化落盘、停栈重启恢复闭环实测与常用运维命令。
Easy!Appointments 1.6.1 Docker Compose 部署指南
目标:用 Docker Compose 一键部署 Easy!Appointments 1.6.1 + MariaDB 双服务栈,完成数据持久化落盘与「停栈—重启—数据恢复」闭环验证,覆盖环境变量全集、常用运维命令与常见问题。本文全部命令与回显均在 Ubuntu 22.04 + Docker Compose v2 环境实测通过,应用地址以 http://192.168.146.128:8128 为例。
一、环境信息
| 项目 | 值 | 来源 |
|---|---|---|
| 操作系统 | Ubuntu 22.04(x86_64) | 实测环境 |
| Compose 编排 | Docker Compose v2(docker compose 子命令) | 实测 |
| 应用镜像 | alextselegidis/easyappointments:1.6.1 | 官方 Docker Hub |
| 数据库镜像 | mariadb:10.11 | 实测 |
| 镜像内运行时 | Apache/2.4.69 (Debian) PHP/8.5.12 | docker compose logs 实测 |
| 应用端口 | 宿主机 8128 → 应用 80 | 实测映射 |
| 项目目录 | /opt/easyappointments(compose 工作目录) | 实测 |
| 数据落盘 | ./data/mariadb(bind mount 到容器 /var/lib/mysql) | 实测 |
二、镜像选型说明
Easy!Appointments 官方在 Docker Hub 发布 alextselegidis/easyappointments 镜像,与 GitHub Releases 同步打 tag(本篇锁用 1.6.1,累计拉取量约 140k,2026-10-23 溯源)。选官方镜像而不自行构建的理由有三:
- 配置零模板:镜像入口脚本按环境变量自动生成 /var/www/html/config.php 与 application/config/email.php,无需手工搬配置文件;
- 运行时自洽:镜像内置 Apache 与满足要求的 PHP 运行时(实测为 PHP/8.5.12),宿主机不装 PHP;
- 升级等价换 tag:应用升级 = 改镜像 tag 重建容器,应用文件永不手工替换。
数据库选用 mariadb:10.11:Easy!Appointments 对数据库仅要求 MySQL 系(官方 README),MariaDB 10.11 为当前 LTS 分支,完全兼容且镜像自带健康检查脚本,可让应用容器等数据库真正可写后再启动,避免「应用先起、库未就绪」的安装向导报错。
💡 技术拓展:容器重启策略不止一种,常见选项对比——
策略 行为 适用场景 no(默认) 退出不重启 一次性调试 on-failure[:N] 仅非零退出时重启,限次数 可能合理退出的任务进程 always 总是重启(手动 stop 的除外) 常驻服务 unless-stopped 总是重启,且手动停止后即使重启引擎也不拉起 常驻服务、计划内停机可控 本文采用
unless-stopped:宿主机重启后服务自动拉起,而运维主动stop的意图会被尊重。
三、前置条件
- Docker Engine 与 Compose v2 已安装:
docker compose version能输出版本号(实测环境为 v2 系); - 端口规划:宿主机 8128 未被占用,检查
ss -ltn | grep ':8128 '应无输出;应用对外端口可在 compose 文件ports处自由更换; - 目录准备:创建项目目录 /opt/easyappointments 并进入,本篇所有文件相对该目录;
- 外网可达:首次部署需要拉取两个镜像(可预先
docker pull导入内网仓库)。
四、部署步骤
4.1 编写 docker-compose.yml
在 /opt/easyappointments 下创建 docker-compose.yml(本文实测采用的完整内容):
name: easyappointments-1.6.1-stack
services:
mariadb:
image: mariadb:10.11
environment:
MARIADB_ROOT_PASSWORD: q1w2e3r4
MARIADB_DATABASE: easyappointments
MARIADB_USER: ea_user
MARIADB_PASSWORD: q1w2e3r4
volumes:
- ./data/mariadb:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped
easyappointments:
image: alextselegidis/easyappointments:1.6.1
depends_on:
mariadb:
condition: service_healthy
environment:
BASE_URL: http://192.168.146.128:8128
DEBUG_MODE: "FALSE"
DB_HOST: mariadb
DB_NAME: easyappointments
DB_USERNAME: ea_user
DB_PASSWORD: q1w2e3r4
MAIL_PROTOCOL: mail
MAIL_SMTP_CRYPTO: tls
MAIL_SMTP_PORT: "587"
MAIL_FROM_ADDRESS: info@example.org
MAIL_FROM_NAME: Example
ports:
- "8128:80"
restart: unless-stopped
networks:
default:
name: easyappointments-161-net
ipam:
config:
- subnet: 10.89.58.0/24
逐项说明:name 显式锁定 Compose 项目名,防止同机多栈目录同名互相干扰;mariadb 的 healthcheck 用官方镜像自带 healthcheck.sh 做可写性探测;easyappointments 的 depends_on 带 condition: service_healthy,即应用容器在数据库健康后才启动;volumes 把数据库目录 bind mount 到宿主机 ./data/mariadb,这是数据持久化的关键行;ports 把应用 80 端口映射到宿主机 8128;网络段显式指定 10.89.58.0/24 以规避同机其他栈的网段冲突。
4.2 启动栈
cd /opt/easyappointments
docker compose up -d
实测回显:
[+] Running 3/3
✔ Network easyappointments-161-net Created
✔ Container easyappointments-161-stack-mariadb-1 Healthy
✔ Container easyappointments-161-stack-easyappointments-1 Started
确认两个服务状态:
docker compose ps
实测回显:
NAME STATUS
easyappointments-161-stack-easyappointments-1 Up 12 seconds
easyappointments-161-stack-mariadb-1 Up 22 seconds (healthy)
4.3 运行安装向导
首次部署需要完成一次安装向导(只做一次,数据落库后即长期有效)。浏览器打开:
http://192.168.146.128:8128/index.php/installation/setup
数据库连接参数已被环境变量注入,向导中保持默认(host 填 mariadb、库名/账号按 compose 中的值),完成管理员账号与公司信息表单后提交。完成后系统自动建表(实测导入 14 张 ea_ 前缀业务表、迁移版本号 69)并跳转登录页。
验证首页可达:
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.146.128:8128/
实测回显:
200
五、数据持久化说明
持久化由两部分构成,缺一不可:
- 数据库数据:compose 中
./data/mariadb:/var/lib/mysqlbind mount,全部业务表落在宿主机 /opt/easyappointments/data/mariadb(实测目录内容):
aria_log.00000001
aria_log_control
ddl_recovery.log
easyappointments
ib_buffer_pool
- 安装配置:应用容器的 config.php 每次启动由入口脚本按环境变量重新生成(见 6.1 节),因此无需持久化应用目录——环境变量 + 数据库卷即可完整重建一套栈。
六、环境变量表
6.1 应用容器(alextselegidis/easyappointments:1.6.1)
入口脚本用这些变量逐行生成 config.php 与 email.php,实测生成的配置文件为:
class Config {
const BASE_URL = 'http://192.168.146.128:8128';
const LANGUAGE = 'english';
const DEBUG_MODE = FALSE;
const DB_HOST = 'mariadb';
const DB_NAME = 'easyappointments';
const DB_USERNAME = 'ea_user';
const DB_PASSWORD = 'q1w2e3r4';
const GOOGLE_SYNC_FEATURE = FALSE;
const GOOGLE_CLIENT_ID = '';
const GOOGLE_CLIENT_SECRET = '';
}
| 变量 | 必填 | 默认行为 | 说明 |
|---|---|---|---|
| BASE_URL | 是 | 无默认,不设则链接错乱 | 对外访问地址,含端口、无尾斜杠 |
| DEBUG_MODE | 否 | FALSE | 排障置 TRUE 时页面直接显示错误详情 |
| DB_HOST | 是 | — | 同 Compose 网络内填服务名 mariadb |
| DB_NAME | 是 | — | 需与 MARIADB_DATABASE 一致 |
| DB_USERNAME / DB_PASSWORD | 是 | — | 需与 MARIADB_USER/PASSWORD 一致 |
| LANGUAGE | 否 | english | 42 种语言可选,简体中文填 chinese |
| GOOGLE_SYNC_FEATURE | 否 | FALSE | 启用 Google 日历同步置 TRUE |
| GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | 否 | 空 | Google API 凭据 |
| MAIL_PROTOCOL | 否 | smtp 时需配 MAIL_SMTP_* 组 | |
| MAIL_SMTP_HOST / MAIL_SMTP_USER / MAIL_SMTP_PASS | 否 | 空 | SMTP 服务器与凭据 |
| MAIL_SMTP_CRYPTO / MAIL_SMTP_PORT | 否 | tls / 默认端口 | 加密方式与端口 |
| MAIL_SMTP_AUTH | 否 | FALSE | 是否启用 SMTP 认证 |
| MAIL_FROM_ADDRESS / MAIL_FROM_NAME | 否 | 空 | 发件人显示 |
| MAIL_REPLY_TO_ADDRESS | 否 | 空 | 回复地址 |
| MAIL_SMTP_DEBUG | 否 | 0 | 置 1 输出 SMTP 会话日志 |
6.2 数据库容器(mariadb:10.11)
| 变量 | 说明 |
|---|---|
| MARIADB_ROOT_PASSWORD | root 口令(仅管理用,应用不使用) |
| MARIADB_DATABASE | 自动创建的库名,与 DB_NAME 对应 |
| MARIADB_USER / MARIADB_PASSWORD | 自动创建的业务账号,与 DB_USERNAME/DB_PASSWORD 对应 |
七、配置文件挂载
默认不需要挂载任何配置文件——安装级配置由环境变量生成。两类进阶场景需要挂载:
- 深度自定义框架配置:把定制好的文件挂进容器对应路径(会覆盖镜像内默认值):
volumes:
- ./custom/config.php:/var/www/html/config.php:ro
- ./custom/email.php:/var/www/html/application/config/email.php:ro
注意:挂载自定义 config.php 后入口脚本不再生成它,后续改动需同步维护该文件;官方镜像每次启动都会把 $config['base_url'] 追加写入 application/config/config.php(幂等,重复启动不会重复写入)。
- 数据库文件迁移:./data/mariadb 即 /var/lib/mysql,整机迁移时直接拷贝该目录到新宿主机同路径后
docker compose up -d,栈以原数据复活。
八、等价 docker run 命令
不使用 Compose 时,以下两条命令与上述 compose 文件完全等价(先建网络,再依次起库与应用):
docker network create --subnet 10.89.58.0/24 easyappointments-161-net
docker run -d --name mariadb --network easyappointments-161-net \
-e MARIADB_ROOT_PASSWORD=q1w2e3r4 \
-e MARIADB_DATABASE=easyappointments \
-e MARIADB_USER=ea_user \
-e MARIADB_PASSWORD=q1w2e3r4 \
-v /opt/easyappointments/data/mariadb:/var/lib/mysql \
--restart unless-stopped \
mariadb:10.11
docker run -d --name easyappointments --network easyappointments-161-net \
-p 8128:80 \
-e BASE_URL=http://192.168.146.128:8128 \
-e DEBUG_MODE=FALSE \
-e DB_HOST=mariadb \
-e DB_NAME=easyappointments \
-e DB_USERNAME=ea_user \
-e DB_PASSWORD=q1w2e3r4 \
--restart unless-stopped \
alextselegidis/easyappointments:1.6.1
差异说明:等价命令没有健康检查联动,需自行等待数据库就绪(约 10–20 秒)再起应用,或像 5.1 节 compose 那样依赖 healthcheck。生产建议优先 Compose。
九、常用运维命令
以下命令均在项目目录 /opt/easyappointments 下执行(实测回显逐条附后)。
docker compose ps # 栈内服务状态
docker compose logs -f easyappointments # 跟踪应用日志
docker compose exec easyappointments bash # 进入应用容器
docker compose restart # 重启全部服务
docker compose stop # 停栈(保留容器)
docker compose start # 恢复停掉的栈
docker compose down # 删容器与网络(数据卷保留)
docker compose down -v # 连同匿名卷删除(bind mount 不受影响)
应用日志实测节选(docker compose logs easyappointments | tail -3):
easyappointments-1 | [Fri Oct 23 02:42:18.507982 2026] [mpm_prefork:notice] [pid 10:tid 10] AH00163: Apache/2.4.69 (Debian) PHP/8.5.12 configured -- resuming normal operations
easyappointments-1 | [Fri Oct 23 02:42:18.508015 2026] [core:notice] [pid 10:tid 10] AH00094: Command line: 'apache2 -D FOREGROUND'
easyappointments-1 | 10.89.58.1 - - [23/Oct/2026:02:42:30 +0000] "GET / HTTP/1.1" 200 92781 "-" "curl/7.81.0"
数据库备份(从容器内 mysqldump 导出,落宿主机当前目录):
docker compose exec -T mariadb mysqldump -uea_user -pq1w2e3r4 easyappointments > ea-$(date +%F).sql
ls -lh ea-*.sql
实测回显:
-rw-r--r-- 1 root root 62K Oct 23 10:55 ea-2026-10-23.sql
恢复:docker compose exec -T mariadb mysql -uea_user -pq1w2e3r4 easyappointments < ea-2026-10-23.sql。
镜像升级(如未来 1.6.2 发布):改 compose 中镜像 tag → docker compose pull → docker compose up -d,数据库结构变更由应用向导自动迁移。
十、数据持久化与重启恢复闭环(实测)
本节完整记录一次「写入 → 停栈 → 重启 → 验证」闭环,证明数据落盘可靠。
第 1 步:写入标记数据(通过 REST API 创建客户):
curl -s -u 'administrator:administrator' -X POST -H 'Content-Type: application/json' \
-d '{"firstName":"重启","lastName":"恢复测试","email":"restart-recovery@example.org","phone":"13800000000"}' \
http://192.168.146.128:8128/index.php/api/v1/customers
实测回显(输出节选):
{"id":5,"firstName":"重启","lastName":"恢复测试","email":"restart-recovery@example.org","phone":"13800000000","address":null,"city":null,"zip":null,"notes":null,"timezone":"UTC","language":"english","customField1":null,"customField2":null,"customField3":null,"customField4":null,"customField5":null,"ldapDn":null}
第 2 步:整栈拆除(容器与网络删除,仅数据目录保留):
docker compose down
实测回显(末尾三行):
Container easyappointments-161-stack-mariadb-1 Removing
Container easyappointments-161-stack-mariadb-1 Removed
Network easyappointments-161-net Removing
Network easyappointments-161-net Removed
确认宿主机数据仍在:
ls /opt/easyappointments/data/mariadb/easyappointments | head -4
实测回显:
db.opt
ea_appointments.frm
ea_appointments.ibd
ea_blocked_periods.frm
第 3 步:重新拉起:
docker compose up -d
实测回显:
Container easyappointments-161-stack-mariadb-1 Healthy
Container easyappointments-161-stack-easyappointments-1 Started
第 4 步:验证恢复——服务与数据双确认:
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.146.128:8128/
curl -s -u 'administrator:administrator' http://192.168.146.128:8128/index.php/api/v1/customers | python3 -m json.tool | grep -A1 firstName
实测回显:
200
"firstName": "重启",
"lastName": "恢复测试",
闭环结论:down 拆掉容器与网络后,bind mount 的数据库文件原样保留;up 后应用无需重装向导、管理员账号与全部业务数据原样可用。同理适用于宿主机重启(restart: unless-stopped 自动拉起)。
十一、常见问题(FAQ)
Q1:应用容器反复重启,日志报数据库连接被拒?
多为数据库未就绪即被访问。确认 compose 中 depends_on 使用了 condition: service_healthy(本篇 4.1 节写法),并确认 mariadb 容器状态为 healthy(docker compose ps 括号标注)。仍失败时查库容器日志:docker compose logs mariadb | tail -20,常见为 MARIADB_* 变量与 DB_* 变量不一致。
Q2:页面打开是安装向导,重新填完又回到向导?
安装向导写库失败时会退回。核对三点:DB_HOST 是否为 compose 服务名(mariadb)而非 localhost;DB_NAME/账号是否与 MARIADB_* 完全一致;BASE_URL 是否与实际访问地址一致(含端口)。排除后 docker compose restart easyappointments 再试。
Q3:修改了 compose 里的 BASE_URL,页面样式丢失或链接跳错?
BASE_URL 参与静态资源引用与邮件链接,改后必须重建容器使入口脚本重新生成配置:docker compose up -d --force-recreate easyappointments,浏览器强刷(Ctrl+F5)清缓存。
Q4:宿主机磁盘满,data/mariadb 能否搬走?
可以。docker compose stop mariadb 后整目录拷贝到新位置,compose 中 volumes 改为新路径(或先在新路径建软链),docker compose start mariadb 验证 healthy 后恢复整栈。
Q5:docker compose down 会删数据吗?
不会。down 只删容器与网络;本方案数据落在 bind mount 目录 ./data/mariadb,不在匿名卷里,down -v 也不影响。真正会丢数据的是删除 ./data/mariadb 目录本身——执行任何清理前先 docker compose exec -T mariadb mysqldump ... 导出备份。
Q6:能否两套 Easy!Appointments 跑在同一台宿主机? 可以,改三处:对外端口(如 8129:80)、Compose 项目 name、网络 subnet(如 10.89.59.0/24),并各自独立 data 目录;两栈互不干扰。
十二、参考链接
- 官方镜像:https://hub.docker.com/r/alextselegidis/easyappointments
- GitHub Releases:https://github.com/alextselegidis/easyappointments/releases
- 官方文档(安装与配置):https://easyappointments.org/docs
- MariaDB Docker 镜像文档:https://hub.docker.com/_/mariadb
- 站内延伸:《Easy!Appointments 1.6.1 介绍与选型指南》《Ubuntu 22.04 安装 Easy!Appointments 1.6.1 指南》《Easy!Appointments 1.6.1 API 使用详解》