dockerで初回起動時のみ特定の処理を行うヘルパースクリプト（docker-entrypoint.sh）

# はじめに

`docker run` とか `docker-compose up -d` とかの **初回起動時のみ** 処理したいことがある時はどうすればいいんだろう？
`docker restart` とか `docker-compose restart` とか `systemctl restart docker` の時には動いてほしくないんだ。

やってみた結果 mariadb + zabbix で初回起動時のみ、構成用の .sql を流し込む。という動作ができるようになりました。
[日本人にやさしそうなMIRACLE ZBXをdocker-composeでまとめた](https://qiita.com/bashaway/items/907e81a153885cdf3845#zabbix%E3%82%B5%E3%83%BC%E3%83%90-%E5%85%BC-web%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A)


# 経緯

zabbix の docker-compose.yml を作成しようとしたら、 dnf でインストールした後に特定ディレクトリにある .sql を MariaDB に食わせる必要がありました。
ビルド時には DB が起動してないので、流し込めない。
MariaDB の初回起動時の .sql ファイル流し込みだと、現時点の .sql は準備できるけど、zabbix のアップデートに追従できない。
rc.local 起動時のスクリプトで実行しようとしたら、ホスト機やサービスの再起動のたびに動いてしまう。

どうしよう。


# 動作の概要

ということで、MariaDB とか mysql とか postgresql とかの公式イメージでやってる、初回起動時の .sql 流し込みのアイデアを拝借しました。

考え方は以下のとおり

1. Dockerfileの `ENTRYPOINT` で `docker-entrypoint.sh` というヘルパースクリプトを呼び出す。
1. Dockerfileの `CMD` に `"/sbin/init"` を書き込んでヘルパースクリプトに引数として渡す。
1. ヘルパースクリプトは判別用のディレクトリの有無を確認し、
 1. ディレクトリが無い場合 -> 作成する
 1. ディレクトリが有る場合 -> なにもしない
1. ヘルパースクリプトは前段の処理のあと、自身を起動した際の引数の文字列（= `/sbin/init`）を実行する

これで、コンテナを初回起動した場合のみ、特定の処理を挟み込むことができる。

# 検証

ホントにできるか検証します。

## ファイルの準備

以下のようにファイルを準備します。

```
sample
|-- Dockerfile
|-- docker-compose.yml
`-- docker-entrypoint.sh
```

```dockerfile:/root/sample/Dockerfile
FROM centos:centos8
COPY docker-entrypoint.sh /tmp
ENTRYPOINT ["/tmp/docker-entrypoint.sh"]
CMD [ "/sbin/init" ]
```

```yml:/root/sample/docker-compose.yml
version: '3'

services:
  sample:
    build: ./
    container_name: sample
    hostname: sample
    restart: always
    cap_add:
      - SYS_ADMIN
    security_opt:
      - seccomp:unconfined
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
    environment:
      TZ: 'Asia/Tokyo'
```



```sh:/root/sample/docker-entrypoint.sh
#!/bin/sh

if [ ! -d "/tmp/check" ]; then
  mkdir /tmp/check
  echo `date` > /tmp/check/first_`date +%Y%m%d_%H%M%S`
else
  echo `date` > /tmp/check/second_`date +%Y%m%d_%H%M%S`
fi

exec "$@"
```

## 動作確認

ビルド＆初回起動 -> コンテナリスタート -> コンテナリスタート
としたとき  `docker-entrypoint.sh` で作成されるダミーファイルの状況を追ってみます。

```bash

# docker-compose up --build -d ; docker exec -it sample ls -1 /tmp/check
Creating network "sample_default" with the default driver
Building sample
Step 1/4 : FROM centos:centos8
 ---> 470671670cac
Step 2/4 : COPY docker-entrypoint.sh /tmp
 ---> 65969481a77a
Step 3/4 : ENTRYPOINT ["/tmp/docker-entrypoint.sh"]
 ---> Running in faa635fbdd2c
Removing intermediate container faa635fbdd2c
 ---> f0fd6de7dd9f
Step 4/4 : CMD [ "/sbin/init" ]
 ---> Running in 4d8a074a1da4
Removing intermediate container 4d8a074a1da4
 ---> cae2d8667ff6
Successfully built cae2d8667ff6
Successfully tagged sample_sample:latest
Creating sample ... done
first_20200220_213008
（↑初回起動時処理のファイルが作成されている）

# docker-compose restart ; docker exec -it sample ls -1 /tmp/check
Restarting sample ... done
first_20200220_213008
second_20200220_213025
（↑初回起動時ではない処理のファイルが作成されている）

# docker-compose restart ; docker exec -it sample ls -1 /tmp/check
Restarting sample ... done
first_20200220_213008
second_20200220_213025
second_20200220_213042
（↑初回起動時ではない処理のファイルが作成されている）
```

ということで、無事にそれっぽいことができました。
ここに zabbix をインストールしたときに作成される .sql を流し込めば、全自動で処理ができそうです。

もちろん、 `/sbin/init` の動作もきちんとできていました。

```
# docker exec -it sample bash
（これでコンテナにログイン）

[root@sample /]# systemctl status
● sample
    State: running
     Jobs: 0 queued
   Failed: 0 units
    Since: Thu 2020-02-20 21:30:58 JST; 3min 14s ago
   CGroup: /docker/f8a80d798e046e22c343f95b4fc9c9b8d623d0ef97ba39becfdc1debe21a0b35
           tq83 bash
           tq96 systemctl status
           tq97 less
           tqinit.scope
           x mq1 /sbin/init
           mqsystem.slice
             tqsystemd-journald.service
             x mq22 /usr/lib/systemd/systemd-journald
             mqdbus.service
               mq36 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
[root@sample /]#
```

うん、大丈夫そう。

# さいごに
ん、よく考えたら、同じようなことを rc.local でやればよかったのかも。
Dockerfile のことを少し知れたので、勉強にはなりましたが。

"ヘルパースクリプト"という言葉自体、あまり使われていないのですね
実は全然イケてない方法だったり？使い方まちがってたりして？と、ちょっとビクビクします
もしくは当たり前すぎんだよ！てな感じでしょうか。。。

[![Screenshot from Gyazo](https://gyazo.com/2c265f1bfb8afeee59d80099e83ac91b/raw)](https://gyazo.com/2c265f1bfb8afeee59d80099e83ac91b)


# 参考
http://docs.docker.jp/engine/reference/run.html
https://github.com/docker-library/mysql/tree/master/8.0