考古|ES 5.1 的别名名字开始校验,下划线开头等老写法会被拒
5.1 的 breaking 只有两条:索引别名开始校验命名规则(空格、不能以
_/-/+开头、长度上限),以及 Log4j 2.6 升级到 2.7。同一页还有三条属于插件作者的改动(UnicastHostsProvider 改成拉模型、ZenPing 与 MasterElectService 不再可插拔、onModule不再有实现)。5.1 之外,5.0 已经删掉了 local discovery(discovery.type: local会启动失败),单节点场景改用single-node。本文按 v5.6.16 源码把规则的实现位置和报错消息列出来,并标出一处文档与源码不一致的地方。
说明
实测环境:本地起的 Elasticsearch 5.6.16 单节点实例(官方 tar 包直接跑,堆内存 512MB,外挂 JDK 8),curl 连本地 9200 端口。本文「实测」小节的命令和输出都出自这个实例;文档与源码依据仍按 v5.6.16 标签下的 migrate_5_1.asciidoc、MetaDataCreateIndexService.java、Strings.java 引用,哪些是文档写明、哪些是我的推断,正文里逐处标注。

图 1:官方文件 migrate_5_1.asciidoc(v5.6.16 标签)。来源:GitHub elastic/elasticsearch v5.6.16 migrate_5_1.asciidoc
别名开始校验命名
5.1 迁移文档(migrate_5_1.asciidoc 第 4–11 行):
For an index alias, when the index alias is created, get, or deleted, the index alias name will be validated.
三条规则:
They may not contain spaces.
They may not begin with
_,-or+.
They may not be more than 100 UTF-8 encoded bytes.
这在源码里的位置是 MetaDataCreateIndexService.validateIndexOrAliasName:索引名和别名共用这一套校验,只是在校验之后索引名额外加了一条必须小写。
if (!Strings.validFileName(index)) {
throw exceptionCtor.apply(index, "must not contain the following characters " + Strings.INVALID_FILENAME_CHARS);
}
if (index.contains("#")) {
throw exceptionCtor.apply(index, "must not contain '#'");
}
if (index.charAt(0) == '_' || index.charAt(0) == '-' || index.charAt(0) == '+') {
throw exceptionCtor.apply(index, "must not start with '_', '-', or '+'" );
}
Strings.INVALID_FILENAME_CHARS 是 \、/、*、?、"、<、>、|、空格、逗号。所以"不能含空格"只是这组禁用字符里的一项,实际禁用范围更大。
别名那条分支在 AliasValidator.validateAliasStandalone 第 104–112 行,调用的是同一个 validateIndexOrAliasName,异常类型是 InvalidAliasNameException。
还有一处文档和源码不一致的地方要留意。迁移文档写 100 UTF-8 字节,而 v5.6.16 里 MAX_INDEX_NAME_BYTES 是 255(第 108 行),索引名和别名都用这个值。也就是说,按 5.1 文档写的 100 字节在 5.6 的代码里已经不是上限了。本文没有逐个版本核对这个数字是什么时候改的,落地时以你所用版本的行为为准;要保险就按 100 字节设计,两个版本都合规。

图 2:官方文件 MetaDataCreateIndexService.java(v5.6.16 标签)。来源:GitHub elastic/elasticsearch v5.6.16 MetaDataCreateIndexService.java
为什么规则长这样
- 不能含
\、/:名字会作为路径的一部分参与文件系统操作; - 不能含
*、?:多索引匹配的通配符; - 不能含逗号:多索引列表的分隔符;
- 不能含
#:注释符; - 不能含
"、<、>、|:URL 里的特殊字符; - 不能以
_、-、+开头:预留给内部设施和表达式(多索引匹配里-用来排除); - 长度上限:集群状态里每个别名都要记一条,名字越长占的元数据越多,名称解析也越慢。
这些是官方文档点出的两条(空格、首字符、长度)之外,按禁用字符集能推出来的对应关系,官方没有逐字符解释。

图 3:官方文件 bootstrap-checks.asciidoc(v5.6.16 标签)。来源:GitHub elastic/elasticsearch v5.6.16 bootstrap-checks.asciidoc
别名不受约束意味着什么
以前别名能含 /、*、空格这些字符。有人会用它们构造带结构信息的名字,比如:
{ "actions": [ { "add": { "index": "logs-2016", "alias": "logs/2016" } } ] }
5.1 起这类名字会报 InvalidAliasNameException。
宽松期积累下来的问题不止一个名字不能用:这些别名在多索引匹配里会被引擎当成通配符解析、在 URL 里需要额外转义、在 _cat/aliases 的输出里因为含空格而难以按列切分。校验把这些问题挡在创建阶段。
日志和监控是这类改动的实际排查手段:这类错误在节点日志里能直接看到 InvalidAliasNameException 和具体原因(含了哪个字符、超了多少字节)。
# 从日志里找被拒的别名
grep -i "InvalidAliasNameException" /var/log/elasticsearch/elasticsearch.log | head -20
# 从代码和脚本里找可能违规的别名
grep -rnE '"alias"\s*:\s*"[^"]*[ \/*?,"<>|#][^"]*"' --include=*.json --include=*.sh --include=*.py . | head -20
第二条是文本匹配,命中后要人看上下文;它只能提供线索,不能当判定标准——最终以创建时是否报错为准。
按命名空间收敛
官方给了方向:
Aliases can be namespaced using dots. For example,
logs.2016.01.
用点号分层。好处是:
- 和已有的索引命名习惯一致,读写两侧不用切换心智;
- 点号在大多数工具里不需要转义,
_cat/aliases的输出能按列切分; - 分层语义明确,
logs.2016.01一眼能看出是 logs 下 2016 年 1 月。
迁移时建新旧对照:老别名 logs/2016 对应新别名 logs.2016,改动点集中在创建别名的地方,别名引用处(查询、写入)跟着改。
如果某些外部系统的别名改动不了(老系统写死了名字),那就不能升到 5.1。这不是能绕过去的限制,校验在建别名时强制。
Log4j 2.6 → 2.7
The bundled Log4j 2.6 and 2.6.1 versions have been updated to Log4j 2.7, in order to fix problems in passing markers to slf4j.
升级 Log4j 是为了修 markers 传给 slf4j 的问题。默认启动的集群不用管;自定义的日志配置(尤其是自己引了 Log4j 依赖的)要确认和目标版本匹配,本文没有逐个版本核对配置文件的兼容性细节。
同一页里三条插件作者的改动
这几条原文没写,属于 5.1 迁移文档同一页(第 20–39 行):
UnicastHostsProvider改成拉模型:插件不再通过回调推主机列表,改由 ES 主动调用buildDynamicHosts拉取,这样能配合dynamic_hosts设置,也能让提供者用上 ES 自身的服务(如NodeService);ZenPing与MasterElectService不再可插拔:在标准进程内运行 ES 时这两个不能换实现,官方保留Ping#isClosed以便第三方实现继续工作;如果插件要替换它们,需要自带Discovery模块;onModule不再有实现:所有模块改由plugin接口实现,插件里不能再写onModule(SomeModule)。
第三条和 5.0 那批 onModule → XxxPlugin 的替换是同一件事的收尾。
local discovery 的删除在 5.0
原文把它放在 5.1 里,实际是 5.0 迁移文档(第 39 行)的说法:
Local discovery was removed in favour of running the embedded transport client. […] This means that setting
discovery.typetolocalwill fail on startup.
local 类型的作用是:在同一个 JVM 里跑多个节点,以同一个 JVM 通信,省一层网络。5.0 删除,官方给出的替代是"跑内嵌的 transport client"。
v5.6.16 里注册的 discovery 类型有 single-node 等(DiscoveryModule 第 84–86 行),没有 local,所以配了 discovery.type: local 会因为找不到这个类型而启动失败。
单节点场景用 single-node:节点自选为 master,且不会加入任何其它节点的集群(bootstrap-checks.asciidoc 第 47–51 行)。它是给"需要把 transport 绑到外部接口、用 transport client 测试"这类场景准备的。
如果原来是"同一 JVM 多节点"的用法(典型是单元测试),替代方案是内嵌 transport client:用 PreBuiltTransportClient 在测试里连一个真实节点,而不是在 JVM 内起多个节点。
升级检查清单
- 列出所有别名,找出含空格、
\、/、*、?、"、<、>、|、逗号、#,或以_、-、+开头的:
curl -s 'localhost:9200/_cat/aliases?v'
- 给它们定新的点号分层名字,建新旧对照表;
- 改创建别名的代码,同时改所有引用处(查询、写入、模板里的
aliases定义); - 外部系统写死的老别名,联系对方改,或者停在 5.0;
- 确认自定义日志配置与目标 Log4j 版本匹配;
- 自研插件按上面三条改(
buildDynamicHosts、不再替换ZenPing/MasterElectService、onModule去掉); discovery.type: local改掉:单节点用single-node,同 JVM 多节点的测试改用内嵌 transport client。
下划线开头的别名还能建吗

图 4:本地 5.6.16 单节点实例上的实测输出。上:普通名字照常建;下:下划线开头被拒
curl -s -X PUT "" \
| jq -r '.error.reason'
Invalid alias name [_foo]: must not start with '_', '-', or '+',HTTP 400。
这条在迁移里容易被忽略,因为 _ 开头的名字在旧版本是合法的,而报错只在建别名的那一刻发生。索引模板和滚动别名如果是脚本批量建的,升级后第一次执行会整批失败 —— 建议在升级前先把现有别名名字过一遍,别等执行时才发现。
写在最后
5.1 的 breaking 只有两条,别名校验和 Log4j 升级。别名这条的影响面比它看起来大:它给以前完全不受约束的别名补上了和索引名同级的校验,[“”“']加了几个字符的限制[“””']只是表象。
我的默认做法是:排查这类问题先看日志,它会明确指出原因(含了哪个字符、超了多长),比翻代码快。改的时候用点号分层,和索引命名保持一致。
另外记住:别名的长度上限在不同版本里不一样(5.1 文档写 100 字节,5.6.16 的源码里是 255)。别把名字设计到贴近上限,留余量比记版本号可靠。
参考
本文对照 Elasticsearch
v5.6.16标签的官方文档核对,引文逐字取自原文;文中「实测」小节的命令与输出来自本地 5.6.16 单节点实例,不含性能对比。
- 点赞
- 收藏
- 关注作者
评论(0)