Ambari插件开发 Apache Ambari项目旨在通过开发用于配置,管理和监控Apache Hadoop集群的软件,使Hadoop管理更简单。Ambari提供了一个直观的,易于使用的Hadoop管理Web UI,由其RESTful API支持。但有时候有一些自定义非官方的需求,这时就需要基于ambari做二次开发了。
Ambari是支持服务扩展的,只需要,编写自定义服务相关配置、脚本,然后扔到它的资源目录,重启服务器就可以了。听起来很简单。它的服务组织结构是分Stack / Service /Component 3层的 。
开发的一个java 服务插件定义目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [daxiang@ark1 common-services]$ pwd /var/lib/ambari-server/resources/common-services [daxiang@ark1 common-services]$ tree ARK_DATA_API/ ARK_DATA_API/ └── 0.0.1 ├── alerts.json ├── configuration │ ├── ark-data-api-env.xml │ └── ark-data-api-server.xml ├── metainfo.xml └── package ├── archive.zip ├── scripts │ ├── ark_app_api.py │ ├── params.py │ └── status_params.py └── templates └── application.properties.j2 5 directories, 9 files
其中 ARK_DATA_API
为服务名
metainfo.xml:服务定义描述文件
alerts.json: 定义了告警的配置
configuration:目录配置了服务允许所需要的环境参数 和 运行参数,这些参数可以通过ambari界面进行管理和修改。
package:包括scripts 和 templates,scripts 定义了从安装、配置、启动、停止和状态的脚本,我这里是python。template 程序运行的配置文件,通过ambari 界面管理的参数 通过这个模版进行替换,生成程序的最终配置,供程序运行使用
metainfo.xml 配置说明(我的服务比较简单,就借用参考文章 的做下记录)
ambari-server/resources/common-services/{A_SERVICE}/metainfo.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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <?xml version="1.0"?> <metainfo > <schemaVersion > 2.0</schemaVersion > <services > <service > <name > ELASTICSEARCH</name > <displayName > ElasticSearch</displayName > <comment > ElasticSearch service</comment > <version > 1.4.0</version > <components > <component > <name > ELASTICSEARCH</name > <displayName > ElasticSearch</displayName > <category > MASTER</category > <cardinality > 1</cardinality > <commandScript > <script > scripts/master.py</script > <scriptType > PYTHON</scriptType > <timeout > 600</timeout > </commandScript > </component > <component > <name > ELASTICSEARCH_NODE</name > <displayName > ElasticSearchNode</displayName > <category > SLAVE</category > <cardinality > ALL</cardinality > <auto-deploy > <enabled > false</enabled > </auto-deploy > <commandScript > <script > scripts/slave.py</script > <scriptType > PYTHON</scriptType > <timeout > 600</timeout > </commandScript > </component > </components > <osSpecifics > <osSpecific > <osFamily > any</osFamily > <packages > <package > <name > elasticsearch</name > </package > </packages > </osSpecific > </osSpecifics > <requiredServices > <service > GANGLIA</service > </requiredServices > <configuration-dependencies > <config-type > elasticsearch-env</config-type > </configuration-dependencies > <monitoringService > false</monitoringService > </service > </services > </metainfo >
服务和组件名字一定要大写,不然后面你会全部重新来过的。不要问我为什么知道,我重试了N次。
package节点的name 很重要。yum install [name]
category节点组件类型可以有MASTER,SLAVE,CLIENT几种,那p2p集群咋办?
MASTER不是必须得有,只有SLAVE也可以
cardinality节点是安装基数。1+ 最少一个 , 0-1 最多一个 ,ALL 全部
一个基础的主配置脚本如下:
master.py
master.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import sysfrom resource_management import *class Master (Script) : def install (self, env) : print 'Install the ES Master' ; def stop (self, env) : print 'Stop the ES Master' ; def start (self, env) : print 'Start the ES Master' ; def status (self, env) : print 'Status of the ES Master' ; def configure (self, env) : print 'Configure the ES Master' ; if __name__ == "__main__" : Master().execute()
举例(一个自定义jar 服务) OK,现在贴上我的配置。单jar包,已经打成rpm包,运行jar包时使用了ambari生成的配置文件,对项目中的配置文件进行覆盖。
1 2 java -jar $jar_file --spring.config.location=classpath:/,$conf_file
服务描述文件:metainfo.xml:
我这metainfo.xml
里面的关键信息包括(信息还是非常少的,少了依赖、集群等等等): 名词和显示名称; 只安装到一台机器; 脚本为scripts/ark_app_api.py
; yum安装包名ark-data-api*
metainfo.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 31 32 33 34 35 36 37 38 <?xml version="1.0"?> <metainfo > <schemaVersion > 2.0</schemaVersion > <services > <service > <name > ARK_DATA_API</name > <displayName > Ark data_api</displayName > <comment > 方舟data-api</comment > <version > 0.0.1</version > <components > <component > <name > ARK_DATA_API</name > <displayName > Ark data_api</displayName > <category > MASTER</category > <cardinality > 1</cardinality > <timelineAppid > arkb</timelineAppid > <commandScript > <script > scripts/ark_app_api.py</script > <scriptType > PYTHON</scriptType > <timeout > 600</timeout > </commandScript > </component > </components > <osSpecifics > <osSpecific > <osFamily > any</osFamily > <packages > <package > <name > ark-data-api*</name > <skipUpgrade > true</skipUpgrade > </package > </packages > </osSpecific > </osSpecifics > </service > </services > </metainfo >
其中scripts/ark_app_api.py
:也是5个方法,分别是install 、configure、start、stop、status。
install:通过ambari安装 yum install ark-data-api*
configure: 安装完成后,做的一些配置信息,比如根据模版信息生成相关文件、生成相关的文件夹,改变用户组和权限等等
start: 如何启动服务。我这是先检测服务允许状态,再通过脚本启动(这个脚本我是打包到rpm包里面的,安装时就放到相关的文件夹中了。而且脚本有维护pid操作)
stop: 如何关停服务。我这也是脚本
status: 通过检测pid文件查询服务状态。(上面的服务启停脚本维护了pid文件)
插件主脚本:ark_app_api.py
参考
package/scripts/ark_app_api.py 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import randomimport sysfrom resource_management import *from resource_management.libraries.script.script import Scriptfrom resource_management.libraries.functions import get_unique_id_and_datefrom resource_management.libraries.functions import conf_selectfrom resource_management.libraries.functions import stack_selectfrom resource_management.libraries.functions import StackFeaturefrom resource_management.libraries.functions.version import compare_versions, format_stack_versionfrom resource_management.libraries.functions.stack_features import check_stack_featurefrom resource_management.libraries.functions.security_commons import build_expectations, \ cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ FILE_TYPE_JAAS_CONF from resource_management import *from resource_management.core.logger import Loggerfrom resource_management.core.resources.system import Executefrom resource_management.libraries.functions.check_process_status import check_process_statusfrom resource_management.libraries.functions.format import formatfrom resource_management.libraries.functions.validate import call_and_match_outputfrom pickle import STOPfrom time import sleepimport ambari_simplejson as jsonclass Master (Script) : def install (self, env) : import params self.install_packages(env) Execute("chown -R {0}:{1} {2}" .format(params.app_user,params.app_group,params.install_dir)) def configure (self, env) : import params env.set_params(params) Directory(params.app_conf_dir, mode=0755 , owner=params.app_user, group=params.app_group, create_parents = True ) File(params.app_conf_dir + '/application.properties' , content=Template('application.properties.j2' ), owner=params.app_user, group=params.app_group, mode=0755 ) Directory(params.install_dir, mode=0755 , owner=params.app_user, group=params.app_group, create_parents = True ) def start (self ,env) : import params env.set_params(params) self.configure(env) no_op_test = format("ls {app_pid_path} >/dev/null 2>&1 && ps `cat {app_pid_path}` | grep `cat {app_pid_path}` >/dev/null 2>&1" ) Execute("sh {0}/startup.sh" .format(params.app_bin_dir), user=params.app_user, not_if=no_op_test ) def stop (self ,env) : import params env.set_params(params) self.configure(env) no_op_test = "" Execute("sh {0}/shutdown.sh" .format(params.app_bin_dir), user=params.app_user ) def status (self, env) : import status_params env.set_params(status_params) check_process_status(params.app_pid_path) if __name__ == "__main__" : Master().execute()
其中脚本中用到的 params.app_bin_dir
和 status_params.app_pid_path
参数,都是来自同目录下定义的参数配置文件 params.py
。status_params 就定义了一个pid 文件的路径,就不贴了。
参数定义:params.py
也可以和 主脚本写在一起,不过参数多了还是分开写方便
params.py 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 31 32 33 34 35 36 37 38 39 40 41 from resource_management.core.logger import Loggerfrom resource_management.libraries.functions import conf_selectfrom resource_management.libraries.functions import stack_selectfrom resource_management import *from resource_management.libraries.functions.get_not_managed_resources import get_not_managed_resourcesfrom resource_management.libraries.functions.expect import expectfrom ambari_commons.ambari_metrics_helper import select_metric_collector_hosts_from_hostnamesfrom ambari_commons import OSCheckfrom resource_management.libraries.resources.hdfs_resource import HdfsResourceimport osconfig = Script.get_config() exec_tmp_dir = Script.get_tmp_dir() install_dir = '/data/micro-services/8082-data-api-jar' app_conf_dir = install_dir + '/conf' app_bin_dir = install_dir + '/bin' app_user = config['configurations' ]['ark-data-api-env' ]['ark-dataapi-user' ] app_group = config['configurations' ]['ark-data-api-env' ]['ark-dataapi-group' ] app_pid = 'lryhis.pid' app_pid_path = app_bin_dir + "/" + app_pid app_server_port = config['configurations' ]['ark-data-api-server' ]['dataapi.app.server.port' ] app_log_file = install_dir + '/logs/application.log' presto_host = config['configurations' ]['ark-data-api-server' ]['dataapi.presto.host' ] presto_query_timeout = config['configurations' ]['ark-data-api-server' ]['dataapi.presto.query.timeout' ] <!-- 省略部分 --> db_host=config['configurations' ]['ark-data-api-server' ]['dataapi.db.host' ] db_admin_user=config['configurations' ]['ark-data-api-server' ]['dataapi.db.admin.user' ] db_admin_password=config['configurations' ]['ark-data-api-server' ]['dataapi.db.admin.password' ] db_pool_size=config['configurations' ]['ark-data-api-server' ]['dataapi.db.pool.size' ]
其中config = Script.get_config()
会在当前目录找相关的configurations 配置文件 比如:
1 config['configurations']['ark-data-api-server']['dataapi.app.server.port']
代表 在configurations
目录下 ark-data-api-server
文件中的 dataapi.app.server.port
配置
ambari参数配置文件:ark-data-api-server.xml 参考:
ark-data-api-server.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 31 32 33 34 35 36 <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration supports_final ="true" > <property > <name > dataapi.app.server.port</name > <value > 8082</value > <final > true</final > <description > 服务启动端口</description > </property > <property > <name > dataapi.db.admin.user</name > <value > xxxx</value > <final > false</final > <description > </description > </property > <property > <name > dataapi.db.admin.password</name > <value > xxxx</value > <property-type > PASSWORD</property-type > <final > false</final > <description > </description > </property > <property > <name > dataapi.db.pool.size</name > <value > 20</value > <final > false</final > <description > </description > </property > </configuration >
其中 supports_final="true"
和 final=true
表示 该参数不可修改
配置文件模版:application.properties.jr
这个模版文件通过特殊占位符,在定义configure 方法中替换占位符,生成application.properties
到指定目录。 而这个配置文件并不是全部配置项,而是一个覆盖的。前面说了启动时 指定了配置文件 为 classpath:/,$conf_file
,这个配置文件会覆盖classpath 目录下的配置
package/scripts/ark_app_api.py 1 2 3 4 5 6 7 File(params.app_conf_dir + '/application.properties' , content=Template('application.properties.j2' ), owner=params.app_user, group=params.app_group, mode=0755 )
最后上一个模版文件:
application.properties.j2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 server.port ={{app_server_port}} jdbc.presto.driverClassName = com.facebook.presto.jdbc.PrestoDriver jdbc.presto.url =jdbc:presto://{{presto_host}}/hive/default jdbc.presto.query.timeout ={{presto_query_timeout}} dbHost ={{db_host}} dbUser ={{db_admin_user}} dbPassword ={{db_admin_password}} jdbc.mysql.driverClassName =com.mysql.jdbc.Driver jdbc.mysql.url =jdbc:mysql://${dbHost}:3306/database_xxx?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8 jdbc.mysql.username =${dbUser} jdbc.mysql.password =${dbPassword} jdbc.mysql.maxActive = {{db_pool_size}} spring.redis.host ={{redis_host}} spring.redis.port ={{redis_port}}
从这里可以看出来,特殊占位符为双大括号, 占位符里面的值为 params.py
文件内定义的。例如 app_server_port
开发插件后的部署 定义 stacks
与参考文章不一样的是,我的服务插件直接塞到了HDP目录下。所以stacks里只填写了服务信息在哪里,其他都是在common-services 里面定义好了。
common-services 和 stacks 处于同一目录
服务的定义配置文件都放在:
1 /var/lib/ambari-server/resources/common-services/ARK_DATA_API/0.0.1
stacks定义文件放在:
1 /var/lib/ambari-server/resources/stacks/HDP/2.0.6/services/ARK_DATA_API/metainfo.xml
metainfo.xml:
告诉ambari该插件相关信息映射到 common-services/ARK_DATA_API/0.0.1下的metainfo.xml
metainfo.xml 1 2 3 4 5 6 7 8 9 <metainfo > <schemaVersion > 2.0</schemaVersion > <services > <service > <name > ARK_DATA_API</name > <extends > common-services/ARK_DATA_API/0.0.1</extends > </service > </services > </metainfo >
部署
将相关文件复制到ambari 的common-services目录和stacks目录 ,ambari-server restart
即可
参考: https://my.oschina.net/naqin/blog/389720