【问题标题】:Deploying, starting and stopping Scala applications on a remote server在远程服务器上部署、启动和停止 Scala 应用程序
【发布时间】:2013-03-12 12:00:24
【问题描述】:

首选方式是在远程 Linux 服务器上部署 Scala 应用程序。

这是在远程服务器上部署 Scala 应用程序的一种相当简单但有限的方法(非常适合快速测试不太敏感的项目):

  1. 从远程服务器我从 git 中提取我的源代码
  2. 使用sbt-assembly插件我在服务器上建了一个jar
  3. 然后我使用 nohup 运行 Scala 应用程序,它允许您在不终止进程的情况下退出远程会话:

    nohup java -jar myapp.jar > myapp.log 2> myapp.err /null &

首先,考虑到它正在使用数据库等资源,一旦进程运行,停止进程的最佳方法是什么。我是否只需查找 java 进程 ID 并对其进行核对?

其次,在重新启动时自动启动 java 应用程序的最佳方法是什么。我记得过去使用过 init.d,但记得因为它是一个 java 应用程序,所以遇到了一些困难。

更新:

我错过了这里房间里的大象。我正在使用 Spray 库,该库又使用 Akka,因此提供了许多有趣的选项。

【问题讨论】:

  • 如何将应用安装为服务或通过 TCP 套接字发送关闭消息?
  • 关于您随时停止进程的第一个要求,我建议使用screen 并在屏幕会话中运行您的jar。使用ctrl+a k 杀死一个窗口和其中运行的进程。
  • 谢谢@Kane。我忘记了那个。这是一个很酷的想法,尤其是因为您可以与其他开发人员共享屏幕(如果我没记错的话)

标签: java linux scala process spray


【解决方案1】:

给猫剥皮的方法有很多种...

您可以使用 sbt-start-script https://github.com/sbt/sbt-start-script 甚至 sbt-native-packager https://github.com/sbt/sbt-native-packager

您可以将 Spray 的 Boot 示例脚本包装在一个简单的 init.d 脚本中,该脚本调用 sbt,如本答案 https://stackoverflow.com/a/17399574/155689 中所述,或者只使用普通的 nohup java 命令。

您可以创建更大的守护进程感知类和脚本,或者使用使用 Jsvc http://commons.apache.org/proper/commons-daemon/jsvc.html 或 Java Service Wrapper 的 init.d 脚本来扩展这些类和脚本。 http://wrapper.tanukisoftware.com/

守护程序和应用程序类的示例:

package com.example.myapplication.server

import akka.actor.{Props, ActorSystem}
import spray.can.Http
import akka.io.IO
import com.example.myapplication.api.MyServiceActor
import org.apache.commons.daemon._

trait ApplicationLifecycle {
  def start(): Unit
  def stop(): Unit
}

abstract class AbstractApplicationDaemon extends Daemon {
  def application: ApplicationLifecycle

  def init(daemonContext: DaemonContext) {}

  def start() = application.start()

  def stop() = application.stop()

  def destroy() = application.stop()
}

class ApplicationDaemon() extends AbstractApplicationDaemon {
  def application = new Application
}

object ServiceApplication extends App {

  val application = createApplication()

  def createApplication() = new ApplicationDaemon

  private[this] var cleanupAlreadyRun: Boolean = false

  def cleanup(){
    val previouslyRun = cleanupAlreadyRun
    cleanupAlreadyRun = true
    if (!previouslyRun) application.stop()
  }

  Runtime.getRuntime.addShutdownHook(new Thread(new Runnable {
    def run() {
      cleanup()
    }
  }))

  application.start()
}


class Application() extends ApplicationLifecycle with Logging {

  private[this] var started: Boolean = false

  private val applicationName = "MyApplication"

  implicit val actorSystem = ActorSystem(s"$applicationName-system")

  def start() {
    logger.info(s"Starting $applicationName Service")

    if (!started) {
      started = true

      val myService = actorSystem.actorOf(Props[MyServiceActor], "my-service")

      IO(Http) ! Http.Bind(myService, interface = "0.0.0.0", port = 8280)
    }
  }

  def stop() {
    logger.info(s"Stopping $applicationName Service")

    if (started) {
      started = false
      actorSystem.shutdown()
    }
  }

}

如果您在/opt/myapplication/myapplication.jar 中部署 jar(对于胖 jar 使用 sbt-assembly),请在 /etc/mycompany 文件夹中添加一些外部配置,然后您可以将其包装在 /etc/init.d/myapplication 脚本中,例如使用 Jsvc :

#!/bin/sh
### BEGIN INIT INFO
# Provides:          myapplication
# Required-Start:    $local_fs $remote_fs $network
# Required-Stop:     $local_fs $remote_fs $network
# Should-Start:      $named
# Should-Stop:       $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Control myapplication
# Description:       Control the myapplication daemon.
### END INIT INFO

set -e

if [ -z "${JAVA_HOME}" ]; then
        JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:/bin/java::")
fi
JAVA_OPTS="-Xms512m -Xmx1024m"

APP=myapplication

PID=/var/run/${APP}.pid
OUT_LOG=/var/log/myapplication/${APP}_out.log
ERR_LOG=/var/log/myapplication/${APP}_err.log

DAEMON_USER=yourserviceuser

APP_LOG_CONFIG=/etc/mycompany/${APP}_logback.xml
APP_CONFIG=/etc/mycompany/${APP}.conf
APP_HOME=/opt/${APP}
APP_CLASSPATH=$APP_HOME/${APP}.jar
APP_CLASS=com.example.myapplication.server.ApplicationDaemon

if [ -n "$APP_LOG_CONFIG}" ]; then
        JAVA_OPTS="-Dlogback.configurationFile=${APP_LOG_CONFIG} ${JAVA_OPTS}"
fi

DAEMON_ARGS="-home ${JAVA_HOME} -Dconfig.file=${APP_CONFIG} ${JAVA_OPTS} -pidfile ${PID} -user ${DAEMON_USER} -outfile ${OUT_LOG} -errfile ${ERR_LOG} -cp ${APP_CLASSPATH} ${APP_CLASS}"

. /lib/lsb/init-functions

case "$1" in
        start)
                log_daemon_msg "Starting ${APP}"
                cd ${APP_HOME} && jsvc ${DAEMON_ARGS}
                log_end_msg 0
                ;;
        stop)
                log_daemon_msg "Stopping ${APP}"
                cd ${APP_HOME} && jsvc -stop ${DAEMON_ARGS}
                log_end_msg 0
                ;;
        *)
                log_success_msg "Usage:  {start|stop}"
                echo "Usage:  {start|stop}"
                exit 1
                ;;
esac

exit 0

有了这个你现在可以sudo service myapplication start|stop

如果您希望它在启动时自动启动,则运行此命令

sudo update-rc.d myapplication defaults

这种守护程序方法适用于我拥有的 Spray 应用程序。

【讨论】:

  • 优秀的答案!不幸的是,我不太明白“在 /etc/mycompany 文件夹中添加一些外部配置”部分。你是指罐子等吗?还是很好的答案。
  • 外部配置是可选的,由 APP_LOG_CONFIG 和 APP_CONFIG 引用,即 logback 和 spray 的 application.conf 配置,可能会覆盖 jar 配置中的配置。但是你也需要你的 build.sbt 和 Build.scala 来监听这些 javaoptions。如果您不需要它们,您可以通过删除它们的引用来简化您的 DAEMON_ARGS 和 JAVA_OPTS。
【解决方案2】:

如果maven 合适,那么可以使用以下插件:http://evgeny-goldin.com/wiki/Sshexec-maven-plugin

可能很容易移植到sbt

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-06
    • 2013-10-05
    • 1970-01-01
    • 2020-07-24
    • 1970-01-01
    • 2016-12-01
    • 2012-09-15
    • 1970-01-01
    相关资源
    最近更新 更多