オンプレからtroccoエージェントを使ってBigQueryへデータを転送してみる

### ※2023年8月現在troccoエージェント機能はクローズとなっております

この記事は
[データエンジニアのためのSaaS「trocco(トロッコ)」のアドベントカレンダー 2021](https://qiita.com/advent-calendar/2021/trocco)

3日目の記事になります。

こんにちは。trocco開発メンバーのいとうです。

この記事ではtroccoエージェントを利用して
外部からのアクセスができない（が、内部起点にインターネットへはアクセスできる）サーバから、データをbigqueryに転送する仕組みを構築してみます。

# 背景

trocco®はインターネットに公開されたデータベースやサービスに対して
データを抽出したり送り込んだりできますが、ご利用者の環境によっては外部からのアクセスが許可できなかったりします。
例）オンプレなどのデータセンターに存在するサーバなど
そういった制約において、trocco®では以下の手段を用意しています。

- 踏み台方式
- エージェント方式

この記事ではエージェント方式について紹介します。

# troccoエージェントとは

外部アクセスができないサーバの上に存在するデータを
troccoを使ってデータ連携する場合に利用できる機能です。

サーバーにエージェントプログラムを起動しておくことで、
定期的にtrocco®と通信しデータを転送できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/2e2bf364-7dbd-cf6e-2f62-c4e45fe023cb.png)

今回は開発環境Macbookをオンプレサーバーの代わりとして構成します。


# 準備

## trocco®の設定

### APIキーの発行

troccoエージェントとtrocco®との通信のためにAPIキー発行します。
ポータルページ右上の設定から「API KEY設定」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/5cc2379c-fa4d-8734-4001-46de0da9977f.png)

新規作成から、任意の情報を記載します。実運用では
**利用者や用途できるだけ具体的な情報を残しておく**と良さそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/48bf507a-cd2d-b03e-7cec-5593fbc2c1f7.png)

保存ボタンを押すと、APIキーが発行されます。APIキーはtrocco®内部で暗号化されているため、
ここの画面を抜けると参照できなくなります。
忘れないようにメモしておいてください。今回私はローカルPCのメモ帳にコピペしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/6bf05d8a-63ed-d481-5051-38d5f621a735.png)


### エージェント定義の作成

trocco®上でエージェントの設定を作成します。
サイドメニューから「オンプレミス・データ転送」にある「エージェント」をクリックし
遷移先のページより、「新規作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/695fb79c-b4ad-ba3e-24ac-905f10e44263.png)

こちらの任意の名前を設定します。今回は「ito local mac エージェント」としました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/aa3f021f-8a91-e0d0-c7da-2b1cedb92fb6.png)

保存ボタンを押すと、エージェント詳細画面になります。ID（数値）が表示されていますので、こちらもメモしておきます。
IDは画面右上に表示されている数値、もしくはURLから確認できます。

オンプレサーバ（今回はMacbook）にエージェントプログラムをインストールした後に
もう一度この画面を利用しますが、trocco®の設定はここまでです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/7b5eb3c2-766d-584a-f3f6-82ba55efee89.png)


## サーバ側の設定

次はオンプレ側の設定になります。
今回はMacbookを利用しますが、Linuxコンテナの利用できるDockerが使えれば、
LinuxやWindowsでも同様に設定できます。

### Dockerのインストール

troccoエージェントはdockerを使って動作します。
今回はMacで動かすので、Docker Desktop for Macをインストールしておきます。

- https://docs.docker.com/desktop/mac/install/

### Dockerコンテナイメージのダウンロード

エージェントの実体となるプログラムをダウンロードします。
実運用時はtrocco®のサポートチームから署名付きURLなどでダウンロードリンクを提供します。
ダウンロードしたtarファイルを元に以下のコマンドでイメージがDockerにインストールされます。

```
docker load -i trocco-agent.tar  # tarファイルよりイメージのインストールするコマンド
...
docker images  # イメージの一覧を表示するコマンド
```

`docker images` で `trocco-agent` が存在すればokです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/be10770f-78fb-6432-2188-615b0b0e5e07.png)

### 起動

エージェントを以下のコマンドで起動します。
trocco®の設定時にメモしておいた2点

- APIキー
- エージェントID

をここで利用します。

起動コマンドはこんな感じです。

```
docker run \
		-d \
		--volume <ローカルのデータ配置パス>:/tmp/documents \
		--restart=always \
		--log-opt max-size=10m \
		--log-opt max-file=3 \
		--env AGENT_ID=27 \
		--env TROCCO_API_TOKEN=LR***** \
		--env DOCKER_HOST=*** \
		--name trocco-agent \
		trocco-agent \
		./run-trocco-agent
```

今回はmakefileにて以下のように設定しました。

```makefile
agent-id:=***
api-key:=LR****

run:
	docker run \
		-d \
		--volume $(PWD):/tmp/documents \
		--restart=always \
		--log-opt max-size=10m \
		--log-opt max-file=3 \
		--env AGENT_ID=$(agent-id) \
		--env TROCCO_API_TOKEN=$(api-key) \
		--env DOCKER_HOST=$(shell HOSTNAME) \
		--name trocco-agent \
		trocco-agent \
		./run-trocco-agent
```

### trocco®とtroccoエージェントの疎通確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/fc2f1927-c79c-7f33-4bce-aa24dd820432.png)


### サンプルデータ

Macbookのプロセスデータを取ってみます。
こちらの記事を参考にしました。

- [topとjqでプロセスのリモート監視 - Qiita](https://qiita.com/shizu-oka/items/e4bf7b52ec2c0d7c1f5c)

実行したコマンド

```
top -l 1 -o MEM -n 10 | jq -rcsR '[ gsub("\n$";"") | splits("\n") | sub(" +$";"") | sub("^ +";"")] | .[0:5] as $titles | .[11:] as $data | {"sys": [$titles | .[] | split(",") | [.[] | sub("^ +";"")]]} as $sys |  [$data[0] | splits(" +") | sub("%";"") | sub("\\+";"")] as $row_keys | $data[1:] | [.[] | [splits(" +")]] | {"task":[.[] | [. as $r | range($row_keys|length) | {"key": $row_keys[.], "value": $r[.]}] | from_entries] | sort_by(.MEM) | reverse | .[:10]} as $task | $sys * $task' > out.json
```

out.jsonの中身は以下のようになります。

```json
{
  "sys": [
    [
      "Processes: 543 total",
      "3 running",
      "540 sleeping",
      "3513 threads"
    ],
    ...
  ],
  "task": [
    {
      "PID": "7931",
      "COMMAND": "com.docker.hyper",
      "CPU": "0.0",
      ...
    },
    {
      "PID": "4729",
      "COMMAND": "VirtualBoxVM",
      ...
      "RPRVT": "N/A",
      "VPRVT": "N/A",
      "VSIZE": "N/A",
      "KPRVT": "N/A",
      "KSHRD": "N/A"
    },
    ...
  ]
}
```

# 転送設定の作成

ここからは実際にデータを転送する設定を作成していきます。


## troccoエージェント --> GCS

エージェントの画面から、「転送設定を作成」を作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/351a4b9e-4e8e-0f45-1a35-42e241abede8.png)

troccoエージェントの転送方式は

- SCP(CSV) --> BigQuery
- ローカルファイル --> Google Cloud Storage
- ローカルファイル --> BigQuery
- ローカルファイル --> Amazon S3

があります。今回はBigQueryに転送したいのですが、jsonファイルを整形したいので
一度GCSに出力して、trocco本体で整形処理を行った上でBigQueryに送りたいので

`ローカルファイル --> Google Cloud Storage` を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/e6e7d625-1e84-2f69-b38f-2c5c72e14d88.png)


転送設定の名前と、作成したエージェントを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/3f0c8377-f513-0e3e-4e23-8448e395865d.png)

転送元設定

- ファイルタイプ: `csv形式（カンマ区切り）`
- 転送パターン: 差分転送
- 対象ファイルパスプレフィックス: `/tmp/documents/*.json`

とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/92f6d39a-5720-fa6d-1f91-2e9c00b5e537.png)

転送先設定は

- Google Cloud Storage接続情報
- Google Cloud Storageバケット名
- ファイルパス

を指定します。
trocco®のGoogle Cloud Storage接続情報の設定方法については
troccoの中の人達が丁寧に [trocco公式ドキュメント](https://docs.trocco.io/s/article/Google-Cloud-Storage%E3%81%AE%E6%8E%A5%E7%B6%9A%E8%A8%AD%E5%AE%9A?language=ja)にまとめてくれています！参考にしてみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/ef631755-38c7-745e-9653-c0cdca747391.png)


「この内容で保存する」を押すとスケジュール設定画面になります。
ここではCRON形式でスケジュールを設定できますが、
今回は何も入力せず、再度「この内容で保存する」を押して設定を完了させてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/ab9d5400-6ad2-9c17-a2ad-3bfd78313d8e.png)

これで Macbookからtroccoエージェントを介してGCSへ転送する設定が作成できました。
ジョブ実行したあとに画面キャプチャを撮ったので既にジョブ履歴が表示されています（笑

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/8dd6bb0a-8adf-a875-58e0-371d504f9e96.png)

では実際に実行してみます。右上の「実行」ボタンを押すと確認モーダルが出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/79bcf82d-fc3e-f893-ea77-9f0fdb424e91.png)
（改めてこのモーダル見ると、ちょっと不安になりますね。。汗 UX改善タスクとしてissueにしておきます笑）
気にせず、はいを押すとジョブが実行されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/8c60446c-380c-3f93-79cb-863ac4375b7a.png)

問題なく転送されたようです。
ちなみに、内部の動きとしては trocco®からエージェントに通信したわけでなく
エージェントが定期的にtrocco®に対してリクエストを投げていて、実行待ちジョブが存在した場合に
転送設定を読み取りに行き、ジョブを開始します。そのため実行開始までに数秒のラグがあります。


## GCS --> BigQuery

ここまでで、だいぶお腹いっぱいなのですがBigQueryまで転送しちゃいましょう。
あとはいつもの？trocco®の転送設定を作成します。

trocco®サイドバーのデータ転送項目の転送設定を選び、新規転送設定作成ボタンをクリックし

- 転送元: Google Cloud Storage
- 転送先: Google BigQuery

を選び、「この内容で作成」ボタンを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/63aa5d5e-b0fc-c9c6-6236-67b45af920dc.png)

編集画面になります。
この名前もこれまで通り、具体的な名前を記載することをおすすめします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/37d8d5e7-dbae-caa2-3d45-4ec0c566873a.png)

転送元Google Cloud Storageの設定は
troccoエージェントの転送設定で準備した出力先と同じ接続情報を今度は転送元として利用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/58495bad-6edb-67a6-b53e-438389cac562.png)

今回trocco本体の転送設定を挟みたかった理由の一つがこちらの設定です。

- 入力ファイル形式: JSONPath
- JSONPATH: `$.task`

この設定をすることで、ローカルに出力したjsonファイルの taskキー配下の値を転送するようにできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/726b421e-7acd-bc09-45e2-9d333a80a91f.png)


転送先のBigQueryの設定は以下の通りです。

BigQueryの接続情報についても中の人達が懇切丁寧に[ドキュメント](https://docs.trocco.io/s/article/BigQuery%E3%81%AE%E6%8E%A5%E7%B6%9A%E8%A8%AD%E5%AE%9A?language=ja)にまとめてくれているので
こちらを参照ください。今回は私のほうですでに準備してある接続情報を選んで、データセット、テーブル名を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/a5cf6be3-1aca-3682-ca89-bf431397b8ab.png)

一通り設定を終えたらページ下部にある「次のSTEPへ」ボタンを押しますと、
データプレビュー・詳細設定画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/e62cc922-1ceb-7f54-c0d3-4e433d82db10.png)

少し待つと、上記のようにデータがとれることが確認できるはずです。

ここまでくれはほぼ完成です！！（マジモンのフラグだと気づいたのはもう少し後の話。。）

確認画面に遷移し、「保存して適用」を押してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/a7501110-d215-8fdf-bc51-1843f4e9bad9.png)

転送設定の詳細画面に遷移しますので、右上の実行ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/483614f9-6fc1-47f0-1d21-e9807d65465a.png)

ジョブ実行画面です。ジョブを実行ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/6406b0db-ab99-b933-65f3-483019aa34ae.png)


おっと、エラーですね...何も考えずにサポートに問い合わせたいところですが、
問い合わせ -> 中の人 -> itoですね、自分でやってください -> 自分と返ってくるので自分で読み解きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/3684b4ab-587d-f38f-e7cf-784bc1e607a1.png)


> invalid: Invalid field name \"#TH\". Fields must contain only letters, numbers, and underscores, start with a letter or underscore, and be at most 300 characters long.

丁寧にメッセージがかかれていました。BigQueryのカラム名のルールに則ってなかったようです。
転送設定を直しましょう。

転送設定の編集から、プレビュー画面に行き、スクロールすると「カラム定義」項目があります。
ここでカラム名を見ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/6eaff62e-1f89-6fae-f8bd-0715a91e96d0.png)

**一括変換** という機能がありますね。記事考えているときはこの機能こと忘れてましたが、便利機能の一つです。
カラム名の右のチェックボックスを指定するとボタンがゆうこうになります。今回はすべて選んでボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/eb7d647b-540a-24c8-a6d7-01602a6a8f01.png)

変換するを押すと、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/8a3d62cb-f78c-1b37-63dc-485e60c560ba.png)


エラーに出ていた `#TH` を `_TH` と変換してくれました。他のカラムについての反映されています。
カラムが多いときにはとても便利そうですね！！

この変更を再度プレビューで確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/54129208-d0c1-ecef-8f30-6b7efeb08664.png)

変換されてますね。問題なさそうなので、確認画面に行きます。
確認画面では変更内容の差分がハイライトされます。Webエンジニアにはおなじみのdiff表示ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/d6f2b3ba-dd88-fa4b-19db-b6b48ae5faa3.png)

またまた便利機能が見つかってしまった。。 **スキーマ追従機能** になります。
最初の設定からスキーマが変わったので変更前に転送先のテーブルのスキーマを追従するか聞いてくれました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/6c0e2a5b-f621-e400-e66a-5a805877ef26.png)

ですが、今回はテーブルがまだ作成されていないので、追従は不要のため`BigQueryには更新しない`を選び保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/dd262570-b4ba-fb5f-8bbf-9eb4c5a52a34.png)

リベンジです。うまく転送できるか？！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/773efffc-f95a-64b3-a0f2-564b5a61c6fc.png)


...


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/08d5ad16-fba8-9634-6900-3e3989048710.png)

やったー！BigQueryコンソールでも確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/766d55da-7291-38e6-2ac0-ec183070ca04.png)


`Google` `Chome` とカラムが別れてしまっていますね汗 json出力をなおしたほうが良さそうですが、これまた別の機会にします。

## 余談: troccoワークフロー

今回は2つの転送設定を個別に動かしましたが、
troccoワークフローを使い、2つのジョブの前後関係を設定し、定期実行することができます。
前後関係を考慮して、それぞれ別のスケジュールを組む必要はなく
ワークフローを作成し、一つのスケジュール設定で実現できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169862/3be40627-9fd6-b50e-00d8-8c332869c228.png)

# まとめ

troccoエージェントを使って、データをBigQueryに転送してみました。
ちょっとしたエラーに遭遇しましたが、別の機能の紹介をしつつ無事終えました。

trocco®に興味ある方はぜひご連絡ください。
「この記事読んだ！」と問い合わせいただけたらとっても嬉しいです。