> 本文记录了一个真实场景:某第三方推单方要对接我们的订单推送接口(SIP),为不影响正在跑的生产环境,在云端生产应用旁边独立搭建了一套测试环境,通过 Nginx 同一域名下用路径区分 prod / test,做到「环境完全隔离、配置可控、随时可测」。不编造、不猜测,踩过的坑都写出来。
一、背景:为什么需要一套独立的测试环境
我们的订单匹配系统在云端 Ubuntu 上跑着一套 Flask + SQLite 应用,对外提供「订单推送接收接口」(下文简称 SIP 接口),第三方供应商通过这个接口把订单推给我们。
这次有一个新的推单方要对接。按惯例,第三方对接调试时不能直接在生产环境上测,原因很现实:
所以方案很明确:在生产应用旁边,单独搭一套"长得一模一样、但完全隔离"的测试环境,让第三方在测试环境里随便折腾,测通了再切生产。
二、生产环境的现状
先摸清生产环境长什么样,测试环境才能照着搭。生产环境是一套 Flask + SQLite 的订单系统:
|
项目 |
生产环境 |
|
代码目录 |
/opt/qorder-match/app/ |
|
进程方式 |
Gunicorn(systemd 服务 qorder-match.service) |
|
监听端口 |
127.0.0.1:5000(Nginx 反代) |
|
数据库 |
SQLite(data.db) |
|
SIP 接口 |
/api/sip/order/create |
|
SIP 配置存储 |
存在 SQLite system_settings 表里 |
|
SIP 相关键 |
厂商号 vendorNo、验签公钥、我方私钥、API 环境标记、IP 白名单 |
> 关键点:SIP 的配置(vendorNo、公钥、私钥、白名单等)不是写死在代码里,而是存在数据库 system_settings 表里,通过环境变量 ORDER_MATCH_DB 指定数据库文件路径。这一点是后面能轻松搭出隔离环境的基石——换个库文件就等于换一套配置。
三、测试环境的设计思路(隔离点)
要让测试环境「像生产但又不影响生产」,我定了这几个隔离点:
|
隔离点 |
生产 |
测试 |
|
代码目录 |
/opt/qorder-match/app |
/opt/qorder-sip-test/app |
|
虚拟环境 |
/opt/qorder-match/venv |
/opt/qorder-sip-test/venv |
|
数据库 |
data.db(生产库) |
空的 data.db |
|
端口 |
127.0.0.1:5000 |
127.0.0.1:5001 |
|
systemd 服务 |
qorder-match.service |
qorder-sip-test.service |
|
Nginx 路径 |
/api/sip/ |
/api/sip-test/ |
|
API 环境标记 |
production |
test |
|
IP 白名单 |
只放行若干业务 IP |
空(放开,便于测试) |
|
吉客云密钥 |
已配置 |
不配置 |
最后一条是最关键的防呆设计:测试环境故意不配 ERP(吉客云)的对接密钥。这样就算第三方在测试环境发了脏单,接口也只会返回处理结果,而绝不会真正导入到 ERP 系统,等于在数据层面加了一道物理防火墙。
四、搭建步骤
4.1 建独立目录和虚拟环境
# 1. 代码目录
mkdir -p /opt/qorder-sip-test/app
# 2. 独立虚拟环境
python3 -m venv /opt/qorder-sip-test/venv
# 3. 复用生产 venv 的已装包(避免重复下载大包,也保证依赖版本一致)
# 生产环境已经有 playwright、cryptography 等一堆大包,直接复用它的 site-packages
rm -rf /opt/qorder-sip-test/venv/lib/python3.12/site-packages
cp -r /opt/qorder-match/venv/lib/python3.12/site-packages \\
/opt/qorder-sip-test/venv/lib/python3.12/site-packages
> 经验:搭建测试环境时,复用生产环境的 site-packages 是很省事的做法。既不用重新 pip install 一堆大包(省时间、避免网络超时),又能保证测试环境的依赖版本和生产完全一致,避免「测试好好的、上生产却报错」这种版本差异坑。
4.2 复制代码并整理
# 把生产应用代码复制到测试目录(只复制运行时文件)
cd /opt/qorder-match/app
cp -r app.py sip_rsa.py secrets.env templates static \\
/opt/qorder-sip-test/app/
4.3 初始化一个独立的空库
应用代码在模块加载时(init_db())会自动建表,所以只需要给测试环境配一个全新的数据库文件即可。
# 用环境变量指定测试库路径,启动一次让代码自动建表
cd /opt/qorder-sip-test/app
ORDER_MATCH_DB=/opt/qorder-sip-test/app/data.db \\
/opt/qorder-sip-test/venv/bin/python -c "import app"
# 此时会生成一个空的 data.db(含全部表结构)
4.4 注入测试用的 SIP 配置
生产环境的 SIP 配置(vendorNo、验签公钥、我方私钥、IP 白名单)存在生产 data.db 的 system_settings 表里。测试环境需要一个「具备生产配置、但关键项被测试化」的配置。用一段 seed 脚本写入测试库:
# seed_sip.py —— 把生产 SIP 配置灌进测试库,并置为 test 环境
import sqlite3
conn = sqlite3.connect('/opt/qorder-sip-test/app/data.db')
c = conn.cursor()
# 从生产库读出真实配置
prod = sqlite3.connect('/opt/qorder-match/app/data.db')
rows = prod.execute("SELECT key, value FROM system_settings WHERE key LIKE 'sip_%'").fetchall()
# 写入测试库
for k, v in rows:
c.execute("INSERT OR REPLACE INTO system_settings (key, value) VALUES (?, ?)", (k, v))
# 关键:覆盖成测试环境标记
c.execute("INSERT OR REPLACE INTO system_settings (key, value) VALUES ('sip_api_env', 'test')")
# 关键:放开 IP 白名单,方便第三方任意 IP 测试
c.execute("INSERT OR REPLACE INTO system_settings (key, value) VALUES ('sip_allowed_ips', '')")
conn.commit()
conn.close()
prod.close()
print('测试库 SIP 配置写入完成')
> 要点:测试库复用了生产的真实密钥和公钥(保证验签逻辑和生产一致),但只改两个字段——环境标记 test、IP 白名单放开。这样第三方测出来的验签行为跟生产一致,但 IP 限制放松了,测起来不会因为 IP 不对被拒。
4.5 配置 systemd 服务(独立端口)
# /etc/systemd/system/qorder-sip-test.service
[Unit]
Description=SIP Test Env (Flask + Gunicorn)
After=network.target
[Service]
User=root
WorkingDirectory=/opt/qorder-sip-test/app
# 关键:指定独立的测试数据库 + 测试端口
Environment=ORDER_MATCH_DB=/opt/qorder-sip-test/app/data.db
ExecStart=/opt/qorder-sip-test/venv/bin/gunicorn \\
–workers 1 \\
–threads 4 \\
–bind 127.0.0.1:5001 \\
–access-logfile /var/log/qorder-sip-test-access.log \\
–error-logfile /var/log/qorder-sip-test-error.log \\
wsgi:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable qorder-sip-test
systemctl start qorder-sip-test
systemctl is-active qorder-sip-test # active
> 注意:–bind 用 127.0.0.1:5001 而不是 0.0.0.0,测试环境只让本机 Nginx 反代访问,不直接暴露公网端口。生产环境同理也是只绑内网,由 Nginx 统一对外。
五、Nginx 同一域名用路径区分 prod / test
这里是最巧妙的一步。生产和测试都共用同一个域名(api.xx.com.cn),通过 URL 路径前缀区分:
- 生产:/api/sip/… → 反代到 127.0.0.1:5000(生产)
- 测试:/api/sip-test/… → 内部 rewrite 成 /api/sip/… → 反代到 127.0.0.1:5001(测试)
为什么要 rewrite?因为应用里接口路由写死是 /api/sip/order/create,测试环境代码没改,所以 Nginx 要把 /api/sip-test/ 前缀剥掉,转成 /api/sip/ 再转发到测试实例,这样代码一行不用改。
# /etc/nginx/sites-available/api-xx —— 关键配置片段
server {
listen 443 ssl;
server_name api.xx.com.cn;
# 生产 SIP 接口 → 5000
location /api/sip/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 测试 SIP 接口 → 先 rewrite 成 /api/sip/ 再转到 5001
location /api/sip-test/ {
rewrite ^/api/sip-test/(.*)$ /api/sip/$1 break;
proxy_pass http://127.0.0.1:5001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
nginx -t
systemctl reload nginx
> 经验:同一套代码、同一个域名,用 location 前缀 + rewrite 就能在 Nginx 层天然切分出「生产/测试」两个入口,应用层完全不用感知。这让「测试环境验证通过 → 切生产」只需要换一个 URL 前缀,心理负担和数据风险都降到最低。
六、验证:生产 / 测试返回对比
搭完后,用同一个请求打两个地址,对比响应,能非常直观地验证环境是否正确隔离:
# 测试环境(无业务 IP,预期返回「缺少必填字段」,说明已走到参数校验,路由和实例都通了)
curl -s https://api.xx.com.cn/api/sip-test/order/create
# → {"code":xxx,"msg":"缺少必填字段"}
# 生产环境(预期返回「IP 不在白名单」,说明生产防护正常)
curl -s https://api.xx.com.cn/api/sip/order/create
# → {"code":xxx,"msg":"IP 不在白名单"}
|
地址 |
返回 |
含义 |
|
/api/sip-test/order/create |
缺少必填字段 |
测试环境路由通了,走到参数校验,且没被 IP 白名单拦住(因为放开了) |
|
/api/sip/order/create |
IP 不在白名单 |
生产环境防护正常,陌生 IP 被拒之门外 |
这个对比非常关键:同一套代码,两个入口,行为完全符合预期——测试环境放开了、生产环境守着。说明隔离成功,可以放心交给第三方去测。
同时确认两个监听端口都从外部 HTTPS 可达(443/8443 双入口都通)。
七、踩坑与经验
八、总结
|
关键动作 |
为什么 |
|
独立目录 + 独立 venv + 独立空库 |
环境层面物理隔离 |
|
独立端口 5001 + 独立 systemd 服务 |
进程层面隔离,互不影响 |
|
Nginx 路径前缀 + rewrite |
同一域名区分 prod/test,应用代码零改动 |
|
测试环境不配 ERP 密钥 |
防脏单污染生产 ERP,物理级安全防线 |
|
测试放开 IP、生产保持白名单 |
测试方便、生产安全,互不干扰 |
这次「生产环境旁搭隔离测试环境」的核心思路就一句话:用「独立库文件 + 独立端口 + Nginx 路径区分 + 关键能力留空」的组合拳,把测试环境隔离得干净、干净、可回滚。同样的思路可以复用到任何「第三方对接、需安全测试」的场景。
如果你也踩过类似的坑,欢迎评论区交流。觉得有用可以点赞收藏






