如何关闭 HTTP 组件仅保留 replication?
解读
在国内生产环境中,CouchDB 常被部署在政务内网、金融 DMZ、物联网边缘节点等场景,安全基线要求“非必要服务一律关停”。HTTP API 一旦暴露,就意味着任何能解析 5984 端口的进程都可触发读写,因此部分架构师希望只保留 4369(epmd)与 5986(集群通信)端口,让节点之间仍能完成多主复制,而外部客户端无法再走 REST 接口写数据。
面试时,考官不仅想看你会不会“关端口”,更想确认你对CouchDB 内部管线、BEAM 虚拟机、chttpd/couch_httpd 模块、OTP 应用树的理解深度,以及能否在不破坏集群元数据的前提下完成灰度变更。
知识点
- CouchDB 的 HTTP 栈由两层构成:
couch_httpd:早期单进程 acceptor,负责 /_utils、/_config 等路径。chttpd:3.x 默认的“集群 HTTP”,基于 Ranch,监听[chttpd] port = 5984。
二者均挂在couch_primary_servicesOTP 应用树下,与 replication 不在同一监督分支。
- Replication 依赖的是内部 Erlang RPC 与
_replicator数据库,不依赖 5984 端口;节点间走 Erlang 分布式协议(4369 及随机高位),所以关闭 HTTP 不会中断复制。 - 关闭方式有三种粒度:
- 配置层:把端口写成
0或绑定到127.0.0.1; - 代码层:在
etc/vm.args加-couch_httpd start false或-chttpd start false; - 打包层:重新编译
couch.app.src,把couch_httpd、chttpd从applications列表摘除。
- 配置层:把端口写成
- 国内等保要求需同步完成主机防火墙(firewalld/iptables)与CouchDB 配置双重收敛,并留**5986(集群维护端口)**给运维审计。
- 灰度顺序:先关
chttpd→ 观察_replicator作业 → 再关couch_httpd→ 重启单节点 → 滚动到全集群,每一步回滚方案需提前写好 systemd 模板与 ini 备份。
答案
- 登录任一节点,先确认复制正常:
curl -s http://127.0.0.1:5984/_replicator | jq '._replication_state' - 修改 local.ini,追加两行:
[chttpd]
port = 0
bind_address = 127.0.0.1
[httpd]
port = 0
bind_address = 127.0.0.1 - 可选加固:在 vm.args 末尾加
-couch_httpd start false
-chttpd start false - systemctl restart couchdb,确认监听:
ss -lntp | grep 5984 # 应无结果
ss -lntp | grep 4369 # epmd 仍在 - 在另一节点写一条连续复制:
curl -X PUT http://<admin>:<pwd>@127.0.0.1:5986/_replicator/edge2dmz -d '{"source":"edge","target":"http://<admin>:<pwd>@dmz:5986/db"}'
返回 201 且状态持续 “triggered” 即证明仅关闭 HTTP API,复制未受影响。 - 最后把变更同步到 ansible playbook,并关闭公网安全组 5984 入方向,完成等保收敛。
拓展思考
- 如果未来需要临时开放只读 REST,可再启一个 Nginx + lua-resty-couchdb 的只读反向代理,只允许 GET /{db}/{doc} 与 GET /{db}/_changes,不暴露 /_config、/_users,实现“零信任”最小化暴露。
- 在 K8s 侧,可用 CouchDB 3.3 的 sidecar 模式,把
chttpd容器端口从 service 里摘除,只保留couchdb@127.0.0.1的 headless service 给 StatefulSet,让 Prometheus 通过 5986/_node/_stats 拉指标,兼顾监控与安全。 - **国内信创环境(鲲鹏 + 麒麟 V10)**下,Erlang 版本锁在 23.x,关闭 HTTP 后需验证 OTP 的 distribution over TLS 是否仍兼容,否则节点间会报
** Connection attempt from disallowed node **,需要在etc/vm.args加-proto_dist inet_tls并导入国密证书。