在K8s通过cloud_on_k8s的官方es组件实现了es的自动化的部署;但是在最近的一个实例部署过程中,我们发现了pod一直无法拉起,然后针对这个问题进行定位和分析。
1. 日志
{"@timestamp":"2023-09-20T07:33:32.445Z", "log.level":"ERROR", "message":"fatal exception while booting Elasticsearch", "ecs.version": "1.2.0","service.name":"ES_ECS","event.dataset":"elasticsearch.server","process.thread.name":"main","log.logger":"org.elasticsearch.bootstrap.Elasticsearch","elasticsearch.node.name":"elasticsearch-5roiysme4074-es-default-0","elasticsearch.cluster.name":"elasticsearch-5roiysme4074","error.type":"java.lang.IllegalStateException","error.message":"failed to obtain node locks, tried [/usr/share/elasticsearch/data]; maybe these locations are not writable or multiple nodes were started on the same data path?","error.stack_trace":"java.lang.IllegalStateException: failed to obtain node locks, tried [/usr/share/elasticsearch/data]; maybe these locations are not writable or multiple nodes were started on the same data path?\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.env.NodeEnvironment.<init>(NodeEnvironment.java:285)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.node.Node.<init>(Node.java:478)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.node.Node.<init>(Node.java:322)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.bootstrap.Elasticsearch$2.<init>(Elasticsearch.java:214)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.bootstrap.Elasticsearch.initPhase3(Elasticsearch.java:214)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:67)\nCaused by: java.io.IOException: failed to obtain lock on /usr/share/elasticsearch/data\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.env.NodeEnvironment$NodeLock.<init>(NodeEnvironment.java:230)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.env.NodeEnvironment$NodeLock.<init>(NodeEnvironment.java:198)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.env.NodeEnvironment.<init>(NodeEnvironment.java:277)\n\t... 5 more\nCaused by: java.nio.file.NoSuchFileException: /usr/share/elasticsearch/data/node.lock\n\tat java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)\n\tat java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)\n\tat java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)\n\tat java.base/sun.nio.fs.UnixPath.toRealPath(UnixPath.java:825)\n\tat org.apache.lucene.core@9.4.2/org.apache.lucene.store.NativeFSLockFactory.obtainFSLock(NativeFSLockFactory.java:94)\n\tat org.apache.lucene.core@9.4.2/org.apache.lucene.store.FSLockFactory.obtainLock(FSLockFactory.java:43)\n\tat org.apache.lucene.core@9.4.2/org.apache.lucene.store.BaseDirectory.obtainLock(BaseDirectory.java:44)\n\tat org.elasticsearch.server@8.6.0/org.elasticsearch.env.NodeEnvironment$NodeLock.<init>(NodeEnvironment.java:223)\n\t... 7 more\n\tSuppressed: java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/node.lock\n\t\tat java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:90)\n\t\tat java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)\n\t\tat java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)\n\t\tat java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:218)\n\t\tat java.base/java.nio.file.Files.newByteChannel(Files.java:380)\n\t\tat java.base/java.nio.file.Files.createFile(Files.java:658)\n\t\tat org.apache.lucene.core@9.4.2/org.apache.lucene.store.NativeFSLockFactory.obtainFSLock(NativeFSLockFactory.java:84)\n\t\t... 10 more\n"}
ERROR: Elasticsearch did not exit normally - check the logs at /usr/share/elasticsearch/logs/elasticsearch-5roiysme4074.log
ERROR: Elasticsearch exited unexpectedly
2. 分析过程
2.1 字面分析问题
"error.message":"failed to obtain node locks, tried [/usr/share/elasticsearch/data]; maybe these locations are not writable or multiple nodes were started on the same data path?","error.stack_trace":"java.lang.IllegalStateException: failed to obtain node locks, tried [/usr/share/elasticsearch/data]; maybe these locations are not writable or multiple nodes were started on the same data path?
从这个字面的分析来看,是es启动会获取一个节点锁(/usr/share/elasticsearch/data), 这个路径。 无法获取到。给的错误的原因是这个目录无法写入或者多个节点共享这个目录。
2.2 存储路径分析
这个路径是有hostpath csi提供的storageClass,它最终映射到本地的一个目录,如下图.
2.3 使用关键字搜索
使用bing 搜索"failed to obtain node locks"
比较多的搜索结果指向了es的这个配置: node.max_local_storage_nodes, 这个配置是说允许多个es实例部署到同一个目录。
分析: 这里感觉和实际不是很符合,没有其他实例也使用这个目录,因为在另外一个环境也类似的单节点的部署,也没有报错。但是两者使用的存储不一样。 一个是hostpath csi提供的, 一个是 ceph csi提供的。
因此打算再去看看代码跑错的地方
2.4 es代码抛错地方
github.com/elastic/elasticsearch/blob/9ce92a94a5894074e4ccf79e0b6c41ccd8a94170/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java#L198
2.5 文件锁实现
lucene.apache.org/core/7_3_1/core/org/apache/lucene/store/NativeFSLockFactory.html
这里提到(Specifically, on certain NFS environments the java.nio.* locks will fail) 在nfs上可能会lock fail。
这里怀疑在hostpath上也遇到了类似nfs的问题。因为nfs貌似也是挂载到本地目录使用的。
2.6 使用另外一种存储localpath进行实例部署
结果: 发现一切正常。
分析: 两者都是用本机目录进行直接提供存储。 对比了一下两者我发现。localpath创建出来的本地目录权限是777. 而hostpath的目录权限是755.
2.7 还是使用hostpath那个目录,修改777部署实例
结果: 服务能正常起来
分析: 这个时候想起来了es是不让用root启动的,是用user id=1000去启动的。 这个userid 映射到宿主机是一个xxx的账号,这个账号是没有权限去写入hostpath csi创建出来的目录的。
2.8 为什么hostpath和localpath创建出来的本地目录会有这个差别?
查看了localpath的csi实现后,找到了localpath csi对本地目录进行权限777赋予的地方。虽然搞不清楚为啥会这样子做。但是基本是这个原因了。
2.9 localpath的基本工作原因
volume的创建或者清理: 是通过启动一个pod来执行setup或者teardown脚本来进行目录处理的。将目录挂载到这个pod里。
相当于将耗时高的任务通过创建pod的方式进行处理,然后定期轮询pod的状态,来判断结果,然后执行完毕,defer里进行pod删除。
2.10 es为什么使用userid=1000启动不用root
查过官方的文档,貌似是因为安全风险,所以不建议使用root启动。本身应用用root启动会报错,除非人工加一个配置。
3. 总结
本文介绍了在k8s环境使用cloud_on_k8s进行es的自动部署过程中的一个问题定位,通过日志、搜索、代码、存储,定位到是写入数据目录权限不够的问题, 但是其实后面想想,其实es本身应该将这个permission denied的报错通过日志的形式返回给前端展示才对,而不是一个(these locations are not writable), 因为相对于前者的提示,易于理解、熟悉来说, 后者的提示语就更闲得个性化定制了。