Systemd 作为 Linux 的系统启动器,功能强大。

本文通过一个简单例子,介绍 Systemd 如何设置定时任务。这不仅实用,而且可以作为 Systemd 的上手教程。

一、定时任务

所谓定时任务,就是未来的某个或多个时点,预定要执行的任务,比如每五分钟收一次邮件、每天半夜两点分析一下日志等等。

Linux 系统通常都使用 cron 设置定时任务,但是 Systemd 也有这个功能,而且优点显著。

  • 自动生成日志,配合 Systemd 的日志工具,很方便除错
  • 可以设置内存和 CPU 的使用额度,比如最多使用50%的 CPU
  • 任务可以拆分,依赖其他 Systemd 单元,完成非常复杂的任务

下面,我就来演示一个 Systemd 定时任务:每小时发送一封电子邮件。

二、邮件脚本

先写一个发邮件的脚本mail.sh


#!/usr/bin/env bash

echo "This is the body" | /usr/bin/mail -s "Subject" someone@example.com

上面代码的someone@example.com,请替换成你的邮箱地址。

然后,执行这个脚本。


$ bash mail.sh

执行后,你应该就会收到一封邮件,标题为Subject

如果你的 Linux 系统不能发邮件,建议安装 ssmtp 或者 msmtp。另外,mail命令的用法,可以参考这里

三、Systemd 单元

学习 Systemd 的第一步,就是搞懂”单元”(unit)是什么。

简单说,单元就是 Systemd 的最小功能单位,是单个进程的描述。一个个小的单元互相调用和依赖,组成一个庞大的任务管理系统,这就是 Systemd 的基本思想。

由于 Systemd 要做的事情太多,导致单元有很多不同的种类,大概一共有12种。举例来说,Service 单元负责后台服务,Timer 单元负责定时器,Slice 单元负责资源的分配。

每个单元都有一个单元描述文件,它们分散在三个目录。

  • /lib/systemd/system:系统默认的单元文件
  • /etc/systemd/system:用户安装的软件的单元文件
  • /usr/lib/systemd/system:用户自己定义的单元文件

下面的命令可以查看所有的单元文件。


# 查看所有单元
$ systemctl list-unit-files

# 查看所有 Service 单元
$ systemctl list-unit-files --type service

# 查看所有 Timer 单元
$ systemctl list-unit-files --type timer

四、单元的管理命令

下面是常用的单元管理命令。


# 启动单元
$ systemctl start [UnitName]

# 关闭单元
$ systemctl stop [UnitName]

# 重启单元
$ systemctl restart [UnitName]

# 杀死单元进程
$ systemctl kill [UnitName]

# 查看单元状态
$ systemctl status [UnitName]

# 开机自动执行该单元
$ systemctl enable [UnitName]

# 关闭开机自动执行
$ systemctl disable [UnitName]

五、Service 单元

前面说过,Service 单元就是所要执行的任务,比如发送邮件就是一种 Service。

新建 Service 非常简单,就是在/usr/lib/systemd/system目录里面新建一个文件,比如mytimer.service文件,你可以写入下面的内容。


[Unit]
Description=MyTimer

[Service]
ExecStart=/bin/bash /path/to/mail.sh

可以看到,这个 Service 单元文件分成两个部分。

[Unit]部分介绍本单元的基本信息(即元数据),Description字段给出这个单元的简单介绍(名字叫做MyTimer)。

[Service]部分用来定制行为,Systemd 提供许多字段。

  • ExecStartsystemctl start所要执行的命令
  • ExecStopsystemctl stop所要执行的命令
  • ExecReloadsystemctl reload所要执行的命令
  • ExecStartPreExecStart之前自动执行的命令
  • ExecStartPostExecStart之后自动执行的命令
  • ExecStopPostExecStop之后自动执行的命令

注意,定义的时候,所有路径都要写成绝对路径,比如bash要写成/bin/bash,否则 Systemd 会找不到。

现在,启动这个 Service。


$ sudo systemctl start mytimer.service

如果一切正常,你应该就会收到一封邮件。

六、Timer 单元

Service 单元只是定义了如何执行任务,要定时执行这个 Service,还必须定义 Timer 单元。

/usr/lib/systemd/system目录里面,新建一个mytimer.timer文件,写入下面的内容。


[Unit]
Description=Runs mytimer every hour

[Timer]
OnUnitActiveSec=1h
Unit=mytimer.service

[Install]
WantedBy=multi-user.target

这个 Timer 单元文件分成几个部分。

[Unit]部分定义元数据。

[Timer]部分定制定时器。Systemd 提供以下一些字段。

  • OnActiveSec:定时器生效后,多少时间开始执行任务
  • OnBootSec:系统启动后,多少时间开始执行任务
  • OnStartupSec:Systemd 进程启动后,多少时间开始执行任务
  • OnUnitActiveSec:该单元上次执行后,等多少时间再次执行
  • OnUnitInactiveSec: 定时器上次关闭后多少时间,再次执行
  • OnCalendar:基于绝对时间,而不是相对时间执行
  • AccuracySec:如果因为各种原因,任务必须推迟执行,推迟的最大秒数,默认是60秒
  • Unit:真正要执行的任务,默认是同名的带有.service后缀的单元
  • Persistent:如果设置了该字段,即使定时器到时没有启动,也会自动执行相应的单元
  • WakeSystem:如果系统休眠,是否自动唤醒系统

上面的脚本里面,OnUnitActiveSec=1h表示一小时执行一次任务。其他的写法还有OnUnitActiveSec=*-*-* 02:00:00表示每天凌晨两点执行,OnUnitActiveSec=Mon *-*-* 02:00:00表示每周一凌晨两点执行,具体请参考官方文档

七、[Install] 和 target

mytimer.timer文件里面,还有一个[Install]部分,定义开机自启动(systemctl enable)和关闭开机自启动(systemctl disable)这个单元时,所要执行的命令。

上面脚本中,[Install]部分只写了一个字段,即WantedBy=multi-user.target。它的意思是,如果执行了systemctl enable mytimer.timer(只要开机,定时器自动生效),那么该定时器归属于multi-user.target

所谓 Target 指的是一组相关进程,有点像 init 进程模式下面的启动级别。启动某个Target 的时候,属于这个 Target 的所有进程都会全部启动。

multi-user.target是一个最常用的 Target,意为多用户模式。也就是说,当系统以多用户模式启动时,就会一起启动mytimer.timer。它背后的操作其实很简单,执行systemctl enable mytimer.timer命令时,就会在multi-user.target.wants目录里面创建一个符号链接,指向mytimer.timer

八、定时器的相关命令

下面,启动刚刚新建的这个定时器。


$ sudo systemctl start mytimer.timer

你应该立刻就会收到邮件,然后每个小时都会收到同样邮件。

查看这个定时器的状态。


$ systemctl status mytimer.timer

查看所有正在运行的定时器。


$ systemctl list-timers

关闭这个定时器。


$ sudo systemctl stop myscript.timer

下次开机,自动运行这个定时器。


$ sudo systemctl enable myscript.timer

关闭定时器的开机自启动。


$ sudo systemctl disable myscript.timer

九、日志相关命令

如果发生问题,就需要查看日志。Systemd 的日志功能很强,提供统一的命令。


# 查看整个日志
$ sudo journalctl

# 查看 mytimer.timer 的日志
$ sudo journalctl -u mytimer.timer

# 查看 mytimer.timer 和 mytimer.service 的日志
$ sudo journalctl -u mytimer

# 从结尾开始查看最新日志
$ sudo journalctl -f

# 从结尾开始查看 mytimer.timer 的日志
$ journalctl -f -u timer.timer

十、参考链接

(完)

文档信息

Categories: Developer

Related Posts

Developer

DDOS 攻击的防范教程

一个多月前,我的个人网站遭受 DDOS 攻击,下线了50多个小时。这篇文章就来谈谈,如何应对这种攻击。 需要说明的是,我对 DDOS 并不精通,从没想过自己会成为攻击目标。攻击发生以后,很多素昧平生的朋友提供了各种帮助和建议,让我学到了很多东西。这里记录的就是对我最有帮助的一些解决方案。 一、DDOS 是什么? 首先,我来解释一下,DDOS 是什么。 举例来说,我开了一家餐厅,正常情况下,最多可以容纳30个人同时进餐。你直接走进餐厅,找一张桌子坐下点餐,马上就可以吃到东西。 很不幸,我得罪了一个流氓。他派出300个人同时涌进餐厅。这些人看上去跟正常的顾客一样,每个都说”赶快上餐”。但是,餐厅的容量只有30个人,根本不可能同时满足这么多的点餐需求,加上他们把门口都堵死了,里三层外三层,正常用餐的客人根本进不来,实际上就把餐厅瘫痪了。 这就是 DDOS 攻击,它在短时间内发起大量请求,耗尽服务器的资源,无法响应正常的访问,造成网站实质下线。 DDOS 里面的 DOS 是 denial of service(停止服务)的缩写,表示这种攻击的目的,就是使得服务中断。最前面的那个 D 是 distributed (分布式),表示攻击不是来自一个地方,而是来自四面八方,因此更难防。你关了前门,他从后门进来;你关了后门,他从窗口跳起来。 二、DDOS 的种类 DDOS 不是一种攻击,而是一大类攻击的总称。它有几十种类型,新的攻击方法还在不断发明出来。网站运行的各个环节,都可以是攻击目标。只要把一个环节攻破,使得整个流程跑不起来,就达到了瘫痪服务的目的。 其中,比较常见的一种攻击是 cc 攻击。它就是简单粗暴地送来大量正常的请求,超出服务器的最大承受量,导致宕机。我遭遇的就是 cc 攻击,最多的时候全世界大概20多个 Read more…

Developer

HTTP/2 服务器推送(Server Push)教程

HTTP/2 协议的主要目的是提高网页性能。 头信息(header)原来是直接传输文本,现在是压缩后传输。原来是同一个 TCP 连接里面,上一个回应(response)发送完了,服务器才能发送下一个,现在可以多个回应一起发送。 服务器推送(server push)是 HTTP/2 协议里面,唯一一个需要开发者自己配置的功能。其他功能都是服务器和浏览器自动实现,不需要开发者关心。 本文详细介绍服务器推送的原理和配置方法。 一、传统的网页请求方式 下面是一个非常简单的 HTML 网页文件index.html。 hello world 这个网页包含一张样式表style.css和一个图片文件https://0x1024.com/wp-content/uploads/2018/03/example.png。为了渲染这个网页,浏览器会发出三个请求。第一个请求是index.html。 GET /index.html HTTP/1.1 服务器收到这个请求,就把index.html发送给浏览器。浏览器发现里面包含了样式表和图片,于是再发出两个请求。 GET /style.css HTTP/1.1 GET /https://0x1024.com/wp-content/uploads/2018/03/example.png HTTP/1.1 这就是传统的网页请求方式。它有两个问题,一是至少需要两轮 HTTP 通信,二是收到样式文件之前,网页都会显示一片空白,这个阶段一旦超过2秒,用户体验就会非常不好。 二、传统方式的改进 一种解决办法就是把外部资源合并在网页文件里面,减少 HTTP 请求。比如,把样式表的内容写在 标签之中,把图片改成 Read more…

Developer

Nginx 容器教程

春节前,我看到 Nginx 加入了 HTTP/2 的 server push 功能,就很想试一下。 正好这些天,我在学习 Docker,就想到可以用 Nginx 容器。万一哪里改乱了,直接删掉,再重启一个容器就可以了。 下面就是我搭建 Nginx 容器的过程,以及如何加入 SSL 证书。你会看到 Docker 用来测试软件的新功能,真的很方便,很值得学习。如果你还不会 Docker,可以先看《Docker 入门教程》,非常简单,半小时以内就能学会。 一、HTTP 服务 Nginx 的最大作用,就是搭建一个 Web Server。有了容器,只要一行命令,服务器就架设好了,完全不用配置。 $ docker container run -d -p 127.0.0.2:8080:80 --rm Read more…