Service Fabric kümelerinde denetimli Chaos'a neden olun

Bulut altyapıları gibi büyük ölçekli dağıtılmış sistemler doğal olarak güvenilir değildir. Azure Service Fabric, geliştiricilerin güvenilir olmayan bir altyapının üzerine güvenilir dağıtılmış hizmetler yazmasına olanak tanır. Güvenilir olmayan bir altyapının üzerine sağlam dağıtılmış hizmetler yazmak için geliştiricilerin hizmetlerinin kararlılığını test edebilmesi gerekirken, temel alınan güvenilmez altyapı hatalardan dolayı karmaşık durum geçişlerinden geçmektedir.

Hata Ekleme ve Küme Analizi Hizmeti (Hata Analizi Hizmeti olarak da bilinir) geliştiricilere hizmetlerini test etmek için hataları başlatma olanağı sağlar. Bir bölümü yeniden başlatma gibi bu hedeflenen sanal hatalar, en yaygın durum geçişlerinin kullanılmasına yardımcı olabilir. Bununla birlikte, hedeflenen simülasyon hataları tanım gereği taraflıdır ve bu nedenle yalnızca tahmin etmek zor, uzun ve karmaşık durum geçişleri sırasında ortaya çıkan hataları kaçırabilir. Taraflı olmayan bir test için Chaos kullanabilirsiniz.

Kaos, küme genelinde uzun süreler boyunca düzenli aralıklarla oluşan hataların (hem düzgün hem de düzgün olmayan) benzetimini gerçekleştirir. Düzgün bir hata, bir dizi Service Fabric API çağrısından oluşur. Örneğin, çoğaltmayı yeniden başlatma hatası düzgün bir hatadır çünkü bu bir çoğaltmada açık tarafından yakından takip edilir. Çoğaltmayı kaldırma, birincil çoğaltmayı taşıma, ikincil çoğaltmayı taşıma ve taşıma örneği, Chaos tarafından yapılan diğer düzgün hatalardır. Düzgün olmayan hatalar, yeniden başlatma düğümü ve yeniden başlatma kod paketi gibi işlem çıkışlarıdır.

Chaos'u oran ve hata türleriyle yapılandırdıktan sonra, kümede ve hizmetlerinizde hata oluşturmaya başlamak için Chaos'u C#, PowerShell veya REST API aracılığıyla başlatabilirsiniz. Chaos'u belirli bir süre (örneğin, bir saat) çalışacak şekilde yapılandırabilir, bundan sonra Chaos otomatik olarak durdurulabilir veya istediğiniz zaman durdurmak için StopChaos API'sini (C#, PowerShell veya REST) çağırabilirsiniz.

Not

Mevcut biçimiyle Chaos, yalnızca güvenli hatalara neden olur ve bu da dış hataların yokluğunda çekirdek kaybı veya veri kaybının hiçbir zaman gerçekleşmediğini gösterir.

Chaos çalışırken, şu anda çalıştırmanın durumunu yakalayan farklı olaylar üretir. Örneğin, ExecuteingFaultsEvent, Chaos'un bu yinelemede yürütmeye karar vermiş olduğu tüm hataları içerir. ValidationFailedEvent, kümenin doğrulanması sırasında bulunan doğrulama hatasının (sistem durumu veya kararlılık sorunları) ayrıntılarını içerir. Kaos çalıştırmaları raporunu almak için GetChaosReport API'sini (C#, PowerShell veya REST) çağırabilirsiniz. Bu olaylar, iki yapılandırma tarafından dikte edilen bir kesme ilkesine sahip olan güvenilir bir sözlükte kalıcı hale getirilir: MaxStoredChaosEventCount (varsayılan değer 25000) ve StoredActionCleanupIntervalInSeconds (varsayılan değer 3600'dür). Tüm StoredActionCleanupIntervalInSeconds Chaos denetimleri ve en son MaxStoredChaosEventCount olayları dışında tümü güvenilir sözlükten temizlenir.

Kaosa Neden Olan Hatalar

Kaos, Service Fabric kümesinin tamamında hata oluşturur ve aylar veya yıllar içinde görülen hataları birkaç saate sıkıştırır. Araya kaydedilen hataların yüksek hata oranıyla birleşimi, aksi takdirde atlanabilecek köşe durumlarını bulur. Chaos'un bu alıştırması, hizmetin kod kalitesinde önemli bir gelişmeye yol açar.

Kaos, aşağıdaki kategorilerdeki hataların neden olmasıdır:

  • Düğümü yeniden başlatma
  • Dağıtılan kod paketini yeniden başlatma
  • Çoğaltmayı kaldırma
  • Çoğaltmayı yeniden başlatma
  • Birincil çoğaltmayı taşıma (yapılandırılabilir)
  • İkincil çoğaltmayı taşıma (yapılandırılabilir)
  • Örneği taşıma

Kaos birden çok yinelemede çalışır. Her yineleme, belirtilen süre için hatalardan ve küme doğrulamasından oluşur. Kümenin dengelenmesi ve doğrulamanın başarılı olması için harcanan süreyi yapılandırabilirsiniz. Küme doğrulamada bir hata bulunursa Chaos, UTC zaman damgası ve hata ayrıntılarıyla bir ValidationFailedEvent oluşturur ve kalıcı hale getirir. Örneğin, en fazla üç eşzamanlı hata ile bir saat boyunca çalışacak şekilde ayarlanmış bir Chaos örneğini düşünün. Kaos üç hataya neden olur ve kümenin durumunu doğrular. StopChaosAsync API'sinden veya bir saatlik geçişlerden açıkça durdurulana kadar önceki adım boyunca yinelenir. Küme herhangi bir yinelemede iyi durumda değilse (yani, kararlı olmaz veya geçirilen MaxClusterStabilizationTimeout içinde iyi duruma gelmezse), Chaos bir ValidationFailedEvent oluşturur. Bu olay bir sorun olduğunu ve daha fazla araştırma gerekebileceğini gösterir.

Kaosun neden olduğu hataları almak için GetChaosReport API'sini (PowerShell, C# veya REST) kullanabilirsiniz. API, geçirilen devamlılık belirtecini veya geçirilen zaman aralığını temel alarak Chaos raporunun bir sonraki segmentini alır. Chaos raporunun bir sonraki segmentini almak için ContinuationToken'ı belirtebilir veya StartTimeUtc ve EndTimeUtc aracılığıyla zaman aralığını belirtebilirsiniz, ancak aynı çağrıda continuationToken ve zaman aralığını belirtemezsiniz. 100'den fazla Chaos olayı olduğunda, Chaos raporu bir segmentin 100'den fazla Chaos olayı içermediği segmentlerde döndürülür.

Önemli yapılandırma seçenekleri

  • TimeToRun: Chaos'un başarılı bir şekilde bitmeden önce çalıştığı toplam süre. Chaos'u StopChaos API'sinde TimeToRun dönemi boyunca çalışmadan önce durdurabilirsiniz.

  • MaxClusterStabilizationTimeout: ValidationFailedEvent oluşturmadan önce kümenin iyi duruma gelmesini bekleme süresi üst sınırı. Bu bekleme, küme kurtarılırken küme üzerindeki yükü azaltmaktır. Gerçekleştirilen denetimler şunlardır:

    • Küme durumunun uygun olup olmadığını
    • Hizmet durumu iyiyse
    • Hizmet bölümü için hedef çoğaltma kümesi boyutuna ulaşıldıysa
    • InBuild çoğaltması olmadığını
  • MaxConcurrentFaults: Her yinelemede tetiklenen en fazla eşzamanlı hata sayısı. Sayı ne kadar yüksek olursa, Kaos o kadar agresif olur ve yük devretmeler ve kümenin geçtiği durum geçiş bileşimleri de daha karmaşıktır.

Not

MaxConcurrentFaults değerinin ne kadar yüksek olduğuna bakılmaksızın, Chaos garantileri - dış hataların yokluğunda - çekirdek kaybı veya veri kaybı yoktur.

  • EnableMoveReplicaFaults: Birincil, ikincil çoğaltmaların veya örneklerin taşınmasına neden olan hataları etkinleştirir veya devre dışı bırakır. Bu hatalar varsayılan olarak etkindir.
  • WaitTimeBetweenIterations: Yinelemeler arasında beklenme süresi. Diğer bir ifadeyle, bir dizi hata yürütüldükten ve kümenin sistem durumunun ilgili doğrulamasını tamamladıktan sonra Chaos'un duraklatılacağı süre. Değer ne kadar yüksekse, ortalama hata ekleme oranı da o kadar düşüktür.
  • WaitTimeBetweenFaults: Tek bir yinelemede ardışık iki hata arasında beklenme süresi. Değer ne kadar yüksekse hataların eşzamanlılığı (veya çakışması) o kadar düşük olur.
  • ClusterHealthPolicy: Küme sistem durumu ilkesi, Kaos yinelemeleri arasında kümenin durumunu doğrulamak için kullanılır. Kümenin sistem durumu hatalıysa veya hata yürütme sırasında beklenmeyen bir özel durum oluşursa, Chaos kümenin kurtarılabilmesi için biraz zaman sağlamak üzere bir sonraki sistem durumu denetiminden önce 30 dakika bekler.
  • Bağlam: (dize, dize) türündeki anahtar-değer çiftleri koleksiyonu. Harita, Chaos çalıştırması hakkındaki bilgileri kaydetmek için kullanılabilir. Bu tür çiftler 100'den fazla olamaz ve her dize (anahtar veya değer) en fazla 4095 karakter uzunluğunda olabilir. Bu harita, isteğe bağlı olarak belirli bir çalıştırmayla ilgili bağlamı depolamak için Chaos çalıştırmasının başlangıcı tarafından ayarlanır.
  • ChaosTargetFilter: Bu filtre, Chaos hatalarını yalnızca belirli düğüm türlerine veya yalnızca belirli uygulama örneklerine hedeflemek için kullanılabilir. ChaosTargetFilter kullanılmazsa, Chaos tüm küme varlıklarını hataya neden olur. ChaosTargetFilter kullanılıyorsa, Chaos yalnızca ChaosTargetFilter belirtimini karşılayan varlıkları hataya neden olur. NodeTypeInclusionList ve ApplicationInclusionList yalnızca birleşim semantiğine izin verir. Başka bir deyişle, NodeTypeInclusionList ve ApplicationInclusionList'in kesişimini belirtmek mümkün değildir. Örneğin, "bu uygulama yalnızca bu düğüm türünde olduğunda hata" belirtilmesi mümkün değildir. NodeTypeInclusionList veya ApplicationInclusionList'e bir varlık eklendikten sonra, bu varlık ChaosTargetFilter kullanılarak dışlanamaz. ApplicationInclusionList'te applicationX görünmese bile, nodeTypeInclusionList içinde bulunan nodeTypeY düğümünde olduğundan bazı Chaos yinelemelerinde applicationX hatası oluşabilir. Hem NodeTypeInclusionList hem de ApplicationInclusionList null veya boşsa, bir ArgumentException oluşturulur.
    • NodeTypeInclusionList: Chaos hatalarına dahil etmek için düğüm türlerinin listesi. Bu düğüm türlerinin düğümleri için tüm hata türleri (düğümü yeniden başlatma, kod paketini yeniden başlatma, çoğaltmayı kaldırma, çoğaltmayı yeniden başlatma, birincil öğeyi taşıma, ikincil öğeyi taşıma ve taşıma örneği) etkinleştirilir. NodeTypeInclusionList içinde bir nodetype (örneğin NodeTypeX) görünmüyorsa NodeTypeX düğümleri için düğüm düzeyinde hatalar (NodeRestart gibi) hiçbir zaman etkinleştirilmez, ancak ApplicationInclusionList içindeki bir uygulama NodeTypeX düğümünde bulunuyorsa NodeTypeX için kod paketi ve çoğaltma hataları yine etkinleştirilebilir. Bu listeye en fazla 100 düğüm türü adı eklenebilir; bu sayıyı artırmak için MaxNumberOfNodeTypesInChaosTargetFilter yapılandırması için bir yapılandırma yükseltmesi gerekir.
    • ApplicationInclusionList: Chaos hatalarına dahil etmek için uygulama URI'lerinin listesi. Bu uygulamaların hizmetlerine ait tüm çoğaltmalar Chaos tarafından çoğaltma hatalarına uygundur (çoğaltmayı yeniden başlatma, çoğaltmayı kaldırma, birincil öğeyi taşıma, ikincil öğeyi taşıma ve örneği taşıma). Chaos, yalnızca kod paketi yalnızca bu uygulamaların çoğaltmalarını barındırıyorsa kod paketini yeniden başlatabilir. Bir uygulama bu listede görünmüyorsa, uygulama NodeTypeInclusionList'e dahil edilen düğüm türünde bir düğümde sona eriyorsa, bazı Chaos yinelemelerinde hataya neden olabilir. Ancak applicationX, yerleştirme kısıtlamaları aracılığıyla nodeTypeY'ye bağlıysa ve ApplicationInclusionList'de applicationX yoksa ve NodeTypeInclusionList'de nodeTypeY yoksa, applicationX hiçbir zaman hataya neden olmaz. Bu listeye en fazla 1000 uygulama adı eklenebilir; bu sayıyı artırmak için MaxNumberOfApplicationsInChaosTargetFilter yapılandırması için bir yapılandırma yükseltmesi gerekir.

Chaos'u çalıştırma

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
}