Workgroup and Multi-domain clusters in Windows Server 2016 を見ていたら、コメント欄に
というようなやり取りがありました。
ワークグループクラスター上の AlwaysOn がサポートされるのねということで組んでみました。
以下のような構成で構築しています。
組んだ際のポイントだけをつらつらと。
今回は、Windows Server 2016 TP3 と SQL Server 2016 CTP 2.4 を使用しています。
どちらもプレビューですので RTM した際には状況が変わっているかもしれません。
今回は SQL Server の通信については、内部 IP を使用して設定をしていますので。使用する仮想マシンの内部 IP については固定化しておくことを、推奨します。
Contents
WSFC の構築
こちらは一般的なワークグループクラスターと Azure 上の WSFC の構築と作業は変わりません。
ワークグループクラスターを使用する場合、DNS サフィックスの設定が必要となります。
Azure の場合、DHCP から「~internal.cloudapp.net」の DNS サフィックスが割り当てられていますが、ワークグループクラスターを構築する場合に必要となる DNS サフィックスは、システムのプロパティから設定する方になりますので、以下を設定しておきます。
ワークグループクラスターのどちらかのノードにログインして、以下のスクリプトで設定をして、各ノードを再起動します。
$cred = Get-Credential $Node = @("SQL2016-01","SQL2016-02") $Script = { $DNSSuffix = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE).DNSDomain Set-ItemProperty registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters -Name "NV Domain" -Value $DNSSuffix Register-DnsClient } Invoke-Command -ComputerName $Node -ScriptBlock $script -Credential $cred -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck) Invoke-Command -ComputerName $Node -ScriptBlock {Restart-Computer -Force} -Credential $cred -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)
TP3 のワークグループクラスターでは、DNS サフィックスが設定されていないと、コンピューター名リソースをオンラインにすることができませんので、何かしら設定をしておく必要があります。
今回は DHCP で配布されてきたものを設定しています。
本来は使用している DNS に登録が行える DNS サフィックスを指定するのがベストなのですが、現状、Azure には自由に使用ができるプライベート DNS がありませんので、コンピューター名アカウントの DNS の確認チェックについては NG のままになってしまうかと。今回は検証のため、ひとまず無視します。
# 内部に何かしら DNS を立てた場合は DNS の確認は行うことができますので、本来はきちんと DNS 周りを見直した方がよいです。
DNS サフィックスの登録が終わったら WSFC を作成します。
TP3 のワークグループクラスターは、PowerShell で操作をする必要がありますので、WSFC の構築についても PowerShell を使う必要があります。
最初に、各ノードに WSFC の機能を追加します。
#? これは GUI でもできます。
$cred = Get-Credential $Node = @("SQL2016-01","SQL2016-02") $script = { if((Get-WindowsFeature -Name Failover-Clustering).InstallState -ne "Installed"){ Install-WindowsFeature -Name Failover-Clustering -IncludeManagementTools }else{ Write-Output "Failover-Clustering Feature has Installed" } } Invoke-Command -ComputerName $Node -ScriptBlock $script -Credential $cred -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)
次に、管理アクセスポイントとして DNS を設定するようにして、WSFC を構築します。
$Node = @("SQL2016-01","SQL2016-02") New-Cluster -Name WSFC-CNO -node $Node -AdministrativeAccessPoint Dns
構築が終わったら「Get-ClusterNode」で両ノードが「Up」になっているかを確認します。
現状、TP3 を使用して、Azure 上に構築した場合、片側が「Down」になっている可能性が高いかと思います。
その場合は、以下を実行します。
# 各ノードで実行して Up になっているノードを確認 Get-ClusterNode # Up になっていたノードで実行 Stop-Cluster -Force # Down になっていたノードで実行 Start-Cluster # 必要に応じて実行 Start-ClusterNode "<Up になっていたノード>"
最初の起動だけ、うまくいっていない感じなので、両ノードが正常に起動した状態にできることをこの状態で確認しておきます。
げtこのままでは 2 ノード構成のためどちらか片方のノードで障害が発生したらクラスターが停止してしまいますので、奇数の投票数を保持するため、Azure ストレージを使用して、クラウドウィットネス (Cloud Witness) を設定しておきます。
Set-ClusterQuorum -CloudWitness -AccountName <ストレージアカウント名> -AccessKey <ストレージアカウントキー>
ここまで頑張ると、以下までは持っていけるかと思います。
Azure 上に WSFC を構築した場合、DHCP で割り当てられた IP アドレスはリソースをオンラインにしているノードのものになってしまうため、これの解決を行います。
# AWS も同じですが。
Get-ClusterResource "Cluster IP Address" | Set-ClusterParameter -Multiple @{Address="169.254.1.1";SubnetMask="255.255.255.255";OverrideAddressMatch=1;EnableDhcp=0} Start-ClusterGroup -Name "Cluster Group"
これで、一通りのリソースとノードがオンラインの状態となります。
デフォルトで使用できる Azure の内部 DNS では、動的な DNS レコードの登録ができないようなので、コンピューター名のリソースの「StatusDNS」については「0」にすることができないのが現状の問題ではありますが。
SQL Server の設定
SQL Server 2016 CTP 2.4 をインストールして、以下の作業を実施しておきます。
- SQL Server 構成マネージャーからAlwaysOn の有効化
- TCP1433/TCP5022/TCP59999 の Windows Firewall の許可
SQL Server の手順を書くと長くなるのでポイントだけ書いておきたいと思います。
ワークグループ環境の AlwaysOn では証明書を使用したエンドポイントにより、レプリケーションの通信を行う必要があります。
# サービスアカウントをミラーアカウントにしても行けるかもしれませんが、証明書を使用した方がよいかなと。
以下のような形で、証明書を使用したエンドポイントを作成します。
$cred = Get-Credential $Node = @("SQL2016-01","SQL2016-02") # 全ノードでマスターキーとエンドポイント用のユーザーを作成 $Node | %{Invoke-Sqlcmd -ServerInstance $_ -Query "CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MASTER KEY Passw0rd'"} $Node | %{Invoke-Sqlcmd -ServerInstance $_ -Query "CREATE LOGIN AlwaysOnEndpoint WITH PASSWORD = 'AlwaysOn Passw0rd',CHECK_EXPIRATION=ON;CREATE USER AlwaysOnEndpoint FOR LOGIN AlwaysOnEndpoint"} # 一台目でエンドポイントで使用する証明書を作成 Invoke-Sqlcmd -ServerInstance $Node[0] -Query "CREATE CERTIFICATE AlwaysOnEndpoint_Cert AUTHORIZATION AlwaysOnEndpoint WITH SUBJECT = 'AlwaysOn Endpoint',START_DATE = '01/01/2015',EXPIRY_DATE = '01/01/2100'" # 証明書を使用したエンドポイントを作成 $endpoint = New-SqlHadrEndpoint "AlwaysOnEndpoint" -Port 5022 -Path "SQLSERVER:\SQL\$($Node[0])\Default" -AuthenticationOrder Certificate -Certificate "AlwaysOnEndpoint_Cert" Set-SqlHadrEndpoint -InputObject $endpoint -State "Started" Invoke-Sqlcmd -ServerInstance $Node[0] -Query "GRANT CONNECT ON ENDPOINT::AlwaysOnEndpoint TO AlwaysOnEndpoint" # 証明書のバックアップを取得 New-Item -Path "C:\certtemp" -ItemType Directory Invoke-Sqlcmd -ServerInstance $Node[0] -Query "BACKUP CERTIFICATE AlwaysOnEndpoint_Cert TO FILE = 'C:\certtemp\certbackup.cer' WITH PRIVATE KEY (FILE='C:\certtemp\certbackup.pvk', ENCRYPTION BY PASSWORD='Enc Passw0rd')" # 証明書をセカンダリーにコピー $cer = [System.IO.File]::ReadAllBytes("C:\certtemp\certbackup.cer") $pvk = [System.IO.File]::ReadAllBytes("C:\certtemp\certbackup.pvk") $script = { param($cer,$pvk) New-Item -Path "C:\certtemp" -ItemType Directory > $null [System.IO.File]::WriteAllBytes("C:\certtemp\certbackup.cer",$cer) [System.IO.File]::WriteAllBytes("C:\certtemp\certbackup.pvk",$pvk) } $Node[1..($Node.Count -1)] | %{Invoke-Command -ComputerName $_ -ScriptBlock $Script -ArgumentList $cer,$pvk -Credential $cred -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)} # コピーした証明書を使用して 2 台目にエンドポイントを作成 $Node[1..($Node.Count -1)] | %{Invoke-Sqlcmd -ServerInstance $_ -Query "CREATE CERTIFICATE AlwaysOnEndpoint_Cert AUTHORIZATION AlwaysOnEndpoint FROM FILE='C:\certtemp\certbackup.cer' WITH PRIVATE KEY (FILE='C:\certtemp\certbackup.pvk', DECRYPTION BY PASSWORD='Enc Passw0rd')"} $Node[1..($Node.Count -1)] | %{Invoke-command -ComputerName $_ -ScriptBlock { $endpoint = New-SqlHadrEndpoint "AlwaysOnEndpoint" -Port 5022 -Path "SQLSERVER:\SQL\$($ENV:COMPUTERNAME)\Default" -AuthenticationOrder Certificate -Certificate "AlwaysOnEndpoint_Cert" Set-SqlHadrEndpoint -InputObject $endpoint -State "Started"} -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)} $Node[1..($Node.Count -1)] | %{Invoke-Sqlcmd -ServerInstance $_ -Query "GRANT CONNECT ON ENDPOINT::AlwaysOnEndpoint TO AlwaysOnEndpoint"}
次に可用性グループを作成します。
この際、エンドポイントの URL については、IP アドレスを設定しています。
# 更新可能な内部 DNS を使っている場合は、DNS 名でよいと思います。
$Node = @(@("SQL2016-01","10.0.0.4"),@("SQL2016-02","10.0.0.5")) | %{[PSCustomObject]@{Name=$_[0];IP=$_[1]}} $AGName = "AG01" # 可用性グループの作成 $replica = ( $Node | %{New-SqlAvailabilityReplica ` -Name "$($_.Name)" ` -EndpointURL "TCP://$($_.IP):5022" ` -AvailabilityMode "SynchronousCommit" ` -FailoverMode "Automatic" ` -ConnectionModeInSecondaryRole AllowAllConnections ` -Version 13 ` -AsTemplate} ) New-SqlAvailabilityGroup ` -Name $AGName ` -Path "SQLSERVER:\SQL\$($Node[0].Name)\DEFAULT" ` -AvailabilityReplica $replica ` -DtcSupportEnabled ` -DatabaseHealthTrigger # セカンダリを可用性グループに追加 $Node[1..($Node.Count -1)] | %{Join-SqlAvailabilityGroup ` -Path "SQLSERVER:\SQL\$($_.Name)\Default" ` -Name $AGName}
最後にリスナーですが、これは一度仮の IP を割り当てて、リスナーを作成して、
$Node = @("SQL2016-01","SQL2016-02") $AGName = "AG01" $ListenerName = "SQL-LN" # リスナーの作成 New-SqlAvailabilityGroupListener -Name $ListenerName -Port 1433 -StaticIp "10.0.0.100/255.224.0.0"` -Path "SQLSERVER:\SQL\$($Node[0])\Default\AvailabilityGroups\$AGName"
最後にプローブポートをつけて、クラウドサービスの固定 IP の割り当てを行います。
Get-ClusterResource "AG01_10.0.0.100" | Set-ClusterParameter -Multiple @{Address="<クラウドサービスのパブリックIP>";SubnetMask="255.255.255.255";OverrideAddressMatch=1;ProbePort=59999} Stop-ClusterGroup AG01 Start-ClusterGroup AG01
これで、準備は整いましたので、あとは可用性グループにデータベースを追加します。
あとは ALB か ILB を設定すれば、以下のようなワークグループでの AlwaysOn を Azure で構築可能です。
フェールオーバーのテストをしてみましたが ALB 経由で、切り替えもできていました。
# 適当に設定してしまったので LB 側の切り替えに時間がかかっていたため、プローブポートの監視設定はちょっと見直した方がよさげでしたが。
手順の最適化ができればもう少しシンプルな方法で構築できるかと。。
# 現状、構成はシンプルですが手順が面倒。
現状の課題は、DNS の動的登録ができないので、コンピューター名リソース (クラスター名オブジェクトとリスナー) の「StatusDNS」のチェックの回避をどうするかですかね。