只有累積,沒有奇蹟

2022年1月26日 星期三

[OpenTelemetry] 現代化監控使用 OpenTelemetry 實現 : 在 .NET 如何使用 OpenTelemetry

前言
最近在 suvery 監控相關議題時接觸到 OpenTelemetry 蒐集遙測數據的開源框架,覺得這議題挺有趣的因此整理變成系列文,這篇是研究 OpenTelemetry 的系列文第三篇, 這系列主要會分為四篇分別是 若對於上述內容有問題或是不清楚的地方,歡迎提出來一起討論

OpenTelemetry in .NET
在前面分別提到了甚麼是可觀測性 (Observability) 以及 OpenTelemetry 蒐集遙測數據的規範標準,在 OpenTelemetry 在多種主流的程式語言都有實作,這邊就來分享在 .NET 中要如何使用 OpenTelemetry。在 OTel (OpenTelemetry簡稱)中 .NET Core 及 .NET Framework 都有支援(後者版本需高於 4.6.1),在 Github 位置是 opentelemetry-dotnet,與開發者最直接相關的為下列部分
  • API
  • SDK
  • Instrumentation libraries
  • Exporter libraries
以下分別針對個別項目做簡單介紹

API
Github : OpenTelemetry .NET API
可觀測性 (Observability) 中的三個重要元素 Tracing、Metrics、Logging 在 OTel dotnet 分別皆有實作,在支援的程度上除了 Tracing 是 1.0 之外,其他兩項分別還在 Alpha 與 Beta 階段。在 OTel dotnet 上提供 Tracing API, Logging API, Metrics API, Context and Propagation API 等 API 讓開發者依據各自需求作使用。

SDK
Github : OpenTelemetry .NET SDK
SDK 是 OTel API 的實現,目前 SDK 中實現了 Tracing API、Metrics API 和 Context API 等功能。當開發者使用與安裝配置的 SDK 後,所使用到的 API 方法將會開始生成、蒐集、發送遙測數據內容,並提供開發者自訂義 process pipleline 與導出到特定後端功能。目前 SDK 有與 ILogging 進行整合。

Instrumentation libraries
Github : OpenTelemetry .NET Instrumentation Library
提供常用的 Library 讓開發者在使用 OpenTelemetry 上更方便,像是 ASP.NET、ASP.NET Core、HttpClient、SQL Client 或是 SQL Client 等。你的應用程式代碼中如果有用到 httpclient 就可以引用其 OTel httpclient library,使用後就會自動蒐集生成遙測資料,開發者不用寫很多代碼即可達到其目的。目前在 dotnet 支援的 library 可以參考官方網站 OpenTelemetry registry 說明頁。

Exporter libraries
Github : OpenTelemetry .NET Exporter Library
蒐集遙測數據之後這些數據內容呈現的工具,像是在 Demo code 常使用的 console 或是 memory 中,亦或可以選擇 jaeger、zipkin、prometheus 等常見的工具,如果是在 Cloud 上在三大雲端服務廠商 AWS、Azure、GCP 也開始支援 OpenTelemetry 的輸出資料呈現。舉例來說在 Azure 上就可以透過 Azure Application Insights 以及 Azure Monitor 等服務查看蒐集到的資料呈現。

Package
Metrics
這裡列出在 .NET 中與 OpenTelemetry 會有相關的 namespace,其中 Instrumentation 與 Exporter 前面章節已經有提到過不在重複。在 .NET 6 之後的版本開始添加 OpenTelemetry 的支援。System.Diagnostics.Metrics 是 OpenTelemetry Metrics API 規範在 .NET 的實現,Meter 類別是在使用時須要先建立的物件與對象,在依據場景來使用所需要 Metrics 的指標與方法,在目前 Metrics API 支援以下幾個儀器類型
  • Counter : 計數器,可以使用 Add 方法來計算變化率或是總和
  • ObservableCounter : 與 Counter 類似,用於可觀察的儀器值 例如 CPU 時間(針對不同 Process、Task or 用戶模式)
  • ObservableGauge : 非相加值得可觀察儀器,例如當前室溫
  • Histogram : 繪製直方圖或計算百分位數的工具
或許對於上述描述的說明還有些模糊或是不清楚,在微軟 MSDN 文件有提到使用 Metrics 儀器的使用時機
  • 如果是計算內容是隨著時間增加的值 : 建議使用 Counter 與 ObservableCounter
  • 如果是計算時事物 : 建議使用 Histogram
  • 其他類像是業務指標、Cache 命中率、緩存大小 : 建議使用 ObservableGauge
詳細可以參考 MSDN Best practices when selecting an instrument type 文件說明

Tracing
在 Trace 部分可以透過 .NET 內建類別 Activity 或是使用 OpenTelemetry Tracing API 都可以達到其目的,兩者使用後都會定義到 Span 概念。.NET 5 開始提供 Activity 的類別作為 tracing 追蹤的目的,建立實例後可以透過 StartActivity 啟動 Activity 並定義其追蹤名稱;在 OpenTelemetry API 也可以透過 Tracer 達到一樣的目的,透過 TracerProvider 建立實體後使用 StartActiveSpan 來定義追蹤物件。因此你可以發現在微軟官方的部落格或是 sample code 上都是使用內建的 Activity API 做完範例, 兩者的 API 比較差異可以參考上圖

今晚來點 Demo
以上講很多概念與 OpenTelemetry API 的介紹,接著我們就來針對些好玩的部分做 Demo 讓大家對於 OpenTelemetry 在 .NET 的應用更了解,每個 Demo Code 範例代碼皆已上傳到 Github 上,有興趣的朋友可以將 clone 下來自己執行試玩看看

Demo#1 : Console
source code : OpenTelemetrySample
第一個 Demo 開始透過簡單的 Console 作為起手式,在建立專案後請先引用 OpenTelemerey.NET SDK 及 OpenTelemerey.NET Exporter Console 相關 Package,相關指令如下
dotnet add package OpenTelemetry --version 1.1.0
dotnet add package OpenTelemetry.Exporter.Console --version 1.1.0
Program.cs 中進入點 main 程式碼如下
class Program
{
	private static readonly ActivitySource MyActivitySource = new ActivitySource("Company.Product.Library");

	public static void Main()
	{
		using var tracerProvider = Sdk.CreateTracerProviderBuilder()
			.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("SampleService"))
			.SetSampler(new AlwaysOnSampler())
			.AddSource("Company.Product.Library")
			.AddConsoleExporter()
			.Build();

		using (var activity = MyActivitySource.StartActivity("SayHello"))
		{
			activity?.SetTag("foo", 1);
			activity?.SetTag("bar", "NET Conf 2021, Hej !");
			activity?.SetTag("baz", new int[] { 1, 2, 3 });
		}
	}
}
程式碼說明
  • Line 3 : 建立 ActivitySource 物件,並定義名稱為 Company.Product.Library
  • Line 7 : 透過 SDK 建立 TracerProvider 物件並設定 Trace Service 的名稱為 SampleService
  • Line 9 : 設定 Sampler 採樣器配置設定為 alwaysOn
  • Line 10 : 定義 TraceProvider source 名稱為 Company.Product.Library (需要與 line 3 相同)
  • Line 11 : 設定 Exporter,這裡使用的是在 Console 輸出其結果
  • Line 14~19 : 設定 Actitity 物件並定義其 tag 的 key 與 value, ex : foo 值為 1
執行結果如下
除了上述程式碼的說明之外可以看到的是其中 activity 會自動蒐集其起始的時間 (startTime) 與花費時間 (Duration) 等在 trace 中重要的資訊,tagObject 也可以依據需求定義客製化的 attribute 或是屬性資訊,以上是簡單的範例一部分

Demo#2 : API
source code : OpenTelemetrySample.API
接著的範例是使用 API 的部分,在將範例的內容結果顯示在 Zipkin 工具上,因此在建立專案後請先引用 OpenTelemerey.NET SDK 及 OpenTelemerey.NET Exporter Console 及 OpenTelemetry.Exporter.Zipkin Package,相關指令如下
dotnet add package OpenTelemetry --version 1.1.0
dotnet add package OpenTelemetry.Exporter.Console --version 1.1.0
dotnet add package OpenTelemetry.Exporter.Zipkin --version 1.1.0
在這範例中會將蒐集到的遙測數據資料輸出到 Zipkin 中,Zipkin 是分散式追蹤的工具,可以幫助開發者在工具中以視覺化的方式了解到請求中哪一段緩慢的原因與百分比資訊,相關介紹可以透過 官網說明,快速使用方式可以透過 docker 在本機執行下列語法
docker run -d -p 9411:9411 openzipkin/zipkin
Program.cs 中進入點 main 程式碼如下
class Program
{
	private static readonly ActivitySource MyActivitySource = new ActivitySource("Company.Product.Library", "1.0");
	static void Main(string[] args)
	{
		using var tracerProvider = Sdk.CreateTracerProviderBuilder()
		   .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyService"))
		   .AddSource("Company.Product.Library")
		   .AddZipkinExporter(zipkinOptions =>
		   {
			   zipkinOptions.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
		   })               
		   .AddConsoleExporter()
		   .Build();

		DoSomeWork();

		Console.WriteLine("Example work done"); 
	}

	static void DoSomeWork()
	{
		using (var a = MyActivitySource.StartActivity("SomeWork"))
		{
			StepOne();
			StepTwo();
		}

		using (var activity = MyActivitySource.StartActivity("StepThree", ActivityKind.Server))
		{
			activity?.SetTag("http.method", "GET");
			if (activity != null && activity.IsAllDataRequested == true)
			{
				activity.SetTag("http.url", "http://www.google.com");
				activity.SetTag("body", "This is a book");
			}
		}
	}

	static void StepOne()
	{
		using (var a = MyActivitySource.StartActivity("StepOne"))
		{
			Task.Delay(500);
		}
	}

	static void StepTwo()
	{
		using (var a = MyActivitySource.StartActivity("StepTwo"))
		{
			Task.Delay(1000);
		}
	}
}  
程式碼說明,其中有些與 Demo#1 相同的就不在重複說明唷
  • Line 7 : 設定 Service 的名稱為 MyService
  • Line 9 : 將遙測數據在 zipkin 上顯示,並定義輸出的 endpoint 為 http://localhost:9411/api/v2/spans
  • Line 23 : 定義 activity 物件名稱叫 Somework,其中執行 StepOne 與 StepTwo 兩個方法
  • Line 29 : 定義新的 activity 物件名稱為 StepThree,類型為 Server,內容模擬向第三方發出請求,因此會有定義 httpMethod、呼叫 endpoint 方法與送出 body 內容。
  • Line 40 : 定義 StepOne activity 物件,內容為 delay 500 毫秒
  • Line 48 : 定義 StepTwo activity 物件,內容為 delay 1 秒
執行結果如下
整理一下幾個重點
  • 順序為 StepOne > StepTwo > SomeWork > StepThree
  • StepOne 的 Id 為 StepTwo、SomeWork、StepThree 的 ParentId,上一篇提到的 Context Propagation 上下文關聯是透過此來定義
  • 每個 activity 會自動取得執行的 instance Id,這在雲端環境有這資訊很方便
  • 每個 Activity 皆有紀錄花費時間與起始時間
接著我們可以透過 zipkin 來看一下上面數據的視覺化呈現為和,開啟瀏覽器後輸入 http://localhost:9411/zipkin/ ,在按下 run query 按鈕

可以透過視覺化的方式來看到整個的關聯圖,已這個例子來說,Total Spans 一共三個,總共花費的時間為 60.288 ms,其中 stepOne 與 stepTwo 各自花費的時間,以及這兩個 span 中所各自定義的屬性是甚麼(右邊區塊),可以發現的是透過 zipkin 分散式追蹤工具在理解上更為清晰,如果在執行過程中有某個 span 是緩慢的可以更清楚的知道緩慢的原因與時間,那麼回到真實的世界會是甚麼樣子呢 ? 我們可以透過範例三知道。

Demo#3 : Automatic instrumentation
source code :
OpenTelemetrySample.WeatherForecast.Client
OpenTelemetrySample.WeatherForecast.API
在這範例中會透過較真實案例來說明,前端 Client 專案會透過 httpClient 呼叫後端 API 來取得 WeatherForecast 資訊,後端 API 會將資訊紀錄在 Redis 資料庫中,在亂數回 weather 資訊。我們會在這專案中引用自動蒐集遙測數據的 Library 像是 AddHttpClientInstrumentation,在發出 Httpclient 請求時自動蒐集相關遙測數據,並將其關聯結果呈現在 zipkin 工具上。
在 Client 專案的 Startup.cs 程式碼如下
public class Startup
{	
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddControllersWithViews();

		services.AddOpenTelemetryTracing(Configuration);
	}

	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
		//...省略
	}
	
	static class ServiceCollectionExtensions
    {

        public static IServiceCollection AddOpenTelemetryTracing(this IServiceCollection services, IConfiguration configuration)
        {
            var zipkinServiceName = configuration.GetValue("Zipkin:ServiceName");
            var zipkinEndpoint = configuration.GetValue("Zipkin:Endpoint");
            
            services.AddOpenTelemetryTracing((builder) => builder
                    .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(zipkinServiceName))
                    .AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddZipkinExporter(zipkinOptions =>
                    {
                        zipkinOptions.Endpoint = new Uri($"{zipkinEndpoint}");
                    })
                    .AddConsoleExporter());

            return services;
        }
    }
}
程式碼說明
  • Line 7 : 在 ConfigureService 方法中加上 AddOpenTelemetryTracing 擴充方法
  • Line 20~21 : 取得 Config 設定檔中定義 zipkin 的 serviceName 與 endpoint
  • Line 25 : 將 OpenTelemerey 新增到 ASP.NET Core 應用程式中
  • Line 26 : 針對 httpclient 進行遙測數據的蒐集
  • Line 27 : 輸出到 zipkin 工具

另外,在 API 專案的 Startup.cs 程式碼如下
public class Startup
{	
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddControllers();

		services.AddOpenTelemetryTracing(Configuration);

		services.AddStackExchangeRedisCache(options =>
		{
			options.InstanceName = "Redis Cache";
			options.Configuration = "localhost:6379";
		});
	}

	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
   {
		// 省略
	}	
	static class ServiceCollectionExtensions
    {

        public static IServiceCollection AddOpenTelemetryTracing(this IServiceCollection services, IConfiguration configuration)
        {
            var zipkinServiceName = configuration.GetValue("Zipkin:ServiceName");
            var zipkinEndpoint = configuration.GetValue("Zipkin:Endpoint");
            var redisEndpoint = configuration.GetValue("Redis:Endpoint");

            var connection = ConnectionMultiplexer.Connect(redisEndpoint);
            services.AddSingleton(connection);
            
            services.AddOpenTelemetryTracing((builder) => builder
                    .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(zipkinServiceName))
                    .AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddRedisInstrumentation(
                        connection,
                        option=>
                        {
                            option.FlushInterval = TimeSpan.FromSeconds(5);
                        }
                    )
                    .AddZipkinExporter(zipkinOptions =>
                    {
                        zipkinOptions.Endpoint = new Uri($"{zipkinEndpoint}");
                    })
                    .AddConsoleExporter());

            return services;
        }
    }	
}
  
API 的 Startup.cs 程式碼說明
  • Line 9 : 新增 Redis instance
  • Line 30 : 設定 redis life cycle
  • Line 36 : 設定 redis 自動蒐集 Library

撰寫完代瑪之後,開啟瀏覽器輸入 https://localhost:44321/weatherforecast 進行測試,可以發現有正常的回傳 weather 的資訊

接著在開啟 zipkin 看這次的範例會如何呈現

在 zipkin 中可以看到請求的相關連資訊,整個請求可以分為四個 span 每個 span 所花費的時間都可以透過 zipkin 顯示出來,從 Demo Website 到呼叫 Demo API 再到後面的 Redis 緩存 cache 都一目了然呈現,其中像是點選 localhost:6379 redis 的話,右邊區塊會呈現蒐集 redis 相關的遙測數據資料,舉例來說 redis 的 servicename, endpoint、db.system 都有提供,是透過哪個 library 搜集器的資訊與版本也可以看到。

結論
在過去如果要達到相同目蒐集請求資訊的話,可能在代碼上會加上很多不同的類別,像是要蒐集時間可能會使用 stopwatch 來計算執行時間、定義要蒐集的資訊加上logging,logging 會在加上 tag 或是自定義 attribute 蒐集想要的內容、上述部分如果沒有一個好的規範可能在代碼上就會相對雜亂一些,如果希望規模或一致性會定義蒐集資訊的共用規範或是 library 則需要很大的前置作業與功夫來規範。透過 OpenTelemerey 則可以大大省下這些時間,就像上面範例三的 Demo 內容透過 OTel 提供的 Library 就可蒐集請求的遙測數據資料,並 exporter 到指定的工具或是平台上,在透過自己習慣使用的平台來查詢異常問題的 root cause 或是異常的 service 是哪個,三大雲平台廠商在 OpenTelemetry 規範孵蛋成功後,也開始在自己的平台上陸續支援這套蒐集遙測數據的規範,像是在 Azure 就有在 MSDN 與技術部落格說明 Azure Monitor 與 Application insign 如何整合 OpenTelemetry。

這篇文章的範例與程式碼在 Github 上都可以看的到,位置是 OpenTelemetrySample with .NET Core,各位有興趣的朋友都可以 Clone 專案下來在自己的本機環境試試看,hope it help :D

參考
https://opentelemetry.io/

2022年1月15日 星期六

[OpenTelemetry] 現代化監控使用 OpenTelemetry 實現 : OpenTelemetry 開放遙測

前言
最近在 suvery 監控相關議題時接觸到 OpenTelemetry 蒐集遙測數據的開源框架,覺得這議題挺有趣的因此整理變成系列文,這篇是研究 OpenTelemetry 的系列文第二篇, 這系列主要會分為四篇分別是 若對於上述內容有問題或是不清楚的地方,歡迎提出來一起討論

Telemetry
Telemetry 這詞是由兩個單字所組成,分別是 Tele(遠程) 與 Metron(測量),意思是「遙測」,是指在遠端蒐集測量值或是數據資料,在將其蒐集到的資料自動傳輸到接收端進行監控。在軟體可能過去比較少聽到這詞,但在其他產業項是醫療或是汽車已經是很普及的用語,像是如果今天不舒服住院時,護士可能會在你手上擺放貼片,這些貼片會將身體的資料量是脈搏、血壓等資訊透過病床旁邊的儀器,在送到相同樓層的護理站,當有異常或是心跳部正常時就會透過數據進行遠端監控,確保身體狀況是 OK 沒問題的;在汽車業可能是當車進車廠保養時,維修技師即可透過線插入設備後即可了解車目前的狀況如何,哪邊可能是有意常要注意要修理的。

OpenTelemetry
圖片來源 : CNCF Dev Status

熱門開源框架
從 CNCF (Cloud Native Computing Foundation) 所提供的查詢網頁可以看的到,OpenTelemetry 是 CNCF 中第二個活耀的 Open source 項目(僅次於 K8S),最近微軟在推的 Dapr 也是使用 OpenTelemetry 作為內建的蒐集數據基礎 ,到底 OpenTelemetry 是什麼 ? 可以在這幾年可以竄升的這麼快,這麼火紅呢 ? 我們先來了解一下過去是怎麼做的以及有哪些解決方案

過去是如何蒐集遙測數據的呢
圖片來源 : Construction and Operation of Observability Platform using Istio
在過去分散式追蹤 (Distributed Tracing) 較著名有 OpenCensus 及 OpenTracing 框架,各自有所定義的規範及實作的內容,透過以上圖可以清楚了解 OpenCensus、OpenTracing 及 OpenTelemetry 之間的關係,在過去 Google 在內部是透過自己開發的 Dapper 系統進行進行分散式追蹤問題確認,在 2016 年提出 OpenCensus 作為分散式追蹤及監控規範標準;同一年 CNCF 提出 OpenTracing 分散式追蹤的標準,提供廠商中立的 API 且在多種程式語言均有實現可以使用。兩種分散式追蹤規範有各自的標準與定義的 API,有各有各的擁護者與愛好者(沒有絕對的好壞)。在 2019 年由多間軟體大廠共同合作下制定了 OpenTelemetry,OpenTelemetry 取代了 OpenCensus 及 OpenTracing 成為新一代的遙測數據蒐集標準。

從官網的介紹摘要如下
 OpenTelemetry 是雲原生(Cloud Native) 的可觀測性(Observability)框架,提供了標準化的 API、SDK 與協議自動檢測、蒐集和導出遙測數據資料(Metric、log、Trace),支援 W3C 定義的 Http trace-context 規範,降低開發者在蒐集遙測數據上的困難度,方便後續進行分析與了解應用程式的性能與行為,。
OpenTelemetry 是由 CNCF (Cloud Native Computing Foundation) 組織孵化的開源專案,2021/5 通過由 OpenTracing 與 OpenCensus 兩個框架合併,結合兩項分散式追蹤框架重要的特性成為下一代收集遙測數據的新標準,並在 2021/8 月時 OpenTelemetry 進入孵化階段(狀態頁)。

核心元件
在 OpenTelemetry 中核心如下
  • API : 開發人員可以透過 OpenTelemetry API 自動生成、蒐集應用程式(Application)的遙測數據資料(Metrics, Log, Trace),每個程式語言都需實作OpenTelemetry 規範所定義的 API 方法簽章。
  • SDK : SDK 是 OpenTelemetry API 的實現
  • OTLP : OTLP 規範定義了遙測數據的編碼與客戶端及服務器之間如何交換的協議 (gRPC、HTTP)
  • Collector : OpenTelemetry 中儲存庫,用於接收、處理、導出遙測數據到各種後端平台

Context Propagation
OpenTelemetry 其中一個重要的功能為 Context Propagation,甚麼是 Context Propagation 呢 ? 這是在 Trace 中一個相當重要的特性,當今天你的系統架構服務(Service)越切越細時,Client 發出一個請求可能 Response 會由多個資料來源所組成,也藉意味著當今天如果其中一個資料來源緩慢時就可能整個 Response 都會受到影響,透過 Trace 可以得知這多個資料來源是哪一個緩慢所影響,以下圖為例
系統中最小的單位叫做 Span,透過上圖可以得知是由 Span A 資料來源會是 Span B 及 Span C,Span B 資料來源為 Span D,Span C 的來源會是 Span E 與 Span F (G、H 暫省略),也就意味著 Span C 是 Span E 與 Span F 的上一層。某個請求從進入到回傳整個過程叫做 Trace,那麼我要如何知道 Span A~H 是相關聯的呢 ? 每個 Span 都會有兩個重要的 ID 分別是 SpanId 與 TraceId, SpanID 是每個 Span 各自都會擁有的 ID, TraceID 整串會是相同的 ID 值,這樣在追蹤上就可以透過 TraceID 來將有關聯的 Span 串起來。在上圖所示 Span A~H 這整串的 TraceID 會在相同的,其 ID 值會是 Span A 的 SpanID。
接著再繼續來看,光透過 SpanId 與 TraceId 是沒有辦法得知各自 Span 異常或是緩慢資訊的,在 Span 其實還有蒐集很多相關的內容,以下圖為例這會是 當請求來 SpanB 背後所收集的資訊,可能會把發送到 Server 的 request / response 以及 server 的回應內容,以及所花費的時間記錄下來,才可以透過這些已知已知的訊息找出哪一個服務緩慢,需要服務下線或者是做一些進一步的調整。
Support
OpenTelemetry 是蒐集遠端遙測數據的規範及標準,在目前既有有名的程式語言中都有實作 OpenTelemetry 的開源框架。另外在 Collector 上一些 OpenSource 像是 Grafana 與 Zinkin && Jaeger 現在都支援。詳細要看目前支援什麼 Language、Instrumentation、Exporter 可以到官方網站 Register 自行搜尋。

結論
這篇主要在介紹 OpenTelemetry 的前世今生,解決什麼問題與 Trace 中重要的觀念 Context Propagation,最後在帶到目前支援的程式語言,那麼 OpenTelemetry 規範在 NET 是如何實作的呢 ? 下一篇我將分享在 NET 中如何使用 OpenTelemetry API 以及一些 Demo Code 讓大家可以更容易進入 OpenTelemetry,謝謝


參考
https://opentelemetry.io/

2022年1月1日 星期六

[OpenTelemetry] 現代化監控使用 OpenTelemetry 實現 : 可觀測性(Observability)

前言
最近在 suvery 監控相關議題時接觸到 OpenTelemetry 蒐集遙測數據的開源框架,覺得這議題挺有趣的因此整理變成系列文,這篇是研究 OpenTelemetry 的系列文第一篇, 這系列主要會分為四篇分別是 若對於上述內容有問題或是不清楚的地方,歡迎提出來一起討論

企業面臨的挑戰
隨著科技技術不斷的創新,軟體架構與基礎建設演變速度也是變化相當快,如下圖所示可能(但不限)於以下幾個階段
  • 第一階段 :應用程式架構設計上較為簡單,在商業需求沒那麼複雜架構上不會針對其模組做太多的切割,程式會運行執行緒(Process)執行單一的請求,在進行測試與問題排除上較為簡單;在開發完畢之後會佈署在 VM 或是會 IDC 機房中提供給使用者來使用。
  • 第二階段 :隨著雲端技術的推出,企業開始嘗試將應用程式佈署到雲端的 Iaas 服務上,或是結合公有雲與私有雲的優勢,將企業中機密資料在私有雲進行處理,其他資訊在公有雲服務上進行運算,開發人員在應用程式的開發與整合上有更多與 Cloud 服務整合的機會,選擇 Cloud 上適合的服務使用。
  • 第三階段 :雲端技術逐漸成熟與穩定,更多的雲端基礎建設服務與新服務不斷的推出,在架構設計上越來越多人開始討論分散式系統架構的設計,也越來越多人考慮將資料庫從IDC機房遷移到 Cloud 雲端上,加上像是 RDS、DynamoDB、Redis 等雲端資料庫, 這些服務或組件讓大家在雲端開發與管理上更為方便。在架構上也更為彈性搭配著企業需求來使用;在基礎建設上也伴隨著容器化(dockerize)的技術成熟開始進入新的世代。
  • 第四階段 : 過去幾年大家一直在討論軟體架構從單體到微服務 serverless、service meshes 等各種可能的組合,主要目的可能是在服務架構設計時如何降低服務之間的依賴關係;在越來越多企業使用虛擬容器技術 Docker 後發現管理 Docker 實務上是困難的,因此 Kubernetes(K8S) 開始盛行,幫助團隊在調度 Docker 上更為容易與靈活,

但與所有美好事物一樣都是有代價的;當服務越切越細時,分佈式系統帶來了眾多的挑戰。當開發的世界變得複雜,可能會帶來哪些影響呢 ? 可能會分為三個部分:
  • 架構 : 從單體式架構 > SOA > 分散式架構 > 微服務 開發人員在開發上變得更繁瑣,要注意的事情變得更多。例如 : 服務彼此之間的溝通要如何進行 ? 在事件的紀錄上要如何處理 ? 當一個請求要經過多個服務的時候相對的延遲(Lateny)也會增加,服務的拆分與請求時間的部分該如何達到平衡 ?
  • 測試 : 當系統架構變得複雜,測試人員或團隊在測試上勢必需要花更多時間來了解既有架構要如何進行測試。
  • 監控 : 上線後的監控會是最大的挑戰,當你所在的產品服務是 7* 24 小時,系統架構切分很細或是很複雜時,當今天某個服務故障時要如何快速定位問題變的是重要的關鍵

案例: Facebook 當機事件
圖片來源 : https://3c.ltn.com.tw/news/46163
或許上面文章提到的部分有點抽象,我們來看一個真實的案例,2021/10/4 facebook、whatApp及 IG 大當機,整個案件根本原因是因為工程師在部署新功能時有錯,導致對外 DNS 服務異常,詳細可以看 facebook 官方技術部落格或 Cloudfare 的異常事故報告 說明。
這裡簡單整理事件造成的影響:
  • 修復時間 : 從問題開始到最後修復完成近花費 6 小時
  • 影響人數 : 八千萬人無法使用
  • 損失金額 : 10 億台幣
  • 影響股價 : 問題發生後,股票下跌 4.89 %
最重要的可能是客戶滿意度,你可以想像一下今天剛好忙碌一天想滑一下手機看 IG、facebook 或是 whatApp 找朋友聊天時,但發現這些服務都沒有辦法使用心情會是如何 ? 使用者可能會跑去使用其他服務,facebook 官方在服務異常地當下也在 twitter 發文說明問題與處理的狀況,隨著時間一分一秒的過去,客戶逐漸地在流失到其他服務像是 twitter,廣告主投放的廣告也無法正常投放。

從上述問題來看,如何讓問題發生時能夠快速地被定位變得更加重要,在企業服務越切越細的世代,最快時間找到問題變得相當的關鍵。如果今天是發生在你的公司或是負責的系統發生異常時,你要如何快速的定位問題呢 ?

圖片來源 : https://trends.google.com.tw/trends/?geo=TW
根據 Google 關鍵字報告搜尋 監控(Monitor) 關鍵字,可以發現在過去 20 年的時間大家對於應用程式的監控越來越重視,背後的涵義可能是在這變化這麼快、不確定性這麼高、架構越來越複雜的時代,是否有更好的方法或方式來進行監控,或者也可以反問,或許在雲原生(Cloud Native)時代的監控,應該具備甚麼樣的特性?


可觀測性(Observability)
分散式系統意味者分散式故障,當出現故障時,可能很難快速恢復服務,甚至不知道從哪裡開始。您如何解析龐大的依賴關係,取決於你對系統的了解與問題定位有很大的關聯。可觀測性簡單來說可以幫助開發人員或是 SRE 夥伴了解,什麼是慢的 ? 什麼是壞的 ? 以及需要做什麼來提高性能。
可觀測性三個重要的特性
  • Metrice : 系統有狀況
  • Logs : 問題是甚麼
  • Traces : 哪裡出問題
過去監控可以解決的是已知的已知問題,可觀測性要解決的是已知的未知的問題,那麼什麼是已知的未知呢 ?
圖片來源 : https://dzone.com/articles/microservice-observability-part-1-disambiguating-o
橫軸 : Data available, 指的你是否有 log 的資料
縱軸 : 對於系統的理解程度

接者可以透過橫軸縱軸了解的程度切分為四個象限,可能光看文字會有點抽象,以下透過一些簡單的例子舉例說明
  • known knowns : 這些事情是我們知道且可以理解的,舉例 : 在雲端伺服器建立新的 instance 時,核心數為 1 core,記憶體為 1G 可以提供應用程式使用。
  • known Unknowns : 事情是我們知道但沒辦法理解的,舉例 : 應用程式接收到多大的請求量時,系統會 crash 造成服務中斷;電商遇到雙11節慶流量會很大,但我們不確定請求量會成長幾倍?
  • Unknown Knowns : 事情是我們知道但自己是沒有意會到的
  • Unknown Unknowns : 不了解會發生甚麼事以及其帶來的影響,舉例 : 不了解系統在高流量時會發生甚麼問題,也不清楚會有哪些服務會被影響到

圖片來源 : https://dzone.com/articles/microservice-observability-part-1-disambiguating-o
右下角就是過去常常聽到的監控(Monitoring),知道可能會異常的點會有那些因此加上偵測點進行判斷,如果超過其筏值或是水位值會發送告警通知。左下角是可觀測性(Observability),希望可以透過已知的訊息更了解系統的狀況。

可觀測性(Observability) 的目的是什麼呢 ?
可觀測性不僅僅是收集數據的內容,希望透過可觀測性工具或平台可以回答下列問題:
  • 請求通過了哪些服務
  • 每個服務在處理請求時做了什麼
  • 如果請求很慢,瓶頸在哪裡
  • 如果請求失敗,錯誤發生在哪裡
  • 請求的關鍵路徑上是什麼
  • 執行與正常系統行為有何不同
  • 為什麼花了這麼長時間
我們可以透過追蹤(Traces)的方式來蒐集或得到上述問題的答案。

Distributed Tracing
圖片來源 : https://eng.uber.com/optimizing-observability/
過去大家常提到的監控(Monitor)是透過 Metrics 及 Log 來達到其目的,可觀測性(Observability) 更著重在追蹤(Trace)的部分,Trace 的目的是把系統中所有模組與組件之間的交互關係透過 TraceID 串聯起來,TraceID 是請求(Request)開始生成一個關聯ID,接著再將其記錄在該事務(Span)的每個請求中,可以讓你知道一個請求或是某個事件從頭到尾的信息內容,也就是透過上下文關係(Context)來取得請求中各服務之間的關聯。

舉例來說,以上圖內容為例,當使用者發送一個請求來的時候,從 A 點服務開始,接著分別經過 B、E 兩個服務,B 的服務資料來源是透過 C 與 D 的服務取得,以及這些服務各自所花費的時間為何。當我們蒐集到每個服務的資訊與所花費的時間,發現請求有緩慢或是服務異常的問題時,監控團隊可以透過遙測的數據定位可能異常的服務來進行問題排除與處理,加快可能異常服務的問題確認時間。

結論
可觀測性是這幾年新的術語,今天簡單介紹了可觀測性(Observability)的目的,關鍵核心是透過 Traces 提供系統行為及上下文關係,那麼在實務上要如何蒐集這些數據資料呢 ? 下一篇我們將分享如何使用 CNCF(Cloud Native Computing Foundation) 的 Open Source 框架 OpenTelemetry 來蒐集遠端遙測數據資料,讓你的系統更具備可觀測性(Observability)。


參考
Monitoring in the time of Cloud Native Microservice Observability, Part 1: Disambiguating Observability and Monitoring

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com