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 statussystemctl status chronyd && chronyc trackingsudo apt-get install -y chrony # 또는 sudo yum install -y chronyexport TZ=Asia/Seoul 은 현재 셸 세션에만 임시 적용되므로, 영구 변경은 반드시 timedatectl set-timezone 을 사용할 것
시간 불일치가 만드는 장애 시나리오

서버 운영에서 시간 동기화는 '있으면 좋은 것'이 아니라 필수 인프라입니다. 시간이 틀렸을 때 발생하는 실제 장애 유형을 살펴보겠습니다.
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 Before와 Not 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 계층 구조 원리

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를 대체한 이유

새 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 클라이언트로 사용합니다.
| 항목 | chrony | ntpd |
|---|---|---|
| 개발 연도 | 1990s (현재도 활발히 개발) | 1980s (개발 축소) |
| 빠른 초기 동기화 | 매우 빠름 (수 초) | 느림 (수 분) |
| 불안정한 네트워크 | 잘 처리 | 취약 |
| VM / 컨테이너 환경 | 최적화 | 문제 많음 |
| 간헐적 인터넷 연결 | 지원 | 미지원 |
| 설정 파일 | /etc/chrony.conf | /etc/ntp.conf |
| 제어 도구 | chronyc | ntpq, ntpdc |
| 메모리 사용 | 적음 | 많음 |
| RHEL 8/9 기본 | O | X (제거됨) |
VM과 컨테이너 환경에서 chrony가 특히 중요한 이유:
가상 머신은 하이퍼바이저가 CPU 자원을 다른 VM과 나누어 사용하기 때문에 VM 내부 시계가 실제보다 느리게 흐릅니다. VM이 일시 정지(suspend/migrate)된 후 깨어나면 몇 초~수 분의 시계 드리프트가 발생합니다. chrony는 이런 큰 오프셋도 빠르게 보정할 수 있도록 설계되어 있습니다.
3-1. NTP 포트 표 & 데몬 1개 원칙
NTP 포트/프로토콜 표 — 방화벽 규칙 작성 기준

시간 동기화 트러블슈팅의 절반은 방화벽 문제입니다. 어떤 포트를 열어야 하는지 정확히 알아야 합니다.
| 서비스 | 프로토콜/포트 | 방향 | 설명 |
|---|---|---|---|
| NTP (chrony/ntpd) | UDP 123 | Outbound | 외부 NTP 서버와 동기화 (필수) |
| NTP 서버로 동작 | UDP 123 | Inbound | 내부 클라이언트에게 시간 제공 |
| chronyc 원격 관리 | UDP 323 | Inbound (선택) | chronyc로 원격에서 chrony 관리 |
| NTS-KE (키 교환) | TCP 4460 | Outbound (선택) | 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 충돌 방지

한 서버에서 NTP 데몬은 반드시 1개만 실행해야 합니다. 두 데몬이 동시에 UDP 123 포트를 소유하려 해서 충돌하거나, 서로 다른 서버를 참조해 시계가 엇갈릴 수 있습니다.
3가지 데몬 비교:
| 데몬 | 패키지 | 기본 탑재 배포판 | 권장 여부 |
|---|---|---|---|
| systemd-timesyncd | systemd 내장 | Ubuntu 18.04+, Debian 12+ | 단순 클라이언트 용도로 적합 |
| chrony | chrony | RHEL 8+, Ubuntu 20.04+ | 권장 — VM/클라우드 환경에 최적화 |
| ntpd | ntp | 구형 배포판 | 신규 설치 비권장 (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
이제 실습을 진행합니다.
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 타임서버 설정
chrony의 핵심 설정 파일은 /etc/chrony.conf입니다. 기본 설정을 확인하고 한국 환경에 맞게 수정합니다.
# 현재 설정 확인
sudo cat /etc/chrony.conf
기본 설정 파일 구조 (RHEL 계열):
# /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는 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 time | NTP 기준 대비 로컬 오프셋 | 수 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은 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 time | UTC 기준 시각 |
RTC time | 하드웨어(BIOS) 시계 |
Time zone | 현재 시간대 |
System clock synchronized | NTP 동기화 완료 여부 |
NTP service | NTP 클라이언트 동작 여부 |
RTC in local TZ | HW 시계가 로컬 시간대 사용 여부 (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은 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로 다수 서버 시간 설정 배포:
# 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의 시각이 틀립니다.
# 노드 시간 확인 (kubectl exec으로 파드 내부)
kubectl exec -it <pod-name> -- date
# 노드와 동일한 시각이 출력되어야 함
모니터링: Prometheus + Alertmanager로 시간 드리프트 경보
# 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
모든 서버에 ? 표시, Reach가 0 — 연결이 전혀 안 됨.
원인 진단:
# 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 환경에서 시간 드리프트가 발생하는 원인:
- CPU Steal Time: 하이퍼바이저가 CPU를 다른 VM과 공유하면서 VM의 타이머 인터럽트가 지연됨
- VM 일시 정지: 스냅샷, 라이브 마이그레이션, 절전 모드 후 복구 시 시계가 멈췄다 재개됨
- 시계 소스 불안정: 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/컨테이너 환경 주의사항:
# 컨테이너는 호스트 커널 공유 → 컨테이너 내부에서 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 → 도메인 멤버
# 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 정책
- PDC Emulator: 외부 Stratum 1/2 서버와 동기화
- 다른 DC들: PDC Emulator와 동기화
- 도메인 멤버 서버: DC와 동기화 (
w32tm /resync또는 chrony) - 비도메인 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으로 브루트포스를 차단하는 리눅스 보안 체크리스트를 다룹니다.