infra
Platform

모듈 맵

[Linux] chrony & NTP 서버 동기화로 트랜잭션 시간 오차 방지

0 / 37 완료

펼치기
0 / 37 완료0%

Linux · 26 / 37

[Linux] chrony & NTP 서버 동기화로 트랜잭션 시간 오차 방지

서버 시간이 틀리면 토큰이 만료된다 — chrony, timedatectl 완전 설정

🚨INCIDENT ALERT
HIGH

JWT 인증 토큰이 간헐적으로 거부된다는 문제 신고가 들어왔습니다. 로그를 보니 token issued in the future 에러입니다. 코드 변경은 없었는데 왜 갑자기? 원인을 추적해보니 서버 중 한 대의 시계가 다른 서버보다 30초 빠르게 맞춰져 있었습니다. 클라우드 VM이 마이그레이션된 이후 NTP 데몬이 재시작되지 않아 시계가 조금씩 벗어났던 겁니다. 분산 시스템에서는 서버마다 시간이 같아야 한다는 전제 조건이 있고, 그게 깨지면 인증과 로그 분석이 모두 엉킵니다.

시간 동기화 (NTP & chrony)

서버의 시스템 시계가 몇 초만 틀려도 JWT 인증 토큰 검증 실패, TLS 핸드셰이크 오류, 로그 순서 뒤집힘 같은 장애가 발생합니다. 클라우드 환경에서는 VM이 일시 정지(sleep/migrate)된 후 시계가 수십 초~수 분씩 틀어지는 일이 흔합니다. NTP(Network Time Protocol)와 chrony를 이용해 서버 시간을 정확하게 유지하는 방법을 배웁니다.


1. 서버 시간이 왜 중요한가

이번 챕터에서 배울 것
  • 1서버 시간 불일치가 JWT·TLS·로그에 미치는 영향
  • 2NTP Stratum 계층 구조와 시간 동기화 원리
  • 3chrony가 ntpd를 대체한 이유 및 차이점
  • 4chrony 설치, /etc/chrony.conf 타임서버 설정
  • 5chronyc tracking / sources 로 동기화 상태 모니터링
  • 6timedatectl로 타임존 변경 및 hwclock 동기화
실습 환경 준비
현재 시간 동기화 상태 확인
timedatectl status
chrony 데몬 상태 및 동기화 추적 확인
systemctl status chronyd && chronyc tracking
chrony 패키지 설치 (미설치 시)
sudo apt-get install -y chrony # 또는 sudo yum install -y chrony
타임존 설정 주의사항

export TZ=Asia/Seoul 은 현재 셸 세션에만 임시 적용되므로, 영구 변경은 반드시 timedatectl set-timezone 을 사용할 것

💡개념

시간 불일치가 만드는 장애 시나리오

NTP Stratum 계층 구조와 시간 동기화 흐름

서버 운영에서 시간 동기화는 '있으면 좋은 것'이 아니라 필수 인프라입니다. 시간이 틀렸을 때 발생하는 실제 장애 유형을 살펴보겠습니다.

JWT / OAuth 토큰 만료

JWT(JSON Web Token)에는 iat(발급 시각)와 exp(만료 시각)가 Unix timestamp로 저장됩니다. 인증 서버와 애플리케이션 서버 간 시계가 30초 이상 차이 나면 방금 발급된 토큰도 "이미 만료"로 처리됩니다.

AuthServer  time: 10:00:00  → JWT exp = 10:30:00
AppServer   time: 10:31:00  → "토큰 만료!" (실제 5분밖에 안 지남)

TLS 인증서 유효성 검증 실패

TLS 인증서에는 Not BeforeNot After 필드가 있습니다. 클라이언트 시계가 서버보다 1시간 앞서 있으면, 아직 유효한 인증서도 "미래 인증서"로 거부됩니다. HTTPS 연결 자체가 실패합니다.

분산 시스템 로그 추적 불가

마이크로서비스 환경에서 서비스 A → B → C 순서로 호출이 일어났는데 각 서버 시계가 제각각이면 로그를 시간순으로 정렬해도 실제 호출 순서와 다르게 보입니다. Jaeger, Zipkin 같은 분산 트레이싱 도구도 타임스탬프 기반이므로 시간 불일치 시 추적 그래프가 왜곡됩니다.

Kerberos / Active Directory 도메인 조인 실패

Kerberos 프로토콜은 보안상 클라이언트와 KDC(Key Distribution Center) 간 시계 차이가 5분 이내여야 한다고 강제합니다. 시간이 5분 이상 벗어나면 도메인 조인, 로그인, Kerberos 티켓 갱신이 모두 실패합니다.

KRB5KRB_AP_ERR_SKEW: Clock skew too great

데이터베이스 복제 및 분산 합의

PostgreSQL 스트리밍 복제, Cassandra, etcd, CockroachDB 등은 노드 간 시계 차이를 기반으로 이벤트 순서를 결정합니다. 시계 차이가 크면 데이터 충돌 해결이 틀려지거나 복제가 중단될 수 있습니다.


2. NTP 프로토콜과 계층 구조

💡개념

NTP Stratum 계층 구조 원리

NTP Stratum 계층 구조 원리 상세

chronyc tracking을 실행했을 때 "Stratum: 2"가 뜨는데, 이게 좋은 건지 나쁜 건지, 왜 Stratum 값이 낮을수록 더 정확한 건지 궁금합니다. 또 사내 NTP 서버를 따로 구성해야 한다는 말을 들었는데 왜 Google이나 AWS의 공개 NTP 서버를 바로 쓰면 안 되는지도 의문입니다. Stratum 계층 구조를 이해하면 이 질문들에 답이 생기고, timedatectl 출력에서 시간 동기화 품질을 판단할 수 있게 됩니다.

NTP(Network Time Protocol, RFC 5905)는 인터넷을 통해 클라이언트가 정확한 시각을 얻을 수 있게 해주는 프로토콜입니다. 계층(Stratum) 구조로 정확도를 등급화합니다.

Stratum 0 (기준 시계)
  ├── GPS 위성 수신기
  ├── 세슘/루비듐 원자시계
  └── 무선 표준시 수신기 (DCF77, WWVB)

Stratum 1 (1차 타임서버)
  ├── time.google.com  (Google)
  ├── time.cloudflare.com  (Cloudflare)
  ├── time.bora.net  (KT)
  └── time.nist.gov  (미국 표준기술연구소)
      (Stratum 0 장비에 직접 연결)

Stratum 2 (2차 타임서버)
  ├── pool.ntp.org  (전 세계 자원봉사 서버 풀)
  └── 기업 내부 NTP 서버
      (Stratum 1에 동기화)

Stratum 3 (3차 타임서버 / 일반 서버)
  └── 우리가 설정하는 서버들
      (Stratum 2에 동기화)

Stratum 번호가 낮을수록 더 정확합니다. Stratum 16은 "동기화 안 됨"을 의미합니다.

NTP 동기화 메커니즘

클라이언트는 서버에 요청 패킷을 보내고 4개의 타임스탬프를 이용해 오프셋(offset)과 왕복 지연(round-trip delay)을 계산합니다:

T1 : 클라이언트가 요청 전송한 시각
T2 : 서버가 요청을 받은 시각
T3 : 서버가 응답을 전송한 시각
T4 : 클라이언트가 응답을 받은 시각

왕복 지연 = (T4 - T1) - (T3 - T2)
오프셋    = ((T2 - T1) + (T3 - T4)) / 2

계산된 오프셋만큼 로컬 시계를 천천히 조정합니다. 급격한 시계 점프는 오히려 장애를 유발하므로, chrony는 시계를 slew(조금씩 당기거나 미루기) 방식으로 부드럽게 보정합니다.

사용 포트

NTP는 UDP 123번 포트를 사용합니다. 방화벽에서 이 포트가 막혀 있으면 동기화가 전혀 작동하지 않습니다.


3. chrony vs ntpd

💡개념

chrony가 ntpd를 대체한 이유

chrony vs ntpd 비교 — 빠른 수렴과 불안정 네트워크 대응

새 Ubuntu 22.04 서버에서 시간 동기화 설정을 하려는데, 이전 팀원 문서는 전부 ntpd 기반이고 현재 운영 중인 일부 서버도 ntpd를 씁니다. 검색하면 chrony를 쓰라는 글이 나오는데, 그냥 두 개 다 설치해서 쓰면 안 되냐는 생각이 드는 상황입니다. 실제로 혼용하면 UDP 123 포트 충돌이나 시계 엇갈림으로 더 큰 문제가 생깁니다. chrony가 ntpd를 대체하게 된 배경을 알면 어떤 도구를 선택해야 하는지, 기존 ntpd 서버를 언제 마이그레이션해야 하는지 기준이 생깁니다.

과거에는 ntpd(NTP daemon)가 표준이었지만, RHEL 7 / CentOS 7부터 chrony가 기본으로 채택되었고, Ubuntu 18.04+, Debian 10+ 등 대부분의 최신 배포판이 chrony를 기본 NTP 클라이언트로 사용합니다.

항목chronyntpd
개발 연도1990s (현재도 활발히 개발)1980s (개발 축소)
빠른 초기 동기화매우 빠름 (수 초)느림 (수 분)
불안정한 네트워크잘 처리취약
VM / 컨테이너 환경최적화문제 많음
간헐적 인터넷 연결지원미지원
설정 파일/etc/chrony.conf/etc/ntp.conf
제어 도구chronycntpq, ntpdc
메모리 사용적음많음
RHEL 8/9 기본OX (제거됨)

VM과 컨테이너 환경에서 chrony가 특히 중요한 이유:

가상 머신은 하이퍼바이저가 CPU 자원을 다른 VM과 나누어 사용하기 때문에 VM 내부 시계가 실제보다 느리게 흐릅니다. VM이 일시 정지(suspend/migrate)된 후 깨어나면 몇 초~수 분의 시계 드리프트가 발생합니다. chrony는 이런 큰 오프셋도 빠르게 보정할 수 있도록 설계되어 있습니다.


3-1. NTP 포트 표 & 데몬 1개 원칙

💡개념

NTP 포트/프로토콜 표 — 방화벽 규칙 작성 기준

NTP UDP 123 방화벽 규칙 — 클라이언트/서버 설정

시간 동기화 트러블슈팅의 절반은 방화벽 문제입니다. 어떤 포트를 열어야 하는지 정확히 알아야 합니다.

서비스프로토콜/포트방향설명
NTP (chrony/ntpd)UDP 123Outbound외부 NTP 서버와 동기화 (필수)
NTP 서버로 동작UDP 123Inbound내부 클라이언트에게 시간 제공
chronyc 원격 관리UDP 323Inbound (선택)chronyc로 원격에서 chrony 관리
NTS-KE (키 교환)TCP 4460Outbound (선택)Network Time Security — 인증된 NTP

실무 포인트: 대부분의 서버는 UDP 123 Outbound 만 열면 됩니다. NTS는 최신 보안 기능이며 아직 범용 사용은 드뭅니다.

로컬 터미널
# 방화벽에서 NTP 허용 확인 (클라이언트)
sudo firewall-cmd --list-all | grep ntp
# 또는
sudo iptables -L OUTPUT -n | grep 123

# UDP 123 포트 테스트 (서버 도달 여부)
nc -uzv pool.ntp.org 123
# 결과: Connection to pool.ntp.org 123 port [udp/ntp] succeeded!
💡개념

동기화 데몬 1개 원칙 — timesyncd vs chrony vs ntpd 충돌 방지

동기화 데몬 충돌 방지 — timesyncd vs chrony vs ntpd

한 서버에서 NTP 데몬은 반드시 1개만 실행해야 합니다. 두 데몬이 동시에 UDP 123 포트를 소유하려 해서 충돌하거나, 서로 다른 서버를 참조해 시계가 엇갈릴 수 있습니다.

3가지 데몬 비교:

데몬패키지기본 탑재 배포판권장 여부
systemd-timesyncdsystemd 내장Ubuntu 18.04+, Debian 12+단순 클라이언트 용도로 적합
chronychronyRHEL 8+, Ubuntu 20.04+권장 — VM/클라우드 환경에 최적화
ntpdntp구형 배포판신규 설치 비권장 (chrony로 대체)

충돌 징후 진단:

서버 터미널
# 동시에 실행 중인 NTP 관련 데몬 확인
systemctl is-active systemd-timesyncd chrony ntpd 2>/dev/null

# 충돌 로그 확인 (chrony가 시작 실패하는 경우)
journalctl -u chrony --since "10 min ago" | grep -E "error|fail|conflict"
# 흔한 메시지: "Could not open /dev/pps0" 또는 "Already running as NTP daemon"

# UDP 123 포트를 점유한 프로세스 확인
sudo ss -ulnp | grep 123

올바른 전환 절차 (timesyncd → chrony):

서버 터미널
# Step 1: timesyncd 중지 및 비활성화 (충돌 방지)
sudo systemctl disable --now systemd-timesyncd

# Step 2: chrony 설치 및 시작
sudo apt install -y chrony          # Ubuntu/Debian
sudo dnf install -y chrony          # RHEL/CentOS

sudo systemctl enable --now chronyd

# Step 3: 데몬 1개만 실행 중인지 확인
systemctl is-active systemd-timesyncd   # → inactive
systemctl is-active chronyd             # → active

# Step 4: 동기화 확인
chronyc tracking | grep "System time"

클라우드 환경 주의: AWS EC2, GCP, Azure는 각자 내부 NTP 서버(169.254.169.123 등)를 제공합니다. 인스턴스 내 chrony 설정에서 이 주소를 우선 사용하면 외부 UDP 123 포트를 열지 않아도 됩니다.


4. chrony 설치 및 기본 설정

실습 전 디렉토리와 예제 파일을 먼저 준비합니다.

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/linux/part5/exam_233 && cd /tmp/linux/part5/exam_23

# NTP 설정 샘플 파일
cat > chrony.conf.sample << 'EOF'
# /etc/chrony.conf 예제
# 공개 NTP 풀 서버 사용
pool 2.pool.ntp.org iburst maxsources 4

# 한국 NTP 서버 (KT, KRISS)
server time.bora.net iburst
server ntp.kornet.net iburst

# 로컬 시계 허용 오차 (초)
makestep 1.0 3

# 하드웨어 타임스탬프 사용 (지원하는 경우)
# hwtimestamp *

# 동기화 로그
logdir /var/log/chrony
log measurements statistics tracking
EOF

이제 실습을 진행합니다.

chrony 설치 및 서비스 활성화

RHEL / CentOS / Rocky Linux / AlmaLinux:

로컬 터미널
# 설치 (RHEL 8/9는 기본 설치되어 있음)
sudo dnf install -y chrony

# 서비스 시작 및 부팅 자동 시작 등록
sudo systemctl enable --now chronyd

# 서비스 상태 확인
sudo systemctl status chronyd

Ubuntu / Debian:

로컬 터미널
# 설치
sudo apt update && sudo apt install -y chrony

# 서비스 활성화
sudo systemctl enable --now chrony

# Ubuntu에서는 서비스 이름이 'chrony' (d 없음)
sudo systemctl status chrony

기존 timesyncd 비활성화 (Ubuntu):

Ubuntu 18.04+는 systemd-timesyncd라는 가벼운 NTP 클라이언트가 기본으로 실행됩니다. chrony와 충돌하므로 비활성화합니다:

서버 터미널
# systemd-timesyncd 중지 및 비활성화
sudo systemctl disable --now systemd-timesyncd

# 비활성화 확인
systemctl is-active systemd-timesyncd
# 출력: inactive

ntpd가 있다면 충돌 방지:

서버 터미널
# ntpd가 실행 중인지 확인
systemctl is-active ntpd 2>/dev/null || echo "ntpd 없음"

# ntpd가 있다면 중지
sudo systemctl disable --now ntpd 2>/dev/null
🔍실행 후 확인할 것
  • systemctl status chronyd 에서 'active (running)' 상태가 확인된다
  • timedatectl status 에서 'NTP synchronized: yes' 항목이 표시된다
  • chronyc tracking 에서 'System time' 오프셋이 수 밀리초 이내로 줄어들어 있다
  • chronyc sources -v 에서 타임서버 목록이 나타나고 * 표시가 현재 동기화 중인 서버를 가리킨다

5. chrony.conf 타임서버 설정

/etc/chrony.conf 타임서버 설정

chrony의 핵심 설정 파일은 /etc/chrony.conf입니다. 기본 설정을 확인하고 한국 환경에 맞게 수정합니다.

로컬 터미널
# 현재 설정 확인
sudo cat /etc/chrony.conf

기본 설정 파일 구조 (RHEL 계열):

INI
# /etc/chrony.conf

# ── 타임서버 설정 ────────────────────────────────────────────
# pool: 여러 서버를 자동으로 선택 (권장)
# iburst: 초기 동기화를 빠르게 (첫 연결 시 패킷을 연속 전송)
pool 2.rhel.pool.ntp.org iburst

# ── 한국/아시아 권장 타임서버 ────────────────────────────────
# pool.ntp.org: 지역 기반 서버 자동 선택
pool kr.pool.ntp.org iburst

# KT 한국 타임서버
server time.bora.net iburst

# Google 공개 NTP (IPv4/IPv6 지원, Stratum 1)
server time.google.com iburst

# AWS Time Sync Service (EC2 인스턴스에서 권장)
# 링크-로컬 주소, 외부 네트워크 불필요
server 169.254.169.123 prefer iburst

# Cloudflare NTP
server time.cloudflare.com iburst

# ── 드리프트 파일 ────────────────────────────────────────────
# 시계 주파수 오차를 저장, 재시작 후 빠른 보정에 사용
driftfile /var/lib/chrony/drift

# ── 큰 시간 차이 자동 수정 ──────────────────────────────────
# 오프셋이 1초 이상이면 즉시 수정 (최초 3회만)
makestep 1.0 3

# ── 하드웨어 클럭 동기화 ────────────────────────────────────
rtcsync

# ── 로컬 네트워크에 NTP 서버로 제공 ─────────────────────────
# (내부 NTP 서버로 운용할 때만 활성화)
# allow 192.168.0.0/16

# ── 로그 설정 ───────────────────────────────────────────────
logdir /var/log/chrony

AWS EC2 환경 전용 설정:

로컬 터미널
# EC2에서는 AWS Time Sync를 우선 사용
sudo tee /etc/chrony.conf > /dev/null <<'EOF'
# AWS Time Sync Service (링크-로컬, 항상 사용 가능)
server 169.254.169.123 prefer iburst

# 백업 서버
pool kr.pool.ntp.org iburst
server time.google.com iburst

driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony
EOF

설정 변경 후 chrony 재시작:

서버 터미널
sudo systemctl restart chronyd

# 문법 오류 확인 (재시작 실패 시)
sudo chronyd -Q 'server time.bora.net iburst'

6. 동기화 상태 확인

chronyc로 동기화 상태 모니터링

chronyc는 chronyd와 통신하는 명령줄 도구입니다. 동기화 상태를 확인하는 두 가지 핵심 명령을 살펴봅니다.

chronyc sources — 타임서버 목록 및 상태:

로컬 터미널
chronyc sources -v
  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = synced, '+' = combined, '-' = not combined,
| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 169.254.169.123               3   6   377    42    +15us[  +8us] +/-  473us
^+ time.bora.net                 2   7   377   107    -2ms[ -2ms] +/-   12ms
^+ time.google.com               1   8   255    43    +1ms[ +1ms] +/-   15ms
^- kr.pool.ntp.org               2   9   177   212    -5ms[ -5ms] +/-   25ms

상태 기호 해석:

기호의미
*현재 동기화 중인 서버 (최적 서버)
+보조로 함께 사용 중인 서버
-선택되지 않은 서버
?연결 불가 서버
x시간 오류 가능성 있는 서버

Reach 컬럼: 최근 8번의 폴링 성공 여부를 8진수로 표현합니다. 377은 8번 모두 성공, 0은 연결 안 됨.

chronyc tracking — 전체 동기화 상태:

로컬 터미널
chronyc tracking
Reference ID    : A9FEA97B (169.254.169.123)
Stratum         : 4
Ref time (UTC)  : Thu Mar 26 01:23:45 2026
System time     : 0.000015372 seconds fast of NTP time
Last offset     : +0.000015372 seconds
RMS offset      : 0.000018521 seconds
Frequency       : 2.345 ppm fast
Residual freq   : +0.012 ppm
Skew            : 0.234 ppm
Root delay      : 0.000946234 seconds
Root dispersion : 0.001234567 seconds
Update interval : 64.2 seconds
Leap status     : Normal

핵심 항목 해석:

항목의미정상 범위
Reference ID현재 동기화 서버-
Stratum현재 계층 번호1~4
System timeNTP 기준 대비 로컬 오프셋수 ms 이내
Frequency시계 주파수 오차 (ppm)±100 ppm 이내
Leap status윤초 상태Normal

chronyc makestep — 즉시 시간 강제 보정:

로컬 터미널
# 큰 오프셋이 있을 때 즉시 시계를 맞춤 (서비스 재시작 없이)
sudo chronyc makestep

# 확인
chronyc tracking | grep "System time"

기타 유용한 chronyc 명령:

로컬 터미널
# 현재 NTP 소스 간단 요약
chronyc sourcestats

# chrony 서버 활동 통계
chronyc activity

# 특정 서버 정보 상세 확인
chronyc ntpdata 169.254.169.123

7. timedatectl로 시간대 및 NTP 관리

timedatectl의 대안: 로우레벨 시스템 타임존 수동 강제 매핑

timedatectl 도구가 유실되었거나 레거시 컨테이너 환경에서는 아래와 같이 타임존 zoneinfo 정보를 시스템 로컬타임 경로에 심볼릭 링크로 맵핑해야 합니다.

로컬 터미널
# 1. 기존 localtime 매핑 링크 제거
$ sudo rm -f /etc/localtime

# 2. zoneinfo DB에서 원하는 타임존(Asia/Seoul)을 /etc/localtime으로 심볼릭 링크 생성
$ sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

이러면 timedatectl 없이도 시스템 전체 트랜잭션 타임이 한국 표준시(KST)로 수동 강제 고정됩니다.

timedatectl 명령어 완전 가이드

timedatectl은 systemd가 제공하는 시간 관리 통합 도구입니다. 시간대 변경, NTP 활성화/비활성화, 현재 상태 확인을 한 도구에서 처리합니다.

현재 시간 설정 상태 확인:

로컬 터미널
timedatectl
               Local time: Thu 2026-03-26 10:23:45 KST
           Universal time: Thu 2026-03-26 01:23:45 UTC
                 RTC time: Thu 2026-03-26 01:23:45
                Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

항목 설명:

항목의미
Local time현재 시간대 기준 로컬 시각
Universal timeUTC 기준 시각
RTC time하드웨어(BIOS) 시계
Time zone현재 시간대
System clock synchronizedNTP 동기화 완료 여부
NTP serviceNTP 클라이언트 동작 여부
RTC in local TZHW 시계가 로컬 시간대 사용 여부 (no = UTC 권장)

Asia/Seoul 시간대 설정:

로컬 터미널
# 사용 가능한 시간대 목록 검색
timedatectl list-timezones | grep -i seoul
# 출력: Asia/Seoul

timedatectl list-timezones | grep -i asia | head -20

# 시간대 설정
sudo timedatectl set-timezone Asia/Seoul

# 확인
timedatectl | grep "Time zone"
# 출력: Time zone: Asia/Seoul (KST, +0900)

다른 시간대 예시:

로컬 터미널
# UTC (국제 표준)
sudo timedatectl set-timezone UTC

# 미국 동부
sudo timedatectl set-timezone America/New_York

# 일본 (한국과 같은 UTC+9, 하지만 JST)
sudo timedatectl set-timezone Asia/Tokyo

NTP 동기화 활성화/비활성화:

로컬 터미널
# NTP 동기화 활성화
sudo timedatectl set-ntp true

# NTP 동기화 비활성화 (수동 시간 설정 시 필요)
sudo timedatectl set-ntp false

# 상태 확인
timedatectl show | grep NTP

수동으로 시간 설정 (NTP 비활성화 상태에서만):

로컬 터미널
# NTP 먼저 비활성화
sudo timedatectl set-ntp false

# 날짜+시간 설정
sudo timedatectl set-time "2026-03-26 10:30:00"

# 날짜만 설정
sudo timedatectl set-time "2026-03-26"

# NTP 다시 활성화
sudo timedatectl set-ntp true

8. date 명령어와 hwclock

date 명령어와 hwclock로 시간 확인 및 동기화

datehwclock은 NTP 없이도 시간을 확인하고 조정할 수 있는 전통적인 도구입니다.

date 명령어 — 현재 시간 확인:

로컬 터미널
# 현재 시간 (기본: 로컬 시간대)
date
# 출력: Thu Mar 26 10:23:45 KST 2026

# UTC로 출력
date -u
# 출력: Thu Mar 26 01:23:45 UTC 2026

# 포맷 지정 출력
date "+%Y-%m-%d %H:%M:%S"
# 출력: 2026-03-26 10:23:45

# Unix timestamp 출력
date +%s
# 출력: 1774687425

# Unix timestamp를 날짜로 변환
date -d @1774687425

date 명령어 — 시간 수동 설정 (NTP 비활성화 상태에서):

로컬 터미널
# 날짜와 시간 설정 (MMDDHHmmYYYY.ss 형식)
sudo date 032610302026.00
# 2026년 3월 26일 10시 30분 00초

# 또는 문자열 형식 (GNU date)
sudo date -s "2026-03-26 10:30:00"
sudo date -s "Thu Mar 26 10:30:00 KST 2026"

hwclock — 하드웨어(RTC) 시계 관리:

하드웨어 시계(RTC, Real-Time Clock)는 서버 전원이 꺼져도 배터리로 계속 작동하는 BIOS 내장 시계입니다. 시스템 시계(커널이 관리)와 별개로 존재합니다.

로컬 터미널
# 하드웨어 시계 현재 값 읽기
sudo hwclock --show
# 출력: 2026-03-26 01:23:45.234567+00:00

# 시스템 시계 → 하드웨어 시계 동기화 (-w: write to hw)
sudo hwclock --systohc

# 하드웨어 시계 → 시스템 시계 동기화 (-s: set system)
sudo hwclock --hctosys

# 하드웨어 시계를 UTC 기준으로 설정 (서버 권장)
sudo hwclock --systohc --utc

# 하드웨어 시계를 로컬 시간 기준으로 설정 (비권장)
sudo hwclock --systohc --localtime

서버 권장 구성: HW 시계를 UTC로 유지

로컬 터미널
# /etc/adjtime 파일로 HW 시계 모드 확인
cat /etc/adjtime
# 마지막 줄이 UTC → 하드웨어 시계가 UTC 사용 중
# 마지막 줄이 LOCAL → 로컬 시간 사용 중 (Windows 듀얼부팅 환경)

부팅 시 시간 복원 순서:

1. BIOS/UEFI: HW 시계(RTC) 읽기
2. 커널: HW 시계 → 시스템 시계 설정
3. systemd: chronyd 시작
4. chronyd: NTP 서버와 동기화 → 시스템 시계 보정
5. chronyd: rtcsync 설정 시 HW 시계도 갱신
로컬 터미널
# chrony의 rtcsync 옵션으로 HW 시계 자동 갱신 확인
grep rtcsync /etc/chrony.conf
# 출력: rtcsync

9. 실무 활용: JobContext

💼
실무 맥락
현업 패턴

신규 서버 투입 체크리스트

새 서버를 프로덕션에 투입하기 전 시간 설정 확인은 기본 점검 항목입니다:

로컬 터미널
#!/bin/bash
# 신규 서버 시간 동기화 점검 스크립트

echo "=== 시간대 확인 ==="
timedatectl | grep "Time zone"

echo "=== NTP 서비스 상태 ==="
timedatectl | grep "NTP service"

echo "=== 동기화 상태 ==="
timedatectl | grep "synchronized"

echo "=== chrony 소스 ==="
chronyc sources 2>/dev/null || echo "chrony 미설치"

echo "=== 시스템 오프셋 ==="
chronyc tracking 2>/dev/null | grep "System time"

AWS EC2 AMI 빌드 시 chrony 설정 포함

EC2 인스턴스를 AMI로 만들 때 AWS Time Sync Service(169.254.169.123)를 최우선 타임서버로 설정하는 것이 AWS 공식 권장사항입니다. 이 링크-로컬 주소는 인터넷 접속 없이도 항상 사용 가능합니다.

Ansible로 다수 서버 시간 설정 배포:

YAML
# time-sync.yml
---
- name: NTP 시간 동기화 설정
  hosts: all
  become: true
  tasks:
    - name: chrony 설치
      package:
        name: chrony
        state: present

    - name: chrony.conf 배포
      template:
        src: chrony.conf.j2
        dest: /etc/chrony.conf
        backup: yes
      notify: restart chronyd

    - name: 시간대 설정
      community.general.timezone:
        name: Asia/Seoul

    - name: chronyd 활성화
      service:
        name: chronyd
        state: started
        enabled: true

  handlers:
    - name: restart chronyd
      service:
        name: chronyd
        state: restarted

Kubernetes Pod 시간 동기화

컨테이너(Pod)는 호스트 노드의 시스템 시계를 공유합니다. 별도의 NTP 클라이언트 불필요. 따라서 Kubernetes 노드의 chrony 설정이 중요합니다. 노드 시계가 틀리면 모든 Pod의 시각이 틀립니다.

Kubernetes
# 노드 시간 확인 (kubectl exec으로 파드 내부)
kubectl exec -it <pod-name> -- date
# 노드와 동일한 시각이 출력되어야 함

모니터링: Prometheus + Alertmanager로 시간 드리프트 경보

YAML
# Prometheus 알람 규칙 예시
groups:
  - name: time-sync
    rules:
      - alert: NTPNotSynchronized
        expr: node_timex_sync_status != 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} NTP 동기화 실패"

      - alert: ClockOffsetTooLarge
        expr: abs(node_timex_offset_seconds) > 0.1
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "시계 오프셋 100ms 초과: {{ $value }}s"

10. 트러블슈팅

증상:

로컬 터미널
chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^? time.bora.net                 0   8     0     -     +0ns[   +0ns] +/-    0ns
^? time.google.com               0   8     0     -     +0ns[   +0ns] +/-    0ns
^? 169.254.169.123               0   8     0     -     +0ns[   +0ns] +/-    0ns

모든 서버에 ? 표시, Reach0 — 연결이 전혀 안 됨.

원인 진단:

로컬 터미널
# NTP 서버에 UDP 123 포트 연결 테스트
# ntpdate로 단발성 쿼리 (진단 목적)
sudo ntpdate -q time.google.com 2>&1 | head -5

# tcpdump로 NTP 패킷 확인
sudo tcpdump -i any port 123 -n

# firewalld 규칙 확인 (RHEL 계열)
sudo firewall-cmd --list-all

# iptables 규칙 확인
sudo iptables -L OUTPUT -n | grep 123
sudo iptables -L INPUT -n | grep 123

# nmap으로 외부에서 포트 확인 (다른 서버에서 실행)
nmap -sU -p 123 <서버IP>

해결 방법:

로컬 터미널
# firewalld에서 NTP 허용 (RHEL/CentOS/Rocky)
sudo firewall-cmd --permanent --add-service=ntp
sudo firewall-cmd --reload

# 또는 포트 번호로 직접 허용
sudo firewall-cmd --permanent --add-port=123/udp
sudo firewall-cmd --reload

# ufw에서 허용 (Ubuntu)
sudo ufw allow 123/udp
sudo ufw reload

# iptables 직접 규칙 추가
sudo iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
sudo iptables -A INPUT -p udp --sport 123 -j ACCEPT

# 규칙 저장
sudo service iptables save  # RHEL
sudo iptables-save > /etc/iptables/rules.v4  # Debian/Ubuntu

# AWS EC2: Security Group에서 Outbound UDP 123 허용 확인
# AWS Management Console → EC2 → Security Groups → Outbound Rules
# 또는 AWS CLI:
aws ec2 describe-security-groups --group-ids sg-xxxxxxxx \
  --query 'SecurityGroups[].IpPermissionsEgress'

방화벽 해결 후 chrony 재시작:

서버 터미널
sudo systemctl restart chronyd
sleep 10
chronyc sources
# Reach 값이 점점 올라가야 함: 1, 3, 7, 17, 37, 77, 177, 377

증상:

서버를 재시작하거나 VM 마이그레이션 후 시계가 수 분씩 틀어집니다. chronyc tracking에서 오프셋이 수 초 이상으로 나타납니다.

로컬 터미널
chronyc tracking | grep "System time"
# System time     : 45.234567890 seconds slow of NTP time
# ← 45초나 느림!

원인 분석:

VM 환경에서 시간 드리프트가 발생하는 원인:

  1. CPU Steal Time: 하이퍼바이저가 CPU를 다른 VM과 공유하면서 VM의 타이머 인터럽트가 지연됨
  2. VM 일시 정지: 스냅샷, 라이브 마이그레이션, 절전 모드 후 복구 시 시계가 멈췄다 재개됨
  3. 시계 소스 불안정: VM 내부의 가상 시계(TSC)가 불안정

해결 방법:

로컬 터미널
# 1. chrony.conf에 makestep 설정 확인 및 조정
sudo grep makestep /etc/chrony.conf
# makestep 1.0 3  ← 1초 이상 오프셋이면 즉시 수정 (최초 3회)

# 큰 드리프트 허용을 위해 수정 (VM 환경 권장)
sudo sed -i 's/^makestep.*/makestep 1.0 -1/' /etc/chrony.conf
# -1: 횟수 제한 없이 항상 즉시 수정

# 2. 지금 당장 시계 강제 보정
sudo chronyc makestep
chronyc tracking | grep "System time"

# 3. VM 환경에서 빠른 폴링 설정 (chrony.conf 추가)
# maxpoll 6 → 최대 64초마다 폴링 (기본값보다 자주)
echo "maxpoll 6" | sudo tee -a /etc/chrony.conf
sudo systemctl restart chronyd

# 4. KVM/QEMU 게스트: 호스트 시계 사용 (가장 안정적)
# /etc/chrony.conf에 refclock 추가
echo "refclock PHC /dev/ptp0 poll 3 dpoll -2 offset 0" | sudo tee -a /etc/chrony.conf
sudo systemctl restart chronyd

# 5. 시스템 클럭 소스 확인
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
# kvm-clock → KVM 최적화 시계 (좋음)
# tsc → TSC (VM에서 불안정 가능)
# hpet → HPET (느리지만 안정)

# TSC를 사용 중이라면 kvm-clock으로 변경 (KVM 환경)
echo kvm-clock | sudo tee /sys/devices/system/clocksource/clocksource0/current_clocksource

Docker/컨테이너 환경 주의사항:

Docker
# 컨테이너는 호스트 커널 공유 → 컨테이너 내부에서 NTP 실행 불필요/불가
# 호스트 노드의 시계를 동기화해야 함

# 컨테이너 내부에서 시간 확인
docker exec my-container date
# 호스트와 동일해야 함

# 다르다면 호스트 chrony 상태 확인
chronyc tracking

증상:

Linux 서버를 Active Directory 도메인에 조인할 때 다음 오류 발생:

realm join example.com
# 오류:
# adcli: joining domain failed: kinit: Clock skew too great
# KRB5KRB_AP_ERR_SKEW: Clock skew too great while getting initial credentials

또는 이미 조인된 서버에서 SSH 로그인, sudo, samba 인증이 실패:

pam_krb5: TGT expired
kinit: Clock skew too great while renewing credentials

원인:

Kerberos 프로토콜 사양(RFC 4120)에 따라 클라이언트와 KDC(도메인 컨트롤러) 간 시계 차이가 5분(300초)을 초과하면 보안상 모든 인증을 거부합니다. 이는 재전송 공격(Replay Attack) 방지를 위한 필수 보안 설계입니다.

진단 절차:

로컬 터미널
# 1. 현재 서버 시각과 도메인 컨트롤러 시각 비교
date -u
# 현재 서버 UTC 시각

# 도메인 컨트롤러에 NTP 쿼리 (DC 주소로 변경)
ntpdate -q dc01.example.com 2>&1
# 출력에서 offset 값 확인
# offset 320.456789 sec  ← 5분 20초 차이, Kerberos 거부

# 또는 chronyc로 DC를 임시 타임서버로 테스트
chronyc -a 'server dc01.example.com iburst'
chronyc sources | grep dc01

# 2. 현재 오프셋 확인
chronyc tracking | grep "System time"

# 3. DC와 직접 시간 비교
ssh dc01.example.com "date -u"  # Windows DC라면 원격 PowerShell 사용
# 또는 smbclient로 DC 시각 확인
smbclient -N -L dc01.example.com 2>/dev/null | grep -i time

해결 방법:

로컬 터미널
# 방법 1: DC를 NTP 서버로 설정 (도메인 환경 표준)
sudo tee /etc/chrony.conf > /dev/null <<EOF
# 도메인 컨트롤러를 타임서버로 사용 (Kerberos 동기화 보장)
server dc01.example.com iburst prefer
server dc02.example.com iburst

# 백업: 공개 NTP
pool kr.pool.ntp.org iburst

driftfile /var/lib/chrony/drift
makestep 1.0 -1
rtcsync
EOF

sudo systemctl restart chronyd

# 방법 2: 즉시 강제 동기화 (큰 오프셋 수정)
sudo chronyc makestep

# 방법 3: ntpdate로 즉시 수동 동기화 (긴급 시)
sudo ntpdate dc01.example.com

# 동기화 후 도메인 조인 재시도
chronyc tracking | grep "System time"
# System time: 0.002345678 seconds fast of NTP time  ← OK

sudo realm join example.com -U Administrator

Windows DC의 NTP 설정 확인:

도메인 환경에서 시간 권위 체계: DC(PDC Emulator) → 외부 NTP → 도메인 멤버

POWERSHELL
# Windows DC에서 NTP 소스 확인
w32tm /query /source
w32tm /query /status

# PDC Emulator를 외부 NTP와 동기화 (Windows PowerShell)
w32tm /config /manualpeerlist:"time.google.com,0x8 time.bora.net,0x8" /syncfromflags:manual /reliable:yes /update
net stop w32tm && net start w32tm
w32tm /resync /force

예방: 도메인 환경 NTP 정책

  1. PDC Emulator: 외부 Stratum 1/2 서버와 동기화
  2. 다른 DC들: PDC Emulator와 동기화
  3. 도메인 멤버 서버: DC와 동기화 (w32tm /resync 또는 chrony)
  4. 비도메인 Linux: 공개 NTP pool 사용

11. 빠른 참조

필수 명령어 요약

로컬 터미널
# 현재 시간 상태 전체 확인
timedatectl

# 동기화 서버 목록 확인
chronyc sources -v

# 동기화 상세 상태 확인
chronyc tracking

# 시간대 변경 (한국)
sudo timedatectl set-timezone Asia/Seoul

# NTP 활성화
sudo timedatectl set-ntp true

# 즉시 시계 강제 보정
sudo chronyc makestep

# chrony 서비스 재시작
sudo systemctl restart chronyd

# HW 시계 → 시스템 시계 동기화
sudo hwclock --hctosys

# 시스템 시계 → HW 시계 저장
sudo hwclock --systohc

# 현재 시각 확인 (UTC)
date -u

# chrony 로그 확인
sudo journalctl -u chronyd -f

시간대(Timezone) 자주 쓰는 목록

로컬 터미널
timedatectl list-timezones | grep -E "Seoul|Tokyo|Shanghai|Singapore|UTC|London|New_York"
# Asia/Seoul       → KST (UTC+9)
# Asia/Tokyo       → JST (UTC+9)
# Asia/Shanghai    → CST (UTC+8)
# Asia/Singapore   → SGT (UTC+8)
# UTC              → UTC (UTC+0)
# Europe/London    → GMT/BST
# America/New_York → EST/EDT (UTC-5/-4)

chrony.conf 타임서버 빠른 선택 가이드

로컬 터미널
# 한국 서버 (권장)
server time.bora.net iburst        # KT (Stratum 1)
pool kr.pool.ntp.org iburst        # 한국 NTP 풀

# 글로벌 서버
server time.google.com iburst      # Google (Stratum 1)
server time.cloudflare.com iburst  # Cloudflare (Stratum 1)

# AWS EC2 전용
server 169.254.169.123 prefer iburst  # AWS Time Sync (링크-로컬)

# GCP 전용
server metadata.google.internal iburst  # GCP 메타데이터 서버

# Azure 전용
server time.windows.com iburst     # Microsoft Azure

동기화 확인 자동화 스크립트

로컬 터미널
#!/bin/bash
# /usr/local/bin/check-ntp-sync.sh

OFFSET=$(chronyc tracking 2>/dev/null | grep "System time" | awk '{print $4}')
THRESHOLD=1.0  # 1초 이상이면 경고

if [ -z "$OFFSET" ]; then
    echo "ERROR: chrony not running"
    exit 2
fi

# Python으로 부동소수점 비교
OVER=$(python3 -c "print(1 if abs(float('$OFFSET')) > $THRESHOLD else 0)" 2>/dev/null)

if [ "$OVER" = "1" ]; then
    echo "WARNING: NTP offset ${OFFSET}s exceeds ${THRESHOLD}s threshold"
    exit 1
else
    echo "OK: NTP offset ${OFFSET}s"
    exit 0
fi
로컬 터미널
chmod +x /usr/local/bin/check-ntp-sync.sh

# cron으로 5분마다 실행
echo "*/5 * * * * root /usr/local/bin/check-ntp-sync.sh >> /var/log/ntp-check.log 2>&1" \
  | sudo tee /etc/cron.d/ntp-check

정리

서버 시간 동기화는 인증, 보안, 로깅, 분산 시스템 모두에 영향을 미치는 인프라 기초입니다.

작업명령어
현재 상태 확인timedatectl
한국 시간대 설정timedatectl set-timezone Asia/Seoul
NTP 활성화timedatectl set-ntp true
동기화 서버 확인chronyc sources -v
오프셋 확인chronyc tracking
즉시 시계 보정chronyc makestep
설정 파일/etc/chrony.conf

다음 모듈에서는 OS 하드닝을 통해 SSH 설정, 계정 보안, fail2ban으로 브루트포스를 차단하는 리눅스 보안 체크리스트를 다룹니다.

지식 확인

퀴즈 — 5문제

Q1

`timedatectl status` 출력에서 'NTP synchronized: yes'가 표시되지 않을 때 가장 먼저 확인해야 할 명령어는?

Q2

분산 서비스에서 두 서버의 시간이 500밀리초 이상 벌어지기 시작했습니다. 이 상황에서 발생할 수 있는 실질적인 장애는?

Q3

`chronyc tracking` 명령어 출력에서 'System time' 항목이 나타내는 값은?

Q4

`hwclock --systohc` 명령어의 역할은?

Q5

서버의 타임존을 'Asia/Seoul'로 변경하는 올바른 명령어는?

0 / 5 답변

🧪 실습으로 확인하기

새 서버 인수인계 — 처음 30분

초급

낯선 Linux 서버를 인수받았을 때 OS, 서비스, 로그를 빠르게 파악하는 루틴을 직접 수행한다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

linux중급 · 55
[Linux] journalctl로 모든 커널/서비스 로그 검색 및 실시간 모니터링
Linux 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점