Spring Boot アプリケーションをDockerで動かす 第2回

河上です。

前回に引き続き、Spring BootアプリケーションをDockerで動かしていきます。

Spring Boot アプリケーションはインメモリDBでの開発がとても楽ですが、リリース前はやはり本番と同じデータベースサーバで動作を確認したくなります。
そして、それを行うにしても以下のような作業を行う必要があって本当に面倒です。

  • データベースサーバのインストールと起動
  • スキーマを作成するSQLの実行
  • テストデータの登録
  • アプリケーションの起動

もう1つ、これらを手動で行っている場合の大きな問題点としてデータベースの状態が毎回変わる、もしくはどういう状態かわからないのでテストしにくいという問題があります。

今回は、これらの問題を解消すべくDockerコンテナを使って自動化してしまいます。

Dockerは1.5が出ていますが、まだ1.4です。

データーベースサーバは、PostgreSQL 9.4 を使います。

ディレクトリ構成は以下のようにdatabaseとtodoというディレクトリがフラットに並んでいます。gradleはマルチモジュール構成です。

build.gradle
settings.gradle
database/
    Dockerfile
todo/
    Dockerfile
    build.gradle

まずはデータベースサーバをビルドするDockerfileから

おなじみFROMとMAINTAINERは前回参照

FROM ubuntu
MAINTAINER foo@example.com

PostgreSQLのインストール

RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
RUN apt-get update && \
    apt-get install -y \
        python-software-properties \
        software-properties-common \
        postgresql-9.4 \
        postgresql-client-9.4 \
        postgresql-contrib-9.4

こちらもapt-getでごりごりインストールするだけです。

ここ以降のRUNはpostgresというユーザで行いますよという宣言

USER postgres

これはapt-getでパッケージからPostgreSQLをインストールすると作られているユーザです。

そしてユーザとデータベースの作成

RUN /etc/init.d/postgresql start &&\
 psql --command "CREATE USER todo_user WITH SUPERUSER PASSWORD 'todo_password';" &&\
 createdb -O todo_user todo_database

todo_userというユーザをtodo_passwordというパスワードで作成し、さらにtodo_databaseというデータベースを作成し、オーナーとしてtodo_userを指定しています。

ネットワーク系の設定

RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.4/main/pg_hba.conf
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.4/main/postgresql.conf

echoで設定ファイルを書き込んでいます。

EXPOSEでポートをコンテナ間ネットワークに公開

EXPOSE 5432

DDLの追加と実行

ADD schema.sql ./schema.sql
RUN /etc/init.d/postgresql start &&\
psql todo_database < ./schema.sql

最後にこのコンテナイメージが起動した時に実行されるコマンド

CMD ["/usr/lib/postgresql/9.4/bin/postgres", \
"-D", "/var/lib/postgresql/9.4/main", \
"-c", "config_file=/etc/postgresql/9.4/main/postgresql.conf"]

Dockerfileの記述はこれで終了です。


ビルド(databaseディレクトリでの作業)

docker build -t todo_database ./

まずは単独で実行

docker run -p 5432:5432 -name todo-db

単独で実行していても面白くもなんとものでアプリケーションとつなげてみます。

が、ここでアプリケーションから外部データベースに接続するために前回のDockerfileを少し書き換えます。

具体的には最後の行のCMD部分

CMD ["java", "-jar", "./todo.jar"]

を以下のように

CMD ["java", "-jar", "./todo.jar",\
 "--spring.datasource.initialize=false",\
 "--spring.datasource.driverClassName=org.postgresql.Driver",\
 "--spring.datasource.url=jdbc:postgresql://${DB_PORT_5432_TCP_ADDR}:${DB_PORT_5432_TCP_PORT}/todo_database",\
 "--spring.datasource.username=todo_user",\
 "--spring.datasource.password=todo_password"]

この部分にはかなりSpring Bootの良さが詰まっていてですね。
どういうことかというと、Spring Bootでアプリケケーションを作っていれば「起動時のコマンドラインでコンフィギュレーションを上書きする機能が特になにも意識しなくてもアプリケーションに備わっている」ということです。これを一から実現するのはそこそこ面倒なコードを書かなければならないので大変助かります。

もう1点、真ん中あたりのこの部分。なんだか変な環境変数みたいなものでjdbcのデータベース接続先URLを記述していますね。これは後述します。

"--spring.datasource.url=jdbc:postgresql://${DB_PORT_5432_TCP_ADDR}:${DB_PORT_5432_TCP_PORT}/todo_database",\

データベース・サーバとつながるように起動してみます。
(todoディレクトリでの作業です。)

データベース用コンテナのビルド

docker build -t todo_database_image ../database

アプリケーションの実行可能Jarファイル生成

../gradlew :todo:build

アプリケーション用コンテナのビルド

docker build -t todo_application_image ./

データベースサーバの起動

docker run -d --name todo_db_container todo_database_image

アプリケーションサーバの起動

docker run -p 8080:8080 --name todo_app_container --link=todo_db_container:db todo_application_image

この–linkに指定しているtodo_db_container:dbが肝です。
todo_db_containerという名前をつけて起動したデータベースサーバに、その名前でリンクするのですが、さらにそれを:dbとして”db”という別名を与えています。

そしてこの名前が、後述すると言っていたアプリケーションのdockerファイルに記述した変な環境変数の先頭となります。5432はデータベースサーバのDockerfileでEXPOSEしたPORTです。

"--spring.datasource.url=jdbc:postgresql://${DB_PORT_5432_TCP_ADDR}:${DB_PORT_5432_TCP_PORT}/todo_database",\

つまりリンク先のIPアドレスとポートを知るには以下の名前を環境変数として使えば良いことになります。

${リンクした時に指定した別名_PORT_リンクするコンテナがEXPOSEしているポート_TCP_ADDR}
${リンクした時に指定した別名_PORT_リンクするコンテナがEXPOSEしているポート_TCP_PORT}

はい。非常にわかりにくいですね。
何がわかりにくいかっていうと、イメージ名とコンテナ名とか参照する名前の多さではないかと思います。

一応、私は以下のように覚えていますがすぐ忘れます…

docker build -t イメージ名 Dockerfileの置き場所
docker run --name コンテナ名 runする対象イメージ名
docker run --name コンテナ名 --link 接続先コンテナ名:別名 runする対象イメージ名

あとは上記をシェルスクリプト化すれば毎回同じデータベースの状態で、
アプリケーション一式の確認環境が起動するようになります。


動作するサンプルが以下にありますのでご参考まで。

https://github.com/haljik/todo-example/tree/with-postgre-sql

Boot2DockerなどDockerデーモンが起動している状態で、
todo/standaloneWithPostgreSQL.shを実行するとPostgreSQLとアプリケーションが起動します。


次回は最近でたDocker1.5の変更点を追いつつ、figというツールを使って複数コンテナのリンクをもう少しわかりやすく管理する方法の解説をします。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中