0%

Docker Composeを使ってClojure開発環境を作る

前回Docker ComposeでSailsの開発環境を用意しました。Clojureの勉強用環境もDocker Composeで作ってみます。開発用のJavaはDockerコンテナを使いながら、ホストマシンのEmacsを使ってコードを編集できるようにします。

プロジェクト

プロジェクトのディレクトリを作成します。Dockerfile、docker-compose.yml、m2ディレクトリを作成します。

$ cd ~/clojure_apps
$ tree .
.
├── Dockerfile
├── docker-compose.yml
└── m2

DockerとDocker Composeのバージョンを確認しておきます。

$ docker version
...
Server version: 1.6.0
$ docker-compose --version
docker-compose 1.2.0

Dockerfile

ベースイメージはオフィシャルのclojureを使います。Dockerホストの作業ユーザーと同じuidでユーザーを作成して、ボリューム内に作成されたファイルを編集できるようにします。

~/clojure_apps/Dockerfile
FROM clojure
MAINTAINER Masato Shimizu <ma6ato@gmail.com>

WORKDIR /usr/src/app

RUN apt-get update && apt-get install sudo net-tools && \
rm -rf /var/lib/apt/lists/*

RUN adduser --disabled-password --gecos '' --uid 1000 docker && \
adduser docker sudo && \
echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
mkdir /home/docker/.m2 && \
chown -R docker:docker /usr/src/app /home/docker/.m2

VOLUME /home/docker/.m2
USER docker
RUN lein

ENTRYPOINT ["lein"]

Dockerイメージは開発中も変更がないのでローカルにビルドしておきます。

$ cd ~/clojure_apps
$ docker build -t clojure .

REPL

Clojure REPLを実行するための基本的なdocker-compose.ymlを用意します。WORKDIR/usr/src/appをカレントディレクトリにマップします。

~/clojure_apps/docker-compose.yml
lein: &defaults
image: clojure
volumes:
- .:/usr/src/app
- ./m2:/home/docker/.m2

Docker Composeのleinサービスをワンショットで実行します。初回実行時に/home/docker/.m2にMavenよりjarファイルがダウンロードされます。.m2ディレクトリはDockerホストにマップしているので2回目以降からはダウンロード済みのjarファイルを使います。

$ cd ~/clojure_apps
$ docker-compose run --rm lein repl
...
nREPL server started on port 54118 on host 127.0.0.1 - nrepl://127.0.0.1:54118
REPL-y 0.3.5, nREPL 0.2.6
Clojure 1.6.0
OpenJDK 64-Bit Server VM 1.7.0_79-b14
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e

user=>

Compojure

WebフレームワークのCompojureを使ったプロジェクトを作成してみます。

$ cd ~/clojure_apps
$ docker-compose run --rm lein new compojure hello-world
Retrieving compojure/lein-template/0.4.2/lein-template-0.4.2.pom from clojars
Retrieving compojure/lein-template/0.4.2/lein-template-0.4.2.jar from clojars
Removing clojureapps_lein_run_1...

カレントディレクトリにCompojureのプロジェクトが作成されました。

$ tree hello-world/
hello-world/
├── README.md
├── project.clj
├── resources
│   └── public
├── src
│   └── hello_world
│   └── handler.clj
└── test
└── hello_world
└── handler_test.clj

Docker ComposeからCompojureを起動するためにdocker-compose.ymlを編集します。working_dirにComposeプロジェクトのディレクトリを指定します。

~/clojure_apps/docker-compose.yml
lein: &defaults
image: clojure
volumes:
- .:/usr/src/app
- ./m2:/home/docker/.m2
working_dir: /usr/src/app/hello-world
ports:
- "3000:3000"

Ringサーバーを起動します。lein ring serverで起動するとNo X11 DISPLAY variable was setとエラーがでてしまいます。ヘッドレスなDockerなのでlein ring server-headlessで起動します。また--service-portsフラグを指定してDockerホストにポートをマップします。

$ docker-compose run --rm --service-ports lein ring server-headless
...
2015-05-16 09:42:00.573:INFO:oejs.Server:jetty-7.6.8.v20121106
2015-05-16 09:42:00.608:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000
Started server on port 3000

初回起動時にはjarファイルをダウンロードするためすこし時間がかかります。Dockerホストで別のシェルを起動してサーバーの起動を確認します。

$ curl -s localhost:3000
Hello World

Clojure Script REPL

CompojureプロジェクトでClojure Script REPLを使ってみます。project.clj:plugin:cljsbuildディレクティブを追加します。Clojureは1.6を使っているのでClojure Scriptは1.6系の0.0-3126を指定します。

~/clojure_apps/hello-world/project.clj
(defproject hello-world "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.6.0"]
[compojure "1.3.1"]
[ring/ring-defaults "0.1.2"]
[org.clojure/clojurescript "0.0-3126"]]
:plugins [[lein-ring "0.8.13"]
[lein-cljsbuild "1.0.6"]]
:ring {:handler hello-world.handler/app}
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring-mock "0.1.5"]]}}
:cljsbuild {
:builds [{
:source-paths ["src-cljs"]
:compiler {
:output-to "resources/public/main.js"
:optimizations :whitespace
:pretty-print true}}]}))

docker-compose.ymlはClojure Script用のサービスをcljsleinとしてleinサービスをマージして用意します。

~/clojure_apps/docker-compose.yml
lein: &defaults
image: clojure
volumes:
- .:/usr/src/app
- ./m2:/home/docker/.m2
working_dir: /usr/src/app/hello-world
ports:
- "3000:3000"
cljslein:
<<: *defaults
ports:
- "9000:9000"

Docker Composeからcljsleinサービスを実行します。

$ docker-compose run --rm --service-ports cljslein trampoline cljsbuild repl-rhino
Running Rhino-based ClojureScript REPL.
...
Retrieving org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar from central
Retrieving org/clojure/tools.reader/0.8.16/tools.reader-0.8.16.jar from central
To quit, type: :cljs/quit
ClojureScript:cljs.user>

Clojure Script REPLからJavaScriptのDate関数を呼んでみます。

ClojureScript:cljs.user> (js/Date)
#inst "2015-05-16T10:17:55.200-00:00"