Contents

daemon

Daemon 守护进程服务

  • systemd
  • supervisor
  • launchd

一、supervisor

​ 是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。它是通过fork/exec的方式把这些被管理的进程当作supervisor的子进程来启动,这样只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去即可。也实现当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,可以选择是否自己启动和报警。supervisor还提供了一个功能,可以为supervisord或者每个子进程,设置一个非root的user,这个user就可以管理它对应的进程

supervisor官网:supervisord.org

安装supervisor

supervisor是用python开发的,支持yum和pip安装

[root@localhost ~]# yum install supervisor

[root@localhost ~]# pip3 install supervisor

配置文件

若没有配置文件,可以生成主配置文件模板

[root@localhost ~]# echo_supervisord_conf > /etc/supervisord.conf

# 参考链接
http://supervisord.org/configuration.html

配置模板

# 参数可以根据需求进行调整
[program:pock]
directory=/home/sugars/pock/backend
command=/home/sugars/pock/pock_venv/bin/python3 /home/sugars/pock/backend/sugars.py
autostart=true
autorestart=true
startsecs=7
user = sugars
stderr_logfile=/var/log/supervisor/pock-err.log
stdout_logfile=/var/log/supervisor/pock.log
redirect_stderr = true
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 3
stopasgroup=true
killasgroup=true

配置文件读取目录于顺序

# 配置文件明名为supervisor.comf,如果不使用`-c` 指定文件名,则自动匹配的文件路径为
1)  ../etc/supervisord.conf
2)  ../supervisord.conf
3)  $CWD/supervisord.conf
4)  $CWD/etc/supervisord.conf
5) /etc/supervisord.conf
6) /etc/supervisor/supervisord.conf
systemctl start supervisord

supervisor命令说明

supervisorctl status                查看进程运行状态 
supervisorctl start g_name      启动进程 
supervisorctl stop g_name      关闭进程 
supervisorctl restart g_name   重启进程 
supervisorctl update                重新载入配置文件(配置文件修改后使用该命令加载新的配置) 
supervisorctl shutdown             关闭
supervisord supervisorctl clear g_name 清空进程日志 
supervisorctl 进入到交互模式下。使用help查看所有命令。 
supervisorctl start/stop/restart + all 表示启动,关闭,重启所有进程
supervisorctl reload        //重新启动配置中的所有程序

新添加一个program后的操作

root@serialt:~#  supervisorctl update sugar

停止sugar服务

root@serialt:~#  supervisorctl stop sugar

启动sugar服务

root@serialt:~#  supervisorctl start sugar

重启sugar服务

root@serialt:~#  supervisorctl restart sugar

日志管理

在rhel中,supervisord的日志轮转管理是通过调用logrotate来实现的,其配置文件内容是

[root@serialt ~]# cat /etc/logrotate.d/supervisor 
/var/log/supervisor/*.log {
       missingok
       weekly
       notifempty
       nocompress
}

会自动轮转/var/log/supervisor/*.log的文件,若把用supervisor管理的日志也输入到/var/log/supervisor/里会导致日志文件被轮转成带时间戳的格式

sugar.log
sugar.log-20211019
sugar.log-20211026
sugar.log.1
sugar.log.2
sugar.log.3

两种解决办法:

1)修改supervisord的日志记录文件,同时修改supervisord的logrotate配置文件

2)supervisord管理的进程的日志不输出到/var/log/supervisor

日志压缩

supervisord本身日志只能轮转,不能用于压缩,要实现日志压缩论证,需要借用logrotate

[root@tc ~]# cat /etc/logrotate.d/sugar
/var/log/sugar/*.log {
       missingok
       daily
       notifempty
       create 0664 nobody root
       #dateext
       #dateformat .%Y%m%d
       rotate 20
       compress
       delaycompress
       copytruncate
       size 10K
}

二、systemd

常用命令

systemctl restart foo
systemctl stop foo
systemctl start foo
systemctl status foo
systemctl cat foo
systemctl daemon-reload

配置模板

[Unit]
Description=Gins
After=network-online.target
Documentation=https://serialt.github.io/
Documentation=https://serialt.github.io on-failure always

[Service]
Type=simple
WorkingDirectory=/usr/local/gin
KillSignal=SIGTERM
ExecStart=/usr/local/gins/bin/gins start
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=on-failure
RestartSec=3
TimeoutSec=50
User=gins  
Group=gins 

[Install]
WantedBy=multi-user.target

systemd 236之后的版本可以直接在systemd的unit文件里面配置StandardOutputStandardError两个参数来将相关运行日志输出到指定的文件中。

[Unit]
Description=Gins
After=network.target
Documentation=https://serialt.github.io/
Documentation=https://serialt.github.io on-failure always

[Service]
Type=simple
WorkingDirectory=/usr/local/gin
KillSignal=SIGTERM
ExecStart=/usr/local/gins/bin/gins start
ExecStop=/bin/kill -SIGTERM $MAINPID
# append类型可以在原有文件末尾继续追加内容,而file类型则是重新打开一个新文件
# 两者的区别类似于 echo >> 和 echo >
StandardOutput=append:/home/coredns/logs/coredns.log
StandardError=append:/home/coredns/logs/coredns_error.log
Restart=on-failure
RestartSec=3
TimeoutSec=50
User=gins  
Group=gins 

[Install]
WantedBy=multi-user.target

三、launchd服务

https://www.fythonfang.com/blog/2021/4/19/mac-launchd-daemons-and-agents-tutorial

基本概念

launchd 是 MacOS 上用于管理系统级或者用户级后台服务进程的管理工具。也是官方推荐的系统后台进程管理工具,就好像在 Linux 系统里,我们使用 systemd 去管理后台服务进程一样。

launchd 是一个程序,以系统常驻进程的形态运转,是 MacOS 系统启动后的第一个进程,在 Terminal 终端,键入命令 ps aux 可以看到,launchd 的进程ID(PID)是 1。也即这是系统的第一个进程

launchd 交互的工具,叫 launchctl。可以认为是它的管理客户端程序。通过该命令,我们可以发送指令给 launchd 完成对系统服务或后台进程的管理。

launchd 的管理对象都是后台进程,这些后台进程使用一种特定格式的配置文件叫 launchd.plist 来描述被管理的对象。这种文件是 XML 格式的,根据不同的运行权限,放在不同的目录里面,请看下面的表格。

目录 说明
~/Library/LaunchAgents 用户自己提供的用户级 Agent。
/Library/LaunchAgents 管理员提供的用户级 Agent。
/Library/LaunchDaemons 管理员提供的系统级 Daemon。
/System/Library/LaunchAgents 苹果官方提供的用户级 Agent。
/System/Library/LaunchDaemons 苹果官方提供的系统级 Daemon。

存放 launchd 配置文件的常用目录

通过 launchd 管理的进程,人为被分为了几个种类:

  • 服务(Services)—— 在后台运行,用以支持图形界面应用(GUI App)运行的服务进程,比如响应系统全局快捷键,或者进行网络通信等;
  • 守护进程(Daemons)—— 理论上,不属于服务的后台进程,都归为守护进程一类,不过这里特指运行在后台,且不能与用户交互图形界面产生联系的进程;
  • 代理(Agents)—— 以用户的名义,在后台运行的进程,可以和用户图形界面产生联系,比如呼起一个软件的界面,不过官方不推荐这么用。

一般文件名都以com.domain.programName.plist格式命名,不管是 Daemons 还是 Agents 格式都是一样的,只是存放位置不同。看下面一个 hello world 的例子 ~/Library/LaunchAgents/com.example.hello.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.hello</string>

    <key>ProgramArguments</key>
    <array>
        <string>/bin/echo</string>
        <string>hello world</string>
    </array>

</dict>
</plist>

上面定义了一个最简单的任务只使用了LabelProgramAgruments两个键

  • Label这是个必须的键,指定这个任务名
  • ProgramArguments是带参数的可执行文件上面等同于运行/bin/echo hello world命令,如果执行的程序不带参数可以使用Program键,但一个任务中必须包含这两个中的其中一个键

还有一些常用的键名,所有的键可参考man 5 launchd.plist或者这里

Keys Description
EnvironmentVariables 设置运行环境变量
StandardOutPath 标准输出到文件
StandardErrorPath 标准错误到文件
RunAtLoad 是否再加载的时候就运行
StartInterval 设置程序每隔多少秒运行一次
KeepAlive 是否设置程序是一直存活着 如果退出就重启
UserName 设置用户名只在 Daemons 可用
WorkingDirectory 设置工作目录
# 检查配置
$ plutil ~/Library/LaunchAgents/com.example.hello.plist
/Users/fython/Library/LaunchAgents/com.example.hello.plist: OK

# 加载配置文件
$ launchctl load ~/Library/LaunchAgents/com.example.hello.plist

# 启动服务
$ launchctl start com.example.hello

$ cat /tmp/hello.log
hello world

$ launchctl list | grep hello
-   0   com.example.hello

$ launchctl remove com.example.hello  # remove jobs

一个任务首先需要被加载(load),然后启动(start)正常运行完退出,所以我们查看/tmp目录下会有日志输出

​ 1)任务一般都要手动启动(start),如果设置了RunAtLoad或者KeepAlive则在launchctl load时就启动

​ 2)使用launchctl list列出当前加载的任务,第一列代表进程id,因为上面的程序运行一次就退出了所以显示-,第二列是程序上次运行退出的code,0代表正常退出,如果是正数代表退出的时候是有错误的,负数代表是接收到信号被终止的

​ 3)launchctl stop <service_name>可以终止一个在运行中的任务,launchctl unload <path>指定路径卸载一个任务,launchctl remove <service_name>通过服务名卸载任务

​ 4)launchctl load <path>只会加载没有被disable的任务,可以加-w参数 launchctl load -w <path>覆盖如果设置了disable的,下次开机启动一定会起来。launchctl unload <path>只会停止和卸载这个任务,但下次启动还会加载,可以使用-w参数launchctl unload -w <path>停止任务,下次启动也不会起来,也就是标记了disable

​ 5)调试一个任务可以配合使用plutil命令检查语法,设置StandardOutPathStandardErrorPathDebug键,也可以看看苹果自带的Console.app应用中的system.log

基本操作

# 罗列系统当前运行的进程清单
launchctl list

# 查看特定服务的配置信息
launchctl list com.adobe.AdobeCreativeCloud

# 加载特定的服务配置
launchctl load <file_path>

# 卸载特定的服务配置
launchctl unload -w /Library/LaunchAgents/com.adobe.AdobeCreativeCloud.plist
# 特此说明,-w 参数的作用是,如果自动执行了 load 命令尝试去恢复服务注册,则让其无效

plist 文件配置

www.launchd.info

配置参数

1)配置job名

<key>Label</key>
<string>com.local.cmd</string>

2)cmd启动配置

<key>ProgramArguments</key>
<array>
	<string>/usr/bin/rsync</string>
	<string>--archive</string>
	<string>--compress-level=9</string>
	<string>/Volumes/Macintosh HD</string>
	<string>/Volumes/Backup</string>
</array>

执行后命令

/usr/bin/rsync --archive --compress-level=9 "/Volumes/Macintosh HD" "/Volumes/Backup"

3)环境变量设置

<key>EnvironmentVariables</key>
<dict>
	<key>PATH</key>
	<string>/bin:/usr/bin:/usr/local/bin</string>
</dict>

3)设置工作目录

<key>WorkingDirectory</key>
<string>/tmp</string>

4)资源限制

<key>HardResourceLimits</key>
<dict>
	<key>FileSize</key>
	<integer>1048576</integer>
</dict>
<key>SoftResourceLimits</key>
<dict>
	<key>FileSize</key>
	<integer>524288</integer>
</dict>

5)其他

RunAtLoad

<key>RunAtLoad</key>
<true/>

每隔多少秒执行

单位 秒

<key>StartInterval</key>
<integer>3600</integer>

定时执行

<key>StartCalendarInterval</key>
<dict>
	<key>Hour</key>
	<integer>3</integer>
	<key>Minute</key>
	<integer>0</integer>
</dict>

可用参数

Month Integer Month of year (1..12, 1 being January)
Day Integer Day of month (1..31)
Weekday Integer Day of week (0..7, 0 and 7 being Sunday)
Hour Integer Hour of day (0..23)
Minute Integer Minute of hour (0..59)

示例1:

/Library/LaunchDaemons/com.fython.clash.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>

    <key>Label</key>
    <string>com.fython.clash</string>

    <key>RunAtLoad</key>
    <true/>

    <key>UserName</key>
    <string>root</string>

    <key>StandardErrorPath</key>
    <string>/Users/fython/bin/clash/stderr.log</string>

    <key>StandardOutPath</key>
    <string>/Users/fython/bin/clash/stdout.log</string>

    <key>WorkingDirectory</key>
    <string>/Users/fython/bin/clash</string>

    <key>ProgramArguments</key>
    <array>
      <string>/Users/fython/bin/clash/clash</string>
      <string>-f</string>
      <string>config.yaml</string>
      <string>-d</string>
      <string>/Users/fython/bin/clash</string>
    </array>

    <key>KeepAlive</key>
    <true/>

  </dict>
</plist>