2013-03-12 155 views
13

在远程Linux服务器上部署Scala应用程序的首选方式是什么。在远程服务器上部署,启动和停止Scala应用程序

这是一个相当简单的,但有限的,部署在远程服务器上Scala的应用程序的方式(尼斯不那么敏感项目的快速测试):

  1. 从远程服务器我拉我的从混帐
  2. 源使用sbt-assembly插件我建立服务器上的罐子
  3. 我然后运行使用nohup的Scala的应用程序,它可以让你在不终止该进程退出远程会话:

    nohup的Java的罐子myapp.jar> myapp.log 2> myapp.err <的/ dev/null的&

首先,什么是停止的过程中,一旦运行它的最好办法,考虑到它的使用资源如数据库等。我只是查找Java进程ID并核弹它?其次,在重新启动时自动启动Java应用程序的最佳方式是什么?我记得过去曾经使用过init.d,但记得自从它是一个Java应用程序以来就有些艰难。

更新:

我错过了在房间里的大象在这里。我使用Spray库,它依次使用Akka,以便提供一些有趣的选项。

+0

将应用程序安装为服务或通过TCP套接字发送关机消息如何? – 2013-03-12 14:52:07

+0

关于您在任何时候停止进程的第一个要求,我建议使用'screen'并在屏幕会话中运行您的jar。使用'ctrl + a k'杀死一个窗口及其中运行的进程。 – Kane 2013-03-12 19:43:49

+0

谢谢@Kane。我忘记了那个。这是一个很酷的想法,尤其是因为您可以与其他开发人员共享屏幕(如果内存能为我服务) – Jack 2013-03-13 04:25:27

回答

17

有多种方法对皮肤一只猫......

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

你可以将Spray的Boot示例脚本包装在一个简单的ini t.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罐子(使用SBT-大会脂肪罐子),加上一些外部的配置在/etc/mycompany文件夹,然后你可以使用的jsvc包装,在一个/etc/init.d/myapplication脚本,例如:

#!/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 

此守护程序方法适用于喷雾的应用程序我有。

+0

优秀的答案!不幸的是,我不太清楚“在/ etc/mycompany文件夹中添加一些外部配置”部分。你是指罐子等?仍然很好的答案。 – Jack 2014-03-19 09:21:27

+0

外部配置是可选的,并由 APP_LOG_CONFIG和APP_CONFIG 引用,即logback和spray的application.conf配置,可能会覆盖jar配置中的配置。但是你需要你的build.sbt和Build.scala来监听这些java选项。 如果你不需要它们,你可以通过删除它们的引用来简化你的DAEMON_ARGS和JAVA_OPTS。 – flurdy 2014-03-19 11:18:45

相关问题