構成
WindowsServerでファイル共有を2台の物理サーバー(Active系とHot-Standby系)で行う場合です。
■物理サーバー2台
■ディスク 4TB×4本等
■RAID5またはRAID6等
■Windows Server IoT 2022 for Storage等
■参加済みドメイン:onpless.com

■現代となっては古典的な構成かもしれませんが、構築コストの関係でこのように運用している中小企業は現在も多いのではと思っています。
■CALを必要としないWindows Server IoT for Storage(旧Windows Storage Server)を採用することがも多いかと思います。既にデバイスCALを積んでいればCALに応じたWindows Serverを使えばよいかと思いますが、BaffaloやIODATAなどですでにOS搭載のものが安価で販売されていますのでそちらを利用するのが良いかと思います。
■CNAMEを使ったエイリアスアクセスは現在非推奨ですし、エイリアスで名前解決が行えないクライアントも少なくないですが、この構成でサーバー再起動を必要とせずに系の切替を行うにはエイリアスアクセスしかないかと思っています。エイリアスアクセスについてはこちらの記事が大変参考になりました。
DFSレプリケーション(DFSR)を利用したファイル同期
GUIでDFSRを設定する
設定方法は以下の記事が大変参考になりました。
ファイルサーバー : DFS レプリケーションを設定
https://www.server-world.info/query?os=Windows_Server_2019&p=smb&f=13
ステージング容量のサイジングについて
DFSRでは並行して最大32ファイルをステージング領域に展開し、
各ノードに対してレプリケーションデータを配布します。
本記事では2台構成ですが、DFSRは複数台でレプリケーションを前提に設計されています。
充分なパフォーマンス及びレプリケーショントラブルを回避するため十分大きなサイズを用意します。
一般的な利用だと数十GBから数百GBになると思います。
ステージング容量のサイズの算出方法は以下がわかり易いです。
レプリケートされたフォルダーに必要な最小ステージング領域 DFSR を決定する方法この記事は、DFSR が正常に機能するために必要な最小ステージング領域を計算する方法に関するクイック リファレンス ガイドです。
私の2週間待っても初期同期が完了せずに断念しました。
数百GBであればいける印象です。
ファイル共有ごとにDFSRを設定するとあるいはうまくいくかもしれませんが、1ファイル共有で数TBや数十TBディスク容量を使用している場合もあるかと思いますのでなんらか工夫は必要と思います。
コマンドでDFSRを設定する
プライマリノードの変更など細かい設定はGUIではできなさそうでしたのでコマンドで行いました。
プライマリノードは初期同期でのみ影響するため、変える必要は実際ないかもしれません。
■DFSRの公式ドキュメント
コマンドに関してはこちらを参考にしました。
DFSR Module | Microsoft Learn
Robocopyを利用したミラーリング
DFSRの場合、容量が大きいと初期同期に時間がかかりすぎてしまうことと、
トラブルシュートの難易度も高いように思います。
トラブルの際にDFSR再構築となるとサーバー再起動が必要となってしまいます。
Robocopyであれば再起動が必要になることがまずないのとわかり易いので扱いやすいです。
【Robocopyのデメリット】
■インターフェースの帯域を使うこと
■mirrorする方向を間違えると大変なことになること
■リアルタイム同期が出来ないなこと
【Robocpyコマンド作成時のポイント】
①メインのファイルサーバーにない場合はレプリカからも削除すること
⇒/MIRオプションを利用すること
②同一ファイルはスキップするようにすること(全てをコピーすると時間がかかり過ぎる)
⇒/MIRオプションを利用すること
③コピーの再試行は行わず、待機もしないこと(再試行と待機をしてると時間がかかり過ぎる)
⇒/R:0 /W:0
④権限情報もコピーすること
⇒/COPYALL オプションですが、MIRオプションにて権限コピーされるとのことなので、不要みたいです。
上記をもとに作成したrobocopyのオプションは以下です。
/MIR /COPYALL /R:0 /W:0 /MT:4
ネットワーク経由で全てコピーしていると数TBでも数日かかるうえパフォーマンスも低下します。
ネットワークを経由せずともディスク書込み速度にて頭打ちとなります。
実際に利用する場合は差分のみをコピーします。
実行間隔と差分ファイルの量にもよります。実行間隔は短い方が実行時間は短いです。
30分に1度の実行でスケジュールした場合だと10分前後で数TBのMirrorが完了しました。
ファイル共有ごとに実行せず、然るべき権限にてD$やC$でMIRしたほうがコードの修正がなくて良いかと思います。
レプリカではなく、バックアップ取得用にRobocopyを実行する場合も毎回全てはコピーせず、一度ファイルをコピーしたのち、あとは前回フルバックアップを流量し差分をコピーするようにしないと時間がかかり過ぎて使えません。
特にmirrorの方向については注意しないといけない為、スクリプト側でエイリアスのCNAMEで指示されたサーバーから他方のサーバーにコピーするようにしています。
以下のようなPowerShellスクリプトを作成してみました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
############################################################################## #パラメータ #------------------------------------------------- $ServerA = "fsv-a" #どちらがメインかは自動判定 $ServerB = "fsv-b" #どちらがメインかは自動判定 #------------------------------------------------- $Alias = "fsv"#ファイルサーバーのエイリアス名 $ZoneName="onpless.com"#ドメイン名 #------------------------------------------------- $ShareName="D`$\share"#サーバーの共有フォルダ名 #------------------------------------------------- ############################################################################## #ログ出力 #------------------------------------------------- # 現在のスクリプトのディレクトリを取得 $currentDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path # ひとつ上のディレクトリを取得 $parentDirectory = Split-Path -Parent $currentDirectory $LogFileDirectory = $currentDirectory # ログファイルのパスを指定 $logFile = Join-Path -Path $LogFileDirectory -ChildPath "logfile.txt" $message = "------------------------------------------------------------" $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" $message = "コピースクリプトを開始します・・・" $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" ############################################################################## #変数定義 #------------------------------------------------- $ServerAName = $ServerA + "." + $ZoneName $ServerBName = $ServerB + "." + $ZoneName $AliasName = $Alias + "." + $ZoneName $AliasFQDN = $Alias + "." + $ZoneName + "." $ThisServerName = $env:COMPUTERNAME +"." + $ZoneName $ExecuteFlg=$false#ツール実行フラグ ############################################################################## #ファイルサーバーの死活確認 #------------------------------------------------- Write-Host "'$ServerA'の稼働状況を確認しています... " -NoNewline $pingResultA = Test-Connection -ComputerName $ServerAName -Count 5 -Quiet Write-Host "→" -$pingResultA if (-not $pingResultA) { # ここでアラートを送信する処理を記述します $message = "Host '$ServerAName' と疎通ができない為、処理を中止します。" Write-Host $message $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" exit } else { Write-Host "'$ServerAName'は稼働中です。" Write-Host "'$ServerB'の稼働状況を確認しています... " -NoNewline $pingResultB = Test-Connection -ComputerName $ServerBName -Count 5 -Quiet Write-Host "→" -$pingResultA if (-not $pingResultB) { # ここでアラートを送信する処理を記述します $message = "Host '$ServerBName' と疎通ができない為、処理を中止します。" Write-Host $message $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" exit } else { Write-Host "'$ServerBName'は稼働中です。" $ExecuteFlg=$ture } } ############################################################################## #アクティブなファイルサーバー検出 #------------------------------------------------- $DNSRecords=Resolve-DnsName $AliasFQDN $CnameRecord="" foreach ($DNSRecord in $DNSRecords) { if($DNSRecord.type -eq 'CNAME'){ $CnameRecord=$DNSRecord.namehost } } if($CnameRecord -eq ""){ $message = "名前解決に失敗した為、処理を中止します。" Write-Host $message $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" exit } ############################################################################## #コピー元とコピー先を決定します。 #------------------------------------------------- if($CnameRecord -eq $ServerAName){ $RepSrcServerName = $ServerAName $RepDstServerName = $ServerBName }elseif($CnameRecord -eq $ServerBName){ $RepSrcServerName = $ServerBName $RepDstServerName = $ServerAName }else{ $message = "DNS設定またはスクリプトの設定が不正です。処理を中止します。" Write-Host $message $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" exit } Write-Host "====================================================================" Write-Host "ファイルコピー方向:メイン'$RepSrcServerName'⇒レプリカ'$RepDstServerName'" Write-Host "====================================================================" #if($ThisServerName -eq $RepSrcServerName){ # $message = "コピー操作はレプリカで稼働しているサーバーにて実行します。処理を中止します。" # write-host $message # $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # # タイムスタンプ付きメッセージをログファイルに書き込む # Add-Content -Path $logFile -Value "$timestamp - $message" # exit #} $message = "ファイルコピー方向:メイン'$RepSrcServerName'⇒レプリカ'$RepDstServerName'" $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" $CmdString = "robocopy " + "\\$RepSrcServerName\$ShareName" + " \\$RepDstServerName\$ShareName " + "/MIR /COPYALL /R:0 /W:0 /MT:4" write-host "コマンドを実行中・・・" Write-Host "====================================================================" Invoke-Expression $CmdString $message = "コピーが完了しました" $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # タイムスタンプ付きメッセージをログファイルに書き込む Add-Content -Path $logFile -Value "$timestamp - $message" |
まとめ
DFSRでレプリケーションするのが比較的スマートですが、
イベントログを追いかけて、DFSRデータベースをリセットして再同期してとあれこれ思い悩みましたので、トラブルシュートの難易度が高いように思いました。
HCIやクラスタによるHA構成が導入できればそちらのほうがよいかと思いました。
物理サーバーは長年使えばいつか壊れるのでファイルサーバーの冗長化は必須というなかで、NutanixやSynologyが高いとか使いにくいとかで採用できないのであれば、
「ファイルサーバーでDFSR使うくらいならRobocopyでいいや」というのが個人の見解です。




コメント