多くのチームと同様に、現在自宅で働いている人がいます。これらのリモートクライアントはファイアウォールの背後にあり(制御できない)、静的IPアドレスはありません。つまり、SSHを介してこれらのクライアントに直接アクセスすることはできません。ただし、クライアントはSSHを介してサーバーにアクセスできます。 (他の理由で強化されたSSHは、すでにすべてのクライアントとサーバーに設定されています。)
私たちの要件は、各クライアントで同期されたファイルセット(複数のディレクトリにあります)を維持し、効率的に実行することです。私は各クライアントがrsync
NN秒ごとにコマンドを実行しないように努めています。サーバーの関連ファイルが変更された場合は、クライアントに通知することをお勧めします。
また、私たちの実装では、SSH、rsync、inotifyツール、bashまたはPython(およびawk、cutなどのツール)のみを使用できます。特に、NextCloud、OwnCloud、SyncThing、SeaFileなどは使用できません。
サーバーで開いている唯一のリスニングポートはSSH用であり、保守または更新したい唯一のパッケージはデプロイメントリポジトリのコアパッケージです。
アイデアは、各クライアントにサーバーの逆SSHトンネルを確立させることです。これにより、サーバーは次のスクリプトを実行できます。
#!/bin/bash
while true; do
inotifywait -r -e modify,attrib,close_write,move,create,delete /path/to/source/folder
for port_user in "$(netstat -Wpet | grep "ESTABLISHED" | grep 'localhost.localdomain:' | grep 'sshd:' | cut -d ':' -f2-3 | cut -d ' ' -f1,4)"; do
uport=$(echo $port_user | cut -d ' ' -f1)
uu=$(echo $port_user | cut -d ' ' -f2)
sudo -u $uu rsync -avz -e "ssh -p $uport -i /home/$uu/.ssh/id_ed25519" /path/to/source/folder $uu@localhost:/path/to/destination/folder
done
done
フィードバックを探しています。まず、上記のbashスクリプトを改善または整理できますか?cut
たとえば、あまりにも多くのステートメントを使用する必要があるようです。
編集:以下はroaimaの素晴らしい質問とコメントへの答えです。
ファイルサーバーのスクリプトはrootとして実行されます。クライアントのスクリプトはそうではありません。
&7. これは私の netstat コマンドの出力例です。
netstat -Wpetl
tcp 0 0 localhost.localdomain:22222 0.0.0.0:* LISTEN myuser 42137 8381/sshd: myuser
「競争条件があります...」 - ありがとう。今はこの問題を無視してみましょう。
「質問がなくなりました…」 - もう一度ありがとう。私はこれがクライアント側で簡単に解決できると思います。これは、ユーザーがログインしたときに実行されるクライアントスクリプトです。
#!/bin/bash
synchost=sync.example.com
syncpath="path/to/sync/folder"
uu=$(logname)
uport=222222 #hard code per client device
# initial sync upon connecting:
rsync -avzz -e "ssh -i /home/$uu/.ssh/id_ed25519" /"$syncpath"/ $uu@$synchost:/"$syncpath"
# loop until script is stopped when user logs out
while true; do
inotifywait -r -e modify,attrib,close_write,move,create,delete /"$syncpath"
rsync -avzz -e "ssh -i /home/$uu/.ssh/id_ed25519" /"$syncpath"/ $uu@$synchost:/"$syncpath"
done
ユーザーがいつでも実行して強制的に同期できるオンデマンドスクリプトもあります。ループのない上記のスクリプトは次のとおりですwhile
。
- これはサーバースクリプトの現在のバージョンです:
syncpath="path/to/sync/folder"
while true; do
inotifywait -r -e modify,attrib,close_write,move,create,delete /"$syncpath"
netstat -Wpetl | grep "LISTEN" | grep 'localhost.localdomain:' | grep 'sshd:' | while read proto rq sq local remote state uu inode prog
do
uport=${local#*:}
sudo -u $uu rsync -avzz -e "ssh -p $uport -i /home/$uu/.ssh/id_ed25519" /"$syncpath"/ $uu@localhost:/"$syncpath"
done
done
- 「転送中に接続が切断された場合は、他の人をブロックしないように、クライアントの各ssh / rsyncにタイムアウトを設定することを検討する必要があります。」
これは素晴らしいアドバイスです。ただし、一部の有効なrsync
更新は、実行に平均よりもはるかに長くかかることがあります。一般的で必要な長いrsync
更新を処理しながら、更新中にクライアントが切断されるまれなケースを処理する適切な方法を提案できますか?
私は非常に簡単な方法でタイムアウトと(ほとんど)競争条件を解決するアイデアを持っています。まず、各ユーザーログイン時に、最初のクライアント側同期は長期実行更新ジョブを処理する必要があります。したがって、サーバー側の同期作業時間は、右側の尾がそれほど長くはありません。タイムアウトパラメータとスリープ時間を最適化し、次の方法を使用できます。
syncpath="path/to/sync/folder"
while true; do
inotifywait -r -e modify,attrib,close_write,move,create,delete /"$syncpath"
netstat -Wpetl | grep "LISTEN" | grep 'localhost.localdomain:' | grep 'sshd:' | while read proto rq sq local remote state uu inode prog
do
uport=${local#*:}
timeout 300s sudo -u $uu rsync -avzz -e "ssh -p $uport -i /home/$uu/.ssh/id_ed25519" /"$syncpath"/ $uu@localhost:/"$syncpath"
done
sleep 90
netstat -Wpetl | grep "LISTEN" | grep 'localhost.localdomain:' | grep 'sshd:' | while read proto rq sq local remote state uu inode prog
do
uport=${local#*:}
timeout 900s sudo -u $uu rsync -avzz -e "ssh -p $uport -i /home/$uu/.ssh/id_ed25519" /"$syncpath"/ $uu@localhost:/"$syncpath"
done
done
最後のコメントです。コマンドで表されるパラメーターはrsync
最終パラメーターではありません。ご提案いただきありがとうございます。しかし、コマンドのすべてのオプションを評価するのに時間がかかりますrsync
。
答え1
いくつかの考え
あなたのスクリプトは(おそらく)rootとして実行されているので、動作し、
netstat -Wpet
操作sudo -u ${user}
が簡単になります。たとえば、リバース接続を使用すると、ライン
ssh -R 20202:localhost:22 centralserver
からポートとユーザーの組み合わせを取得できませんnetstat | grep | grep | cut ...
。netstat -Wpet | grep "ESTABLISHED" | grep sshd: tcp 0 36 centralserver:ssh client:37226 ESTABLISHED root 238622975 15198/sshd: roaima
そのため、スクリプトで可能な変更を効果的にテストすることはできません。ここで何を見ると予想しますか?
競合状態があるため、
inotifywait
完了後に2番目のファイルが変更されると、他のファイルが変更される前にそのファイルがすべてのターゲットシステムに伝播されない可能性があります。この問題に対する解決策は、単一インスタンスからイベントを受信し、
inotifywait
各イベントに対して一連の転送を実行することです。rsync
ただし、更新の頻度によっては、クライアントのネットワーク接続が飽和する可能性があります。一連の変更後に接続するクライアントは、次回までその変更を受け取らないため、欠落している問題があります。次ファイルが変更されます。アップデートが非常に重要な場合は、接続したらすぐにクライアントのコピーを更新する方法を検討する必要があります。
ssh
rsync
転送中に接続が切断された場合は、他の人をブロックしないようにクライアント固有のタイムアウトを考慮する必要があります。このようなコードスニペットがある場合は、ステートメントを変数操作(および演算子)
bash
に置き換えることができます。cut
%
#
/
while read -r proto recvq sendq localaddrport foreignaddrport state user inode pidprogram name do localaddr="${localaddrport%:*}" localport="${localaddrport#*:}" foreignaddr="${foreignaddrport%:*}" foreignport="${foreignaddrport#*:}" pid="${pidprogram%/*}" program="${pidprogram#*/}"; program="${program%:}" echo "Foreign address = $foreignaddr and port = $foreignport" echo "PID = $pid, program = $program" echo "Name = $name" done < <(netstat -Wpet | grep '\<localhost.localdomain:.*\<ESTABLISHED\>.*/sshd:')
netstat
コマンドの予想出力を表示できる場合は、awk
ライン処理を簡素化するために使用できます。