#deployment #maven #heroku
#развертывание #maven #heroku
Вопрос:
Я исследую Heroku как платформу и пытаюсь запустить на ней базовое веб-приложение Java. Веб-приложение уже создается и запускается с Maven (с использованием Tomcat и cargo-maven-plugin), поэтому я решил, что это должно быть легко, учитывая, что Heroku использует Maven для управления задачами установки / развертывания.
Однако это не тот случай, поскольку я не могу заставить эту штуку фактически запуститься. В моем Procfile
есть следующее:
web: sh ./startServer-heroku.sh
И startServer-heroku.sh
это просто:
mvn clean install cargo:start -Dcargo.maven.wait=true
Это отлично работает, когда я тестирую локально с помощью foreman start
команды, как описано в документах руководства Heroku. Но когда я пытаюсь это сделать на реальном сервере Heroku, я получаю следующие сообщения журнала:
2011-11-09T02:30:27 00:00 heroku[web.1]: State changed from created to starting
2011-11-09T02:30:27 00:00 heroku[slugc]: Slug compilation finished
2011-11-09T02:30:33 00:00 heroku[web.1]: Starting process with command `sh ./startServer-heroku.sh`
2011-11-09T02:30:33 00:00 app[web.1]: ./startServer-heroku.sh: 1: mvn: not found
2011-11-09T02:30:33 00:00 heroku[web.1]: Process exited
2011-11-09T02:30:34 00:00 heroku[web.1]: State changed from starting to crashed
Похоже, что mvn
нигде не найдено в системном PATH
, поэтому команда не выполняется.
Возможно ли вызвать mvn
из Heroku Procfile
? И есть ли где-нибудь, где есть окончательный список команд, которые доступны и которых нет из Procfile
?
Комментарии:
1. взгляните на jcabi-heroku-maven-plugin , который автоматизирует весь процесс развертывания в Heroku. возможно, это поможет в вашем случае.
Ответ №1:
Maven отсутствует в slug, который развертывается в dynos. Он доступен только во время компиляции. Одним из вариантов решения этой проблемы является использование appassembler-maven-plugin
и jar
packaging для генерации сценария запуска:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.1.1</version>
<configuration>
<assembleDirectory>target</assembleDirectory>
<programs>
<program>
<mainClass>foo.Main</mainClass>
<name>webapp</name>
</program>
</programs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>
Тогда Procfile
было бы:
web: sh target/bin/webapp
Другим вариантом является упаковка maven-dependency-plugin
и war
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>7.5.3.v20111011</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
С Procfile
из:
web: java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war
Комментарии:
1. Спасибо, это имеет смысл, хотя в вашем примере используется Jetty. Вы знаете, возможно ли сделать аналогичную вещь с Tomcat?
2. В Tomcat нет эквивалента jetty-runner, но вы все равно можете выполнить развертывание с помощью Tomcat несколькими способами. Один из них использует встроенный Tomcat. Проверьте эту статью, которая была только что опубликована: devcenter.heroku.com/articles /…
Ответ №2:
Ответ Джеймса содержит хорошие инструкции по запуску Jetty для работы на Heroku, и его комментарий содержит ссылку на хорошую ссылку по использованию встроенного Tomcat. Но также возможно запустить стандартную, автономную версию Tomcat на Heroku. Вот как я смог заставить его работать:
Сначала настройте свой POM для установки и настройки Tomcat как части вашей сборки, а также для развертывания вашего приложения в установленном экземпляре Tomcat:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
<container>
<containerId>tomcat6x</containerId>
<zipUrlInstaller>
<url>http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.18/bin/apache-tomcat-6.0.18.zip</url>
</zipUrlInstaller>
<dependencies>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</dependency>
</dependencies>
</container>
<configuration>
<type>standalone</type>
<deployables>
<deployable>
<groupId>com.yourcompany.name</groupId>
<artifactId>yourArtifact</artifactId>
<type>war</type>
<properties>
<context>ROOT</context>
</properties>
</deployable>
</deployables>
</configuration>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>install</goal>
<goal>configure</goal>
<goal>deploy</goal>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
Затем создайте урезанный server.xml
файл, который будет работать на Heroku:
<?xml version='1.0' encoding='utf-8'?>
<Server port="-1">
<Listener className="org.apache.catalina.core.JasperListener" />
<Service name="Catalina">
<Connector port="${http.port}" protocol="HTTP/1.1" connectionTimeout="20000"/>
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"/>
</Engine>
</Service>
</Server>
… это необходимо, потому что вашему приложению Heroku разрешено привязываться только к одному порту (который меняется каждый раз при создании нового экземпляра и указан в $PORT
переменной окружения). Попытка привязки к любому другому порту приведет к сбою вашего приложения. Поскольку порт является динамическим, к нему необходимо передать server.xml
через http.port
системное свойство, но мы вернемся к этому позже.
Пока вы этим занимаетесь, также создайте persistence.xml
файл, который будет работать с Heroku:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="quiz_devel">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show.sql" value="true"/>
<property name="hibernate.c3p0.acquire_increment" value="1"/>
<property name="hibernate.c3p0.idle_test_period" value="10"/>
<property name="hibernate.c3p0.max_size" value="20"/>
<property name="hibernate.c3p0.max_statements" value="40"/>
<property name="hibernate.c3p0.min_size" value="1"/>
<property name="hibernate.c3p0.timeout" value="30"/>
</properties>
</persistence-unit>
</persistence>
Обратите внимание, что здесь не hibernate.connection.url
указано. Это потому, что Heroku указывает URL базы данных, который ваше приложение должно использовать в $DATABASE_URL
переменной среды.
Теперь пришло время создать простой сценарий оболочки, который настраивает среду и настраивает все так, чтобы Tomcat действительно мог запускаться:
#point to the correct configuration and webapp
CATALINA_BASE=`pwd`/target/cargo/configurations/tomcat6x
export CATALINA_BASE
#copy over the Heroku config files
cp ./server-heroku.xml ./target/cargo/configurations/tomcat6x/conf/server.xml
cp ./persistence-heroku.xml ./target/cargo/configurations/tomcat6x/webapps/ROOT/WEB-INF/classes/META-INF/persistence.xml
#make the Tomcat scripts executable
chmod a x ./target/cargo/installs/apache-tomcat-6.0.18/apache-tomcat-6.0.18/bin/*.sh
#set the correct port and database settings
JAVA_OPTS="$JAVA_OPTS -Dhttp.port=$PORT -Dhibernate.connection.url=$DATABASE_URL"
export JAVA_OPTS
#start Tomcat
./target/cargo/installs/apache-tomcat-6.0.18/apache-tomcat-6.0.18/bin/catalina.sh run
Это выполняет ряд действий:
- Он сообщает Tomcat использовать артефакты конфигурации и развертывания, которые упакованы как часть вашей сборки, установив
CATALINE_BASE
значение указывать в правильном месте. - Он перезаписывает файлы по умолчанию
server.xml
иpersistence.xml
их вариантами, специфичными для heroku. - Он помечает все сценарии запуска в экземпляре Tomcat, которые cargo установил как часть сборки, как исполняемые.
- Он определяет значения для
http.port
иhibernate.connection.url
на основе переменных среды, предоставляемых платформой Heroku. - Наконец, он запускает Tomcat. Обратите внимание, что вы не можете использовать
startup.sh
для этого, так какstartup.sh
это приведет к запуску Tomcat в новом процессе и последующему завершению. Heroku этого не понимает и думает, что завершениеstartup.sh
является завершением процесса Tomcat.
Наконец, последний шаг — настроить ваш Procfile
для вызова вашего сценария запуска, что-то вроде:
web: sh startServer-heroku.sh
При таком подходе у вас может быть проект, совместимый с Heroku, при этом сохраняя его способность работать автономно как стандартное веб-приложение Java.
Ответ №3:
Да, вы можете вызвать maven из вашего Procfile.
Для этого вам необходимо включить maven в свой slug, внеся небольшую модификацию в скрипт ‘compile’ в пакете сборки java, как описано в README.md файл из проекта «heroku-buildpack-java»:
Например, если вы хотите, чтобы maven был доступен для использования во время выполнения в вашем приложении, вам просто нужно скопировать его из каталога кэша в каталог сборки, добавив следующие строки в сценарий компиляции:
for DIR in ".m2" ".maven" ; do
cp -r $CACHE_DIR/$DIR $BUILD_DIR/$DIR
done
Я добавил эти строки сразу после загрузки maven и перед изменением на BUILD_DIR .
Затем в вашем Procfile вы можете вызвать .maven/ bin / mvn
Если вы создаете артефакт (war / jar), который вы можете запускать с java или в контейнере сервлета, то вам, вероятно, следует сделать что-то по пути других ответов, но если вам действительно нужно запустить maven, то это способ.