Edited at

docker-composeでバッチを走らせる with Javaバッチ

More than 1 year has passed since last update.

Dockerはデーモンプロセスを立ち上げるだけが能ではありません。一度だけ実行するバッチも立ち上げるのも全然アリです。

ただ、 docker コマンドでコンテナを起動するのは辛いですね。バッチでも docker-compose を使いましょう。

今回はバッチとしてJavaで書かれた単純なCLIプログラムを動かしたいと思います。


ソース

https://github.com/knjname/2018-02_DockerComposeBatchExample

ここからDLできます。


構成の紹介


Java アプリ部分

適当にSpring bootで作った。下記のようなものを実行する。バッチの引数や環境変数をプリントするだけのものです。

    // Spring boot で CommandLineRunner 噛ませた時のmain関数みたいなやつ

@Override
public void run(String[] args) throws Exception {
System.out.println("Batch run!!!");
System.out.println(" args: " + String.join(", ", args));
System.out.println(" env: " + System.getenv("MY_ENV_VALUE"));
}

どうでもいいですが、Javaでどうでもいいものを作る時はSpring Bootを使いましょう。


Dockerfile 部分

前段(... AS builder)でビルドしたJARを後半のコンテナに取り込み、 java -jar ビルドしたJAR を動かす内容です。

FROM library/openjdk:9.0.1-11-jre-slim AS builder

ADD . /app
WORKDIR /app
RUN ./gradlew bootRepackage
RUN mv /app/build/libs/*.jar /app.jar

FROM library/openjdk:9.0.1-11-jre-slim
COPY --from=builder /app.jar /app.jar
ENTRYPOINT [ "java", "-jar", "/app.jar" ]

最後がENTRYPOINTになっているので、バッチに引数を渡すことができます。


docker-compose.yml 部分

とりあえず MY_ENV_VALUE としてバッチへの環境変数を与えています。実際はデータベースの接続先とかを与えることになると思います。ほか、ボリューム指定としてバッチのファイル書き出しディレクトリとかもここで指定ですね。


docker-compose.yml

version: "3.4"

services:

batch:
# 上記のDockerを docker build . -t myjavabatch でビルドした場合は image: myjavabatch とかになると思います
build: ./
environment:
MY_ENV_VALUE: "foobarbaz"

# restart: always とか要らない
# ports: も要らない
# network_mode: "host" は指定してもデーモン類と違って特に問題ないはず。
# DockerのイメージはWebなどと兼用せず、バッチ専用のものを作ったほうがいいです。



バッチの起動方法を探ってみる

いつものように、下記のように起動してはいけません。これではサービス(デーモン)の起動になってしまいます。

$ docker-compose up -d

バッチのような起動したい時に起動する、使い捨てのコンテナは下記のように --rm をつけて起動するのが基本です。

$ docker-compose run --rm batch バッチの引数

ついでにいうと、上記ではTTYをとれない場合(crontabからの実行など)、うまくいきません。 -T を指定してあげましょう。

$ docker-compose run --rm -T batch バッチの引数

しかし、まだこれでも罠があります。

docker-compose run は今のところ(2018/02/06時点)、コンテナ名を ベースの名前_run_1 というように連番で採番しますが、 _1 が被って起動できないことがあります。

コンテナ名が被らないように名前を乱数で明示的につけてあげましょう。

$ docker-compose run --rm -T --name `uuidgen` batch バッチの引数

これでバッチリです。



  • uuidgen コマンド を利用。

  • 敢えて同一の種類のバッチの名前を被せることで、二重起動を防ぐとかもできそうですが、ここでは触れません。


ラッパシェルもほしいよね

上記のコマンドのラッパシェルも作ってあげましょう。


wrapper.sh

#!/bin/bash

docker-compose run --rm -T --name `uuidgen` batch "$@"


ラッパシェル経由でバッチを呼ばれば、バッチリです。 :smile_cat:

$ ./wrapper.sh my java batch is forever!!

さらにcronで定期実行したい人は crontab -e で下記のようなcronエントリを定義してあげましょう。


crontab

# /dev/null に捨てるのはやめましょう。最近のOSの場合は systemd-cat にあげたりしましょう。

0 * * * * cd /my/path/to/project && ./wrapper.sh my java batch is forever!!! 2>&1 | systemd-cat


おわりに

docker-compose でバッチ実行も別に特殊なことではありません。(ではないと信じたい)

Let's challenge!!