Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes

1685
10-09-2021
Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes

Làm thế nào để theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes là một vấn đề được nhiều người quan tâm. Vì theo dõi tình trạng sức khỏe của các dịch vụ nền cốt lõi ASP.NET (ASP.NET Core Background Services) là điều cần thiết để xác định tính khả dụng, khả năng hoạt động bình thường của các ứng dụng.

Những điều cần biết về việc theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes

Trong hệ sinh thái .NET Core, các dịch vụ nền cốt lõi (background services) được gọi là những hosted services. Bởi một máy chủ đơn lẻ ví dụ như máy chủ web có thể vận hành những dịch vụ trong nền khi nó vẫn đang hoạt động. Về vấn đề triển khai, bạn cần có một hosted service để triển khai giao diện IHostedService.

Về phần Kubernetes, nền tảng này dựa vào các probes trong ứng dụng để kiểm tra sức khỏe của ứng dụng đó. Khi ứng dụng của bạn không phản hồi hay phản hồi không chính xác với các yêu cầu của probe, Kubernetes sẽ khởi động lại pod.

Nếu chưa rõ về các probes được Kubernetes sử dụng, bạn có thể tham khảo thêm tại link.

Một khái niệm khác cần được quan tâm là ASP.NET. Nó là một nền tảng được dùng để tạo ra các ứng dụng web-based. Nền tảng này được phát hành bởi Microsoft. Bạn có thể tìm hiểu thêm về khái niệm và cấu trúc của ASP.NET.

Trong dịch vụ cốt lõi ASP.NET, bạn có thể dùng gói Microsoft.AspNetCore.Diagnostics.HealthChecks để theo dõi tình trạng sức khỏe  các ứng dụng của mình. Tuy nhiên, gói kiểm tra sức khỏe này không hỗ trợ việc thăm dò sức khỏe của các dịch vụ bên ngoài. Nếu các ứng dụng của bạn phụ thuộc vào các dịch vụ bên ngoài, bạn vẫn có thể thực hiện điều này bằng cách triển khai IHealthCheck. BizFly Cloud sẽ giúp bạn nắm được cách triển khai cơ bản!

Chúng tôi sẽ thực hiện theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes. Nhưng tại sao lại triển khai theo dõi tình trạng sức khỏe dựa trên TCP đã sửa đổi thay vì dựa trên HTTP mặc định? Vì có thể bạn sẽ không muốn bao hàm toàn bộ phần mềm định tuyến và phần mềm trung gian vào trong các dịch vụ nền  mà việc kiểm tra sức khỏe của các dịch vụ cốt lõi ASP.NET phụ thuộc vào. Trong những trường hợp này, việc để một port duy nhất gắn với TCP listener là hợp lý.

Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes

Việc tiến hành kiểm tra tình trạng sức khỏe dịch vụ cốt lõi ASP.NET bao gồm một phần mềm trung gian, một hosted service và một vài libraries. 

Phần mềm trung gian

The Nine to Five Application là một dịch vụ nền (background service) đơn giản. Nó giúp hiển thị số phút còn lại trong 1 ngày làm việc theo giờ hành chính (từ 9 giờ sáng đến 5 giờ chiều). 

Chúng tôi sẽ định cấu hình kiểm tra sức khỏe trên dịch vụ này và triển khai nó cho cụm Kubernetes cục bộ. Bạn có thể tham khảo mã nguồn của ứng dụng này tại link.

Hosted service 

Đoạn mã sau đây thể hiện hosted service ghi lại thời gian còn lại trong ngày làm việc.

public class Worker : BackgroundService

{

    private readonly ILogger _logger;

    public Worker(ILogger logger)

    {

        _logger = logger;

    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)

    {

        var fiveOClock = new TimeSpan(17, 0, 0);

        var nineOClock = new TimeSpan(9, 0, 0);

        var timeZone = DateTimeZoneProviders.Tzdb["Australia/Sydney"];

        while (!stoppingToken.IsCancellationRequested)

        {

            var now = Instant.FromDateTimeUtc(DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc));

            var nowLocal = now.InZone(timeZone).ToDateTimeUnspecified();

            if (nowLocal.DayOfWeek == DayOfWeek.Saturday || nowLocal.DayOfWeek == DayOfWeek.Sunday)

            {

                _logger.LogInformation("{Time}: Cheers to the weekend!", nowLocal);

            }

            else

            {

                var message = nowLocal switch

                {

                    _ when nowLocal.Hour >= 9 && nowLocal.Hour <= 16 =>

                    $"Hang in there, just {fiveOClock.Subtract(nowLocal.TimeOfDay).TotalMinutes} minutes to go!",

                    _ when nowLocal.Hour < 9 =>

                    $"Get ready, office hours start in {nineOClock.Subtract(nowLocal.TimeOfDay).TotalMinutes} minutes!",

                    _ => "You are done for the day, relax!"

                };

                _logger.LogInformation("{Time}: " + message, nowLocal);

            }

            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);

        }

    }

}

Trước tiên, muốn thực thi dịch vụ này, bạn cần đăng ký với máy chủ (host) bằng phương pháp AddHostedService.

public static IHostBuilder CreateHostBuilder(string[] args)

{

    return Host.CreateDefaultBuilder(args)

        .ConfigureServices((hostContext, services) =>

        {

            // Register services that do actual work here.

            services.AddHostedService();

        });

}

Sau khi đã đăng ký, bạn tiến hành thêm dịch vụ kiểm tra sức khỏe vào dịch vụ cần kiểm tra (cụ thể ở đây là ứng dụng The Nine to Five Application) và triển khai nó cho Kubernetes.

Dịch vụ kiểm tra sức khỏe (Health Check Service)

Bạn có thể thêm dịch vụ kiểm tra sức khỏe tùy chỉnh cho ứng dụng của bạn bằng cách triển khai IHealthCheck. Việc này yêu cầu xác định phương thức  CheckHealthAsync. Tuy nhiên, việc thực hiện kiểm tra này luôn báo cáo trạng thái tốt của ứng dụng. Để nhận được báo cáo chính xác về tình trạng thực tế của dịch vụ, bạn có thể thêm custom logic vào phương thức này.

public Task CheckHealthAsync(HealthCheckContext context,

    CancellationToken cancellationToken = new CancellationToken())

{

    return Task.FromResult(new HealthCheckResult(HealthStatus.Healthy));

}

Để đăng ký dịch vụ health check tùy biến, bạn cần thêm loại dịch vụ (service type) và tên khám vào dịch vụ health check bằng phương pháp AddCheck như dưới đây:

return Host.CreateDefaultBuilder(args)

    .ConfigureServices((hostContext, services) =>

    {

        // Health check services. A custom health check service is added for demo.

        services.AddHealthChecks().AddCheck("custom_hc");

    });

Tiếp theo, bạn cần gán một TCP listener vào port kiểm tra sức khỏe để báo cáo tình trạng của dịch vụ khi Kubernetes tiến hành thăm dò trên pod lưu trữ dịch vụ. Bên dưới là cách triển khai TcpHealthProbeService:

public sealed class TcpHealthProbeService : BackgroundService

{

    private readonly HealthCheckService _healthCheckService;

    private readonly TcpListener _listener;

    private readonly ILogger _logger;

    public TcpHealthProbeService(

        HealthCheckService healthCheckService,

        ILogger logger,

        IConfiguration config)

    {

        _healthCheckService = healthCheckService ?? throw new ArgumentNullException(nameof(healthCheckService));

        _logger = logger;

        // Attach TCP listener to the port in configuration

        var port = config.GetValue("HealthProbe:TcpPort") ?? 5000;

        _listener = new TcpListener(IPAddress.Any, port);

    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)

    {

        _logger.LogInformation("Started health check service.");

        await Task.Yield();

        _listener.Start();

        while (!stoppingToken.IsCancellationRequested)

        {

            // Gather health metrics every second.

            await UpdateHeartbeatAsync(stoppingToken);

            Thread.Sleep(TimeSpan.FromSeconds(1));

        }

        _listener.Stop();

    }

    private async Task UpdateHeartbeatAsync(CancellationToken token)

    {

        try

        {

            // Get health check results

            var result = await _healthCheckService.CheckHealthAsync(token);

            var isHealthy = result.Status == HealthStatus.Healthy;

            if (!isHealthy)

            {

                _listener.Stop();

                _logger.LogInformation("Service is unhealthy. Listener stopped.");

                return;

            }

            _listener.Start();

            while (_listener.Server.IsBound && _listener.Pending())

            {

                var client = await _listener.AcceptTcpClientAsync();

                client.Close();

                _logger.LogInformation("Successfully processed health check request.");

            }

            _logger.LogDebug("Heartbeat check executed.");

        }

#pragma warning disable CA1031 // Do not catch general exception types

        catch (Exception ex)

        {

            _logger.LogCritical(ex, "An error occurred while checking heartbeat.");

        }

#pragma warning restore CA1031 // Do not catch general exception types

    }

}

Dựa trên kết quả phản hồi, ta có thể trả lời lại probe hoặc tạm dừng TCP listener và không báo cáo tình trạng sức khỏe của dịch vụ. Sau khi liên tiếp thất bại trong việc nhận phản hồi, Kubernetes sẽ xem chúng như các lỗi vĩnh viễn. Với các HTTP probes, chúng cho phép bạn phản hồi mọi yêu cầu thăm dò với mã trạng thái thích hợp để xác định tình trạng sức khỏe của dịch vụ. Tuy nhiên, với TCP probes, bạn chỉ có thể hoặc chấp nhận hoặc từ chối kết nối TCP từ Kubernetes để chỉ ra tình trạng của dịch vụ.

Triển khai (deploy) tới Kubernetes

Bước tiếp theo trong việc theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes là triển khai dịch vụ tới Kubernetes. Bạn sử dụng lệnh sau để build một  container image với tên là nine-to-five:1.0.0 (sử dụng Dockerfile trong OfficeCountdownClock).

docker build . -t nine-to-five:1.0.0

Sau đó, bạn tiến hành deploy (triển khai) dịch vụ nền dưới dạng một pod. Bạn có thể tìm thấy thông số dưới đây trong file deploy.yml.

apiVersion: v1

kind: Namespace

metadata:

  name: demo-ns

---

apiVersion: v1

kind: Pod

metadata:

  name: worker

  namespace: demo-ns

  labels:

    app: nine-to-five

spec:

  containers:

    - name: nine-to-five

      image: nine-to-five:1.0.0

      ports:

        - name: health-check

          containerPort: 5000

          hostPort: 5000

      readinessProbe:

        tcpSocket:

          port: health-check

        initialDelaySeconds: 5

        failureThreshold: 2

        timeoutSeconds: 3

        periodSeconds: 10

      livenessProbe:

        tcpSocket:

          port: health-check

        initialDelaySeconds: 15

        failureThreshold: 2

        timeoutSeconds: 3

        periodSeconds: 20

Bạn tiếp tục thực hiện lệnh sau để áp dụng tệp kê khai: .

kubectl apply -f deploy.yaml

Dưới đây là ảnh chụp lại hoạt động của dịch vụ nền:

Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes - Ảnh 1.

Trong kết quả đầu ra trước đó, bạn có thể thấy các sự kiện nhật ký được tạo ra khi triển khai các TCP probes. 

Để theo dõi các sự kiện Kubernetes được tạo trong demo-ns namespace một cách liên tục, bạn thực hiện lệnh dưới đây:

watch -n .5 kubectl get events -n demo-ns

Hình ảnh dưới đây thể hiện kết quả đầu ra được tạo khi triển khai lệnh trước đó. Bạn sẽ không thấy lỗi nào trong kết quả này. Điều đó chứng tỏ dịch vụ của bạn vẫn đang hoạt động ổn định.

Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes - Ảnh 2.

Bây giờ bạn sẽ cập nhật custom health check để thay đổi tình trạng sức khỏe của dịch vụ và kiểm tra lại nhật ký.

Mô phỏng việc kiểm tra tình trạng dịch vụ cốt lõi ASP.NET thất bại

Để mô phỏng tình trạng không ổn định của dịch vụ, bạn điều hướng đến CustomHealthCheck class và update đoạn mã dưới đây:

public Task CheckHealthAsync(HealthCheckContext context,

    CancellationToken cancellationToken = new CancellationToken())

{

    return Task.FromResult(new HealthCheckResult(HealthStatus.Unhealthy));

}

Triển khai lệnh dưới đây để tạo một docker image khác của ứng dụng:

docker build . -t nine-to-five:2.0.0

Sau đó, bạn thực hiện lệnh sau để redeploy pod hiện có với container image bạn vừa tạo:

cat <

apiVersion: v1

kind: Pod

metadata:

  name: worker

  namespace: demo-ns

  labels:

    app: nine-to-five

spec:

  containers:

    - name: nine-to-five

      image: nine-to-five:2.0.0

      ports:

        - name: health-check

          containerPort: 5000

          hostPort: 5000

      readinessProbe:

        tcpSocket:

          port: health-check

        initialDelaySeconds: 5

        failureThreshold: 2

        timeoutSeconds: 3

        periodSeconds: 10

      livenessProbe:

        tcpSocket:

          port: health-check

        initialDelaySeconds: 15

        failureThreshold: 2

        timeoutSeconds: 3

        periodSeconds: 20

EOF

Bạn hãy xem lại các sự kiện Kubernetes mà ta đang theo dõi bằng watch command như hình dưới đây:

Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes - Ảnh 3.

Trong lần kiểm tra kết quả trước, Kubernetes đã chỉ ra rằng tình trạng dịch vụ không ổn định vì các lần thăm dò thất bại liên tục. Bây giờ, bạn tiến hành kiểm tra tình trạng của ứng dụng bằng cách kiểm tra nhật ký của nó.

Hướng dẫn theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes - Ảnh 4.

Bạn có thể xem lại các bản ghi ở những lần kiểm tra không thành công trước đó. Do các lần kiểm tra thất bại liên tục, Kubernetes đã khởi động lại pod. Vì thế báo cáo nhật ký cuối cùng như hình trên cho thấy ứng dụng đã đóng.

Trên đây là quá trình giúp bạn có thể theo dõi dịch vụ cốt lõi ASP.NET với TCP Probes trên Kubernetes. Hãy theo dõi BizFly Cloud để được cập nhật những bài viết mới nhất về công nghệ!

Nếu đang tìm kiếm một dịch vụ triển khai Kubernetes hoàn toàn tự động, không cần công sức vận hành, xây dựng hệ thống container cho ứng dụng chỉ với vài thao tác đơn giản,... hãy tham khảo BizFly Kubernetes Engine.

SHARE