ssh 状态下如何后台运行程序?谁来保证远程会话的持续性?这篇文章告诉你

2021-09-23 22:03:57 932 技术小虫有点萌

对于刚接触linux的同学,可能有这样的体验:

  • 使用ssh登录到服务器后,执行了一个耗时较长的脚本,有时因为网络不稳定或者手滑导致ssh远程连接断开,导致程序执行失败。

  • 需要长时间稳定跑的脚本。我们希望能够在linux后台跑,关闭远程连接也没事,且可以随时查看输出信息或者操作

  • tips: 以下操作基于centos7

举栗子看一下

# vi /etc/ssh/sshd_config
ClientAliveInterval 2m          # 2 minutes
ClientAliveCountMax 0            # 0 次

#重启ssh服务
# service sshd restart  

  • 编写shell 脚本,循环往一个文件里面写入数字,到120s 会断开连接
#!/bin/bash

for((i=1;i<=100000;i++));
do
echo "$i " >>num.log;
sleep 1
done
  • 超时断开 -
  • 进程已经被关闭

这里简单介绍一下ps -ef 每列的含义 字段含义如下: UID PID PPID C STIME TTY TIME CMD root 18887 18828 0 08:09 pts/0 00:00:00 grep ApacheJetspeed

ps:将某个进程显示出来 -A  显示所有程序。 -e  此参数的效果和指定”A”参数相同。 -f  显示UID,PPIP,C与STIME栏位。 grep命令是查找 中间的|是管道命令 是指ps命令与grep同时执行 UID PID PPID C STIME TTY TIME CMD root 18887 18828 0 08:09 pts/0 00:00:00 grep ApacheJetspeed

各相关信息的意义:

UID 程序被该 UID 所拥有

PID 就是这个程序的 ID

PPID 则是其上级父程序的ID

C CPU 使用的资源百分比

STIME 系统启动时间

TTY 登入者的终端机位置

TIME 使用掉的 CPU 时间。

CMD 所下达的指令为何

  • 共写入了120次,即120s
  • 原因: 在linux 里面,所有的进程都有父进程,当ssh连接到服务器上时,打开的shell就是你所有执行命令的父进程,因为你所有的命令都是基于shell里面去执行的。那么当shh断开连接的时候,父进程就退出了,由于我们没有做特殊处理,系统收到父进程的sigterm信号,父进程下面的子进程会被全部kill掉

解决方案:

  • nohup
[root@localhost ~]# nohup ./for.sh  &
[1] 6326
[root@localhost ~]# nohup: ignoring input and appending output to ‘nohup.out’
  • screen
# 使用yum安装screen
yum install screen
# 创建一个名为test的会话窗口
screen -S test
# 暂离窗口
Ctrl+a d(即按住Ctrl,依次再按a,d)
# 查看存在的会话窗口
screen -ls
# 进入窗口
screen -r test
screen -r 进程ID
# 关闭窗口
exit
# 窗口切换
Ctrl+a c :在当前screen会话中创建窗口
Ctrl+a w :窗口列表
Ctrl+a n :下一个窗口
Ctrl+a p :上一个窗口
Ctrl+a 0-9 :在第0个窗口和第9个窗口之间切换
  • tmux 相关参考 Tmux是一个优秀的终端复用软件,类似GNU Screen,但来自于OpenBSD,采用BSD授权。使用它最直观的好处就是,通过一个终端登录远程主机并运行tmux后,在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机。是BSD实现的Screen替代品,相对于Screen,它更加先进:支持屏幕切分,而且具备丰富的命令行参数,使其可以灵活、动态的进行各种布局和操作。
ctrl+b ?            显示快捷键帮助
ctrl+b 空格键       采用下一个内置布局,这个很有意思,在多屏时,用这个就会将多有屏幕竖着展示
ctrl+b !            把当前窗口变为新窗口
ctrl+b  "           模向分隔窗口
ctrl+b %            纵向分隔窗口
ctrl+b q            显示分隔窗口的编号
ctrl+b o            跳到下一个分隔窗口。多屏之间的切换
ctrl+b 上下键      上一个及下一个分隔窗口
ctrl+b C-方向键    调整分隔窗口大小
ctrl+b &           确认后退出当前tmux
ctrl+b [           复制模式,即将当前屏幕移到上一个的位置上,其他所有窗口都向前移动一个。
ctrl+b c           创建新窗口
ctrl+b n           选择下一个窗口
ctrl+b l           最后使用的窗口
ctrl+b p           选择前一个窗口
ctrl+b w           以菜单方式显示及选择窗口
ctrl+b s           以菜单方式显示和选择会话。这个常用到,可以选择进入哪个tmux
ctrl+b t           显示时钟。然后按enter键后就会恢复到shell终端状态
ctrl+b d           脱离当前会话;这样可以暂时返回Shell界面,输入tmux attach能够重新进入之前的会话
  • linux 设置定时任务开启脚本
    • cron介绍 我们经常使用的是crontab命令是cron table的简写,它是cron的配置文件,也可以叫它作业列表,我们可以在以下文件夹内找到相关配置文件。 1./var/spool/cron/ 目录下存放的是每个用户包括root的crontab任务,每个任务以创建者的名字命名 2./etc/crontab 这个文件负责调度各种管理和维护任务。 3./etc/cron.d/ 这个目录用来存放任何要执行的crontab文件或脚本。 4.我们还可以把脚本放在/etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly目录中,让它每小时/天/星期、月执行一次。

我们上面介绍的这些,只能保证脚本的常驻运行,但是并不能保证脚本异常终止后的自动重启。接下来我们聊一聊supervisor

  • supervisor Supervisor 是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。

安装参考 配置和使用参考

cd /usr/local/src
cd /usr/local/src

wget https://pypi.python.org/packages/7b/17/88adf8cb25f80e2bc0d18e094fcd7ab300632ea00b601cbbbb84c2419eae/supervisor-3.3.2.tar.gz

tar -zxvf supervisor-3.3.2.tar.gz

cd supervisor-3.3.2

python setup.py install

Tips:错误1:ImportError: No module named setuptools

## 下载pip
wget https://files.pythonhosted.org/packages/c2/f7/c7b501b783e5a74cf1768bc174ee4fb0a8a6ee5af6afa92274ff964703e0/setuptools-40.8.0.zip
unzip setuptools-40.8.0.zip 
cd setuptools-40.8.0
python setup.py install

Tips:错误2:error: Could not find suitable distribution for Requirement.parse(‘meld3>=0.6.5‘),安装meld扩展

wget https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz#md5=3ccc78cd79cffd63a751ad7684c02c91
tar zxvf meld3-1.0.2.tar.gz
cd meld3-1.0.2
python setup.py install

安装完毕

[root@localhost ~]# supervisord -v --查看版本号
3.3.2
[root@localhost ~]# echo_supervisord_conf > /etc/supervisord.conf --生成配置文件
[root@localhost ~]# supervisord -c /etc/supervisord.conf --启动
[root@localhost ~]# ps aux | grep supervisord  --查看进程
root       9214  0.0  0.9 215256  9804 ?        Ss   06:41   0:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
root       9216  0.0  0.0 112808   964 pts/0    R+   06:41   0:00 grep --color=auto supervisord
[root@localhost ~]# 
# 查看最终配置文件
[root@localhost etc]# cat supervisord.conf  |grep -v '^;' |grep -v '^$'

[unix_http_server]
file=/tmp/supervisor.sock   ; the path to the socket file
[inet_http_server]         ; inet (TCP) server disabled by default
# 注意ip的配置,后面说的很明白 如果需要局域网访问 需要写成 *:port
port=*:9001        ; ip_address:port specifier, *:port for all iface
username=admin              ; default is no username (open server)
[supervisord]
logfile=/tmp/supervisord.log ; main log file; default $CWD/supervisord.log
logfile_maxbytes=50MB        ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10           ; # of main logfile backups; 0 means none, default 10
loglevel=info                ; log level; default info; others: debug,warn,trace
pidfile=/tmp/supervisord.pid ; supervisord pidfile; default supervisord.pid
nodaemon=false               ; start in foreground if true; default false
minfds=1024                  ; min. avail startup file descriptors; default 1024
minprocs=200                 ; min. avail process descriptors;default 200
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
[include]
files = /etc/supervisord.conf.d/*.conf
[root@localhost etc]#   

通过浏览器访问一下(此处注意,如果是centos7 需要关闭防火墙systemctl stop firewalld 或者加入ip白名单均可)

  • supervisorctl的用法
supervisord : 启动supervisor

supervisorctl reload :修改完配置文件后重新启动supervisor

supervisorctl status :查看supervisor监管的进程状态

supervisorctl start all | 进程名 :启动全部或某进程

supervisorctl stop all | 进程名 :停止全部或某进程

supervisorctl stop all:停止进程,注:start、restart、stop都不会载入最新的配置文件。

supervisorctl update:根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启
# 读取有更新(增加)的配置文件,不会启动新添加的程序 
$supervisorctl reread
 # 重启配置文件修改过的程序 
$supervisorctl update
 # 查看程序状态 
$supervisorctl status
 # 启动程序 App_name 
$supervisorctl start App_name
 # 关闭程序 App_name 
$supervisorctl stop App_name
 # 重启程序 App_name 
supervisorctl -c /etc/supervisord.conf restart App_name:    
supervisorctl -c /etc/supervisord.conf start App_name:App_name_01
#supervisord **重载配置要用update ,不要用reload!

$supervisorctl restart App_name
以上命令也可以在supervisorctl Shell中执行:
$supervisorctl
supervisor>reread
supervisor> update
supervisor> status
supervisor> start App_name
supervisor> stop App_name
supervisor> restart App_name


  • 创建一个任务
[root@localhost supervisord.conf.d]# vi for.conf                                                                                                                                                                                              

; 设置进程的名称,使用 supervisorctl 来管理进程时需要使用该进程名
[program:fornumber]
command=/bin/sh /root/for.sh   ;具体命令
;numprocs=1                 ; 默认为1
;process_name=%(program_name)s   ; 默认为 %(program_name)s,即 [program:x] 中的 x
;directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录
user=root                 ; 使用 oxygen 用户来启动该进程
; 程序崩溃时自动重启,重启次数是有限制的,默认为3次
autorestart=true
redirect_stderr=true        ; 重定向输出的日志
stdout_logfile = /var/log/supervisord/tornado_server.log
loglevel=info

重载配置文件supervisorctl reload 启动服务supervisord -c /etc/supervisord.conf

在这里插入图片描述

在conf目录下,生成了num.log 文件,我刚开始也疑惑了一下,为什么日志打到了这个目录,一瞬间明白了,for.sh 里面有echo "$i " >>num.log; 就是输出当前目录,但是命令执行的时候就是以配置文件为脚本运行的目录了。所以我们写的时候最好写绝对路径。

[root@localhost supervisord.conf.d]# ls
for.conf  num.log
在这里插入图片描述
  • 设置开机自启

# vi supervisord.service 
#supervisord.service

[Unit] 
Description=Supervisor daemon

[Service] 
Type=forking 
ExecStart=/usr/bin/supervisord -c /etc/supervisord.conf 
ExecStop=/usr/bin/supervisorctl shutdown 
ExecReload=/usr/bin/supervisorctl reload 
KillMode=process 
Restart=on-failure 
RestartSec=42s

[Install] 
WantedBy=multi-user.target

将文件拷贝到/usr/lib/systemd/system/(注意文件的权限要可执行) cp supervisord.service /usr/lib/systemd/system/

[root@localhost init.d]# systemctl enable supervisord
Failed to execute operation: Access denied
# 如果出现denied 输入下面命令
[root@localhost init.d]# setenforce 0
[root@localhost init.d]# systemctl enable supervisord
Created symlink from /etc/systemd/system/multi-user.target.wants/supervisord.service to /usr/lib/systemd/system/supervisord.service.
[root@localhost init.d]# systemctl is-enabled supervisord
enabled

其他参考

https://blog.51cto.com/u_13677412/3673751

https://www.zhihu.com/question/20709809