Dockerはデーモンプロセスを立ち上げるだけが能ではありません。一度だけ実行するバッチも立ち上げるのも全然アリです。
ただ、 docker
コマンドでコンテナを起動するのは辛いですね。バッチでも docker-compose
を使いましょう。
今回はバッチとしてJavaで書かれた単純なCLIプログラムを動かしたいと思います。
ソース
ここから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
としてバッチへの環境変数を与えています。実際はデータベースの接続先とかを与えることになると思います。ほか、ボリューム指定としてバッチのファイル書き出しディレクトリとかもここで指定ですね。
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
コマンド を利用。 - 敢えて同一の種類のバッチの名前を被せることで、二重起動を防ぐとかもできそうですが、ここでは触れません。
ラッパシェルもほしいよね
上記のコマンドのラッパシェルも作ってあげましょう。
#!/bin/bash
docker-compose run --rm -T --name `uuidgen` batch "$@"
ラッパシェル経由でバッチを呼ばれば、バッチリです。
$ ./wrapper.sh my java batch is forever!!
さらにcronで定期実行したい人は crontab -e
で下記のようなcronエントリを定義してあげましょう。
# /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!!