首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Odoo SaaS Kit 实战:用 Docker 实现独立容器多租户架构
📅 2026/9/25 10:34:22
✍️ 爱科研究院
👁 阅读 3,247
简介Odoo SaaS Kit是一套基于Docker的多租户Odoo实例管理方案通过容器化技术将每个客户独立成实例支持同服务器或远程部署有效简化企业级Odoo服务的搭建与运维。这份PDF文档共一个文件大小约3.99MB系统讲解了从安装Python库docker、erppeek、paramiko到创建基础Docker镜像、配置Nginx虚拟主机与PostgreSQL的完整流程同时指导读者创建Odoo-SAAS-Data、docker_vhosts、common_addons等目录并调整Odoo用户权限授权其执行Docker命令及控制Nginx。文档深入介绍了配置SaaS服务器、设计订阅计划计费周期、试用期、模块列表等操作并专门列出模块上传、域名管理、日志查看、备份恢复及重启客户端进程等常见问题的解决方法。目前已有139人学习下载适合具备一定服务器管理和Odoo使用经验、面向企业级部署与运维的IT专业人员作为搭建多租户Odoo环境的实用参考。1. 用 Docker 给每个客户开独立 OdooSaaS Kit 到底解决什么问题做 Odoo 多租户最常见的两个路子是改多公司模块或者让多个数据库共用一套代码。前者隔离性差客户数据混在一个库里出问题谁都跑不掉后者的确是多数 SaaS 产品的做法但遇到客户要装不同模块、不同版本插件时就非常痛苦。Odoo SaaS Kit 走的是第三条路用 Docker 为每个客户创建独立的 Odoo 实例容器客户之间不但数据库隔开连运行环境都隔开。主服务器还是一个 Odoo 后台但每个客户实例是单独的容器有独立的 odoo.conf、独立插件目录、独立 Nginx 虚拟主机甚至可以把容器开到远程服务器上。这套东西适合有服务器管理经验、打算把 Odoo 包装成订阅制产品来卖的团队不只会装 Odoo还得玩得转 Docker 和 Nginx 的基本操作。2. 架构先立住目录结构、配置文件与 saas.conf 的五个关键路径2.1 三个目录的分工与所有权问题odoo_saas_kit 模块安装成功后第一步不是急着创建客户实例而是把目录结构搭起来。模块依赖的目录有三个分工完全不同。Odoo-SAAS-Data 是所有 SaaS 客户端文件的大本营每个客户端的 odoo.conf、数据目录、Nginx 虚拟主机配置都放在这个目录下面按客户分目录管理。docker_vhosts 是 Nginx 虚拟主机配置的存放处模块运行时会在这里为每个客户端生成独立的配置文件实现一个客户一个子域名。common_addons 则是所有客户端和模板共享的插件路径放在这里的模块对所有实例可见。# 假设您的 Odoo 安装在 /opt/odoo mkdir -p /opt/odoo/Odoo-SAAS-Data/docker_vhosts mkdir -p /opt/odoo/common_addons chown -R odoo:odoo /opt/odoo/Odoo-SAAS-Data chown -R odoo:odoo /opt/odoo/common_addons目录建好后立刻改所有权这一步别偷懒。odoo_saas_kit 模块是以 Odoo 服务用户的身份去读写这些目录的如果目录所有者是 root后面创建客户端容器时会直接报权限错误而且报错位置很隐蔽——不是在 Odoo 日志里而是在 Docker 挂载卷时静默失败。2.2 配置文件族模板容器与客户端容器的分野系统里有三个基础配置文件全部放在 Odoo-SAAS-Data 下。odoo.conf 是 SaaS 客户端的默认 Odoo 配置创建客户端容器时以它为蓝本生成每个客户端自己的配置文件。odoo-template.conf 是 SaaS 模板的配置模板容器用来预装模块、生成数据库模板之后再从这个模板克隆出客户端数据库。vhosttemplate.txt 是 Nginx 虚拟主机的模板模块根据这个文件为每个客户端生成虚拟主机配置。# 初始配置文件只需放置一次后续客户端配置由模块自动生成 ls -la /opt/odoo/Odoo-SAAS-Data/ -rw-r----- 1 odoo odoo 1024 odoo.conf -rw-r----- 1 odoo odoo 1024 odoo-template.conf drwxr-xr-x 2 odoo odoo 4096 docker_vhosts/注意这些文件的属主也必须是 Odoo 用户否则模块在读取模板时会提示权限不足。另外docker_vhosts 目录内生成的每个客户端虚拟主机文件都是文本文件如果您的团队后续要手动调整 Nginx 配置操作前先备份改坏一个 include 文件会导致所有客户端站点无法访问。2.3 saas.conf 参数逐个说saas.conf 位于 odoo_saas_kit/models/lib/ 目录下是整个模块的中枢配置文件。模块创建容器时读这里的路径和参数写错一个客户端实例就起不来。这个文件建议安装后第一时间打开检查。参数示例值作用nginx_vhosts/opt/odoo/Odoo-SAAS-Data/docker_vhosts/Nginx 虚拟主机配置文件的生成路径odoo_saas_data/opt/odoo/Odoo-SAAS-Data/客户端数据目录的根路径common_addons/opt/odoo/common_addons共享插件路径会挂载到每个容器odoo_imageodoobywebkul:12.0创建客户端容器时使用的 Docker 镜像template_odoo_port8888模板容器占用的端口不能与其他程序冲突# 打开 saas.conf 修改路径路径示例 nginx_vhosts /opt/odoo/Odoo-SAAS-Data/docker_vhosts/ odoo_saas_data /opt/odoo/Odoo-SAAS-Data/ common_addons /opt/odoo/common_addons odoo_image odoobywebkul:12.0 template_odoo_port 8888这里有个容易忽略的点odoo_image 参数写的是镜像名加标签如果您的构建命令打的 tag 和这里不一致创建客户端时会报「镜像不存在」。我一般在配置文件里写全名比如odoo_saas_kit_base:12.0构建镜像时保持一致避免多个 Odoo 版本共存时搞混。template_odoo_port 是指模板容器占用的端口它只需要一个客户端容器端口由模块自动分配不用手工规划端口段。3. 构建基础镜像与授权uid/gid 不一致是第一个翻车点3.1 为什么主机和容器内的 uid/gid 必须一致这一节是整个部署过程中最容易被新手忽略、也最容易翻车的点。odoo_saas_kit 的工作方式是主机上维护所有客户端的数据目录和配置文件容器运行时把这些目录挂载进去。挂载卷有一个硬性规定——容器内进程对挂载目录的访问权限取决于目录在主机上的属主和容器的用户映射。如果您的 Odoo 服务用户在主机上是 uid 1001而 Docker 镜像里的 odoo 用户是 uid 101那么容器内的 Odoo 进程访问挂载进来的数据目录时会被系统判定为「其他用户」读写操作全部返回 Permission denied。整个客户端实例能启动但访问页面时报错、session 无法写入、文件上传失败日志里全是权限相关的异常。# 查看主机上 Odoo 服务用户的 uid 和 gid grep odoo /etc/passwd # 输出示例odoo:x:1001:1001::/home/odoo:/bin/bash # 记住这里的 1001 1001Dockerfile 里要用确认 uid/gid 后把这两个数值写进 Dockerfile。基础镜像官方 odoo 镜像里 odoo 用户是 uid 101需要重新构建一个基础镜像来覆盖它。注意不是修改运行中的容器而是改 Dockerfile 后重新 build——所有客户端容器都从这一个镜像创建改容器没用重启就丢。3.2 Dockerfile 构建命令与参数说明官方提供的压缩包里包含 Dockerfile、entrypoint.sh、run_odoo.sh 等文件。构建时重点看两个位置Dockerfile 里 USER odoo 之前的 useradd 或 usermod 指令以及 entrypoint.sh 里是否有依赖固定 uid 的逻辑。# 切换到放 Dockerfile 的目录 cd {your_docker_file_path} # 构建基础镜像注意结尾的点号不能省 docker build -t odoobywebkul:12.0 .构建命令的 -t 参数指定镜像名和标签建议直接命名为 saas.conf 里 odoo_image 参数的值这样后面不用改配置。构建过程中如果报一些 apt 源连接超时常见原因是国内网络访问 Ubuntu 官方源慢换成国内源再重试。构建完成后用docker images确认镜像存在顺便检查一下镜像内 odoo 用户的 uid 是否和主机一致。# 运行一个临时容器验证 uid docker run --rm odoobywebkul:12.0 id odoo # 期望输出 uid1001(odoo) gid1001(odoo)这里多花一分钟验证能省下后面创建十来个客户端时反复排查的时间。镜像内 uid 和主机 uid 不一致的后果是客户端容器能创建出来但数据写不进挂载目录日志刷一堆 Permission denied定位问题要花半天。3.3 Odoo 用户的四个附加权限镜像构建好之后还要给 Odoo 服务用户开四个权限缺一个模块就跑不到业务闭环。第一个是允许 Odoo 用户执行 Docker 命令odoo_saas_kit 模块是通过 Docker Python API 来创建和管理容器的本质上是在 Apache 的 worker 进程里调 docker-py所以执行用户必须能访问 Docker 守护进程。# 将 Odoo 用户加入 docker 组 usermod -a -G docker {your_odoo_user}这里说明一下加入 docker 组相当于给这个用户 root 权限因为 docker 组用户可以直接操作宿主机上的所有容器和镜像。如果您的服务器上还有别的业务跑在 Docker 里建议给 Odoo 用户单独建一台机器或者至少限制它的权限。# 允许 Odoo 用户免密执行 nginx 命令追加到 /etc/sudoers # 手动编辑或使用 visudo visudo # 追加一行 {your_odoo_user} ALL(ALL) NOPASSWD:/usr/sbin/nginxsudoers 配好后测试一下sudo -u odoo /usr/sbin/nginx -t如果能正常输出语法检查结果说明配置生效。第三个权限是在 Nginx 主配置中加入虚拟主机目录的 include否则模块动态生成的客户端站点配置不会生效。# 在 nginx.conf 的 http 块中追加 include /opt/odoo/Odoo-SAAS-Data/docker_vhosts/*.conf;第四个权限是 PostgreSQL 连接权限。客户端容器和模板容器都需要连接一个 PostgreSQL 服务器来创建和管理数据库。如果 PostgreSQL 和 Odoo 不在同一台机器上需要修改 postgresql.conf 和 pg_hba.conf 允许 Odoo 应用服务器所在网段的连接。# pg_hba.conf 追加典型场景Odoo 在 172.17.0.0/16 网段 host all all 172.17.0.0/16 md5 host all all {odoo_server_private_ip}/32 md5这几个权限的顺序建议先做 sudoers 和 include再做 docker 组最后配 PostgreSQL。全部配置完后再重启 Nginx 和 Docker 服务确保配置文件加载无误。4. 远程服务器部署SSH、2375 端口与镜像搬运的完整流程4.1 远程服务器的用户体系必须与主服务器对齐如果您的客户规模超出了单台服务器的承载能力可以把客户端容器部署到远程服务器上。远程服务器的配置流程和主服务器大部分相同但有三个关键区别。第一个区别是远程服务器上的 Odoo 系统用户uid 和 gid 必须与主服务器上的 Odoo 用户完全一致因为客户端数据目录仍然由主服务器模块管理远程容器挂载的数据要和主服务器共享。# 在远程服务器上创建与主服务器 uid/gid 一致的 Odoo 用户 adduser --system --home /opt/{your_odoo_user} \ --shell /bin/bash --uid {odoo_user_uid} --gid {odoo_user_gid} \ {your_odoo_user}这里的设计意图很明确odoo_saas_kit 模块通过 paramiko 走 SSH 连接远程服务器模块代码会在远程服务器上按相同的目录结构维护数据。如果两边的 uid/gid 不一致主服务器上创建的目录在远程容器内因为权限不匹配会出现奇怪的挂载失败或者文件写入异常。4.2 暴露 Docker API 和 SSH 认证配置第二个区别是需要让远程 Docker 守护进程监听 TCP 端口这样主服务器的 docker-py 库才能跨机器创建容器。默认的 Docker 守护进程监听本机 socket不修改的话远程调用会直接连接失败。# 修改 /lib/systemd/system/docker.service 文件中的 ExecStart 行 ExecStart/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 # 重载 systemd 并重启 Docker systemctl daemon-reload service docker restart改完后用ss -tlnp | grep 2375确认端口监听状态。这是一把双刃剑把 Docker API 暴露到网络上后任意能访问这个端口的人都能操作您的 Docker 守护进程建议立即配置防火墙规则仅允许主服务器的 IP 访问 2375 端口。# 示例只允许主服务器 IP 访问 2375 iptables -A INPUT -p tcp --dport 2375 ! -s {主服务器IP} -j DROP第三个区别是远程服务器上也要创建 Odoo-SAAS-Data 目录、odoo.conf 和 odoo-template.conf并保证所有目录的属主是远程的 Odoo 用户。模块通过 SSH 连接远程服务器后会在远程服务器上按主服务器的路径结构创建客户端目录路径不一致同样会导致挂载失败。4.3 镜像搬运的三种方式对比基础镜像在主服务器构建完成后需要同步到远程服务器。这里有三条路按推荐程度排序第一种是用 docker save 导出镜像文件再在远程服务器 docker load 导入适合服务器之间没有共享镜像仓库、带宽还可以的场景。第二种是将镜像推送到 Docker Hub 之类的镜像仓库在远程服务器上拉取。第三种是拿着同一份 Dockerfile 和辅助文件在远程服务器上重新构建适合不在同一内网、且带宽较差的情况。# 主服务器导出镜像 docker save odoobywebkul:12.0 odoo_saas_base.tar # 拷贝到远程服务器 scp odoo_saas_base.tar {远程服务器IP}:/tmp/ # 远程服务器导入 docker load odoo_saas_base.tar导出导入的方式最简单但要注意镜像文件可能比较大1GB 到 2GB 都很常见拷的时候预留好磁盘空间。如果主服务器和远程服务器在同一内网也可以用 nc 配合 docker save 直接流式传输不用占用磁盘空间。远程服务器上还需要安装 Python 依赖docker、erppeek、paramiko、psycopg2。注意 psycopg2 在部分系统上需要先安装 libpq-dev 才能编译。# 远程服务器安装依赖Ubuntu/Debian 系 sudo apt install libpq-dev pip3 install docker erppeek paramiko psycopg2全部配置完成后回到主服务器的 Odoo 后台在 SaaS 工具包配置里添加远程服务器。SSH 连接信息里的 SFTP 主机填远程服务器的本地或私有 IP别填 localhost模块是通过 SSH 协议和远程服务器通信的填 localhost 等于是连自己。5. 业务配置落地SaaS 服务器、订阅计划与客户端实例创建5.1 配置 SaaS 服务器数据库连接测试是关键节点环境搭好之后回到 Odoo 后台走业务配置流程。入口是「SaaS 工具包 配置 SaaS 服务器」。这里要填写两套信息服务器信息和数据库服务器信息。服务器信息里最关键的是「主机服务器」字段选择「自身」表示客户端容器跑在当前这台服务器上选择「远程主机」则需要在下方填写 SSH 连接信息。数据库服务器信息决定了模板数据库和客户端数据库建在哪里。数据库主机必须填写 PostgreSQL 实际监听的地址端口默认 5432用户名和密码要求有创建数据库的权限。# 常见问题数据库填 localhost 导致容器内连不上 # 容器内的 localhost 是容器自己不是宿主机 # 正确做法是填宿主机在 Docker 网桥上的 IP一般是 172.17.0.1提交前点击「测试连接」按钮模块会实际连接一次数据库返回成功或错误信息。这一步是体检比后面创建实例时爆错要友好得多。如果测试连接失败优先检查 pg_hba.conf 的网段配置和 postgresql.conf 里 listen_addresses 是否允许外部 IP。在远程服务器场景下数据库主机的填写还有一层细节如果 PostgreSQL 跑在主服务器上远程服务器上的客户端容器要连主服务器的数据库数据库主机字段要填主服务器的私有 IP同时 pg_hba.conf 里要放行 Docker 网段 172.17.0.0/16 和远程服务器所在网段。5.2 创建订阅计划和数据库模板SaaS 服务器配置完成后进入「SaaS 工具包 SaaS SaaS 计划」创建订阅计划。计划是面向客户的套餐定义决定了实例创建在哪个服务器上、可以安装哪些模块、计费方式是什么。字段说明注意点SaaS 服务器选择已配置的服务器实例只会运行在所选服务器上数据库模板模板数据库名称留空则自动按计划名生成计费周期 / 重复间隔天/周/月/年决定发票生成频率试用期(天数)试用天数试用期内不计费默认计费标准固定费率或基于用户数固定费率每周期生成固定金额发票计划配置里有一项「相关模块」非常关键这里列出了这个计划的客户实例将安装的模块。点击「创建数据库模板」按钮后系统会在 PostgreSQL 里创建一个模板数据库并安装这个模块列表里的所有模块耗时取决于模块数量一般几分钟到几十分钟。模板数据库创建完成后下一步是「创建合同」。在弹出的向导里选择合作伙伴、填写合同价格系统会生成一份销售合同。这里注意合同和实例的关系合同是绑定客户和计划的关联记录后续的发票、订阅状态变化都挂在合同上。5.3 手动创建实例与 Ask from Customer 两种域名获取方式合同创建完成后就能为特定客户创建 Odoo 实例。核心输入只有一个客户希望绑定的域名。手动方式是在「域名」字段直接输一个当前没被占用的唯一域名保存后点击「创建并确认客户端」。系统会在选定的服务器上创建容器、生成 Nginx 虚拟主机配置、创建数据库并从模板克隆然后给客户发送一封邮件邮件里包含设置 Odoo 密码的链接和访问网址。另一个方式是点击「Ask from Customer」按钮系统会给客户发一封邮件让客户自己挑选心仪的域名。客户在链接里输入的域名会被系统校验唯一性通过后自动创建实例。自动创建流程里域名分配、容器创建、邮件通知是串行执行的域名被占用时会在这一步直接报错。实例创建完成后在「SaaS 客户端」菜单里能看到所有客户端的记录。每条记录包含容器状态、域名、数据库状态等信息。客户端记录页面上提供了启动、停止、重启实例的按钮还有一个「登录」按钮可以直接进入客户的 Odoo 实例后台——这是 wk_saas_tool 模块提供的能力允许主服务器运维人员免密码登录客户端实例适合帮忙排查问题时使用。5.4 模块上传通用与特定客户端两种路径日常运营中给客户补装模块是高频操作。这里有两个路径放错位置会导致某些客户看到模块、某些看不了。通用模块放在 common_addons 目录下所有客户端和模板都能看到。特定客户端的模块放在该客户端的数据目录下的 addons 文件夹中。# 通用模块路径 /opt/odoo/common_addons/{module_name}/ # 特定客户端模块路径示例 /opt/odoo/Odoo-SAAS-Data/{client_container_name}/data-dir/addons/11.0/{module_name}/添加模块后记得把权限和所有权改成 Odoo 用户然后重启目标客户端容器。不带权限设置就直接重启的话模块代码能读但运行时写不了数据日志里会有权限相关错误排查起来费劲。6. 避坑与排查从日志到主密码的运维实战五条记录6.1 现象、原因、解决五个高频坑实录第一个坑创建客户端容器时卡在「Starting container」超过十分钟。原因是模块在等待容器内部 Odoo 服务就绪但容器内部 Odoo 起不来通常是 uid/gid 不匹配导致挂载目录无法写入。解决检查主机和镜像内 odoo 用户的 uid/gid一致后重新构建镜像并更新 saas.conf 里的镜像名。第二个坑客户端实例能访问但登录报数据库连接失败。原因是客户端容器内的 Odoo 配置里数据库主机写的是 localhost容器内没有 PostgreSQL。解决确认 postgresql.conf 的 listen_addresses 是否允许来自 Docker 网桥的流量pg_hba.conf 里要放行 172.17.0.0/16 网段。# 查看客户端容器日志最常用的排错命令 docker exec -it {client_container_name} bash -c \ tail -f /var/log/odoo/odoo-server.log第三个坑Nginx 配置了 include 但客户端域名访问 502。原因是 docker_vhosts 目录下生成的虚拟主机文件权限不对Nginx worker 进程读不到。解决chown 给 odoo 用户同时确认 nginx 配置文件里 include 路径的结尾有*.conf。第四个坑远程服务器添加后测试连接失败SSH 认证报错。原因是远程服务器开的标准 SSH 配置不允许密码登录或者 Odoo 用户在远程上没有 shell。解决确认 sshd_config 里 PasswordAuthentication 为 yes使用adduser时指定--shell /bin/bash。第五个坑备份恢复后客户端登录不了密码不对。原因是每个客户端实例的主密码写入在客户端的 odoo.conf 中路径是 Odoo-SAAS-Data/{client_container_name}/odoo-server.conf而 saas.conf 里存的是默认主密码备份后主密码可能被覆盖。解决找到客户端对应的 odoo-server.conf 文件直接查看 master_pwd 字段。# 重启特定客户端容器 docker restart {client_container_name} # 查看所有容器状态 docker ps -a | grep odoo从那以后我每次给客户开新实例都强制走一遍流程先测数据库连接再创建模板库实例起来后第一件事不是发邮件而是自己先登录一次确认页面能打开。日志和主密码这两个位置我记在笔记里排障时先看它们省掉大半弯路。希望帮到你。本文还有配套的精品资源点击获取
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/25 10:34:22
Win7老机器提速实战:从诊断到优化的完整指南
2026/9/25 10:29:21
AI模型部署总卡参数调整?三步打破“改参数等研发”的工期魔咒
2026/9/25 10:29:21
OpenClaw政府支持全景深度分析:冰火博弈、战略动机与未来生态驯化
2026/9/25 12:04:26
电控工程师简历突围:用开源项目构建可验证工程信号
2026/9/25 12:04:26
WSL Dashboard如何实现实时状态监控?wsl命令执行与输出解析机制深度剖析
2026/9/25 12:04:26
把Mermaid图表变成Excalidraw白板:快速上手lavish-axi可编辑图表演示指南
2026/9/25 12:04:26
STM32开源项目实战:代码、原理图与Proteus仿真全打通
2026/9/25 12:04:26
openclaw-uninstall 报错“无法识别为 cmdlet”怎么解决?TaoToken 环境下的 PowerShell 卸载排查指南
2026/9/25 11:59:26
OpenClaw 一体化包部署实战:解压异常与 Gateway 离线排查指南(含 TaoToken 配置)
2026/9/25 0:03:37
AI元人文:从工具使用到思维重构的深度探索
2026/9/25 0:03:37
Python+CNN车牌识别实战:从数据预处理到模型训练与部署
2026/9/25 0:03:37
Vim基础操作全攻略:保存退出、模式切换与高频命令实战
2026/9/25 5:41:44
深入解析Transformer多头注意力机制与工程优化
2026/9/25 5:41:44
OpenClaw 的 Skills 跑学习任务,模型通道改到 TaoToken 通道行不行?
2026/9/25 5:41:44
ChatGPT报错Oops, an error occurred! 全链路排查指南