上文已经简单介绍了前因,这里我们就最核心的内容:如何做,进行讲解。
二. 集成实现过程详解
Ambari下服务资源的定义结构如下图所示:
|_ stacks
|_ <stack_name>
|_ <stack_version>
metainfo.xml
|_ hooks
|_ repos
repoinfo.xml
|_ services
|_ <service_name>
metainfo.xml
metrics.json
|_ configuration
{configuration files}
|_ package
{files, scripts, templates}
下面就以Elasticsearch服务的集成为例,对所有核心的实现内容逐一讲解,elasticsearch集成项目定义结构如下图所示:
1. metainfo.xml - 服务整体描述
首先,通过metainfo.xml文件对elasticsearch这个服务进行一个标准的描述,完整文件如下所示:
<?xml version="1.0"?>
<metainfo>
<schemaVersion>2.0</schemaVersion>
<services>
<service>
<name>ELASTICSEARCH</name>
<displayName>Elasticsearch</displayName>
<comment>A highly scalable open-source full-text search and analytics engine. Including storage, searching, and analyzing big volumes of data quickly and in near real time. </comment>
<version>6.4.2</version>
<components>
<component>
<name>ELASTICSEARCH_MASTER</name>
<displayName>Elasticsearch Master</displayName>
<category>MASTER</category>
<cardinality>1+</cardinality>
<versionAdvertised>true</versionAdvertised>
<commandScript>
<script>scripts/es_master.py</script>
<scriptType>PYTHON</scriptType>
<timeout>1200</timeout>
</commandScript>
<logs>
<log>
<logId>elasticsearch_master</logId>
<primary>true</primary>
</log>
</logs>
</component>
<component>
<name>ELASTICSEARCH_SLAVE</name>
<displayName>Elasticsearch Slave</displayName>
<category>SLAVE</category>
<cardinality>0+</cardinality>
<versionAdvertised>true</versionAdvertised>
<commandScript>
<script>scripts/es_slave.py</script>
<scriptType>PYTHON</scriptType>
<timeout>1200</timeout>
</commandScript>
<logs>
<log>
<logId>elasticsearch_slave</logId>
<primary>true</primary>
</log>
</logs>
</component>
</components>
<osSpecifics>
<osSpecific>
<osFamily>any</osFamily>
<packages>
<package>
<name>elasticsearch-6.4.2</name> <!-- Not using. Please make sure the os already contains all the dependencies. -->
</package>
</packages>
</osSpecific>
</osSpecifics>
<commandScript>
<script>scripts/service_check.py</script>
<scriptType>PYTHON</scriptType>
<timeout>300</timeout>
</commandScript>
<configuration-dependencies>
<config-type>elasticsearch-config</config-type>
<config-type>elasticsearch-env</config-type>
<config-type>elasticsearch-log4j</config-type>
</configuration-dependencies>
<restartRequiredAfterChange>true</restartRequiredAfterChange>
<quickLinksConfigurations>
<quickLinksConfiguration>
<fileName>quicklinks.json</fileName>
<default>true</default>
</quickLinksConfiguration>
</quickLinksConfigurations>
</service>
</services>
</metainfo>
主要参数
=========================================================================================
定义服务的名称,显示名称,描述,版本号等核心信息:
<name>ELASTICSEARCH</name>
<displayName>Elasticsearch</displayName>
<comment>A highly scalable open-source full-text search and analytics engine. Including storage, searching, and analyzing big volumes of data quickly and in near real time. </comment>
<version>6.4.2</version>
=========================================================================================
定义服务的组件信息,这里针对elasticsearch的实现,分为了两种组件:master和slave,这里以master为例:
<component>
<name>ELASTICSEARCH_MASTER</name>
<displayName>Elasticsearch Master</displayName>
<category>MASTER</category>
<cardinality>1+</cardinality>
<versionAdvertised>true</versionAdvertised>
<commandScript>
<script>scripts/es_master.py</script>
<scriptType>PYTHON</scriptType>
<timeout>1200</timeout>
</commandScript>
<logs>
<log>
<logId>elasticsearch_master</logId>
<primary>true</primary>
</log>
</logs>
</component>
其中基础信息包括组件名称(name),组件显示名称(displayname)。
其它的几个较重要参数,包括:
1) category:描述组件是主服务、节点从服务或是客户端。
category 类别 | 默认生命周期命令实现 |
MASTER | install, start, stop, configure, status |
SLAVE | install, start, stop, configure, status |
CLIENT | install, configure, status |
2)cardinality:描述组件的数量限制。
cardinality格式 | 格式说明举例 |
数字 | <cardinality>1</cardinality>:表示只能有一个 |
数字区间 | <cardinality>0-1</cardinality>:表示最少有零个,最多有一个 |
数字单增区间 | <cardinality>1+</cardinality>:表示最少有1个 |
ALL | <cardinality>ALL</cardinality>:表示所有节点都需要 |
3)commandScript:组件生命周期命令的执行脚本实现。
=========================================================================================
所支持的OS系统定义,以及需要安装的软件包(此处作者在脚本实现过程中没有执行依赖包安装,默认各依赖均已具备):
<osSpecifics>
<osSpecific>
<osFamily>any</osFamily>
<packages>
<package>
<name>elasticsearch-6.4.2</name> <!-- Not using. Please make sure the os already contains all the dependencies. -->
</package>
</packages>
</osSpecific>
</osSpecifics>
osFamily可定义如:redhat6,redhat7,ubuntu14,ubuntu16等。
package-name,即为yum install或apt-get install的软件包名称。
=========================================================================================
服务检查的脚本实现(注意,此py脚本的命名要保持一致):
<commandScript>
<script>scripts/service_check.py</script>
<scriptType>PYTHON</scriptType>
<timeout>300</timeout>
</commandScript>
=========================================================================================
配置文件依赖,即服务安装前需要填写或修改的配置文件:
<configuration-dependencies>
<config-type>elasticsearch-config</config-type>
<config-type>elasticsearch-env</config-type>
<config-type>elasticsearch-log4j</config-type>
</configuration-dependencies>
=========================================================================================
修改配置后是否重启(一般都为true):
<restartRequiredAfterChange>true</restartRequiredAfterChange>
=========================================================================================
界面快速链接的json脚本实现(注意,此json脚本的命名要保持一致):
<quickLinksConfigurations>
<quickLinksConfiguration>
<fileName>quicklinks.json</fileName>
<default>true</default>
</quickLinksConfiguration>
</quickLinksConfigurations>
其它参数
2. configuration - 服务配置项描述
configuration的目录及内容如下图所示(不包括original,这个是作者为了方便开发放入的原始配置文件):
1)配置类型
这里可以简单分为两种类型的配置文件,及-env和其它。
1)-env.xml 配置文件
env配置要求必须存在,主要描述安装包路径,用户,组,pid文件目录,根目录等。文件内容此处截取部分以作简单举例,如下图所示:
<property>
<name>elasticsearch.download.url</name>
<value></value>
<display-name>Elasticsearch Download Url</display-name>
<description>Elasticsearch package download url. The official download url is: https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.2.tar.gz</description>
</property>
<property>
<name>elasticsearch.user</name>
<value>elasticsearch</value>
<display-name>Elasticsearch User</display-name>
<property-type>USER</property-type>
<description>Elasticsearch unix user.</description>
</property>
<property>
<name>elasticsearch.group</name>
<value>elasticsearch</value>
<display-name>Elasticsearch Group</display-name>
<property-type>GROUP</property-type>
<description>Elasticsearch unix group.</description>
</property>
<property>
<name>elasticsearch.base.dir</name>
<value>/opt/elasticsearch</value>
<display-name>Elasticsearch Base Directory</display-name>
<description>Elasticsearch base directory.</description>
</property>
<property>
<name>elasticsearch.pid.dir</name>
<value>/var/run/elasticsearch</value>
<display-name>Elasticsearch Pid Directory</display-name>
<description>Elasticsearch pid file directory.</description>
</property>
2)其它应用配置文件
其它应用配置配置文件即指应用本身所需的配置文件,例如elasticsearch的核心配置文件:elasticsearch.yml。但是我们在做实现的时候,并非要严格按照原始配置文件的格式。例如,可以将elasticsearch.yml中的内存拆分为多个部分,如es-config-general.xml和es-config-network.xml等。在作者的实现中,作者将jvm.options的内容放入了-env配置文件,然后-config.xml文件对应elasticsearch.yml。
具体的配置映射实现方式,将在第三节package 中说明。大家只要知道,此处的配置文件,就是为了在Ambari上安装服务或是安装服务后修改配置时展现配置并接受配置参数所实现的,如下图内容:
2)配置格式
上面讲了配置文件的类型定义,下面详细说下内容格式。
默认我们使用最简单的格式,如下:
<property>
<name>elasticsearch.base.dir</name>
<value>/opt/elasticsearch</value>
</property>
但是上述格式,缺少了说明,且参数文字的显示效果可能不是很好。我们可以添加<display-name>来替换<name>的显示,以打达到更好的显示效果;添加<description>来显示说明或描述。
<property>
<name>elasticsearch.base.dir</name>
<value>/opt/elasticsearch</value>
<display-name>Elasticsearch Base Directory</display-name>
<description>Elasticsearch base directory.</description>
</property>
效果如图所示:
默认情况下,<value>值不能为空,如果想允许值为空,可在<value-attributes>下添加<empty-value-valid>为true:
<property>
<name>server.basePath</name>
<value>none</value>
<value-attributes>
<empty-value-valid>true</empty-value-valid>
</value-attributes>
<display-name>Server Base Path</display-name>
</property>
<value-attributes>还支持一些常见的格式,例如boolean:
此处要注意,xml中boolean值的内容请填写false或true,但是后续代码在获取参数时,获取的是boolean类型的False和True。
<property>
<name>server.rewriteBasePath</name>
<value>false</value>
<value-attributes>
<type>boolean</type>
<overridable>false</overridable>
</value-attributes>
<display-name>Server Rewrite Base Path</display-name>
<description>xxxx</description>
</property>
<value-attributes>针对密码类型的支持:
<property>
<name>zeppelin.ssl.keystore.password</name>
<value>change me</value>
<value-attributes>
<type>password</type>
</value-attributes>
<property-type>PASSWORD</property-type>
<description>Keystore password. Can be obfuscated by the Jetty Password tool</description>
</property>
<value-attributes>针对数字类型的支持,其中type可以为int,long等,minimum和maximum为可选参数用于给定范围,unit为单位显示等,例如:
<property>
<name>ops.interval</name>
<value>5000</value>
<value-attributes>
<type>int</type>
<minimum>100</minimum>
<maximum>50000</maximum>
<unit>Milliseconds</unit>
<increment-step>100</increment-step>
<overridable>false</overridable>
</value-attributes>
<display-name>Ops Interval</display-name>
<description>Set the interval in milliseconds to sample system and process performance metrics. Minimum is 100ms. Defaults to 5000.</description>
</property>
上述格式已然能满足大部分需求了。当然,Ambari对此xml文件的支持还包括很多其它参数,有兴趣的童鞋可参考Ambari下的通用服务目录下的配置:/var/lib/ambari-server/resources/common-services。
3. package - 服务交互代码实现
package的目录及内容如下图所示:
1) scripts - 脚本实现
其中,params、service_check和status_params为必实现的脚本,分别用于参数的获取和加工、服务检查、为服务检查提供参数;es_master和es_slave是根据组件(component)区分的声明周期实现脚本,如下所示:
脚本 | 具体实现 | 要求及说明 |
params.py | 通过Script类的方法读取之前configuration文件夹中.xml文件定义的各个配置变量,有需要的话进行进一步加工,例如单位的添加,列表的解析,布尔值的转换等 | 脚本名称不可修改 |
service_check.py | 可直接使用内置方法实现,具体请见源码 | 脚本名称要与metainfo.xml中定义的保持一致 |
status_params.py | 引用pid文件目录及文件参数即可 | 内容参数可以与params中的对应项保持一致(之所以独立,是受ambari的检查机制读所限,其在做检查时是不会走params的) |
es_master.py/es_slave.py | 组件的install,start,stop等命令的实现 | 脚本名称要与metainfo.xml中定义的保持一致 |
具体内容,请参见源码,都是模式化的实现,很简单。
2) templates - 配置模板
通过.xml对配置的定义,以及params.py对配置项的处理,以jinjia2为基础的templates模板将最终完成配置映射到原始应用配置文件的过程。例如:
在elasticsearch.master.yml.j2文件中引用params中定义好的用于master节点的配置,如:
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
path.data: {{master_path_data}}
#
# Path to log files:
#
path.logs: {{master_path_logs}}
在configure方法的实现中,实现master模板和对应master配置文件的替换:
configurations = params.config['configurations']['elasticsearch-config']
File(format("{es_master_conf_dir}/elasticsearch.yml"),
content=Template("elasticsearch.master.yml.j2", configurations=configurations),
owner=params.es_user,
group=params.es_group
)
4. 其它资源文件
1) quicklinks
基础格式如下,只需改写显示名称,组件名称,对应的端口号即可。property请填写.xml中定义的端口参数名称(此处作者比较偷懒,直接走默认端口):
{
"name": "default",
"description": "default quick links configuration",
"configuration": {
"protocol": {
"type":"http",
"checks":[
]
},
"links": [
{
"name": "elasticsearch_ui",
"label": "Elasticsearch UI",
"requires_user_name": "false",
"component_name": "ELASTICSEARCH_MASTER",
"url":"%@://%@:%@",
"port":{
"http_property": "http_port",
"http_default_port": "9200",
"https_property": "http_port",
"https_default_port": "9200",
"regex": "\\w*:(\\d+)",
"site": "elastic-config"
}
}
]
}
}
2) alerts
通quicklinks一样,作者犯懒,只实现了针对端口的简单检查。只需要改写服务名称,组件名称,说明信息,端口号即可。
{
"ELASTICSEARCH": {
"service": [],
"ELASTICSEARCH_MASTER": [
{
"name": "elasticsearch_port",
"label": "Elasticsearch Port",
"description": "This host-level alert is triggered if the 9200 port is unreachable.",
"interval": 1,
"scope": "HOST",
"source": {
"type": "PORT",
"uri": "{{elasticsearch-config/http.port}}",
"default_port": 9200,
"reporting": {
"ok": {
"text": "TCP OK - {0:.3f}s response on port {1}"
},
"warning": {
"text": "TCP OK - {0:.3f}s response on port {1}",
"value": 1.5
},
"critical": {
"text": "Connection failed: {0} to {1}:{2}",
"value": 5
}
}
}
}
]
}
}
其它更为复杂的alerts.json配置同样请参考Ambari(HDP)下内置Hadoop组件的配置:/var/lib/ambari-server/resources/common-services。
5. 关于监控
对于监控,作者在此只做下简单说明。如果要求Ambari与第三方软件集成实现监控,仅仅简单实现metrics.json和widgets.json是不够的。此两个配置文件,仅能实现界面监控图表的显示和图标对应metrics信息的关联实现,但是不能实现具体数据的采集。这里官网文档也仅以几句话简单带过:
刚开始作者傻傻地以为有多智能,只要json定义了采集项,采集信息就能自动收集。实际上并不行,上图的说明并不全。首选,要有采集的实现,将数据采集送给monitor。这个部分Ambari自己的Hadoop组件有专门的Hadoop sinks做了实现。而上述第一条所阐述的内容同样需要自行通过脚本或是代码实现将信息推送至collector。之后第二条和第三条才是定义metrics和widgets。
另外Elasticsearch在6.3之后已经天然集成x-pack。用过x-pack的肯定都知道其秒级的监控实现。所以监控这块其自身实现已然很强大了。具体内容在下一章节会进行简单说明。