S3へのデータ転送を帯域制御する方法

aws-cli

まいど、大阪の市田です。
今回は、AWS CLIでS3にデータ転送する際のトラフィック帯域を抑える方法についてご紹介します。

帯域を抑えたい場面としては「自分の作業でオフィスの回線を圧迫しないようにする」といったような場合です。逆にAWS CLIのaws s3 syncを速くする場合は、下記のエントリが参考になるかと思います。

AWS CLI S3 Configurationを試したら想定以上にaws s3 syncが速くなった話

方法

具体的な方法として、下記の3つをご紹介します。

  • マルチパートアップロードの並列度を変える
  • 帯域制御ツールを使う
  • ファイルを分割して個別にアップロードし、S3上で結合

以下に、それぞれについて説明していきたいと思います。

マルチパートアップロードの並列度の変更

帯域制御とは直接関連がないですが、マルチパートアップロードの並列度を変えることで、少しながら帯域を抑えることが出来ます。 追加でインストールしなければいけないものが無く、お手軽に試すことが出来ます。

これは「AWS CLI S3 Configuration」を利用することで設定可能です。
詳細は下記のドキュメントが参考になると思います。aws help s3-configでも確認することができます。

AWS CLI S3 Configuration — AWS CLI 1.11.84 Command Reference

デフォルトの状態でaws s3 cpコマンドを実行すると、利用帯域は下記のようになりました。

04-default

次に、並列処理をしないように変更してみます。通常は並列化して効率的にアップロードするものですが、逆に並列度を下げることで帯域を抑えることができるという訳です。
設定変更は、aws configure setコマンドで行います。 下記では、並列度を「1」にしていますが、デフォルトは「10」です。

$ aws configure set default.s3.max_concurrent_requests 1

この状態でaws s3 cpコマンドを実行すると下記のようになりました。
左側の部分が先程実施したデフォルトの状態で、右側の部分が並列度を1にした状態です。並列度を下げた方が送信の利用帯域が下がっています。

06-concurrent1

設定を少し変えるだけで利用帯域を少し抑えることが出来ました。
ただし、お手軽に実施できる代わりに、帯域について厳密な制御はできません。そういう場合は他の選択肢を選ぶことになります。

尚、他の設定変更の場合もaws configure setコマンドを使います。コマンドの実行結果はそのまま~/.aws/configに反映されます。
勿論、直接コンフィグを編集しても構いませんが、コマンドから設定するのが楽かと思います。

次に帯域制御ツールを使う方法を試してみます。

様々なツールがあると思いますが、今回は、Mac向けとして「Network Link Conditioner」を使ってみます。これは、Xcodeに標準でついてくる帯域制限アプリです。

下記でインストール方法や使い方をご紹介しているので参考にして頂ければと思います。

[iOS][OS X] 「Network Link Conditioner」を使って iOS 端末または Mac の通信速度をシミュレートする

尚、macOS Sierraの場合は、"Additional Tools for Xcode 8.1"に含まれている最新バージョンの「Network Link Conditioner」を使います。 「Hardware IO Tools for Xcode 7.3」にある「Network Link Conditioner」では、帯域制御が効きませんでした。

osx - Network Link Conditioner not working on macOS Sierra - Stack Overflow

インストールが出来たら、実際に帯域制限をしてみます。今回は「Uplink 5mbps / Downlink 1mbps」というカスタムプロファイルを作成して適用しました。
確かに送信の帯域が、ぐっと下がっているのが分かります。

08-nwlinkconditioner

上記は「max_concurrent_requests = 1」 の状態で試しましたが、デフォルトの状態で試しても利用帯域に変化はありませんでした。そりゃそうですよね。

09-cuhnk50MB-concurrent8

Linuxの場合は、trickleコマンドなどが便利に使えるようですが、今回は割愛します。
また、Windowsの場合は「ポリシー ベースの QoS の概要」 で特定プロセスや全体に対してアウトバウンドのトラフィックを制限することが出来ます。こちらも今回は割愛させて頂きます。

ポリシー ベースのサービスの品質 (QoS) の構成

ファイル分割・アップロード・結合

3つ目は、帯域制限というよりは「帯域を多く使わない方法」になります。
概要を先に書くと「帯域を多く使わないサイズにファイルを分割・アップロードしてから結合する」というものです。

今回は「splitfile」というファイルをアップロードしてみることにします。
このファイルのサイズは1GB(1024MB)として、3つに分割しました。下記のsplitコマンドで、splitfileを「part.〜」というファイルに500MB単位で分割します。

$ split -b 500m splitfile part.

分割できたら下記のコマンドでマルチパートアップロードを開始します。開始と書いていますが、まだアップロード自体は行われていません。コマンドを実行すると「UploadId」を取得出来ます。この「UploadId」を結合処理まで利用します。

コマンドの実行時は--bucketでアップロードするバケット名を指定します。また--keyで分割対象のファイル名を指定します。

$ aws s3api create-multipart-upload --bucket s3bandtest --key splitfile

{
    "Bucket": "s3bandtest",
    "UploadId": "xxxxxxxxxxxxxxxxxxxxxxx",
    "Key": "splitfile"
}

次に実際に分割したファイルをアップロードしていきます。今回は3つに分割されたファイルをそれぞれアップロードします。
--upload-idで先程の「UploadId」を指定します。--bodyで分割したファイルを指定します。

$ aws s3api upload-part \
--bucket s3bandtest \
--key splitfile  \
--upload-id <先程のUploadId> \
--part-number 1 \
--body part.aa

コマンドを実行すると下記のようにETag情報が返ってきます。この情報は最後にファイルを結合する時に必要になるので控えておきます。

{
    "ETag": "\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\""
}

残りの2ファイルについても同様です。

$ aws s3api upload-part \
--bucket s3bandtest \
--key splitfile  \
--upload-id <先程のUploadId> \
--part-number 2 \
--body part.ab

{
    "ETag": "\"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\""
}

$ aws s3api upload-part \
--bucket s3bandtest \
--key splitfile  \
--upload-id <先程のUploadId> \
--part-number 3 \
--body part.ac

{
    "ETag": "\"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\""
}

ここで補足ですが、このaws s3api upload-partコマンド時の実行時は、分割したファイルがそのままアップロードされます。 今回は、1GBを3つに分割したので、最初の2つのファイルのサイズは500MBです。帯域制限は何もしていないので、この500MBのファイルがそのままアップロードされることで、帯域を最大限利用して転送しています。

下記の画像は500MBのファイルアップロードした時のトラフィックの状況です。32.9MB/sでアウトバウンドのトラフィックが発生していることが分かります。
これでは、回線を圧迫してしまう可能性がある為、ファイルを分割する際は分割するサイズに注意しましょう。

07-splitupload

分割したファイルを全てアップロード出来たら、下記のようなJSONファイルを作成します。このファイルの情報を元に分割ファイルが結合されます。
尚、PartNumberに指定する数字は、先程のaws s3api upload-partコマンドで指定したものと合わせる必要があります。

今回は下記の内容で「upload.json」というファイルを作成しました。

{
  "Parts": [
    {
        "ETag": "\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"",
        "PartNumber": 1
    },
    {
        "ETag": "\"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\"",
        "PartNumber": 2
    },
    {
        "ETag": "\"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\"",
        "PartNumber": 3
    }
    ]
}

最後にaws s3api complete-multipart-uploadコマンドでファイルを結合します。--multipart-uploadで先程作成したJSONファイルを指定します。

$ aws s3api complete-multipart-upload \
--bucket s3bandtest \
--key splitfile \
--multipart-upload file://~/temp/testdata/upload.json \
--upload-id <先程のUploadId>

完了したら以下のような結果が返ってきます。これで元の「splitfile」がS3上に作成されて確認することができます。

{
    "ETag": "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"",
    "Bucket": "s3bandtest",
    "Location": "https://s3bandtest.s3.amazonaws.com/splitfile",
    "Key": "splitfile"
}

結論

  • とりあえず簡単に帯域を抑えたい場合は、並列度を下げてみるのがいいかと思います。
  • 厳密に帯域を抑えたい、制御したい場合は「Network Link Conditioner」のような専用のツールを使うのがよいでしょう。
  • ファイル分割は作業量が多いですが、分割サイズをきちんと検討した上で利用すれば、有用だと思います。

最後に

回線があまり太くない場合や、オフィスの帯域を圧迫して他の業務に影響を出したくないといったケースは多々あると思います。
そんな時に、S3に大量・大容量のデータをアップロードする時は、これらの方法を活用して頂ければと思います。

以上です。