Heroku - 我可以从Procfile中调用Maven吗?

时间:2011-11-09 03:57:11

标签: deployment maven heroku

我正在调查Heroku作为一个平台,我正在尝试使用基本的Java webapp来运行它。 webapp已经构建并运行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,因此命令失败。

是否可以从Heroku mvn调用Procfile?是否有任何具有明确的命令列表的地方Procfile

3 个答案:

答案 0 :(得分:7)

Maven不会被部署到dynos的slu ..它仅在编译时可用。处理此问题的一个选项是使用appassembler-maven-pluginjar包装生成启动脚本:

        <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-pluginwar打包:

        <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 :(得分:2)

詹姆斯的回答提供了让Jetty在Heroku上工作的良好指示,他的评论包括一个关于使用嵌入式Tomcat的良好参考的链接。但也可以在Heroku上运行Tomcat的标准独立版本。以下是我能够让它发挥作用的方式:

首先,设置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>

接下来,创建一个适用于Heroku的精简版server.xml文件:

<?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,但我们稍后会这样做。

在您使用它的同时,还要创建一个可与Heroku一起使用的persistence.xml文件:

<?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指定了您的应用应在$DATABASE_URL环境变量中使用的数据库URL。

现在是时候创建一个简单的shell脚本来配置环境并设置所有内容,以便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

这是做了很多事情:

  1. 通过将CATALINE_BASE设置为指向正确的位置,它告诉Tomcat使用货物打包作为构建的一部分的配置和部署工件。
  2. 它会使用特定于heroku的变体覆盖默认的server.xmlpersistence.xml文件。
  3. 它标记Tomcat实例中作为构建的一部分安装的货物作为可执行文件的所有启动脚本。
  4. 它根据Heroku平台提供的环境变量指定http.porthibernate.connection.url的值。
  5. 最后,它运行Tomcat。请注意,您无法使用startup.sh执行此操作,因为startup.sh将在新进程中启动Tomcat然后终止。 Heroku不理解这一点,并认为startup.sh的终止是Tomcat进程的终止。
  6. 最后,最后一步是设置Procfile来调用你的启动脚本,类似于:

    web: sh startServer-heroku.sh
    

    使用这种方法,您可以拥有一个与Heroku兼容的项目,同时仍然保留其作为标准Java Web应用程序独立运行的能力。

答案 2 :(得分:0)

是的,您可以从Procfile中调用maven。

要做到这一点,你需要通过对“编译”进行一些小修改来将maven包含在你的slug中。 java构建包中的脚本,如README.md file from the "heroku-buildpack-java" project

中所述
  

例如,如果您希望在应用程序中可以在运行时使用maven,则只需将以下行添加到编译脚本中,即可将其从缓存目录复制到构建目录:

for DIR in ".m2" ".maven" ; do
 cp -r $CACHE_DIR/$DIR $BUILD_DIR/$DIR
done

我在maven下载之后,在更改为BUILD_DIR之前added these lines

在Procfile中,您可以调用.maven / bin / mvn

如果你正在构建一个可以从java或servlet容器中运行的工件(war / jar),那么你应该在其他答案的路上做一些事情,但是如果你真的需要运行maven,那么这就是方法。