OctopusDeploy - SQL 永远在线部署

OctopusDeploy - SQL Always-On Deployment

我有一个用于部署到数据库服务器的 OctopusDeploy 进程。 这些步骤包括将数据库备份从一个位置复制到本地服务器,将它们还原到 SQL 服务器,然后 运行 对它们进行 dacpac 以将它们升级到特定版本。

一切正常,但我们现在添加了一个新环境,我不知道如何为它配置部署过程。

最初,服务器是一个 windows 集群环境,触手 运行ning 作为集群服务(这意味着单个部署目标)。

但是,设置我们服务器的公司出于某种原因无法使集群正常工作,现在给了我们一些介于两者之间的方法:
我们有两台服务器,每台服务器都安装、配置了触手,并在上面 运行ning。
每条触手都有一个独特的指纹,并且总是 运行 并且可以访问。
在windows台服务器上,SQL台服务器已安装并配置为"always on",一台服务器为主,另一台为备用。

这个想法是,如果主要的死亡,次要的会收拾残局并且 运行 没问题。

从概念上讲,这对我们有用,因为我们有 SQL 服务器连接的 "clustered" ip,我们的网络应用程序不会注意到差异。

(重要的是要注意,我不能更改此设置 - 这是一个使用我们给定的工作的情况......)

现在,在 Octopus 中,我只需要部署到该环境中的一台服务器,就好像我要部署到两台服务器一样,我要么重复任务(如果 运行 作为滚动部署),或者更糟的是,有冲突的部署(如果 运行 异步)。

我最初尝试为每个服务器添加一个辅助角色("PrimaryNode"、"SecondaryNode"),但后来我发现 Octopus 将角色视为 "or" 而不是 "and",所以这对我们开箱即用

然后我研究了编写 powershell 脚本来检查具有角色 "dbserver" 和 "primarynode" 的机器是否具有 "Online" 的状态和 "healthy" 的运行状况,然后根据状态设置一个输出变量:

##CONFIG##
$APIKey = "API-OBSCURED"
$MainRole = "DBServer" 
$SecondaryRole = "PrimaryNode"

$roles = $OctopusParameters['Octopus.Machine.Roles'] -split ","

$enableFailoverDeployment = $false

foreach($role in $roles)
{
    if ($role -eq "FailoverNode")
    {
        #This is the failovernode - check if the primary node is up and running
        Write-Host "This is the failover database node. Checking if primary node is available before attempting deployment."

        $OctopusURL = "https://myOctourl"  ##$OctopusParameters['Octopus.Web.BaseUrl']
        $EnvironmentID = $OctopusParameters['Octopus.Environment.Id']
        $header = @{ "X-Octopus-ApiKey" = $APIKey }
        $environment = (Invoke-WebRequest -UseBasicParsing "$OctopusURL/api/environments/$EnvironmentID" -Headers $header).content | ConvertFrom-Json
        $machines = ((Invoke-WebRequest -UseBasicParsing ($OctopusURL + $environment.Links.Machines) -Headers $header).content | ConvertFrom-Json).items
        $MachinesInRole = $machines | ?{$MainRole -in $_.Roles}
        $MachinesInRole = $MachinesInRole | ?{$SecondaryRole -in $_.Roles}
        $measure = $MachinesInRole | measure
        $total = $measure.Count

        if ($total -gt 0)
        {
            $currentMachine = $MachinesInRole[0]
            $machineUri = $currentMachine.URI

            if ($currentMachine.Status -eq "Online")
            {
                if ($currentMachine.HealthStatus -eq "Healthy")
                {
                    Write-Host "Primary node is online and healthy."
                    Write-Host "Setting flag to disable failover deployment."
                    $enableFailoverDeployment = $false
                }
                else
                {
                    Write-Host "Primary node has a health status of $($currentMachine.HealthStatus)."
                    Write-Host "Setting flag to enable failover deployment."
                    $enableFailoverDeployment = $true
                }
            }
            else
            {
                Write-Host "Primary node has a status of $($currentMachine.Status)."
                Write-Host "Setting flag to enable failover deployment."
                $enableFailoverDeployment = $true
            }
        }
        break;
    }
}

Set-OctopusVariable -name "EnableFailoverDeployment" -value $enableFailoverDeployment

这似乎有效 - 我可以判断我应该部署到主要还是次要。

但是,我现在对如何让部署过程使用它感到困惑。
显然,如果主节点离线,则无论如何都不会在其上进行部署。 然而,如果两个触手都在线且健康,那么章鱼将尝试部署到它们身上。

部署过程包含大约 12 个独特的步骤,并成功用于其他几个环境(所有单服务器配置),但如前所述,现在还需要部署到一个奇怪的 active/warm 环境。

有什么想法可以实现吗? (如果你能在角色中指定 "AND" 就好了。)

更新 1 我现在发现您可以通过网络 api 更新特定机器 "IsDisabed",所以我在上面的末尾添加了代码到 enable/disable 辅助节点,具体取决于结果而不是设置输出变量。

虽然这确实会更新机器的状态,但实际上不会影响正在进行的部署过程。 如果我停止并重新启动整个过程,机器会相应地被正确拾取为 enabled/disabled,但是同样,如果它的状态在部署期间发生变化,Octopus 似乎 "smart" 不足以识别它, 排除这个选项。 (我确实尝试在这个脚本之前和之后添加一个健康检查步骤以查看是否有所不同,但是当健康检查意识到机器被禁用时,它仍然对其余步骤没有影响)

更新 2 我现在还在 API 中找到 "Deployment" 的 "ExcludedMachineIds" 属性,但是在部署后尝试更新它时出现 405(不允许)错误正在处理中。
啊..为什么这不容易?

ok - 所以我们采用的方法是针对集群的 Always-On SQL 实例创建一个脚本 运行,它标识了主要节点和次要节点,如下所示:

SELECT TOP 1 hags.primary_replica 
FROM  sys.dm_hadr_availability_group_states hags 
INNER JOIN sys.availability_groups ag 
   ON ag.group_id = hags.group_id 
WHERE ag.name = '$alwaysOnClusterInstance';

这让我获得了主服务器的主机名。 然后我决定在 OctopusDeploy 中将主机名包含在机器的实际显示名称中。

然后我用 Powershell 将上述 SQL 的结果与当前机器显示名称 ($OctopusParameters['Octopus.Machine.Name'])

进行简单的 "like" 比较

如果匹配,则我将此步骤的输出变量设置为等于 OctopusDeploy 机器的内部 ID ($OctopusParameters['Octopus.Machine.Id'])

最后,在每一步开始时,我简单地将当前机器id与上面提到的输出变量进行比较,以确定我是在主节点还是辅助节点上,并据此采取行动(通常通过退出该步骤如果它是辅助节点,则立即)

最后要注意的是,我关心的每一步 运行 在哪台机器上,都必须 运行 作为 "Rolling step",带有 windows 大小为 1.

幸运的是,如果我们不在主节点上,我们通常只是退出,这不会为我们的部署过程增加任何实时时间。