Megosztás a következőn keresztül:


Szabályozott káosz indukálása Service Fabric-fürtökben

A nagy léptékű elosztott rendszerek, például a felhőinfrastruktúrák eredendően megbízhatatlanok. Az Azure Service Fabric lehetővé teszi a fejlesztők számára, hogy megbízható elosztott szolgáltatásokat írjanak egy megbízhatatlan infrastruktúra fölé. Ahhoz, hogy robusztus elosztott szolgáltatásokat írjanak egy megbízhatatlan infrastruktúrára, a fejlesztőknek képesnek kell lenniük a szolgáltatások stabilitásának tesztelésére, miközben a mögöttes megbízhatatlan infrastruktúra a hibák miatt bonyolult állapotváltásokon megy keresztül.

A hibainjektálási és fürtelemzési szolgáltatás (más néven a Hibaelemzési szolgáltatás) lehetővé teszi a fejlesztők számára, hogy hibákat indukáljanak a szolgáltatásaik teszteléséhez. Ezek a célzott szimulált hibák, például egy partíció újraindítása segíthetnek a leggyakoribb állapotváltások gyakorlásában. A célzott szimulált hibák azonban definíció szerint elfogultak, így előfordulhat, hogy nem jelennek meg olyan hibák, amelyek csak nehezen előrejelezhető, hosszú és bonyolult állapotváltások sorozatában jelennek meg. Az elfogulatlan teszteléshez használhatja a Chaos-t.

A káosz időszakos, egymást átfedő hibákat szimulál (mind kecses, mind nem szégyenletes) a fürtben hosszabb időn keresztül. A kecses hiba Service Fabric API-hívások készletéből áll, például az újraindítási replika hibája egy kecses hiba, mert ez egy szoros, majd egy replikán való megnyitás. A replika eltávolítása, az elsődleges replika áthelyezése, a másodlagos replika áthelyezése és az áthelyezési példány a Káosz által gyakorolt többi kecses hiba. A nem gyalázatos hibák a folyamatból való kilépések, például a csomópont újraindítása és a kódcsomag újraindítása.

Miután konfigurálta a Chaos-t a sebességével és a hibák típusával, elindíthatja a Chaost a C#, a PowerShell vagy a REST API használatával, hogy hibákat generáljon a fürtben és a szolgáltatásokban. Konfigurálhatja a Chaos-t úgy, hogy egy adott időszakra (például egy órára) fusson, amely után a Chaos automatikusan leáll, vagy bármikor leállíthatja a StopChaos API-t (C#, PowerShell vagy REST).

Megjegyzés

Jelenlegi formájában a Chaos csak a biztonságos hibákat idézi elő, ami azt jelenti, hogy külső hibák hiányában kvórumvesztés, vagy adatvesztés soha nem fordul elő.

Miközben a Chaos fut, különböző eseményeket hoz létre, amelyek rögzítik a futtatás állapotát. Egy ExecuteingFaultsEvent például tartalmazza azokat a hibákat, amelyeket a Chaos az iterációban végrehajtott. A ValidationFailedEvent a fürt érvényesítése során észlelt érvényesítési hiba (állapot- vagy stabilitási problémák) részleteit tartalmazza. Meghívhatja a GetChaosReport API-t (C#, PowerShell vagy REST) a Chaos-futtatások jelentésének lekéréséhez. Ezek az események egy megbízható szótárban maradnak meg, amelynek csonkítási szabályzatát két konfiguráció szabja meg: MaxStoredChaosEventCount (alapértelmezett érték: 25000) és StoredActionCleanupIntervalInSeconds (alapértelmezett érték: 3600). Minden StoredActionCleanupIntervalInSeconds Chaos-ellenőrzés és az összes legutóbbi MaxStoredChaosEventCount esemény ki van ürítve a megbízható szótárból.

Káosz okozta hibák

A káosz a teljes Service Fabric-fürtön hibákat generál, és néhány órára tömöríti a hónapokban vagy években látható hibákat. Az összefűzött hibák és a magas hibaarány együttese olyan sarokeseteket talál, amelyek egyébként kimaradhatnak. A Chaos e gyakorlata a szolgáltatás kódminőségének jelentős javulásához vezet.

A káosz a következő kategóriák hibáit idézi elő:

  • Csomópont újraindítása
  • Üzembe helyezett kódcsomag újraindítása
  • Replika eltávolítása
  • Replika újraindítása
  • Elsődleges replika áthelyezése (konfigurálható)
  • Másodlagos replika áthelyezése (konfigurálható)
  • Példány áthelyezése

A káosz több iterációban fut. Minden iteráció a megadott időszakra vonatkozó hibákból és fürtérvényesítésből áll. Konfigurálhatja a fürt stabilizálásához és a sikeres ellenőrzéshez szükséges időt. Ha hiba található a fürtérvényesítésben, a Chaos létrehoz és megőriz egy ValidationFailedEvent értéket az UTC időbélyeggel és a hiba részleteivel. Vegyük például a Chaos egy példányát, amely úgy van beállítva, hogy egy órán keresztül fusson, legfeljebb három egyidejű hibával. A káosz három hibát okoz, majd ellenőrzi a fürt állapotát. Végighalad az előző lépésen, amíg explicit módon le nem állítja a StopChaosAsync API-val, vagy egy óra elteltével. Ha a fürt bármilyen iterációban nem kifogástalan állapotúvá válik (azaz nem stabilizálódik, vagy nem lesz kifogástalan állapotú a maxClusterStabilizationTimeout átadott verziójában), a Chaos egy ValidationFailedEvent értéket hoz létre. Ez az esemény azt jelzi, hogy valami hiba történt, és további vizsgálatra lehet szükség.

A Káosz által okozott hibák lekéréséhez használhatja a GetChaosReport API-t (PowerShell, C#vagy REST). Az API lekéri a Chaos-jelentés következő szegmensét az átadott folytatási jogkivonat vagy az átadott időtartomány alapján. Megadhatja a ContinuationToken értéket a Chaos-jelentés következő szegmensének lekéréséhez, vagy megadhatja az időtartományt a StartTimeUtc és az EndTimeUtc használatával, de ugyanabban a hívásban nem adhatja meg a ContinuationToken és az időtartományt is. Ha több mint 100 Chaos-esemény van, a Chaos-jelentés olyan szegmensekben jelenik meg, amelyekben egy szegmens legfeljebb 100 Chaos-eseményt tartalmaz.

Fontos konfigurációs beállítások

  • TimeToRun: A Chaos teljes ideje, mielőtt sikerrel befejeződik. A StopChaos API-val leállíthatja a Chaost, mielőtt a TimeToRun időszakra futott volna.

  • MaxClusterStabilizationTimeout: Az a maximális időtartam, amíg a fürt kifogástalan állapotba kerül, mielőtt létrejön egy ValidationFailedEvent. Ez a várakozás csökkenti a fürt terhelését a helyreállítás során. Az elvégzett ellenőrzések a következők:

    • Ha a fürt állapota rendben van
    • Ha a szolgáltatás állapota rendben van
    • Ha a célreplikakészlet mérete elérhető a szolgáltatáspartícióhoz
    • Hogy nem léteznek InBuild replikák
  • MaxConcurrentFaults: Az egyes iterációkban kiváltott egyidejű hibák maximális száma. Minél magasabb a szám, annál agresszívabb a Káosz, és a feladatátvételek és a fürt által átvitt állapotváltási kombinációk is összetettebbek.

Megjegyzés

Függetlenül attól, hogy a MaxConcurrentFaults milyen magas értékkel rendelkezik, a Chaos garantálja - külső hibák hiányában - nincs kvórumvesztés vagy adatvesztés.

  • EnableMoveReplicaFaults: Engedélyezi vagy letiltja azokat a hibákat, amelyek az elsődleges, másodlagos replikák vagy példányok áthelyezését okozzák. Ezek a hibák alapértelmezés szerint engedélyezve vannak.
  • WaitTimeBetweenIterations: Az iterációk közötti várakozási idő. Ez azt az időtartamot jelzi, amikor a Chaos szünetelteti a hibát, és befejezte a fürt állapotának megfelelő ellenőrzését. Minél magasabb az érték, annál alacsonyabb az átlagos hibainjektálási arány.
  • WaitTimeBetweenFaults: A két egymást követő hiba közötti várakozási idő egyetlen iterációban. Minél nagyobb az érték, annál alacsonyabb a hibák egyidejűsége (vagy átfedése között).
  • ClusterHealthPolicy: A fürt állapotszabályzata a fürt állapotának ellenőrzésére szolgál a Káosz iterációk között. Ha a fürt állapota hibás, vagy ha váratlan kivétel történik a hiba végrehajtása során, a Chaos 30 percet vár a következő állapot-ellenőrzés előtt , hogy időt biztosítson a fürtnek az újraéledéshez.
  • Környezet: Kulcs-érték párok (sztring, sztring) gyűjteménye. A térkép a Chaos-futtatás adatainak rögzítésére használható. Legfeljebb 100 ilyen pár lehet, és minden sztring (kulcs vagy érték) legfeljebb 4095 karakter hosszúságú lehet. Ezt a térképet a Chaos-futtatás kezdője állítja be, hogy opcionálisan tárolja az adott futtatás környezetét.
  • ChaosTargetFilter: Ezzel a szűrővel a Chaos-hibákat csak bizonyos csomóponttípusokra vagy csak bizonyos alkalmazáspéldányokra lehet célozni. Ha a ChaosTargetFilter nincs használatban, a Chaos az összes fürt-entitást meghibásodik. A ChaosTargetFilter használata esetén a Chaos csak azokat az entitásokat hibásítja meg, amelyek megfelelnek a ChaosTargetFilter specifikációnak. A NodeTypeInclusionList és az ApplicationInclusionList csak az egyesítő szemantika engedélyezését teszi lehetővé. Más szóval a NodeTypeInclusionList és az ApplicationInclusionList metszetét nem lehet megadni. Például nem adható meg "az alkalmazás hibája csak akkor, ha az adott csomóponttípuson van". Miután egy entitást a NodeTypeInclusionList vagy az ApplicationInclusionList tartalmaz, az entitás nem zárható ki a ChaosTargetFilter használatával. Még akkor is, ha az applicationX nem jelenik meg az ApplicationInclusionListban, néhány Káosz iterációban az ApplicationX hibás lehet, mert történetesen a NodeTypeInclusionList részét képező nodeTypeY csomóponton található. Ha a NodeTypeInclusionList és az ApplicationInclusionList is null értékű vagy üres, a rendszer argumentumException értéket ad.
    • NodeTypeInclusionList: A Chaos-hibák között szerepeltetni kívánt csomóponttípusok listája. Ezen csomóponttípusok csomópontjainál minden hibatípus (csomópont újraindítása, codepackage újraindítása, replika eltávolítása, replika újraindítása, elsődleges áthelyezés, másodlagos áthelyezés és példány áthelyezése) engedélyezve van. Ha egy csomóponttípus (például NodeTypeX) nem jelenik meg a NodeTypeInclusionList listán, akkor a csomópontszintű hibák (például a NodeRestart) soha nem lesznek engedélyezve a NodeTypeX csomópontjainál, de a kódcsomag- és replikahibák továbbra is engedélyezhetők a NodeTypeX esetében, ha az ApplicationInclusionList egyik alkalmazása a NodeTypeX egyik csomópontján található. A listában legfeljebb 100 csomóponttípus neve szerepelhet, a szám növeléséhez konfigurációfrissítésre van szükség a MaxNumberOfNodeTypesInChaosTargetFilter konfigurációhoz.
    • ApplicationInclusionList: Az alkalmazás URI-jainak listája, amely szerepel a Chaos-hibák között. Ezeknek az alkalmazásoknak a szolgáltatásaihoz tartozó összes replika replika replikahibákra (replika újraindítása, replika eltávolítása, elsődleges áthelyezés, másodlagos áthelyezés és példány áthelyezése) a Chaos által kezelhető. A káosz csak akkor indíthat újra egy kódcsomagot, ha a kódcsomag csak ezeknek az alkalmazásoknak a replikáit üzemelteti. Ha egy alkalmazás nem jelenik meg ebben a listában, akkor is hibás lehet bizonyos Chaos-iterációkban, ha az alkalmazás a NodeTypeInclusionList részét képező csomóponttípus egyik csomópontjára kerül. Ha azonban az applicationX az elhelyezési korlátozásokon keresztül kötődik a nodeTypeY-hoz, és az ApplicationX hiányzik az ApplicationInclusionList listából, és a nodeTypeY hiányzik a NodeTypeInclusionList listából, akkor az ApplicationX soha nem lesz hibás. A listában legfeljebb 1000 alkalmazásnév szerepelhet, a szám növeléséhez konfigurációfrissítésre van szükség a MaxNumberOfApplicationsInChaosTargetFilter konfigurációhoz.

A Chaos futtatása

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Fabric;

using System.Diagnostics;
using System.Fabric.Chaos.DataStructures;

static class Program
{
    private class ChaosEventComparer : IEqualityComparer<ChaosEvent>
    {
        public bool Equals(ChaosEvent x, ChaosEvent y)
        {
            return x.TimeStampUtc.Equals(y.TimeStampUtc);
        }
        public int GetHashCode(ChaosEvent obj)
        {
            return obj.TimeStampUtc.GetHashCode();
        }
    }

    static async Task Main(string[] args)
    {
        var clusterConnectionString = "localhost:19000";
        using (var client = new FabricClient(clusterConnectionString))
        {
            var startTimeUtc = DateTime.UtcNow;

            // The maximum amount of time to wait for all cluster entities to become stable and healthy. 
            // Chaos executes in iterations and at the start of each iteration it validates the health of cluster
            // entities. 
            // During validation if a cluster entity is not stable and healthy within
            // MaxClusterStabilizationTimeoutInSeconds, Chaos generates a validation failed event.
            var maxClusterStabilizationTimeout = TimeSpan.FromSeconds(30.0);

            var timeToRun = TimeSpan.FromMinutes(60.0);

            // MaxConcurrentFaults is the maximum number of concurrent faults induced per iteration. 
            // Chaos executes in iterations and two consecutive iterations are separated by a validation phase.
            // The higher the concurrency, the more aggressive the injection of faults -- inducing more complex
            // series of states to uncover bugs.
            // The recommendation is to start with a value of 2 or 3 and to exercise caution while moving up.
            var maxConcurrentFaults = 3;

            // Describes a map, which is a collection of (string, string) type key-value pairs. The map can be
            // used to record information about the Chaos run. There cannot be more than 100 such pairs and
            // each string (key or value) can be at most 4095 characters long.
            // This map is set by the starter of the Chaos run to optionally store the context about the specific run.
            var startContext = new Dictionary<string, string>{{"ReasonForStart", "Testing"}};

            // Time-separation (in seconds) between two consecutive iterations of Chaos. The larger the value, the
            // lower the fault injection rate.
            var waitTimeBetweenIterations = TimeSpan.FromSeconds(10);

            // Wait time (in seconds) between consecutive faults within a single iteration.
            // The larger the value, the lower the overlapping between faults and the simpler the sequence of
            // state transitions that the cluster goes through. 
            // The recommendation is to start with a value between 1 and 5 and exercise caution while moving up.
            var waitTimeBetweenFaults = TimeSpan.Zero;

            // Passed-in cluster health policy is used to validate health of the cluster in between Chaos iterations. 
            var clusterHealthPolicy = new ClusterHealthPolicy
            {
                ConsiderWarningAsError = false,
                MaxPercentUnhealthyApplications = 100,
                MaxPercentUnhealthyNodes = 100
            };

            // All types of faults, restart node, restart code package, restart replica, move primary
            // replica, move secondary replica, and move instance will happen for nodes of type 'FrontEndType'
            var nodetypeInclusionList = new List<string> { "FrontEndType"};

            // In addition to the faults included by nodetypeInclusionList,
            // restart code package, restart replica, move primary replica, move secondary replica,
            //  and move instance faults will happen for 'fabric:/TestApp2' even if a replica or code
            // package from 'fabric:/TestApp2' is residing on a node which is not of type included
            // in nodeypeInclusionList.
            var applicationInclusionList = new List<string> { "fabric:/TestApp2" };

            // List of cluster entities to target for Chaos faults.
            var chaosTargetFilter = new ChaosTargetFilter
            {
                NodeTypeInclusionList = nodetypeInclusionList,
                ApplicationInclusionList = applicationInclusionList
            };

            var parameters = new ChaosParameters(
                maxClusterStabilizationTimeout,
                maxConcurrentFaults,
                true, /* EnableMoveReplicaFault */
                timeToRun,
                startContext,
                waitTimeBetweenIterations,
                waitTimeBetweenFaults,
                clusterHealthPolicy) {ChaosTargetFilter = chaosTargetFilter};

            try
            {
                await client.TestManager.StartChaosAsync(parameters);
            }
            catch (FabricChaosAlreadyRunningException)
            {
                Console.WriteLine("An instance of Chaos is already running in the cluster.");
            }

            var filter = new ChaosReportFilter(startTimeUtc, DateTime.MaxValue);

            var eventSet = new HashSet<ChaosEvent>(new ChaosEventComparer());

            string continuationToken = null;

            while (true)
            {
                ChaosReport report;
                try
                {
                    report = string.IsNullOrEmpty(continuationToken)
                        ? await client.TestManager.GetChaosReportAsync(filter)
                        : await client.TestManager.GetChaosReportAsync(continuationToken);
                }
                catch (Exception e)
                {
                    if (e is FabricTransientException)
                    {
                        Console.WriteLine("A transient exception happened: '{0}'", e);
                    }
                    else if(e is TimeoutException)
                    {
                        Console.WriteLine("A timeout exception happened: '{0}'", e);
                    }
                    else
                    {
                        throw;
                    }

                    await Task.Delay(TimeSpan.FromSeconds(1.0));
                    continue;
                }

                continuationToken = report.ContinuationToken;

                foreach (var chaosEvent in report.History)
                {
                    if (eventSet.Add(chaosEvent))
                    {
                        Console.WriteLine(chaosEvent);
                    }
                }

                // When Chaos stops, a StoppedEvent is created.
                // If a StoppedEvent is found, exit the loop.
                var lastEvent = report.History.LastOrDefault();

                if (lastEvent is StoppedEvent)
                {
                    break;
                }

                await Task.Delay(TimeSpan.FromSeconds(1.0));
            }
        }
    }
}
$clusterConnectionString = "localhost:19000"
$timeToRunMinute = 60

# The maximum amount of time to wait for all cluster entities to become stable and healthy.
# Chaos executes in iterations and at the start of each iteration it validates the health of cluster entities.
# During validation if a cluster entity is not stable and healthy within MaxClusterStabilizationTimeoutInSeconds,
# Chaos generates a validation failed event.
$maxClusterStabilizationTimeSecs = 30

# MaxConcurrentFaults is the maximum number of concurrent faults induced per iteration.
# Chaos executes in iterations and two consecutive iterations are separated by a validation phase.
# The higher the concurrency, the more aggressive the injection of faults -- inducing more complex series of
# states to uncover bugs.
# The recommendation is to start with a value of 2 or 3 and to exercise caution while moving up.
$maxConcurrentFaults = 3

# Time-separation (in seconds) between two consecutive iterations of Chaos. The larger the value, the lower the
# fault injection rate.
$waitTimeBetweenIterationsSec = 10

# Wait time (in seconds) between consecutive faults within a single iteration.
# The larger the value, the lower the overlapping between faults and the simpler the sequence of state
# transitions that the cluster goes through.
# The recommendation is to start with a value between 1 and 5 and exercise caution while moving up.
$waitTimeBetweenFaultsSec = 0

# Passed-in cluster health policy is used to validate health of the cluster in between Chaos iterations. 
$clusterHealthPolicy = new-object -TypeName System.Fabric.Health.ClusterHealthPolicy
$clusterHealthPolicy.MaxPercentUnhealthyNodes = 100
$clusterHealthPolicy.MaxPercentUnhealthyApplications = 100
$clusterHealthPolicy.ConsiderWarningAsError = $False

# Describes a map, which is a collection of (string, string) type key-value pairs. The map can be used to record
# information about the Chaos run.
# There cannot be more than 100 such pairs and each string (key or value) can be at most 4095 characters long.
# This map is set by the starter of the Chaos run to optionally store the context about the specific run.
$context = @{"ReasonForStart" = "Testing"}

#List of cluster entities to target for Chaos faults.
$chaosTargetFilter = new-object -TypeName System.Fabric.Chaos.DataStructures.ChaosTargetFilter
$chaosTargetFilter.NodeTypeInclusionList = new-object -TypeName "System.Collections.Generic.List[String]"

# All types of faults, restart node, restart code package, restart replica, move primary replica, and move
# secondary replica will happen for nodes of type 'FrontEndType'
$chaosTargetFilter.NodeTypeInclusionList.AddRange( [string[]]@("FrontEndType") )
$chaosTargetFilter.ApplicationInclusionList = new-object -TypeName "System.Collections.Generic.List[String]"

# In addition to the faults included by nodetypeInclusionList, 
# restart code package, restart replica, move primary replica, move secondary replica faults will happen for
# 'fabric:/TestApp2' even if a replica or code package from 'fabric:/TestApp2' is residing on a node which is
# not of type included in nodeypeInclusionList.
$chaosTargetFilter.ApplicationInclusionList.Add("fabric:/TestApp2")

Connect-ServiceFabricCluster $clusterConnectionString

$events = @{}
$now = [System.DateTime]::UtcNow

Start-ServiceFabricChaos -TimeToRunMinute $timeToRunMinute -MaxConcurrentFaults $maxConcurrentFaults -MaxClusterStabilizationTimeoutSec $maxClusterStabilizationTimeSecs -EnableMoveReplicaFaults -WaitTimeBetweenIterationsSec $waitTimeBetweenIterationsSec -WaitTimeBetweenFaultsSec $waitTimeBetweenFaultsSec -ClusterHealthPolicy $clusterHealthPolicy -ChaosTargetFilter $chaosTargetFilter -Context $context

while($true)
{
    $stopped = $false
    $report = Get-ServiceFabricChaosReport -StartTimeUtc $now -EndTimeUtc ([System.DateTime]::MaxValue)

    foreach ($e in $report.History) {

        if(-Not ($events.Contains($e.TimeStampUtc.Ticks)))
        {
            $events.Add($e.TimeStampUtc.Ticks, $e)
            if($e -is [System.Fabric.Chaos.DataStructures.ValidationFailedEvent])
            {
                Write-Host -BackgroundColor White -ForegroundColor Red $e
            }
            else
            {
                Write-Host $e
                # When Chaos stops, a StoppedEvent is created.
                # If a StoppedEvent is found, exit the loop.
                if($e -is [System.Fabric.Chaos.DataStructures.StoppedEvent])
                {
                    return
                }
            }
        }
    }

    Start-Sleep -Seconds 1
}