사전 단일 장애점
개념

단일 장애점

gabury1

단일 장애점은 그것 하나가 멈추면 시스템 전체를 함께 멈추게 하는 지점입니다. 서버를 열 대로 늘려 두어도 모든 요청이 반드시 거쳐 가는 곳은 하나뿐일 수 있습니다. 그 하나가 멈추는 순간 나머지 아홉 대도 일을 못 합니다. 시스템을 설계할 때는 이런 지점을 먼저 찾아냅니다.

쉽고 빠른 이해

단일 장애점은 그 하나가 죽으면 서비스 전체가 멈추는 구성 요소입니다. 웹 서버 열 대가 데이터베이스 한 대를 같이 쓰고 있으면 그 데이터베이스가 단일 장애점입니다.

이 이름을 따로 두는 것은 장애가 나기 전에 위험한 곳을 짚어 두려는 것입니다. 서버 대수만 세면 여유가 있어 보입니다. 모두가 지나가는 한 지점은 대수에 안 잡힙니다.

어떻게 찾는가:

  1. 요청이 들어와 응답이 나갈 때까지 거쳐 가는 것을 늘어놓습니다
  2. 하나씩 지워 보고 남은 것으로 서비스가 되는지 봅니다
  3. 지웠을 때 서비스가 멈추는 것이 단일 장애점입니다

대가가 있습니다. 없애려면 같은 것을 두 벌 이상 두어야 해서 장비와 운영 비용이 늘어납니다. 두 벌이 서로 자기가 주인이라고 여기는 상황도 새로 대비해야 합니다.

상세

출입구가 하나뿐인 건물을 떠올려 봅시다. 안에 사무실이 스무 개 있어도 그 문이 잠기면 스무 개가 모두 문을 닫습니다. 사무실을 늘리는 것으로는 이 문제가 풀리지 않습니다.

단일 장애점(Single Point of Failure, SPOF)은 그것 하나가 멈추면 시스템 전체가 멈추는 구성 요소를 가리킵니다. 웹 서버 열 대가 데이터베이스 한 대에 붙어 있는 구성이 그렇습니다. 웹 서버가 몇 대든 그 데이터베이스가 내려가면 요청은 하나도 처리되지 않습니다.

무엇을 단일 장애점으로 치나

판정은 한 문장입니다. 이것을 빼고도 서비스가 되는가. 빼면 안 되는 것이 단일 장애점입니다.

같은 일을 할 수 있는 것을 하나 더 두는 일을 다중화라고 합니다. 두 벌만 두는 다중화를 따로 이중화라고 부릅니다. 다중화가 안 된 구성 요소가 곧 단일 장애점입니다.

그래서 이 말은 고장이 잦다는 뜻이 아닙니다. 고장 날 확률이 아무리 낮아도 대신할 것이 없으면 단일 장애점입니다.

아래는 웹 서버를 셋으로 늘려 두고 데이터베이스는 한 대만 둔 구성입니다.

flowchart TD
    C["사용자"] --> W1["웹 서버 1"]
    C --> W2["웹 서버 2"]
    C --> W3["웹 서버 3"]
    W1 --> DB["데이터베이스 · 한 대"]
    W2 --> DB
    W3 --> DB

웹 서버는 한 대가 죽어도 남은 두 대가 받습니다. 데이터베이스는 화살표가 모두 한 곳으로 모이므로 대신할 것이 없습니다. 서버를 열 대로 늘려도 이 그림의 아래쪽은 바뀌지 않습니다.

「전체」가 어디까지인지도 같이 정해야 합니다. 결제만 멈추고 상품 조회는 되는 구성이라면 결제 서버는 결제 기능의 단일 장애점이고 서비스 전체의 단일 장애점은 아닙니다. 무엇에 대한 단일 장애점인지를 붙여 말하면 이 혼동이 사라집니다.

기능마다 구역을 갈라 놓으면 이렇게 보입니다.

flowchart TD
    C["사용자"] --> G["앞단"]
    subgraph PAYZONE["결제 기능"]
        PAY["결제 서버 · 한 대"]
    end
    subgraph QZONE["조회 기능"]
        Q1["조회 서버 1"]
        Q2["조회 서버 2"]
    end
    G --> PAY
    G --> Q1
    G --> Q2

결제 서버가 멈추면 결제 구역만 멈춥니다. 조회 구역으로 가는 경로는 그대로 살아 있습니다.

자주 생기는 곳

단일 장애점은 대개 「모두가 지나가는 곳」과 「모두가 물어보는 곳」에 생깁니다. 요청을 나누어 주는 장치(로드 밸런서), 이름을 주소로 바꿔 주는 DNS(Domain Name System, 도메인 네임 시스템), 상태를 혼자 쥐고 있는 저장소가 그렇습니다.

어디 흔한 예 멈추면
들어오는 길목 게이트웨이 한 대 · 로드 밸런서 한 대 요청이 안으로 못 들어옵니다
이름을 주소로 바꾸는 단계 DNS 서버 한 대 주소를 못 얻어 접속이 시작되지 않습니다
상태를 쥔 곳 쓰기를 받는 데이터베이스 한 대 저장과 조회가 함께 멈춥니다
모두가 물어보는 곳 인가 서버 · 설정 저장소 로그인과 기동이 막힙니다
눈에 안 띄는 바닥 전원 회선 하나 · 네트워크 스위치 하나 위에 놓인 서버가 여러 대여도 같이 내려갑니다

표의 마지막 줄이 특히 잘 놓칩니다. 서버를 세 대로 늘려 놓고도 그 셋을 같은 전원과 같은 스위치에 물려 두면 위층만 셋이고 아래층은 여전히 하나입니다.

구성도에 그려지는 층과 안 그려지는 층을 나눠 보면 이렇습니다.

flowchart TD
    subgraph SEEN["구성도에 그려지는 것"]
        W1["웹 서버 1"]
        W2["웹 서버 2"]
        W3["웹 서버 3"]
    end
    subgraph HIDDEN["구성도에 안 그려지는 것"]
        SW["네트워크 스위치 · 한 대"]
        PW["전원 회선 · 하나"]
    end
    W1 --> SW
    W2 --> SW
    W3 --> SW
    W1 --> PW
    W2 --> PW
    W3 --> PW

위층의 서버 셋은 눈에 보이고 대수도 셉니다. 아래층의 스위치와 전원 회선은 구성도에 안 그려져서 오래 남습니다.

사람도 같은 자격으로 셉니다. 배포 열쇠를 한 명만 쥐고 있거나 복구 절차가 한 명의 기억에만 있으면 그 사람이 단일 장애점입니다.

찾아내는 순서

구성도를 펴 놓고 하나씩 지워 보는 것이 기본 절차입니다. 요청이 들어와 응답이 나갈 때까지 거치는 것을 빠짐없이 적습니다. 그다음 각각에 「이것이 지금 없어지면 무엇이 안 되나」를 적습니다.

여기서 두 가지를 더 봐야 합니다. 첫째, 여러 대로 보이는 것들이 정말 따로 죽는지입니다. 한 기계 위에 여러 개를 띄워 쓰는 컨테이너 셋은 그 기계가 꺼지면 셋 다 꺼집니다. 둘째, 그 구성 요소가 멈췄을 때 앞단이 버텨 주는지입니다.

세 물음을 하나로 이으면 이런 순서가 됩니다.

flowchart TD
    A["거쳐 가는 것을 늘어놓는다"] --> B["하나를 지워 본다"]
    B --> C{"지우면 서비스가 멈추나"}
    C -->|"멈춘다"| S["단일 장애점"]
    C -->|"버틴다"| D{"여러 대가 정말 따로 죽나"}
    D -->|"아니다"| S
    D -->|"그렇다"| E{"멈춰도 앞단이 버텨 주나"}
    E -->|"아니다"| F["멈춤이 옆으로 번진다"]
    E -->|"그렇다"| B

버텨 주지 못하면 멈춤이 옆으로 번집니다. 한 곳의 멈춤이 그것을 기다리던 곳까지 끌고 내려가는 것을 연쇄 장애라고 합니다. 이때는 단일 장애점 하나가 그림보다 훨씬 넓은 구역을 멈춰 세웁니다.

서버가 멀쩡히 떠 있는지 주기로 물어 확인하는 헬스 체크는 이 작업의 도구이지 답이 아닙니다. 죽은 것을 알려 줄 뿐, 대신할 것을 만들어 주지는 않습니다.

없애는 방법

없애는 방법은 하나뿐입니다. 같은 일을 할 것을 하나 더 두고, 한쪽이 죽으면 다른 쪽이 받게 하는 것입니다. 죽은 쪽에서 살아 있는 쪽으로 일을 넘기는 이 전환을 페일오버라고 합니다.

상태를 안 가진 구성 요소는 이 일이 쉽습니다. 웹 서버는 요청을 받아 처리하고 잊어버립니다. 똑같은 것을 한 대 더 띄우고 앞에 로드 밸런서를 두면 끝납니다.

상태를 쥔 구성 요소는 데이터를 같이 들고 있어야 해서 한 단계가 더 붙습니다. 원본의 변경을 다른 쪽에 계속 옮겨 담는 복제가 그 단계입니다. 복제가 조금 뒤처진 상태에서 전환이 일어나면 마지막 몇 건을 잃을 수 있습니다.

전환이 어떤 차례로 일어나는지를 상태로 늘어놓으면 이렇습니다.

stateDiagram-v2
    A: 주가 쓰기를 받는다
    B: 주가 멈춘다
    C: 대기가 주로 올라선다
    D: 새 주가 쓰기를 받는다
    A --> B
    B --> C
    C --> D: 복제가 못 따라간 몇 건은 잃는다

두 벌을 어떻게 쓸지는 둘 중 하나로 갈립니다. 둘 다 평소에 일을 받게 하는 방식을 액티브-액티브라고 합니다. 노는 장비가 없습니다. 대신 양쪽이 같은 데이터를 동시에 건드리지 않게 막아야 합니다.

한쪽만 일하고 다른 쪽은 기다리게 하는 방식은 액티브-스탠바이라고 합니다. 구성이 단순합니다. 대신 기다리는 장비 값을 계속 냅니다.

없애고도 남는 것

다중화를 해도 단일 장애점이 사라지지 않고 옮겨 가는 경우가 많습니다. 데이터베이스를 주·대기 두 대로 만들고 사용자 앞에 로드 밸런서를 한 대 세우면, 이제는 그 한 대가 모든 요청이 지나는 곳이 됩니다.

flowchart TD
    C["사용자"] --> LB["로드 밸런서 · 한 대"]
    LB --> W1["웹 서버 1"]
    LB --> W2["웹 서버 2"]
    LB --> W3["웹 서버 3"]
    W1 --> P["데이터베이스 · 주"]
    W2 --> P
    W3 --> P
    P -.복제.-> S["데이터베이스 · 대기"]

아래층은 튼튼해졌습니다. 데이터베이스 주가 죽으면 대기가 올라섭니다. 대신 맨 위에 새 지점이 하나 생겼습니다. 지점이 옮겨 갔다는 것을 모르면 「이중화했으니 됐다」고 믿은 채로 남습니다.

전환하는 장치 자신도 같은 문제를 안고 있습니다. 죽은 쪽을 가려내고 넘겨주는 판단을 한 곳에서만 하고 있으면 그 판단이 곧 단일 장애점입니다.

여분이 있어도 같은 원인으로 함께 죽는 것은 또 다른 문제입니다. 두 대에 같은 버그가 들어 있거나, 같은 인증서가 같은 날 만료되거나, 같은 배포가 두 대에 동시에 나가면 둘이 같은 순간에 멈춥니다. 대수는 둘입니다. 견디는 힘은 한 대입니다.

반대로 둘 다 살아 있으면서 서로를 죽은 것으로 보는 경우도 있습니다. 양쪽이 자기가 주인이라고 여기고 각각 쓰기를 받으면 데이터가 갈라집니다. 이것이 스플릿 브레인입니다. 흔한 대비책은 과반이 동의해야 주인이 되게 하는 정족수입니다.

알고 남기는 선택

모든 단일 장애점을 없애는 것이 목표는 아닙니다. 없애는 값이 멈췄을 때 잃는 값보다 크면 남겨 두는 편을 고릅니다. 사내 도구나 야간에만 도는 배치는 대개 여기에 듭니다.

남기기로 했으면 멈춤을 짧게 만드는 쪽에 힘을 씁니다. 백업을 정해진 주기로 받아 두고, 다시 띄우는 절차를 미리 적어 두고, 한 번은 실제로 해 보는 것입니다.

손을 못 쓰는 쪽은 단일 장애점이 있는 줄 모르는 경우입니다. 모르면 대비도 못 하고 터진 뒤에야 어디였는지 알게 됩니다. 그래서 어디를 알고 남겼는지를 목록으로 적어 둡니다.

멈추는 시간을 줄여 늘 쓸 수 있게 만드는 것을 고가용성이라고 합니다. 이 목록을 줄여 가는 일이 고가용성을 만드는 작업의 절반입니다.

관련 항목

단일 장애점을 없애는 수단

이중화 · 핫 스탠바이 · 콜드 스탠바이 · 액티브-액티브 · 액티브-스탠바이 · 부하 분산 · 클러스터 · 수평 확장 · 가용 영역 · 샤딩

단일 장애점이 남았는지 재는 지표

가용성 · 평균 복구 시간 · 평균 고장 간격 · 가동률 · 서비스 수준 목표 · 오류 예산

하나가 멈출 때 번지는 실패

부분 실패 · 회색 장애 · 재시도 폭풍 · 썬더링 허드 · 병목 · 커넥션 고갈

번지는 것을 끊는 대비책

서킷 브레이커 · 벌크헤드 · 타임아웃 · 재시도 · 속도 제한 · 우아한 성능 저하

단일 장애점이 자주 생기는 구성 요소

프록시 · 리버스 프록시 · 메시지 브로커 · 공유 데이터베이스 · 설정 저장소 · etcd · 캐시 서버

여분을 두고 나서 새로 생기는 문제

리더 선출 · 하트비트 · 펜싱 · 분단 · 일관성 · 복제 지연

단일 장애점을 미리 찾아내는 방법

카오스 엔지니어링 · 장애 주입 · 모니터링 · 재해 복구 · 장애 훈련 · 의존성 분석

단일 장애점을 다루는 상위 분야

분산 시스템 · 신뢰성 공학 · SRE · 내결함성 · 장애 격리

다른 이름: SPOF · Single Point of Failure