Skip to content
isdnetworks
Go back

한 대뿐이라 손댈 수가 없었다

수집 서버가 한 대였다. 고쳐야 할 것이 있는데 못 고치고 있었다.

Table of contents

Open Table of contents

멈출 수 없었다

장비 스무 대가 그 서버로 HTTP 로 보낸다. 서버를 내리면 그동안 자료가 안 들어온다.

장비가 큐에 쌓아 두므로 잃지는 않는데 오래 내리면 그 큐가 넘친다. 큐 상한이 하루치라 정지 가능 시간이 그보다 짧아야 했다.

큐 상한   하루치
서버 정지 가능 시간   그보다 짧아야 한다

계산상 몇 시간은 되는데 실제로는 아무도 collect-01 을 몇 시간 내리자고 하지 않았다. 자료를 놓칠 위험을 지고 고치자는 말을 꺼내기가 어려웠다. InfluxDB 판 하나 올리자고 하루치 자료를 걸 수는 없었다.

미룬 것이 쌓였다

미룬 목록이 이렇게 됐다.

시계열 저장소 판 올리기   미룸
디스크 늘리기             미룸
커널 갱신                 미룸
수집 코드 구조 개선        미룸

넉 달째 미루고 있었다. InfluxDB 판 올리기부터 커널 갱신까지 한 번에 해야 할 것이 계속 늘었다.

한 대뿐이라 못 고치고 못 고치니 더 손대기 어려워지는 상태였다. 미루는 것이 미루는 이유를 만들고 있었고 그 고리를 어디선가 끊어야 했다.

조치 — 고치려고 늘린 한 대

그래서 한 대를 더 놓기로 했다. 부하 때문이 아니라 고치기 위해 늘리는 것이었다.

서버 A   지금 것
서버 B   새로 놓는다

장비가 collect-01collect-02 중 하나로 보내게 하면 하나를 내려도 다른 쪽이 받는다. 멈춰도 되는 상태를 먼저 만들고 고치는 순서다.

늘리는 이유를 분명히 안 해 두면 CPU 도 남는데 왜 늘리느냐는 말이 나온다. 처리량이 아니라 정지 가능 시간을 사는 것이라고 적어 뒀다. 같은 장비를 두 대 놓는데 목적이 다르면 뒤에 붙일 것도 달라진다.

어떻게 나눌지 정했다

앞에 분산기를 두는 방법도 있었는데 장비 쪽에서 고르는 방식으로 했다.

static const char *servers[] = { "collect-01", "collect-02" };

int send_row(row_t *r) {
    for (int i = 0; i < 2; i++) {
        int idx = (cur_server + i) % 2;
        if (post_to(servers[idx], r) == 0) { cur_server = idx; return 0; }
    }
    return -1;
}

cur_server 에 기억해 둔 쪽을 먼저 부르고 post_to 가 실패하면 다른 쪽으로 넘어간다. 성공한 쪽을 다시 기억하므로 다음부터는 살아 있는 쪽으로 바로 간다.

분산기를 두면 그것이 다시 하나뿐인 고장 지점이 된다. 하나를 없애려고 늘리면서 새 하나를 만드는 셈이라 안 뒀다.

제약 — 두 곳에 나뉘는 자료

나눠 받으면 자료가 두 곳에 있게 된다. 조회할 때 둘을 다 봐야 한다.

서버 A ─┐
        ├─→ 저장소
서버 B ─┘

그래서 두 서버가 같은 InfluxDB 에 쓰게 했다. 저장소가 다시 하나가 되지만 그쪽은 갱신이 잦지 않았다.

수집 서버는 코드를 자주 고치므로 그쪽을 늘린 것이었다. 무엇을 늘려야 하는지는 무엇을 자주 손대는지가 정했다.

전부를 둘로 만들면 비용도 두 배가 되고 맞춰야 할 것도 늘어난다. 자주 손대는 계층만 고른 것이 이번 판단의 핵심이었다.

미룬 것을 하나씩 처리했다

한 대를 내릴 수 있게 되니 미룬 것을 꺼냈다.

1. B 를 새 판으로 세운다
2. 장비 몇 대를 B 로 보내 본다
3. 값이 맞는지 확인한다
4. 전부 B 로 보낸다
5. A 를 새 판으로 올린다
6. 다시 나눈다

한 번에 하나씩 했다. 넉 달 미룬 것을 3주에 걸쳐 다 처리했다.

하나씩 하니 문제가 나도 어느 단계 때문인지 curl 한 번으로 갈렸다. 한꺼번에 했으면 커널 갱신 탓인지 InfluxDB 판 탓인지 구분이 안 됐을 것이다.

검증 — 두 대가 같은 결과를 내는가

두 서버가 같은 결과를 내는지 확인이 필요했다.

$ for h in collect-01 collect-02; do
    echo -n "$h "
    curl -s "http://$h:8086/query?db=sensor&q=SELECT+count(*)+FROM+reading+WHERE+time>now()-1h"
  done

같은 InfluxDB 를 보므로 count(*) 값이 같아야 하고 다르면 한쪽이 못 쓰고 있는 것이다.

판을 올린 뒤에는 이 count(*) 를 며칠 지켜봤다. 올린 직후에 맞는 것과 며칠 뒤에도 맞는 것이 다른 확인이었다.

결과 — 따라온 것과 새로 생긴 것

원래 목적은 고칠 수 있게 하는 것이었는데 다른 것도 따라왔다. 한 대가 죽어도 수집이 안 멈춘다.

실제로 한 번 디스크 오류로 collect-01 이 죽었는데 아무도 몰랐다. collect-02 가 다 받고 있었기 때문이다.

그런데 그것이 새 문제이기도 했다. 죽은 것을 모르는 상태가 며칠 갔다.

# 두 대 다 살아 있는지 확인
for h in $SERVERS; do
    curl -sf "http://$h:8086/ping" > /dev/null || echo "DOWN: $h"
done

curl -sf/ping 이 안 되면 호스트 이름을 찍게 했다. 한 대가 죽어도 서비스가 되니 알림이 오히려 더 필요해졌다.

전에는 죽으면 수집이 멈춰서 그 자체가 알림이었는데 collect-02 가 받아 주면서 그 신호를 잃었다.

정리


Share this post on:

Previous Post
큐에 쌓인 것이 두 종류다
Next Post
대수를 늘려도 안 빨라진다