Visual StudioでゼロダウンタイムでIISにWebアプリケーションを配備する


導入


以前は、Visual StudioからWebアプリケーションをWeb展開ツールを使用してIISに展開する方法について書きました.あなたが気づいたかもしれない1つの問題は、展開の間に起こるダウンタイムです.
それで、このポストで、私は使用方法について書きますBlue-Green Deployment ゼロダウンタイム展開を達成するためのIISで
アプリケーション要求ルーティング(ARR)をインストールするために、少なくともIIS 7を必要とします.

目次

  • Basic Idea
  • Steps
  • Scripts
  • Remote Access
  • Integration with Visual Studio
  • 基本理念

    We will create two server, blue and green. One of them will be accessible through public network for our clients, and the other only through the private network for testing purposes.

    Deployment is done in these steps:

    1. We will deploy our new web app to the private network server.
    2. Make sure it is working as intended.
    3. Make the private network server accessible to public network, now both servers can receive clients requests.
    4. Gracefully shutdown the original public network server; now it can't receive new requests but will finish its current requests.
    5. Make the original public network server accessible only to private network, then turn it on.
    6. Now the green and blue servers are switched, and in each deployment they will get switched again.

    ステップ

    1 . Webプラットフォームインストーラのダウンロードとインストール


    それは直接ツールをダウンロードする代わりにWebPIを使用することをお勧めしますWebPIも依存関係を取得し、現在のシステム設定に応じてこれらのツールを設定します.
    Web Platform Installer

    2 .アプリケーションリクエストルーティング( ARR )を取得する

  • IWISマネージャを介さずに直接WebPIを開きます.
  • ARRを検索し、インストールします.

  • 青と緑のサーバを作る


    つのウェブサイト、クライアントの要求を受信するポート80のいずれかを、我々はそれをサーバーファームと呼ぶでしょう.他の2つのポートは、8001と8002といって、この例ではそれぞれ青と緑と呼ばれます.

    つのウェブサイトにはcontent.html どちらがblue or green その中に書かれている.
    また、これらのウェブサイトをホストファイルに追加し、localhostを指すようにします.
    Windowsでは、ホストファイルは次のとおりです.C:\Windows\System32\drivers\etc\hosts
    テストしましょう.

    サーバーファームの設定


    クリエイトserver farm , そして、我々はちょうど青と緑のウェブサイトに割り当てられたポート番号でそれに2つのサーバーを追加します.


    再起動規則を追加するプロンプトを取得します.我々は、後で我々自身の書き換え規則を加えます.
    これは次のようになります.

    健康診断の構成


    ヘルステストはどのサーバーがクライアントリクエストを受け取るかを決めるでしょう.
    最初にHTMLファイルを作成するis-up.html ウェブサイトの横でcontent.html ファイル.そのうちの一つが含まれます1 , その他0 .
    テストしましょう.

    当社のサーバーファームに移動し、健康テストを構成します
  • URLはis-up.html ファイル.
  • インターバル毎にレスポンスをチェックします.
  • レスポンスマッチは、我々が健康的であると思うものです.

  • 検証する.

    サーバーファームで監視と管理をチェックします.

    問題ログ


    Health Testは毎秒ごとに各サーバに要求をします、そして、これらの要求は記録されます.

    この問題を解決するためには、is-up.html ログファイルからapplicationHost.config IISのファイル.私が知っている限りでは、これはIISマネージャを通して可能ではありません.
    WindowsではapplicationHost.config ファイルは次の場所にあります.C:\Windows\System32\inetsrv\config\applicationHost.configこのコードをapplicationHost.config ファイル.
        <location path="blue/is-up.html">
            <system.webServer>
                <httpLogging dontLog="true" />
            </system.webServer>
        </location>
        <location path="green/is-up.html">
            <system.webServer>
                <httpLogging dontLog="true" />
            </system.webServer>
        </location>
    
    このようにしてapplicationHost.config ファイルは次のようになります.

    URL書き換え規則の追加


    URL Rewrite は、指定した規則に従ってリクエストURLを変更するために使用されます.
    我々はサーバーファームへのルート要求にこのモジュールを使用します.
    WebPIを使用してARRをインストールした場合、URLの書き換えもインストールされます.
    IIS ManagerのメインノードからURLの書き換えを選択し、空白のルールを追加します.

    パターンはURLにマッチします:.*
    ポートの条件を追加する80
    アクションタイプは次のとおりです.サーバーファームへのルート

    最後に、以下のようになります.

    テストしましょう.

    我々が変わるならば0 and 1is-up.html ファイル

    現在、thigsが設定されます.次に、展開プロセスを自動化するスクリプトを書きます.

    スクリプト

    We can use PowerShell to automate some processes.

    サーバーファームオブジェクトを取得する


    function get-webfarm {
        param($webFarmName)
    
        $assembly = [System.Reflection.Assembly]::LoadFrom("$env:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll")
        $mgr = new-object Microsoft.Web.Administration.ServerManager "$env:systemroot\system32\inetsrv\config\applicationhost.config"
        $conf = $mgr.GetApplicationHostConfiguration()
        $section = $conf.GetSection("webFarms")
        $webFarms = $section.GetCollection()
        $webFarms | Where {
            $_.GetAttributeValue("name") -eq $webFarmName
        }
    }
    

    サーバーが不健全であるか確認するには


    $webFarmName = "server-farm"
    
    $webFarm = get-webfarm $webFarmName
    $unhealthyServer = $webFarm.GetCollection() | where {
        !$_.GetChildElement("applicationRequestRouting").GetChildElement("counters").GetAttributeValue("isHealthy")
    }
    
    $unhealthyServer.GetAttributeValue("address")
    

    スイッチブルーとグリーン


    $webFarmName = "server-farm"
    $addressBlue = "blue"
    $addressGreen = "green"
    $siteBlue = "http://blue:8001/"
    $siteGreen = "http://green:8002/"
    $pathBlue = "C:\inetpub\sites\blue"
    $pathGreen = "C:\inetpub\sites\green"
    $warmUpRequestURL = "content.html"
    $healthCheckFileName = "is-up.html"
    $healthyResponse = "1"
    $unhealthyResponse = "0"
    
    $webFarm = get-webfarm $webFarmName
    
    # Get health check interval
    $healthCheckTimeoutS = $webFarm.GetChildElement("applicationRequestRouting").GetChildElement("healthCheck").GetAttributeValue("interval").TotalSeconds
    
    $healthyServer = $webFarm.GetCollection() | where {
        $_.GetChildElement("applicationRequestRouting").GetChildElement("counters").GetAttributeValue("isHealthy")
    }
    
    $siteToWarm = $siteBlue
    $serverAddressToBringDown = $addressGreen
    $pathToBringDown = $pathGreen
    $pathToBringUp = $pathBlue
    
    if($healthyServer.GetAttributeValue("address") -eq $addressBlue) {
        $siteToWarm = $siteGreen
        $serverAddressToBringDown = $addressBlue
        $pathToBringUp = $pathGreen
        $pathToBringDown = $pathBlue
    }
    
    # Initializing unhealthy server, and making sure it works as intended
    Write-Host "Warming up $($siteToWarm)"
    Do {
        $time = Measure-Command {
            $res = Invoke-WebRequest "$($siteToWarm)$($warmUpRequestURL)"
        }
        $ms = $time.TotalMilliSeconds
        If ($ms -ge 400) {
            Write-Host "$($res.StatusCode) from $($siteToWarm) in $($ms)ms"
        }
    } While ($ms -ge 400)
    
    Write-Host "$($res.StatusCode) from $($siteToWarm) in $($ms)ms"
    
    # If the unhealthy server is fine, start the switching operation
    if ($res.StatusCode -eq 200) {
        Write-Host "Bringing $($pathToBringUp) up"
        (Get-Content $pathToBringUp\$healthCheckFileName).replace($unhealthyResponse, $healthyResponse) | Set-Content $pathToBringUp\$healthCheckFileName
    
        # Wait for health check to mark the server healthy, afterwards we can bring down the other server
        Write-Host "Waiting for health check to pass in $($healthCheckTimeoutS) seconds..."
        Start-Sleep -s $healthCheckTimeoutS
    
        Write-Host "Draining $($pathToBringDown)"
    
        $serverToBringDown = $webFarm.GetCollection() | Where {
            $_.GetAttributeValue("address") -eq $serverAddressToBringDown
        }
        $arrToBringDown = $serverToBringDown.GetChildElement("applicationRequestRouting")
    
        $method = $arrToBringDown.Methods["SetState"]
        $methodInstance = $method.CreateInstance()
    
        # 0 = Available
        # 1 = Drain
        # 2 = Unavailable
        # 3 = Unavailable Gracefully
        $methodInstance.Input.Attributes[0].Value = 1
        $methodInstance.Execute()
    
        # loop till there is no requests, then bring the server down
        $currentRequests = $arrToBringDown.GetChildElement("counters").GetAttributeValue("currentRequests")
        While($currentRequests -gt 0) {
            Start-Sleep -s 1
            $currentRequests = $arrToBringDown.GetChildElement("counters").GetAttributeValue("currentRequests")
        }
    
        Write-Host "Bringing $($pathToBringDown) down"
        (Get-Content $pathToBringDown\$healthCheckFileName).replace($healthyResponse, $unhealthyResponse) | Set-Content $pathToBringDown\$healthCheckFileName
    
        $methodInstance.Input.Attributes[0].Value = 0
        $methodInstance.Execute()
    } else {
        Write-Host "Cannot warm up $($siteToWarm)"
    }
    

    リモートアクセス

    To run these scripts remotely, we will use Windows Remote Management (WinRM) .
    WinRMを設定するには、次の手順に従います.
  • 確認ポート5985 ネットワークを通して開いています.
  • ランwinrm qc リモートサーバーでy を返します.
  • WinRMサービスがローカルに動いていることを確認してください.
  • ランwinrm set winrm/config/client '@{TrustedHosts="your.server.IP"}' ローカル.
  • これでスクリプトをリモートで実行できますInvoke-Command
    Invoke-Command -ComputerName your.server.ip -ScriptBlock {
    
        $webFarmName = "server-farm"
    
        $assembly = [System.Reflection.Assembly]::LoadFrom("$env:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll")
        $mgr = new-object Microsoft.Web.Administration.ServerManager "$env:systemroot\system32\inetsrv\config\applicationhost.config"
        $conf = $mgr.GetApplicationHostConfiguration()
        $section = $conf.GetSection("webFarms")
        $webFarms = $section.GetCollection()
        $webFarm = $webFarms | Where {
            $_.GetAttributeValue("name") -eq $webFarmName
        }
    
        $unhealthyServer = $webFarm.GetCollection() | where {
            !$_.GetChildElement("applicationRequestRouting").GetChildElement("counters").GetAttributeValue("isHealthy")
        }
    
        $unhealthyServer.GetAttributeValue("address")
    } -Credential (New-Object System.Management.Automation.PSCredential ("user", (ConvertTo-SecureString 'password' -AsPlainText -Force)))
    
    これは単純な認証ですlink .

    Visual Studioとの統合

    This builds on my previous .

    We will use Targets MSBuildで、どのサーバーを配備するかを制御し、配備後に青と緑のサーバーを切り替える.
    プロジェクトディレクトリにPowerShellスクリプトを追加します\Properties\Scripts\発行プロファイルで、これらのコード行をプロジェクトタグに追加します.
    <Target Name="DetermineUnhealthy" BeforeTargets="BeforePublish">
        <Exec Command="powershell -executionpolicy unrestricted &quot;&amp; &quot;&quot;$(MSBuildProjectDirectory)\Properties\Scripts\GetUnhealthy.ps1&quot;&quot;&quot;"
                ConsoleToMSBuild="true">
            <Output TaskParameter="ConsoleOutput" PropertyName="DeployIisAppPath"/>
        </Exec>
    </Target>
    
    このコードは、展開の開始前に不健康なサーバーを決定し、DeployIisAppPath プロパティ.
    IISのサイト下のアプリケーションの場合:
    <Target Name="DetermineUnhealthy" BeforeTargets="BeforePublish">
        <Exec Command="powershell -executionpolicy unrestricted &quot;&amp; &quot;&quot;$(MSBuildProjectDirectory)\Properties\Scripts\GetUnhealthy.ps1&quot;&quot;&quot;"
                ConsoleToMSBuild="true">
            <Output TaskParameter="ConsoleOutput" PropertyName="UnhealthySite"/>
        </Exec>
        <CreateProperty Value="$(UnhealthySite)/AppName">
            <Output TaskParameter="Value" PropertyName="DeployIisAppPath"/>
        </CreateProperty>
    </Target>
    
    配備後に直接切り替えたい場合は、次のコードを追加します.
    <Target Name="SwitchBlueGreen" AfterTargets="AfterPublish">
        <Exec Command="powershell -executionpolicy unrestricted &quot;&amp; &quot;&quot;$(MSBuildProjectDirectory)\Properties\Scripts\SwitchBlueGreen.ps1&quot;&quot;&quot;" />
    </Target>
    
    私の場合、2つのプロファイルがあります.
    これで、ゼロダウンタイムでIISに展開できます.

    参考

  • How to Deploy Anything in IIS with Zero Downtime on a Single Server
    私がここで書いたもののほとんどを私に教えました.
  • MSBuild