Um Kosten zu senken, arbeiten viele Datenbankadministratoren mit Azure VM’s. Diese virtuelle Maschinen sparen hohe Hardware- und Administrationskosten von hochverfügbaren SQL Server Umgebungen ein und ermöglichen damit eine höhere Skalierbarkeit.
In diesem Artikel implementieren wir eine Hochverfügbarkeitsgruppe (mit End-to-End Verschlüsselung) mit PowerShell in einer Azure Umgebung. Ihre Always On Availability Gruppe wird mit folgenden Bestandteilen konfiguriert, die wir Ihnen im folgenden Artikel näher beschreiben werden:
Diese Konfiguration kommt nicht nur dem Punkt der Kosteneinsparung sehr entgegen, sie ermöglicht zudem die Einsparung von Rechenstunden, da Sie mit zwei synchronen Replikaten und Failover Clustern mit drei Knoten arbeiten.
Um Zeit zu sparen, richten wir die Hochverfügbarkeitsgruppe mit einem PowerShell Skript und nicht mit GUI ein. Hierfür müssen im Vorhinein folgende Vorkehrungen getroffen werden bevor wir starten:
Import-Module "C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\Azure\Azure.psd1"
Get-AzurePublishSettingsFile
Import-AzurePublishSettingsFile <publishsettingsfilepath>
Der im Skript enthaltene Befehl Get-AzurePublishSettingsFile generiert automatisch ein Verwaltungszertifikat mit Azure und lädt es auf Ihren Computer herunter. Bei Durchführung öffnet sich ein Browserfenster, welches Sie nun auffordert sich in Ihrem Microsoft Konto einzuloggen und den Azure-Abonnement einzugeben.
$location = "West US"
$affinityGroupName = "test_group"
$affinityGroupDescription = "Test SQL HADR Affinity Group"
$affinityGroupLabel = "IaaS BI Affinity Group"
$networkConfigPath = "C:\scripts\Network.netcfg"
$virtualNetworkName = "test_network"
$storageAccountName = "<uniquestorageaccountname>"
$storageAccountLabel = "Test SQL HADR Storage Account"
$storageAccountContainer = "https://" + $storageAccountName + ".blob.core.windows.net/vhds/"
$winImageName = (Get-AzureVMImage | where {$_.Label -like "Windows Server 2008 R2 SP1*"} | sort PublishedDate -Descending)[0].ImageName
$sqlImageName = (Get-AzureVMImage | where {$_.Label -like "SQL Server 2012 SP1 Enterprise*"} | sort PublishedDate -Descending)[0].ImageName
$dcServerName = "test_server"
$dcServiceName = "<uniqueservicename>"
$availabilitySetName = "SQLHADR"
$vmAdminUser = "AzureAdmin"
$vmAdminPassword = "password"
$workingDir = "c:\scripts\"
Beachten Sie bei der Variablen-Definition folgende Gesichtspunkte:
Die Variablen $storageAccountName und $dcServiceName müssen eindeutig definiert sein, da sie verwendet werden, um Ihr Cloudspeicherkonto bzw. Ihren Cloudserver zu identifizieren.
Die Namen, die Sie für die Variablen $affinityGroupName und $virtualNetworkName angeben, werden im Konfigurationsdokument für virtuelle Netzwerke festgelegt, welches wir später verwenden.
$sqlImageName gibt den aktualisierten Namen des VM-Images an, welches das SQL Server 2012 Service Pack 1 Enterprise Edition enthält.
Wir verwenden password als durchgängiges Kennwort im gesamten Tutorial.
3. Nachfolgend erstellen wir eine Affinitätsgruppe mit folgendem Befehl:
New-AzureAffinityGroup `
-Name $affinityGroupName `
-Location $location `
-Description $affinityGroupDescription `
-Label $affinityGroupLabel
Set-AzureVNetConfig `
-ConfigurationPath $networkConfigPath
Die Konfigurationsdatei ist ein XML. Dokument mit dem virtuellen Netzwerk test_network und der Affinitätsgruppe test_group. Das virtuelle Netzwerk hat nun die IP Adresse 10.10.0.0/16 mit den Subnetzen 0.10.1.0/24 und 10.10.2.0/24. Unterschieden werden die Subnetze durch ein Frontend- und ein Backendsubnetz. Im Frontend Subnetz können Sie Clientanwendungen platzieren. Das hintere Subnetz eignet sich für die Einrichtung von SQL Server VMs.
<NetworkConfiguration xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration">
<VirtualNetworkConfiguration>
<Dns />
<VirtualNetworkSites>
<VirtualNetworkSite name="test_network" AffinityGroup="test_group">
<AddressSpace>
<AddressPrefix>10.10.0.0/16</AddressPrefix>
</AddressSpace>
<Subnets>
<Subnet name="Front">
<AddressPrefix>10.10.1.0/24</AddressPrefix>
</Subnet>
<Subnet name="Back">
<AddressPrefix>10.10.2.0/24</AddressPrefix>
</Subnet>
</Subnets>
</VirtualNetworkSite>
</VirtualNetworkSites>
</VirtualNetworkConfiguration>
</NetworkConfiguration>
```xml
New-AzureStorageAccount `
-StorageAccountName $storageAccountName `
-Label $storageAccountLabel `
-AffinityGroup $affinityGroupName
Set-AzureSubscription `
-SubscriptionName (Get-AzureSubscription).SubscriptionName `
-CurrentStorageAccount $storageAccountName
New-AzureVMConfig `
-Name $dcServerName `
-InstanceSize Medium `
-ImageName $winImageName `
-MediaLocation "$storageAccountContainer$dcServerName.vhd" `
-DiskLabel "OS" |
Add-AzureProvisioningConfig `
-Windows `
-DisableAutomaticUpdates `
-AdminUserName $vmAdminUser `
-Password $vmAdminPassword |
New-AzureVM `
-ServiceName $dcServiceName `
–AffinityGroup $affinityGroupName `
-VNetName $virtualNetworkName
$VMStatus = Get-AzureVM -ServiceName $dcServiceName -Name $dcServerName
While ($VMStatus.InstanceStatus -ne "ReadyRole")
{
write-host "Waiting for " $VMStatus.Name "... Current Status = " $VMStatus.InstanceStatus
Start-Sleep -Seconds 15
$VMStatus = Get-AzureVM -ServiceName $dcServiceName -Name $dcServerName
}
Get-AzureRemoteDesktopFile `
-ServiceName $dcServiceName `
-Name $dcServerName `
-LocalPath "$workingDir$dcServerName.rdp"
Ist der Domänencontroller erfolgreich bereitgestellt, konfigurieren wir als nächstes die Active Directory Domäne auf ebendiesem Controller.
dcpromo.exe ` /unattend ` /ReplicaOrNewDomain:Domain ` /NewDomain:Forest ` /NewDomainDNSName:corp.test.com ` /ForestLevel:4 ` /DomainNetbiosName:CORP ` /DomainLevel:4 ` /InstallDNS:Yes ` /ConfirmGc:Yes ` /CreateDNSDelegation:No ` /DatabasePath:"C:\Windows\NTDS" ` /LogPath:"C:\Windows\NTDS" ` /SYSVOLPath:"C:\Windows\SYSVOL" ` /SafeModeAdminPassword:"password"
Import-Module ActiveDirectory
$pwd = ConvertTo-SecureString "password" -AsPlainText -Force
New-ADUser `
-Name 'Install' `
-AccountPassword $pwd `
-PasswordNeverExpires $true `
-ChangePasswordAtLogon $false `
-Enabled $true
New-ADUser `
-Name 'SQLSvc1' `
-AccountPassword $pwd `
-PasswordNeverExpires $true `
-ChangePasswordAtLogon $false `
-Enabled $true
New-ADUser `
-Name 'SQLSvc2' `
-AccountPassword $pwd `
-PasswordNeverExpires $true `
-ChangePasswordAtLogon $false `
-Enabled $true
CORP\Install wird verwendet, um alles zu konfigurieren, was mit den SQL Server-Dienstinstanzen, dem Failovercluster und der Verfügbarkeitsgruppe zu tun hat. CORP\SQLSvc1 und CORP\SQLSvc2 werden als SQL Server-Dienstkonten für die beiden SQL Server-VMs verwendet.
Cd ad:
$sid = new-object System.Security.Principal.SecurityIdentifier (Get-ADUser "Install").SID
$guid = new-object Guid bf967a86-0de6-11d0-a285-00aa003049e2
$ace1 = new-object System.DirectoryServices.ActiveDirectoryAccessRule $sid,"CreateChild","Allow",$guid,"All"
$corp = Get-ADObject -Identity "DC=corp,DC=test,DC=com"
$acl = Get-Acl $corp
$acl.AddAccessRule($ace1)
Set-Acl -Path "DC=corp,DC=test,DC=com" -AclObject $acl
Die oben angegebene GUID ist die GUID für den Computerobjekttyp. Um Active Directory Objekte für das Failovercluster zu erstellen, benötigt das Konto CORP\Install benötigt die Berechtigungen, “Alle Eigenschaften lesen” und “Computerobjekte erstellen” zu dürfen. Hier handelt es sich um die Standardkonfiguration, sodass Sie hier nichts aktiv einrichten müssen.
Nachdem wir die Konfiguration des Domänencontrollers und der Active Directory vorgenommen haben, erstellen wir im nächsten Schritt SQL Server VMs.
$domainName= "corp"
$FQDN = "corp.test.com"
$subnetName = "Back"
$sqlServiceName = "<uniqueservicename>"
$quorumServerName = "test_quorum"
$sql1ServerName = "test_SQL1"
$sql2ServerName = "test_SQL2"
$availabilitySetName = "SQLHADR"
$dataDiskSize = 100
$dnsSettings = New-AzureDns -Name "testBackDNS" -IPAddress "10.10.0.4"
Die IP Adresse 10.10.0.4 wird der ersten VM zugewiesen, die im Subnetz der Azure VM erstellt wurde. zum Gegenprüfen, ob diese mit der IP Adresse des Domänencontrollers übereinstimmt, verwenden Sie den Befehl IPCONFIG.
New-AzureVMConfig `
-Name $quorumServerName `
-InstanceSize Medium `
-ImageName $winImageName `
-MediaLocation "$storageAccountContainer$quorumServerName.vhd" `
-AvailabilitySetName $availabilitySetName `
-DiskLabel "OS" |
Add-AzureProvisioningConfig `
-WindowsDomain `
-AdminUserName $vmAdminUser `
-Password $vmAdminPassword `
-DisableAutomaticUpdates `
-Domain $domainName `
-JoinDomain $FQDN `
-DomainUserName $vmAdminUser `
-DomainPassword $vmAdminPassword |
Set-AzureSubnet `
-SubnetNames $subnetName |
New-AzureVM `
-ServiceName $sqlServiceName `
–AffinityGroup $affinityGroupName `
-VNetName $virtualNetworkName `
-DnsSettings $dnsSettings
Auch hier gehen wir erneut auf die Einzelheiten des Befehls ein:
# Create test_SQL1...
New-AzureVMConfig `
-Name $sql1ServerName `
-InstanceSize Large `
-ImageName $sqlImageName `
-MediaLocation "$storageAccountContainer$sql1ServerName.vhd" `
-AvailabilitySetName $availabilitySetName `
-HostCaching "ReadOnly" `
-DiskLabel "OS" |
Add-AzureProvisioningConfig `
-WindowsDomain `
-AdminUserName $vmAdminUser `
-Password $vmAdminPassword `
-DisableAutomaticUpdates `
-Domain $domainName `
-JoinDomain $FQDN `
-DomainUserName $vmAdminUser `
-DomainPassword $vmAdminPassword |
Set-AzureSubnet `
-SubnetNames $subnetName |
Add-AzureEndpoint `
-Name "SQL" `
-Protocol "tcp" `
-PublicPort 1 `
-LocalPort 1433 |
New-AzureVM `
-ServiceName $sqlServiceName
# Create test_SQL2...
New-AzureVMConfig `
-Name $sql2ServerName `
-InstanceSize Large `
-ImageName $sqlImageName `
-MediaLocation "$storageAccountContainer$sql2ServerName.vhd" `
-AvailabilitySetName $availabilitySetName `
-HostCaching "ReadOnly" `
-DiskLabel "OS" |
Add-AzureProvisioningConfig `
-WindowsDomain `
-AdminUserName $vmAdminUser `
-Password $vmAdminPassword `
-DisableAutomaticUpdates `
-Domain $domainName `
-JoinDomain $FQDN `
-DomainUserName $vmAdminUser `
-DomainPassword $vmAdminPassword |
Set-AzureSubnet `
-SubnetNames $subnetName |
Add-AzureEndpoint `
-Name "SQL" `
-Protocol "tcp" `
-PublicPort 2 `
-LocalPort 1433 |
New-AzureVM `
-ServiceName $sqlServiceName
Foreach ($VM in $VMs = Get-AzureVM -ServiceName $sqlServiceName)
{
write-host "Waiting for " $VM.Name "..."
# Loop until the VM status is "ReadyRole"
While ($VM.InstanceStatus -ne "ReadyRole")
{
write-host " Current Status = " $VM.InstanceStatus
Start-Sleep -Seconds 15
$VM = Get-AzureVM -ServiceName $VM.ServiceName -Name $VM.InstanceName
}
write-host " Current Status = " $VM.InstanceStatus
# Download remote desktop file
Get-AzureRemoteDesktopFile -ServiceName $VM.ServiceName -Name $VM.InstanceName -LocalPath "$workingDir$($VM.InstanceName).rdp"
}
Letztlich sind die SQL Server VMs nun bereitgestellt. Weiter geht es mit der Initialisierung der Failovercluster-VMs.
In diesem Abschnitt müssen wir nun die drei Server ändern, die wir im Failovercluster und der SQL Server-Installation verwendet haben.
Wir starten mit test_quorum und führen die folgenden Schritte aus:
Import-Module ServerManager
Add-WindowsFeature Failover-Clustering
net localgroup administrators "CORP\Install" /Add
logoff.exe
Initialisieren nun als Nächstes test_SQL1 und test_SQL2 und führen Sie folgende Schritte aus, die für beide SQL Server-VMs identisch sind.
Import-Module ServerManager
Add-WindowsFeature Failover-Clustering
net localgroup administrators "CORP\Install" /Add
Set-ExecutionPolicy -Execution RemoteSigned -Force
Import-Module -Name "sqlps" -DisableNameChecking
net localgroup administrators "CORP\Install" /Add
Invoke-SqlCmd -Query "EXEC sp_addsrvrolemember 'CORP\Install', 'sysadmin'" -ServerInstance "."
Invoke-SqlCmd -Query "CREATE LOGIN [NT AUTHORITY\SYSTEM] FROM WINDOWS" -ServerInstance "."
Invoke-SqlCmd -Query "GRANT ALTER ANY AVAILABILITY GROUP TO [NT AUTHORITY\SYSTEM] AS SA" -ServerInstance "."
Invoke-SqlCmd -Query "GRANT CONNECT SQL TO [NT AUTHORITY\SYSTEM] AS SA" -ServerInstance "."
Invoke-SqlCmd -Query "GRANT VIEW SERVER STATE TO [NT AUTHORITY\SYSTEM] AS SA" -ServerInstance "."
netsh advfirewall firewall add rule name='SQL Server (TCP-In)' program='C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn\sqlservr.exe' dir=in action=allow protocol=TCP
In diesem Abschnitt haben wir nun das Failovercluster mit allen vorhandenen SQL Servern initialisiert und konfiguriert. Im nächsten Schritt erstellen wir eine Verfügbarkeitsgruppe.
$server1 = "test_SQL1"
$server2 = "test_SQL2"
$serverQuorum = "test_quorum"
$acct1 = "CORP\SQLSvc1"
$acct2 = "CORP\SQLSvc2"
$password = "password"
$clusterName = "test_cluster"
$timeout = New-Object System.TimeSpan -ArgumentList 0, 0, 30
$db = "test_db"
$backupShare = "\\$server1\backup"
$quorumShare = "\\$server1\quorum"
$ag = "AG1"
Set-ExecutionPolicy RemoteSigned -Force
Import-Module "sqlps" -DisableNameChecking
$wmi1 = new-object ("Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer") $server1
$wmi1.services | where {$_.Type -eq 'SqlServer'} | foreach{$_.SetServiceAccount($acct1,$password)}
$svc1 = Get-Service -ComputerName $server1 -Name 'MSSQLSERVER'
$svc1.Stop()
$svc1.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped,$timeout)
$svc1.Start();
$svc1.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running,$timeout)
$wmi2 = new-object ("Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer") $server2
$wmi2.services | where {$_.Type -eq 'SqlServer'} | foreach{$_.SetServiceAccount($acct2,$password)}
$svc2 = Get-Service -ComputerName $server2 -Name 'MSSQLSERVER'
$svc2.Stop()
$svc2.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped,$timeout)
$svc2.Start();
$svc2.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running,$timeout)
Set-ExecutionPolicy Unrestricted -Force
.\CreateAzureFailoverCluster.ps1 -ClusterName "$clusterName" -ClusterNode "$server1","$server2","$serverQuorum"
Enable-SqlAlwaysOn `
-Path SQLSERVER:\SQL\$server1\Default `
-Force
Enable-SqlAlwaysOn `
-Path SQLSERVER:\SQL\$server2\Default `
-NoServiceRestart
$svc2.Stop()
$svc2.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped,$timeout)
$svc2.Start();
$svc2.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running,$timeout)
$backup = "C:\backup"
New-Item $backup -ItemType directory
net share backup=$backup "/grant:$acct1,FULL" "/grant:$acct2,FULL"
icacls.exe "$backup" /grant:r ("$acct1" + ":(OI)(CI)F") ("$acct2" + ":(OI)(CI)F")
Invoke-SqlCmd -Query "CREATE database $db"
Backup-SqlDatabase -Database $db -BackupFile "$backupShare\db.bak" -ServerInstance $server1
Backup-SqlDatabase -Database $db -BackupFile "$backupShare\db.log" -ServerInstance $server1 -BackupAction Log
Restore-SqlDatabase -Database $db -BackupFile "$backupShare\db.bak" -ServerInstance $server2 -NoRecovery
Restore-SqlDatabase -Database $db -BackupFile "$backupShare\db.log" -ServerInstance $server2 -RestoreAction Log -NoRecovery
$endpoint =
New-SqlHadrEndpoint MyMirroringEndpoint `
-Port 5022 `
-Path "SQLSERVER:\SQL\$server1\Default"
Set-SqlHadrEndpoint `
-InputObject $endpoint `
-State "Started"
$endpoint =
New-SqlHadrEndpoint MyMirroringEndpoint `
-Port 5022 `
-Path "SQLSERVER:\SQL\$server2\Default"
Set-SqlHadrEndpoint `
-InputObject $endpoint `
-State "Started"
Invoke-SqlCmd -Query "CREATE LOGIN [$acct2] FROM WINDOWS" -ServerInstance $server1
Invoke-SqlCmd -Query "GRANT CONNECT ON ENDPOINT::[MyMirroringEndpoint] TO [$acct2]" -ServerInstance $server1
Invoke-SqlCmd -Query "CREATE LOGIN [$acct1] FROM WINDOWS" -ServerInstance $server2
Invoke-SqlCmd -Query "GRANT CONNECT ON ENDPOINT::[MyMirroringEndpoint] TO [$acct1]" -ServerInstance $server2
$primaryReplica =
New-SqlAvailabilityReplica `
-Name $server1 `
-EndpointURL "TCP://$server1.corp.test.com:5022" `
-AvailabilityMode "SynchronousCommit" `
-FailoverMode "Automatic" `
-Version 11 `
-AsTemplate
$secondaryReplica =
New-SqlAvailabilityReplica `
-Name $server2 `
-EndpointURL "TCP://$server2.corp.test.com:5022" `
-AvailabilityMode "SynchronousCommit" `
-FailoverMode "Automatic" `
-Version 11 `
-AsTemplate
New-SqlAvailabilityGroup `
-Name $ag `
-Path "SQLSERVER:\SQL\$server1\Default" `
-AvailabilityReplica @($primaryReplica,$secondaryReplica) `
-Database $db
Join-SqlAvailabilityGroup `
-Path "SQLSERVER:\SQL\$server2\Default" `
-Name $ag
Add-SqlAvailabilityDatabase `
-Path "SQLSERVER:\SQL\$server2\Default\AvailabilityGroups\$ag" `
-Database $db
Mit den vorangegangenen Schritten haben wir nun eine SQL Server Always On Availability Gruppe mit PowerShell in Azure erfolgreich implementiert.
Kontaktieren Sie uns gerne über das
Kontaktformular und vereinbaren ein unverbindliches
Beratungsgespräch mit unseren Berater:innen zur
Bedarfsevaluierung. Gemeinsam optimieren wir Ihre
Umgebung und steigern Ihre Performance!
Wir freuen uns auf Ihre Kontaktaufnahme!