1. 项目概述:为什么现在必须认真对待 Odoo 17 的 Docker 部署
最近两周,我连续帮三位不同行业的客户部署 Odoo 17,其中两位明确要求“不能碰服务器原系统,所有环境必须隔离”。这不是巧合,而是当前企业数字化落地的真实水位线——没人再愿意为一套业务系统去动生产服务器的 Python 版本、PostgreSQL 配置或系统级依赖。Odoo 17 本身对 Python 3.10+、PostgreSQL 14+、wkhtmltopdf 0.12.6+ 等组件有刚性要求,而一台刚装好的 Ubuntu 22.04 默认只带 Python 3.10.12 和 PostgreSQL 14.12,表面看似乎“刚好匹配”,但实际部署时你会发现:wkhtmltopdf 官方包不支持 ARM 架构(比如树莓派或 M1/M2 Mac),PostgreSQL 14.12 缺少pg_trgm扩展导致搜索模块报错,Python 的cryptography库又因 OpenSSL 版本冲突反复编译失败……这些不是理论风险,是我上周在某制造企业现场花 4 小时逐行排查的实录。
Docker 不是“让部署变酷”的玩具,它是解决这类环境撕裂问题的工业级止血钳。用 Docker 安装 Odoo 17,本质是把整个运行栈——从底层操作系统镜像、数据库服务、Web 服务、静态文件缓存,到 Odoo 自身的 Python 运行时和依赖包——全部打包进一个可验证、可复现、可迁移的声明式单元。你不需要记住“先装什么、后改哪行配置、重启哪个服务”,只需要一份docker-compose.yml,执行docker compose up -d,127 秒后就能在浏览器打开http://localhost:8069进入初始化向导。更重要的是,它天然支持多版本并存:你可以在同一台服务器上,用odoo17-prod、odoo17-test、odoo16-legacy三个独立容器组分别运行不同分支,彼此内存隔离、端口隔离、数据卷隔离,连日志都互不干扰。这已经不是“推荐做法”,而是中型以上企业运维团队的准入门槛。如果你还在用git clone && python3 -m pip install -r requirements.txt && ./odoo-bin这套流程部署生产环境,那相当于开着拖拉机跑高速公路——能动,但随时可能被甩出赛道。
2. 整体架构设计与方案选型逻辑
2.1 为什么放弃“单容器全栈”模式?
最直觉的思路是:找一个预装 Odoo 17 的镜像,docker run -p 8069:8069 -v /opt/odoo/data:/var/lib/odoo odoo:17一行命令搞定。我试过官方odoo:17镜像,也试过社区热门的tvtmr/odoo:17.0,结果在第三天就遇到硬伤:当客户需要启用邮件网关(mail_gateway)模块时,容器内缺少sendmail或postfix服务,而强行在运行中的容器里apt-get install postfix会破坏镜像层一致性,下次docker pull更新后所有手动安装都会丢失。更麻烦的是日志管理——所有服务(Odoo 主进程、PostgreSQL、Nginx 反向代理)的日志混在同一个容器 stdout 里,用docker logs -f odoo17查问题时,你得靠关键词过滤,效率极低。
所以最终采用三容器解耦架构:odoo-app(仅含 Odoo Python 环境与代码)、db-postgres(专用 PostgreSQL 实例)、proxy-nginx(可选,用于 HTTPS 终止与多租户路由)。这种设计不是为了炫技,而是基于四个刚性约束:
- 故障域隔离:PostgreSQL 崩溃不会导致 Odoo 进程退出,反之亦然;
- 升级弹性:升级 Odoo 版本只需重建
odoo-app容器,数据库容器完全不动; - 资源可控:可单独为
db-postgres分配 2GB 内存限制,避免其吃光服务器资源; - 合规审计:数据库密码、SSL 证书等敏感信息通过 Docker Secret 或环境变量注入,不硬编码在镜像中。
提示:如果你只是本地开发测试,完全可以省略
proxy-nginx,直接用 Odoo 内置的 Werkzeug 服务器(默认监听0.0.0.0:8069),但生产环境必须加反向代理层,这是 Odoo 官方文档第 3 章第 2 节明确强调的安全基线。
2.2 镜像选型:官方镜像 vs 社区定制镜像的实战权衡
Docker Hub 上有超过 17 个标称支持 Odoo 17 的镜像,我重点对比了三类:
- 官方
odoo:17镜像(由 Odoo SA 官方维护):基础镜像基于python:3.10-slim-bookworm,体积仅 582MB,启动快,安全更新及时。但它默认不包含wkhtmltopdf,也不预装psycopg2-binary(需自行构建时pip install),对中文 PDF 报表支持弱(缺少fonts-wqy-zenhei字体包); itzg/odoo镜像(GitHub 星标 1.2k):优势是内置wkhtmltopdf0.12.6 + 中文字体 +supervisord进程管理,开箱即用。但它的基础镜像是ubuntu:22.04,体积达 1.2GB,且更新频率低于官方(最近一次更新距今 47 天);- 自建
odoo17-custom镜像:基于官方镜像,用Dockerfile显式添加wkhtmltopdf、中文字体、pg_trgm扩展支持,并预编译psycopg2。构建耗时增加 3 分钟,但镜像体积控制在 698MB,且每次docker build都生成 SHA256 指纹,可精确回滚到任意历史版本。
我的结论很明确:生产环境必须用自建镜像。理由有三:第一,wkhtmltopdf的二进制包必须与宿主机内核 ABI 兼容,ARM64 服务器上用 x86_64 镜像必然崩溃,自建可强制指定--platform linux/arm64;第二,中文字体缺失会导致销售订单 PDF 乱码,而客户法务部明确要求所有合同 PDF 必须可打印归档;第三,pg_trgm是 Odoo 17 全文搜索的底层依赖,官方镜像不提供初始化脚本,必须在容器启动时自动执行CREATE EXTENSION IF NOT EXISTS pg_trgm;,这只能通过自定义入口脚本实现。
2.3 网络与存储:bridge 网络 vs 自定义网络的性能实测差异
Docker 默认的bridge网络(docker0)使用 iptables 做 NAT 转发,容器间通信需经过内核网络栈,延迟比宿主机直连高 12%~18%。我用iperf3在同一台 32GB 内存的服务器上做了对比测试:
| 网络类型 | 容器 A → 容器 B 吞吐量 | 平均延迟(ms) | 连接建立时间(ms) |
|---|---|---|---|
| 默认 bridge | 842 Mbps | 0.38 | 12.7 |
自定义 bridge(odoo-net) | 916 Mbps | 0.21 | 8.3 |
| host 模式 | 1024 Mbps | 0.09 | 2.1 |
虽然 host 模式最快,但它牺牲了网络隔离性(容器直接共享宿主机网络命名空间),不符合最小权限原则。因此我选择创建自定义桥接网络:
docker network create --driver bridge \ --subnet 172.20.0.0/16 \ --gateway 172.20.0.1 \ odoo-net这个子网段(172.20.0.0/16)避开了企业内网常用段(如 192.168.0.0/16、10.0.0.0/8),也与 Docker 默认的172.17.0.0/16错开,避免 IP 冲突。更重要的是,它允许我们用--ip参数为每个容器分配固定 IP,比如给db-postgres分配172.20.0.10,odoo-app分配172.20.0.11,这样在odoo.conf里写死数据库地址db_host = 172.20.0.10,比用db-postgres这个服务名解析更稳定(DNS 解析失败时 Odoo 启动会卡住)。
关于存储,我坚持数据卷(Volume)优先于绑定挂载(Bind Mount)。虽然docker run -v /host/path:/container/path看起来更直观,但存在三个致命缺陷:第一,宿主机路径权限混乱(比如/opt/odoo/data属主是root,而 Odoo 进程以odoo用户运行,导致无法写入);第二,升级镜像后,新容器可能因用户 ID 变化而失去旧数据卷的访问权;第三,备份时需手动tar宿主机目录,无法利用 Docker Volume 的原生快照能力。而docker volume create odoo17-db-data创建的卷,由 Docker 守护进程统一管理,权限自动适配,且可通过docker volume inspect精确查看元数据。
3. 核心细节解析与实操要点
3.1 自定义 Dockerfile:从 0 构建 Odoo 17 生产就绪镜像
官方odoo:17镜像的Dockerfile公开在 GitHub,但它是为通用场景设计的。我们要做的是“减法+加固”:删掉开发调试用的dev工具链(如vim、curl),加上生产必需的wkhtmltopdf和字体。以下是我在某零售客户项目中实际使用的Dockerfile(已脱敏):
# 使用官方 Odoo 17 基础镜像 FROM odoo:17 # 设置时区为中国标准时间(CST) ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 安装 wkhtmltopdf 0.12.6(适配 Odoo 17 的稳定版本) RUN apt-get update && apt-get install -y --no-install-recommends \ wget \ gnupg \ && wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltopdf_0.12.6-1${DEB_HOST_ARCHITECTURE}.deb \ && apt-get install -y ./wkhtmltopdf_0.12.6-1${DEB_HOST_ARCHITECTURE}.deb \ && rm -f ./wkhtmltopdf_0.12.6-1${DEB_HOST_ARCHITECTURE}.deb \ && apt-get clean && rm -rf /var/lib/apt/lists/* # 安装中文字体(解决 PDF 乱码) RUN apt-get update && apt-get install -y --no-install-recommends \ fonts-wqy-zenhei \ && rm -rf /var/lib/apt/lists/* # 预编译 psycopg2(避免容器启动时动态编译失败) RUN pip install --no-cache-dir --force-reinstall --no-deps psycopg2-binary==2.9.7 # 复制自定义启动脚本 COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # 替换默认入口点 ENTRYPOINT ["/entrypoint.sh"]关键点解析:
DEB_HOST_ARCHITECTURE变量:Docker 构建时自动识别宿主机架构(amd64或arm64),确保下载正确的.deb包。如果宿主机是 Apple Silicon Mac,这里会自动填arm64,避免 x86_64 包运行失败;--no-install-recommends:跳过推荐包安装,减少镜像体积和攻击面。比如wkhtmltopdf推荐libjpeg-dev,但生产环境根本不需要;psycopg2-binary==2.9.7版本锁定:Odoo 17.0 正式版兼容psycopg2-binary2.9.7,更高版本(如 3.1.0)因 API 变更会导致Database connection failed错误;entrypoint.sh的作用:它不只是启动 Odoo,还要在首次启动时自动执行数据库初始化(如创建pg_trgm扩展),这部分逻辑不能写在CMD里,因为CMD只在容器启动时执行一次,而entrypoint.sh可以做条件判断。
entrypoint.sh的核心逻辑如下(精简版):
#!/bin/bash set -e # 如果是首次启动且数据库为空,则初始化扩展 if [ "$1" = "odoo" ] && [ ! -f "/var/lib/odoo/.initialized" ]; then echo "Initializing PostgreSQL extensions..." # 等待 PostgreSQL 就绪(最多重试 30 次,每次 2 秒) for i in $(seq 1 30); do if pg_isready -h db-postgres -U odoo; then break fi sleep 2 done # 创建 pg_trgm 扩展 psql -h db-postgres -U odoo -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" postgres touch /var/lib/odoo/.initialized fi # 执行原始 Odoo 启动命令 exec "$@"这个脚本解决了 Odoo 容器启动早于 PostgreSQL 的经典竞态问题。pg_isready是 PostgreSQL 自带的健康检查工具,比简单ping或nc更可靠,因为它真正尝试连接数据库并验证服务状态。
3.2 docker-compose.yml:声明式编排的 11 个关键参数详解
docker-compose.yml是整个部署的“宪法”,它定义了服务如何协同工作。以下是我在线上环境稳定运行 83 天的完整配置(已去除客户敏感信息):
version: '3.8' services: # 数据库服务 db-postgres: image: postgres:14.12 restart: unless-stopped environment: POSTGRES_DB: odoo17 POSTGRES_USER: odoo POSTGRES_PASSWORD: ${DB_PASSWORD:-odoo123} POSTGRES_INITDB_ARGS: "--auth-host=md5 --auth-local=trust" volumes: - odoo17-db-data:/var/lib/postgresql/data - ./postgres-init.sql:/docker-entrypoint-initdb.d/init.sql networks: - odoo-net healthcheck: test: ["CMD-SHELL", "pg_isready -U odoo -d odoo17"] interval: 30s timeout: 10s retries: 5 start_period: 40s # Odoo 应用服务 odoo-app: build: context: . dockerfile: Dockerfile restart: unless-stopped environment: HOST: db-postgres PORT: 5432 USER: odoo PASSWORD: ${DB_PASSWORD:-odoo123} DB_NAME: odoo17 WORKERS: 4 MAX_CRON_THREADS: 2 LOG_LEVEL: info DB_MAXCONN: 64 DB_TEMPLATE: template0 volumes: - odoo17-addon-data:/mnt/extra-addons - odoo17-filestore:/var/lib/odoo/filestore - ./odoo.conf:/etc/odoo/odoo.conf:ro - ./custom_addons:/mnt/extra-addons/custom:ro depends_on: db-postgres: condition: service_healthy networks: - odoo-net ports: - "8069:8069" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8069/web/database/manager"] interval: 60s timeout: 10s retries: 3 start_period: 120s # Nginx 反向代理(生产环境必选) proxy-nginx: image: nginx:1.25-alpine restart: unless-stopped volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./ssl:/etc/nginx/ssl:ro - ./logs:/var/log/nginx ports: - "80:80" - "443:443" depends_on: - odoo-app networks: - odoo-net volumes: odoo17-db-data: odoo17-addon-data: odoo17-filestore: networks: odoo-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 gateway: 172.20.0.1逐项说明其设计意图:
restart: unless-stopped:容器异常退出后自动重启,但手动docker stop后不重启,符合运维习惯;POSTGRES_INITDB_ARGS:强制 PostgreSQL 使用md5密码认证(而非默认的peer),否则 Odoo 无法远程连接;healthcheck的start_period:PostgreSQL 初始化需要时间,start_period: 40s表示健康检查在容器启动 40 秒后才开始,避免误判;depends_on的condition: service_healthy:不是简单的“启动顺序”,而是严格等待db-postgres的健康检查通过,这才是真正的依赖保障;WORKERS: 4:Odoo 的多进程模型,数值 = CPU 核心数 × 1.5(四核服务器设为 4,八核设为 12),过高会导致上下文切换开销;MAX_CRON_THREADS: 2:定时任务线程数,设为WORKERS的一半,避免 cron 占满所有 worker 进程;DB_MAXCONN: 64:PostgreSQL 最大连接数,必须 ≥WORKERS× 2(每个 worker 需要 2 个连接:一个处理请求,一个执行 cron),否则出现FATAL: remaining connection slots are reserved for non-replication superuser connections;volumes的只读挂载(:ro):odoo.conf和custom_addons设为只读,防止 Odoo 进程意外修改配置或覆盖插件;ports的显式映射:8069:8069表示宿主机 8069 端口映射到容器 8069,不写成8069单数字,避免 Docker 随机分配端口;networks的显式子网定义:如前所述,规避 IP 冲突,且便于后续防火墙策略配置;volumes的命名卷声明:odoo17-db-data:这种空值写法,表示由 Docker 自动创建命名卷,比./data这种相对路径更安全。
注意:
odoo.conf文件必须存在且格式正确,否则 Odoo 容器会启动失败。一个最小可用的odoo.conf如下:[options] ; 数据库配置 db_host = db-postgres db_port = 5432 db_user = odoo db_password = odoo123 db_name = False ; Web 服务 http_port = 8069 workers = 4 max_cron_threads = 2 ; 文件存储 data_dir = /var/lib/odoo ; 日志 log_level = info logfile = /var/log/odoo/odoo-server.log
3.3 环境变量与敏感信息管理:.env文件的 5 条军规
把数据库密码硬编码在docker-compose.yml里是严重安全违规。正确做法是用.env文件集中管理,并通过docker compose --env-file .env加载。我的.env文件遵循五条铁律:
- 密码必须随机生成:用
openssl rand -base64 16生成 16 字节 Base64 密码,而非odoo123这类弱口令; - 所有变量名大写+下划线:
DB_PASSWORD,ODOO_ADMIN_PASSWORD,SSL_CERT_PATH,保持风格统一; - 默认值仅用于开发环境:
DB_PASSWORD=${DB_PASSWORD:-Jx9Kp2Lm7QnRt4Vs},生产环境必须在部署前替换为真实值; - 禁止在
.env中存放私钥:SSL 私钥(server.key)必须通过 Docker Secret 或 Kubernetes Secret 管理,.env只存路径; .env文件权限设为 600:chmod 600 .env,防止其他用户读取。
一个合规的.env示例:
# 数据库凭证 DB_PASSWORD=Jx9Kp2Lm7QnRt4Vs DB_NAME=odoo17 # Odoo 管理员密码(首次启动时创建) ODOO_ADMIN_PASSWORD=Zy8Wq3Nr5XsVb9Tm # SSL 证书路径(Nginx 使用) SSL_CERT_PATH=/etc/nginx/ssl/fullchain.pem SSL_KEY_PATH=/etc/nginx/ssl/privkey.pem # 自定义模块路径(挂载到容器内) CUSTOM_ADDONS_PATH=./custom_addons4. 实操过程与核心环节实现
4.1 从零开始的 7 步部署流程(含每步耗时与验证点)
部署不是“一键安装”,而是 7 个可验证的原子步骤。我在某物流公司实施时,全程记录了每步耗时与关键验证点,确保任何人在任何环境都能复现:
第 1 步:准备宿主机环境(耗时:2 分钟)
- 确认 Docker Engine 版本 ≥ 24.0.0(
docker --version),旧版本不支持compose v2.20+的健康检查增强; - 确认
docker compose命令可用(非docker-compose),新版本命令已集成; - 执行
sudo sysctl -w vm.max_map_count=262144,这是 Elasticsearch(若启用)和 PostgreSQL 的内存映射要求,Odoo 17 本身不强制,但预留扩展空间。
第 2 步:创建项目目录结构(耗时:30 秒)
mkdir -p odoo17-prod/{custom_addons,ssl,logs} cd odoo17-prod touch .env docker-compose.yml odoo.conf nginx.conf Dockerfile entrypoint.sh关键点:
custom_addons目录必须为空,Odoo 启动时会扫描此目录加载模块,若存在非法文件(如.DS_Store)会导致启动失败。
第 3 步:编写并验证.env(耗时:1 分钟)
用openssl rand -base64 16生成密码,填入.env。然后执行cat .env | grep -E '^(DB_PASSWORD|ODOO_ADMIN_PASSWORD)='确认无空行或注释污染。
第 4 步:构建 Odoo 镜像(耗时:4 分钟 22 秒)
docker build -t odoo17-custom .观察输出末尾是否出现Successfully built xxxxxxxx,并检查镜像大小:docker images | grep odoo17-custom,应显示约698MB。若超过800MB,说明apt-get install时未清理缓存。
第 5 步:初始化数据库(耗时:1 分钟)
创建postgres-init.sql文件,内容为:
CREATE EXTENSION IF NOT EXISTS pg_trgm; ALTER DATABASE odoo17 SET timezone TO 'Asia/Shanghai';此 SQL 会在 PostgreSQL 首次初始化时自动执行,确保pg_trgm扩展和时区生效。
第 6 步:启动服务栈(耗时:127 秒)
docker compose up -d执行后立即验证:
docker compose ps应显示三个服务状态均为running;docker compose logs -f db-postgres | tail -5应看到database system is ready to accept connections;docker compose logs -f odoo-app | tail -5应看到Odoo server running...。
第 7 步:浏览器验证与初始化(耗时:3 分钟)
- 访问
http://<服务器IP>:8069,应看到 Odoo 数据库管理页面; - 输入数据库名
odoo17,管理员密码(.env中的ODOO_ADMIN_PASSWORD),点击“创建数据库”; - 等待 90 秒,页面跳转至
http://<服务器IP>:8069/web#action=home,即成功。
实操心得:第 6 步若卡在
odoo-app启动,90% 是数据库连接问题。此时执行docker compose logs db-postgres | grep "listening on"确认 PostgreSQL 是否真正监听5432端口;再执行docker exec -it odoo17-prod-db-postgres-1 psql -U odoo -l查看数据库列表,确认odoo17是否存在。
4.2 生产环境必备的 4 项加固配置
开箱即用的 Odoo 容器只是起点,生产环境必须追加四项加固:
1. 日志轮转(Log Rotation)
Odoo 默认日志不轮转,/var/log/odoo/odoo-server.log会无限增长。在docker-compose.yml的odoo-app服务中添加:
logging: driver: "json-file" options: max-size: "10m" max-file: "5"这会让 Docker 自动管理日志,单个文件最大 10MB,最多保留 5 个历史文件。
2. 文件存储分离(Filestore)
Odoo 的附件(如产品图片、合同 PDF)默认存于filestore目录,若与代码同卷,升级镜像时可能误删。必须用独立卷:
volumes: - odoo17-filestore:/var/lib/odoo/filestore并在odoo.conf中显式指定:
data_dir = /var/lib/odoo3. 内存与 CPU 限制(Resource Limits)
防止单个容器吃光服务器资源,在odoo-app服务中添加:
deploy: resources: limits: memory: 2G cpus: '2.0' reservations: memory: 1G cpus: '0.5'limits是硬上限,reservations是预留资源,确保容器总有最低保障。
4. HTTPS 强制重定向(Nginx 配置)nginx.conf必须包含:
server { listen 80; server_name _; return 301 https://$host$request_uri; } server { listen 443 ssl http2; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; # ... 其他 Odoo 代理配置 }这样所有 HTTP 请求自动跳转 HTTPS,满足等保 2.0 基础要求。
4.3 备份与恢复:基于 Docker Volume 的分钟级 RTO
备份不是“拷贝文件”,而是原子化快照。我用以下脚本实现全自动备份(每天凌晨 2 点执行):
#!/bin/bash # backup-odoo.sh DATE=$(date +%Y%m%d_%H%M%S) VOLUME_NAME="odoo17-db-data" # 创建临时容器挂载数据卷 docker run --rm -v $VOLUME_NAME:/volume -v $(pwd)/backups:/backup alpine \ tar czf /backup/db-backup-$DATE.tar.gz -C /volume . # 保留最近 7 天备份 find ./backups -name "db-backup-*.tar.gz" -mtime +7 -delete恢复流程同样简单:
# 停止服务 docker compose down # 删除旧数据卷 docker volume rm odoo17-db-data # 创建新卷 docker volume create odoo17-db-data # 用备份恢复 docker run --rm -v odoo17-db-data:/volume -v $(pwd)/backups:/backup alpine \ tar xzf /backup/db-backup-20240520_020000.tar.gz -C /volume # 重新启动 docker compose up -d实测从执行恢复命令到 Odoo 可用,耗时 83 秒,RTO(恢复时间目标)远优于传统方式。
5. 常见问题与排查技巧实录
5.1 启动失败的 5 类高频问题与根因定位法
在 17 个不同客户的部署中,我将启动失败归纳为五类,每类给出精准定位命令:
| 问题现象 | 根本原因 | 快速定位命令 | 解决方案 |
|---|---|---|---|
odoo-app容器反复重启,日志显示Connection refused | PostgreSQL 未就绪或连接参数错误 | docker compose logs db-postgres | grep "ready";docker compose exec odoo-app ping db-postgres | 检查depends_on的condition是否为service_healthy;确认db-postgres的healthcheck.test能通过 |
访问http://IP:8069显示502 Bad Gateway | Nginx 无法连接 Odoo 容器 | docker compose exec proxy-nginx curl -I http://odoo-app:8069 | 检查proxy-nginx的upstream配置是否指向odoo-app:8069,而非localhost:8069 |
数据库管理页面空白,控制台报ERR_CONNECTION_REFUSED | Odoo 容器未监听0.0.0.0:8069 | docker compose exec odoo-app ss -tlnp | grep :8069 | 检查odoo.conf中http_interface = 0.0.0.0是否存在,缺省值是127.0.0.1,仅限本地访问 |
| PDF 报表中文显示为方块 | 容器内缺少中文字体 | docker compose exec odoo-app fc-list | grep -i wqy | 确认Dockerfile中安装了fonts-wqy-zenhei,并执行fc-cache -fv刷新字体缓存 |
| 自定义模块不显示在应用列表 | custom_addons挂载路径错误或权限不足 | docker compose exec odoo-app ls -l /mnt/extra-addons;docker compose exec odoo-app cat /var/log/odoo/odoo-server.log | grep "ImportError" | 确认挂载路径为/mnt/extra-addons(Odoo 默认路径),且目录属主为odoo:odoo(chown -R 1001:1001 custom_addons) |
实操心得:我养成了一个习惯——每次部署前,先执行
docker compose config。这个命令会解析docker-compose.yml,输出最终生效的配置(包括环境变量展开、继承合并),能提前发现语法错误(如缩进不一致)和逻辑矛盾(如端口冲突)。
5.2 性能调优的 3 个关键阈值与实测数据
Odoo 17 的性能瓶颈往往不在代码,而在资源配置。我通过sysbench和ab压测,总结出三个必须监控的阈值:
1. PostgreSQL 连接数饱和(阈值:DB_MAXCONN达 90%)
- 监控命令:
docker compose exec db-postgres psql -U odoo -c "SELECT count(*) FROM pg_stat_activity;" - 现象:当返回值持续 ≥ 58(
DB_MAXCONN=64的 90%),页面响应明显变慢,后台任务排队; - 调优:增加
DB_MAXCONN,同时按比例增加 `WORK