Play Framework を Elastic Beanstalk(Java Platform, 非Tomcat) にデプロイする

[AWS Official Blog](https://aws.amazon.com/blogs/aws/) に [Elastic Beanstalk Update – Support for Java and Go](https://aws.amazon.com/jp/blogs/aws/elastic-beanstalk-update-support-for-java-and-go/) という News が流れてきました.
任意の Java, Golang アプリケーションが実行可能になり, Nginx がそれらに対して Reverse Proxy することができるようになったようです.

ちなみに現在運用しているサービスでは Play Framework を使用しており, [play2-war-plugin](https://github.com/play2war/play2-war-plugin) を使用してElastic Beanstalk の Tomcat にデプロイしています.
この場合, 前段に Apache が存在し裏の Tomcat に Reverse Proxy するというような構成になっていますが, 今回試す環境では, 前段に Nginx, 裏に Java のプロセスとしての Play となります.

では, 雑に試していきます.
以下で使用したサンプルプロジェクトについては https://github.com/lulichn/play-beanstalk-sample においてあります.

# 前提
以下の例では Play Framework は2.3系(Java7) を使用し, Play コマンドで操作を行っています.
以降のバージョンや, Activator でも大きく手順は変わらないと思いますが, 適宜読み替えて下さい.

# Elastic Beanstalk 環境の作成
適当な Application に Web Server Environment を作成します.

Web Console から作成する場合は, 「Java」を選択します.
![EnvType.png](https://qiita-image-store.s3.amazonaws.com/0/55056/0b7c4c2a-c8d4-6e90-1079-2ad924eb636f.png)

CLIからの場合は, 「"64bit Amazon Linux 2015.03 v2.0.2 running Java 7(or 8)"」を使用します.

```lang:SolutionStackName
$ aws elasticbeanstalk list-available-solution-stacks | jq .SolutionStacks[]
...
"64bit Amazon Linux 2015.03 v2.0.2 running Java 8"
"64bit Amazon Linux 2015.03 v2.0.2 running Java 7"
"64bit Amazon Linux 2015.03 v2.0.3 running Go 1.4"
```

以降は今までの Beanstalk の環境作成と同じ手順です.

# アプリケーションアーカイブファイルの作成
- サンプルプロジェクトの作成  
今回は適当なサンプルプロジェクト「PlayBeanstalkSample」を作成しました.

```lang:NewApplication
$ play new play-beanstalk-sample
What is the application name? [play-beanstalk-sample]
> PlayBeanstalkSample

Which template do you want to use for this new application?
> 1

Have fun!
```

- アーカイブの作成  
[Creating a standalone version of your application](https://www.playframework.com/documentation/2.3.x/ProductionDist) に従って, zip ファイルを作成します.  
以下の様な構成の zip ファイルができあがりました.

```lang:dist
$ cd play-beanstalk-sample
$ play dist

./target/universal/playbeanstalksample-1.0-SNAPSHOT.zip
  ├ bin/
  │  ├ playbeanstalksample
  │  └ playbeanstalksample.bat
  ├ conf/
  ├ lib/
```

- Procfile の作成  
Play アプリケーションを Elastic Beanstalk で実行するために, Procfile というファイルで実行方法を指定してあげる必要があります.  
(単独で実行できるJarファイルであれば, 省略できるようです?)  
[Using the AWS Elastic Beanstalk Java SE Platform](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/java-se-platform.html) に従って, Procfileを作成します.

```lang:Procfile
web: sh bin/playbeanstalksample
```

- Nginx の設定の変更  
Play アプリケーションを bin/playbeanstalksampleから実行するとデフォルトで Listen するポートは9000番となりますが, Elastic Beanstalk の Nginx がデフォルトで転送するポートは5000番となっているので変更が必要になります.  
どちら側からすり合わせても良いと思いますが, 今回は Nginx の設定側を変更する事にします.  
環境を作成した際に立ち上がってきたインスタンスから, Nginx の設定ファイル (conf.d/elasticbeanstalk/00_application.conf) をコピーしてきて, proxy_pass の部分のみ変更を行いました.  

```lang:00_application.conf
$ less conf.d/elasticbeanstalk/00_application.conf

location / {
    proxy_pass          http://127.0.0.1:9000;  # :5000 -> :9000
    proxy_http_version  1.1;

    proxy_set_header    Connection          $connection_upgrade;
    proxy_set_header    Upgrade             $http_upgrade;
    proxy_set_header    Host                $host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
}
```

- zip の再構成  
以上で準備は整ったので, Elastic Beanstalk にデプロイできる形に Procfile 等を込めて, 圧縮しなおします.  
ここで .ebextensions/nginx ディレクトリ以下に Nginx の設定ファイルを配置するとデプロイの際に設定が上書きされます.

```lang:PlayBeanstalkSample-1.0-SNAPSHOT-eb.zip
./target/universal/PlayBeanstalkSample-1.0-SNAPSHOT-eb.zip
  ├ .ebextensions/
  │  └ nginx/
  │      └ conf.d/
  │          └ elasticbeanstalk/
  │              └ 00_application.conf
  ├ bin/
  ├ conf/
  ├ lib/
  ├ Procfile
```

- ビルドタスクの追加  
play dist コマンドで zip を作って, 必要なファイルをコピーして, 圧縮してを繰り返すのは大変なので, 不恰好な単純なビルドタスクを追加します.  

```lang:build.sbt
import play.PlayScala

name := "PlayBeanstalkSample"

version := "1.0-SNAPSHOT"

libraryDependencies ++= Seq(
)     

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.5"

lazy val distEb = TaskKey[Unit]("dist-eb", "")

distEb := {
  // Constants
  val targetFile = s"./target/${name.value}-${version.value}-eb.zip"
  val procFile   = "Procfile"
  val extensions = ".ebextensions"
  // Make Package
  val result = (packageBin in Universal).value
  val parent  = result.getParent
  val archive = Path.apply(result).base
  // Decompression
  IO.unzip(result, new File(s"$parent"))
  // Copy Files
  IO.copyFile(new File(procFile), new File(s"$parent/$archive/$procFile"))
  IO.copyDirectory(new File(extensions), new File(s"$parent/$archive/$extensions"))
  // Re-Compression
  IO.zip(Path.allSubpaths(new File(s"$parent/$archive")), new File(targetFile))
}
```  


```lang:dist-eb
$ play dist-eb

./target/PlayBeanstalkSample-1.0-SNAPSHOT-eb.zip
  ├ .ebextensions/
  │  └ nginx/
  │      └ conf.d/
  │          └ elasticbeanstalk/
  │              └ 00_application.conf
  ├ bin/
  ├ conf/
  ├ lib/
  ├ Procfile
```

# デプロイ  
Web Console などから, 再構成した zip ファイルをアップロード・デプロイします.
![play-eb.png](https://qiita-image-store.s3.amazonaws.com/0/55056/a93112cb-df60-550f-8332-a5c2dd63ea7f.png)
起動を確認できました. (こんなにシンプルでしたっけ）  
/var/log/eb-activity.log でデプロイ時などのログが見られます.

Play Framework で作成したアプリケーションを Tomcat に対してしてデプロイするより, 内容的かなりシンプルになった様に感じますね.
前段の Nginx の設定を変更するのも, Apache の場合より容易です.