ansible copyモジュールが遅い問題

# 概要
これまで手運用でscpで実行していた処理を、Ansible化するに伴い``copy``モジュールでの実行に変更したところ、やたら遅い。時間的にいえば**5倍弱くらいかかる**。(10GBくらいのデータと28GBくらいのデータをアップロードする必要があるのだが、それぞれ15分、45分もかかる)。ネットにも``ansible copy very slow``などという記事が結構ある。

大抵の記事では[``synchronize``](https://docs.ansible.com/ansible/latest/modules/synchronize_module.html)モジュールを代替として使えという提案になっている。
実際に置き換えてどう変わるのか実験する。


# syncronizeモジュールのリファレンス訳
synchronizeはplaybookで一般的なタスクをすばやく簡単に実行するためのrsyncのラッパーである。実行すると、管理サーバ上で実行される。もちろん、commandモジュールを利用して自身でrsyncコマンドを呼び出すことも可能だが、公平な数の定型オプションとホストファクトも追加する必要があります。同期化はrsyncのフルパワーへのアクセスを提供することを意図していませんが、最も一般的な呼び出しをより簡単に実装します。あなたのユースケースに応じて、コマンドやシェルを使って直接rsyncを呼び出す必要があるかもしれません。

## パラメータ

|パラメータ|選択肢|コメント|
|---|---|---|
|archive|yes/no (デフォルト:yes)|rsync archiveフラグ。再帰フラグ、リンクフラグ、パーミッションフラグ、時間フラグ、オーナーフラグ、グループフラグ、-Dフラグを有効にする|
|checksum |yes/no (デフォルト：no)|mod-timeとサイズではなくchecksumに基づいてスキップする。|
|compress |yes/no (デフォルト：yes)|転送中にファイルデータを圧縮する。問題が発生しない限りこのオプションを有効にしておいてください。|
|copy_links|yes/no (デフォルト：no)|シンボリックリンクが指す本体がコピーされるときにシンボリックリンクをコピーする|
|delete|yes/no (デフォルト：no)|転送後にsrcに存在しない場合dest内のファイルを削除する。このオプションはrecursive=yesである必要がある|
|dest(必須)|yes/no (デフォルト：no)|同期元ホストから同期される管理対象ホスト上のパス。絶対パスでも相対パスでもOK。|
|dest_port|よくわからん|管理対象ホストのSSHのポート番号。Ansible2.0以前ではこの値より``ansible_ssh_port``インベントリ変数が優先された|
|dirs|yes/no (デフォルト：no)|再帰なしでディレクトリを転送する|
|existing_only|yes/no (デフォルト：no)|同期先に存在するファイルのみ同期|
|group|yes/no (デフォルト：no)|グループを同期する|
|link_dest(ver2.5から)||翻訳できず|
|links|yes/no (デフォルト：archiveパラメータと同じ)|シンボリックリンクとしてシンボリックリンクをコピーする|
|mode|pull/push(デフォルトpush)|同期の方向を指定pushではローカルホストorデリゲートがsrc側、pullではリモートホストがsrc側|
|owner|yes/no(デフォルト：archiveパラメータと同じ)|オーナーを同期する(スーパーユーザー時のみ)|
|partial|yes/no (デフォルト:no)|
rsyncに、残りのファイルの後続の転送をはるかに高速化する部分ファイルを保持するように指示します。|
|perms|yes/no(デフォルト：archiveパラメータと同じ)|パーミッションを同期する|
|private_key||SSHベースのrsync接続に使用する秘密鍵を指定する|
|recursive|yes/no(デフォルト：archiveパラメータと同じ)|ディレクトリ内を再帰的に同期する|
|rsyc_opts||配列で特別な追加オプションを渡す|
|rsync_path||リモートホストで実行するrsyncコマンドを指定する。rsync man pageの``--rsync-path``を参照|
|rsync_timeout|デフォルト：0|rsyncコマンドの--timeoutを秒で指定する |
|set_remote_user|デフォルト：yes|リモートのパスのための``user@``を指定する。インベントリユーザと一致しないホストのリモートユーザを定義するカスタムssh設定がある場合は、このパラメータをnoに設定する必要があります。|
|src(必須)||同期先に同期される同期元ホスト上のパス。絶対パスでも相対パスでもOK。|
|times|yes/no(デフォルト：archiveパラメータと同じ)|タイムスタンプを同期する|
|use_ssh_args(ver2.0より)|yes/no(デフォルト：no)|ansible.cfgでのssh_argsの値を使う |
|verify_host|yes/no(デフォルト：no)|同期先ホストの鍵を検証する|


## 注意
- rsyncが管理サーバ、管理対象サーバともにインストールされていなければならない
- synchronizeモジュールにとって、"local host"は同期元、"destination host"は同期先のホスト。
- "local host"はdelegate_toパラメータによって異なるホストに書き換えることができる。これにより一台のリモートマシンで2台のリモートホスト間のコピーができる。
- syncronizeのモジュールのsrcパラメータに対するユーザーとパーミッションはlocal hostでAnsibleタスクを実行するユーザーのものである。(またはdelegete_toを使うならばdelegate_toホストのremote_userのもの)
- syncronizeのモジュールのdestパラメータに対するユーザーとパーミッションは、become=yesとなっているならば、同期先のremote_userまたはbecome_userのものである。
- 現在、synchronizeモジュールはパスワードなしのsudoでの権限昇格に限定されている。これはrsync自身がリモートマシンに接続していて、rsyncにsudoの認証情報を渡す方法を提供されていないためです。
- 現時点では、同期方式（ssh、paramiko、local、およびdocker）は、これらの接続タイプに対して決定されているため、同期をサポートする接続タイプはごくわずかです。 rsync自体が接続を行い、rsyncがパスワードを接続に渡す方法を提供しないため、これらの接続にはパスワードが必要ではないことに注意してください。
- ``dest=~/x``は``~<remote_user>/x``というパスになる。たとえsudoを使っているとしても。
- 冗長な出力で user/host/パスが予想されるものであることを検証しろ。
など

# 置き換え実験
``rsync``コマンドがインストールされていないと使えないと公式に書いてあるので、インストールされていることが前提。

## 元々のcopyモジュール部分
    - name: copyモジュールでのデータアップロード
      copy:
        src: <管理サーバ上のアップロード対象ファイルのパス>
        dest: <管理対象サーバ上のアップロード先のパス>
      when: not ansible_check_mode


## 置き換えその1
    - name: synchronizeモジュールでのデータアップロード
      synchronize:
        src: <管理サーバ上のアップロード対象ファイルのパス>
        dest: <管理対象サーバ上のアップロード先のパス>
      when: not ansible_check_mode

→エラーメッセージ

    failed": true, "msg": "sudo: sudo を実行するには tty がなければいけません。すみません\nrsync: connection unexpectedly closed (0 bytes received so far) [sender]\nrsync error: error in rsync protocol data stream (code 12) at io.c(600) [sender=3.0.6]\n", "rc": 12}


管理対象サーバの/etc/suodersの「Defaults    requiretty」をコメントアウトしておかないとだめだそう。
コメントアウトするとうまくいったけど、すっごくおそかった。copyモジュールで15分かかるデータファイルでsyncronizeモジュールで14分。誤差レベル。
これはきっとデフォルトで圧縮を噛ませているからでは？ということで、再度実験。


## 置き換えその2

    - name: synchronizeモジュールでのデータアップロード
      synchronize:
        src: <管理サーバ上のアップロード対象ファイルのパス>
        dest: <管理対象サーバ上のアップロード先のパス>
        compress: no
      when: not ansible_check_mode


爆速！3分で終わった。圧縮しないほうが良さそう。でも/etc/sudoersの「Defaults    requiretty」をコメントアウトがだるい・・・インフラ担当に必要性を説明して作業依頼出してとか・・・めんどくせぇ