ElasticSearch 实践过程中遇到的几个小问题

ulimit “不生效”

有一台机器的在启动 ES 的时候始终报错

1
max file descriptors [65000] for elasticsearch process is too low

但是我已经在/etc/security/limits.conf里增加了如下配置,

1
2
3
4
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
elasticsearch hard nofile 65536
elasticsearch soft nofile 65536

按理说ulimit -n的时候应该是会到看到 65536 的,但是依旧输出的是 65000。郁闷

最后受这篇文章 ulimit 到底谁说了算? 的启发,我把排查目标放在了 profile 和 bashrc 文件上面。

最终发现原来还真是有人在这台机器的/etc/bashrc的文末加了一句ulimit -n 65000,把这行去掉后就正常了。

如果你也出现了这个问题,建议排查一下以下四个文件:

1
2
3
4
/etc/profile
/etc/bashrc
~/.profile
~/.bashrc

由 X-Pack 的认证机制引起的问题

X-Pack 是一个 Elastic Stack 的扩展,将安全,警报,监视,报告和图形功能包含在一个易于安装的软件包中。在 Elasticsearch 5.0.0 之前,您必须安装单独的 Shield,Watcher 和 Marvel 插件才能获得在 X-Pack 中所有的功能。

ES、Kibana 和 X-Pack 的安装很简单,有需要的可以参看:安装 Elasticsearch、Kibana 和 X-Pack

HTTP REST API

在终端里访问 REST API 的时候

1
curl -XGET 'localhost:9200/_cat/health?v&pretty'

会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "error": {
    "root_cause": [
      {
        "type": "security_exception",
        "reason": "missing authentication token for REST request [/_cat/health?v&pretty]",
        "header": {
          "WWW-Authenticate": "Basic realm=\"security\" charset=\"UTF-8\""
        }
      }
    ],
    "type": "security_exception",
    "reason": "missing authentication token for REST request [/_cat/health?v&pretty]",
    "header": {
      "WWW-Authenticate": "Basic realm=\"security\" charset=\"UTF-8\""
    }
  },
  "status": 401
}

解决办法是添加上账号密码就好了

1
curl --user elastic:changeme -XGET 'localhost:9200/_cat/health?v&pretty'

这样就能正常访问了:

1
2
epoch      timestamp cluster  status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1487747024 15:03:44  myClusterName yellow          1         1     19  19    0    0       19             0                  -                 50.0%

这个账号密码实际上是 X-Pack 这个插件附带的认证功能

Java API

使用 X-Pack 后,使用 Java API 获取 Client 的时候会报一个类似这样的错:

1
2
3
4
5
...
NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{pyXJL2PeTtGejUwbpycDUg}{127.0.0.1}{127.0.0.1:9300}]]
...
Caused by: org.elasticsearch.ElasticsearchSecurityException: missing authentication token for action [cluster:monitor/nodes/liveness]
...

其实看到Caused by的时候基本就已经能确定是因为 X-Pack 引入的认证机制而引起的错误。

既然知道了错误原因,那么就加上用户名和密码呗!但是事情却不是想的那么简单,ES 的官方文档里并没有提到在哪里可以加用户名和密码。

那么想必 Google 一下就能解决了吧,但是这个解决的过程其实并不顺利。首先 ES 的版本比较多,如果是 ES5 之前的版本是基本没见到这个问题的,其次也不是每个人都会装 X-Pack 的啊!最后 pass 掉了几种过气了的解决方案后,终于在 ES 的社区找到了个类似的问题,最终解决的办法其实 elastic 已经写到了它的文档中,只不过不是 ElasticSearch 的文档而是 X-Pack 的文档里(唉,其实我应该早想到的 [捂脸])

解决的关键是要引入一个 jar 包 x-pack-transport-5.2.1.jar,在构造 TransportClient 时用这个 jar 包里的 PreBuiltXPackTransportClient 替换掉 PreBuiltTransportClient,然后就可以在 Settings 里定义xpack.security.user了。

解决步骤如下(以使用 Maven 来管理依赖为例):

  1. 首先在 pom.xml 文件里添加仓库和依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


   
      
      
         elasticsearch-releases
         https://artifacts.elastic.co/maven
         
            true
         
         
            false
         
      
      ...
   
   ...

   
      
      
         org.elasticsearch.client
         x-pack-transport
         {version}
      
      ...
   
   ...

 
  1. 然后在配置里加入xpack.security.user就好了
1
2
3
4
5
6
7
8
9
import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
...

TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
        .put("cluster.name", "myClusterName")
        .put("xpack.security.user", "elastic:changeme")
        ...
        .build())
    .addTransportAddress(new InetSocketTransportAddress("localhost", 9300));

正常的话做完上边两步后代码就能顺利执行了。不过我因为用的 Maven 库是公司的私服,在第一步添加依赖的时候始终加不上,而 x-pack-transport-5.2.1.jar 这个包直接以及间接依赖的包多达二十多个,一个个手动添加是不现实的,这时候只能开始处理 Maven 的配置问题。由于 Maven 平时我也只是直接使用,settings.xml 配置文件都是从前辈那里传下来的,没有仔细研究过,这次算是补补课了。

我的 ${HOME}/.m2/settings.xml 里之前 mirrors 节点的配置如下:

1
2
3
4
5
6
7
8
9
10

  
    ...
  
  
    nexus
    *
    http://my.host.com/nexus/groups/public
  

问题是由*这个配置引起的,mirrorOf的含义如下:

mirrorOf:用来表示该 mirror 是关联的哪一个仓库,其值为其关联仓库的 id。当要同时关联多个仓库时,这多个仓库之间可以用逗号隔开;当要关联所有的仓库时,可以使用 “” 表示;当要关联除某一个仓库以外的其他所有仓库时,可以表示为 “,!repositoryId”;当要关联不是 localhost 或用 file 请求的仓库时,可以表示为 “external:*”。

既然找到了问题所在,那么对应的改之即可,即 id 为 elasticsearch-releases 的这个 repository 不再会关联到 nexus 这个镜像上

1
2
3
4
5
6
7
8
9
10

  
  ...
  
  
    nexus
    *,!elasticsearch-releases
    http://my.host.com/nexus/groups/public
  

P.S.

You can also add an Authorization header to each request. If you’ve configured global authorization credentials, the Authorization header overrides the global authentication credentials. This is useful when an application has multiple users who access Elasticsearch using the same client. You can set the global token to a user that only has the transport_client role, and add the transport_client role to the individual users.

代码片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;

import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
...

TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
        .put("cluster.name", "myClusterName")
        .put("xpack.security.user", "transport_client_user:changeme")
        ...
        .build())
    .build()
    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300))
    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9301))

String token = basicAuthHeaderValue("elastic", new SecuredString("changeme".toCharArray()));

client.filterWithHeader(Collections.singletonMap("Authorization", token))
    .prepareSearch().get();

关于 SSL 可以参考 Java Client and Security

你可能感兴趣的:(elasticsearch)