사전 시계 동기화
개념

시계 동기화

gabury1고친 사람 github-actions[bot]

시계 동기화는 여러 컴퓨터의 시계가 같은 시각을 가리키게 맞추는 일입니다. 컴퓨터 시계는 혼자 두면 조금씩 틀어집니다. 그래서 믿을 만한 기준 시계에 때때로 지금 몇 시인지 묻습니다. 그리고 어긋난 만큼 고칩니다.

쉽고 빠른 이해

시계 동기화는 서버들이 같은 시각을 쓰게 만드는 일입니다. 서버 두 대의 로그를 합쳐 시간 순으로 읽을 수 있는 것이 이 덕분입니다.

맞추지 않으면 서버마다 시각이 조금씩 벌어집니다. 그러면 어느 요청이 먼저였는지 못 가립니다. 막 만든 토큰을 다른 서버가 벌써 만료됐다고 거절하기도 합니다.

  1. 기준 시각을 가진 서버에 지금 몇 시냐고 묻습니다
  2. 답이 오가는 데 걸린 시간을 걷어 내고 내 시계가 얼마나 어긋났는지 계산합니다
  3. 그만큼 시계를 옮기거나, 시계가 가는 속도를 바꿔 서서히 따라잡습니다

대가는 완벽하게는 못 맞춘다는 것입니다. 아주 가까운 때에 난 두 사건의 순서는 시각만으로 못 가립니다. 시계를 뒤로 옮기면 같은 시각이 두 번 지나가기도 합니다.

상세

친구 넷이 저녁 일곱 시에 만나기로 했다고 해 봅시다. 한 사람의 손목시계는 5분 빠릅니다. 다른 사람 것은 3분 느립니다. 그래서 헤어지기 전에 다 같이 한 사람의 휴대폰 시각을 보고 바늘을 맞춥니다. 그래야 일곱 시에 같은 곳에 모입니다.

컴퓨터 안에도 지금 몇 시인지 알려 주는 시계가 있습니다. 이 시계를 벽시계라고 부릅니다. 로그에 찍히는 시각, 토큰에 적는 만료 시각이 전부 벽시계에서 나옵니다.

시계 동기화는 기기마다 따로 도는 벽시계를 하나의 기준 시각에 맞추는 일입니다. 서버 두 대의 로그를 한 화면에 합쳐 보는 장면을 떠올려 보십시오. 두 서버가 같은 기준을 따르고 있어야 두 로그를 한 줄씩 시간 순으로 섞을 수 있습니다.

시각을 쓰는 판정은 대개 두 기기의 시계를 맞댑니다. 시각을 찍는 서버와 그 시각을 검사하는 서버가 다르기 때문입니다. 두 시계가 어긋나 있으면 아래처럼 판정이 틀어집니다.

하는 일 시계가 어긋나면
서버 여럿의 로그를 시간 순으로 합친다 결과가 원인보다 먼저 찍힌 것처럼 보인다
토큰이나 세션의 만료를 검사한다 막 발급한 토큰을 이미 만료됐다고 거절한다
인증서의 유효 기간을 검사한다 멀쩡한 인증서를 아직 유효하지 않다고 본다
시각으로 만드는 일회용 비밀번호를 맞춰 본다 맞게 입력한 코드를 틀렸다고 거절한다
가로챈 메시지를 다시 보내는 재전송 공격을 시각으로 거른다 정상 메시지를 낡았다고 버린다
분산 락의 만료 시각을 센다 락이 풀렸다고 먼저 믿은 서버가 끼어든다

여섯 줄 모두 한 기기가 찍은 시각을 다른 기기가 읽습니다. 시계 동기화는 그 두 시계를 한 기준에 묶어 둡니다.

이 절은 여섯 가지를 차례로 봅니다. 시계가 어긋나는 까닭, 어긋남을 재는 법, 고치는 법, 기준 시각이 내려오는 길, 시계를 맞추는 프로토콜, 그리고 맞춰도 남는 오차입니다. 끝으로 동기화된 시계로도 못 푸는 일을 가릅니다.

시계가 어긋나는 까닭

컴퓨터가 시간을 세는 박자는 수정 진동자에서 나옵니다. 작은 수정 조각에 전기를 주면 일정한 횟수로 떨립니다. 컴퓨터는 이 떨림을 세어 시간이 얼마나 흘렀는지 압니다.

떨리는 횟수는 부품마다 조금씩 다릅니다. 온도가 바뀌어도 흔들립니다. 그래서 두 컴퓨터를 같은 시각에서 출발시켜도 며칠 뒤에는 서로 다른 시각을 가리킵니다.

이렇게 시계가 기준보다 빠르거나 느리게 가는 것을 클록 드리프트라고 합니다. 드리프트는 속도의 차이입니다. 속도가 아주 조금만 달라도 시간이 쌓이면 벌어진 양이 커집니다.

지금 이 순간 기준에서 벌어져 있는 양은 오프셋이라고 부릅니다. 기준 시각이 10:00:00 일 때 내 시계가 10:00:02 를 가리키면 오프셋은 2초입니다.

시계가 한꺼번에 크게 틀어지는 때도 있습니다. 멈춰 두었던 가상 머신을 되살리면 멈춘 시간만큼 뒤처진 채 깨어납니다. 동기화를 맡은 프로그램이 죽어 있으면 드리프트만큼 조용히 계속 벌어집니다.

시계 동기화는 오프셋을 재서 0 가까이로 되돌리는 일입니다. 드리프트는 멈추지 않으므로 한 번 맞추고 끝나지 않습니다. 때때로 다시 재고 다시 고칩니다.

어긋남을 재는 법

이 소절은 요청 한 번의 왕복으로 오프셋을 재는 방법을 봅니다. 시각 네 개를 찍고 그 넷으로 계산합니다.

기준 서버에 시각을 물으면 답이 도착했을 때 그 답은 이미 낡았습니다. 네트워크를 건너오는 동안 시간이 흘렀기 때문입니다. 그래서 오간 시간도 함께 재서 걷어 냅니다.

묻는 쪽을 클라이언트라 하겠습니다. 클라이언트가 요청을 보낸 시각이 t1 입니다. 서버가 받은 시각이 t2, 답을 보낸 시각이 t3 입니다. 클라이언트가 답을 받은 시각이 t4 입니다. t1 과 t4 는 클라이언트 시계로 찍습니다. t2 와 t3 은 서버 시계로 찍습니다.

sequenceDiagram
    participant 클라이언트
    participant 서버 as 기준 서버
    Note over 클라이언트: t1 · 보낸 시각을 내 시계로 찍는다
    클라이언트->>서버: 지금 몇 시입니까
    Note over 서버: t2 · 받은 시각을 서버 시계로 찍는다
    Note over 서버: t3 · 답을 보내는 시각을 찍는다
    서버-->>클라이언트: t2 와 t3 을 담아 돌려준다
    Note over 클라이언트: t4 · 받은 시각을 내 시계로 찍는다

답에는 서버가 찍은 시각 둘이 실려 옵니다. 클라이언트는 자기가 찍은 둘과 합쳐 넷을 손에 쥡니다.

아래는 서버 시계가 클라이언트보다 0.5초 앞서 있을 때의 값입니다. 단위는 밀리초입니다. 가는 길과 오는 길이 똑같이 걸렸다고 잡았습니다.

t1 = 1000
t2 = 1530
t3 = 1540
t4 = 1070

왕복 = (t4 - t1) - (t3 - t2)     // 60
오프셋 = ((t2-t1) + (t3-t4)) / 2  // 500

왕복은 전체 걸린 시간에서 서버가 요청을 붙잡고 있던 시간을 뺀 값입니다. 네트워크 위에서 보낸 시간만 남아 0.06초가 나옵니다. 이 값을 왕복 시간이라고 부릅니다.

오프셋은 가는 길에서 본 차이와 오는 길에서 본 차이를 평균 낸 값입니다. 가는 길의 차이 t2 - t1 은 530 입니다. 서버가 앞선 500 에 요청이 가는 데 걸린 30 이 더해진 값입니다.

오는 길의 차이 t3 - t4 는 470 입니다. 앞선 500 에서 답이 오는 데 걸린 30 을 뺀 값입니다. 두 값을 더하면 +30 과 -30 이 서로 지워집니다. 남은 1000 을 둘로 나눈 500 이 오프셋입니다.

서버 시계가 0.5초 앞서 있다는 결과입니다. 클라이언트는 자기 시계를 0.5초 앞으로 옮기면 됩니다.

이 계산은 가는 길과 오는 길이 같은 시간 걸렸다고 가정합니다. 가는 데 50, 오는 데 10 이 걸렸다면 +50 과 -10 이 다 지워지지 않습니다. 남은 40 의 절반인 20 만큼 오프셋이 틀립니다.

틀리는 폭은 왕복 시간의 절반을 넘지 않습니다. 한쪽 길이 왕복 시간을 전부 차지할 때 가장 크게 틀리기 때문입니다. 그래서 왕복이 짧은 가까운 서버일수록 더 촘촘하게 맞춥니다.

한 번 재고 믿지는 않습니다. 여러 번 잰 뒤 왕복이 짧았던 측정을 더 믿습니다. 왕복이 짧을수록 길이 막혔을 여지가 작기 때문입니다.

어긋남을 고치는 두 방법

오프셋을 알았으면 시계를 고칩니다. 고치는 방법은 둘입니다. 어느 쪽을 고르느냐에 따라 시계가 뒤로 가기도 합니다.

스텝은 시계 값을 목표 시각으로 곧장 옮깁니다. 한순간에 맞춰집니다. 시계가 앞서 있었다면 값이 뒤로 돌아갑니다. 그러면 같은 시각을 두 번 지나가게 됩니다.

슬루는 시계가 가는 속도를 잠깐 바꿔 서서히 따라잡습니다. 앞서 있으면 조금 느리게, 뒤처져 있으면 조금 빠르게 갑니다. 값이 뒤로 가지 않는 대신 다 맞추기까지 오래 걸립니다.

스텝 슬루
하는 일 값을 곧장 옮긴다 속도를 바꿔 서서히 맞춘다
맞춰지는 때 한순간 오래 걸린다
값이 뒤로 가나 간다 안 간다
주로 쓰는 때 많이 어긋났을 때 조금 어긋났을 때

보통은 조금 어긋났으면 슬루로, 많이 어긋났으면 스텝으로 고칩니다. 부팅 직후처럼 크게 틀어져 있을 때 슬루로 따라잡으면 너무 오래 걸리기 때문입니다.

스텝이 벽시계를 뒤로 돌리면 그 벽시계로 잰 경과 시간이 음수가 됩니다. 그래서 걸린 시간을 잴 때는 동기화가 값을 옮기지 않는 단조 시계를 씁니다. 단조 시계는 한 방향으로만 늘어나는 시계입니다.

기준 시각이 내려오는 길

기준 서버도 누군가에게서 시각을 받아야 합니다. 이렇게 다른 서버에 시각을 나눠 주는 서버를 시간 서버라고도 부릅니다. 이 소절은 그 시각이 어디서 시작해 일반 서버까지 내려오는지 봅니다.

맞추는 목표는 UTC(Coordinated Universal Time, 협정 세계시)입니다. UTC 는 온 세계가 함께 쓰는 기준 시각입니다. 서울의 서버와 런던의 서버가 같은 값을 가리키게 하려면 둘이 따를 한 기준이 필요합니다.

나라마다 다른 시간대나 서머타임은 이 값에 안 들어갑니다. 동기화는 UTC 만 맞춥니다. 그 나라 몫은 사람에게 보여 줄 때 더합니다. 그래서 서머타임이 시작되는 날에도 동기화가 할 일은 달라지지 않습니다.

맨 위에는 원자시계 같은 정밀한 장치가 있습니다. GPS(Global Positioning System, 위성 위치 확인 시스템) 위성도 아주 정밀한 시각을 실어 보냅니다. 그 시각을 받는 GPS 수신기도 맨 위의 장치로 씁니다. 이런 장치에 직접 붙은 서버가 1차 시간 서버가 됩니다.

1차 서버에 묻는 서버가 2차, 2차에 묻는 서버가 3차가 되는 식으로 계층이 내려갑니다. 계층을 하나 내려갈 때마다 네트워크를 건너며 생긴 오차가 조금씩 더해집니다. 대신 위 계층의 서버 몇 대가 아래 계층의 수많은 서버를 떠받칠 수 있습니다.

flowchart TD
    subgraph top["맨 위 · 기준 장치"]
        A["원자시계 · GPS 수신기"]
    end
    subgraph l1["1차 시간 서버"]
        B1["서버 가"]
        B2["서버 나"]
    end
    subgraph l2["2차 시간 서버"]
        C["회사 안의 시간 서버"]
    end
    subgraph l3["일반 서버"]
        D1["애플리케이션 서버"]
        D2["데이터베이스 서버"]
        D3["캐시 서버"]
    end
    A --> B1
    A --> B2
    B1 --> C
    B2 --> C
    C --> D1
    C --> D2
    C --> D3

회사 안에 시간 서버를 하나 두는 구성이 흔합니다. 나머지 서버는 모두 그 서버에 묻습니다. 바깥 서버에 각자 묻는 것보다 서버끼리의 차이가 작아집니다. 모두가 같은 서버를 기준으로 삼기 때문입니다.

시계를 맞추는 프로토콜

서버 대부분은 NTP(Network Time Protocol, 네트워크 시간 프로토콜)로 시계를 맞춥니다. NTP 는 앞에서 본 네 시각 계산을 일반 네트워크 위에서 소프트웨어로 돌립니다. 인터넷 너머의 서버에 맞추면 오차는 대개 밀리초 단위에 머뭅니다. 앞에서 본 계층 번호를 NTP 는 stratum이라고 부릅니다.

PTP(Precision Time Protocol, 정밀 시간 프로토콜)는 같은 계산을 훨씬 촘촘하게 합니다. 시각을 소프트웨어가 아니라 네트워크 카드와 스위치 같은 장비가 찍습니다. 패킷이 장비를 지나는 순간을 하드웨어가 적으므로 운영체제가 처리하느라 늦어지는 시간이 계산에서 빠집니다.

PTP 는 거래 순서를 가려야 하는 금융 시스템, 통신망, 공장 설비처럼 NTP 의 오차로는 모자란 곳에서 씁니다. 대신 PTP 를 지원하는 네트워크 장비가 있어야 합니다. 일반 백엔드 서버는 대개 NTP 로 충분합니다.

맞춰도 남는 오차

시계 동기화는 오차를 줄일 뿐 없애지는 못합니다. 이 소절은 남는 오차가 무엇을 흔드는지, 시스템이 그 오차를 어떻게 견디는지 봅니다.

서버 둘이 같은 기준을 따라도 어느 한순간 두 시계는 조금 다릅니다. 두 서버에서 아주 가까운 때에 사건이 하나씩 났다고 해 봅시다. 먼저 난 사건이 더 늦은 타임스탬프를 다는 일이 생깁니다.

그래서 시각을 견주는 쪽은 여유를 둡니다. 토큰의 만료 시각을 검사할 때 조금 지났어도 받아 주는 것이 그 예입니다. 여유가 없으면 동기화가 잘 돌고 있어도 경계에 걸린 요청이 오락가락 거절됩니다.

재전송 공격은 남이 가로챈 메시지를 나중에 그대로 다시 보내는 공격입니다. 이 공격을 막는 쪽도 여유를 둡니다. 보내는 쪽은 메시지에 보낸 시각을 싣습니다. 받는 쪽은 자기 시각과 너무 먼 메시지를 버립니다.

Kerberos 라는 인증 방식이 이렇게 합니다. 어디서부터 너무 먼지를 정하는 값이 곧 여유입니다. 그래서 두 시계의 차이가 그 여유보다 작아야 정상 메시지가 버려지지 않습니다.

동기화된 시계로도 못 푸는 일

시계를 잘 맞춰도 벽시계가 맡으면 안 되는 일이 둘 있습니다. 걸린 시간을 재는 일과, 여러 서버에서 난 사건의 앞뒤를 확실히 가리는 일입니다.

걸린 시간은 앞에서 본 단조 시계로 잽니다. 사건의 앞뒤는 논리 시계로 가립니다. 논리 시계는 시각 대신 사건마다 번호를 매겨 앞뒤만 따지는 방법입니다.

하려는 일 쓸 시계
사람이나 다른 기기에 보여 줄 시각을 찍는다 동기화된 벽시계
요청 하나가 얼마나 걸렸는지 잰다 단조 시계
서버 여럿에서 난 사건의 앞뒤를 확실히 가린다 논리 시계

첫 줄만 시계 동기화에 기댑니다. 나머지 두 줄은 동기화가 틀어져도 답이 흔들리지 않는 시계를 고른 것입니다.

관련 항목

시계 동기화를 맡는 프로토콜과 프로그램

NTP · PTP · SNTP · chrony · ntpd · systemd-timesyncd

오프셋을 재고 고르는 알고리즘

크리스티안 알고리즘 · 버클리 알고리즘 · 마르줄로 알고리즘 · 왕복 시간

시계 동기화가 따라가는 기준 시각

UTC · TAI · 원자시계 · GPS 수신기 · stratum · 윤초 · 타임스케일

시계를 어긋나게 하는 원인

수정 진동자 · 클록 드리프트 · 클록 스큐 · 오프셋 · 실시간 시계 · 가상 머신

시계 동기화가 벽시계 값을 고치는 방식

스텝 · 슬루 · 윤초 문지르기

동기화 대상인 벽시계와 맞세워지는 시계

벽시계 · 단조 시계 · 논리 시계 · 램포트 시계 · 벡터 시계 · 하이브리드 논리 시계 · TrueTime

맞춰진 시계에 기대는 판정

타임스탬프 · 재전송 공격 · Kerberos · 일회용 비밀번호 · 인증서 · 토큰 · 세션 · 리스 · 분산 락 · 로그

시각을 적고 읽는 형식

RFC 3339 · ISO 8601 · 유닉스 시간 · 에포크 · 시간대 · 서머타임

시계 동기화가 속하는 상위 분류

시간과 시계 · 분산 시스템 · 네트워크 · 운영체제

다른 이름: clock synchronization · time synchronization · 시각 동기화 · 시간 동기화 · 클록 동기화