news 2026/10/27 4:19:12

Odoo 17 Docker 生产部署:三容器架构与镜像定制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Odoo 17 Docker 生产部署:三容器架构与镜像定制实战

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 终止与多租户路由)。这种设计不是为了炫技,而是基于四个刚性约束:

  1. 故障域隔离:PostgreSQL 崩溃不会导致 Odoo 进程退出,反之亦然;
  2. 升级弹性:升级 Odoo 版本只需重建odoo-app容器,数据库容器完全不动;
  3. 资源可控:可单独为db-postgres分配 2GB 内存限制,避免其吃光服务器资源;
  4. 合规审计:数据库密码、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)
默认 bridge842 Mbps0.3812.7
自定义 bridge(odoo-net)916 Mbps0.218.3
host 模式1024 Mbps0.092.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

逐项说明其设计意图:

  1. restart: unless-stopped:容器异常退出后自动重启,但手动docker stop后不重启,符合运维习惯;
  2. POSTGRES_INITDB_ARGS:强制 PostgreSQL 使用md5密码认证(而非默认的peer),否则 Odoo 无法远程连接;
  3. healthcheck的start_period:PostgreSQL 初始化需要时间,start_period: 40s表示健康检查在容器启动 40 秒后才开始,避免误判;
  4. depends_on的condition: service_healthy:不是简单的“启动顺序”,而是严格等待db-postgres的健康检查通过,这才是真正的依赖保障;
  5. WORKERS: 4:Odoo 的多进程模型,数值 = CPU 核心数 × 1.5(四核服务器设为 4,八核设为 12),过高会导致上下文切换开销;
  6. MAX_CRON_THREADS: 2:定时任务线程数,设为WORKERS的一半,避免 cron 占满所有 worker 进程;
  7. DB_MAXCONN: 64:PostgreSQL 最大连接数,必须 ≥WORKERS× 2(每个 worker 需要 2 个连接:一个处理请求,一个执行 cron),否则出现FATAL: remaining connection slots are reserved for non-replication superuser connections;
  8. volumes的只读挂载(:ro):odoo.conf和custom_addons设为只读,防止 Odoo 进程意外修改配置或覆盖插件;
  9. ports的显式映射:8069:8069表示宿主机 8069 端口映射到容器 8069,不写成8069单数字,避免 Docker 随机分配端口;
  10. networks的显式子网定义:如前所述,规避 IP 冲突,且便于后续防火墙策略配置;
  11. 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文件遵循五条铁律:

  1. 密码必须随机生成:用openssl rand -base64 16生成 16 字节 Base64 密码,而非odoo123这类弱口令;
  2. 所有变量名大写+下划线:DB_PASSWORD,ODOO_ADMIN_PASSWORD,SSL_CERT_PATH,保持风格统一;
  3. 默认值仅用于开发环境:DB_PASSWORD=${DB_PASSWORD:-Jx9Kp2Lm7QnRt4Vs},生产环境必须在部署前替换为真实值;
  4. 禁止在.env中存放私钥:SSL 私钥(server.key)必须通过 Docker Secret 或 Kubernetes Secret 管理,.env只存路径;
  5. .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_addons

4. 实操过程与核心环节实现

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/odoo

3. 内存与 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 refusedPostgreSQL 未就绪或连接参数错误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 GatewayNginx 无法连接 Odoo 容器docker compose exec proxy-nginx curl -I http://odoo-app:8069检查proxy-nginx的upstream配置是否指向odoo-app:8069,而非localhost:8069
数据库管理页面空白,控制台报ERR_CONNECTION_REFUSEDOdoo 容器未监听0.0.0.0:8069docker 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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 4:18:23

Qt QTest单元测试实战:骨架、断言、数据驱动与CI接入避坑

简介&#xff1a;这份压缩包面向使用 Qt 进行 C 开发、希望系统补齐单元测试实践的开发者&#xff0c;核心是用 QTest 框架演示普通应用类与 DLL 内部非导出类方法的测试思路&#xff0c;覆盖测试用例设计、数据驱动输入和断言校验等关键环节。示例按普通应用、单元测试与简单 …

作者头像 李华
网站建设 2026/10/27 4:18:00

IPv4编址与静态路由实验:从地址规划到三层可达性验证

1. 实验整体设计与思路拆解1.1 这个实验到底在练什么IPv4编址和IPv4路由基础&#xff0c;这两个词摆在一起&#xff0c;很多人第一反应是"不就是配个IP地址、写条静态路由嘛"。但我带过不少刚接触网络方向的朋友&#xff0c;真正动手的时候&#xff0c;十个里有八个会…

作者头像 李华
网站建设 2026/10/27 4:17:02

impeccable用法全解析:从词源到地道搭配与常见误区

impeccable这个词&#xff0c;我在英语学习和实际使用中琢磨了很久。很多人第一次见到它&#xff0c;背的是"完美的"&#xff0c;然后就不管了。但真正到了写作、口语、甚至商务场合要用的时候&#xff0c;往往用得不够味&#xff0c;甚至用错地方。这个词不是简单的…

作者头像 李华
网站建设 2026/10/27 4:16:04

Ponytail微组件:轻量级非模态浮层设计与原生实现

1. 项目概述&#xff1a;从“ponytail”热词切入&#xff0c;搞懂它到底是什么、能做什么、适合谁用最近刷技术社区、插件市场甚至设计工具讨论区&#xff0c;“ponytail”这个词高频出现&#xff0c;尤其在搜索“插件 ponytail 如何使用”时&#xff0c;结果里混杂着UI组件库文…

作者头像 李华
网站建设 2026/10/27 4:15:51

AgentLink:跨AI模型的轻量级本地通信协议设计

1. 这不是“联机对战”&#xff0c;而是让两个AI模型真正协作的底层通信设计你搜“Claude Code 和 Codex 怎么一起用”&#xff0c;刷出来的全是零散配置教程、报错截图、安装失败日志&#xff0c;甚至还有人把“cc switch local proxy failed while handling codex endpoint /…

作者头像 李华
网站建设 2026/10/27 4:15:50

二叉树数据结构:核心概念与C语言实现详解

1. 二叉树基础概念与核心操作二叉树是每个节点最多有两个子节点的树结构&#xff0c;这种数据结构在计算机科学中有着广泛的应用。我们先从最基础的部分开始&#xff0c;逐步深入理解二叉树的各项操作。1.1 二叉树的基本结构二叉树由节点(Node)组成&#xff0c;每个节点包含三个…

作者头像 李华