-
命名服务
我们提供了用于命名的常规服务类别。系统不使用这些类别,但它们可帮助管理员了解服务的一般用途。
这些类别显示在
/var/svc/manifest 中,它们包括:
-
application -- 较高级别的应用程序,如 apache
-
milestone -- 其他服务的集合,如
name-services
-
platform -- 特定于平台的服务,如动态重新配置守护进程
-
system -- Solaris 系统服务,如 coreadm
-
device -- 特定于设备的服务
-
network -- 网络/Internet 服务,如协议
-
site -- 特定于站点的描述
服务名称描述了所提供的服务,并且包含某种类别标识符和实际服务名称(用 '/' 分隔)。服务名称应有效地标识管理员所提供的服务。
实例名称描述了有关该实例的所有特定功能。大多数服务会提供一个“缺省”实例。某些服务(如 Oracle)可能需要根据管理配置选项来创建实例。
对于作为产品一部分提供的服务或者通常超出特定于站点的定义范围的服务,它们应在类别或服务名称中包含股票代号或后跟逗号的 Java 样式反向域前缀,以保证唯一性。
例如,cron 服务在其开始处指定了以下内容,这正符合上面所述的命名约定:
<service
name='system/cron'
type='service'
version='1'>
-
指定服务是否可以具有多个实例
如果在系统中同时运行服务的多个二进制文件会产生错误,则必须将其定义为
single_instance 服务。此标记用于通知重新启动程序无论是何种管理配置,都不要同时启动多个服务实例。
大多数配置和系统服务需要
single_instance 标记。对于可以同时运行多个配置(如使用不同的数据库源或在不同端口上运行)的服务(如 Web 服务器或数据库),不
应将其指定为
single_instance。
请在 service 块后面指定以下内容:
<single_instance />
-
指定服务模型
smf(5) 提供了各种不同的服务模型,以便为具有不同运行时特性的服务提供重新启动功能。目前,这些模型是由
svc.startd 和
inetd 重新启动程序提供的。将来,这些重新启动程序或其他重新启动程序可能还会提供其他模型。虽然本文档介绍了
svc.startd(1M) 和
inetd(1M) 模型,但若想了解有关它们所提供的应用程序模型的更多详细信息,请参见重新启动程序文档。
如果服务是由
inetd 启动的,请参见下面的
“编写
inetd 服务清单”,因为我们提供了一个可简化转换过程的工具。
svc.startd 是一个基于进程的重新启动程序。它为服务进程提供了三个完全不同的模型:
-
临时服务,通常为配置服务,并且不要求长时间运行的进程提供服务。通用临时服务负责引导时在内核中清除或加载配置属性。
有时,当难以满足合同或等待服务的方法要求时,还可以使用临时服务来解燃眉之急。我们不建议使用这种方法,而只能将其作为权宜之计。
-
等待服务,在子进程的生命周期内运行,并且会在该进程退出时重新启动。
-
合同服务,是标准系统守护进程。它们要求在启动后持续运行的进程提供服务。终止合同服务中的所有进程被视为服务错误,这将导致重新启动该服务。
缺省的服务模型为
合同,但您可以通过下列方法对其进行修改:对于临时服务,请在服务清单中指定以下内容:
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
对于等待服务,请在服务清单中指定以下内容:
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='child' />
</property_group>
-
指定服务的启动/停止方式
smf 主要通过其
方法与服务进行交互。您必须为
svc.startd 管理的服务提供
stop 和
start 方法,这些方法既可以直接调用服务二进制文件,也可以调用进行更复杂设置的脚本。对于
svc.startd 管理的服务,
refresh 方法是可选的。不同的重新启动程序可能需要不同的方法。
您可以方便地将现有
init 脚本用作服务方法的基础。我们为
svc.startd 支持的方法提供了以下规则和准则:
- 所有方法
-
shell 脚本应包含
/lib/svc/share/smf_include.sh,以获取对便利函数的访问权限并返回值定义。
-
故障必须导致返回显式错误。所有非 0 值均被视为错误。您可以使用
SMF_EXIT_* 定义为重新启动程序提供额外的信息(例如,为了避免由于配置错误而导致重新启动)。
-
方法应发出有关故障的日志消息。
svc.startd 将在服务日志文件中记录这些消息,以便管理员可确定发生了什么错误。
-
:kill 和
:true 关键字适用于所有方法定义。
:true 仅对重新启动程序返回成功消息。
:kill 可终止服务的 start 方法所启动的所有进程。所有进程的列表是由服务合同确定的。
-
start 方法
-
svc.startd 管理的所有服务均需要 start 方法。
-
只有在启用了服务并且已满足相关项的要求后,才能运行 start 方法。因此,如果服务由于任何配置错误而无法联机,start 方法将会退出并显示
SMF_EXIT_ERR_CONFIG。
-
如果服务的类型为
合同并且 start 方法返回成功消息,则该方法必须使守护进程保持运行状态,因为退出所有进程将导致重新启动该服务。
-
对于
合同和
临时服务,start 方法在提供服务后才会返回成功消息。请注意,这也适用于守护进程;守护进程不应在执行
fork() 后就从其初始进程中执行
exit(),而应等到已积累启动错误并且可以进行报告时再返回。很多
init 脚本通常在启动守护进程后立即返回,它基于以下事实:串行引导只需花“一点儿时间”来启动相关服务。既然相关服务是在服务从其 start
方法成功返回后精确启动的(通常是立即启动),因此,不接受不精确的语义。
如果无法更改守护进程/服务的代码,则在返回成功消息之前,需要对服务进行肯定性测试。如果没有其他可用的选项,请在成功返回消息之前插入一个长度合适的
sleep()。
-
stop 方法
-
svc.startd 管理的所有服务均需要 stop 方法。
-
stop 方法可以在很多不同的方案中运行,其中包括相关项已脱机、服务失败以及管理员请求禁用或重新启动。
-
因此,如果服务在执行完成后不再运行,stop 方法应返回成功消息,即使在启动执行后服务没有运行也是如此。这是因为可以在错误方案中调用 stop 方法。
-
refresh 方法
您必须为所有方法提供超时。超时应定义为在速度较慢或负载较大的系统中运行方法所需的最长时间(以秒为单位)。如果方法运行超过其超时时间,则会将其终止。如果方法可能运行无限长的时间(如大型文件系统
fsck),则可以将无限长的超时指定为 '0'。
我们强烈建议不要将用户交互(即,通过控制台输入)作为服务方法的一部分。采用这种做法的脚本在未进行任何修改的情况下将无法正常运行,因为
stdin/stdout/stderr 不是服务方法的
/dev/console。
我们提供了一组方法标记,它们可在常用属性值的方法规范中使用。
smf_method(5) 中提供了完整的列表。
缺省方法环境是从
init(1M) 中继承的,并将
PATH 设置为
/usr/sbin:/usr/bin。以
SMF_ 开头的变量是为框架使用而保留的。在
smf_method(5) 中定义的
SMF_ 变量将提供给所有方法;这些变量包括
SMF_FMRI、
SMF_METHOD 和
SMF_RESTARTER。
最后,每种方法可以指定
方法上下文,以便定义在方法执行过程中使用的系统和安全属性。我们建议尽可能使用精简的权限和安全 uid 和 gid 来启动长时间运行的服务。
下面是 start 方法规范的示例。
<exec_method
type='method'
name='start'
exec='/lib/svc/method/svc-cron'
timeout_seconds='60'>
<method_context>
<method_credential user='root' group='root' />
</method_context>
</exec_method>
-
确定要忽略的故障
如果服务本身的性能较差,或者它生成的子进程的性能较差,则您需要通知重新启动程序预计会发生某些错误,并且这些错误不会构成服务故障。
您可以指定不将子进程中的核心转储视为错误,或者指定外部终止信号不是错误。下面是指定这两者都不是错误的示例。
<property_group name='startd' type='framework'>
<propval name='ignore_error' type='astring' value='core,signal' />
</property_group>
-
指定相关项
这是服务转换过程中最棘手的部分,因为大多数相关项不是显式声明的。相关项共有两种不同的类型:
文件和
服务相关项。
首先,指定启动服务所需的其他服务。例如,服务是否要求检测网络、配置本地设备以及提供名称服务?
在确定服务所依赖的内容后,您需要指定故障传播模型。对于每个相关项,请确定在以下情况下是否应重新启动服务:
-
none -- 仅启动操作需要相关项。故障或管理操作均不需要重新启动
-
error -- 在相关项出现故障(核心转储和系统故障等)时重新启动
-
restart -- 如果重新启动相关项,则也应重新启动服务
-
refresh -- 如果刷新相关项(更改其配置),则应该重新启动服务
这些值对应于通过
restart_on 属性处理指定相关项重新启动的功能。
相关项可以按分组形式来指定。可能的分组包括:
-
require_all -- 在启动相关项之前,组中的所有服务必须处于联机或降级状态
-
require_any -- 在启动相关项之前,组中的某个服务必须处于联机或降级状态
-
optional_all -- 如果服务已启用并且能够运行(未处于维护中),在启动相关项之前,它们必须处于联机或降级状态
-
exclude_all -- 如果服务已启用并且处于联机或降级状态,则不应启动相关项
如果服务依赖于已运行的传统脚本,我们强烈建议您将传统脚本转换为
smf(5) 服务,或者由您的供应商进行转换。除此之外,您还可以在脚本所属的
重大事件中指定相关项。这绝不会从传统服务中传播错误,因此,仅适用于
restart_on=none 相关项。
最后,由于您费尽周折才找出需要某个相关项的原因,因此,请编写一些注释以便为将来的维护人员提供帮助!
<!-- Must be able to resolve hostnames. -->
<dependency
name='nameservice'
type='service'
grouping='require_all'
restart_on='none'>
<service_fmri value='svc:/milestone/name-services' />
<dependency>
-
指定相关项
如果要提供的服务依赖于未提供的其他服务,则您可以在清单中指定此服务,而无需修改不属于您的清单。即,您可以通过使用相关规范,在 Sun 提供的服务之前方便地运行您的服务。
如果尚未转换完所有相关服务,则需要转换这些服务,因为无法在传统脚本中指定相关项。
为避免发生冲突,我们建议您将服务名称放在相关项名称的前面。
例如,如果要提供的服务 (mysvc) 必须在 syslog 之前启动,请使用以下内容:
<dependent
name='mysvc_syslog'
grouping='optional_all'
restart_on='none'>
<service_fmri value='svc:/system/system-log' />
<dependent>
-
将服务插入到
重大事件中
如果以前将服务传送到
rc?.d 目录中,并且其他服务可能依赖于该服务,则应该将对应于以前传送位置的
重大事件作为相关项。
例如,如果服务以前是在运行级别 2 中启动的,则以下子句可确保在启动服务后才会将运行级别 2 视为完成。
<dependent
name='mysvc_multi-user'
grouping='require_all'
restart_on='none'>
<service_fmri value='svc:/milestone/multi-user' />
<dependent>
-
创建一个缺省实例(如果适用)
如果在第一次启动服务时不需要额外的管理干预来进行配置,则应该为该服务配置一个缺省实例。
如果该实例的配置与服务没有任何差异,则可以使用以下内容轻松完成此操作:
<create_default_instance enabled='false' />
或者,也可以显式地定义该实例。
<instance name='default' enabled='false'>
<!-- instance-specific properties, methods, etc. go here. -->
</instance>
我们建议您将所有实例作为禁用的实例提供,除非它们对系统引导至关重要。然后,可以由管理员或通过配置文件对其进行自定义(将在其他地方介绍)。
-
创建模板信息以描述服务
在 C 语言环境和手册页参考中描述至少一个通用名称。通用名称应该
- 很短(40 个字符或更少);
- 避免使用大写字母(Solaris 等商标除外);
- 避免使用标点符号;
- 避免使用文字服务(但会区分客户机和服务器)。
-
此信息是以不同形式的
svcs(1) 提供的,用于为管理员提供有关服务的简明信息,并说明从哪里获取更多技术信息。通用名称可以进行本地化。
<template>
<common_name>
<loctext xml:lang='C'>
Solaris fault manager
<loctext>
<common_name>
<documentation>
<manpage title='fmd' section='1M' manpath='/usr/share/man' />
<documentation>
<template>
-
编写/更新管理命令
如果服务已具有用于停止、启动或重新启动服务的管理命令,请对其进行更新以使用
svcadm(1M) 或 libscf 调用。如果管理命令在
smf(5) 外部显式地启动守护进程,则
smf(5) 框架将不知道正在运行其他守护进程。可能出现的问题包括守护进程发生冲突、合同错误以及使用
svcs(1) 时缺少可视性。
-
从
/etc/rc?.d 位置和
/etc/init.d 中删除脚本
如果没有删除
init 脚本,它将仍会在传统模式下运行。