Systemd 是 CoreOS 采用的服务进程和系统资源管理工具。它最初的设计目标主要是克服传统 Linux 主流启动程序 SysV-init 的固有设计缺陷,提高系统的启动速度。 Systemd 的设计思路借鉴了 Mac OSX 系统的启动程序 Launchd,相比其他的 SysV-init 替代方案,例如 Ubuntu 的 Upstart,Systemd 的设计更加前卫。
事实上,Systemd 是一系列系统工具的集合,其作用也远远不仅是启动操作系统,它还接管了后台服务启动、结束、状态查询,以及日志归档、设备管理、电源管理、定时任务等许多指责,并支持通过特定事件(如插入特定 USB 设备)和特定端口数据触发的 On-demand 任务。在 CoreOS 的世界里,几乎所有与应用程序打交道的工作,都会交由 Systemd 进行管理,包括运行在容器中的服务。
Systemd的设计理念
- 尽可能启动更少的进程
- 尽可能将更多的进程并行启动
- 采用 CGroup 跟踪和管理进程的生命周期
- 统一管理服务日志
Systemd的服务管理
Unit
Unit 是 Systemd 管理服务的基本单元,可以认为每个服务就是一个 Unit,并使用一个 Unit 文件定义。在 Unit 文件中需要包含相应服务的描述、属性以及需要运行的命令。
在 CoreOS 中运行服务的命令通常是一系列的容器操作,而将具体的服务进程封装在容器中。
Target
Target 是 Systemd 中用于指定服务启动组的方式,相当于 SysV-init 中的“运行级别”。每次系统启动时都会运行与当前系统具有相同级别 Target 关联的所有服务,如果服务不需要跟随系统自动启动,则完全可以忽略这个 Target 的内容。
通常来说,大多数 Linux 用户平时使用的都是“多用户模式”这个级别,对应的 Target 值为 “multi-user.target”,它有一个等效的可用值是“default.target”。
Hello Systemd
在 CoreOS 中,用户的自定义服务配置文件通常放在 /etc/systemd/system
目录中。进入这个目录,新建一个名为“hello.service”的文件,内容如下:
|
|
这里需要注意两个地方:
- ExecStart 属性只能包含一条主要命令,而在这个属性的前后可以分别使用 ExecStartPre 和 ExecStartPost 指定更多的辅助命令;ExecStop同理。
- 有些辅助命令会加上一个减号(-),表示忽略这些命令的出错。
日志管理
Systemd 通过其标准日志服务 Journald 将其管理的所有后台进程打印到 std:out(即控制台)的输出重定向到了日志文件。
日志文件是二进制格式的,必须使用 Journald 提供的配套程序 journalctl 来查看。
Journalctl
Journalctl 的使用非常简单,默认不带任何参数时会输出系统和所有后台进程的混合日志,可以通过一些参数选择性地过滤所需的内容。
- –dmesg:内核输出的日志
- –system:各类系统服务的控制台输出
- –unit xxx.service:用户指定的服务日志
高级用法:
- –follow:实时跟踪日志输出
- –since和–unit:指定显示的日志时间区间(e.g. –since “2015-07-20 12:00:00” –until “2015-07-20 12:30:00”),–since=today可以直接打印当天输出的日志。
- –reverse:反向输出日志
- –lines:查看最新的N行日志(e.g. –lines n)
- –output:改变输出日志的格式(e.g. –output json-pretty)
另外,除了使用 –unit 参数指定 Unit 名称外,还可以指定程序名称和指定进程 PID。
- $ journalctl /usr/lib/systemd/systemd
- $ journalctl _PID=1
日志大小限制
默认日志最大限制为所在文件系统容量的 10%。即:如果/var/log/journal
存储在 50GB 的根分区中,那么日志最多存储 5GB 数据。
可以通过修改 /etc/systemd/journald.conf
中的 SystemMaxUse
来指定该最大限制。比如SystemMaxUse=50M
。
服务的生命周期
- 当一个新的 Unit 文件被放入
/etc/systemd/system/
或/usr/lib/systemd/system/
目录中时,它是不会被 Systemd 识别到的;($ systemctl list-unit-files) - systemctl start|enable 激活;($ systemctl list-units
) - 使用 systemctl start 命令可以启动指定的服务,启动时会一次执行定义在 Unit 文件中的 ExecStartPre、ExecStart、ExecStartPost命令
- 使用 ststmectl stop 命令可以结束指定的服务,结束时会依次执行定义在 Unit 文件中的 ExecStopPre、ExecStop、ExecStopPost命令;
- systemctl restart 命令相当于先结束指定的服务,然后立即重新启动它;
- systemctl kill命令会立即杀死服务,而不会执行 Unit 中指定的结束命令;
- systemctl enable xxx.service 设置开机启动;
- systmectl disable xxx.service 取消开机启动;
- systemctl daemon-reload 重新加载 Unit 文件;
- systemctl reset-failed 移除已经被标记为丢失的 Unit 文件。
服务的Unit文件
服务的 Unit 文件可以分为三个配置区段,其中 Unit 段和 Install 段是所有 Unit 文件通用的,用于配置服务(或其他系统资源)的描述、依赖和随系统启动方式,而 Service 段则是服务类型的 Unit 文件(后缀为.service)特有的,用于定义服务的具体管理和操作方法。
配置区段的常用参数
- Unit
- Description: 一段描述这个 Unit 文件的文字。
- Documentation: 指定服务的文档,可以是一个或多个文档的路径。
- Requires: 依赖的其他 Unit 列表,依赖服务启动失败会导致这个服务被终止。
- Wants: 与 Requires 相似,但不考虑依赖服务是否启动成功。
- After: 与 Requires 相似,但是在列出的服务都启动完成后才会启动当前服务。
- Before: 与 After 相反。
- BindsTo: 与 Requires 相似,但是关联性更强。
- Part Of: 这是一个 BindTo 作用的子集,仅在列出的任何模块失败或重启时,终止或重启当前服务,而不会随列出的模块的启动而启动。
- OnFailure: 当这个模块启动失败时,就会自动启动列出的每个模块。
- Conflicts: 与这个模块有冲突的模块,如果列出的模块中有已经在运行的,这个服务就不能启动;反之亦然。
- Install
- WantedBy: 和前面的 Wants 相似,只是后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。
- RequiredBy: 和前面的 Requires 相似,只是后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。
- Also: 当这个服务被 enable/disable 时,将自动 enable/disable 后面列出的每个模块。
- Service
- Type: 服务的类型,常用的有 simple 和 forking。
- RemainAfterExix: 值为 true 或 false(也可以写为 yes 或 no),默认为 false。当配置值为 true 时,Systemd 只会负责启动服务进程,之后即便服务进程退出了,Systemd也仍然会认为这个服务还在运行中。这个配置主要是提供给一些并非常驻内存,而是启动注册后立即退出,燃火等待消息按需启动的特许类型服务使用的。
- ExecStart: 这个参数是疾呼每个”.service”文件都会有的,指定服务启动的主要命令,在每个配置文件中只能使用一次。
- ExecStartPre: 指定在启动执行 ExecStart 命令前的准备工作,在同一个配置文件中可以有多个。
- ExecStartPost: 指定在启动执行 ExecStart 命令后的首位工作,在同一个配置文件中可以有多个。
- TimeoutStartSec: 启动服务时的超时时间。Docker 第一次运行时可能会需要从网络上下载服务的镜像文件,因此造成比较严重的延时,建议将值指定为 0,从而关闭检测。
- ExecStop: 停止服务所需要执行的主要命令,在每个配置文件中只能有一个。
- ExecStopPost: 指定在 ExecStop 命令执行后的收尾工作,在同一个配置文件中可以有多个。
- TimeoutStopSec: 停止服务时的超时时间。
- Restart: 这个值用语指定在什么情况下需要重启服务进程。常用的值有 no、on-success、on-failure、on-abnormal、on-abort 和 always。默认值为 no。如下表格表示分别在哪些情况下,服务会被重新启动。
- RestartSec: 如果服务需要被重启,这个参数的值为服务被重启前的等待秒数。
- ExecReload: 重新加载服务所需执行的主要命令。
- Environment: 为服务添加环境变量。
- EnviromentFile: 指定加载一个包含服务所需的环境变量列表的文件,文件中的每一行都是一个环境变量的定义。
- Nice: 服务的进程优先级,值越小优先级越高,默认为0。其中-20 为最高优先级,19为最低优先级。
- WorkingDirectory: 指定服务的工作目录。
- RootDirectory: 指定服务进程的根目录。如果配置了这个参数,服务将无法访问指定目录以外的任何文件。
- User: 指定运行服务的用户,会影响服务对本地文件系统的访问权限。
- Group: 指定运行服务的用户组,会影响服务对本地文件系统的访问权限。
- MountFlags: 之歌值其实是服务的 Mount Namespace 的配置,会影响服务进程上下文中挂载点的信息,即服务是否会继承主机上已有的挂载点,以及如果服务运行时执行了挂载或卸载设备的操作,是否会真实地在主机上产生效果。可选值是 shared、slava 或 private。
- LimitCPU/LimitSTACK/LimitNOFILE/LimitNPROC 等,限制特定服务可用的系统资源量。
Systemd 中用于兼容 SysV-init 运行级别的目标 Unit 文件
SysV-init 运行级别 | Systemd 运行目标 | 注释 |
---|---|---|
0 | runlevel0.target, poweroff.target | 关闭系统 |
1 | runlevel1.target, rescue.target | 单用户模式 |
2 | runlevel2.target, multi-user.target | 用于定义/域特定运行级别。默认等同于级别 3 |
3 | runlevel3.target, multi-user.target | 多用户,无图形界面,用户可以通过终端或网络登录 |
4 | runlevel4.target, multi-user.target | 用于定义/域特定运行级别。默认等同于级别 3 |
5 | runlevel5.target, graphical.target | 多用户,图形界面,通常为所有运行级别 3 的服务并启动图形界面服务 |
6 | runlevel6.target, reboot.target | 重启 |
emergency | emergency.target | 急救模式(Emergency shell) |
Service 段的 Restart 属性
服务退出原因 | no | always | on-failure | on-abnormal | on-abort | on-success |
---|---|---|---|---|---|---|
正常退出 | √ | √ | ||||
异常退出 | √ | √ | ||||
启动/停止超时 | √ | √ | √ | |||
被异常杀死 | √ | √ | √ | √ |
Unit 文件占位符
在 Unit 文件中,有时会需要使用到一些与运行环境有关的信息,例如节点 ID、运行服务的用户等。这些信息可以使用占位符来表示,然后在实际运行时被动态地替换为实际的值。
占位符 | 作用 |
---|---|
%n | 完整的 Unit 文件名,包括 .service 后缀名 |
%p | Unit 模版文件名中在 @ 符号之前的部分,不包括 @ 符号 |
%i | Unit 模版文件名中在 @ 符号之后的部分,不包括 @ 符号和 .service 后缀名 |
%t | 存放系统运行时文件的目录,通常都是 “/run” |
%u | 运行服务的用户们,如果 Unit 文件中没有指定,则默认为 root |
%U | 运行服务的用户 ID |
%h | 运行服务的用户 Home 目录,即 ${HOME} 环境变量的值 |
%s | 运行服务的用户默认 Shell 类型,即 ${SHELL} 环境变量 |
%m | 实际运行的节点的 Machine ID,对于运行位置敏感的服务比较有用 |
%b | Boot ID,这是一个随机数值,每个节点各不相同,并且每次节点重启时都会改变 |
%H | 实际运行节点的主机名 |
%v | 内核版本,即 “uname -r”命令输出的内容 |
%% | 在 Unit 模版文件中表示一个普通的百分号 |
Unit 模版
在现实中国年,往往有一些应用需要被复制多份运行,Systemd 定义了一种特殊的服务 Unit 文件,称之为 “Unit 模版”。
Unit 模版文件的写法与普通的服务 Unit 文件几本相同,不过 Unit 模版的文件名是以 @ 符号结尾的。通过模版启动服务实例时,需要在其文件名的 @ 字符后面添加一个参数字符串。
apache@.service
|
|
模版服务的启动也与普通的托管于 Systemd 的服务大致相同。
$ sudo systemctl start apache@8080.service