Eseguire Chaos in ambiente controllato nei cluster di Service FabricInduce controlled Chaos in Service Fabric clusters

I sistemi distribuiti di grandi dimensioni come le infrastrutture cloud sono intrinsecamente inaffidabili.Large-scale distributed systems like cloud infrastructures are inherently unreliable. Azure Service Fabric consente agli sviluppatori di scrivere servizi distribuiti affidabili in un'infrastruttura inaffidabile.Azure Service Fabric enables developers to write reliable distributed services on top of an unreliable infrastructure. Per scrivere servizi distribuiti affidabili in un'infrastruttura inaffidabile, gli sviluppatori devono poter testare la stabilità dei servizi quando nell'infrastruttura inaffidabile sottostante si verificano transizioni di stato complesse a causa di errori.To write robust distributed services on top of an unreliable infrastructure, developers need to be able to test the stability of their services while the underlying unreliable infrastructure is going through complicated state transitions due to faults.

Il servizio di fault injection e analisi del cluster (noto anche come servizio di analisi degli errori) offre agli sviluppatori la possibilità di causare errori per testare i servizi.The Fault Injection and Cluster Analysis Service (also known as the Fault Analysis Service) gives developers the ability to induce faults to test their services. Questi errori simulati mirati, ad esempio il riavvio di una partizione, consentono di generare le transizioni di stato più comuni.These targeted simulated faults, like restarting a partition, can help exercise the most common state transitions. Gli errori simulati mirati sono tuttavia parziali per definizione e quindi possono non coprire i bug che si verificano solo in sequenze di transizioni di stato difficili da prevedere, lunghe e complesse.However targeted simulated faults are biased by definition and thus may miss bugs that show up only in hard-to-predict, long and complicated sequence of state transitions. Per un test imparziale, è possibile usare Chaos.For an unbiased testing, you can use Chaos.

Chaos simula errori periodici con interfoliazione, normali e anomali, nel cluster per lunghi periodi di tempo.Chaos simulates periodic, interleaved faults (both graceful and ungraceful) throughout the cluster over extended periods of time. Un errore normale è costituito da una serie di chiamate API di Service Fabric, ad esempio, il riavvio della replica. Questo errore è normale, poiché si tratta di una chiusura seguita da un'apertura in una replica.A graceful fault consists of a set of Service Fabric API calls, for example, restart replica fault is a graceful fault because this is a close followed by an open on a replica. La rimozione di una replica e lo spostamento della replica primaria o della replica secondaria sono altri errori normali generati da Chaos.Remove replica, move primary replica, and move secondary replica are the other graceful faults exercised by Chaos. Sono errori anomali le uscite da processi, ad esempio il riavvio di un nodo o di un pacchetto di codice.Ungraceful faults are process exits, like restart node and restrat code pacakge.

Dopo aver configurato Chaos con la frequenza e la tipologia di errori, è possibile avviarlo attraverso le API C#, PowerShell o REST per iniziare a generare errori nel cluster e nei servizi.Once you have configured Chaos with the rate and the kind of faults, you can start Chaos through C#, Powershell, or REST API to start generating faults in the cluster and in your services. È possibile configurare Chaos per l'esecuzione per un periodo di tempo specifico (ad esempio per un'ora), dopo il quale Chaos si arresta automaticamente, oppure è possibile chiamare l'API StopChaos (C#, PowerShell o REST) per arrestarlo in qualsiasi momento.You can configure Chaos to run for a specified time period (for example, for one hour), after which Chaos stops automatically, or you can call StopChaos API (C#, Powershell, or REST) to stop it at any time.

Nota

Nella sua forma attuale, Chaos causa solo errori sicuri, il che implica che in assenza di errori esterni non si verifica mai una perdita del quorum o di dati.In its current form, Chaos induces only safe faults, which implies that in the absence of external faults a quorum loss, or data loss never occurs.

Durante l'esecuzione, Chaos genera eventi diversi che acquisiscono lo stato dell'esecuzione al momento.While Chaos is running, it produces different events that capture the state of the run at the moment. Un oggetto ExecutingFaultsEvent contiene ad esempio tutti gli errori eseguiti da Chaos nell'iterazione.For example, an ExecutingFaultsEvent contains all the faults that Chaos has decided to execute in that iteration. Un oggetto ValidationFailedEvent contiene i dettagli di un errore di convalida (problemi di integrità o di stabilità) rilevato durante la convalida del cluster.A ValidationFailedEvent contains the details of a validation failure (health or stability issues) that was found during the validation of the cluster. È possibile chiamare l'API GetChaosReport (C#, PowerShell o REST) per ottenere il report delle esecuzioni di Chaos.You can invoke the GetChaosReport API (C#, Powershell, or REST) to get the report of Chaos runs. Gli eventi vengono conservati in un oggetto Reliable ​Dictionary, che ha un criterio di troncamento influenzato da due configurazioni: MaxStoredChaosEventCount (valore predefinito 25000) e StoredActionCleanupIntervalInSeconds (valore predefinito 3600).These events get persisted in a reliable dictionary, which has a truncation policy dictated by two configurations: MaxStoredChaosEventCount (default value is 25000) and StoredActionCleanupIntervalInSeconds (default value is 3600). A ogni intervallo indicato da StoredActionCleanupIntervalInSeconds Chaos esegue un controllo e tutti gli eventi MaxStoredChaosEventCount, escluso il più recente, vengono eliminati dall'oggetto Reliable ​Dictionary.Every StoredActionCleanupIntervalInSeconds Chaos checks and all but the most recent MaxStoredChaosEventCount events, are purged from the reliable dictionary.

Errori indotti da ChaosFaults induced in Chaos

Chaos genera errori in tutto il cluster di Service Fabric e comprime in poche ore gli errori riscontrati in mesi o anni.Chaos generates faults across the entire Service Fabric cluster and compresses faults that are seen in months or years into a few hours. Grazie alla combinazione di errori con interfoliazione e un'elevata frequenza di errori è possibile trovare casi limite che altrimenti non verrebbero considerati.The combination of interleaved faults with the high fault rate finds corner cases that may otherwise be missed. Questa applicazione di Chaos consente di ottenere un notevole miglioramento della qualità del codice del servizio.This exercise of Chaos leads to a significant improvement in the code quality of the service.

Chaos induce errori delle categorie seguenti:Chaos induces faults from the following categories:

  • Riavvio di un nodoRestart a node
  • Riavvio di un pacchetto di codice distribuitoRestart a deployed code package
  • Rimozione di una replicaRemove a replica
  • Riavvio di una replicaRestart a replica
  • Spostamento di una replica primaria (configurabile)Move a primary replica (configurable)
  • Spostamento di una replica secondaria (configurabile)Move a secondary replica (configurable)

Chaos esegue più iterazioni.Chaos runs in multiple iterations. Ogni iterazione consiste in errori e convalide cluster per il periodo di tempo specificato.Each iteration consists of faults and cluster validation for the specified period. È possibile configurare anche il tempo impiegato per la stabilizzazione del cluster e il completamento della convalida.You can configure the time spent for the cluster to stabilize and for validation to succeed. Se viene rilevato un errore di convalida dei cluster, Chaos genera e mantiene un ValidationFailedEvent con il timestamp UTC e i dettagli dell'errore.If a failure is found in cluster validation, Chaos generates and persists a ValidationFailedEvent with the UTC timestamp and the failure details. Si consideri, ad esempio, un'istanza di Chaos configurata per un'esecuzione della durata di un'ora e con un massimo di tre errori simultanei.For example, consider an instance of Chaos that is set to run for an hour with a maximum of three concurrent faults. Chaos provoca tre errori, per poi convalidare l'integrità del cluster.Chaos induces three faults, and then validates the cluster health. Ripete il passaggio precedente finché non viene esplicitamente interrotto con l'API StopChaosAsync o dopo un'ora.It iterates through the previous step until it is explicitly stopped through the StopChaosAsync API or one-hour passes. Se in una qualsiasi iterazione il cluster diventa non integro, ovvero non si stabilizza o non diventa integro entro il tempo definito da MaxClusterStabilizationTimeout, Chaos genera un evento ValidationFailedEvent.If the cluster becomes unhealthy in any iteration (that is, it does not stabilize or it does not become healthy within the passed-in MaxClusterStabilizationTimeout), Chaos generates a ValidationFailedEvent. Questo evento indica che si è verificato un errore e potrebbe richiedersi un'ulteriore analisi.This event indicates that something has gone wrong and might need further investigation.

Per sapere quali errori sono stati indotti da Chaos, è possibile usare l'API GetChaosReport (PowerShell, C# o REST).To get which faults Chaos induced, you can use GetChaosReport API (powershell, C#, or REST). L'API ottiene il segmento successivo del report di Chaos in base al token di continuazione passato o all'intervallo di tempo passato.The API gets the next segment of the Chaos report based on the passed-in continuation token or the passed-in time-range. È possibile specificare ContinuationToken per ottenere il segmento successivo del report di Chaos oppure è possibile specificare l'intervallo di tempo tramite StartTimeUtc ed EndTimeUtc, ma non è possibile specificare sia ContinuationToken che l'intervallo di tempo nella stessa chiamata.You can either specify the ContinuationToken to get the next segment of the Chaos report or you can specify the time-range through StartTimeUtc and EndTimeUtc, but you cannot specify both the ContinuationToken and the time-range in the same call. Quando ci sono più di 100 eventi di Chaos, il report di Chaos viene restituito in segmenti e ogni segmento contiene non più di 100 eventi.When there are more than 100 Chaos events, the Chaos report is returned in segments where a segment contains no more than 100 Chaos events.

Opzioni di configurazione importantiImportant configuration options

  • TimeToRun: tempo totale di esecuzione di Chaos prima del completamento con esito positivo.TimeToRun: Total time that Chaos runs before it finishes with success. È possibile arrestare Chaos prima sia trascorso il tempo di esecuzione indicato da TimeToRun tramite l'API StopChaos.You can stop Chaos before it has run for the TimeToRun period through the StopChaos API.

  • MaxClusterStabilizationTimeout: tempo di attesa massimo perché il cluster diventi integro prima che venga generato un evento ValidationFailedEvent.MaxClusterStabilizationTimeout: The maximum amount of time to wait for the cluster to become healthy before producing a ValidationFailedEvent. Questa attesa consiste nel ridurre il carico nel cluster durante il ripristino.This wait is to reduce the load on the cluster while it is recovering. I controlli eseguiti sono:The checks performed are:

    • Se l'integrità del cluster è OKIf the cluster health is OK
    • Se l'integrità del servizio è OKIf the service health is OK
    • Se si ottiene la dimensione di set di replica di destinazione per la partizione di servizioIf the target replica set size is achieved for the service partition
    • Che non esistano repliche InBuildThat no InBuild replicas exist
  • MaxConcurrentFaults: numero massimo di errori simultanei indotti in ogni iterazione.MaxConcurrentFaults: The maximum number of concurrent faults that are induced in each iteration. Maggiore è il numero, più aggressivo è Chaos e più complesse saranno le combinazioni di failover e transizioni di stato a cui viene sottoposto il cluster.The higher the number, the more aggressive Chaos is and the failovers and the state transition combinations that the cluster goes through are also more complex.

Nota

Indipendentemente da quanto è alto il valore di MaxConcurrentFaults, Chaos garantisce che in assenza di errori esterni non si verifichi una perdita di quorum o di dati.Regardless how high a value MaxConcurrentFaults has, Chaos guarantees - in the absence of external faults - there is no quorum loss or data loss.

  • EnableMoveReplicaFaults: abilita o disabilita gli errori che causano lo spostamento delle repliche primarie o secondarie.EnableMoveReplicaFaults: Enables or disables the faults that cause the primary or secondary replicas to move. Questi errori sono disabilitati per impostazione predefinita.These faults are disabled by default.
  • WaitTimeBetweenIterations: quantità di tempo di attesa tra due iterazioni.WaitTimeBetweenIterations: The amount of time to wait between iterations. Indica il tempo durante cui Chaos rimane in pausa dopo l'esecuzione di un ciclo di errori e il completamento della convalida dell'integrità del cluster corrispondente.That is, the amount of time Chaos will pause after having executed a round of faults and having finished the corresponding validation of the health of the cluster. Maggiore è il valore, minore è la frequenza media di inserimento degli errori.The higher the value, the lower is the average fault injection rate.
  • WaitTimeBetweenFaults: quantità di tempo di attesa tra due errori consecutivi in una singola iterazione.WaitTimeBetweenFaults: The amount of time to wait between two consecutive faults in a single iteration. Maggiore è il valore, minore sarà la concorrenza degli errori, ovvero la loro sovrapposizione.The higher the value, the lower the concurrency of (or the overlap between) faults.
  • ClusterHealthPolicy: criteri di integrità del cluster usati per convalidare l'integrità del cluster tra iterazioni di Chaos.ClusterHealthPolicy: Cluster health policy is used to validate the health of the cluster in between Chaos iterations. Se si verifica un errore di integrità del cluster o un'eccezione imprevista durante l'esecuzione dell'errore, Chaos attende 30 minuti prima del successivo controllo di integrità, per fornire al cluster il tempo di recupero.If the cluster health is in error or if an unexpected exception happens during fault execution, Chaos will wait for 30 minutes before the next health-check - to provide the cluster with some time to recuperate.
  • Context: raccolta di coppie chiave-valore di tipo (string, string).Context: A collection of (string, string) type key-value pairs. La mappa può essere usata per registrare le informazioni sull'esecuzione di Chaos.The map can be used to record information about the Chaos run. Non possono esserci più di 100 coppie di questo tipo e ogni stringa (chiave o valore) può essere costituita da un massimo di 4095 caratteri.There cannot be more than 100 such pairs and each string (key or value) can be at most 4095 characters long. Questa mappa viene impostata dalla funzione di avvio dell'esecuzione di Chaos per l'archiviazione facoltativa del contesto dell'esecuzione specifica.This map is set by the starter of the Chaos run to optionally store the context about the specific run.

Come eseguire ChaosHow to run Chaos

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

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

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 void 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
            };

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

            try
            {
                client.TestManager.StartChaosAsync(parameters).GetAwaiter().GetResult();
            }
            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)
                        ? client.TestManager.GetChaosReportAsync(filter).GetAwaiter().GetResult()
                        : client.TestManager.GetChaosReportAsync(continuationToken).GetAwaiter().GetResult();
                }
                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;
                    }

                    Task.Delay(TimeSpan.FromSeconds(1.0)).GetAwaiter().GetResult();
                    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;
                }

                Task.Delay(TimeSpan.FromSeconds(1.0)).GetAwaiter().GetResult();
            }
        }
    }
}
$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"}

Connect-ServiceFabricCluster $clusterConnectionString

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

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

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
}