ambari install逻辑详解[三]
# 3.2.3 install 过程详细解析
在 execute()
方法中,当命令名称为 install
时,会调用组件的 install
方法。下面我们逐步解析 install
过程的执行机制。
# 3.2.3.1 install
方法的调用逻辑
首先在 execute()
中,通过以下逻辑选择了 install
方法:
method = self.choose_method_to_execute(self.command_name)
假设 command_name
是 "install"
,那么 method
将会对应到 self.install()
。
在 choose_method_to_execute
方法中,它会根据传入的 command_name
从当前类中动态获取到 install
方法并返回供后续调用。
然后,在 execute()
方法中:
with Environment(self.basedir, tmp_dir=Script.tmp_dir) as env:
if not self.is_hook():
self.execute_prefix_function(self.command_name, 'pre', env)
method(env)
if not self.is_hook():
self.execute_prefix_function(self.command_name, 'post', env)
2
3
4
5
6
7
8
通过 method(env)
,调用了实际的 install
方法,并且在调用之前和之后分别执行了 pre_install
和 post_install
钩子。
提示
这种写法确保每次调用 install 都自动带上前置检查与后置收尾,方便企业场景下插入环境自检、自动校验、后置通知等一系列业务流程。
# 3.2.3.2 组件中的 install
方法
以 Kafka 为例,kafka_broker.py
中的 install
方法代码如下:
class KafkaBroker(Script):
def install(self, env):
self.install_packages(env)
2
3
可以看到,KafkaBroker
类中的 install
方法直接调用了父类 Script
中的 install_packages
方法。
笔记
- 绝大多数 Ambari 组件的 install 方法都采用这种设计,调用
install_packages
可以统一支持包列表下发、操作系统适配、参数透传等全流程。 - 这种封装简化了各类组件 install 代码,也减少了维护成本。
# 3.2.3.3 Script
类的 install_packages
方法
Script
类中的 install_packages
负责处理安装任务:
def install_packages(self, env):
"""
List of packages that are required by service is received from the server
as a command parameter. The method installs all packages from this list
exclude_packages - list of regexes (possibly raw strings as well), the
packages which match the regex won't be installed.
NOTE: regexes don't have Python syntax, but simple package regexes which support only * and .* and ?
"""
config = self.get_config()
if 'host_sys_prepped' in config['ambariLevelParams']:
# do not install anything on sys-prepped host
if config['ambariLevelParams']['host_sys_prepped'] is True:
Logger.info("Node has all packages pre-installed. Skipping.")
return
pass
try:
package_list_str = config['commandParams']['package_list']
agent_stack_retry_on_unavailability = bool(config['ambariLevelParams']['agent_stack_retry_on_unavailability'])
agent_stack_retry_count = int(config['ambariLevelParams']['agent_stack_retry_count'])
if isinstance(package_list_str, basestring) and len(package_list_str) > 0:
package_list = json.loads(package_list_str)
for package in package_list:
if self.check_package_condition(package):
name = self.format_package_name(package['name'])
# HACK: On Windows, only install ambari-metrics packages using Choco Package Installer
# TODO: Update this once choco packages for hadoop are created. This is because, service metainfo.xml support
# <osFamily>any<osFamily> which would cause installation failure on Windows.
if OSCheck.is_windows_family():
if "ambari-metrics" in name:
Package(name)
else:
Package(name,
retry_on_repo_unavailability=agent_stack_retry_on_unavailability,
retry_count=agent_stack_retry_count)
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
核心逻辑:
- 读取
package_list
:从commandParams
中提取package_list
,这是由ambari-server
传递给ambari-agent
的安装包列表。package_list
通常包含一个 JSON 字符串,列出需要安装的软件包。 - 解析和检查:使用
json.loads
将package_list
解析为实际的 Python 列表,并通过check_package_condition
方法来判断该包是否符合安装条件。 - 调用
Package
资源:对于每个符合条件的软件包,调用Package
资源类来执行安装。
提示
这种机制支持了:
- 按需选择性安装(可插入条件检查)
- 支持 agent 自带重试机制
- 兼容 Windows/类 Unix 跨平台场景
- 支持未来自动跳过 sys-prepped 已装环境,灵活适配多云和镜像场景
# 3.2.3.4 Package
资源类解析
Package
类是一个资源管理类,它专门用于处理软件包的安装过程。它被设计为通过环境自动调用合适的安装逻辑:
但是实例化是父类Resource的方法
def __new__(cls, name, env=None, provider=None, **kwargs):
if isinstance(name, list):
names_list = name[:]
while len(names_list) != 1:
cls(names_list.pop(0), env, provider, **kwargs)
name = names_list[0]
env = env or Environment.get_instance()
provider = provider or getattr(cls, 'provider', None)
r_type = cls.__name__
if r_type not in env.resources:
env.resources[r_type] = {}
obj = super(Resource, cls).__new__(cls)
env.resources[r_type][name] = obj
env.resource_list.append(obj)
return obj
def __init__(self, name, env=None, provider=None, **kwargs):
if isinstance(name, list):
name = name[-1]
if hasattr(self, 'name'):
return
self.env = env or Environment.get_instance()
self.name = name
self.provider = provider or getattr(self, 'provider', None)
self.arguments = {}
for key, value in kwargs.items():
try:
arg = self._arguments[key]
except KeyError:
raise Fail("%s received unsupported argument %s" % (self, key))
else:
try:
self.arguments[key] = arg.validate(value)
except InvalidArgument, exc:
raise InvalidArgument("%s %s" % (self, exc))
if not self.env.test_mode:
self.env.run()
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
详细解析:
Package
继承自Resource
类,默认的action
被设置为install
,表示此资源的主要功能就是安装软件包。- 当
Package
被实例化时,env.run_action(self, self.action)
会被调用,这意味着它会执行install
这一操作。 - 通过这种抽象,将具体的安装操作交由
Environment
类和Provider
来完成,从而实现了更强的扩展性。
笔记
这种资源化/自动分派机制一方面兼容 Python 领域最佳实践(类似 Ansible Resource),另一方面也为未来的资源二次开发、平台迁移(如 Kubernetes CRD)留足了空间。
# 3.2.3.5 使用 Environment
来调用 find_provider
在 Environment
类中,run_action
方法用于根据资源类型选择并执行对应的 Provider
:
逻辑解读:
find_provider
会找到与资源对应的Provider
类,例如PackageProvider
。- 然后,利用
getattr
获取Provider
中对应的action
方法(例如action_install
),并调用执行。 - 这种机制允许根据环境动态地调整执行逻辑,使代码更加灵活。
提示
所以用户无需关心底层是 apt/yum 还是 zypper,Provider 系统自动适配本地平台,极大降低维护和扩展成本。
# 3.2.3.6 find_provider
函数详解
find_provider
方法的作用是根据当前系统的操作环境,动态选择合适的 Provider
类来执行操作(比如安装软件包)。:
主要步骤:
输入检查:
- 接收三个参数:
env
(系统环境)、resource
(资源类型),和一个可选的class_path
(指定Provider
路径)。
- 接收三个参数:
查找
Provider
:- 如果没有明确的
class_path
,方法会在PROVIDERS
和LIBRARY_PROVIDERS
中查找。 - 根据系统的
os_family
(比如RedHat
或Debian
),找到与资源类型对应的Provider
类。
- 如果没有明确的
动态加载:
- 找到
class_path
后,通过动态导入的方式加载对应的Provider
类,确保可以在不同系统上执行正确的操作。
- 找到
笔记
这一步确保了不同 Linux 发行版甚至 Windows 也能用同一套安装脚本,实现一套代码、多端适配。
# 3.2.3.7 PackageProvider
和实际的安装执行
文件位置:ambari-common/src/main/python/resource_management/core/providers/packaging.py
接下来的 action_install
会实际调用操作系统的命令行工具完成安装:
class PackageProvider(Provider):
def __init__(self, *args, **kwargs):
super(PackageProvider, self).__init__(*args, **kwargs)
self._pkg_manager = ManagerFactory.get()
def action_install(self):
package_name = self.get_package_name_with_version()
self._pkg_manager.install_package(package_name, self.__create_context())
def action_upgrade(self):
package_name = self.get_package_name_with_version()
self._pkg_manager.upgrade_package(package_name, self.__create_context())
def action_remove(self):
package_name = self.get_package_name_with_version()
self._pkg_manager.remove_package(package_name, self.__create_context())
def __create_context(self):
"""
Build RepoCallContext from Resource properties
"""
return RepoCallContext(
ignore_errors=self.resource.ignore_failures,
use_repos=self.resource.use_repos,
skip_repos=self.resource.skip_repos,
log_output=True if self.resource.logoutput is None or self.resource.logoutput is True else False,
retry_count=self.resource.retry_count,
retry_sleep=self.resource.retry_sleep,
retry_on_repo_unavailability=self.resource.retry_on_repo_unavailability,
retry_on_locked=self.resource.retry_on_locked
)
def get_package_name_with_version(self):
if self.resource.version:
return self.resource.package_name + '-' + self.resource.version
else:
return self.resource.package_name
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
Manager 会进行统一管理
对于具体实现,例如 YumManager
:
功能说明:
PackageProvider
的action_install
会利用_pkg_manager
的install_package
方法。_pkg_manager
可能是YumManager
或者AptManager
,它们封装了系统命令行工具(如yum
或apt-get
)的具体调用。
提示
无论环境是 RHEL/CentOS/Ubuntu/SLES,最终安装动作都交给本地包管理器执行,实现了平台无关的“一键式”体验。
# 4. 最佳实践与技巧 💡
# 4.1 优化配置技巧
在设计和编写 Ambari 组件的 Python 脚本时,我们经常需要在安装、启动、停止等关键任务之前或之后执行额外的业务逻辑。
提示
为此,Ambari 提供了 pre_
和 post_
方法机制,让你可以灵活地在主流程前后插入自定义逻辑,实现自动化、企业级的流程控制。
例如:
- 在组件安装前执行系统检查,提前终止不合规安装,避免浪费资源;
- 在安装完成后自动清理临时文件、初始化数据或生成报告,让组件上线更自动化;
- 升级前后执行兼容性检查或配置变更,保证平滑升级体验。
实践场景举例:
- 安装前验证配置
- 在
pre_install
方法中,验证依赖包、必要配置,发现问题立即终止,提升安装的安全性与成功率。
- 在
- 安装后自动化任务
- 在
post_install
中触发数据迁移、初始化操作或环境收尾,无需人工介入,效率更高。
- 在
- 跨版本升级处理
- 通过
pre_upgrade
、post_upgrade
方法处理版本冲突、兼容性检查、配置平滑切换等,保障升级过程稳定。
- 通过
笔记
这些 pre_
和 post_
方法不仅适用于 install
操作,也可以扩展到 start
、stop
、restart
等所有生命周期节点。借助这种自定义机制,你可以让集群部署和管理流程变得更细致、更安全、更契合业务场景。
# 5. 总结与延伸学习 🚀
# 5.1 内容回顾
本篇文章系统梳理了 Ambari 组件的执行逻辑,尤其是通过自定义 Python 脚本实现多种运维自动化任务的方法,内容包括:
执行流程解析:
- 从 Ambari
install
命令切入,分析了核心的execute
函数; - 解析了命令参数、环境初始化、组件主流程以及
pre_
和post_
方法的自动化逻辑插入点。
- 从 Ambari
安装过程详解:
- 以 Kafka 组件为例,详细剖析了如何通过
Script
类的install_packages
实现包的自动化安装; - 拆解了
Package
资源执行原理,以及如何动态选取Provider
(如 YumManager/AptManager)来适配不同操作系统。
- 以 Kafka 组件为例,详细剖析了如何通过
代码执行链路梳理:
KafkaBroker.install(env)
↓
Script.install_packages(env)
↓
Environment.run_action(self, action) # action 为 "install"
↓
find_provider(self, resource, provider) # 找到相应的 PackageProvider 类
↓
PackageProvider.action_install()
↓
YumManager.install_package(name) 或 AptManager.install_package(name)
2
3
4
5
6
7
8
9
10
11
代码逻辑分层:
- 各关键节点都配有分层代码示例和详细解读,让执行机制、扩展点一目了然;
- 通过
pre
和post
方法介绍,帮助读者在各操作阶段插入定制化运维逻辑。
自定义
pre_
和post_
方法:- 展示了如何用这些方法进行前置检查、日志目录准备、版本兼容等一站式操作,大幅提升了脚本自动化能力。
find_provider
函数解析:- 详细说明了根据操作系统与资源类型,如何自动定位最优 Provider,实现平台无关的包管理操作。
# 5.2 后续学习方向
通过本章内容,你已经初步掌握了 Ambari 组件的执行逻辑和自定义自动化脚本的设计思路。 为了进一步深化对 Ambari 运维体系的理解,建议继续关注以下进阶主题:
提示
深入学习 stack-hooks 的最佳实践:
- 例如,如何通过公用 stack-hooks 实现 distro-select 的版本标记、批量环境自定义等运维场景。