AzCopyでのストレージコンテナ間の同期をWebJobsで実行する

# はじめに
ストレージアカウントの地理冗長の方法としてGRS, RA-GRS, GZRS, RA-GZRSなどがあり、ペアリージョン(Japan EastならJapan West)にデータをレプリケーションすることができるが、ペアリージョンではない例えば海外のリージョンなどのストレージにデータを同期したい場合はこれらのオプションでは実現できない。そのためなんらかの別の方法でデータをコピーする必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/f17ab251-4bfb-b752-bb70-a888663246d9.png)
[AzCopy V10](https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-azcopy-v10) にはsyncモードがあり、これを利用して片方向のデータ同期が可能だ。これを定期的に実行するのが良さそうなので今回はこの手法を検討したい。ほかの方法としては例えば [Data Factory](https://docs.microsoft.com/ja-jp/azure/data-factory/introduction) が考えられる。Data Factoryによる [増分コピーの手法](https://docs.microsoft.com/ja-jp/azure/data-factory/tutorial-incremental-copy-overview) を使用してデータをコピーすることができるが、こちらの場合はソース側のストレージでのデータ削除には対応できないため、削除が無いような処理の場合には有効である。

このAzCopyだが、便利なコマンドなので定期実行を自動化させたいところだが、このために仮想マシンをわざわざ新しくで作成したくないし、オンプレミス環境での実行もシステムがばらけてなんとなく嫌なので、なにかPaaSを利用して実現したい。オペレーションの自動化のPaaSといえばAzure Automationが挙げられるが、azcopyを実行することができない。App ServiceのWebJobであれば、exeファイルを実行することができるため、これを利用して自動化の仕組みを作れそうだという事で、試したメモを残す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/a8c0cc7c-a99a-67b4-40ca-85a5ed9db277.png)

# AzCopyでコンテナー間の同期を実行する
AzCopyのsyncコマンドはファイル名と最後に変更されたタイムスタンプが比較されてファイルが同期される。`--delete-destination`を`true`にするとプロンプト無しに存在しなくなったファイルが削除される。何も聞かれずに削除されるようになるので安全をとって [論理的な削除](https://docs.microsoft.com/ja-jp/azure/storage/blobs/soft-delete-blob-overview) を有効にしておくのもあり。

[こちらのドキュメント](https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-azcopy-blobs?toc=/azure/storage/blobs/toc.json#synchronize-files) の通りにコマンドを実行すればOK。

```bash
azcopy sync 'https://<source-storage-account-name>.blob.core.windows.net/<container-name>' 'https://<destination-storage-account-name>.blob.core.windows.net/<container-name>' --recursive --delete-destination=true
```

今回は [SASトークン](https://docs.microsoft.com/ja-jp/azure/storage/common/storage-sas-overview) を使用してアクセスを承認する方法をとる。SASトークンの場合は以下のようになる。

```bash
azcopy sync 'https://<source-storage-account-name>.blob.core.windows.net/<container-name><SAS-token>' 'https://<destination-storage-account-name>.blob.core.windows.net/<container-name><SAS-token>' --recursive --delete-destination=true
```

実際に実行すると以下のような出力が得られる。ここでは1ファイル同期されている。

```
INFO: Any empty folders will not be processed, because source and/or destination doesn't have full folder support

Job dbd88421-6a26-094d-6397-35632c65572d has started
Log file is located at: /home/hideo/.azcopy/dbd88421-6a26-094d-6397-35632c65572d.log

INFO: azcopy: A newer version 10.6.0 is available to download

100.0 %, 0 Done, 0 Failed, 1 Pending, 1 Total, 2-sec Throughput (Mb/s): 0

Job dbd88421-6a26-094d-6397-35632c65572d Summary
Files Scanned at Source: 3
Files Scanned at Destination: 2
Elapsed Time (Minutes): 0.1001
Number of Copy Transfers for Files: 1
Number of Copy Transfers for Folder Properties: 0
Total Number Of Copy Transfers: 1
Number of Copy Transfers Completed: 1
Number of Copy Transfers Failed: 0
Number of Deletions at Destination: 0
Total Number of Bytes Transferred: 27683
Total Number of Bytes Enumerated: 27683
Final Job Status: Completed
```

# AzCopy syncコマンドをWebJobで定期実行する

## WebJobsプラットフォームの準備
WebJobはWeb App, API App, Mobile Appと同様に同じApp Serviceのプラットフォーム上でプログラムやスクリプトを実行する機能になるため、WebJobの利用にApp Serviceに加えた追加コストは発生しない。
[Azure App Service で Web ジョブを使用してバックグラウンド タスクを実行する](https://docs.microsoft.com/ja-jp/azure/app-service/webjobs-create) に従ってApp Serviceの環境を準備する。現在のところWebJobsはApp Service on Linuxではサポートされていないため、Windowsでデプロイすること。


## バッチファイルの準備
run.cmdというファイル名でAzCopyのsyncコマンドを記述する。
注意事項としては、SASトークンに "%" がしばしば含まれているが、これらは変数解釈されてしまうため、"%%"としてエスケープしておく必要がある。また、ストレージのPATHを囲うのはシングルクォートではなくダブルクォートを使用すること。

```bash
azcopy.exe sync "https://<source-storage-account-name>.blob.core.windows.net/<container-name><SAS-token>" "https://<destination-storage-account-name>.blob.core.windows.net/<container-name><SAS-token>" --recursive --delete-destination=true
```

## AzCopyのダウンロード
[こちら](https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-azcopy-v10) からAzCopy V10 (Windows 64 ビット)のzipをダウンロードし、Zipを解凍してazcopy.exeファイルを抽出しておく。

## WebJobファイルの準備

先に作成した`run.cmd`とダウンロードした`azcopy.exe`をZip圧縮してWebJobにアップロードするファイルの準備をしておく。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/c6a453a9-ba03-ba31-643a-577f7dae8730.png" width=50%>

## WebJobへのアップロードと設定
App Serviceの管理画面からWebJobsを選択してWebJobの管理画面に遷移する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/fa072160-3a37-d034-c4ca-d9c315460593.png" width=70%>

`+Add`をクリックしてWebJobを追加する。`File Upload`のところで先ほど作成したZipファイルを指定する。`Triggers`にはScheduledを選択し、実行時間のNCRONTAB式で記述する。下図の例は30分おきに実行する指定。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/96c261fd-99ec-0a94-0b27-18db4b58f6b4.png" width=80%>

NCRONTAB式については [こちらのドキュメント](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-timer?tabs=csharp#ncrontab-expressions)を参照。

## WebJobの実行と確認

設定が完了すると下図のようにジョブが登録される。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/f408774e-0413-99e1-7ea7-5735a3d40078.png" width=80%>

ジョブ名をクリックして`Run`を実行すると強制的にトリガーが起動される。うまくいけばStatus欄Completedのようになる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/a2c44388-740b-d11a-975e-419facc8e6cb.png" width=80%>

Logを確認してAzCopyのsyncコマンドが正常に動作していることが確認できればOK。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/3945daa8-35c1-0c7c-2d40-d51703b807d6.png" width=80%>