SaltStack Job 管理 及 saltutil.signal_job 模块的问题

我的未来我决定 提交于 2020-05-07 10:59:26

原文链接:http://www.huangdc.com/66

1、什么是 Job

    在salt 中,每次执行一次salt命令就会产生一个Job ,Salt 实时管理的任务都是作为Job来执行的;在maste执行一次salt 命令,minion 就会产生一个唯一的 job id ,job id 可以在minion 机器 /var/cache/salt/minion/proc/ 查看 ,我们可以通过 job id 获取到job 的执行状态等信息

例如,测试环境如下:

    master : 192.168.202.72

    minion : 192.168.201.37

我们在master 执行一个命令: salt '192.168.201.37' cmd.run "sh /root/dc.sh"

## minion 的脚本:(为方便测试及查看,dc.sh 脚本做了 sleep 50s)
[root@localhost ~]# cat /root/dc.sh 
#!/bin/sh
i=1
while (($i<50)) ; do
    echo "$i"
    let ++i 
    sleep 1
done

## master 执行:
[root@local200-72 ~]# salt '192.168.201.37' cmd.run "sh /root/dc.sh"

## minion 查看:
[root@localhost ~]# ll /var/cache/salt/minion/proc/
total 8
-rw-r--r-- 1 root root 113 Aug  8 15:11 20150808231316960508


2、Job 常用管理命令 

(1)saltutil 模块中的 job管理⽅法  https://docs.saltstack.com/en/develop/ref/modules/all/salt.modules.saltutil.html

        saltutil.running #查看minion当前正在运⾏的jobs

        saltutil.find_job <jid> #查看指定jid的job(minion正在运⾏的jobs)

        saltutil.signal_job <jid> <single> #给指定的jid进程发送信号

        saltutil.term_job <jid> #终⽌指定的jid进程(信号为15)

        saltutil.kill_job <jid> #终⽌指定的jid进程(信号为9)

        ## salt '*' saltutil.signal_job <job id> 15

(2)salt runner 中的job管理⽅法 https://docs.saltstack.com/en/develop/topics/jobs/index.html

        salt-run jobs.active     #查看所有minion当前正在运⾏的jobs(在所有minions上运⾏saltutil.running)

        salt-run jobs.lookup_jid  <jid>  #从master jobs cache中查询指定jid的运⾏结果

        salt-run jobs.list_jobs      #列出当前master jobs cache中的所有job

    

(3)runner 模块获取job 状态信息

import salt.runner
opts = salt.config.master_config('/etc/salt/master')
runner = salt.runner.RunnerClient(opts)
runner.cmd('jobs.lookup_jid', [str(job_id)])


3、简单实例 

(1)在master 执行一个命令: salt '192.168.201.37' cmd.run "sh /root/dc.sh"  ; 并查看job 的状态

## master 窗口1执行:
[root@local200-72 ~]# salt '192.168.201.37' cmd.run "sh /root/dc.sh"

## master 窗口2查看:
[root@local200-72 ~]# salt-run jobs.active   
20150808232959218377:
    ----------
    Arguments:
        - sh /root/dc.sh
    Function:
        cmd.run
    Returned:
    Running:
        |_
          ----------
          192.168.201.37:
              15767
    Target:
        192.168.201.37
    Target-type:
        glob
    User:
        root
[root@local200-72 ~]# salt-run jobs.lookup_jid 20150808232959218377
192.168.201.37:
    1
    2
    3
   ...(省略)

## minion:
[root@localhost ~]# ll /var/cache/salt/minion/proc/
total 8
-rw-r--r-- 1 root root 113 Aug  8 15:26 20150808232959218377

(2)在master 执行一个命令: salt '192.168.201.37' cmd.run "sh /root/dc.sh"  ; 并在中途通过job id  kill 掉这个 job 

## master 窗口1执行:
[root@local200-72 ~]# salt '192.168.201.37' cmd.run "sh /root/dc.sh"


## minion:查看 job id
[root@localhost ~]# ll /var/cache/salt/minion/proc/
total 8
-rw-r--r-- 1 root root 113 Aug  8 15:44 20150808234707827791
[root@localhost ~]# ps -ef |grep dc.sh
root     16986 16985  0 15:44 ?        00:00:00 sh /root/dc.sh
[root@localhost ~]# ps -ef |grep 16985
root     16985     1  0 15:44 ?        00:00:00 /usr/bin/python2.6 /usr/bin/salt-minion -d
root     16986 16985  0 15:44 ?        00:00:00 sh /root/dc.sh

## master 查看执行 kill
[root@local200-72 ~]# salt '192.168.201.37' saltutil.signal_job 20150808234707827791 15
192.168.201.37:
    Signal 15 sent to job 20150808234707827791 at pid 16985
    
    
## minion 继续查看 进程及job id
[root@localhost ~]# ps -ef |grep dc.sh
root     16986 1 0 15:44 ?        00:00:00 sh /root/dc.sh
[root@localhost ~]# ll /var/cache/salt/minion/proc/
total 0

## 细心的朋友会发现,当我们在master 执行命令调用其他脚本的时候, saltutil.signal_job 把 job id kill 掉了,但是之前此 job 调用的脚本,并没有被kill 掉 (为什么呢)


4、Job 管理中的  signal_job 问题(如上问题)

    当我们在master 执行命令调用其他脚本的时候, saltutil.signal_job 把 job id kill 掉了,但是之前此 job 调用的脚本,并没有被kill 掉 。

    (1)linux 中,子进程及父进程的问题  , 我们可以来测试一下 linux 中是如何kill 掉进程及父进程的

          通过a.sh 脚本调用 dc.sh     (dc.sh 如上不变) , a.sh 如下:

            测试1 , kill 命令

[root@localhost ~]# cat a.sh 
#!/bin/sh

sh /root/dc.sh

echo "xx"
[root@localhost ~]# sh a.sh &
[root@localhost ~]# ps -ef |grep a.sh 
root     17559 14095  0 15:52 pts/2    00:00:00 sh a.sh
[root@localhost ~]# ps -ef |grep 17559
root     17559 14095  0 15:52 pts/2    00:00:00 sh a.sh
root     17560 17559  0 15:52 pts/2    00:00:00 sh /root/dc.sh
[root@localhost ~]# kill 17559
[root@localhost ~]# ps -ef |grep 17559
root     17633 17569  0 15:52 pts/3    00:00:00 grep 17559
[root@localhost ~]# ps -ef |grep dc.sh
root     17560     1  0 15:52 pts/2    00:00:00 sh /root/dc.sh

## 同样,当kill 掉 父进程的id 17559 后, 子进程 dc.sh 仍然在运行  (发现父进程是被kill掉了,但是子进程还活着,而且PPID已经换成为1,也就是init(1)进程)

        测试2 , kill  -9 命令  (测试结果同上)

说明: kill 一般只能杀掉单个进程,同样,我们也可以发送信号给进程组

        好,测试3,kill -- -<gpid> (kill 掉进程组)  ( 注意 两个横线<空格>一个横线<gpid>)

        查看组进程ID : ps efo pid,pgid,ppid,comm 

 好了,终于把 父进程和 子进程都kill 掉了,,,问题又来了,,,为什么 salt  saltutil.signal_job 并没有把 子进程也kill 掉呢?

    (2)修改 minion 的 /usr/lib/python2.6/site-packages/salt/modules/saltutil.py  模块代码

我们来看一下 salt saltutil 模块的代码:/usr/lib/python2.6/site-packages/salt/modules/saltutil.py

[root@local200-72 modules]# vim /usr/lib/python2.6/site-packages/salt/modules/saltutil.py

... (前面省略)
## 我们直接来看一下  signal_job 函数的代码,下面我加了注释

def signal_job(jid, sig):
    '''
    Sends a signal to the named salt job's process

    CLI Example:

    .. code-block:: bash

        salt '*' saltutil.signal_job <job id> 15
    '''
    for data in running():
        if data['jid'] == jid:
            try:
                os.kill(int(data['pid']), sig)
                ## 这个地方,你会发现 saltutil 只是使用kill 命令,所以无法kill 掉子进程 ; 
                ## 但你往下看代码,你会发现 saltutil 是有去kill child_pids ,我测试后发现,data['child_pids'] 的值是为空的; 有就是说 salt并没有获取到 child_pids 。 所以 无法kill 掉子进程。
                ## 好了,原因我们知道了,我们应该如何修改呢,当然可以修改salt的saltutil 模块代码
                ## 解决方法 使用 os.killpg(gpid,sig) 
                
                ## 将上面的 os.kill(int(data['pid']), sig) 修改为 os.killpg(os.getpgid(int(data['pid'])),sig)  即可
                
                
                if 'child_pids' in data:
                    for pid in data['child_pids']:
                        os.kill(int(pid), sig)
                return 'Signal {0} sent to job {1} at pid {2}'.format(
                        int(sig),
                        jid,
                        data['pid']
                        )
            except OSError:
                path = os.path.join(__opts__['cachedir'], 'proc', str(jid))
                if os.path.isfile(path):
                    os.remove(path)
                return ('Job {0} was not running and job data has been '
                        ' cleaned up').format(jid)
    return ''
    
... (后面省略)


    好了,问题终于解决了,,,

    总结,这里简单描述一下 salt job 的管理及遇到saltutil.signal_job 的问题

原文链接:http://www.huangdc.com/66

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!