最近买了台新服务器,想着直接把旧服务器上的1Panel整个迁移过来,结果OpenResty容器怎么都启动不起来,折腾了好久才发现是个很隐蔽的问题,记录一下排查过程
问题现象
从旧服务器把1Panel的配置快照搬到新服务器后,OpenResty容器状态显示异常,查看日志报错如下:
1 | [emerg] 1#1: could not build map_hash, you should increase map_hash_bucket_size: 32 |
看到这个报错一开始以为是map规则太多了,但检查配置发现stream分流里就一条规则:
1 | map $ssl_preread_server_name $backend_name { |
解决方法
遇到这个报错,需要在stream {}块中调大哈希桶大小。正确配置如下:
1 | stream { |
深挖原因
为什么一条规则需要64字节?
map_hash相关的两个参数含义不同:
map_hash_max_size:控制规则数量map_hash_bucket_size:控制单条规则占用的内存大小
在64位系统中,单条规则实际占用内存包括:
- 域名字符串本身(video.sample.com约16字节)
- 指针和内部标记(约10字节)
- 内存对齐(对齐到8的倍数)
- 桶结束标记NULL指针(8字节)
算下来至少需要40字节,32字节的桶确实装不下
为什么旧服务器不报错?
更诡异的是,旧服务器上同样的配置(即使不设置map_hash_bucket_size)运行完全正常。这让我怀疑是不是系统层面有差异。
尝试查看CPU缓存行大小:
1 | getconf LEVEL1_DCACHE_LINESIZE |
两台服务器返回都是64,看起来没区别。但继续查看CPU型号信息:
1 | cat /proc/cpuinfo | grep "model name" | uniq |
这时候真相大白:
- 旧服务器:
AMD EPYC 7452 32-Core Processor - 新服务器:
QEMU Virtual CPU version 2.5+
原来问题出在虚拟化策略上。Nginx会通过CPUID指令获取CPU型号,然后根据内置的CPU数据库来决定缓存行大小:
- 旧服务器的云服务商使用了CPU直通(Host-Passthrough),Nginx识别到真实的AMD EPYC处理器,自动使用64字节缓存行,所以即使配置写32也会被自动对齐到64
- 新服务器使用的是QEMU虚拟CPU,Nginx无法识别型号,为了保守起见fallback到32字节,导致真的就只有32字节可用
总结
- 在stream块中使用map时,需要在同一块中配置
map_hash_bucket_size参数 - 如果使用了较长的域名或后端名称,建议显式设置
map_hash_bucket_size 64或更大,不要依赖自动对齐 - 不同云服务商的虚拟化策略可能导致Nginx底层行为不一致,迁移配置时需要注意
- Docker容器内的环境可能和宿主机不同,原本正常的配置在容器里可能需要调整