uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

Dockerを使ってGrails開発

Grails開発でdockerを使用するためのDockerfileを、備忘録兼ねて晒します。開発中に使うものです。運用用は、別のものになるでしょうかね。
onesysadmin/docker-grailsを元にして、Proxy設定を行い、いくつかの工夫をしております(プロキシ設定については、Dockerコンテナに透過的プロキシ設定とかをすれば不要なのかも)。
https://blog.linode.com/wp-content/uploads/2014/01/docker.pnghttp://www.simplicityitself.com/wp-content/uploads/2013/04/grails.png

利点

Grails開発でDockerを使う利点は以下の通り。

  • 準備が簡単。Grailsのインストールはもとより、gvmやJDKのインストールすらいらない。grails wrapperなどもいらないわけだ。
  • 環境を汚さない。JDKのインストールをしなくてすむ、Postgresなどをコンテナに封入して連携する、などによる。
  • 設定含めた開発時実行環境を開発メンバー間でシェアできる
  • Dockerはlinuxなので、開発環境windows、実行環境linuxによる差異をなくせる。windowsの場合、boot2dockerなどを使って仮想マシン上でlinuxを動かすことになる(MacOSXではDocker 1.3のboot2dockerでホストのフォルダの共有とVOLUME対応(-v指定による共有)がされているそう]だが、まだ試していない。)。

Dockerfileの準備

まず、onesysadmin/docker-grailsをcloneするなりダウンロードするなりして、使用したいGrailsのバージョンのDockerfileを、Grailsプロジェクトのトップディレクトリにコピーします。

Dockerfileの修正

以下に修正点を示します。修正したDockerfile全体はこちらのgistに

Proxyの設定

開発環境がファイアウォール背後でproxyの設定が必要になる場合は以下のように設定します。今回の場合、プロキシは以下において必要になります。

ENV PROXY_HOST 10.2.3.4
ENV PROXY_PORT 18080
ENV http_proxy http://${PROXY_HOST}:${PROXY_PORT}/
ENV https_proxy http://${PROXY_HOST}:${PROXY_PORT}/

ENTRYPOINT, EXPOSEの設定

ENTRYPOINT [ "/usr/local/bin/gvm-exec.sh", "grails", "-Dgrails.work.dir=/app/.grails" ]
EXPOSE 8080

gvm-exec.shや後で出てくるgvm-wrapper.shは「onesysadmin/docker-grails」の親であるイメージ「onesysadmin/gvm」が入れてくれているスクリプトです。

RUN関係

RUN gvm-wrapper.sh install grails 2.4.4 && \
    gvm-wrapper.sh flush archives && \
    cd /app && \
    gvm-exec.sh grails -Dgrails.work.dir=/app/.grails add-proxy client --host="${PROXY_HOST}" --port="${PROXY_PORT}" && \
    gvm-exec.sh grails -Dgrails.work.dir=/app/.grails set-proxy client
RUN mv /root/.grails/ProxySettings.groovy /root/.grails/ProxySettings.groovy.bak && \
    sed -e "s/'http.nonProxyHosts':''/'http.nonProxyHosts':'localhost'/" /root/.grails/ProxySettings.groovy.bak > /root/.grails/ProxySettings.groovy

proxyの設定がポイント。interactiveにしたときでrun-appしてstop-appして再起動したときにportが所有されつづけてしまう問題(address already in useとなる)の回避のため、nonProxyにlocalhostを設定します。

GRAILS_PROJECT/grails-app/conf/BuildConfigの修正

grails.project.dependency.resolutionの「mavenLocal()」を以下のように修正します。

        mavenLocal("/app/.m2")

これで依存jarやプラグインが毎回ダウンロードされるのを防ぐことができます*1。これがポイント。

コンテナのビルド

$ cd /path/to/grails/project
$ docker build -t you/project .

Grailsコンテナ実行(例)

$ docker run -it -p 8080:8080 -v /path/to/grails/project:/app you/project:latest # grailsインタラクティブ起動
$ docker run -d -p 8080:8080 -v /path/to/grails/project:/app you/project:latest run-app # grails run-app実行

ホストの8080ポートにアクセスするとコンテナの8080(Grailsのデフォルトポート)にアクセスできます。

DBコンテナとの連携

必要に応じて。以下はPostgresを前提とします。
Postgresのコンテナは、以下を参考に「orchardup/postgresql/」を使用します。

以降、通常のDB接続の設定(ドライバ追加のためにBuildConfig.groovyのdependenciesに「 runtime 'postgresql:postgresql:9.1-901.jdbc4'」を追加するなど)を行なった上での設定です。

DataSource.groovyの修正

driverClassNameやDB認証などの修正
dataSource {
    pooled = true
    // jmxExport = true
    driverClassName = "org.postgresql.Driver"
    username = System.getenv("DB_ENV_POSTGRESQL_USER")
    password = System.getenv("DB_ENV_POSTGRESQL_PASS")
}
JDBC URLの設定

必要な環境について、URLを設定します。

            url = "jdbc:postgresql://${System.getenv('DB_PORT_5432_TCP_ADDR')}:${System.getenv('DB_PORT_5432_TCP_PORT')}/docker"

「docker」はorchardup/postgresqlが初期的にCREATEしてくれるデータベース名です。別の名前にしたいときは、orchardup/postgresqlの起動時にDB名を指定できるので、それにあわせたものを使えば良いでしょう。

DB連携したGarilsコンテナ実行(例)

$ docker run -d -p 5432:5432 -e POSTGRESQL_USER=docker -e POSTGRESQL_PASS=docker --name pg orchardup/postgresql:latest
$ docker run -d -p 8080:8080 -v /path/to/grails/project:/app --link pg:db  you/project:latest run-app

おまけ

ENTRYPOINTを指定しているので、bashなどを起動したい場合は、

$ docker run -it -v /path/to/grails/project:/app --link pg:db  --entrypoint "bash" you/project:latest

こうかな。

まとめ

上記は、Grailsに限らず、開発時にdockerを使用する場合のパターンの一つになると思うが、ホストのディレクトリのマッピング(-v)を駆使してソース含めほとんどのファイルを外に置くコンセプトなので、単独実行するのと使用感も制約もあまりかわらない。しかし、このパターンだと、リモート利用、つまりdockerクライアントを実行するマシンとdockerデーモンが動作するマシンが別の場合には利用できない。つまりdockerデーモンを動かしているホストのlinuxマシンにログインできるアカウントがないと利用できない。dockerは単体でNFSや高速rsyncみたいなものを提供しないのが残念である。できればもっと開発用PaaSっぽく使えるのに!(ADDじゃ遅そうだし動的更新ができない)*2

このパターンであれば、Eclipse/GGTSなどをdockerで実行するのもそんなに難しくはない。

*1:なお、grails.project.dependency.resolver = "maven"にしているのにもかかわらず、/root/.grails/ivy-cacheが作成され何かが入っている。理由不明

*2:将来的にはFUSEで対応されるかもしれないんだって。やったー。