.NET CORE health check

原文:

Health checks in ASP.NET Core | Microsoft Learn

 

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/healthz");

app.Run();

  

 

docker

HEALTHCHECK CMD curl --fail http://localhost:5000/healthz || exit

  

 

自定义health check、

public class SampleHealthCheck : IHealthCheck

 

实战参考:Health Checks in ASP.Net Core - .Net Core Central (dotnetcorecentral.com)

Health checks are a critical part of any distributed system. Especially in the era of microservices, it is extremely important to understand if the service is running healthy or not. In this blog, I will walk through how to implement Health Checks in ASP.Net Core.

ASP.Net Core provides support for health checks out of the box through middleware and extension methods.

What are the different aspects of an application that we need through health checks?

  • Firstly, finding out dependencies such as a database or other services are alive or not
  • Secondly, the usage of physical resources on the machine

We usually use the health checks for monitoring applications and to see how the applications are behaving. The health check results are also in use for scaling out applications based on how the service is degrading in response time.

To further dig deeper into the topic let us start by creating an ASP.Net Core Web application. And let us demonstrate the feature.

Creating a new ASP.Net Core Web Application

First of all, we will create a new ASP.Net Core web application. To achieve that, I will open up Visual Studio 2019, and select the menu option File -> New -> Project.

Once the new project creation window pops up, I will select the ASP.Net Core Web Application. And then I will click on the Next button.

Secondly, on the next page of the pop-up, I will provide the name of the project in the Project Name field as HealthCheck.Demo. And then click on the Create button.

Finally, on the final page, I will select the API template option. And I will keep other values default (ASP.Net Core 3.1) and click on the Create button.

Setting up Health Checks in ASP.Net Core

Once the project is created, I will set up a basic health check. For that, I will start with the Startup class.

Change in ConfigureServices method

In the ConfigureServices of the Startup class, I will add the health check system into the dependency injections container. For that, I will call the AddHealthChecks extension method on the IServiceCollection instance. The method will return an instance of IHealthChecksBuilder.

As the name suggests the IHealthChecksBuilder provides methods to set up and chain health checks.

As a first step, I will call the AddCheck method on the IHealthChecksBuilder instance returned by the AddHealthChecks.

The AddCheck takes two parameters, the first one is a name for the health check. And the second one is a Func delegate which returns the HealthCheckResult struct.

For the time being, I will just return HealthCheckResult.Healthy as the return.

1
2
services.AddHealthChecks()
    .AddCheck("Test Health Check", () => HealthCheckResult.Healthy());

Change in Configure method

Once the dependency injection container setup is complete, it is time to set up the health check middleware. For that, I will update the implementation of the UseEndpoints extension method call to the IApplicationBuilder instance.

Inside of the delegate call for the UseEndpoints method parameter; I will call the MapHealthChecks extension method on the IEndpointRouteBuilder instance.

The MapHealthChecks method takes the URL pattern as the parameter. I will pass api/health as the parameter value. You can use what suits your application.

Microsoft uses /health as the endpoint in all the examples of their documentation here.

1
2
3
4
5
app.UseEndpoints(endpoints =>
    {
        endpoints.MapHealthChecks("api/health");
        endpoints.MapControllers();
    });

The complete change in code inside of the Startup class is below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
 
namespace HealthCheck.Demo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
 
        public IConfiguration Configuration { get; }
 
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
 
            services.AddHealthChecks()
                .AddCheck("Test Health Check", () => HealthCheckResult.Healthy());
        }
 
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
 
            app.UseHttpsRedirection();
 
            app.UseRouting();
 
            app.UseAuthorization();
 
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHealthChecks("api/health");
                endpoints.MapControllers();
            });
        }
    }
}

Running the application

Once the code is complete, I will run the application to test the health check endpoint.

After running the application, if I go to the /api/health endpoint I will see the response as Healthy.

health checks in asp.net coreHealthy Response

Using Multiple Health Checks Providers

Now, in a real-life scenario, we will never return a HealthCheckResult.Healthy from the health checks return and call it a day. For health checks, we will try to validate multiple aspects of the application to see if they are working as expected.

For example, let us say we want to check the state of our database. And make sure that we are able to successfully connect to the database.

For that, we will create a new class, DbHealthCheckProvider. The only responsibility of this class is to connect to the database. And make sure that the connection was successful.

I will create this class as a static class and it will have a single static method Check. The method Check will take the connection string of the database. Now since that class will be called from the Startup class, and will not be used anywhere, it is fair to do this. Also, we will never have to test this class, for obvious reasons; it does not add any value.

For this to work, I will install the NuGet package System.Data.SqlClient.

Inside of the Check method, I will open a connection to my local SQL Server And if the connection was successful, I will return HealthCheckResult.Healthy. Otherwise, I will return HealthCheckResult.Unhealthy from the method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Data.SqlClient;
 
namespace HealthCheck.Demo
{
    public static class DbHealthCheckProvider
    {
        public static HealthCheckResult Check(string connectionString)
        {
            // Code to check if DB is running
            try
            {
                using var connection = new SqlConnection(connectionString);
                connection.Open();
                return HealthCheckResult.Healthy();
            }
            catch
            {
                return HealthCheckResult.Unhealthy();
            }
        }
    }
}

Setting up Startup class

Once the DbHealthCheckProvider class is ready, I will set up the Startup to use this class.

For that, I will update the ConfigureServices method. And I will change the delegate implementation inside the AddCheck method. Instead of calling an inline delegate to return HealthCheckResult.Healthy. Now, I will call the DbHealthCheckProvider.Check.

And for the database connection string, I will pass an empty string. This should result in an Unhealthy response when we run the application.

1
2
3
4
5
6
7
8
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
 
    services.AddHealthChecks()
        .AddCheck("DB Health Check", () =>
            DbHealthCheckProvider.Check(""));
}

Once the code inside of Startup is configured to use the new DbHealthCheckProvider class, I will run the application.

Unhealthy ResponseUnhealthy Response

As expected, when we navigate to the /api/health endpoint, we can see an Unhealthy response back.

Health checks in asp.net core console responseConsole Response

In the above console response, we can see that the Health Checks system logs a fail in the console. This log can be used to identify what exactly failed. As you can see it prints the name of the health checks “DB Health Check”.

The HealthCheckResult.Unhealthy or HealthCheckResult.Healthy method takes an optional string description parameter. This is very handy when it comes to logging.

To demonstrate that I will update the implementation of the HealthCheckResult.Unhealthy method inside of the DbHealthCheckProvider. And I will add a description to the Unhealthy method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Data.SqlClient;
 
namespace HealthCheck.Demo
{
    public static class DbHealthCheckProvider
    {
        public static HealthCheckResult Check(string connectionString)
        {
            // Code to check if DB is running
            try
            {
                using var connection = new SqlConnection(connectionString);
                connection.Open();
                return HealthCheckResult.Healthy();
            }
            catch
            {
                return HealthCheckResult.Unhealthy("Could not connect to database!");
            }
        }
    }
}

Now if I run the application and navigate to the /api/health endpoint. I will see the description text “Could not connect to database!” will print out in the console.

unhealthy with descriptionUnhealthy with description

Queue Health Checks Provider

Now, let us say we our application also connects to a RabbitMQ server for processing messages. Now to check if we are able to connect to RabbitMQ server, we will need to write a health check provider.

Putting all health checks inside of a single class makes things difficult to manage. As well as harder to find out what was the cause of the issue.

Hence, I will create a new class, MqHealthCheckProvider. This will also be a static class with a single static method Check. For this class, we will just return HealthCheckResult.Healthy from the method, we will not implement any real RabbitMQ connectivity for the demo.

1
2
3
4
5
6
7
8
9
10
11
12
13
using Microsoft.Extensions.Diagnostics.HealthChecks;
 
namespace HealthCheck.Demo
{
    public static class MqHealthCheckProvider
    {
        public static HealthCheckResult Check(string mqUri)
        {
            // Code to check if MQ is running
            return HealthCheckResult.Healthy();
        }
    }
}

Once the MqHealthCheckProvider is ready, I will update the Startup class to configure the MqHealthCheckProvider.

Since the AddCheck method also returns IHealthChecksBuilder instance, I will chain another AddCheck to configure the MqHealthCheckProvider.

And for this case as well when we call the Check method of the MqHealthCheckProvider class, we will pass the MQ Server string as an empty string.

1
2
3
4
5
6
7
8
9
10
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
 
    services.AddHealthChecks()
        .AddCheck("DB Health Check", () =>
            DbHealthCheckProvider.Check(""))
        .AddCheck("MQ Health Check", () =>
            MqHealthCheckProvider.Check(""));
}

Now when I run the application, I will still see the response as Unhealthy. This is because the health check is the aggregation of all the health checkpoints configured.

Hence, I will update the DbHealthCheckProvider.Check call to pass a valid connection string to make that call as healthy as well.

I will get the connection string from the appsettings.json file.

1
2
3
4
5
6
7
8
9
10
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
 
    services.AddHealthChecks()
        .AddCheck("DB Health Check", () =>
            DbHealthCheckProvider.Check(Configuration["Connection"]))
        .AddCheck("MQ Health Check", () =>
            MqHealthCheckProvider.Check(""));
}

Now if I run the application, I will see Healthy in response.

Using Generic AddCheck method

The next thing I am going to walk through is how to use the generic AddCheck method. For that let us consider our application is interacting with third part SAAS software like Sendgrid through an HTTP connection.

Firstly, I will create a new class that is responsible for checking if an HTTP request to Sendgrid is successful or not. I will name the class as SendgridHealthCheckProvider.

This class will implement the interface IHealthCheck, which is part of the namespace Microsoft.Extensions.Diagnostics.HealthChecks. I will implement the method CheckHealthAsync from the interface.

For the time being for the demo, I will return HealthCheckResult.Healthy from the method. I will not make a real HTTP call outside.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Threading;
using System.Threading.Tasks;
 
namespace HealthCheck.Demo
{
    public class SendgridHealthCheckProvider : IHealthCheck
    {
        public Task<HealthCheckResult> CheckHealthAsync(
            HealthCheckContext context,
            CancellationToken cancellationToken = default)
        {
            return Task.FromResult(HealthCheckResult.Healthy());
        }
    }
}

Once the SendgridHealthCheckProvider is ready, I will go and update the Startup class to configure it.

For the configuration, I will use the generic version of the AddCheck method.

1
2
3
4
5
6
7
8
9
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
 
    services.AddHealthChecks()
        .AddCheck("DB Health Check", () => DbHealthCheckProvider.Check(Configuration["Connection"]))
        .AddCheck("MQ Health Check", () => MqHealthCheckProvider.Check(""))
        .AddCheck<SendgridHealthCheckProvider>("Sendgrid Health Check");
}

Finally, I will run the application. And as expected, I will get a Healthy response from the API call.

Implementing Degraded option

One of the options of the HealthCheckResult struct that we have not tried so far is the Degraded. Now the question is when we will use this option?

Whenever a database or some external service is taking more than the SLA, we can send a Degraded response. This can be achieved by using a timer to check the time taken by the external dependency.

To demonstrate that, let us update the MqHealthCheckProvider class. Now in the Check method, it will return HealthCheckResult.Degraded.

1
2
3
4
5
6
7
8
9
10
11
12
13
using Microsoft.Extensions.Diagnostics.HealthChecks;
 
namespace HealthCheck.Demo
{
    public static class MqHealthCheckProvider
    {
        public static HealthCheckResult Check(string mqUri)
        {
            // Code to check if MQ is running
            return HealthCheckResult.Degraded("MQ is running slow!");
        }
    }
}

Now if we run the application, we can see that the response is Degraded.

degraded responseDegraded response

And also as expected the console log will print a warning, along with the same of the health check. And the description message “MQ is running slow!”.

degraded console outputDegraded warning in console

Health Checks in ASP.Net Core through Postman

Now, the question is, how the monitoring tools will interpret this response. Well, the best way to implement that will be through checking the HTTP status code. And if needed further investigation then checking the response body.

Let us run the requests through the postman and see how it all works. So to test that, we will just run the application as it is running now. Which means it will return a Degraded response.

If we navigate to the /api/health, we will see the HTTP Status code is still 200, but the response body is Degraded.

postman health checks in asp.net coreDegraded in Postman

Now let us see how an Unhealthy response will look like in postman. Specifically what status code will it return.

For doing that, let us just update the Startup classes ConfigureServices method. And for the AddCheck of the DbHealthCheckProvider, let us pass an empty connection string. This will create an Unhealthy response.

1
2
3
4
5
6
7
8
9
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
 
    services.AddHealthChecks()
        .AddCheck("DB Health Check", () => DbHealthCheckProvider.Check(""))
        .AddCheck("MQ Health Check", () => MqHealthCheckProvider.Check(""))
        .AddCheck<SendgridHealthCheckProvider>("Sendgrid Health Check");
}

Once I make this change, let us run the application and check the postman response.

Unhealthy response in Postman

In the response, we can see that the HTTP status code is 503 Service Unavailable. And the response string is Unhealthy. Based on the HTTP status code any motoring tool can find out if the service is up or down.

For a Healthy response, we will get an HTTP Status Code of 200 and Healthy in the response string.

Conclusion

The Health Checks in ASP.Net Core implementation is really awesome in my opinion. It is super simple to implement and integrate with individual implementation classes.

I have done a YouTube video going through the concepts here.

The source code is available in Github here.

 

 

参考:

Health monitoring | Microsoft Learn

Docker-HealthCheck指令探测ASP.NET Core容器健康状态-阿里云开发者社区 (aliyun.com)

使用ASP.NET Core实现Docker的HealthCheck指令 - 程序员大本营 (pianshen.com)

Xabaril/AspNetCore.Diagnostics.HealthChecks: Enterprise HealthChecks for ASP.NET Core Diagnostics Package (github.com)

AspNetCore.Diagnostics.HealthChecks/kubernetes-liveness.md at master · Xabaril/AspNetCore.Diagnostics.HealthChecks (github.com)

ASP.NET Core Health Checks Explained (elmah.io)

(142条消息) .NET Core 3.0之深入源码理解HealthCheck(一)_dotNET跨平台的博客-CSDN博客

(142条消息) .NET Core 3.1之深入源码理解HealthCheck(二)_dotNET跨平台的博客-CSDN博客

在.NET Core 中实现健康检查 - 腾讯云开发者社区-腾讯云 (tencent.com)

ASP.NET CORE在docker中的健康检查(healthcheck) - 波多尔斯基 - 博客园 (cnblogs.com)

 

HealthCheck 不仅是对应用程序内运行情况、数据流通情况进行检查,还包括应用程序对外部服务或依赖资源的健康检查。

 

健康检查通常是以暴露应用程序的HTTP端点的形式实施,可用于配置健康探测的的场景有 :

 

  1. 容器或负载均衡器 探测应用状态,执行既定策略,例如:容器探测到应用unhealthy可终止后续的滚动部署或者重启容器;负载均衡器探测到实例unhealthy能将请求路由到健康的运行实例。

 

  1. 对应用程序种依赖的第三方服务进行健康探测,比如redis、database、外部服务接口

 

  1. 内存、硬盘、网络等物理依赖资源的探测

 

HealthCheck提供对外暴露程序运行状态的机制。

 

容器HEALTHCHECK指令

 

一般情况下我们很容易知道容器正在运行running, 但容器作为相对独立的应用执行环境,有时候并不知道容器是否以预期方式正确运作working

 

Dockerfile HEALTHCHECK指令提供了探测容器以预期工作的轮询机制,轮询内容可由应用自身决定。

 

具体而言:通过在容器内运行shell命令来探测容器健康状态,以Shell命令的退出码表示容器健康状态:

 

0 指示容器健康

1 指示容器不健康

2 指示不使用这个退出码

 

// 可定义轮询interval、探测超时timeout、 重试retries参数轮询探测
HEALTHCHECK [OPTIONS] CMD command

 

Every Linux or Unix command executed by the shell script or user has an exit status. Exit status is an integer number. 0 exit status means the command was successful without any errors. A non-zero (1-255 values) exit status means command was a failure.

linux shell执行成功,返回0;为对接Docker-HealcthCheck失败退出码1,要对Shell执行失败返回退出码1

 

对Web应用,自然会联想到使用curl命令访问端点去探测容器应用:

 

curl web端点成功,命令返回0(真);curl web端点失败,命令返回非0(假)

 

// curl -f 表示请求失败返静默输出
HEALTHCHECK --interval=5m --timeout=3s --retries=3 CMD curl -f http://localhost:5000/healthz || exit 1

 

探测命令在stdout或stderr输出的任何内容会在容器Health Status中存储,可通过docker inspect [ContainerId] 查看HealthCheck状态。

 

下面渐进式演示使用Docker平台的HEALTHCHECK指令对接 ASP.NET Core程序的健康检查能力。

 

 

 

 

ASP.NET Core实现HealthCheck端点

 

ASPNET Core在2.2版本内置了健康检查的能力:终端中间件(满足该路径的url请求,将会被该中间件处理)。

 

public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks();
}
 
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHealthChecks("/healthcheck");
}

 

Asp.NetCore 3.1将健康检查集成在 EndPoints,请自行修改。

 

请求/healthcheck端点, 程序会进行健康检查逻辑并响应输出, 默认的行为:

 

对healthy、degraded状态返回200 OK响应码;对于unhealthy返回503 Service Unavailable响应码

 

② 响应体只会包含简单的HealthStatus枚举字符串

 

③ 将每次健康检查的结果写入HealthReport对象。

作为企业级项目,存在对Web项目物理资源和服务依赖的健康检查需求, 这里我们为避免重复造轮子,引入了开源的力量。

 

开源社区对HealthCheck的支持

 

开源的企业级AspNetCore.Diagnostics.HealthChecks系列组件,该系列组件支持多种物理资源和服务依赖的健康检查,支持报告推送,支持友好的检查报告UI(支持后台轮询检查)、支持webhook通知。

 

下面的步骤演示了对web程序HTTP请求、Redis、Sqlite等服务进行健康检查的端点配置

 

① 引入AspNetCore.HealthChecks.Redis 、 AspNetCore.HealthChecks.Sqlite nuget库

 

② Startup.cs配置并启用健康检查

 

// 以下代码截取自 Startup.ConfigureServices方法,对swagger服务地址、redis、sqlte进行健康检查
services.AddHealthChecks().AddAsyncCheck("Http", async () =>
                      {
                        using (HttpClient client = new HttpClient())
                        {
                            try
                            {
                                var response = await client.GetAsync("http://localhost:5000/swagger");
                                if (!response.IsSuccessStatusCode)
                                {
                                    throw new Exception("Url not responding with 200 OK");
                                }
                            }
                            catch (Exception)
                            {
                                return await Task.FromResult(HealthCheckResult.Unhealthy());
                            }
                        }
                        return await Task.FromResult(HealthCheckResult.Healthy());
                    })
                    .AddSqlite(
                        sqliteConnectionString: Configuration.GetConnectionString("sqlite"),
                        healthQuery: "select count(*) as count from ProfileUsageCounters;",
                        name: "sqlite",
                        failureStatus: HealthStatus.Degraded,
                        tags: new string[] { "db", "sqlite", "sqlite" }
                     )
                    .AddRedis(Configuration.GetConnectionString("redis"), "redis", HealthStatus.Unhealthy, new string[] { "redis", "redis" })
                    .Services
                    .AddMvc();
// 以下代码截取自Startup.Configure方法:启用/healthz作为检查端点
 app.UseHealthChecks("/healthz").UseMvcWithDefaultRoute();    //  这里仍然只会响应 200/503状态码+简单的HealthStatus枚举值

 

再次强调,容器HealthCheck指令不关注Shell命令的执行过程,只关注shell命令的执行结果

 

// docker-compose.yml文件健康检查 参考如下配置:
healthcheck:
      test: curl -f http://localhost/healthcheck || exit 1
      interval: 1m30s
      timeout: 10s
      retries: 3

 

HealthChecks-UI 了解一下

 

抛开Docker的HEALTHCHECK指令、负载均衡器的轮询机制不谈,我们的Web自身也可以进行 轮询健康检查并给出告警。

 

就我们上面的Web 实例来说,我们只对外提供的是一个 /healthcheck 检查端点,引入HealthChecks.UI.dll 将会在前端生成友好的HealthReport 界面, 该库支持后台轮询检查、支持webhook 通知。

 

这里就不展开说明,自行前往AspNetCore.Diagnostics.HealthChecks查看相应文档,效果如下

 

 

 

 

 

至此,本文内容完毕:

 

  • 使用ASP.NET Core框架实现一个稍复杂的HealthCheck端点 /healthz

 

  • 使用docker的HEALTHCHECK指令对接Web应用健康检查端点

 

 

ASP.NET CORE在docker中的健康检查(healthcheck)

 

在使用docker-compose的过程中,很多程序都提供了健康检查(healthcheck)的方法,通过健康检查,应用程序能够在确保其依赖的程序都已经启动的前提下启动,减少各种错误的发生,同时,合理设计的健康检查也能够提供给外界关于应用程序状态的一些信息。

大多数docker镜像的详细说明中,会交代如何进行健康检查,.NET 6也提供了文档。也有其他人写了文章说了更细节的内容。

实践一下,现在docker-compose里面是这样:

version: '3.7'
services:
  postgres:
    container_name: postgres-container
    image: postgres:13
    volumes:
      - ./postgres_data:/var/lib/postgresql/data/pgdata
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: "123456"
      PGDATA: "/var/lib/postgresql/data/pgdata"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5  
    networks: 
      - dotnet-network
  
  dotnet-service:
    image: dotnet-service:latest
    container_name: dotnet-service-container
    ports:
      - 5000:5000
    build:
      context: ./TestWeb/
      dockerfile: ./TestWeb/Dockerfile
    environment:
      # - ASPNETCORE_ENVIRONMENT=Docker
      - ASPNETCORE_URLS=http://*:5000
      - App__SelfUrl=http://*:5000
    restart: on-failure 
    depends_on:
      postgres:
        condition: service_healthy
    networks: 
      - dotnet-network

networks:
  dotnet-network:
    external: false

首先调整一下程序:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/healthz");

app.Run();

如果直接运行的话,web访问/healthz节点,那么可以页面将直接返回Healthy。看上去没有问题,那我们按照要求调整一下Dockerfile

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
HEALTHCHECK CMD curl --fail http://localhost:5000/healthz || exit
ENTRYPOINT ["dotnet", "TestWeb.dll"]

然后调整docker-compose.yml文件,让postgres启动依赖dotnet-service:

version: '3.7'
services:
  postgres:
    container_name: postgres-container
    image: postgres:13
    volumes:
      - ./postgres_data:/var/lib/postgresql/data/pgdata
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: "123456"
      PGDATA: "/var/lib/postgresql/data/pgdata"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5  
    depends_on:
      dotnet-service:
        condition: service_healthy
    networks: 
      - dotnet-network
  
  dotnet-service:
    image: dotnet-service:latest
    container_name: dotnet-service-container
    ports:
      - 5000:5000
    build:
      context: ./TestWeb/
      dockerfile: ./TestWeb/Dockerfile
    environment:
      # - ASPNETCORE_ENVIRONMENT=Docker
      - ASPNETCORE_URLS=http://*:5000
      - App__SelfUrl=http://*:5000
    restart: on-failure 
    networks: 
      - dotnet-network

networks:
  dotnet-network:
    external: false

然后postgres就启动不了了,提示容器unhealthy。运行docker ps,发现.net容器的后面有一个unhealthy。但是程序访问正常,说明只是没有正确运行这段话而已。

Dockerfile方案#

文档说.NET 6的镜像里面不带curl这个工具了,需要手动装一下:

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
RUN apt-get update && apt-get install -y curl
HEALTHCHECK CMD curl --fail http://localhost:5000/healthz || exit
ENTRYPOINT ["dotnet", "TestWeb.dll"]

这样再看,就完全没问题了,如果把aspnet:6.0改成aspnet:6.0-alpine,空间更节省了(默认是239M,alpine版是108M),还自带了wget,也没必要这么麻烦先安装curl(比较费时间按)。

HEALTHCHECK CMD wget --spider http://localhost:5000/healthz || exit

docker-compose方案#

我不太喜欢去改Dockerfile,而是更倾向于修改docker-compose.yml文件。当然,没有curl依然还是得在Dockerfile中添加相应语句安装,但是可以删除HEALTHCHECK这一条了,直接在docker-compose.yml中对应服务下面添加:

healthcheck:
    test: ["CMD-SHELL", "wget --spider http://localhost:5000/healthz || exit"]
    interval: 10s
    timeout: 5s
    retries: 5  

 

 

Health Checks in ASP.Net Core - .Net Core Central (dotnetcorecentral.com)

 

posted @ 2022-11-23 16:09  PanPan003  阅读(212)  评论(0编辑  收藏  举报