先说下问题背景

 目前手上开发的产品是springboot微服务的,我们用jenkins来做的部署,部署脚本如下:

1.build脚本 负责从git服务器拉脚本

2.微服务脚本:

#!/bin/sh
appname=${JOB_NAME}
echo "${WORKSPACE}"
echo "Stopping $appname Application"
kill -9 $(netstat -nlp | grep :${port} | awk '{print $7}' | awk -F"/" '{ print $1 }')
rm -f /www/app/$appname-1.0.jar
cp /var/lib/jenkins/workspace/build_folder/$appname/target/$appname.jar /www/app/$appname.jar
chmod 777 /www/app/$appname.jar
\cp /var/lib/jenkins/workspace/build_folder/$appname/src/main/resources/py/* /www/app/
chmod 777 /www/app/*.py

echo "Starting $appname Application"
BUILD_ID=${JOB_NAME}-${port} nohup java -jar -Xms128m -Xmx256m /www/app/$appname.jar --server.port=${port} --spring.profiles.active=${profiles}&
echo "Running $appname Application"

微服务脚本大概就是先去kill -9 掉原有的app进程,然后删除app.jar,copy git下新的app.jar到 /www/app/ 目录下,然后chmod,最后启动jar

对于刚开始的测试项目,这个脚本好像没有什么问题,但是这个里面其实有几个隐藏的问题。

1.当服务器重启,所有服务会down掉。特别对于aws服务器,有时还会被收掉,你的服务down掉了,你都不知道。

2.如果某些服务意外down掉,服务器也不会感受到,需要额外加逻辑去处理这种异常

3.kill -9的方式并不属于进程的正常关闭。

网上搜了很多springboot的部署资料,发现都没有关于这方面的。

所以下面的方法也是自己想出来的,并不代表行业标准解决方案。

通过写 app.service 来启动所有的jar。

好处:

centos 的app.sevice可以随机启动,这点毋容置疑,而且还可以设置down掉之后重启服务,这正是我需要的,但是必须要修改jenkins部署脚本。

先说app.service如何写:

[Unit]
Description=SM EDU Simulator Service
After=sm-edu-api-registry.service

[Service]
User=jenkins
Group=jenkins
ExecStart=/usr/bin/java -jar -Xms128m -Xmx256m /www/app/simulator.jar --server.port=8083 --spring.profiles.active=dev
ExecReload=/usr/bin/ps -ef | grep simulator | grep -v grep | awk '{print $2}' | xargs kill -9 && /usr/bin/java -jar -Xms128m -Xmx256m /www/app/simulator.jar --server.port=8083 --spring.profiles.active=dev
ExecStop=/usr/bin/ps -ef | grep simulator | grep -v grep | awk '{print $2}' | xargs kill -9
Restart=always
[Install]
WantedBy=multi-user.target

After表示在某个进程之后启动

User和Group设置主要是以某个用户去启动

Type=

  • 设置进程的启动类型,必须是下列值之一:simple, forking, oneshot, dbus, notify, idle 之一。
  • 如果设为"simple"(设置了 ExecStart= 但未设置 BusName= 时的默认值),那么表示 ExecStart= 所设定的进程就是该服务的主进程。 如果此进程需要为其他进程提供服务,那么必须在该进程启动之前先建立好通信渠道(例如套接字),以加快后继单元的启动速度。
  • "dbus"(设置了 ExecStart= 与 BusName= 时的默认值)与"simple"类似,不同之处在于该进程需要在 D-Bus 上获得一个由 BusName= 指定的名称。 systemd 将会在启动后继单元之前,首先确保该进程已经成功的获取了指定的 D-Bus 名称。设置为此类型相当于隐含的依赖于 dbus.socket 单元。
  • "oneshot"(未设置 ExecStart= 时的默认值)与"simple"类似,不同之处在于该进程必须在 systemd 启动后继单元之前退出。 此种类型通常需要设置 RemainAfterExit= 选项。
  • 如果设为"forking",那么表示 ExecStart= 所设定的进程将会在启动过程中使用 fork() 系统调用。这是传统UNIX守护进程的经典做法。 也就是当所有的通信渠道都已建好、启动亦已成功之后,父进程将会退出,而子进程将作为该服务的主进程继续运行。 对于此种进程,建议同时设置 PIDFile= 选项,以帮助 systemd 准确定位该服务的主进程,进而加快后继单元的启动速度。
  • "notify"与"simple"类似,不同之处在于该进程将会在启动完成之后通过 sd_notify(3) 之类的接口发送一个通知消息。 systemd 将会在启动后继单元之前,首先确保该进程已经成功的发送了这个消息。 如果设置为此类型,那么 NotifyAccess= 将只能设置为"all"或者"main"(默认)。 注意,目前 Type=notify 尚不能在 PrivateNetwork=yes 的情况下正常工作。
  • "idle"与"simple"类似,不同之处在于该进程将会被延迟到所有的操作都完成之后再执行。 这样可以避免控制台上的状态信息与 shell 脚本的输出混杂在一起。

ExecStart=

  • 在启动该服务时需要执行的命令行(命令+参数)。有关命令行的更多细节可参见后文的"命令行"小节。 仅在设置了 Type=oneshot 的情况下,才可以设置任意个命令行(包括零个),否则必须且只能设置一个命令行。 多个命令行既可以在同一个 ExecStart= 中设置,也可以通过设置多个 ExecStart= 来达到相同的效果。 如果设为一个空字符串,那么先前设置的所有命令行都将被清空。 如果不设置任何 ExecStart= 指令,那么必须确保设置了 RemainAfterExit=yes 指令。 命令行必须以一个绝对路径表示的可执行文件开始,并且其后的那些参数将依次作为"argv[1] argv[2] ..."传递给被执行的进程。 如果在绝对路径前加上可选的"@"前缀,那么其后的那些参数将依次作为"argv[0] argv[1] argv[2] ..."传递给被执行的进程。 如果在绝对路径前加上可选的"-"前缀,那么即使该进程以失败状态(例如非零的返回值或者出现异常)退出,也会被视为成功退出。 可以同时使用"-"与"@"前缀,且顺序任意。 如果设置了多个命令行,那么这些命令行将以其在单元文件中出现的顺序依次执行。 如果某个无"-"前缀的命令行执行失败,那么剩余的命令行将不会被执行,同时该单元将变为失败(failed)状态。 当未设置 Type=forking 时,这里设置的命令行所启动的进程将被视为该服务的主守护进程。

ExecStartPre=, ExecStartPost=

  • 设置在执行 ExecStart= 之前/后执行的命令行。语法规则与 ExecStart= 完全相同。 如果设置了多个命令行,那么这些命令行将以其在单元文件中出现的顺序依次执行。 如果某个无"-"前缀的命令行执行失败,那么剩余的命令行将不会被执行,同时该单元将变为失败(failed)状态。 仅在所有无"-"前缀的 ExecStartPre= 命令全部执行成功的前提下,才会继续执行 ExecStart= 命令。 ExecStartPost= 命令仅在服务已经被成功启动之后才会运行,判断的标准基于 Type= 选项。 具体说来,对于 Type=simple 或 Type=idle 就是主进程已经成功启动;对于 Type=oneshot 来说就是主进程已经成功退出; 对于 Type=forking 来说就是初始进程已经成功退出;对于 Type=notify 来说就是已经发送了"READY=1"; 对于 Type=dbus 来说就是已经取得了 BusName= 中设置的总线名称。 注意,不可将 ExecStartPre= 用于需要长时间执行的进程。 因为所有由 ExecStartPre= 派生的子进程都会在启动 ExecStart= 服务进程之前被杀死。

ExecReload=

  • 这是一个可选的指令,用于设置当该服务被要求重新载入配置时所执行的命令行。语法规则与 ExecStart= 完全相同。 另外,还有一个特殊的环境变量 $MAINPID 可以用于表示主进程的PID,例如可以这样使用: /bin/kill -HUP $MAINPID 注意,像上例那样,通过向守护进程发送复位信号,强制其重新加载配置文件,并不是一个好习惯。 因为这是一个异步操作,所以不适用于需要按照特定顺序重新加载配置文件的服务。 我们强烈建议将 ExecReload= 设置为一个能够确保重新加载配置文件的操作同步完成的命令行。

ExecStop=

  • 这是一个可选的指令,用于设置当该服务被要求停止时所执行的命令行。语法规则与 ExecStart= 完全相同。 执行完此处设置的命令行之后,该服务所有剩余的进程将会根据 KillMode= 的设置被杀死(参见 systemd.kill(5) 手册)。 如果未设置此选项,那么当此服务被停止时,该服务的所有进程都将会根据 KillMode= 的设置被立即全部杀死。 与 ExecReload= 一样,也有一个特殊的环境变量 $MAINPID 可以用于表示主进程的PID 一般来说,仅仅设置一个结束服务的命令,而不等待其完成,是不够的。 因为当此处设置的命令执行完之后,剩余的进程会被 SIGKILL 信号立即杀死,这可能会导致数据丢失。 因此,这里设置的命令必须是同步操作,而不能是异步操作。

Restart=

  • 当服务进程正常退出、异常退出、被杀死、超时的时候,是否重新启动该服务。 "服务进程"是指 ExecStart=, ExecStartPre=, ExecStartPost=, ExecStop=, ExecStopPost=, ExecReload= 中设置的进程。 当进程是由于 systemd 的正常操作(例如 systemctl stop|restart)而被停止时,该服务不会被重新启动。 "超时"可以是看门狗的"keep-alive ping"超时,也可以是 systemctl start|reload|stop 操作超时。
  • 该选项可以取下列值之一:no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, always "no"(默认值)表示不会被重启。"always"表示会被无条件的重启。 "on-success"表示仅在服务进程正常退出时重启,所谓"正常退出"是指: 退出码为"0",或者进程收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 信号并且退出码符合 SuccessExitStatus= 的设置。 "on-failure"表示仅在服务进程异常退出时重启,所谓"异常退出"是指: 退出码不为"0",或者进程被强制杀死(包括"core dump"以及收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 之外的其他信号), 或者进程由于看门狗或者 systemd 的操作超时而被杀死。 对于 on-failure, on-abnormal, on-abort, on-watchdog 的分别适用于哪种异常退出。

Unit 的状态

systemctl status命令用于查看系统状态和单个 Unit 的状态

[root@DaMoWang ~]# systemctl status    # 显示系统状态
[root@DaMoWang ~]# sysystemctl status httpd.service # 显示单个 Unit 的状态
[root@DaMoWang ~]# systemctl -H root@192.168.94.104 status httpd.service # 显示远程主机的某个 Unit 的状态

Unit 管理

对于用户来说,最常用的是下面这些命令,用于启动和停止 Unit(主要是 service)

[root@DaMoWang ~]# systemctl start httpd.service    # 立即启动一个服务
[root@DaMoWang ~]# systemctl stop httpd.service # 立即停止一个服务
[root@DaMoWang ~]# systemctl restart httpd.service # 重启一个服务
[root@DaMoWang ~]# systemctl kill httpd.service # 杀死一个服务的所有子进程
[root@DaMoWang ~]# systemctl reload httpd.service # 重新加载一个服务的配置文件
[root@DaMoWang ~]# systemctl daemon-reload # 重载所有修改过的配置文件
[root@DaMoWang ~]# systemctl show httpd.service # 显示某个 Unit 的所有底层参数
[root@DaMoWang ~]# systemctl show -p CPUShares httpd.service # 显示某个 Unit 的指定属性的值
[root@DaMoWang ~]# systemctl set-property httpd.service CPUShares=500 # 设置某个 Unit 的指定属性

在最后遇到一个坑,发现jenkins没有权限执行systemctl

为了不破坏安全性。

最后解决方案为 在系统级别加入service,但是在部署时,不用service。jenkins还是保持之前的脚本。

centos 随机启动脚本编写的更多相关文章

  1. redhat nginx随机启动脚本

    开机自动启动nginx 1.    扔脚本进去/etc/init.d/ 2.    授权     chmod +x nginx 3.    一旦抛出:binsh^M错误就执行编码改写     设置do ...

  2. rsync随机启动脚本

    服务端 #!/bin/sh # chkconfig: # description: Saves and restores system entropy pool for \ #create by xi ...

  3. nginx的编译安装以及启动脚本编写

    Nginx的编译安装和启动脚本的编写 Nginxd的功能强大,可以实现代理.负载均衡等企业常用的功能.下面介绍一下nginx的编译安装方法: 1. 下载 官方下载地址:http://nginx.org ...

  4. [转]CentOS开机启动脚本

    转载自http://www.2cto.com/os/201306/220559.html   我的一个Centos开机自启动脚本的制作   一.切换到/etc/init.d/   二.制作sh脚本 v ...

  5. nginx二进制编译-启动脚本编写

    首先先把这个文件上传到root目录下,并解压 #tar zxf nginx-1.11.2.tar.gz 写脚本 # vi nginx-running.sh 内容如下 #!/bin/bash #chkc ...

  6. centos LNMP第一部分环境搭建 LAMP LNMP安装先后顺序 php安装 安装nginx 编写nginx启动脚本 懒汉模式 mv /usr/php/{p.conf.default,p.conf} php运行方式SAPI介绍 第二十三节课

    centos  LNMP第一部分环境搭建 LAMP安装先后顺序  LNMP安装先后顺序 php安装 安装nginx  编写nginx启动脚本   懒汉模式  mv   /usr/local/php/{ ...

  7. linux shell 之尝试编写 企业级 启动脚本

    企业Shell面试题10:开发企业级MySQL启动脚本 说明: MySQL启动命令为: 1 /bin/sh mysqld_safe --pid-file=$mysqld_pid_file_path 2 ...

  8. 自己编写服务启动脚本(一):functions文件详细分析和说明

    本文目录: 1.几个显示函数2.action函数3.is_true和is_false函数4.confirm函数5.pid检测相关函数 5.1 checkpid.__pids_var_run和__pid ...

  9. Centos 配置开机启动脚本启动 docker 容器

    Centos 配置开机启动脚本启动 docker 容器 Intro 我们的 Centos 服务器上部署了好多个 docker 容器,因故重启的时候就会导致还得手动去手动重启这些 docker 容器,为 ...

随机推荐

  1. flume 诞生背景 数据同步

    flume一开始是cloudlera的项目 当时他们的工程师需要一次次地为客户编写工具 来实现数据的自动化导入

  2. centos Dockerfile

     FROM centos:latest   LABEL maintainer "wojiushixiaobai"   WORKDIR /opt       ENV GUAC_VER ...

  3. webdriver报不可见元素异常方法总结

    最近一直在学Selenium相关东西,学到webdriver这块,出现报不可见元素异常方法异常,后来网上找了好多相关资料都没搞定,也没看明白,最后发现是xpath中写了calss属性有问题.现在把学习 ...

  4. php实现手机定位功能的实例

    最近在做通过定位手机用户,进行消息推送,经过分析最终做法如下 mobile.php文件 当用户当手机访问该页面时,通过实现页面表单隐藏封装自动提交获取手机浏览器经纬度并post给服务器 1 2 3 4 ...

  5. 阶段5 3.微服务项目【学成在线】_day17 用户认证 Zuul_12-用户退出-服务端

    实现退出 用户退出要以下动作: 1.删除redis中的token 2.删除cookie中的token controller内定义 spring securety config内放行 对这个url放行 ...

  6. std::function以及std::bind

    转自:https://blog.csdn.net/shuilan0066/article/details/82788954 示例1 : 普通函数 void gFunc() { cout << ...

  7. 浅谈-对modbus的理解

    浅谈-对modbus的理解 一.简介 Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准.1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP ...

  8. 【422】Insert often-used pieces of text in gedit

    Ref: Snippets Enable Snippets Plugin Browse Snippets Using Snippets To insert a nippet into your cur ...

  9. Django补充之模板语言

    路由系统 Django页面详情以及分页 举个例子: 有一组后台数据,需要展示到页面上,但由于数据量较大,那就需要做分页了吧,那么怎么才能将页面详情和分页都融合进去呢,Django里的路由系统加上正则表 ...

  10. mybatis/tk mybatis下实体字段是关键字/保留字,执行报错

    实体如下: import com.fasterxml.jackson.annotation.JsonFormat; import com.xxx.web.bean.PagesStatic; impor ...