如何跨 Linux 上的可用性组副本同步 SQL 服务器代理作业?

How to synchronize SQL Server Agent jobs across availability group replicas on Linux?

我在 Linux 上有两个 SQL Server 2019 实例 运行。这两个实例都包含一个使用 AlwaysOn 可用性组同步的数据库。数据库中的数据已同步,但问题是 SQL 代理作业不是数据库本身的一部分。

因此,当我在主副本上创建 SQL 服务器代理作业时,此配置不会复制到辅助副本。因此,在创建每个工作之后,我总是还必须去中学并在那里创建工作。而且我必须一直跟踪我所做的所有更改。

使用可用性组时,是否有一种内置方法可以自动执行 Linux 上 SQL 服务器作业的这种跨副本同步?跨 AG 副本的作业同步似乎应该已经由 SQL Server/SQL Server Agent 工具原生支持,但我从 Microsoft 找不到任何东西,只有一个名为 DBA Tools 的第三方工具我可以使用 PowerShell 编写自己的自动化脚本。

dbatools 可以同步它们,但我还没有在 linux 上的 AG 运行 上尝试过。让我知道它是否有效! 第一个参数是你的AG名称,第二个参数是你集群的虚拟网络名称。

param($AvailabilityGroup, $SqlInstance)
    try {
        $replicas = Get-DbaAgReplica -AvailabilityGroup $AvailabilityGroup -SqlInstance $SqlInstance
        $primary = $replicas | Where-Object Role -EQ Primary | Select-Object -ExpandProperty Name
        $secondaries = $replicas | Where-Object Role -EQ Secondary | Select-Object -ExpandProperty Name
        $primaryInstanceConnection = Connect-DbaInstance $primary -ClientName 'ScriptBorrowedFromWhosebug'
        $secondaries | ForEach-Object {
            $secondaryInstanceConnection = Connect-DbaInstance $_ -ClientName 'ScriptBorrowedFromWhosebug'
            Copy-DbaAgentJob -Source $primaryInstanceConnection -Destination $secondaryInstanceConnection -Force
        }
    }
    catch {
        $msg = $_.Exception.Message
        Write-Error "Error while syncing jobs for Availability Group '$($AvailabilityGroup): $msg'"
    }

经过反复试验,我最终得到了适用于 Ubuntu Linux 18.04 的脚本。非常感谢 Derik Hammer and his blog 提供脚本的基础,也感谢 David Söderlund 的回复。

要使脚本运行,您需要为 Linux 安装 PowerShell 以及 DBATools 和 SqlCmd2 PowerShell 模块。您还必须将 sql 凭据存储在某个文件中。我为我选择了 /var/opt/mssql/secrets/creds.xml 并将访问权限更改为仅 root。脚本可以将登录、DBmail 设置、SQL 代理类别、作业、操作员和时间表从主要副本同步到所有辅助副本(取消注释你需要的内容,但要小心,顺序很重要,有些东西不能在一个连接中同步,即operators 和 jobs),如果有的话,跳过配置副本。

您可以将计划执行设置为 root,并使用 CRON 将输出记录到文件中。要进行设置,运行:

sudo crontab -e

并将此行添加到文件中:

*/5 * * * * pwsh /<PATH>/sync-sql-objects.ps1 >> /<PATH>/sync-sql-objects.log

脚本:

<# 
.DESCRIPTION
This script will detect your Availability Group replicas and copy all of its instance level objects from primary replica to secondary replicas within the Availability Group. It will skip any configuration replicas.
 
.EXAMPLE
sudo pwsh sync-sql-objects.ps1
 
.NOTES
One limitation of this script is that it assumes you only have one availability group. This script should run on your configuration replica server.
 
.LINK
https://www.sqlhammer.com/synchronizing-server-objects-for-availability-groups/
 
DEBUG
To see logs on Ubuntu Linux, install Postfix Mail Transfer Agent and then go to see mails in /var/mail/<username>
#>  

Write-Output ("Sync started: " + (Get-Date -Format G))
 
#Error handling
$ErrorActionPreference = "stop";
 
Trap 
{
    $err = $_.Exception
    while ( $err.InnerException )
    {
 
        $err = $err.InnerException
        Write-Output $err.Message
 
    };
}
 
# Prerequisites
try
{
    Write-Output "Valiating prerequisites."

    # You need to have these modules installed in advance, otherwise the import will fail

    if ((Get-Module -Name dbatools) -eq $null)
    {
        Import-Module dbatools | Out-Null
    }

    if ((Get-Module -Name Invoke-SqlCmd2) -eq $null)
    {
        Import-Module Invoke-SqlCmd2 | Out-Null
    }

    Write-Output "Prerequisites loaded."
 
}
catch
{
    Write-Error $_.Exception.Message -EA Continue
    Write-Error "One or more of the prerequisites did not load. Review previous errors for more details." -EA Continue
    return
}

# Detect Availability Group replicas
Write-Output "Begin query for Availability Group replicas"
 
$ConfigurationMode = "CONFIGURATION_ONLY"
$Hostname = hostname 
$Credentials = Import-CliXml -Path /var/opt/mssql/secrets/creds.xml

$ReplicasQuery = @"
SELECT replica_server_name,
availability_mode_desc,
primary_replica
FROM sys.availability_replicas AR
INNER JOIN sys.dm_hadr_availability_group_states HAGS
INNER JOIN sys.availability_groups AG ON AG.group_id = HAGS.group_id
    ON HAGS.group_id = AR.group_id;
"@

$Replicas = Invoke-Sqlcmd2 -ServerInstance $Hostname -Query $ReplicasQuery -ConnectionTimeout 30 -Credential $Credentials
 
if(([DBNull]::Value).Equals($Replicas[0].primary_replica))
{
    Write-Error "Availability Group query returned no results. Confirm that you connected to a SQL Server instance running an Availability Group. No work was accomplished."
    return
}
 
Write-Output "Completed query of Availability Group replicas"
 
foreach($replica in $Replicas)
{
    # Skip if destination replica is primary replica itself
    if($replica.primary_replica.CompareTo($replica.replica_server_name) -eq 0)
    {
        continue
    }
 
    # Skip configuration replicas
    if($replica.availability_mode_desc.CompareTo($ConfigurationMode) -eq 0)
    {
        continue
    }

    #Connect
    $PrimaryReplica = Connect-DbaInstance $replica.primary_replica -ClientName 'ConfigurationReplica' -SqlCredential $Credentials
    $SecondaryReplica = Connect-DbaInstance $replica.replica_server_name -ClientName 'ConfigurationReplica' -SqlCredential $Credentials

    Write-Output "Copying instance objects from $sourceReplica to $replica"

    # Copy objects
    # Write-Output "Copying Logins."
    # Copy-DbaLogin -Source $PrimaryReplica -Destination $SecondaryReplica

    # Write-Output "Copying DBMail."
    # Copy-DbaDbMail -Source $PrimaryReplica -Destination $SecondaryReplica -Force

    # Write-Output "Copying Agent Categories."
    # Copy-DbaAgentJobCategory -Source $PrimaryReplica -Destination $SecondaryReplica -Force

    # Write-Output "Copying Agent Schedules."
    # Copy-DbaAgentSchedule -Source $PrimaryReplica -Destination $SecondaryReplica -Force

    # Write-Output "Copying Operators."
    # Copy-DbaAgentOperator -Source $PrimaryReplica -Destination $SecondaryReplica -Force

    Write-Output "Copying Jobs."
    Copy-DbaAgentJob -Source $PrimaryReplica -Destination $SecondaryReplica -Force
       
    Write-Output "Copy complete from $PrimaryReplica to $SecondaryReplica"
}

Write-Output "SQL Instance object sync complete."

尽情享受吧!