介绍
在针对 Linux 进行开发时,会出现创建在系统打开或关闭时执行的交互式脚本的任务。 在 System V 中这很容易,但在 systemd 中则需要进行调整。 但它可以有自己的计时器。
为什么我们需要目标?
人们常说目标相当于系统 V -init 中的运行级别。 我从根本上不同意。 它们有更多,您可以将包分组,例如,使用一个命令启动一组服务并执行其他操作。 而且,它们没有层次结构,只有依赖关系。
启用时运行交互式脚本的目标示例(功能概述)
目标本身的描述:
cat installer.target
[Unit]
Description=My installer
Requires=multi-user.target
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target
AllowIsolate=yes
Wants=installer.service
该目标将在 multi-user.target 启动并调用 installer.service 时启动。 然而,可能有多个这样的服务。
cat installer.service
[Unit]
# описание
Description=installer interactive dialog
[Service]
# Запустить один раз, когда остальное будет запущенно
Type=idle
# Команда запуска - вызов скрипта
ExecStart=/usr/bin/installer.sh
# Интерактивное взаимодействие с пользователем через tty3
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes
[Install]
WantedBy=installer.target
最后是正在执行的脚本的示例:
#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answer
最重要的是选择final.target - 系统启动时应到达的目标。 在启动过程中,systemd 将遍历依赖项并启动它所需的一切。
有不同的方法来选择 Final.target,我为此使用了 loader 选项。
最终的启动看起来像这样:
- 引导加载程序启动
- 引导加载程序通过传递 Final.target 参数开始启动固件
- Systemd 开始启动系统。 按顺序从 basic.target 通过其依赖项(例如,multi-user.target)转到 installer.target 或 work.target。 后者使系统以所需的模式工作
准备启动固件
创建固件时,总是会出现在启动时恢复系统状态并在关闭时保存系统状态的任务。 状态意味着配置文件、数据库转储、接口设置等。
Systemd 在同一目标中并行运行进程。 有些依赖项允许您确定脚本的启动顺序。
它在我的项目中如何工作(
- 系统启动
- settings_restore.service服务启动,它检查数据部分中是否存在settings.txt文件。 如果不存在,则将参考文件放在其位置。接下来,恢复系统设置:
- 管理员密码
- 主机名,
- 时区
- 语言环境
- 确定是否所有媒体都在使用。 默认情况下,图像尺寸较小 - 以便于复制和记录到媒体。 启动时,它会检查是否仍有未使用的空间。 如果有,则对磁盘重新分区。
- 从 MAC 地址生成机器 ID。 这对于通过 DHCP 获取相同的地址很重要
- 网络设置
- 限制日志的大小
- 外部驱动器正在准备工作(如果启用了相应的选项并且驱动器是新的)
- 启动 postgresq
- 恢复服务启动。 需要准备zabbix本身及其数据库:
- 检查是否已经有zabbix数据库。 如果没有,它是从初始化转储创建的(包含在 zabbix 中)
- 创建时区列表(需要在 Web 界面中显示它们)
- 找到当前IP,显示在issue中(邀请登录控制台)
- 邀请发生变化 - 出现“准备工作”短语
- 固件可供使用
服务文件很重要,它们决定了启动顺序
[Unit]
Description=restore system settings
Before=network.service prepare.service postgresql.service systemd-networkd.service systemd-resolved.service
[Service]
Type=oneshot
ExecStart=/usr/bin/settings_restore.sh
[Install]
WantedBy=multi-user.target
正如您所看到的,我安装了依赖项,以便我的脚本首先起作用,然后网络才会启动并且 DBMS 才会启动。
以及第二个服务(zabbix准备)
#!/bin/sh
[Unit]
Description=monitor prepare system
After=postgresql.service settings_restore.service
Before=zabbix-server.service zabbix-agent.service
[Service]
Type=oneshot
ExecStart=/usr/bin/prepare.sh
[Install]
WantedBy=multi-user.target
这里有点复杂。启动也是在 multi-user.target 中,但是在启动 postgresql DBMS 和我的setting_restore 之后。 但在启动 zabbix 服务之前。
logrotate 的定时器服务
Systemd 可以替代 CRON。 严重地。 而且,精度不是精确到分钟,而是精确到秒(如果需要的话怎么办)。或者您可以创建一个单调的计时器,由事件的超时调用。
这是一个单调的计时器,从我创建的机器启动开始计算时间。
这将需要 2 个文件
logrotateTimer.service - 服务的实际描述:
[Unit]
Description=run logrotate
[Service]
ExecStart=logrotate /etc/logrotate.conf
TimeoutSec=300
很简单 - 启动命令的描述。
第二个文件 logrotateTimer.timer 是计时器工作的地方:
[Unit]
Description=Run logrotate
[Timer]
OnBootSec=15min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.target
这里有什么:
- 定时器描述
- 首次启动时间,从系统启动开始
- 进一步推出的时期
- 对定时器服务的依赖,其实就是这个字符串让定时器
关机时的交互式脚本和您的关机目标
在另一个开发中,我必须通过我自己的目标执行更复杂的关闭机器版本,以便执行许多操作。 通常建议使用 RemainAfterExit 选项创建一次性服务,但这会阻止您创建交互式脚本。
但事实是 ExecOnStop 选项启动的命令是在 TTY 之外执行的! 检查很容易 - 粘贴 tty 命令并保存其输出。
因此,我通过我的目标实现了关闭。 我不敢保证 100% 正确,但它确实有效!
它是如何完成的(一般而言):
我创建了一个目标 my_shutdown.target,它不依赖于任何人:
my_shutdown.target
[Unit]
Description=my shutdown
AllowIsolate=yes
Wants=my_shutdown.service
当转到这个目标时(通过 systemctlisolate my_shutdwn.target),它启动了 my_shutdown.service 服务,其任务很简单 - 执行 my_shutdown.sh 脚本:
[Unit]
Description=MY shutdown
[Service]
Type=oneshot
ExecStart=/usr/bin/my_shutdown.sh
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes
WantedBy=my_shutdown.target
- 在此脚本中,我执行必要的操作。 为了灵活性和方便性,您可以向目标添加许多脚本:
my_shutdown.sh
#!/bin/bash --login
if [ -f /tmp/reboot ];then
command="systemctl reboot"
elif [ -f /tmp/shutdown ]; then
command="systemctl poweroff"
fi
#Вот здесь нужные команды
#Например, cp /home/user/data.txt /storage/user/
$command
笔记。 使用 /tmp/reboot 和 /tmp/shutdown 文件。 您不能使用参数调用目标。 只有服务是可能的。
但我使用目标是为了在工作中具有灵活性和有保证的行动顺序。
然而,最有趣的事情来了。 机器需要关闭/重新启动。 并且有 2 个选项:
- 将reboot、shutdown 和其他命令(它们仍然是systemctl 的符号链接)替换为您的脚本。在脚本内,转到my_shutdown.target。 然后目标内部的脚本直接调用systemctl,例如systemctl restart
- 一个更简单的选择,但我不喜欢它。 所有接口中,不要调用shutdown/reboot/other,而是直接调用目标systemctlisolate my_shutdown.target
我选择了第一个选项。 在 systemd 中,重新启动(如关机)是 systemd 的符号链接。
ls -l /sbin/poweroff
lrwxrwxrwx 1 root root 14 сен 30 18:23 /sbin/poweroff -> /bin/systemctl
因此,您可以用自己的脚本替换它们:
重新启动
#!/bin/sh
touch /tmp/reboot
sudo systemctl isolate my_shutdown.target
fi
来源: habr.com