美文网首页Docker
S6:容器的健康检查以及依赖检查

S6:容器的健康检查以及依赖检查

作者: 睦月MTK | 来源:发表于2020-03-18 14:39 被阅读0次

    声明:所有的实验示例大部分来自《learn-docker-in-a-month-of-lunches》的作者Elton Stoneman,但运行结果并不都是照搬,大部分实验结果可能与原书不同


    一、健康检查(health checks)和依赖检查(dependency checks)概述

    Q--什么是健康检查(health checks)?有什么用?
    A--健康检查用于判断一个容器是否正常工作,容器处于运行状态并不代表容器是正常工作的,因为容器表现的行为可能完全不符合预期,这样得容器我们称之为不正常工作的容器,而健康检查就用于这方面的检查。健康检查可以仅仅只是一个普通指令,也可以是一个脚本,一串逻辑,具体什么样的返回结果才算正常,由你自己决定。如果容器被标记为不健康后,会产生一个事件,这样运行该容器的平台就可以根据该事件做出相应的响应,比如启动一个新的容器替换这个不健康的容器。

    Q--什么是依赖检查(denpendency checks)?有什么用?
    A--依赖检查用于在容器启动时(没错!只在启动时)检查该容器依赖的服务是否已经运行了,如果没有,该容器进入退出状态。作用就是保持容器与容器之间的依赖关系以及启动顺序。依赖检查可以很简单,比如单纯的通过curl指令访问一下目标服务,如果有正确的响应则算通过,当然实际上检测标准应当更加完善才好。

    Q--Docker Compose有depends_on设置项设置容器间的依赖关系以及启动顺序,为什么还需要依赖检查?
    A--因为Docker Compose仅仅只能在一台机器上控制依赖关系。如果应用太大,就需要被分布在许多台机器上,这时候启动时的依赖关系可就很难维护了。再者强硬的使用依赖关系规定A容器必须运行在B容器之前,这样也不是很好,比如,假设A有50个容器,那么B想要运行则必须等这50个全部运行后才可以运行,然而此时恰恰就有1个容器怎么都运行不起来,那么这段时间B也运行不起来,整个应用在这段时间都是不正常的。若是使用依赖检查的话,只要通过了依赖检查容器就可以启动,而不需要管其他什么乱七八糟的,比如,只要有20个A容器启动了,A服务就"表现"正常了,那么B在启动时就可以通过依赖检查,因此就可以启动了。


    二、健康检查
    • HEALTHCHECK指令
    //...
    # app image
    FROM diamol/dotnet-aspnet
    
    ENTRYPOINT ["dotnet", "/app/Numbers.Api.dll"]
    HEALTHCHECK CMD curl --fail http://localhost/health
    
    WORKDIR /app
    //...
    

    这是某个镜像的Dockerfile中的一部分,健康检查相关的部分就定义在这里,HEALTHCHECK后面接的指令就是用于健康检查的指令。该指令每隔一段时间就执行一次,如果执行的返回值异常超过一定次数,则该容器会被视为不健康的。默认是每30秒执行一次,3次连续异常则被判断为不健康的。

    • 运行一个带健康检查的容器
    $ cd diamol/ch08/exercises/numbers
    $ docker image build -t ch08-numbers-api -f ./numbers-api/Dockerfile.v2 .
    $ docker container run -d -p 8080:80 --health-interval 5s ch08-numbers-api:v2
    

    docker image build命令中的-f选项用于指定创建镜像所需要依据的Dockerfile
    docker container run命令中其实可以指定和健康检查的相关的配置,比如:
    --health-cmd指定要执行的健康检查的命令
    --health-interval指定健康检查命令执行的时间间隔,默认ms
    --health-retries指定失败多少次,容器会被标记为不健康的
    --health-start-period指定在多少秒后才正式开始计算失败次数
    --health-timeout健康检查的超时时间,超时会被认为是检查失败

    • 查看容器的状态
    $ docker container ls
    CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                    PORTS                  NAMES
    2589f5acff74        ch08-numbers-api:v2   "dotnet /app/Numbers…"   24 minutes ago      Up 24 minutes (healthy)   0.0.0.0:8080->80/tcp   competent_sanderson
    

    在STATUS一栏中能看到(healthy)的状态,则代表该容器是健康的。
    因为该应用有一个人为的bug,即访问localhost:8080/rng三次后再访问会报错,所以接下来我们将人为触发这个bug,然后再查看容器的状态

    • 触发bug,查看容器状态
    $ curl localhost:8080/rng
    $ curl localhost:8080/rng
    $ curl localhost:8080/rng
    //等待15秒左右
    $ docker container ls
    CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                      PORTS                  NAMES
    2589f5acff74        ch08-numbers-api:v2   "dotnet /app/Numbers…"   37 minutes ago      Up 37 minutes (unhealthy)   0.0.0.0:8080->80/tcp   competent_sanderson
    

    可以看到该容器被标记为不健康了

    • 查看健康检查的日志
    $ docker container inspect $(docker container ls --last 1 -q)
    //...
    "State": {
                "Status": "running",
                "Running": true,
                "Paused": false,
                "Restarting": false,
                "OOMKilled": false,
                "Dead": false,
                "Pid": 32116,
                "ExitCode": 0,
                "Error": "",
                "StartedAt": "2020-03-17T14:07:33.190455031Z",
                "FinishedAt": "0001-01-01T00:00:00Z",
                "Health": {
                    "Status": "unhealthy",
                    "FailingStreak": 128,
                    "Log": [
                        {
                            "Start": "2020-03-17T22:55:33.399820121+08:00",
                            "End": "2020-03-17T22:55:33.542807814+08:00",
                            "ExitCode": 22,
                            "Output": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\ncurl: (22) The requested URL returned error: 500 Internal Server Error\n"
                        },
                        {
                            "Start": "2020-03-17T22:55:38.572044692+08:00",
                            "End": "2020-03-17T22:55:38.715793476+08:00",
                            "ExitCode": 22,
                            "Output": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\ncurl: (22) The requested URL returned error: 500 Internal Server Error\n"
                        },
                        {
                            "Start": "2020-03-17T22:55:43.74781023+08:00",
                            "End": "2020-03-17T22:55:43.887389369+08:00",
                            "ExitCode": 22,
                            "Output": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\ncurl: (22) The requested URL returned error: 500 Internal Server Error\n"
                        },
    //...
    

    --last是选择最近运行的那个容器,在State下的Health下的Log项中记录了每次检查失败的记录


    三、依赖检查

    注意:依赖检查不像健康检查一样有特殊的指令可以指定,所以依赖检查实际上就是一个简单的条件语句,如果检查通过,则执行运行服务所需要的关键命令,如果没通过,则会因为没有什么其他命令可执行而进入到退出状态。比如:

    CMD curl --fail http://numbers-api/rng && dotnet Numbers.Web.dll
    

    &&表示如果curl --fail http://numbers-api/rng(即依赖检查)没有出错,则执行dotnet Numbers.Web.dll,否则不执行该命令,由于该命令是整个该容器(服务)的启动命令,不执行该命令就等效于容器任务执行完成(因为没有其他命令可以执行),故而进入到退出状态。


    四、健康检查与依赖检查在Docker Compose工具中的应用
    • Docker Compose中健康检查的配置
    services:
      numbers-api:
        image: ch08-numbers-api:vmtk
        ports:
          - "8087:80"
        healthcheck:
          test: ["CMD" , "curl" , "--fail" , "http://localhost/health"]
          interval: 5s
          timeout: 1s
          retries: 2
          start_period: 5s
        networks:
          - app-net
    //...
    

    这是compose文件中的一部分,healthcheck项下就是关于健康检查的配置:
    test后面接一个数组,放的是健康检查的命令,每个字符串占用一个数组元素
    intervaltimeoutretriesstart_period前面已经介绍过了,不再赘述

    • Docker Compose中依赖检查的配置
    //...
    numbers-web:
        image: ch08-numbers-web:v3
        restart: on-failure
        environment:
          - RngApi__Url=http://numbers-api/rng
        ports:
          - "8088:80"
        healthcheck:
          test: ["CMD", "dotnet", "Utilities.HttpCheck.dll", "-t", "150"]
          interval: 5s
          timeout: 1s
          retries: 2
          start_period: 10s
        networks:
          - app-net
    //...
    

    numbers-web服务是依赖于numbers-api服务的,但是我们可以发现它并没有配置depends_on设置项,原因在开头已经解释过了。这里最关键的配置在于restart: on-failure一项,它告诉Docker Compose如果该容器启动失败则重启该容器,这样当容器因为依赖检查失败而进入到退出状态时,Docker Compose会重启它,而此时该容器所依赖的容器可能已经成功运行了,因此该容器也就会通过依赖检查而成功启动。


    五、其他
    • 注意事项:
      • 健康检查是会耗费计算机资源的,所以务必注意健康检查在资源占用和使用效果上的平衡,如果健康检查太严格,那么将过于耗费计算机资源,如果健康检查太宽松,那么健康检查的实际效果可能会大打折扣,而由于没有及时检测到问题,用户的体验将会持续下降。
    • 建议在程序中编写一套自己的工具用于健康检查和依赖检查,而不是使用外部工具,比如curl。理由如下:
      • 减少了需要添加的额外工具,越多的外部工具意味着越多的风险
      • 你自己应用的状态如何只有你自己最清楚,利用外部工具所能达到的效果可能并不如意
      • 你自己编写的工具,你可以检测任何你想要的东西,并根据这些数据来利用更完善的逻辑去判断服务的健康状态
      • 因为是属于程序内部的工具,所以使用的库将是一个统一的库,使用的配置将是一个统一的配置,管理方便
      • 将会得到Docker跨平台的优点,检查结果不会因为平台而影响

    参考文档:
    [1] learn-docker-in-a-month-of-lunches
    [2] 官方文档


    附:
    [1] Elton Stoneman的github项目

    相关文章

      网友评论

        本文标题:S6:容器的健康检查以及依赖检查

        本文链接:https://www.haomeiwen.com/subject/ejrtyhtx.html