GCPにアプリを公開してみたの巻

はじめに

ふとしたことから、最近GCPにアプリを置いて公開してみようというモチベーションが高まっている。
紆余曲折しながらハローワールド的な超簡単なアプリのデプロイまでこぎつけることができた。
とはいえ、その中で結構つまることがあったので、自分のメモがてら記載する。

紆余曲折の数々

※あくまで「自分は」うまくいかなかったという話。もしかしたらほかの人はその手順を踏んだらうまくいくことも十分ありうる。

●Play Framework(2.6.X)で開発してWebアプリをGAEにデプロイしてみよう。
 ⇒ Play FrameworkのGCPへのデプロイをサポートしていたのはバージョン1系のときらしい。今はサポートしていないっぽいため断念。
(参考)deployment - 1.2.x

●Play Framework(2.6.X)で開発して、GCEのインスタンス内に建てたTomcatにデプロイしてみよう。
 ⇒デプロイするにはwarファイルを作らないといけない。Play Frameworkでwarファイルを作るためには「play2-war-plugin」が必要らしい。
 ⇒エラーの内容は忘れたが、warファイルを作ろうとしてもエラーが出て、原因がわからかったので断念。
(参考)Playプロジェクトをapache上にデプロイするための備忘録 - Qiita

JSP/Servletでwarファイルを作って、GCEのインスタンス内に建てたTomcatにデプロイしてみよう。
 ⇒JavaEEの動的Webアプリケーションプロジェクトを作成:OK
  Mavenプロジェクト化してパッケージングの形式として「war」を指定&ビルド:OK
  GCE内にTomcatを建てる:OK
  作ったwarファイルをwebappフォルダ内に設置:OK
  ローカルホストで表示されることを確認:OK
  外部からアクセスして表示されることを確認:NG
 ⇒apache webサーバの.htaccessTomcat版を編集してアクセス許可すればいいはずだ、と思って色々調べてみたが結局どれもうまくいかず、断念。

JSP/Servletでwarファイルを作ってGAEにデプロイしてみよう。
 ⇒最終的にこれに落ち着いた。これのデプロイ手順を本記事では記載する。
※あくまで「自分はこうやったらうまくいった」というのを書いているだけで、必ずしもこれがほかの人にとっての最適解になる訳ではないことをご承知おきいただきたい。

登場人物

GCPGoogle Cloud Platform)
 ⇒自分のGoogleアカウントを使ってプロジェクトを作成しておく必要あり。

・Cloud SDK
 ⇒Installing Google Cloud SDK  |  Cloud SDK Documentation  |  Google Cloud を参照。
  ※Java1.7以上が必要とのこと。自分はJDK1.8をインストール済み。
 ⇒インストール後は環境変数にPathを通しておく。

Maven
 ⇒GAEにデプロイするときや、ビルドするときに必要。自分は最新バージョン(2018/11/05時点)の3.6.0をインストール。
 ⇒Cloud SDKをインストールすることで、デプロイするためのコマンド(後述)を使えるようになる。

デプロイするための手順

1. JSP/Servletの作成およびローカルビルド
 ⇒ローカルで動作確認&Mavenビルドを実施する。pomの内容は以下のような感じ。
 ※自分の場合、eclipseで開くと「versions-maven-plugin」のところで「プラグイン実行がライフサイクルでカバーされていません」というエラーが出るが、Mavenビルドはうまくいくのでとりあえずそのままにしている。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>MyAppProject</groupId>
	<artifactId>MyAppProject</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<build>
	<sourceDirectory>src</sourceDirectory>
	<plugins>
		<plugin>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.1</version>
			<configuration>
				<source>1.7</source>
				<target>1.7</target>
			</configuration>
		</plugin>
 		<plugin>
			<artifactId>maven-war-plugin</artifactId>
			<version>2.3</version>
			<configuration>
				<warSourceDirectory>WebContent</warSourceDirectory>
				<failOnMissingWebXml>false</failOnMissingWebXml>
				<webResources>
					<!-- in order to interpolate version from pom into appengine-web.xml -->
					<resource>
 						<directory>${basedir}/WebContent/WEB-INF</directory>
						<filtering>true</filtering>
						<targetPath>WEB-INF</targetPath>
					</resource>
				</webResources>
			</configuration>
		</plugin>

		<plugin>
			<groupId>org.codehaus.mojo</groupId>
			<artifactId>versions-maven-plugin</artifactId>
			<version>2.1</version>
			<executions>
				<execution>
					<phase>compile</phase>
					<goals>
						<goal>display-dependency-updates</goal>
						<goal>display-plugin-updates</goal>
					</goals>
				</execution>
			</executions>
		</plugin>

		<plugin>
			<groupId>com.google.appengine</groupId>
			<artifactId>appengine-maven-plugin</artifactId>
			<version>${appengine.sdk.version}</version>
			<configuration>
				<enableJarClasses>false</enableJarClasses>
				<!-- Comment in the below snippet to bind to all IPs instead of just localhost -->
				<!-- address>0.0.0.0</address>
				<port>8080</port -->
				<!-- Comment in the below snippet to enable local debugging with a remote debugger
					like those included with Eclipse or IntelliJ -->
				<!-- jvmFlags>
				<jvmFlag>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n</jvmFlag>
				</jvmFlags -->
			</configuration>
		</plugin>

	</plugins>

	</build>
	<!-- properties for Google App Enginie -->
	<properties>
		<appengine.app.version>1</appengine.app.version>
		<appengine.sdk.version>1.9.48</appengine.sdk.version>

		<objectify.version>5.1.5</objectify.version>
		<guava.version>18.0</guava.version>

		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>

		<!-- [Begin] for Google App Engiine -->
		<!-- Compile/runtime dependencies -->
		<dependency>
			<groupId>com.google.appengine</groupId>
			<artifactId>appengine-api-1.0-sdk</artifactId>
			<version>${appengine.sdk.version}</version>
		</dependency>
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- [START Objectify_Dependencies] -->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>${guava.version}</version>
		</dependency>
		<dependency>
			<groupId>com.googlecode.objectify</groupId>
			<artifactId>objectify</artifactId>
			<version>${objectify.version}</version>
		</dependency>
		<!-- [END Objectify_Dependencies] -->

		<!-- Test Dependencies -->
		<dependency>
			<groupId>com.google.appengine</groupId>
			<artifactId>appengine-testing</artifactId>
			<version>${appengine.sdk.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.google.appengine</groupId>
			<artifactId>appengine-api-stubs</artifactId>
			<version>${appengine.sdk.version}</version>
			<scope>test</scope>
		</dependency>

		<!-- [End] for Google App Engine -->
  </dependencies>
</project>

 ⇒以下のコマンドを実行。

mvn clean package

 ⇒ビルドに成功したら、以下のコマンドを実行。少し待つと「[INFO] INFO: Dev App Server is now running」というメッセージがコンソール上に表示されるので、それを確認したらブラウザでlocalhostにアクセスする。

mvn appengine:devserver

2. ${basedir}/WebContent/WEB-INF内にあるweb.xmlを編集する。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>MyAppProject</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>com.deu.myapp.servlet.DispatcherServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/dispatcher</url-pattern>
  </servlet-mapping>
</web-app>

3. ${basedir}/WebContent/WEB-INF内にappengine-web.xmlを作成&編集。

<?xml version="1.0" encoding="UTF-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <application>%アプリケーション名%</application>
    <version>${appengine.app.version}</version>
    <threadsafe>true</threadsafe>
</appengine-web-app>

※アプリケーション名の記入を忘れずに!
 筆者はサンプルを落として必要なところを適宜編集していたが、アプリケーション名の編集を忘れていたためにデプロイ時に「You do not have permission to modify this app.」というメッセージが出てつまった。
(参考)マロマゴ: Google App Engineでハマらない為に

4. GAEにデプロイする。
 ⇒以下のコマンドを実行。

mvn appengine:update

※上記コマンドを実行する前に、どのプロジェクトのGAEを使うかをあらかじめ指定する。

gcloud init

5. デプロイに成功したら、`https://[PROJECT-ID].appspot.com`にアクセスする。

所感

・とにかく疲れたが、なんとかGAEデプロイまでこぎつけることができてよかった。
Mavenがあればよいので、「play2-war-plugin」を使わなくてもよかったんじゃ・・・?
Mavenがあればよいので、Play FrameworkでもGAEにデプロイできるんじゃ・・・?
・結局、GCE内のTomcatコンテンツを外部公開できなかった原因は何だったんだろう?どうすればよかったんだろう・・・?
GCPのドキュメント(ゲストブック アプリケーションの作成  |  Java の App Engine スタンダード環境  |  Google Cloud)からサンプルのzipを落として、「自分がデプロイしようとしている設定ファイルと、サンプルの設定ファイルの差は何だろう」「何が記載されていたらデプロイできるんだろう」というのを適宜編集&比較しながら見れたのがよかったかもしれない。

参考文献

Installing Google Cloud SDK  |  Cloud SDK Documentation  |  Google Cloud
ゲストブック アプリケーションの作成  |  Java の App Engine スタンダード環境  |  Google Cloud
アプリケーションのデプロイ  |  Java の App Engine スタンダード環境  |  Google Cloud
マロマゴ: Google App Engineでハマらない為に
Google App Engineでアプリのバージョンを管理する -無料レンタルサーバ(格安・有料)情報のレンサバ.com
Google App EngineのDeployでハマった話 - へっぽこびんぼう野郎のnewbie日記
Google App Engineでハマった件 | 福岡で働くフリーランスエンジニア | kussuue
[Google App Engine]You do not have permission to modify this app – deferloader
 ⇒「You do not have permission to modify this app.」が出たときの原因の一つに、認証情報などデプロイに使われる情報がキャッシュされてしまっていることもあるらしい。今回作業したときはこのケースではかったが、覚えておいて損はない。
goapp deployないしappcfg.py updateで403返されまくってめちゃくちゃハマった - DRYな備忘録
 ⇒同上。
GCPのgcloudコマンドをインストールする - Qiita
Javaの道:Tomcat(9.アクセス制限)
apacheやtomcatでアクセス制限する - ふなWiki
Tomcat8で、デフォルトだとローカル以外からmanagerが表示できない - Enjoy*Study
Playプロジェクトをapache上にデプロイするための備忘録 - Qiita
Production - 2.6.x
Playframework2.6系でアプリを作る - Qiita
Play Framework 2.6 (Java) の環境構築 - Qiita
JSPとどう連携して使うのか (1/4):Java Servlet徹底解説(前編) - @IT
サーブレット/JSPの役割を理解する (1/4):基礎から学ぶサーブレット/JSP(1) - @IT