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  

程序依然可以正常运行。

P.S. 如果程序没有使用AddHealthChecks,那么也可以使用暴力一点的,直接用HEALTHCHECK CMD curl --fail http://localhost:5000 || exit,只要能够正常访问页面,就认为是Healthy,不过肯定就少了很多高级特性了。