これは、なにをしたくて書いたもの?
というわけで、OKD/Minishift上にマルチモジュール構成のMavenプロジェクトをデプロイしてみます。
お題
OKD/Minishift上に、ごくごく簡単な2つのMavenプロジェクトをデプロイしてみます。
2つというのは、
- WAR
- Uber JAR
という感じで。
ほぼ答えが書いていますが、参考にしたのはこちらのエントリ。
Maven Multi-Module Projects and OpenShift – Red Hat OpenShift Blog
環境
今回の環境は、こちら。
$ minishift version minishift v1.24.0+8a904d0 $ oc version oc v3.10.0+dd10d17 kubernetes v1.10.0+b81c8f8 features: Basic-Auth GSSAPI Kerberos SPNEGO Server https://192.168.42.24:8443 openshift v3.10.0+e3465d0-44 kubernetes v1.10.0+b81c8f8
マルチモジュール構成のWARなアプリケーションをデプロイする
それでは、最初はWARファイルから。
こんな感じのMavenプロジェクトを用意。
$ find pom.xml library web -type f pom.xml library/pom.xml library/src/main/resources/META-INF/beans.xml library/src/main/java/org/littlewings/openshift/MessageService.java web/web.iml web/src/main/java/org/littlewings/openshift/JaxrsActivator.java web/src/main/java/org/littlewings/openshift/MessageResource.java
「web」がWARを構成するプロジェクトで、JAX-RSに関連するクラスを置いています。「library」は「web」から
参照されるプロジェクトで、CDI管理Beanを置いています。
内容は、こんな感じ。
library
pom.xml(の一部)
※依存関係がちょっと雑です
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies>
CDI管理Bean。
library/src/main/java/org/littlewings/openshift/MessageService.java
package org.littlewings.openshift; import javax.enterprise.context.ApplicationScoped; @ApplicationScoped public class MessageService { public String get() { return "Hello World!!"; } }
CDI有効化のための、beans.xml。
library/src/main/resources/META-INF/beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="annotated"> </beans>
web
WARファイル側。
pom.xml(の抜粋)。
<artifactId>web</artifactId> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> </properties> <dependencies> <dependency> <groupId>xxx.yyy</groupId> <artifactId>library</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>ROOT</finalName> </build>
JAX-RS関連のクラス。
リソースクラス。
web/src/main/java/org/littlewings/openshift/MessageResource.java
package org.littlewings.openshift; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @ApplicationScoped @Path("message") public class MessageResource { @Inject MessageService messageService; @GET @Produces(MediaType.TEXT_PLAIN) public String get() { return messageService.get(); } }
JAX-RSの有効化。
web/src/main/java/org/littlewings/openshift/JaxrsActivator.java
package org.littlewings.openshift; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("") public class JaxrsActivator extends Application { }
トップレベルのプロジェクト
トップレベルのプロジェクトは、pom.xmlにモジュールのリストがあるだけです。
pom.xml(の抜粋)。
<packaging>pom</packaging> <modules> <module>library</module> <module>web</module> </modules>
このプロジェクトを「http://〜/maven-multi-project-war.git」という感じでGitリポジトリに登録しておきます。
デプロイする
では、このWebアプリケーションをOKDにデプロイしてみます。
このようなマルチモジュールな構成のMavenプロジェクトをデプロイするには、環境変数「ARTIFACT_DIR」をビルド時に
指定します。
ビルド時に指定するので、いきなり「oc new-app」ではなく、「oc new-build」で始めてみます。
$ oc new-build openshift/wildfly:12.0~http://〜/maven-multi-project-war.git -e ARTIFACT_DIR=web/target
ImageStreamとしてWildfFlyを指定し、環境変数「ARTIFACT_DIR」にはwebモジュールのtargetディレクトリ、
つまり最終的に使われるアーティファクトが生成されるディレクトリを指定します。
環境変数「ARTIFACT_DIR」で適切なディレクトリを指定しなかった場合は、この後にDeploymentConfigを
作成してデプロイした時に、WildFlyになにもデプロイされません…。
ビルドが終わったら、「oc new-app」を実行してデプロイして、Routeもexposeします。
$ oc new-app maven-multi-project-war
$ oc expose svc/maven-multi-project-war
確認。
$ curl maven-multi-project-war-myproject.xxx.xxx.xx.xx.nip.io/message Hello World!!
OKです。
ちなみに、ビルドするモジュールを絞りたい場合は、環境変数「MAVEN_ARGS_APPEND」を併用します。
$ oc new-build openshift/wildfly:12.0~http://〜/maven-multi-project-war.git -e ARTIFACT_DIR=web/target -e MAVEN_ARGS_APPEND='-pl web -am'
これで、Maven実行時には以下のようなコマンドになります。
$ mvn package -Popenshift -DskipTests -B -s /opt/app-root/src/.m2/settings.xml -pl web -am
ビルドしたいモジュールと、関連するモジュールがビルドされる感じになりますね。
YAMLで書くと
今回のBuildConfigをYAMLで書いた場合の、該当の箇所を抜粋すると、こうですね。
apiVersion: v1 kind: BuildConfig ## 省略... spec: ## 省略... strategy: sourceStrategy: env: - name: ARTIFACT_DIR value: web/target - name: MAVEN_ARGS_APPEND value: -pl web -am ## 省略...
という感じになります。
マルチモジュール構成のUber JARなアプリケーションをデプロイする
続いて、マルチモジュール構成なUber JARなプロジェクトをデプロイします。
こちらは、こんな構成。
$ find pom.xml library launcher -type f pom.xml library/pom.xml library/src/main/java/org/littlewings/openshift/MessageService.java launcher/pom.xml launcher/src/main/java/org/littlewings/openshift/App.java
あらシンプル。内容は、Spring BootアプリケーションでUber JARになる方が「launcher」モジュールです。
OpenJDKのImageStreamを入れる
中身に入る前に、OKDにOpenJDKのImageStreamを突っ込んでおきます。
$ oc create -f https://raw.githubusercontent.com/jboss-openshift/application-templates/ose-v1.4.15/openjdk/openjdk18-image-stream.json -n openshift --as system:admin
Uber JARの実行には、こちらを使用します。
Minishiftの、「admin-user」addonは有効にしています。
$ minishift addon list - admin-user : enabled P(0) - anyuid : disabled P(0) - che : disabled P(0) - htpasswd-identity-provider : disabled P(0) - registry-route : disabled P(0) - xpaas : disabled P(0)
では、中身の方へ。
library
pom.xml(の一部)。
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.5.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>
いやぁ、こちらも依存関係が雑です…。
Serviceクラス。
library/src/main/java/org/littlewings/openshift/MessageService.java
package org.littlewings.openshift; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; @Service public class MessageService { public Mono<String> get() { return Mono.just("Hello World!!"); } }
launcher
lancher側。
pom.xml(の一部)。
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.5.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>xxx.yyy</groupId> <artifactId>library</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.0.5.RELEASE</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
起動クラス兼RestController。
launcher/src/main/java/org/littlewings/openshift/App.java
package org.littlewings.openshift; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @SpringBootApplication @RestController public class App { MessageService messageService; public App(MessageService messageService) { this.messageService = messageService; } public static void main(String... args) { SpringApplication.run(App.class, args); } @GetMapping("message") public Mono<String> message() { return messageService.get(); } }
トップレベルのプロジェクト
トップレベルのプロジェクトは、pom.xmlにモジュールのリストがあるだけです。
pom.xml(の一部)。
<packaging>pom</packaging> <modules> <module>library</module> <module>launcher</module> </modules>
WARプロジェクトの時と、同じようなものですね。
このプロジェクトを「http://〜/maven-multi-project-jar.git」という感じでGitリポジトリに登録しておきます。
デプロイする
では、このUber JARなアプリケーションをOKDにデプロイしてみます。
といっても、ここから先の指定は、先程のWebアプリケーションの時と似たようなものです。
「oc new-build」で、環境変数「ARTIFACT_DIR」を「launcher/target」ディレクトリに指定します。
$ oc new-build openshift/redhat-openjdk18-openshift:1.4~http://〜/maven-multi-project-jar.git -e ARTIFACT_DIR=launcher/target
ビルドが終わったら、DeploymentConfigとRouteの作成。
$ oc new-app maven-multi-project-jar $ oc expose svc/maven-multi-project-jar
確認。
$ curl maven-multi-project-jar-myproject.xxx.xxx.xx.xx.nip.io/message Hello World!!
OKですね。
もちろん、「oc new-build」時に環境変数「MAVEN_ARGS_APPEND」を指定できるのも同じです。
$ oc new-build openshift/redhat-openjdk18-openshift:1.4~http://〜/maven-multi-project-jar.git -e ARTIFACT_DIR=launcher/target -e MAVEN_ARGS_APPEND='-pl launcher -am'
実行されるMavenのコマンドは、このようになりました。
$ mvn -Dmaven.repo.local=/tmp/artifacts/m2 -s /tmp/artifacts/configuration/settings.xml -e -Popenshift -DskipTests -Dcom.redhat.xpaas.repo.redhatga -Dfabric8.skip=true package --batch-mode -Djava.net.preferIPv4Stack=true -pl launcher -am
ちなみに、このようなマルチモジュール構成で環境変数「ARTIFACT_DIR」を指定しなかった場合は、
デプロイ対象が見つからずにエラーになります。
Copying Maven artifacts from /tmp/src/target to /deployments ... Running: cp *.jar /deployments /usr/local/s2i/assemble: line 71: cd: /tmp/src/target: No such file or directory cp: cannot stat '*.jar': No such file or directory Aborting due to error code 1 for copying artifacts from /tmp/src/target to /deployments error: build error: non-zero (13) exit code from registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift@sha256:dc84fed0f6f40975a2277c126438c8aa15c70eeac75981dbaa4b6b853eff61a6
ここは、WARの時とは異なりますね。といっても、WARの時はデプロイが空振りするので、結局それでは
ダメなのですけどね。
YAMLで書くと
今回のBuildConfigをYAMLで書いた場合の、該当の箇所を抜粋すると、こうですね。
apiVersion: v1 kind: BuildConfig metadata: ## 省略... spec: ## 省略... strategy: sourceStrategy: env: - name: ARTIFACT_DIR value: launcher/target - name: MAVEN_ARGS_APPEND value: -pl launcher -am ## 省略...
オマケ
ビルドのこういった環境変数を扱っている箇所を、ちょっと探しておきました。
WildFly
ARTIFACT_DIR
https://github.com/openshift-s2i/s2i-wildfly/blob/720a8d33b6af1cdc39d28333d44df7adb6bcda9b/12.0/s2i/bin/assemble#L220
MAVEN_ARGS_APPEND(とMAVEN_ARGS)
https://github.com/openshift-s2i/s2i-wildfly/blob/720a8d33b6af1cdc39d28333d44df7adb6bcda9b/12.0/s2i/bin/assemble#L255-L277
S2Iのスクリプトを確認。
$ oc get istag/wildfly:12.0 -n openshift -o yaml
YAMLを抜粋。
apiVersion: image.openshift.io/v1 generation: 2 image: dockerImageLayers: ## 省略... dockerImageMetadata: ## 省略... Config: Cmd: - /bin/sh - -c - $STI_SCRIPTS_PATH/usage Entrypoint: - container-entrypoint
container-entrypointを見よ、と。
「oc rsh」か「oc debug」で確認。
$ oc rsh dc/maven-multi-project-war ## または $ oc debug dc/maven-multi-project-war
で、「container-entrypointo」は、と。
sh-4.2$ which container-entrypoint /usr/bin/container-entrypoint sh-4.2$ cat $(which container-entrypoint) #!/bin/bash exec "$@"
うん、わからん…。
Dockerfileを見てみます。
https://github.com/openshift-s2i/s2i-wildfly/blob/master/12.0/Dockerfile
「STI_SCRIPTS_PATH」を見ればよいみたいです。
# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH COPY ./s2i/bin/ $STI_SCRIPTS_PATH
そういえば、さっきのYAMLにも書いてありましたね。
Config: Cmd: - /bin/sh - -c - $STI_SCRIPTS_PATH/usage Entrypoint: - container-entrypoint
「/usr/libexec/s2i」らしいです。
sh-4.2$ env | grep STI_SCRIPTS_PATH STI_SCRIPTS_PATH=/usr/libexec/s2i
では、assembleを確認。
sh-4.2$ view $STI_SCRIPTS_PATH/assemble
「MAVEN_ARGS_APPEND」や
# Append user provided args if [ -n "$MAVEN_ARGS_APPEND" ]; then export MAVEN_ARGS="$MAVEN_ARGS $MAVEN_ARGS_APPEND" fi
「ARTIFACT_DIR」を見てみたり。
# the subdirectory within LOCAL_SOURCE_DIR from where we should copy build # artifacts (*.war, *.jar) ARTIFACT_DIR=${ARTIFACT_DIR:-target}
OpenJDK
S2Iのスクリプトを確認。
$ oc get istag/redhat-openjdk18-openshift:1.4 -o yaml
YAMLの抜粋。
apiVersion: image.openshift.io/v1 generation: 2 image: dockerImageLayers: ## 省略... dockerImageManifestMediaType: application/vnd.docker.distribution.manifest.v2+json dockerImageMetadata: Architecture: amd64 Config: Cmd: - /usr/local/s2i/run Env: - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/s2i
「oc rsh」、もしくは「oc debug」でコンテナ内に入って、確認。
$ oc rsh dc/maven-multi-project-jar ## もしくは $ oc debug dc/maven-multi-project-jar $ sh-4.2$ view /usr/local/s2i/assemble
スクリプトの一部を見てみます。
sh-4.2$ view /usr/local/s2i/assemble
こんな感じ。
function build_maven() { # Where artifacts are created during build local build_dir=$1 # Where to put the artifacts local app_dir=$2 local jvm_option_file=/opt/run-java/java-default-options if [ -z "${MAVEN_OPTS}" -a -x "$jvm_option_file" ] ; then export MAVEN_OPTS="$($jvm_option_file)" echo "Setting MAVEN_OPTS to ${MAVEN_OPTS}" fi # Default args: no tests, if a module is specified, only build this module local maven_args=${MAVEN_ARGS:--e -Popenshift -DskipTests -Dcom.redhat.xpaas.repo.redhatga -Dfabric8.skip=true package} # Use batch mode (CLOUD-579) echo "Found pom.xml ... " local mvn_cmd="${maven_env_args} ${maven_args} --batch-mode -Djava.net.preferIPv4Stack=true ${MAVEN_ARGS_APPEND}" echo "Running 'mvn ${mvn_cmd}'"