Docker で Embulk を動かしてファイル変換

Docker コンテナで Embulk を動かせるようにし、テストとして csv ファイルを orc ファイルに変換してみる

## Embulk ?

バッチ的にファイルやデータベースからデータを吸い出し、別のストレージやデータベースにロードできるツール
詳細は[公式ドキュメント](https://www.embulk.org/)か、下記開発者の方のブログを参照ください

- [並列データ転送ツール『Embulk』リリース！](http://frsyuki.hatenablog.com/entry/2015/02/16/080150)

## 環境情報

- Mac OS X Catalina 10.15.7
- Docker version: 20.10.0
- orc-tools version: ORC 1.6.6
    - 最後に orc ファイルの中身を見る時に使用
    - [Homebrew でインストール可能](https://formulae.brew.sh/formula/orc-tools)

## Docker コンテナ内で、手動で Embulk をインストール

とりあえずコンテナ立ち上げて、手動で入れてみる

```bash
docker run -it --rm java:8 bash
```

[公式ドキュメント](https://www.embulk.org/)にあるやり方で embulk をインストール

- 後々イメージ化することを考えると`$HOME` 配下にパスを通すのが難しいので、実行ファイルのインストール場所は、`/usr/local/bin`にする
- なので、公式の手順内の `~/.embulk/bin/embulk` の箇所は、 `/usr/local/bin/embulk`に書き換える
- 手順内の PATH の更新の箇所も行わない

```bash
curl --create-dirs -o /usr/local/bin/embulk -L "https://dl.embulk.org/embulk-latest.jar"
chmod +x /usr/local/bin/embulk
```

公式ドキュメントにあるクイックスタートに沿って実際に動かしてみる

```bash
embulk example ./try1
embulk guess ./try1/seed.yml -o config.yml
embulk run config.yml
```

`embulk run` の結果、以下のような結果が標準出力されればOK

```bash
1,32864,2015-01-27 19:23:49,20150127,embulk
2,14824,2015-01-27 19:01:23,20150127,embulk jruby
3,27559,2015-01-28 02:20:02,20150128,Embulk "csv" parser plugin
4,11270,2015-01-29 11:54:36,20150129,
```

↑ で何をやったのか

- `try1/csv/sample_01.csv.gz` の中身を、stdout (標準出力) に出力した

config.yml の中身

```bash
root@7e9764e79b83:/# cat config.yml
in:
  type: file
  path_prefix: /./try1/csv/sample_
  decoders:
  - {type: gzip}
  parser:
    charset: UTF-8
    newline: LF
    type: csv
    delimiter: ','
    quote: '"'
    escape: '"'
    null_string: 'NULL'
    trim_if_not_quoted: false
    skip_header_lines: 1
    allow_extra_columns: false
    allow_optional_columns: false
    columns:
    - {name: id, type: long}
    - {name: account, type: long}
    - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S'}
    - {name: purchase, type: timestamp, format: '%Y%m%d'}
    - {name: comment, type: string}
out: {type: stdout}
```

`try1/csv/sample_01.csv.gz` の中身確認

```
root@7e9764e79b83:/# zcat try1/csv/sample_*
id,account,time,purchase,comment
1,32864,2015-01-27 19:23:49,20150127,embulk
2,14824,2015-01-27 19:01:23,20150127,embulk jruby
3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin"
4,11270,2015-01-29 11:54:36,20150129,NULL
```

## Dockerfile作成 & コンテナ起動

動作確認が終わったので、上記手動手順を Dockerfile 化する

- ENTRYPOINT で、`/usr/local/vin/embulk` を指定したかったが、ENTRYPOINT では jar を直接的に起動できないので、`java -jar`で起動

```Dockerfile:Dockerfile
FROM java:8

# Install embulk
RUN curl --create-dirs -o /usr/local/bin/embulk -L "https://dl.embulk.org/embulk-latest.jar" &&\
    chmod +x /usr/local/bin/embulk

ENTRYPOINT ["java", "-jar", "/usr/local/bin/embulk"]
```

build

```bash
docker build -t embulk .
```

確認

```bash
docker run -it --rm embulk:latest embulk --version
Embulk v0.9.23
```

## コンテナ内の embulk で csv ファイルを orc 変換

せっかくなので、実際に embulk を使用して csv ファイルを orc ファイルに変換してみる

Dockerファイルを修正

- 先程の Docker ファイルに以下を追加
    - [embulk-output-orc プラグイン](https://github.com/yuokada/embulk-output-orc) のインストール
        - プラグインは、`embulk gem install ${パッケージ名}`のようにしてインストールする
    - WORKDIR を /work に設定

```Dockerfile:Dockerfile
FROM java:8

# Install embulk
RUN curl --create-dirs -o /usr/local/bin/embulk -L "https://dl.embulk.org/embulk-latest.jar" &&\
    chmod +x /usr/local/bin/embulk

RUN embulk gem install embulk-output-orc

WORKDIR /work

ENTRYPOINT ["java", "-jar", "/usr/local/bin/embulk"]
```

build

```bash
docker build -t embulk .
```

作業ディレクトリの構成

```bash
.
├── Dockerfile
└── work
    ├── config.yml
    ├── csv_inputs
    │   └── sample_01.csv.gz
    └── orc_outputs
```

動作イメージ

- `work/config.yml` に書いてある設定に沿って、`./work/csv_inputs/sample_01.csv.gz`を orc　変換して、`./work/orc_outputs/` 配下に出力

`work/config.yml` ファイル中身

- 先程のクイックスタートでできた `config.yml` 内の `out` を `type: orc` に書き換えている
- 今回はデータ量が少なく、空ファイルが出来てしまうのを防ぐため、`exec` の内で、`min_output_tasks` を1にしてファイルを分散させないようする

```yml:config.yml
exec:
  min_output_tasks: 1
in:
  type: file
  path_prefix: /work/csv_inputs/sample_
  decoders:
  - {type: gzip}
  parser:
    charset: UTF-8
    newline: LF
    type: csv
    delimiter: ','
    quote: '"'
    escape: '"'
    null_string: 'NULL'
    trim_if_not_quoted: false
    skip_header_lines: 1
    allow_extra_columns: false
    allow_optional_columns: false
    columns:
    - {name: id, type: long}
    - {name: account, type: long}
    - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S'}
    - {name: purchase, type: timestamp, format: '%Y%m%d'}
    - {name: comment, type: string}
out: 
  type: orc
  path_prefix: /work/orc_outputs/sample
  compression_kind: ZLIB
  overwrite:   true
```

input となる csv ファイルの中身も、クイックスタートの時と同じ

```csv
# Mac では、zcat は gzcat なので注意
gzcat work/csv_inputs/sample_01.csv.gz

id,account,time,purchase,comment
1,32864,2015-01-27 19:23:49,20150127,embulk
2,14824,2015-01-27 19:01:23,20150127,embulk jruby
3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin"
4,11270,2015-01-29 11:54:36,20150129,NULL
```

実行

- コンテナ内の `WORKDIR (/work)` を、`./work`でマウントしている

```bash
docker run -it --rm -v $(pwd)/work:/work embulk:latest run config.yml
```

確認

- orc ファイルができている

```bash
ls work/orc_outputs

sample.000.orc
```

orc ファイルの中身確認

- [orc-tools](https://formulae.brew.sh/formula/orc-tools) で確認可能

```
orc-tools data work/orc_outputs/sample.000.orc

log4j:WARN No appenders could be found for logger (org.apache.hadoop.util.Shell).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (file:/usr/local/Cellar/orc-tools/1.6.6/libexec/orc-tools-1.6.6-uber.jar) to method sun.security.krb5.Config.getInstance()
WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Processing data file work/orc_outputs/sample.000.orc [length: 787]
{"id":1,"account":32864,"time":"2015-01-27 19:23:49.0","purchase":"2015-01-27 00:00:00.0","comment":"embulk"}
{"id":2,"account":14824,"time":"2015-01-27 19:01:23.0","purchase":"2015-01-27 00:00:00.0","comment":"embulk jruby"}
{"id":3,"account":27559,"time":"2015-01-28 02:20:02.0","purchase":"2015-01-28 00:00:00.0","comment":"Embulk \"csv\" parser plugin"}
{"id":4,"account":11270,"time":"2015-01-29 11:54:36.0","purchase":"2015-01-29 00:00:00.0","comment":null}
________________________________________________________________________________________________________________________

```

変換できている！

## 参考文献

- embulk 公式ドキュメント
  - https://www.embulk.org/
- [Embulkを使ってデータをMySQLに投入する環境をDockerで構築する](https://owky.hatenablog.com/entry/2019/02/13/075106)
- [Docker コンテナで Embulk を動かす](https://qiita.com/hoto17296/items/a7d69172050fa9aab045)
- [並列データ転送ツールembulkの実行環境(Docker)とサンプルを作ったので使い方とかをメモ](https://madogiwa0124.hatenablog.com/entry/2019/11/17/130158)