EmbulkでPostgreSQLからS3にデータを転送してみた

タイトルの通りの内容をただやってみたという内容の記事です。

# インストール

## Java8インストール

最初にJavaをインストールします。

Embulkは公式サイトに以下のように書いてありました。
> Embulk v0.9 and v0.10 run on Java 8. Java 9 is not supported officially.

手元の環境にJava 11が入っていたので、最初手抜きでJava 11のままembulkコマンドを試してみたら以下のようなエラーになりました。

```plain
[ERROR] Embulk does not support Java 11 yet.
```

なので、素直にJava 8を別途インストールします。Ubuntuであれば以下のようなコマンドだと思います。

```shell
$ sudo apt install -y openjdk-8-jdk
```

私はデフォルトのJavaを切り替えたくなかったので、embulkコマンドにだけJava 8が適用されるように設定しました。ここでは詳細は省略しますが、ともかくJava 8がインストールできたものとします。

## Embulkインストール

curlコマンドでファイルをダウンロードして実行権限を付与するのみです。

```shell
$ curl --create-dirs -o ~/.embulk/bin/embulk -L "https://dl.embulk.org/embulk-latest.jar"
$ chmod +x ~/.embulk/bin/embulk
```

これをPATHに通しておきます。

インストール完了です。

```shell
$ embulk --version
embulk 0.9.23
```

## EmbulkはBashスクリプトに偽装したjarファイル

ダウンロードしたファイルはURLを見ると一見jarファイルですが、先頭にBashスクリプトが書かれており、スクリプトとして直接実行できます。

jarファイルはzipファイルであり、zipファイルはその構造上zipに無関係のデータを埋め込むことができるため、スクリプトを埋め込むことでBashスクリプトに偽装しています。BashだけでなくWindowsのバッチファイルも書かれているようです。Linuxで実行権限を付与すればBashスクリプトになるし、Windowsで拡張子を `.bat` にすればバッチファイルになります。

つまり、jarファイル、Bashスクリプトファイル、Windowsバッチファイルの3役を1ファイルで兼務しています。

## プラグインインストール

Embulkのプラグインをインストールします。Embulkはfluentdと同じく、プラグインをインストールすることでデータのさまざまな入力元や出力先に対応させられるようです。

PostgreSQLからの入力とS3への出力のためのプラグインをインストールします。

```shell
$ embulk gem install embulk-input-postgresql
$ embulk gem install embulk-output-s3
```

`gem`でプラグインをインストールするあたり、fluentdに似てるし、Rubyでできてるんだなって思ったけど、あれ？最初にJavaが必要という話じゃなかったっけ？ってなって混乱しました。なんとJRubyで動いているらしい。

# PostgreSQL準備

PostgreSQLに以下のようなサンプルデータを用意しました。

```sql
create table sample1 (
  id integer,
  timestamp1 timestamp,
  timestamp2 timestamp with time zone,
  msg text
);
insert into sample1 (id, timestamp1, timestamp2, msg) values (1, '2021-11-14T16:00:00', '2021-11-14T16:00:00', 'msg 1');
insert into sample1 (id, timestamp1, timestamp2, msg) values (2, '2021-11-15T16:00:00', '2021-11-15T16:00:00', 'msg 2');
```

作成結果

```plain
postgres=> \d sample1
                          Table "public.sample1"
   Column   |            Type             | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+---------
 id         | integer                     |           |          |
 timestamp1 | timestamp without time zone |           |          |
 timestamp2 | timestamp with time zone    |           |          |
 msg        | text                        |           |          |

postgres=> select * from sample1;
 id |     timestamp1      |       timestamp2       |  msg
----+---------------------+------------------------+-------
  1 | 2021-11-14 16:00:00 | 2021-11-14 16:00:00+00 | msg 1
  2 | 2021-11-15 16:00:00 | 2021-11-15 16:00:00+00 | msg 2
(2 rows)
```

# 設定ファイル作成

EmbulkはYAMLで設定ファイルを手で書いたら、`embulk guess`コマンドで設定ファイルを事前に生成しなおし、実行は`embulk run`コマンドという流れです。

- `seed.yml` を手で書く
- `$ embulk guess seed.yml -o config.yml`
- `$ embulk run config.yml`

ただ、今回の使い方では `seed.yml` と `config.yml` の内容にまったく差異がありませんでした。

ひとまず設定ファイルを書きます。PostgreSQLへの接続情報と、S3への接続情報、出力のフォーマットなどを書きます。

```seed.yml
in:
  type: postgresql
  host: xxxxxxxx.xxxxxxxx.ap-northeast-1.rds.amazonaws.com
  user: postgres
  password: "xxxxxxxx"
  database: postgres
  table: sample1
out:
  type: s3
  endpoint: s3-ap-northeast-1.amazonaws.com
  access_key_id: XXXXXXXX
  secret_access_key: xxxxxxxx
  bucket: xxxxxxxx
  path_prefix: embulk
  file_ext: .csv.gz
  sequence_format: .%03d.%02d
  formatter:
    type: csv
    delimiter: ","
    newline: LF
    charset: UTF-8
    default_timezone: "Asia/Tokyo"
  encoders:
  - type: gzip
```

`embulk guess` コマンドで設定ファイルを生成

```shell
$ embulk guess seed.yml -o config.yml
```

以下のコマンドで見てみたら、まったく差異がなかったです。

```shell
$ diff <(yq . < seed.yml) <(yq . < config.yml) 
```

# 実行

上述の通り`embulk guess`コマンドに意味がなかったので、以下のどちらのコマンドで同じように動きました。

```shell
$ embulk run seed.yml
```

```shell
$ embulk run config.yml
```

S3バケットに `embulk.000.00.csv.gz` という名前のファイルができます。中身は以下のようなCSVファイルでした。

```shell
$ aws s3 cp s3://xxxxxxxx/embulk.000.00.csv.gz - | gunzip -c
id,timestamp1,timestamp2,msg
1,2021-11-14 16:00:00.000000 +0900,2021-11-15 01:00:00.000000 +0900,msg 1
2,2021-11-15 16:00:00.000000 +0900,2021-11-16 01:00:00.000000 +0900,msg 2
```

以上。