공부/docker

도커 교과서 ch08 (헬스 체크와 디펜던시 체크)

KidAnt 2023. 12. 27. 17:11

도커는 컨테이너를 실행할 때마다 애플리케이션의 기본적인 상태를 확인한다.
-> 도커가 확인하는 것은 컨테이너 안의 프로세스의 실행 상태

이러한 헬스 체크는 기본적인 체크 밖에 불가능하다 -> 보장되는 것은 프로세스의 실행 상태 뿐 애플리케이션의 정상적인 상태X

도커는 애플리케이션의 상태가 실제로 정상인지 확인할 수 있는 정보를 도커 이미지에 포함 시킬 수 있는 기능 보유
(Dockerfile 스크립트에 상태확인을 위한 로직 사용)

#실습1 (헬스 체크 로직 부재)
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises# docker container run -d -p 8080:80 diamol/ch08-numbers-api
Unable to find image 'diamol/ch08-numbers-api:latest' locally
latest: Pulling from diamol/ch08-numbers-api
...(생략)
Status: Downloaded newer image for diamol/ch08-numbers-api:latest
eab66abc9b03ed5d915ba69766f1a26528d716bf9e7729f50863b3f44618052d
#컨테이너 생성

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises# curl http://localhost:8080/rng
46
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises# curl http://localhost:8080/rng
13
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises# curl http://localhost:8080/rng
64
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises# curl http://localhost:8080/rng
{"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1","title":"An error occured while processing your request.","status":500,"traceId":"|e205ac63-49afd163b2a15cb9."}
#4번째 부터 api호출 실패 (500 오류)

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises# docker container ls
CONTAINER ID   IMAGE                     COMMAND                  CREATED              STATUS              PORTS
                       NAMES
eab66abc9b03   diamol/ch08-numbers-api   "dotnet /app/Numbers…"   About a minute ago   Up About a minute   0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_grothendieck
#500 오류가 터졌지만 도커에서는 여전히 Up 상태에 정상 작동중인 것으로 보인다.
# 컨테이너 런타임으로는 프로세스 안에서 무슨일이 일어나고 애플리이션이 정상 작동중인지 알방법이 없다.


Dockerfile의 HEALTHCHECK 인스트럭션: 해당 인스트럭션에 적힌 정보를 바탕으로 애플리케이션이 정상 상태인지 확인한다.
-> 도커가 컨테이너 안에서 실행하는 명령을 지정 -> 해당 명령이 반환하는 상태 코드를 보고 애플리케이션의 상태 판단 -> 일정 횟수 이상 실패시 해당 컨테이너를 이상 상태로 간주

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# cat numbers-api/Dockerfile.v2
FROM diamol/dotnet-sdk AS builder

WORKDIR /src
COPY src/Numbers.Api/Numbers.Api.csproj .
RUN dotnet restore

COPY src/Numbers.Api/ .
RUN dotnet publish -c Release -o /out Numbers.Api.csproj

# app image
FROM diamol/dotnet-aspnet

ENTRYPOINT ["dotnet", "/app/Numbers.Api.dll"]
HEALTHCHECK CMD curl --fail http://localhost/health
#CMD로 curl명령을 실행시켜 컨테이너 내부에서 API의 상태를 확인한다.
# (URL)/health는 버그가 발동했는제 확인하기 위한 또다른 API의 엔드포인트이다.
# (버그시 500 insernal server Error, 정상작동시 200 OK)
#--fail을 붙이면 curl이 전달 받은 상태 코드를 도커에 전달한다 -> 성공시 0 실패하면 0이외의 숫자 반환 -> 도커가 0과 이외의 값으로 정상 작동 확인

WORKDIR /app
COPY --from=builder /out/ .


Dockerfile 스크립트로 새로운 버전의 이미지를 빌드할 때, Dockerfile 스크립트의 파일명이 다를때 buile 부명령을 사용한다.

#실습2 (컨테이너 생성시 dockerfile 파일위치 지정)

cd ./ch08/exercises/numbers
#최상위 디렉토리로 이동

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker image build -t diamol/ch08-numbers-api:v2 -f ./numbers
-api/Dockerfile.v2 .
[+] Building 9.5s (17/17) FINISHED                                                                           docker:default
 => [internal] load build definition from Dockerfile.v2                                                                0.0s
 => => transferring dockerfile: 428B                                                                                   0.0s
 => [internal] load .dockerignore                                                                                      0.0s
 => => transferring context: 2B                                                                                        0.0s
 => [internal] load metadata for docker.io/diamol/dotnet-aspnet:latest                                                 4.7s
 => [internal] load metadata for docker.io/diamol/dotnet-sdk:latest                                                    4.1s
 => [auth] diamol/dotnet-aspnet:pull token for registry-1.docker.io                                                    0.0s
 => [auth] diamol/dotnet-sdk:pull token for registry-1.docker.io                                                       0.0s
 => [builder 1/6] FROM docker.io/diamol/dotnet-sdk@sha256:a78173a9dbd3e6b3b2af1eadad3edd6e8ef7b2801f3cda405928e5da155  0.0s
 => => resolve docker.io/diamol/dotnet-sdk@sha256:a78173a9dbd3e6b3b2af1eadad3edd6e8ef7b2801f3cda405928e5da155c7039     0.0s
 => CACHED [stage-1 1/3] FROM docker.io/diamol/dotnet-aspnet@sha256:d34a231013982f446e54fe15c036bf5e8585ae8e95ff1fd3e  0.0s
 => => resolve docker.io/diamol/dotnet-aspnet@sha256:d34a231013982f446e54fe15c036bf5e8585ae8e95ff1fd3e0c374a40d3f60f7  0.0s
 => [internal] load build context                                                                                      0.0s
 => => transferring context: 4.42kB                                                                                    0.0s
 => CACHED [builder 2/6] WORKDIR /src                                                                                  0.0s
 => [stage-1 2/3] WORKDIR /app                                                                                         0.1s
 => [builder 3/6] COPY src/Numbers.Api/Numbers.Api.csproj .                                                            0.0s
 => [builder 4/6] RUN dotnet restore                                                                                   1.3s
 => [builder 5/6] COPY src/Numbers.Api/ .                                                                              0.0s
 => [builder 6/6] RUN dotnet publish -c Release -o /out Numbers.Api.csproj                                             2.4s
 => [stage-1 3/3] COPY --from=builder /out/ .                                                                          0.0s
 => exporting to image                                                                                                 0.0s
 => => exporting layers                                                                                                0.0s
 => => writing image sha256:e18d43669cfeebcc72e187b712f863d5d3bd4c3879c1d91602628d2c7e127741                           0.0s
 => => naming to docker.io/diamol/ch08-numbers-api:v2
#-f 옵션으로 파일 경로 지정
#실습3 (헬스케어 테스트 진행)

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container run -d -p 8081:80 diamol/ch08-numbers-api:v2
dadccb45cb6bc485f8b60a291fdd844dd35f5764e3f82c884a466cc0969828f6
#컨테이너 생성 후 30초 대기

CONTAINER ID   IMAGE                        COMMAND                  CREATED          STATUS                    PORTS                                   NAMES
dadccb45cb6b   diamol/ch08-numbers-api:v2   "dotnet /app/Numbers…"   49 seconds ago   Up 49 seconds (healthy)   0.0.0.0:8081->80/tcp, :::8081->80/tcp   elated_albattani
eab66abc9b03   diamol/ch08-numbers-api      "dotnet /app/Numbers…"   53 minutes ago   Up 53 minutes             0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_grothendieck
#컨테이너가 실행 중이며 건강(healthy)한 상태로 나오며 이는 헬스체크가 성공적으로 동작했다는 뜻이다.

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# curl http://localhost:8081/rng
82
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# curl http://localhost:8081/rng
65
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# curl http://localhost:8081/rng
7
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# curl http://localhost:8081/rng
{"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1","title":"An error occured while processing your request.","status":500,"traceId":"|9a12d40b-47585b5aa957e8bc."}
#4번째에서 오류 발생

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container ls
CONTAINER ID   IMAGE                        COMMAND                  CREATED             STATUS                      PORTS                                   NAMES
dadccb45cb6b   diamol/ch08-numbers-api:v2   "dotnet /app/Numbers…"   15 minutes ago      Up 15 minutes (unhealthy)   0.0.0.0:8081->80/tcp, :::8081->80/tcp   elated_albattani
eab66abc9b03   diamol/ch08-numbers-api      "dotnet /app/Numbers…"   About an hour ago   Up About an hour            0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_grothendieck
#10초 대기후 컨테이너 상태 확인 시 상태이상(unhealthy)으로 나온다.
#하지만 이상이 발생했다고 해서 컨테이너가 종료되지는 않으므로 여전히 실행중이다.



컨테이너의 이상 상태는 도커 API를 통해 보고된다. 따라서 컨테이너를 실행 중인 프랫폼도 컨테이너 이상 상태를 통보받고 애플리케이션을 복귀하기 위한 조치를 취할 수 있다.
-> 가장 최근의 헬스 체크 수행 결과도 저장돼 있어 컨테이너의 상태를 조사할 때 이 결과를 열람할 수 있다.

헬스 체크가 수행중이라면 그 결과 역시 inspect 명령에서 볼 수 있다.

#실습4 (헬스 체크 결과 출력)

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container inspect $(docker container ls --last 1 --format '{{.ID}}')
[
    {
        "Id": "dadccb45cb6bc485f8b60a291fdd844dd35f5764e3f82c884a466cc0969828f6",
        "Created": "2023-12-26T07:03:24.385423458Z",
        "Path": "dotnet",
        "Args": [
            "/app/Numbers.Api.dll"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 11096,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2023-12-26T07:03:24.827627624Z",
            "FinishedAt": "0001-01-01T00:00:00Z",
            "Health": { #헬스 체크 항목
                "Status": "unhealthy",
                "FailingStreak": 91, #지속적으로 헬스체크를 하기때문에 계속 체크가 돌아갔으며 지금까지 91번 실패했다
                "Log": [ #이하 가장 최근에 수행한 헬스 체크들
                    {
                        "Start": "2023-12-26T17:00:30.096042351+09:00",
                        "End": "2023-12-26T17:00:30.127169896+09:00",
                        "ExitCode": 
                        "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": "2023-12-26T17:01:00.145296075+09:00",
                        "End": "2023-12-26T17:01:00.17747529+09: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": "2023-12-26T17:01:30.186133016+09:00",
                        "End": "2023-12-26T17:01:30.21879033+09: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": "2023-12-26T17:02:00.23288382+09:00",
                        "End": "2023-12-26T17:02:00.265206937+09: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": "2023-12-26T17:02:30.275323237+09:00",
                        "End": "2023-12-26T17:02:30.308047573+09: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"
                    }
                ]
            }
        },
#헬스 체크 로그들
(이하 생략)....



헬스 체크는 애플리케이션 안에서 테스트를 하고 그 결과를 도커에게 통보했으니 기대한 역활을 충분히 해냄 -> 하지만 이런 상태임에도 여전히 애플리케이션을 실행중이고 컨테이너 중지나 새 컨테이너 교체같은 반응이 이루어지지 않았다. 
-> 도커는 이러한 작업을 안전하게 처리할 수 없기 때문 -> 도커 엔진은 단일 서버에서 동작하며 이상 컨테이너를 중지 및 재시작 할 수 있지만 그시간 동안 애플리케이션이 동작 X -> 이상이 생긴 컨테이너의 교체또한 보관된 데이터가 유실 될 수 있고 동작하지 않는다. -> 문제 악화의 여지때문에 통보 후 따로 조치하지 않는다. -> 하지만 헬스체크는 계속 돌아가며 체크 실패일 뿐이엿다면 정상으로 돌아간다

도커 스윙이나 쿠버네티스는 이러한 클러스터 환경에서 헬스체크 기능이 더욱 탁월하다 -> 이상통보를 받으면 자동적으로 조치를 한다. -> 실행할 여력이 존재하기 때문이다.


여러개의 서버로 구성된 클러스터는 일부가 비정상 상태가 되더라도 새 컨테이너를 실행해 복구가 가능하고 서비스 중단으로 이어지지 않는다
-> 하지만 분산 애플리케이션은 교체시 실행 순서를 고려하지 않기 때문에 컨테이너간의 의존관계 문제때문에 이슈가 생긴다.

#실습1 (의존관계 애플리케이션 이슈)

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container rm -f $(docker container ls -aq)
dadccb45cb6b
eab66abc9b03
#기존 컨테이너 삭제

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container run -d -p 8082:80 diamol/ch08-numbers-web
Unable to find image 'diamol/ch08-numbers-web:latest' locally
latest: Pulling from diamol/ch08-numbers-web
(생략)
Status: Downloaded newer image for diamol/ch08-numbers-web:latest
83c3ac9e0ef9a51567f40887e0636cfd80b27213ce7fa531579573fb3d3848b4
#WEB 이미지만 실행

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container ls
CONTAINER ID   IMAGE                     COMMAND                  CREATED         STATUS         PORTS                                   NAMES
83c3ac9e0ef9   diamol/ch08-numbers-web   "dotnet /app/Numbers…"   5 seconds ago   Up 4 seconds   0.0.0.0:8082->80/tcp, :::8082->80/tcp   relaxed_grothendieck
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers#
#확인시 정상 작동으로 보인다.

 


서버 프로세스가 실행 중이고 컨테이너가 정상이지만 API를 사용 할 수 없으므로 웹 애플리케이션도 정상 작동되지 않는다.

의존 관계를 만족하는지 디펜던시 체크 기능도 도커 이미지에서 설정이 가능하다.
-> 디펜던시 체크: 애플리케이션 실행 전에 필요한 요구사항을 확인하는 기능 (모든 요구사항이 확인되면 체크가 성공하고 애플리케이션이 실행된다, 반대라면 실행되지 않는다)
-> 애플리케이션 실행명령에 로직을 추가하는 방법으로 구현된다.

#디펜던시 체크를 추가한 Dockerfile 스크립트
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-web# cat Dockerfile.v2
FROM diamol/dotnet-sdk AS builder

WORKDIR /src
COPY src/Numbers.Web/Numbers.Web.csproj .
RUN dotnet restore

COPY src/Numbers.Web/ .
RUN dotnet publish -c Release -o /out Numbers.Web.csproj

# app image
FROM diamol/dotnet-aspnet

ENV RngApi:Url=http://numbers-api/rng

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

WORKDIR /app
COPY --from=builder /out/ .

#CMD로 디펜던시 체크가 구현되어 있으며 API 요청을 보내 사용 가능한지 확인 후 사용 가능하다면 닷네 코어를 실행한다.
#그 후에도 dotnet 프로세스의 상태를 지속적으로 모니터링 한다.



#실습2 (디펜던시 체크)
user@DESKTOP-KU8L7UV:~$ sudo docker container run -d -p 8084:80 diamol/ch08-numbers-web:v2
[sudo] password for user:
Unable to find image 'diamol/ch08-numbers-web:v2' locally
v2: Pulling from diamol/ch08-numbers-web
...(중략)
Status: Downloaded newer image for diamol/ch08-numbers-web:v2
02d3244ce779adf6d6886718ebbd0fe65729bb50c2d860db35a0278e0bc0f09a
#디펜던시 체크가 포함된 도커 이미지로 컨테이너 실행


user@DESKTOP-KU8L7UV:~$ sudo docker container ls --all
CONTAINER ID   IMAGE                        COMMAND                  CREATED              STATUS                          PORTS                                   NAMES
02d3244ce779   diamol/ch08-numbers-web:v2   "/bin/sh -c 'curl --…"   About a minute ago   Exited (6) About a minute ago                                           pedantic_fermi
83c3ac9e0ef9   diamol/ch08-numbers-web      "dotnet /app/Numbers…"   16 hours ago         Exited (255) 7 minutes ago      0.0.0.0:8082->80/tcp, :::8082->80/tcp   relaxed_grothendieck
#status 항목을 보면 디펜던시 체크가 포함된 diamol/ch08-numbers-web:v2 컨테이너는 체크가 실패해 'Exited (6) About a minute ago'상태로 애플리케이션이 종료되어 있다

crul은 매우 유용한 도구이지만 현장에서는 보안때문에 사용하지 않는다.

도커이미지에는 어플리케이션을 구동하는데 최소한의 내용만 들어가야 하며 추가적인 도구를 넣어봤자 외부공격의 노출여지만 늘릴 뿐이다.
-> curl이 유용하지만 별도의 커스텀 유틸리티를 사용하는 편이 좋다

  • 애플리케이션과 같은 언어로 구현된 커스텀 유틸리티 장점
    • 애플리케이션과 같은 도구를 사용하므로 이미지에 추가적인 소프트웨어 포함이 필요없다
    • 복잡한 체크로직을 적용이 가능하며 크로스 플랫폼 이미지라면 더더욱 유용하다
    • 애플리케이션과 같은 설정을 사용해 URL을 여러곳에 반복 정의하거나 수정에서 누락시키는 일을 방지 가능하다컨테이너 실행 전에 확인이 필요한 모든사항을 검증가능하다(ex: 라이브러리 환경 DB접속, 인증서 파일 유무 등)
#커스텀 유틸리티로 crul대체
...(생략)
# app image
FROM diamol/dotnet-aspnet

ENTRYPOINT ["dotnet", "Numbers.Api.dll"]
HEALTHCHECK CMD ["dotnet", "Utilities.HttpCheck.dll", "-u", "http://localhost/health"]

WORKDIR /app
COPY --from=http-check-builder /out/ .
COPY --from=builder /out/ .
#헬스체크가 테스트 유틸리티로 적용되어 있다.



#실습1 (HTTP 테스트 유틸리티 헬스체크)
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# docker container rm -f $(docker container ls -aq)
02d3244ce779
83c3ac9e0ef9
#컨테이너 초기화

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# docker container run -d -p 8080:80 --health-interval 5s diamol/ch08-numbers-api:v3
Unable to find image 'diamol/ch08-numbers-api:v3' locally
v3: Pulling from diamol/ch08-numbers-api
(생략)
Status: Downloaded newer image for diamol/ch08-numbers-api:v3
643e51c4ce42b1bfb271cf1f26e86a9712fe9de3191dc36fb71b4357d087ae72
#v3 컨테이너 생성

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# docker container ls
CONTAINER ID   IMAGE                        COMMAND                  CREATED          STATUS                    PORTS                                   NAMES
643e51c4ce42   diamol/ch08-numbers-api:v3   "dotnet Numbers.Api.…"   30 seconds ago   Up 29 seconds (healthy)   0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_shirley

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# curl http://localhost:8080/rng
9
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# curl http://localhost:8080/rng
61
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# curl http://localhost:8080/rng
20
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-api# curl http://localhost:8080/rng
{"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1","title":"An error occured while processing your request.","status":500,"traceId":"|255aadd9-4273598f40f5c7b5."}
#4번째에서 500 오류 발생

CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS                     PORTS                                   NAMES
643e51c4ce42   diamol/ch08-numbers-api:v3   "dotnet Numbers.Api.…"   8 minutes ago   Up 8 minutes (unhealthy)   0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_shirley
#세번 연속 실패를 반환해서 status에서 상태이상(unhealthy) 출력



다양한 상황에 대응이 가능하다는 것이 HTTP 테스트 유틸리티의 장점

#디펜던시 체크에도 커스텀 유틸리티 사용하기
# app image
FROM diamol/dotnet-aspnet

ENV RngApi__Url=http://numbers-api/rng

CMD dotnet Utilities.HttpCheck.dll -c RngApi:Url -t 900 && \
    dotnet Numbers.Web.dll

WORKDIR /app
COPY --from=http-check-builder /out/ .
COPY --from=builder /out/ .
#dotnet -t: 유틸리티가 요청에 대한 응답을 기다릴 제한시간
#dotnet -c: 애플리케이션과 같은 설정 파일을 읽어 그 설정대로 대상  URL을 지정
#실습2 (테스트 유틸리티를 활용한 디펜던시 체크)
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-web# docker container run -d -p 8081:80 diamol/ch08-numbers-web:v3
Unable to find image 'diamol/ch08-numbers-web:v3' locally
v3: Pulling from diamol/ch08-numbers-web
..(생략)
Status: Downloaded newer image for diamol/ch08-numbers-web:v3
95e0eb868c9b3a0d79577da30b5a01deac512dd5df9bc02e6d6c8fbceda397ee
#컨테이너 생성 (생성 시 테스트 유틸리티로 디펜던시 체크 수행)

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers/numbers-web# docker container ls --all
CONTAINER ID   IMAGE                        COMMAND                  CREATED          STATUS                      PORTS                                   NAMES
95e0eb868c9b   diamol/ch08-numbers-web:v3   "/bin/sh -c 'dotnet …"   31 seconds ago   Exited (1) 29 seconds ago
                              reverent_goldberg
643e51c4ce42   diamol/ch08-numbers-api:v3   "dotnet Numbers.Api.…"   53 minutes ago   Up 53 minutes (unhealthy)   0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_shirley
#web은 디펜던시 체크가 실패해 status에서 'Exited (1) 29 seconds ago'를 띄우고 있다
#api는 실행 중이지만 이름이 지정되지 않아 web 어플리케이션이 api를 발견하지 못했다.

 

커스텀 테스트 유틸리티를 사용하면 이미지 이식성이 향상된다. -> 컨테이너 플랫폼마다 헬스체크와 디펜던시 체크를 정의하고 실행하는 방법에 차이가 있다 -> 하지만 모든 로직을 테스트 유틸리티로 포함시킨다면 어떤 환경에서도 그대로 동작이 가능하다.


도커 컴포즈는 애플리케이션에서 이상이 생겼을 때 어느 정도 복원이 가능하게 하는 기능이 있다. -> 하지만 이상이 생긴 컨테이너를 새 컨테이너로 대체하지 않는다.(단일 서버에서 애플리케이션을 실행 중이라면 더 심각한 장애를 일으킬 수 있기 때문) -> 종료된 컨테이너를 재시작하거나 이미지에 정의되지 않은 헬스체크를 추가할 수는 있다.

#도커 컴포즈에서의 헬스체크
services:
  numbers-api:
    image: diamol/ch08-numbers-api:v3
    ports:
      - "8087:80"
    healthcheck:
      interval: 5s
      timeout: 1s
      retries: 2
      start_period: 5s
    networks:
      - app-net
#해당 v3 이미지에는 헬스체크가 커스텀 테스트 유틸리티로 적용되어 있다.

 

  • 도커 컴포즈의 healthcheck옵션
    • interval: 헬스 체크 실시 간격(위의 컴포즈에서는 5초)
    • timeout: 응답을 받지못하면 실패로 간주하는 제한시간(1초)
    • retries: 컨테이너의 상태를 이상으로 간주할 때까지 필요한 연속 실패 횟수
    • start_period: 컨테이너 실행 후 첫 헬스체크를 실시하는 시간간격
#컴포즈 파일에 헬스체크 정의하기
  numbers-web:
    image: diamol/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
#test 필드가 헬스체크를 실행하는 명령
#스크립트에 'restart: on-failure'부문이 있으므로 컨테이너가 예기치 않게 종료되면 컨테이너를 재시작한다.
#의존관계를 정의하는 'depends_on' 설정이 없으므로 도커 컴포즈는 컨테이너에 어떤 순서든 실행할 수 있다.
#api 컨테이너가 작동하기 전에 웹 컨테이너가 작동해 디펜던시 체크가 실패하면서 웹 컨테이너가 종료되지만 api 컨테이너도 실행 될 것이므로 마지막에는 디펜던시 마저 성공해 정상 동작 할 것이다.
#실습1 (도커 컴포즈 헬스체크)
root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker-compose up -d
Creating numbers_numbers-api_1 ... done
Creating numbers_numbers-web_1 ... done
#의존관계가 정해져 있지 않기 때문에 동시에 생성된다.

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container ls
CONTAINER ID   IMAGE                        COMMAND                  CREATED          STATUS                            PORTS                                   NAMES
bf0ce2088e66   diamol/ch08-numbers-web:v3   "/bin/sh -c 'dotnet …"   11 seconds ago   Up 9 seconds (health: starting)   0.0.0.0:8088->80/tcp, :::8088->80/tcp   numbers_numbers-web_1
39f9d1d2b593   diamol/ch08-numbers-api:v3   "dotnet Numbers.Api.…"   11 seconds ago   Up 10 seconds (healthy)           0.0.0.0:8087->80/tcp, :::8087->80/tcp   numbers_numbers-api_1
#api부재로 방금 재시작된 web 서비스

root@DESKTOP-KU8L7UV:/home/user/080258/ch08/exercises/numbers# docker container logs numbers_numbers-web_1
HTTPCheck: error. Url http://numbers-api/rng, exception Connection refused
HTTPCheck: status OK, url http://numbers-api/rng, took 209ms
#첫번째는 응답시간 초과로 실패 -> 두번째에 성공해서 정상적으로 api 동작

분산 시스템으로 동작하는 애플리케이션은 유연성과 기민성 면에서 뛰어나다 -> 관리도 그만큼 힘들다 (구성요소간의 복잡한 의존관계)

헬스체크와 디펜던시 체크가 있으므로써 순서를 보장하게 될 필요가 없어졌다.

헬스체크와 디펜던시 체크시 항상 주의가 필요하다
헬스체크: 시스템에 부하를 주는 내용을 지양하며 리소스를 많이 소모하지 않으면서 애플리케이션이 실질적으로 동작중인지 검증할 수 있는 핵심적인 부분을 테스트해야한다.
디펜던시 체크: 테스트에 소모되는 리소스를 크게 신경쓸 필요는 없지만 테스트 대상이 빠짐없이 정확하도록 주의해야한다.(의존관계 등)


연습문제

제공한 memory-check.js 파일을 이용하여 해당하는 Dockerfile에 헬스체크 기능을 추가해 메모리를 임의로 측정시키게 하여라

vi Dockerfile

FROM diamol/node

ENV MAX_ALLOCATION_MB=4096 \
    LOOP_ALLOCATION_MB=512 \
    LOOP_INTERVAL_MS=2000

CMD ["node", "memory-hog.js"]

WORKDIR /app
COPY src/ .

->

FROM diamol/node

ENV MAX_ALLOCATION_MB=4096 \
    LOOP_ALLOCATION_MB=512 \
    LOOP_INTERVAL_MS=2000

CMD node memory-check.js && \
    node memory-hog.js

HEALTHCHECK --interval=5s \
 CMD node memory-check.js
#헬스 체크시 해당 문 실행

WORKDIR /app
COPY src/ .


docker image build -t diamol/ch08-lab:solution -f Dockerfile.solution .
#생성

docker container run diamol/ch08-lab:solution
#실행시 지속적으로 임의의 메모리가 출력되며 무한히 증가한다