2

image.png

插件的作用和分类

elasticsearch(简称es)是一款运用广泛的支持实时搜索的数据库。常用于全文检索、时序数据分析等领域。es本身基于模块设计,同时支持插件定制,当现有的功能和插件无法满足需求的时候,我们可以基于es的插件框架和接口,实现自己的插件,完成功能。
常见的插件,有中文分词、hdfs数据备份还原、云平台自动发现(ec2, gce, azure)等。es的收费模块xpack也是基于插件机制开发出来的,xpack提供了诸如index声明周期管理、es监控、安全认证、告警、机器学习、sql等实用功能。
使用插件实现这些功能的好处是,能够更好的整合es的基础设施,方便部署。

es的插件主要分为如下几大类:

  • 分词插件
  • 扩展restful接口
  • 集群和自动发现
  • Ingest插件
  • 存储插件
  • 脚本扩展
  • 搜索相关
  • 一般插件

这个系列将会基于笔者的实际开发经验,介绍其中扩展restful接口, Ingest插件一般插件

es提供的基础设施

在开始开发前,我们可以先了解一下,作为es的插件可以从es的中获取哪些可直接使用的基础接口。

  • Client: 相当于restful Client,但其内部实现并不是简单的http请求
  • ClusterService: 集群相关管理接口
  • ScriptService: script相关
  • ThreadPool: es管理的线程池,完成多线程或定时任务
  • NodeEnvironment: 节点环境
  • ...

实时上基础设施还有许多,笔者也不是全部了解。只是罗列一下用到过的。es官方对插件开发的文档支持还是很有限的,所以如果希望进行插件开发的话,学习已有的插件源码,甚至是跟踪到es核心源码几乎是必须的。

插件部署

插件在es中运行,除了需要实现一些接口,还需要遵循一些约定。这些约定关系到开发和部署。

在es中外部插件被部署在plugins目录下。有两种方式可以部署插件,一种是通过es自带的elasticsearch-plugin命令安装打包好的插件zip包;另一种是直接复制插件到plugins目录下。一般在开发测试阶段使用第二种方式比较方便。如果希望别人使用,则最好按照要求发布成zip包。

但无论是哪种方式,一个插件最终能被es加载必须满足几个条件:

  1. plugins目录下有对应插件的目录
  2. 目录中包含打好的jar包和依赖包
  3. 目录中包含plugin-descriptor.properties插件描述文件
  4. 目录中包含plugin-security.policy文件,声明插件会用到的java安全项

repository-hdfs插件为例,插件中的包含文件如下(省略了依赖的jar包):

repository-hdfs
├── plugin-descriptor.properties
├── plugin-security.policy
├── repository-hdfs-7.0.0.jar
...

plugin-descriptor.properties

例如

description=The HDFS repository plugin adds support for Hadoop Distributed File-System (HDFS) repositories.
version=7.0.0
name=repository-hdfs
classname=org.elasticsearch.repositories.hdfs.HdfsPlugin
java.version=1.8
elasticsearch.version=7.0.0
extended.plugins=
has.native.controller=false

plugin-descriptor.properties中最关键的部分是elasticsearch.versionclassname。前者声明了插件兼容的es版本(这个版本必须与正在运行的es版本exactly一致),后者是插件的入口类

plugin-security.policy

例如

grant {
  permission java.lang.RuntimePermission "getClassLoader";

  permission java.lang.RuntimePermission "accessDeclaredMembers";
  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";

  permission java.lang.RuntimePermission "setContextClassLoader";

  permission java.util.PropertyPermission "*", "read,write";

  permission java.lang.RuntimePermission "shutdownHooks";


  permission javax.security.auth.AuthPermission "getSubject";

  permission javax.security.auth.AuthPermission "doAs";

  permission javax.security.auth.PrivateCredentialPermission "org.apache.hadoop.security.Credentials * \"*\"", "read";

  permission java.lang.RuntimePermission "accessClassInPackage.sun.security.krb5";

  permission javax.security.auth.AuthPermission "modifyPrivateCredentials";
  permission javax.security.auth.AuthPermission "modifyPrincipals";
  permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KeyTab * \"*\"", "read";
  permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KerberosTicket * \"*\"", "read";

  permission java.lang.RuntimePermission "loadLibrary.jaas";
  permission java.lang.RuntimePermission "loadLibrary.jaas_unix";
  permission java.lang.RuntimePermission "loadLibrary.jaas_nt";
  permission javax.security.auth.AuthPermission "modifyPublicCredentials";

  permission java.security.SecurityPermission "putProviderProperty.SaslPlainServer";

  permission java.security.SecurityPermission "insertProvider.SaslPlainServer";

  permission javax.security.auth.kerberos.ServicePermission "*", "initiate";

  permission java.net.SocketPermission "*", "connect";

  permission java.net.SocketPermission "localhost:0", "listen,resolve";
};

plugin-security.policy是插件对敏感资源(比如本地文件)或者应用代码(类的方法)的访问声明。由于es是在java.security开启的情况下运行插件的,所以有很多我们悉数平常的操作都无法通过jvm的Access Control机制的检查(例如读写文件、反射、Classloader操作、Socket相关),会报莫名其妙的权限问题。es要求插件开发者在plugin-security.policy中声明对敏感资源的需求,并在代码中使用AccessController.doPrivileged包裹包含敏感操作的代码。

插件开发的难点

笔者总结了在插件开发过程中遇到的难点:

  1. jar hell。es对有冲突版本的jar包采取了绝不姑息的态度。无论是es自身依赖的lib,还是插件的lib,插件之间的lib,但凡在加载过程过发现有jar包冲突,就会报jar hell的错误。这给插件管理依赖带来了很大的困难。即使你将es自身的依赖排除在外,也无法保证系统安装的其他插件的依赖jar包与你插件的依赖jar包没有冲突。
  2. 调试困难。虽然es提供了专门用于单元测试的测试框架,但这块几乎没有什么文档,只能自己通过查看官方的插件实现来慢慢摸索。关于具体开发也没有明确的指导,基本只能靠摸索。
  3. 版本强一致。上面也提到了,plugin-descriptor.properties的中的elasticsearch.version必须与运行的es版本完全一致。这给插件的版本分发带来了困难。
  4. security.policy问题。security.policy其实很多开发者不熟悉的,往往会出现插件跑起来各种报权限不够。所以需要在调试阶段逐个去尝试,尤其是你如果依赖其他jar,你可能无法控制这些jar的内部行为,从而无法用AccessController.doPrivileged包裹其内部线程需要访问敏感资源的代码。有的时候,你发现你已经声明了plugin-security.policy,也用AccessController.doPrivileged做了你觉得正确的处理,还是出现权限不够,那么就要考虑是不是你依赖的jar包的内部线程有访问敏感资源。

P_Chou水冗
5.1k 声望256 粉丝

大数据spark/flink/hadoop/elasticsearch/kafka架构与开发