Skip to content
isdnetworks
Go back

떠 있는 것과 응답하는 것은 다르다

서비스가 안 된다는 얘기를 들었다. 서버에 들어가서 확인했다.

$ ps -ef | grep java
deploy  3821  ... java -jar app.jar

떠 있어서 정상이라고 답했다. 그래도 안 된다고 했다.

Table of contents

Open Table of contents

떠 있는데 안 받고 있었다

포트를 봤다.

$ netstat -tlnp | grep 8080

아무것도 안 나온다. 프로세스는 있는데 포트를 안 열고 있었다. 로그를 보니 시작하다 데이터베이스 접속에서 멈춰 있었다.

2014-03-13 09:12:03 Connecting to database...

여기서 더 안 나간다. 접속을 무한정 기다리고 있었다.

프로세스가 살아 있어도 요청을 받는다는 뜻은 아니다. 감시는 앞의 것만 보고 있었으니 정상이라는 답이 틀린 것은 아니었지만 쓸모가 없었다.

검증 — 단계를 나눠 확인했다

이 뒤로 확인을 단계로 나눴다.

1. 프로세스가 있나        ps
2. 포트를 열었나          netstat
3. 연결이 되나            nc -z
4. 응답을 주나            curl
5. 정상 응답인가          응답 내용 확인

각 단계가 다른 것을 본다. netstat 까지는 나오는데 curl 에서 멈추는 경우도 있었다. 연결은 받는데 처리가 안 되는 상태다.

어느 단계에서 끊기는지에 따라 볼 곳이 달라진다. 단계를 나누니 같은 안 된다는 말이 다른 조사로 갈렸다.

상태 확인 경로를 만들었다

5번을 위해 간단한 경로를 하나 뒀다.

@RequestMapping("/health")
public Map<String, Object> health() {
    Map<String, Object> m = new HashMap<String, Object>();
    m.put("status", "ok");
    m.put("db", checkDb() ? "ok" : "fail");
    m.put("cache", checkCache() ? "ok" : "fail");
    m.put("version", APP_VERSION);
    return m;
}
$ curl -s http://localhost:8080/health
{"status":"ok","db":"ok","cache":"fail","version":"1.4.2"}

checkDbcheckCache 를 각각 담으니 캐시가 안 된다는 것이 바로 보인다. 의존하는 것을 하나씩 확인해야 어디가 문제인지 나온다.

상태 확인이 무거우면 그것 때문에 부하가 생긴다.

private boolean checkDb() {
    try {
        jdbcTemplate.queryForObject("SELECT 1", Integer.class);
        return true;
    } catch (Exception e) {
        return false;
    }
}

SELECT 1 처럼 가장 가벼운 쿼리를 쓴다. 실제 업무 쿼리를 돌리면 안 된다. 시간 제한도 뒀는데 확인이 오래 걸리는 것 자체가 이상 신호이기 때문이다.

애매한 생존보다 확실한 종료

시작 중인 것과 준비된 것을 구분했다.

private volatile boolean ready = false;

@PostConstruct
public void onStart() {
    // 초기화
    ready = true;
}

ready 가 아직 거짓이면 상태 경로가 503을 준다. 앞단이 그것을 보고 요청을 안 보낸다. 이걸 넣기 전에는 시작 중인 서버로 요청이 가서 오류가 났다.

무한정 기다리는 것도 고쳤다.

dataSource.setConnectionTimeout(10000);

정해 둔 시간 안에 접속이 안 되면 시작이 실패하고 프로세스가 죽는다. 죽으면 감시가 알아채고 다시 뜬다.

애매하게 살아 있는 것보다 확실하게 죽는 게 대응하기 쉽다. 살아 있으면 아무도 손대지 않지만 죽으면 알림이 간다.

감시도 고쳤다.

# 전
pgrep -f app.jar >/dev/null || alert "프로세스 없음"

# 후
CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 http://localhost:8080/health)
if [ "$CODE" != "200" ]; then
  alert "상태 확인 실패 (HTTP ${CODE})"
fi

pgrep 이 아니라 curl 의 응답 코드를 본다. 떠 있는데 안 받는 상태를 이걸로 잡는다.

정리


Share this post on:

Previous Post
200인데 본문이 비어 있었다
Next Post
프레임워크가 정해 둔 자리