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/ .
- 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)
- 011~012: 버그로 인해 실패
- 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) ]
- 022~057: 현재의 헬스 체크 상태
- 024: FailingStreak는 연속 실패한 횟수
- 025: Log는 가장 최근에 수행한 헬스 체크의 정보
클러스터 환경이 아닐 때의 한계
- 헬스 체크 결과 애플리케이션이 이상 상태임에도 컨테이너의 상태는 여전히 실행 중인 이유는 도커가 이런 작업을 안전하게 처리할 수 없기 때문
- 도커 엔진은 단일 서버에서 동작하는데 이상이 생긴 컨테이너를 중지/교체 할 경우 보관된 데이터가 유실되고 그 시간 동안 애플리케이션도 동작하지 않는다.
- 도커가 여러 대의 서버로 구성되고 도커 스웜이나 쿠버네티스가 관리하는 클러스터 환경에서는 헬스 체크 기능이 더욱 유용하다.
- 헬스 체크를 통해 컨테이너 플랫폼이 컨테이너의 이상 상태를 통보받으면 자동적으로 조치를 취할 수 있기 때문이다.
- 클러스터는 이상 상태를 보이는 컨테이너를 그대로 두고 대체 컨테이너를 실행해 애플리케이션의 중단 시간 없이 상태를 회복할 수 있다.