azcopy の Azure Files (Azure ファイル共有) スループット測定② - ディレクトリが多くて遅いケース -

こんにちは、アーキテクトのやまぱんです。
補足コメントや質問、<font color="red">**いいね、拡散**</font>、是非お願いします🥺！
間違ってたら<font color="red">**優しく**</font>教えてください！

## きっかけ
以前に以下のような記事を書きました、今回は ディレクトリが多数(数千レベル)ある場合に注目して検証してみました

https://qiita.com/aktsmm/items/08541acc1161c7638d9c

## 検証内容など
+ azcopy version 10.26.0
+ Azure Files (standard ファイル共有、および後半ではプレミアムファイル共有) 、東日本リージョンに作成(前回の記事 でも記載したようにストレージ アカウント単位でイングレススループットは 7152MiB/s です）
+ Azure VM (東日本リージョン)
+ OS Linux (Rhel)
+ azcopy のコマンドを OS 上で実施し、Azure Files へディレクトリを指定してその配下のファイルをまとめて転送
+ 使用したデータ
下記コマンドで ランダムファイルを作成、1ファイルあたりの大きさは固定でファイル数を変えて数パターン実施しました。
以下は 100KB のファイルを 2000 個作る例

```shell:create random file
for i in $(seq 1 2000); do
    dd if=/dev/urandom of=file_$i bs=100K count=1
done
```

+ スループット測定
azcopy 終了時に表示される時間で、ファイル総容量を割って算出。

# 検証結果
## 検証結果①  ディレクトリの数やディレクトリ構造における観点
+ ノーマルファイル共有で実施
+ 実施したコマンド
> azcopy copy "｛ソースディレクトリパス｝" "https://{ストレージ アカウント名}.file.core.windows.net/{コンテナー名}/{ディレクトリ名}?sv｛SAS｝" -- recursive -- log-level ERROR

|実施パターン|合計容量(MB)|1ファイルの大きさ|階層|ディレクトリ数|ファイル数|処理時間(分)|実質スループット|
|--------------|--------------|------------------|------|--------------|----------|------------|---------------|
|パターン1|約200MB|112KB|1|1|1830|0.1667|19.99|
|パターン2|約200MB|112KB|6|30|1830|0.1667|19.99|
|パターン3|約200MB|112KB|7|**1860**|1830|1.5671|**2.1271**|


:::note info
上記の結果よりディレクトリの数が多いと非常に時間がかかることがわかる
:::

## 検証結果② ディレクトリの数が多い場合にどのようにすれば早くなるかの観点
+ 実施したコマンド
> azcopy copy "｛ソースディレクトリパス｝" "https://{ストレージ アカウント名}.file.core.windows.net/{コンテナー名}/{ディレクトリ名}?sv｛SAS｝" -- recursive -- log-level ERROR --include-pattern "*"

パターン①：ディレクトリ構造をあらかじめ作成しておいた場合に、--include-pattern "*" を付けたパターン
パターン②：ディレクトリ構造をあらかじめ作成してなかった場合、--include-pattern "*" を付けたパターン
パターン③：ディレクトリ構造もあらかじめ作らず、--include-pattern "*" もつけないパターン

また、ノーマルファイル共有に加え、プレミアムファイル共有でも実施した。

<ノーマルファイル共有>
|パターン|合計容量(MB)|1ファイルの大きさ|階層|ディレクトリ数|ファイル数|--include-pattern"*"|事前ディレクトリ作成|処理時間(分)|実質スループット(MB/S)|
|-----------------------------------------------|--------------|------------------|------|--------------|----------|-----------------------|------------------|------------|------------------------|
|①ディレクトリ構造をあらかじめつくった場合|約200MB|112KB|7|1860|1830|あり|あり|0.1667|**19.996**|
|②ディレクトリ構造を作らず、--include-pattern"*"で実施|約200MB|112KB|7|1860|1830|あり|無し|0.2|**16.667**|
|パターン③|約200MB|112KB|7|1860|1830|無し|無し|1.5671|**2.127**|

*上記のパターン③は検証結果①のパターン③と同一

<プレミアムファイル共有>
|パターン|合計容量(MB)|1ファイルの大きさ|階層|ディレクトリ数|ファイル数|--include-pattern"*"|事前ディレクトリ作成|処理時間(分)|実質スループット(MB/S)|
|----------------------------------------------|--------------|------------------|------|--------------|----------|-----------------------|------------------|------------|------------------------|
|①ディレクトリ構造をあらかじめつくった場合|約200MB|112KB|7|1860|1830|あり|あり|0.1334|**24.988**|
|②ディレクトリ構造を作らず、--include-pattern"*"で実施|約200MB|112KB|7|1860|1830|あり|無し|0.1667|**19.996**|
|パターン③|約200MB|112KB|7|1860|1830|無し|無し|1.13|**2.950**|


:::note info
大量のディレクトリがある場合に速度がでない場合、
+ --recursive --log-level ERROR --include-pattern "*" 、特に <font color="red"> --include-pattern "\*" </font>のオプションを付与して実施することで速度の改善が見込める
+  事前にディレクトリ構造を作ることができる場合は、事前にディレクトリ構造を作ることも有効
+  プレミアムファイル共有を使うことも有効
:::


# 付録
## --include-pattern について
今回の**肝となったオプション**です。
* azcopy copy
https://learn.microsoft.com/ja-jp/azure/storage/common/storage-ref-azcopy-copy
> --include-pattern (string) コピーするときにこれらのファイルのみを含めます。 このオプションでは、ワイルドカード文字 (*) がサポートされます。 ";" を使用してファイルを区切ります。

今回、 --include-pattern **"*"** をつけることで、ファイルのみのコピーになって、基本的にディレクトリが作られていないときだけディレクトリを作るような動きになっているのではないかと推察されます。

## --recursive について
* azcopy copy
https://learn.microsoft.com/ja-jp/azure/storage/common/storage-ref-azcopy-copy
> --recursive ローカル ファイル システムからアップロードするときにサブディレクトリを再帰的に検索します。

## --log-level ERROR について
> --log-level (string) ログ ファイルのログの詳細度を定義します。使用できるレベルは次のとおりです。INFO (すべての要求と応答)、WARNING (遅い応答)、ERROR (失敗した要求のみ)、NONE (出力ログなし)。 (既定値 は"INFO")。 (既定値は "INFO")

今回は ERROR なので失敗した要求以外のログは記録しないようにしています。

* 生成されるログの数を減らす
https://learn.microsoft.com/ja-jp/azure/storage/common/storage-use-azcopy-optimize#decrease-the-number-of-logs-generated

## Azure Files のメトリックについて
今回おもに確認したメトリックは以下になります。
ストレージ アカウント、Azure ファイル共有への転送が遅い時はこのあたりを確認するとよいでしょう。

• Azure Portal > Storage Account > 監視 > メトリック
> * スコープ
[メトリック名前空間]:[ファイル]
[メトリック]:[Tranactions] 
> * 分割基準
[値]:[API name]

+ 以下 参考に画面ショット例(--include-pattern **"*"** を付けていないパターンの場合)です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982508/5bbec5c4-736c-dffc-dad5-09c60aa2e44d.png)
上記の結果をみると、今回のデータセットの場合 **[Create Directory]** に比較的時間がかかっていることがわかります。


• Azure Portal > Storage Account > 監視 > メトリック
> * スコープ
[メトリック名前空間]:[ファイル]
[メトリック]:[Tranactions] 
> * 分割基準
[値]:[Response type]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982508/4ded13b7-beac-aa32-d7b5-e17a4c8e148b.png)

+ ClientOtherErrors とは　- MS Learn
https://learn.microsoft.com/ja-jp/troubleshoot/azure/azure-storage/files/security/files-client-other-errors#what-are-clientothererrors
> ClientOtherError は通常、"見つかりません" や "リソースが既に存在する" など、クライアント側の予期されるエラーを意味します。サーバー側のストレージ ログ ファイルでは、これらの操作は ClientOtherErrors のトランザクション状態で記録されます。

:::note info
その他、参考にしたメトリックとしては以下です。
* [メトリック] : [Success Server Latency] 
* [メトリック] : [Success E2E Latency]
:::


# まとめ
--recursive --log-level ERROR --include-pattern "*" をつけると大量のディレクトリがある場合の azcopy は早くなる！！