8.1.헬스 체크를 지원하는 도커 이미지 빌드하기

HEALTHCHECK 인스트럭션

  • 컨테이너 런타임은 이 인스트럭션에 정의된 정보를 이용해 애플리케이션의 상태가 정상인지 확인 할 수 있다.
  • 애플리케이션의 상태를 판단할 수 있다면 어떤 명령을 지정해도 무방하다.
  • 도커는 일정한 시간 간격으로 컨테이너 안에서 지정된 명령을 실행한다.
  • 상태 코드가 정상이면 컨테이너도 정상으로 간주
  • 상태 코드가 연속으로 일정 횟수(기본값 세 번) 이상 실패로 나오면 해당 컨테이너를 이상 상태로 간주
  • 컨테이너를 실행할 때 --health-interval $(n)s 를 사용하면 몇 초 마다 실행 할 지 지정 가능

Dockerfile 적용

001) FROM diamol/dotnet-aspnet
002) 
003) ENTRYPOINT ["dotnet", "/app/Numbers.Api.dll"]
004) HEALTHCHECK CMD curl --fail http://localhost/health
005) 
006) WORKDIR /app
007) COPY --from=builder /out/ .
  1. 004: --fail 옵션을 붙이면 curl이 전달받은 상태 코드를 도커에 전달 요청이 성공하면 curL이 0을 반환하고 실패하면 0 이외의 숫자를 반환 도커 는 0을 헬스 체크 정상, 0 이외의 값을 비정상으로 간주한다.

컨테이너 실행

  • /rng url를 4번 호출하면 실패하는 API
  • 애플리케이션이 이상 상태에 빠지면 90초를 기다린 후 도커가 이상 상태를 감지하는지 확인한다.
001)  cd ch08/exercises/numbers
002)  docker image build -t diamol/ch08-numbers-api:v2 -f ./numbers-api/Dockerfile.v2 .
003)  docker container run -d -p 8081:80 diamol/ch08-numbers-api:v2
004)  
005)  curl http://localhost:8081/rng
006) 93%
007)  curl http://localhost:8081/rng
008) 40%
009)  curl http://localhost:8081/rng
010) 6%
011)  curl http://localhost:8081/rng
012) {"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1","title":"An error occured while processing your request.","status":500,"traceId":"|7a23b9b4-4eb36a77fe2301bc."}
013)  
014)  docker container ls --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}"
015) CONTAINER ID   NAMES                IMAGE                        STATUS
016) 65b72ef0ad42   vibrant_williamson   diamol/ch08-numbers-api:v2   Up 15 minutes (unhealthy)
  1. 011~012: 버그로 인해 실패
  2. 014~016: 세 번 연속 헬스체크 결과가 실패했기 때문에 컨테이너의 상태가 이상(unhealthy)으로 나온다. 하지만 컨테이너는 여전히 실행 중이다. 이상이 발생한 컨테이너라고 해서 도커가 종료시키지는 않기 때문이다.

도커 컨테이너 검사

001)  docker container inspect 65b72ef0ad42
002) [
003)     {
004)         "Id": "65b72ef0ad422c3e282b9d518d58bf432e840abbb6aeff5a354804810e7829f9",
005)         "Created": "2023-07-02T08:06:43.43977501Z",
006)         "Path": "dotnet",
007)         "Args": [
008)             "/app/Numbers.Api.dll"
009)         ],
010)         "State": {
011)             "Status": "running",
012)             "Running": true,
013)             "Paused": false,
014)             "Restarting": false,
015)             "OOMKilled": false,
016)             "Dead": false,
017)             "Pid": 6927,
018)             "ExitCode": 0,
019)             "Error": "",
020)             "StartedAt": "2023-07-02T08:06:43.650982219Z",
021)             "FinishedAt": "0001-01-01T00:00:00Z",
022)             "Health": {
023)                 "Status": "unhealthy",
024)                 "FailingStreak": 30,
025)                 "Log": [
026)                     {
027)                         "Start": "2023-07-02T08:21:15.991070428Z",
028)                         "End": "2023-07-02T08:21:16.074172845Z",
029)                         "ExitCode": 22,
030)                         "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"
031)                     },
032)                     {
033)                         "Start": "2023-07-02T08:21:46.00447247Z",
034)                         "End": "2023-07-02T08:21:46.07198747Z",
035)                         "ExitCode": 22,
036)                         "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"
037)                     },
038)                     {
039)                         "Start": "2023-07-02T08:22:16.079245762Z",
040)                         "End": "2023-07-02T08:22:16.138392637Z",
041)                         "ExitCode": 22,
042)                         "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"
043)                     },
044)                     {
045)                         "Start": "2023-07-02T08:22:46.143309845Z",
046)                         "End": "2023-07-02T08:22:46.210839178Z",
047)                         "ExitCode": 22,
048)                         "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"
049)                     },
050)                     {
051)                         "Start": "2023-07-02T08:23:16.224203262Z",
052)                         "End": "2023-07-02T08:23:16.300918053Z",
053)                         "ExitCode": 22,
054)                         "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"
055)                     }
056)                 ]
057)             }
058)         },
059)         // 생략
060)     }
061) ]
  1. 022~057: 현재의 헬스 체크 상태
  2. 024: FailingStreak는 연속 실패한 횟수
  3. 025: Log는 가장 최근에 수행한 헬스 체크의 정보

클러스터 환경이 아닐 때의 한계

  • 헬스 체크 결과 애플리케이션이 이상 상태임에도 컨테이너의 상태는 여전히 실행 중인 이유는 도커가 이런 작업을 안전하게 처리할 수 없기 때문
  • 도커 엔진은 단일 서버에서 동작하는데 이상이 생긴 컨테이너를 중지/교체 할 경우 보관된 데이터가 유실되고 그 시간 동안 애플리케이션도 동작하지 않는다.
  • 도커가 여러 대의 서버로 구성되고 도커 스웜이나 쿠버네티스가 관리하는 클러스터 환경에서는 헬스 체크 기능이 더욱 유용하다.
  • 헬스 체크를 통해 컨테이너 플랫폼이 컨테이너의 이상 상태를 통보받으면 자동적으로 조치를 취할 수 있기 때문이다.
  • 클러스터는 이상 상태를 보이는 컨테이너를 그대로 두고 대체 컨테이너를 실행해 애플리케이션의 중단 시간 없이 상태를 회복할 수 있다.

links

social