Azure Data Factory で Azure Cosmos DB のデータを Azure Data Lake Storage Gen2 に日次でコピーする

# 本記事のテーマ
Azure Data Factory の [コピーアクティビティ](https://docs.microsoft.com/ja-jp/azure/data-factory/copy-activity-overview) を用いて Azure Cosmos DB のデータを Azure Data Lake Storage Gen2 に1日1回コピーする方法についてまとめます。
なお、同様の処理を [マッピングデータフロー](https://docs.microsoft.com/ja-jp/azure/data-factory/concepts-data-flow-overview) でも行うことができますが、本記事では扱いません。

# 完成形イメージ
以下が完成形のイメージ図です。黒矢印はデータフロー、青矢印は参照を表しています。
1日1回起動する Data Factory が Cosmos DB の `users` コレクションのドキュメント内の `updateDatetime` という更新日時を条件にしてクエリを実行して Data Lake Storage に Gzip 圧縮された 1行1JSON の形式で出力するというフローになります。

![DataFactory_コピーアクティビティ_CosmosDB_ADLS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/2c2642a8-da0f-42c4-de94-e35437c3c3fb.png)

# 前提
以下のリソースがあることを前提としています。もし無い場合は各サービスのチュートリアルなどを参考に作成頂ければと思います。

- Azure Cosmos DB のアカウント、データベース、コレクションがあること
- Azure Data Lake Storage Gen2 のストレージアカウントとコンテナーがあること
- Azure Data Factory を作成済みであること

# 1. Linked services
まず、Data Factory から Cosmos DB と Data Lake Storage に接続できるように Linked services を設定します。

## 1-1. Cosmos DB
(1) 画面左の鉛筆マークをクリック > Connections > Linked services の [New] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/8cbbae53-dd40-3363-ef6b-16c03bf629d4.png)

(2) 検索キーワードに *cosmos* などと入力し [Azure Cosmos DB (SQL API)] を選択して [Continue] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/ce3338fe-e2ec-7d22-d6d4-2c425453cde4.png)

(3) 必要な項目を入力します。

- Name には任意の名前を入力、ここでは `CosmosDbNakazaxQiitaDf` を使用します
- Account selection method で [From Azure subscription] にチェックします
    -  [Azure subscription] に対象の Azure サブスクリプションを選択
    -  [Cosmos DB account name] に対象の Cosmos DB アカウント名を選択
    -  [Database name] に対象の Cosmos DB データベース名 を選択
-  他の項目を必要に応じて編集した後、画面下部の [Test connection] をクリックし、*Connection successful* と表示されたら [Create] をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/d0b6f81a-43c3-023b-73f0-49810ded8321.png)

以上で Cosmos DB の接続設定は完了です。

## 1-2. Data Lake Storage
(1) 画面左の鉛筆マークをクリック > Connections > Linked services の [New] をクリックし、検索キーワードに *Data Lake* などと入力し [Azure Data Lake Storage Gen2] を選択して [Continue] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/d256db6a-aa11-cf54-c3a6-48f2b637d6cf.png)

(2) 必要な項目を入力します。

- Name には任意の名前を入力、ここでは `DataLakeNakazaxQiitaDf` を使用します
- Account selection method で [From Azure subscription] にチェックします
    -  [Azure subscription] に対象の Azure サブスクリプションを選択
    -  [Storage account name] に対象のストレージアカウント名を選択
-  他の項目を必要に応じて編集した後、画面下部の [Test connection] をクリックし、*Connection successful* と表示されたら [Create] をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/7f67e3ad-b931-ef29-3829-30ffcd1f5ea8.png)

以上で Data Lake Storage の接続設定は完了です。

# 2. Datasets
接続情報を設定したら、次に Datasets を設定していきます。

## 2-1. Cosmos DB
(1) 画面左側の **Datasets** にフォーカスを当てると [...] と表示されるので [New dataset] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/ae5d7fbf-72e7-86e4-0ff4-84e9dfe73e94.png)

(2) 検索キーワードに *cosmos* などと入力し [Azure Cosmos DB (SQL API)] を選択して [Continue] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/1d4841e3-d345-111c-fb58-fb3600f518c8.png)

(3) 必要な項目を入力し [OK] をクリックします。

- Name には任意の名前を入力、ここでは `CosmosDbUsers` を使用します
- Linked service で先に作成したものを選択、ここでは `CosmosDbNakazaxQiitaDf` を使用します
- Collection に対象の Cosmos DB コレクションを選択します
- Import schema で [From connection/store] にチェックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/f165aba4-5f08-fbfe-c4e5-54216ab50fff.png)

### (Tips) Publish all について
Data Factory 上で何かしらの変更を行うと画面左上の [Publish all] の横に数字が表示されます。この数字は、保存されていない変更の数を表しています。この状態でブラウザをリフレッシュすると変更が破棄されてしまいます。変更を保存するために、キリの良いタイミングで [Publish all] を押すようにしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/e8f4c3f4-178f-bf07-ee09-cca84b1d58f5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/310b54f1-c81a-aa62-70ba-f313a4f19c8e.png)

## 2-2. Data Lake Storage
(1) 画面左側の **Datasets** にフォーカスを当てると [...] と表示されるので [New dataset] をクリックします。検索キーワードに *Data Lake* などと入力し [Azure Data Lake Storage Gen2] を選択して [Continue] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/c0c2ffd9-4e20-d5ca-7214-946bf8a7c0ef.png)

(2) [Json] を選択して [Continue] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/732328d1-29c0-26ed-5fc4-03a35b3ef739.png)

(3) 必要な項目を入力し [OK] をクリックします。

- Name には任意の名前を入力、ここでは `DataLakeUsersJson` を使用します
- Linked service で先に作成したものを選択、ここでは `DataLakeStorageQiitaDf` を使用します
- File path の一番左のテキストボックスに Data Lake Storage のコンテナー名を入力します
- Import schema で [From connection/store] にチェックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/c8c25705-063e-b1de-60fb-98b2c439ca55.png)

## 2-3. Data Lake Storage - Parameters
Dataset にはパラメーターを渡すことができます。ここでは、Data Lake Storage について `year=2019/month=01/day=01` のように年月日をディレクトリのパスに入れるための設定をします。

(1) 作成した Data Lake Storage の Dataset の [Parameters] タブの [New] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/9ffd620d-b79c-6b80-ee28-7316a8fb7093.png)

(2) 以下キャプチャのように `year` `month` `day` を定義します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/596f0f91-0f37-9dca-1c89-d9c29aef1851.png)

(3) [Connection] タブの Directory テキストボックスの下の *Add dynamic content* をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/6ff81e5a-8c39-f4b4-c15e-2cc33616b058.png)

(4) 以下の内容を入力し [Finish] をクリックします。

```
year=@{dataset().year}/month=@{dataset().month}/day=@{dataset().day}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/4562e781-85b3-8454-457c-60258d0040bb.png)

(5) [Connection] タブの File テキストボックスの下の *Add dynamic content* をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/fe9c187c-1ea1-cfa7-456f-d1fbd72aaad2.png)

(6) 以下の内容を入力し [Finish] をクリックします。

```
@concat(dataset().year, '-', dataset().month, '-', dataset().day, '.json.gz')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/fc977c52-ef54-11b3-4130-a4c5304b6343.png)

(7) [Connection] タブの Compression type に [gzip] を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/034497eb-f8f0-a9c6-b9cf-5180d33b7766.png)

以上で Datasets の設定は完了です。

# 3. Pipelines
## 3-1. Pipeline 作成
(1) 画面左側の **Pipelines** にフォーカスを当てると [...] と表示されるので [New pipeline] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/98dfcab1-148c-b8c5-29ad-220b73029ff6.png)

(2) Pipeline に分かりやすい名前を設定します。ここでは `PipelineCopyCosmosDbUsersToDataLakeUsersJson` という名前にしています。なお、赤枠で囲んだアイコンをクリックすると Properties の表示/非表示を切り替えられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/6dde7770-5ca6-bd6b-0624-8a582d0f8167.png)

(3) [Paramters] タブを選択し [New] をクリックして以下のようにパラメーターを設定します。この2つのパラメーターは後ほど Cosmos DB へのクエリの WHERE 句で利用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/907684f8-b3ad-b38c-48ac-9ddfe06fec63.png)

## 3-2. Copy data アクティビティ 作成
(1) [Move & transform] を展開し [Copy data] を右側のスペースにドラッグ & ドロップします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/90171177-2d27-f1c6-0128-0adf9515cea0.png)

(2) Copy data アクティビティの [General] タブの Name に分かりやすい名前を設定します。ここでは `CopyCosmosDbUsersToDataLakeUsersJson` という名前にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/758f0caa-d114-1a99-9dc2-485ce0c19495.png)

(3) [Source] タブの Query のテキストボックスの下の *Add dynamic content* をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/f9cbeffd-5c05-fe28-0df5-5930fbac55e7.png)

(4) 以下の内容を入力し [Finish] をクリックします。クエリの内容は Cosmos DB の項目に合わせて適宜修正ください。

```
SELECT * FROM c WHERE 
'@{pipeline().parameters.windowStartTime}' <= c.updateDateTime 
AND c.updateDateTime < '@{pipeline().parameters.windowEndTime}'
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/a72179f8-c0dc-090c-9428-f253e6dfe17b.png)

(5) [Sink] タブを以下のように設定します。

- Sink dataset に先に作成した Dataset `DataLakeUsersJson` を設定します
- Dataset properties に以下の値を設定します
    - year: `@{substring(pipeline().parameters.windowStartTime, 0, 4)}`
    - month: `@{substring(pipeline().parameters.windowStartTime, 5, 2)}`
    - day: `@{substring(pipeline().parameters.windowStartTime, 8, 2)}`
- File pattern に `Set of objects` を指定します

上記のように指定することで、Pipeline のパラメーター `windowsStartTime` に `2019-12-01T00:00:00Z` が指定された場合、year には `2019`、month には `12`、day には `01` が入ります。
また File pattern に `Set of object` を指定すると1行1JSONの形で出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/79b56000-6050-b749-ed43-60abc9fd12af.png)

以上で Pipeline の基本的な設定は完了です。

## 3-3. Debug 実行
Pipeline の設定が正しいかどうかを確認するためにパイプラインをデバッグ実行してみます。

(1) 赤枠の [Debug] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/e3fe7a0c-680c-0a51-81dc-f50c45147555.png)

(2) Cosmos DB に存在する日付範囲をパラメーターに指定して [OK] をクリックします。これによりパイプラインのデバッグ実行が開始されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/9d679704-3b04-0af6-ea68-068327eb0000.png)

(3) 進捗状況を確認するため赤枠部分を適宜クリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/8aa1718e-04c0-18fc-3a9a-60c7f6d26471.png)

(4) STATUS が *Succeeded* になったら NAME にフォーカスを合わせて赤枠で囲んだ眼鏡マークをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/120f93b5-28b2-0a8e-b79c-d450295ab2e9.png)

(5) デバッグ実行の詳細が表示されます。内容に問題ないかを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/02449696-9f97-289b-756e-753b85a62ebb.png)

(6) Data Lake Storage を見てみると `users-json-container/year=2019/month=12/day=01/2019-12-01.json.gz` のファイルが出力されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/783ddbb4-b844-f7f1-fcc6-2391018de06e.png)

(7) Gzip ファイルをダウンロードし展開した結果、以下のように1行1JSON の形式で出力されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/9dc59d6f-c774-6d7b-5220-6cb230c6e436.png)

これで Pipeline & Copy data アクティビティの基本的な設定は完了です。

# 4. Triggers
Pipeline をスケジュール設定して実行するために Trigger を編集していきます。

## 4-1. Trigger 作成
(1) 画面左の鉛筆マークをクリック > Triggers > [New] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/7691763c-9b8e-1004-ea38-eaa0cd2dda97.png)

(2) 以下のように設定し [Advances] を展開します。

- Name には任意の名前を入力、ここでは `TriggerPipelineCopyCosmosDbUsersToDataLakeUsersJson` を使用します
- Type に [Tumbling window] を指定します
- Start Date (UTC) に任意の日時を指定します (* 過去日時を指定した場合の挙動は後述)
- Recurrence に `24 Hours` を指定します
- Activated に `Yes` を指定します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/92dde2cb-c332-e948-845d-d85efb5ec1ec.png)

(3) [Advanced] のデフォルト値は以下のようになっています。これを赤枠のように編集して [OK] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/394a121d-01ec-0a9e-c955-bf801aa0d76e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/40991ae3-4b78-863d-f195-d1767ceb7d5d.png)

### (Tips) Trigger の種類について
Schedule と Tumbling window の違いなどについては以下のドキュメントをご覧ください。

- [Azure Data Factory でのパイプラインの実行とトリガー](https://docs.microsoft.com/ja-jp/azure/data-factory/concepts-pipeline-execution-triggers)
- [Azure Data Factory でのパイプラインの実行とトリガー - トリガーの種類の比較](https://docs.microsoft.com/ja-jp/azure/data-factory/concepts-pipeline-execution-triggers#trigger-type-comparison)

### (Tips) Start Date (UTC) による Pipeline の挙動
Start Date (UTC) について過去と未来の日時の両方を指定可能です。現在日時が `2020/05/30 19:00 (UTC)` の場合、後述する Pipeline への関連付けと Publish を行った後にどのように Pipeline が起動されるかの例を記載します。

**A. 過去日時指定 - Tumbling window & Start Date (UTC) 2020/05/28 00:00 & Recurrence 24 Hours を指定**

- 以下2件の Pipeline が直ちに起動
    - Pipeline 1
        - windowStartTime: `2020-05-28T00:00:00Z`
        - windowEndTime: `2020-05-29T00:00:00Z` 
    - Pipeline 2
        - windowStartTime: `2020-05-29T00:00:00Z`
        - windowEndTime: `2020-05-30T00:00:00Z` 
- 現在日時から5時間後 = `2020/05/31 00:00 (UTC)` に以下の Pipeline が起動
    - windowStartTime: `2020-05-30T00:00:00Z`
    - windowEndTime: `2020-05-31T00:00:00Z` 

**B. 未来日時指定 - Tumbling window & Start Date (UTC) 2020/05/31 00:00 & Recurrence 24 Hours を指定**

- 現在日時から5時間後 = `2020/05/31 00:00 (UTC)` に以下の Pipeline が起動
    - windowStartTime: `2020-05-30T00:00:00Z`
    - windowEndTime: `2020-05-31T00:00:00Z` 

**C. 大幅な過去日時指定 - Tumbling window & Start Date (UTC) 2020/01/01 00:00 & Recurrence 24 Hours を指定**

- 約150件の Pipeline が順次起動 (Advanced の Max concurrency に 10 を指定している場合、最大10件ずつ同時に実行される)
    - Pipeline 1
        - windowStartTime: `2020-01-01T00:00:00Z`
        - windowEndTime: `2020-01-02T00:00:00Z`
    - (途中省略)
    - Pipeline 150
        - windowStartTime: `2020-05-29T00:00:00Z`
        - windowEndTime: `2020-05-30T00:00:00Z` 
- 現在日時から5時間後 = `2020/05/31 00:00 (UTC)` に以下の Pipeline が起動
    - windowStartTime: `2020-05-30T00:00:00Z`
    - windowEndTime: `2020-05-31T00:00:00Z` 

大幅な過去日時を指定すると一気に Pipeline が起動されることになるため、Cosmos DB 側の RU が不足しないかなど注意が必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/3f6670bf-7bd1-7909-ff7a-c70107542ad9.png)


## 4-2. Pipeline への関連付け
(1) 先に作成した Pipeline を選択し [Add trigger] > [New/Edit] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/0787fc9a-b6d9-a960-1f50-4939627b02e0.png)

(2) 先に作成した Trigger を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/6a2b9cee-4d57-c576-b66d-89d4f7756764.png)

(3) すると Edit trigger が表示されます。先ほど必要な内容は編集済みですので [OK] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/1cc35ae9-d237-975c-ba27-ff093ebcfab8.png)

(4) パラメーターの VALUE を以下のように編集します。

- windowStartTime: `@trigger().outputs.windowStartTime`
- windowEndTime: `@trigger().outputs.windowEndTime`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/a54995b3-df65-f2ca-b956-5ff06dff8ff3.png)

(5) Trigger (1) というように Pipeline に Trigger が関連付けられたことを確認したら [Publish] をクリックします。Publish を実施することで Trigger が有効になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/5f50b886-5a60-9c64-9d57-8abe840463f8.png)

## 4-3. Pipeline 実行確認
Trigger を Pipeline に関連付けて Publish することで Trigger が有効になります。前述の通り、Trigger に過去日時を指定した場合は Publish したら直ちに Pipeline が実行されます。未来日時を指定した場合、その日時になると Pipeline が実行されます。
Pipeline が実行されたら以下のようなキャプチャの流れで実行結果を確認すると良いでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/24207c25-dca3-e5ff-6954-2c465048b8ca.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/531609d8-b4da-9d6f-143b-eee8fa1f0ba3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/2c24870c-8005-d8b6-1505-515b625fd0ee.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/c3df283b-3d3b-4e1c-6bf7-772504c72d42.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284925/dbe7480d-c434-6000-6dae-f7a5565a9f6e.png)

以上です。