Развертывание, запуск и остановка приложений Scala на удаленном сервере

Предпочтительным способом является развертывание приложений Scala на удаленном сервере Linux.

Это довольно простой, но ограниченный способ развертывания приложения Scala на удаленном сервере (подходит для быстрого тестирования не очень чувствительных проектов):

  1. С удаленного сервера я вытаскиваю свой исходник из git
  2. Используя подключаемый модуль sbt-assembly, я создаю банку на сервере.
  3. Затем я запускаю приложение Scala с помощью nohup, что позволяет вам выйти из удаленного сеанса без завершения процесса:

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

Во-первых, как лучше всего остановить процесс после его запуска, учитывая, что он использует такие ресурсы, как базы данных и т. Д. Мне просто искать идентификатор процесса Java и уничтожать его?

Во-вторых, как лучше всего автоматически запускать Java-приложение при перезапуске. Я помню, как использовал init.d в прошлом, но помню, что у меня были некоторые трудности, так как это было java-приложение.

Обновление:

Я пропустил слона в комнате здесь. Я использую библиотеку Spray, которая, в свою очередь, использует Akka, так что у нее есть ряд интересных опций.


person Jack    schedule 12.03.2013    source источник
comment
Как насчет установки приложения в качестве службы или отправки сообщения о завершении работы через сокет TCP?   -  person Andriy Plokhotnyuk    schedule 12.03.2013
comment
Что касается вашего первого требования по остановке процесса в любое время, я рекомендую использовать screen и запустить банку в сеансе экрана. Используйте ctrl+a k, чтобы убить окно и запущенные в нем процессы.   -  person Kane    schedule 12.03.2013
comment
Спасибо @Kane. Я забыл об этом. Это классная идея, тем более, что вы можете делиться экранами с другими разработчиками (если мне не изменяет память).   -  person Jack    schedule 13.03.2013


Ответы (2)


Есть несколько способов освежевать кошку...

Вы можете использовать sbt-start-script https://github.com/sbt/sbt-start-script или даже sbt-native-packager https://github.com/sbt/sbt-native-packager

Вы можете обернуть пример сценария загрузки Spray в простой сценарий init.d, который вызывает sbt, как подробно описано в этом ответе https://stackoverflow.com/a/17399574/155689 или просто используйте простую команду nohup java.

Вы можете создавать более крупные классы и сценарии, поддерживающие демона, или расширять их с помощью сценариев init.d, использующих Jsvc http://commons.apache.org/proper/commons-daemon/jsvc.html или Java Service Wrapper. 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()
    }
  }

}

Если вы развернете банку (используйте sbt-assembly для толстой банки) в /opt/myapplication/myapplication.jar, добавите некоторые внешние конфигурации в папку /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, которые у меня есть.

person flurdy    schedule 18.03.2014
comment
Отличный ответ! К сожалению, я не совсем понимаю добавление некоторых внешних конфигураций в часть папки /etc/mycompany. Вы имеете в виду банки и т. д.? Все еще отличный ответ. - person Jack; 19.03.2014
comment
Внешние конфигурации являются необязательными, и на них ссылаются APP_LOG_CONFIG и APP_CONFIG, т. е. logback и конфигурация application.conf спрея, которая может переопределить конфигурацию внутри вашего jar-файла. Но вам нужны ваши build.sbt и Build.scala, чтобы слушать эти javaoptions. Если они вам не нужны, вы можете упростить свои DAEMON_ARGS и JAVA_OPTS, удалив их ссылку. - person flurdy; 19.03.2014

Если maven подходит, можно использовать следующий плагин: http://evgeny-goldin.com/wiki/Sshexec-maven-plugin

Возможно, его можно легко портировать на sbt

person Andriy Plokhotnyuk    schedule 12.03.2013