SnowflakeTech Blogツールデータ分析基盤 

SnowflakeにEmbulkでデータをETL(AWS S3編)

はいさーい!ちゅらデータでPHP書いているaipaです。
みなさまEmbulkというOSSはご存知でしょうか。小さいデータから巨大なデータまでいろんなデータソースへバルク処理してくれるぱっとみシンプルだけど知れば知るほど多機能なすごいやつです。

今回はEmbulkを使って、AWS S3からSnowflakeへデータを保存するまでの手順を紹介します。Embulkはデータソースごとにpluginが用意されており、OSSで用意されたinとoutのpluginを組み合わせることでETLを簡単に実現することができます。

S3に対応したinput plugin「embulk-input-s3」はすでに知らていると思いますが、なんと!snowflakeに対応したplugin、「embulk-output-snowflake」があります!

これなら簡単に試すことが出来ますね!!
そんな風に考えてた時期が僕にもありました


はじめに

  • Embulkを使って、S3からSnowflakeへデータを飛ばそう
  • Dockerを使ってEmbulkを実行する

 

この記事を作成するにあたって使用した環境について

$ sw_vers
ProductName:   Mac OS X
ProductVersion: 10.15.6
BuildVersion:  19G2021

$ docker --version
Docker version 19.03.12, build 48a66213fe

認証情報の用意(AWS)

S3へアクセス可能なIAMユーザーの用意とアクセスキーを発行します。

今回の記事では省略します。
https://qiita.com/rh_/items/24ed1cebbdc3a731aabb

認証情報、DB等の用意(Snowflake)

Snowflakeへの登録手順、今回書き込むリソースの用意などはこの記事では省略します。
ユーザー名、パスワード、アカウントURL、作成するDB、WH名などをメモしておいてください。

 

Embulk実行環境の用意

今回は、Dockerを使ってEmbulkを実行できる環境を用意します。
めんどくさそう?大丈夫、すぐできます。

沖縄のDockerおじさんとのことhotoさんのお力をお借りします。
https://qiita.com/hoto17296/items/a7d69172050fa9aab045

Dockerおじさんが用意したDockerfileを少し修正します。
今回S3 → Snowflakeを行うにあたり利用するデータをpluginを使って用意します。 

 1. embulk-input-randomjで用意したdummyデータをembulk-output-s3でS3へ保存する
 2. 1で保存したデータをembulk-input-s3で取得し、embulk-output-snowflakeでSnowflakeへ保存する

そのため、利用するpluginをDockerfileへ追記しておきます。

FROM openjdk:8-jre-alpine

# Embulk 本体をインストールする
RUN wget -q https://dl.embulk.org/embulk-latest.jar -O /bin/embulk \
  && chmod +x /bin/embulk

# 使いたいプラグインを入れる
RUN apk add --no-cache libc6-compat \
  && embulk gem install embulk-input-randomj embulk-input-s3 \
                        embulk-output-s3 embulk-output-snowflake

WORKDIR /work

ENTRYPOINT ["java", "-jar", "/bin/embulk"]


Dockerfileが用意できたら、イメージをビルドします。

$ docker build -t embulk .


Dockerfileが用意できたら、イメージをビルドします。

$ docker run --rm -it -v $(pwd):/work embulk --help
Embulk v0.9.23
Usage: embulk [-vm-options] <command> [--options]
Commands:
&nbsp;&nbsp; mkbundle&nbsp;&nbsp; <directory>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # create a new plugin bundle environment.
&nbsp;&nbsp; bundle&nbsp;&nbsp;&nbsp;&nbsp; [directory]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # update a plugin bundle environment.
&nbsp;&nbsp; run&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <config.yml>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # run a bulk load transaction.
&nbsp;&nbsp; cleanup&nbsp;&nbsp;&nbsp; <config.yml>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # cleanup resume state.
&nbsp;&nbsp; preview&nbsp;&nbsp;&nbsp; <config.yml>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # dry-run the bulk load without output and show preview.
&nbsp;&nbsp; guess&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <partial-config.yml> -o <output.yml>&nbsp;&nbsp;&nbsp; # guess missing parameters to create a complete configuration file.
&nbsp;&nbsp; gem&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <install | list | help>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # install a plugin or show installed plugins.
&nbsp;&nbsp; new&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <category> <name>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # generates new plugin template
&nbsp;&nbsp; migrate&nbsp;&nbsp;&nbsp; <path>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # modify plugin code to use the latest Embulk plugin API
&nbsp;&nbsp; example&nbsp;&nbsp;&nbsp; [path]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # creates an example config file and csv file to try embulk.
&nbsp;&nbsp; selfupdate [version]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # upgrades embulk to the latest released version or to the specified version.

VM options:
&nbsp;&nbsp; -E...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Run an external script to configure environment variables in JVM
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (Operations not just setting envs are not recommended nor guaranteed.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Expect side effects by running your external script at your own risk.)
&nbsp;&nbsp; -J-O&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Disable JVM optimizations to speed up startup time (enabled by default if command is 'run')
&nbsp;&nbsp; -J+O&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enable JVM optimizations to speed up throughput
&nbsp;&nbsp; -J...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Set JVM options (use -J-help to see available options)
&nbsp;&nbsp; -R--dev&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Set JRuby to be in development mode

Use `<command> --help` to see description of the commands.

準備おkです!!!

 

データの用意

先程記載した手順に沿って、Embulkを実行していきます。
エディタを利用してdummyデータ作成用のconfig.ymlを作成します。

$ vim dummy_config.yml

in:
&nbsp; type: randomj
&nbsp; rows: 10000
&nbsp; threads: 4
&nbsp; primary_key: id
&nbsp; schema:
&nbsp; - {name: id, type: long}
&nbsp; - {name: name, type: string}
&nbsp; - {name: hash, type: string, length: 16}
&nbsp; - {name: hobby, type: string, null_rate: 1000}
&nbsp; - {name: price, type: long}
&nbsp; - {name: day, type: long, min_value: 1, max_value: 31}
&nbsp; - {name: average, type: double}
&nbsp; - {name: rate, type: double, min_value: -100, max_value: 100}
&nbsp; - {name: flag, type: boolean}
&nbsp; - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S'}
&nbsp; - {name: date, type: timestamp, format: '%Y-%m-%d', start_date: 20180101, end_date: 20201231}

out:
&nbsp; type: s3
&nbsp; file_ext: .csv
&nbsp; access_key_id: ${hogehoge}
&nbsp; secret_access_key: ${fugafuga}
&nbsp; bucket: ${bucket_name}
&nbsp; path_prefix: snowflake_test_
&nbsp; endpoint: ${endpoint}
&nbsp; formatter:
&nbsp;&nbsp;&nbsp; type: csv

※${}の箇所はご自分の環境や準備したリソースにあわせて修正してください。(以後、黄色のハイライト表示)

用意ができましたら、早速実行していきましょう。

$ docker run --rm -it -v $(pwd):/work embulk run dummy_config.yml
YYYY-MM-DD 04:57:17.864 +0000: Embulk v0.9.23
YYYY-MM-DD 04:57:19.419 +0000 [WARN] (main): DEPRECATION: JRuby org.jruby.embed.ScriptingContainer is directly injected.
YYYY-MM-DD 04:57:24.418 +0000 [INFO] (main): Gem's home and path are set by default: "/root/.embulk/lib/gems"
YYYY-MM-DD 04:57:25.617 +0000 [INFO] (main): Started Embulk v0.9.23
YYYY-MM-DD 04:57:25.837 +0000 [INFO] (0001:transaction): Loaded plugin embulk-input-randomj (0.5.1)
YYYY-MM-DD 04:57:25.884 +0000 [INFO] (0001:transaction): Loaded plugin embulk-output-s3 (1.5.0)
YYYY-MM-DD 04:57:25.942 +0000 [INFO] (0001:transaction): Using local thread executor with max_threads=12 / output tasks 8 = input tasks 4 * 2
YYYY-MM-DD 04:57:26.027 +0000 [INFO] (0001:transaction): {done:  0 / 4, running: 0}
YYYY-MM-DD 04:57:27.007 +0000 [INFO] (0016:task-0003): Writing S3 file 'snowflake_test_.006.00.csv'
YYYY-MM-DD 04:57:27.008 +0000 [INFO] (0014:task-0001): Writing S3 file 'snowflake_test_.002.00.csv'
YYYY-MM-DD 04:57:27.008 +0000 [INFO] (0013:task-0000): Writing S3 file 'snowflake_test_.000.00.csv'
YYYY-MM-DD 04:57:27.008 +0000 [INFO] (0015:task-0002): Writing S3 file 'snowflake_test_.004.00.csv'
YYYY-MM-DD 04:57:27.023 +0000 [INFO] (0013:task-0000): Writing S3 file 'snowflake_test_.001.00.csv'
YYYY-MM-DD 04:57:27.023 +0000 [INFO] (0015:task-0002): Writing S3 file 'snowflake_test_.005.00.csv'
YYYY-MM-DD 04:57:27.023 +0000 [INFO] (0016:task-0003): Writing S3 file 'snowflake_test_.007.00.csv'
YYYY-MM-DD 04:57:27.042 +0000 [INFO] (0014:task-0001): Writing S3 file 'snowflake_test_.003.00.csv'
YYYY-MM-DD 04:57:46.809 +0000 [INFO] (0001:transaction): {done:  2 / 4, running: 2}
YYYY-MM-DD 04:57:49.228 +0000 [INFO] (0001:transaction): {done:  4 / 4, running: 0}
YYYY-MM-DD 04:57:49.228 +0000 [INFO] (0001:transaction): {done:  4 / 4, running: 0}
YYYY-MM-DD 04:57:49.228 +0000 [INFO] (0001:transaction): {done:  4 / 4, running: 0}
YYYY-MM-DD 04:57:49.240 +0000 [INFO] (main): Committed.
YYYY-MM-DD 04:57:49.240 +0000 [INFO] (main): Next config diff: {"in":{},"out":{}}

うまくいったようです!それではブラウザでS3のBucketを確認してみましょう。

アップロードされていました!これでデータの準備も完了しました。

 

S3 → SnowflakeでETL

それではSnowflakeへデータをETLしていきたいと思います。

ETLを実行するためのconfig.ymlを用意していきましょう。
最初に細かく設定していくのも良いですが、先程用意したデータは列の数が多く手で用意するのは面倒なので、Embulkのguessオプションを使って少し楽をしたいと思います。

最低限の設定だけ記載しているseed.ymlを作成します。

$ vim seed.yml

in:
&nbsp; type: s3
&nbsp; bucket: ${bucket}
&nbsp; path_prefix: snowflake_test_
&nbsp; access_key_id: ${hogehoge}
&nbsp; secret_access_key: ${fugafuga}
out:
&nbsp; type: snowflake

guessオプションを実行して、inputの列やファイルフォーマットを自動推測します。

$ docker run --rm -it -v $(pwd):/work embulk guess seed.yml -o config.yml
YYYY-MM-DD 05:01:07.316 +0000: Embulk v0.9.23
YYYY-MM-DD 05:01:08.781 +0000 [WARN] (main): DEPRECATION: JRuby org.jruby.embed.ScriptingContainer is directly injected.
YYYY-MM-DD 05:01:14.140 +0000 [INFO] (main): Gem's home and path are set by default: "/root/.embulk/lib/gems"
YYYY-MM-DD 05:01:15.338 +0000 [INFO] (main): Started Embulk v0.9.23
YYYY-MM-DD 05:01:15.520 +0000 [INFO] (0001:guess): Loaded plugin embulk-input-s3 (0.5.0)
YYYY-MM-DD 05:01:16.373 +0000 [INFO] (0001:guess): Start listing file with prefix [snowflake_test_]
YYYY-MM-DD 05:01:18.860 +0000 [INFO] (0001:guess): Found total [8] files
YYYY-MM-DD 05:01:18.942 +0000 [INFO] (0001:guess): Try to read 32,768 bytes from input source
YYYY-MM-DD 05:01:19.842 +0000 [INFO] (0001:guess): Open S3Object with
bucket [${bucket}], key [snowflake_test_.000.00.csv], with size [1014702]
YYYY-MM-DD 05:01:20.062 +0000 [INFO] (0001:guess): Loaded plugin embulk (0.9.23)
YYYY-MM-DD 05:01:20.142 +0000 [INFO] (0001:guess): Loaded plugin embulk (0.9.23)
YYYY-MM-DD 05:01:20.224 +0000 [INFO] (0001:guess): Loaded plugin embulk (0.9.23)
YYYY-MM-DD 05:01:20.251 +0000 [INFO] (0001:guess): Loaded plugin embulk (0.9.23)

in:
&nbsp; type: s3
&nbsp; bucket: ${bucket}
&nbsp; path_prefix: snowflake_test_
&nbsp; access_key_id: ${hogehoge}
&nbsp; secret_access_key: ${fugafuga}
&nbsp; parser:
&nbsp;&nbsp;&nbsp; charset: UTF-8
&nbsp;&nbsp;&nbsp; newline: CRLF
&nbsp;&nbsp;&nbsp; type: csv
&nbsp;&nbsp;&nbsp; delimiter: ','
&nbsp;&nbsp;&nbsp; quote: '"'
&nbsp;&nbsp;&nbsp; escape: '"'
&nbsp;&nbsp;&nbsp; trim_if_not_quoted: false
&nbsp;&nbsp;&nbsp; skip_header_lines: 1
&nbsp;&nbsp;&nbsp; allow_extra_columns: false
&nbsp;&nbsp;&nbsp; allow_optional_columns: false
&nbsp;&nbsp;&nbsp; columns:
&nbsp;&nbsp;&nbsp; - {name: id, type: long}
&nbsp;&nbsp;&nbsp; - {name: name, type: string}
&nbsp;&nbsp;&nbsp; - {name: hash, type: string}
&nbsp;&nbsp;&nbsp; - {name: hobby, type: string}
&nbsp;&nbsp;&nbsp; - {name: price, type: long}
&nbsp;&nbsp;&nbsp; - {name: day, type: long}
&nbsp;&nbsp;&nbsp; - {name: average, type: double}
&nbsp;&nbsp;&nbsp; - {name: rate, type: double}
&nbsp;&nbsp;&nbsp; - {name: flag, type: boolean}
&nbsp;&nbsp;&nbsp; - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S.%N %z'}
&nbsp;&nbsp;&nbsp; - {name: date, type: timestamp, format: '%Y-%m-%d %H:%M:%S.%N %z'}
out: {type: snowflake}

Created 'config.yml' file.

できました!この量のテキストをちまちま入力するのは面倒だと思うのでとても便利ですね!

次にoutputのsnowflakeの設定を用意します。
項目の意味などはドキュメントを参考にしました。
https://github.com/trocco-io/embulk-output-snowflake

$ vim config.yml
in:
&nbsp; type: s3
&nbsp; bucket: ${bucket}
&nbsp; path_prefix: snowflake_test_
&nbsp; access_key_id: ${hogehoge}
&nbsp; secret_access_key: ${fugafuga}
&nbsp; parser:
&nbsp;&nbsp;&nbsp; charset: UTF-8
&nbsp;&nbsp;&nbsp; newline: CRLF
&nbsp;&nbsp;&nbsp; type: csv
&nbsp;&nbsp;&nbsp; delimiter: ','
&nbsp;&nbsp;&nbsp; quote: '"'
&nbsp;&nbsp;&nbsp; escape: '"'
&nbsp;&nbsp;&nbsp; trim_if_not_quoted: false
&nbsp;&nbsp;&nbsp; skip_header_lines: 1
&nbsp;&nbsp;&nbsp; allow_extra_columns: false
&nbsp;&nbsp;&nbsp; allow_optional_columns: false
&nbsp;&nbsp;&nbsp; columns:
&nbsp;&nbsp;&nbsp; - {name: id, type: long}
&nbsp;&nbsp;&nbsp; - {name: name, type: string}
&nbsp;&nbsp;&nbsp; - {name: hash, type: string}
&nbsp;&nbsp;&nbsp; - {name: hobby, type: string}
&nbsp;&nbsp;&nbsp; - {name: price, type: long}
&nbsp;&nbsp;&nbsp; - {name: day, type: long}
&nbsp;&nbsp;&nbsp; - {name: average, type: double}
&nbsp;&nbsp;&nbsp; - {name: rate, type: double}
&nbsp;&nbsp;&nbsp; - {name: flag, type: boolean}
&nbsp;&nbsp;&nbsp; - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S.%N %z'}
&nbsp;&nbsp;&nbsp; - {name: date, type: timestamp, format: '%Y-%m-%d %H:%M:%S.%N %z'}
out:
&nbsp; type: snowflake
&nbsp; host: ${host}
&nbsp; user: aipa
&nbsp; password: ${password}
&nbsp; warehouse: aipa_wh
&nbsp; database: aipa_database
&nbsp; schema: PUBLIC
&nbsp; table: aipa_test
&nbsp; mode: replace

はい。これで準備が整いました。いざETL!!ETL!!!ETL!!!!

$ docker run --rm -it -v $(pwd):/work embulk run config.yml
YYYY-MM-DD 05:13:58.757 +0000: Embulk v0.9.23
# 省略
org.embulk.exec.PartialExecutionException: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Field 'url' is required but not set
 at [Source: N/A; line: -1, column: -1]
       at org.embulk.exec.BulkLoader$LoaderState.buildPartialExecuteException(BulkLoader.java:340)

# 省略
              ... 5 more
Caused by: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Field 'url' is required but not set
 at [Source: N/A; line: -1, column: -1]

# 省略

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Field 'url' is required but not set
 at [Source: N/A; line: -1, column: -1]
       at org.embulk.config.TaskSerDe$TaskDeserializer.deserialize(TaskSerDe.java:180)
       at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3708)
       at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2005)
       at org.embulk.config.ModelManager.readObjectWithConfigSerDe(ModelManager.java:73)
       ... 29 more

Error: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Field 'url' is required but not set
 at [Source: N/A; line: -1, column: -1]

おや・・・・?
どうやらだめなようです。

「Error: org.embulk.config.ConfigException:」なので、入力必須の項目があるようです。
エラーにも記載がありますが、「url」が必要とのこと。はて???

https://github.com/trocco-io/embulk-output-snowflake
githubのREADME.mdを確認してもそのような項目は見当たりません。またソースコードをみてもそのような設定も見当たりませんでした。

はて???

腑に落ちない状況ですがとりあえずエラーに従うことにしました。

$ vim config.yml
in:
# inの設定省略
out:
  type: snowflake
  url: ${host}
  host: ${host}
  user: aipa
  password: ${password}
  warehouse: aipa_wh
  database: aipa_database
  schema: PUBLIC
  table: aipa_test
  mode: replace

6行目に「url」を追記しました。

もう一度試してみます。

$ docker run --rm -it -v $(pwd):/work embulk run config.yml
YYYY-MM-DD 05:13:58.757 +0000: Embulk v0.9.23
# 省略
org.embulk.exec.PartialExecutionException: java.lang.RuntimeException: java.lang.ClassNotFoundException: com.snowflake.client.jdbc.SnowflakeDriver

エラーが変わりましたが、またもやよくわからないエラーが。。。

どうやらdriverがないとのこと。ドライバー?????
もう一度githubのコードをよく読む僕、見つからない設定
https://github.com/trocco-io/embulk-output-snowflake/blob/master/build.gradle

そもそもビルドするときに依存で一緒にまとめられるようにビルド設定されてるようだけどなぜ????

ワケガワカラナイヨ

わけがわからないので、ぐーぐるのインデックスの海をさまよっているところ下記サイトを見つけました。
https://rdoc.info/gems/embulk-output-snowflake

RubyDocInfo???(Rubyはあまり詳しくない)、Rubyで実装されていたのか?など疑問に思っていたのも束の間


なんと!!「driver_path」と「url」の項目があります!!!
どうやらgem installでインストールされている「embulk-output-snowflake」は別物のようです(多分)
これに従い、設定の修正とdriverのインストールを行いました。

# https://repo1.maven.org/maven2/net/snowflake/snowflake-jdbc/ からほしいバージョンのリンクをコピー
$ wget https://repo1.maven.org/maven2/net/snowflake/snowflake-jdbc/3.9.2/snowflake-jdbc-3.9.2.jar

$ ls snowflake-jdbc-3.9.2.jar
snowflake-jdbc-3.9.2.jar

$ vim config.yml
in:
# inの設定省略
out:
&nbsp; type: snowflake
&nbsp; driver_path: /work/snowflake-jdbc-3.9.2.jar
&nbsp; url: jdbc:snowflake://${host}/?db=development&warehouse=aipa_wh&role=aipa_role&schema=PUBLIC
&nbsp; user: aipa
&nbsp; password: ${password}
&nbsp; warehouse: aipa_wh
&nbsp; database: aipa_database
&nbsp; schema: PUBLIC
&nbsp; table: aipa_test
&nbsp; mode: replace

ドライバーをダウンロードしてきて、config.ymlを修正しました。

こーんーどーこーそーーー!!

$ docker run --rm -it -v $(pwd):/work embulk run config.yml
YYYY-MM-DD 05:13:58.757 +0000: Embulk v0.9.23
# 省略
Error: java.lang.RuntimeException: net.snowflake.client.jdbc.SnowflakeSQLException: SQL compilation error:
Object does not exist, or operation cannot be performed.

orz

なーーんーーでーーやーーねーーーん

次回へ続く
冗談さておき、マジでもうよくわかりません。

ここまでの流れを整理しましょう。
●githubにある「embulk-output-snowflake」がgemでインストールされない
●infoにある「embulk-output-snowflake」がgemでインストールされるようだけどうまく動かない

もう記事書くのあきらめようかな?とか思っていましたが、githubのREADME.mdみていると
https://github.com/trocco-io/embulk-output-snowflake

>Build
>$ ./gradlew gem  # -t to watch change of files and rebuild continuously

ここに目がいきました。なるほど、ビルドして動かしてみるのもありだなと思いました。

RubyDoc.infoのpluginはソースがどこにあるのかわからないですが、githubのやつはcloneしてビルドすれば動かせるのではと思いました。ということで早速やっていきましょう。

$ git clone https://github.com/trocco-io/embulk-output-snowflake.git
$ cd embulk-output-snowflake/

Dockerおじさんに習って、ビルド環境もDockerコンテナでできないかなと探してたところ、下記記事が見つかりましたので参考にしました。
https://qiita.com/TakiTake@github/items/60b27581166efad24354

あとからわかったことですが、この記事で利用するDocker imageにはgitがインストールされていません。今回のビルドにはgitコマンドが必要なので、Docker imageを新規で作成する必要があります。
https://hub.docker.com/r/takitake/gradle-alpine/dockerfile

$ touch Dockerfile_gradle
$ vim Dockerfile_gradle
FROM adoptopenjdk/openjdk10:alpine
LABEL MAINTAINER="Takeshi Takizawa <takitake.create@gmail.com>"

RUN mkdir /usr/lib/gradle /app
ENV GRADLE_VERSION 4.10.2
ENV GRADLE_HOME /usr/lib/gradle/gradle-${GRADLE_VERSION}
ENV PATH ${PATH}:${GRADLE_HOME}/bin

WORKDIR /usr/lib/gradle
RUN  apk update \
  && apk add curl git
RUN set -x \
  && curl -L -O https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip \
  && unzip gradle-${GRADLE_VERSION}-bin.zip \
  && rm gradle-${GRADLE_VERSION}-bin.zip

そして、なぜかcurlコマンドが必要なのにインストールされておらず、docker build途中でエラーになるため、そいつもインストールするようにしておきます。

Dockerfileが用意できたら、イメージをビルドします。

$ docker build -t takitake/gradle-alpine2 . -f Dockerfile_gradle

イメージ用意できたら、プロジェクトをビルドしていきます。

$ docker run -v $(pwd):/app -w /app -u $UID:$GID takitake/gradle-alpine2 gradle wrapper
Picked up JAVA_TOOL_OPTIONS: -XX:+UseContainerSupport

Welcome to Gradle 4.10.2!

Here are the highlights of this release:
 - Incremental Java compilation by default
 - Periodic Gradle caches cleanup
 - Gradle Kotlin DSL 1.0-RC6
 - Nested included builds
 - SNAPSHOT plugin versions in the `plugins {}` block

For more details see https://docs.gradle.org/4.10.2/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)
> Task :wrapper

BUILD SUCCESSFUL in 59s
1 actionable task: 1 executed

$ docker run -v $(pwd):/app -w /app -u $UID:$GID takitake/gradle-alpine2 ./gradlew clean build
Picked up JAVA_TOOL_OPTIONS: -XX:+UseContainerSupport
Downloading https://services.gradle.org/distributions/gradle-4.10.2-bin.zip
..........................................................................
Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details
> Task :clean UP-TO-DATE
> Task :compileJava
Note: /app/src/main/java/org/embulk/output/snowflake/SnowflakeCopyBatchInsert.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

> Task :processResources NO-SOURCE
> Task :classes
> Task :jar
> Task :assemble

# 省略

> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :checkstyleTest
> Task :test
> Task :check
> Task :build

BUILD SUCCESSFUL in 3m 15s
7 actionable tasks: 6 executed, 1 up-to-date

$ docker run -v $(pwd):/app -w /app -u $UID:$GID takitake/gradle-alpine2 ./gradlew gem
Picked up JAVA_TOOL_OPTIONS: -XX:+UseContainerSupport
Starting a Gradle Daemon, 1 incompatible and 2 stopped Daemons could not

# 省略

  Successfully built RubyGem
  Name: app
  Version: 0.0.3
  File: app-0.0.3.gem

BUILD SUCCESSFUL in 38s
6 actionable tasks: 4 executed, 2 up-to-date

おk!

それではembulkから読み込んでみたいと思います。
自作した(僕が作ったわけじゃないけど)Pluginを読み込むには?でぐぐると下記記事が。
https://gist.github.com/hiroyuki-sato/b9d76bd882efa6e29ae16e01396c82ae

githubのディレクトリをのぞいてみると、「lib」ディレクトリの最下層にrbファイルがある。embulkのオプションの「-I」で指定することでインストールすることなく動作確認することができるようだ。

というわけで試す(藁にもすがる思い)

$ cd ../
$ docker run --rm -it -v $(pwd):/work embulk run -I embulk-output-snowflake/lib config.yml

# 省略

Error: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Field 'host' is required but not set
 at [Source: N/A; line: -1, column: -1]

お!???エラーが変わったぞ!!!

どうやら「host」を設定せよというエラーに変わりました。この項目はgithubのソースに含まれています。これはうまくビルドできたのではないでしょうか!?

というわけで修正します。

$ vim config.yml
in:
# inの設定省略
out:
&nbsp; type: snowflake
&nbsp; host: ${host}
&nbsp; user: aipa
&nbsp; password: ${password}
&nbsp; warehouse: aipa_wh
&nbsp; database: aipa_database
&nbsp; schema: PUBLIC
&nbsp; table: aipa_test
&nbsp; mode: replace

修正後再度実行!

$ docker run --rm -it -v $(pwd):/work embulk run -I embulk-output-snowflake/lib config.yml
YYYY-MM-DD 13:03:51.548 +0000: Embulk v0.9.23
YYYY-MM-DD 13:03:53.344 +0000 [WARN] (main): DEPRECATION: JRuby org.jruby.embed.ScriptingContainer is directly injected.
YYYY-MM-DD 13:03:59.352 +0000 [INFO] (main): Gem's home and path are set by default: "/root/.embulk/lib/gems"
YYYY-MM-DD 13:04:00.850 +0000 [INFO] (main): Started Embulk v0.9.23
YYYY-MM-DD 13:04:01.107 +0000 [INFO] (0001:transaction): Loaded plugin embulk-input-s3 (0.5.0)
YYYY-MM-DD 13:04:01.196 +0000 [INFO] (0001:transaction): Loaded plugin embulk/output/snowflake from a load path

# 省略

YYYY-MM-DD 13:04:26.365 +0000 [INFO] (main): Committed.
YYYY-MM-DD 13:04:26.366 +0000 [INFO] (main): Next config diff: {"in":{"last_path":"snowflake_test_.007.00.csv"},"out":{}}

!!!!!!!!!!!!!!

やたーーーーーー!!!!

できました!!!!

snowflakeへログインしてデータが入っているか確認します。

入っているようです!やったー!!!

 

まとめ

すぐできると思っていた内容にめちゃめちゃハマりました。

とりあえずは、githubのpluginをcloneしてきてビルドすれば動くことが確認できたのでもし試す人いましたらこの手順通りにやると良いのかなと思います。

それではー

 

Snowflakeに関するお問い合わせはサービス/研修のお問い合わせからご連絡ください。

このページをシェアする:



DATUM STUDIOは、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。