如何使用GitLab Runner启动服务并防止其停止?

8
我将使用GitLab CI服务器部署一个简单的Spring Boot应用程序。我的`.gitlab-ci.yml`文件如下所示:
stages:
  - build_and_test
  - deploy

web_server_build_and_test:
  stage: build_and_test
  script:
    - mvn clean package

web_server_deploy:
  stage: deploy
  script:
    - mvn clean package -Pprod
    - service gitlab-runner-test stop
    - cp target/*.war /var/gitlab-runner-test/gitlab-runner-test.war
    - chmod +x /var/gitlab-runner-test/gitlab-runner-test.war
    - service gitlab-runner-test start

部署阶段会产生以下输出:

$ service gitlab-runner-test stop
Stopped [13247]
$ cp target/*.war /var/gitlab-runner-test/gitlab-runner-test.war
$ chmod +x /var/gitlab-runner-test/gitlab-runner-test.war
$ service gitlab-runner-test start
Started [21177]

然而,由于runner完成阶段后服务已停止,我无法加载该应用程序:

$ service gitlab-runner-test status
Not running (process 21177 not found)

我的服务脚本将实际工作委托给组装好的war包:

#!/usr/bin/env bash

export JAVA_HOME=/usr/lib/jvm/java-8-oracle/jre/bin/java
export MODE=service
export APP_NAME=gitlab-runner-test
export PID_FOLDER=/var/run/gitlab-runner-test


/var/gitlab-runner-test/gitlab-runner-test.war $*

此外,当我手动启动服务时(service gitlab-runner-test start),即使用户会话已关闭,它仍然保持运行状态。我不确定问题的根源是什么——Spring Boot启动脚本、GitLab配置、我的服务脚本还是其他什么。我正在运行Ubuntu 14.04,使用GitLab CI多个runner版本0.5.0(c38415a)。升级runner到版本1.0.1(cffb5c7)并不能解决这个问题。

哦,我明白问题出在哪里了。使用nohup来启动一个进程,当父进程退出时不会杀死它。所以可以使用- nohup service gitlab-runner-test start或者nohup /var/gitlab-runner-test/gitlab-runner-test.war $* - Chloe
2个回答

17

为什么这样做是个坏主意...

正如它的文档清楚地表明,GitLab Runner“运行测试并将结果发送到 GitLab”。

由于测试应该及时启动和停止,因此该 Runner 设计为在完成每次构建后杀死所有创建的进程。

所以你的服务被杀掉并不是一个bug,而是一个feature。;)


GitLab CI 文档建议使用 dpl 进行部署

dpl 是一个项目,使您能够在各种 PaaS 提供商上部署应用程序,例如 Google App Engine、Heroku 或 Elastic Beanstalk。

因此,它会向一些 REST API 发送一些请求或通过互联网推送其他数据,并且它的进程会很好地退出。


因此,做你想做的事情实际上需要一些黑客技巧--覆盖默认的 Runner 行为。你不应该长期这样做,因为它可能会在某些 Runner/GitLab 更新后停止工作。

...但如果你坚持要这样做,那么这里是具体方法 :)

在您的情况下,当您想要在 Runner 的主机上实际部署和运行应用程序时,我们需要使用两个黑客技巧:

  • 不要使用默认的shell Runner 执行器,而要使用 ssh 并让执行器 ssh 到自身(受迈克尔的解决方案的启发,如果您也喜欢我的回答,请给他点赞!)
  • 放弃正在由 init 脚本运行的进程(受乔的解决方案启发,再次-请给它点赞!)

好的,以下是具体操作指南:

  1. 确保您可以使用位于/root/.ssh/id_rsa 的 SSH 私钥从 Runner 主机 SSH 到自身,而不需要口令,也不需要确认指纹。运行由 root 运行的 ssh localhost 应该可以非交互式地完成。

  2. 编辑您的 gitlab-runner 配置文件/etc/gitlab-runner/config.toml,使其看起来像:

    [[runners]]
      name = "your-runner-name"
      url = "https://<your_gitlab_instance_fqdn>/ci"
      token = "<your_project_CI_token>"
      tls-ca-file = ""
      executor = "ssh"
      [runners.ssh]
        user = "root"
        password = ""
        host = "localhost"
        port = "22"
        identity_file = "/root/.ssh/id_rsa"
    

(保存配置文件后,运行程序将自动重新加载)

  1. 编辑您的服务脚本,使其创建的进程不是init脚本的子进程,并且不会打开标准输入输出和错误输出:

#!/usr/bin/env bash

export JAVA_HOME=/usr/lib/jvm/java-8-oracle/jre/bin/java
export MODE=service
export APP_NAME=gitlab-runner-test
export PID_FOLDER=/var/run/gitlab-runner-test


/var/gitlab-runner-test/gitlab-runner-test.war $* <&- >&- 2>&- & disown

重试上一次构建或对您的项目仓库进行提交以进行测试。


附注:我使用了一个类似于以下内容的初始化脚本来测试我的解决方案:

#!/usr/bin/env bash

start() {
  # Completely disowned process, not a child
  # Credits: Joe at https://dev59.com/CmIj5IYBdhLWcg3wYUGQ#26420299
  sleep 99999 <&- >&- 2>&- & disown
  exit 0
}

stop() {
  echo "doing nothing"
  exit 0
}

echo "running on $HOSTNAME..."

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  *)
    echo $"Use this options $0 {start|stop}"
    exit 1
esac

使用 gitlab-multi-runner v. 1.02 和 GitLab CE 8.5.0 在 Ubuntu 14.04 上运行。


谢谢!我一到工作岗位就会检查你的解决方案。需要以root身份运行runner吗? - awesoon
那么,如果通过GitLab CI部署是一个坏主意,我不是应该为dpl编写一个自定义提供程序吗? - awesoon
为什么有人应该使用 dpl 而不是 eb deploy 来部署 Elastic Beanstalk? - Chloe
1
可能是因为 dpl 为许多后端提供了统一的接口,不仅包括 AWS,还包括 GCP、Azure、Heroku 等等。@Chloe。 - Greg Dubicki

0

虽然@GregDubicki发布的解决方案完美无缺并包含每个步骤的说明,但我最终采用了具有监视服务的解决方案,该服务在每次构建后重新启动我的服务。

这种方法具有以下优点:

  1. 您不应该在root用户下启动runner
  2. 您不应该担心由runner杀死的进程
  3. (+奖励)现在您拥有一个监视系统!

我对你构建的监控服务很感兴趣。它在Github上吗?你是否在其他地方遇到过类似的东西?似乎启动应用程序是CICD中重要的一步,但似乎并没有得到很好的覆盖。 - Ali
1
好的,这是大约3年前的事情了,自那以后情况发生了很大变化,我目前不在从事那个项目,所以可能记不清CI/CD流程演变的确切细节。虽然我记得我使用的监控系统是mmonit - 运行良好,并解决了重新启动服务的问题(你只需要部署新文件并停止服务 - 监控系统将为您重新启动它)。 但是,一段时间后,我更改了脚本以使用类似于“dpl”的方法。 它并不完全像dpl,因为... - awesoon
1
这并不完全像 dpl,因为我们没有使用云服务器,所以我们的自定义解决方案效果很好。流程非常简单:构建 war 包,将其通过 scp 上传到服务器,然后在该服务器上运行 deploy.sh 脚本。如果需要创建新服务(例如如果我们正在部署测试分支,则需要创建一个新服务,然后再销毁它),deploy.sh 能够胜任此任务,还能准备应用程序配置(如数据库、主机、端口等)。我几个月前曾与该公司的一位开发人员交谈过,他们现在计划切换到 Docker + k8s(或类似的部署平台)。 - awesoon
1
目前我正在使用k8s来进行我的新项目,但是如果你没有使用Docker,我建议你为CI构建使用一个单独的服务器,并将你的应用程序(甚至是开发和暂存环境)部署到一个类似于dpl的方法的单独服务器上。虽然如果你的公司很小,还没有一个专门的应用服务器,你可以使用监控工具来重新启动你的应用程序 - 但这不是一个理想的解决方案。 - awesoon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接