動機
Play Framework でアプリを開発するにあたって
- ローカルの環境にあれこれ(色々なバージョンのJDKなど)インストールしたくない
- 開発メンバーと開発環境を手軽に共有したい(エディタ除く)
- エティタは個人のものを使いたい(ソースコードはローカルの環境から編集したい)
- 出来上がったアプリはDocker環境でデプロイしたい
という希望があって、Dockerで開発環境を構築したのでその記録です。
Play Frameworkは開発が活発で、バージョンごとに変更点も多いです。この記事では現時点(2018/2/6)で最新の Play Framework 2.6について、記述します。
コツ
たとえばRuby on Railsでしたら、一つのDockerコンテナで開発からデプロイまで全部まかなうことができます(実際にはデプロイではまったりしますが)。
参考:Quickstart: Compose and Rails (docker docs)
しかし、Play Framework 2.6の場合、
- プロジェクトの初期化
- 開発
- 本番の実行環境
と、3つのDocker環境を分けて考えたほうがうまくいきます。
これを順を追って説明していきます。
全体の流れ
Dockerの有無に関わらず、Play Framework 2.6では、プロジェクトのビルドにsbtを使用します。
公式ドキュメントに従うと、Play Frameworkによるアプリの開発は
という流れになります。
これら一連の作業をDockerの環境内に封じ込めてしまいたいというのが本記事の主旨です。
Dockerはインストール済みであるという前提で話を進めていきます。
sbt のインストール
まずはsbtをインストールしたDockerコンテナを用意します。
hseeberger さんが Docker Hub に、sbtインストール済みのコンテナを公開してくれています。これを利用するのが早くて確実です。
hseeberger/scala-sbt
これを使えば
$ docker run -it --rm hseeberger/scala-sbt:8u151-2.12.4-1.1.0
でsbtのイメージを含んだDocker環境を利用できます。
- -it はDockerコンテナをインタラクティブな状態で起動するためのオプションです
- --rm は使い終わったコンテナを自動で破棄するためのオプションです
- Javaのバージョンは大事なので、タグまでがっつり指定しています
上記のコマンドを実行すると、Dockerコンテナ内の/bin/bash
が起動しますので、bash上でsbtを起動することができます。
root@72ece8e13c72:~# sbt
※ここでもしも個人公開のリポジトリを使うのが不安だというのでしたら、hseeberger さんが公開してくれているDockerfileを見て、オリジナルのDockerfileを作成しておくのもありです。シンプルなDockerfileですので、一見の価値ありです。
※Java8を使っているのがポイントです。Java9以降でPlay Framework2.6を使おうとするとモジュール不足で色々と大変なことになります。これはDockerの有無とは関係ありません。また、現時点でこの問題を解決できたという情報を見つけられていません。
プロジェクトの初期化
作業するディレクトリに移動します。このディレクトリの直下にアプリ開発用のディレクトリが新たに作られることになります。
たとえば、~/workspaces
ディレクトリにhello
ディレクトリを作ってそこにhelloプロジェクトを収めたい場合は、
cd ~/workspaces
して、ここからsbtのコマンドを使ってhelloディレクトリを作っていくことになります(あせって$ mkdir hello
しないこと)。
作業ディレクトリにcd
したらプロジェクトの初期化のためのDockerコンテナ(コンテナその1)を起動します。
$ docker run -it --rm -v=$(pwd):/root hseeberger/scala-sbt:8u151-2.12.4-1.1.0
- -vはコンテナ内のディレクトリをローカルのディレクトリにマウントするためのオプションです。ここでは、Dockerコンテナ内の
/root
ディレクトリが、ローカルの作業用のディレクトリ(~/workspaces
)にマウントされます。
※Dockerコンテナを起動したときに/root
がコンテナ内のカレントディレクトリになるので、このような指定をしています。
Dockerコンテナが起動したら、コンテナ内でsbt new
を実行します。hello
はこれから作るアプリケーション名です。
# sbt new playframework/play-scala-seed.g8 --name=hello
ここまでで一段落です。
Dockerコンテナを抜けて、カレントディレクトリにプロジェクトが生成されているのを確認します。
$ ls hello
app build.sbt gradle gradlew.bat public
build.gradle conf gradlew project test
共同開発の場合は、このhello
ディレクトリ以下をgit
などで共有することになります。
開発
開発の際には、まずプロジェクトのディレクトリに移動します。
$ cd hello
開発環境(コンテナその2)を起動します。このコンテナは先ほどのプロジェクト初期化用のコンテナとは別物と考えたほうがすっきりいけます。
$ docker run -it --rm -p=9000:9000 -v=$(pwd):/root hseeberger/scala-sbt:8u151-2.12.4-1.1.0
- -p はDockerコンテナのポートをローカル環境のポートに接続するためのオプションです。
Dockerコンテナ内で/bin/bash
が起動されるので、そのコマンドラインからsbtコンソールを起動します。
# sbt
ここでsbtコンソールの起動に時間がかかる(手元の環境で10分以上)ので、このコンテナはなるべく起動しっぱなしにしておいたほうが時間の節約になります。
sbtが起動すると、sbtのコマンドライン上で
$ ~run
などが使えるようになります。
開発中のアプリのソースにはローカルの ~/workspaces/hello
以下の通常のファイルとしてアクセスできるので、好きなエディタやIDEを使って開発できます。
デプロイ
上記の開発環境内のsbtのコマンドライン上で
$ dist
とすれば、ローカルの~/workspaces/hello/target/universal
フォルダにhello-1.0-SNAPSHOT.zip
といったファイルができます。これはDockerを使わない開発手順と何ら変わりがありません。
このzipファイルを本番サーバーに持って行って展開する、というのがPlay Frameworkの通常のデプロイ手順になります。もちろん、サーバー上にJavaの実行環境が正しくインストールされている必要があります。
しかし、サーバー上にたくさんのバージョンのJava実行環境がインストールされているのは美しくないのでDockerでいきましょう。
ここでは、本番サーバー上でDockerが稼働している前提で、docker-compose up
できるようにしていきます。
Docker用ファイルの準備
Play Frameworkでは、プロジェクトのディレクトリ内にdist
というディレクトリを作ってそこにファイルを置いておくと、そのファイルがデプロイの際の配布物にそのまま取り込まれます。
ですので、まずdist
ディレクトリを作成します。
$ cd ~/workspaces/hello
$ mkdir dist
作成したdist
ディレクトリ内にDockerfile
とdocker-compose.yml
を用意します。
Dockerfile
は以下のようになります。
FROM openjdk:8u151-jre
COPY . /root/
RUN chmod +x /root/bin/hello
- 開発環境と、Javaのバージョンを揃えるように注意してください。
- 最後の
RUN chmod...
は、zip形式でアーカイブする際に必要になるものです。tgzなどファイル属性を保持できる形式でアーカイブするのであれば必要ありません。
docker-compose.ymlは以下のようになります。
version: '3'
services:
app:
build: .
command: /root/bin/hello -Dplay.http.secret.key=xxxxxxxxxxxx
ports:
- "9000:9000"
- play.http.secret.keyは適宜設定してください。
デプロイ用アプリケーションの作成
Docker用のファイルの準備ができたら、__開発用のコンテナ(コンテナその2)__のsbtコマンドライン上で、$ dist
(zip形式でアーカイブする場合。tgzの場合は$ universal:packageBin
)を実行します。
~/workspaces/hello/target/universal
ディレクトリにhello-1.0-SNAPSHOT.zip
(tgz形式の場合はhello-1.0-SNAPSHOT.tgz
)ができます。
このファイルを本番用サーバーに持っていって展開すると
Dockerfile conf lib
bin docker-compose.yml share
といったファイル群が取り出せます。
このディレクトリで
$ docker-compose up
すればDockerコンテナ(コンテナその3)内でアプリを起動することができます。
結論
Play FrameworkアプリをDocker上で開発する場合はDockerコンテナを3つ用意して
- Dockerコンテナその1は、
sbt new
専用 - Dockerコンテナその2は開発専用
- Dockerコンテナその3は本番環境専用
と分けて考えるとうまくいきます。