Operations 20 min read

chrony for K8s Time Sync: Deployment, Cluster Verification & Troubleshooting

This guide details deploying chrony for millisecond-level time synchronization in self-hosted Kubernetes clusters, covering server/client configuration, multi-layer validation (node, pod, monitoring), Prometheus/Grafana alerting, production best practices, and common failure troubleshooting with concrete commands and thresholds.

Cilantro Loves Linux
Cilantro Loves Linux
Cilantro Loves Linux
chrony for K8s Time Sync: Deployment, Cluster Verification & Troubleshooting

Introduction: K8s Time Synchronization Pain Points and chrony Value

In distributed Kubernetes clusters, time consistency is the hidden cornerstone for core business stability: certificate validity checks, distributed tracing (Jaeger/Zipkin) span correlation, log timestamp analysis, and distributed transaction (TCC/SAGA) sequencing all rely on millisecond-level node time sync. Once inter-node time drift exceeds 10ms, it can cause premature certificate expiration, log timeline chaos, and broken tracing spans.

Traditional NTP has clear limitations in K8s: first sync takes minutes, unsuitable for rapid node scaling; poor stability under high network latency; and higher CPU/memory footprint, conflicting with container lightweight requirements.

chrony , a modern NTP replacement, offers fast convergence (first sync <10s), low resource usage (memory <10MB), weak-network adaptation (dynamic poll intervals), and local-stratum fault tolerance (maintains intra-cluster sync when external sources fail) , making it the preferred choice for self-hosted K8s time synchronization in 2025.

Core Concepts: chrony Working Principles and Key Features

2.1 Core Components

chronyd : Background daemon handling time-source communication, offset calculation, and system clock adjustment; supports continuous background operation.

chronyc : Command-line management tool for querying sync status, modifying configuration, and forcing sync without service restart.

2.2 Work Modes

For K8s hierarchical architecture, chrony supports three core modes:

Client mode : K8s nodes (Master/Worker) act as clients, syncing time from intra-cluster chrony servers.

Server mode : Deploy 1-2 dedicated nodes as chrony servers, connecting to public time sources (Aliyun, Cloudflare) to provide unified time for cluster nodes.

Peer mode : Multiple chrony servers sync with each other, forming intra-cluster time-source redundancy to avoid single points of failure.

2.3 Key Features (K8s-Optimized)

Fast sync (iburst) : Client sends 8 burst requests at startup, shortening first sync time, fitting K8s dynamic scaling.

Local stratum : When external public sources are unavailable, server automatically uses local time as source (configure local stratum N, N=8-10 recommended), ensuring intra-cluster time consistency.

Native IPv6 support : Adapts to future IPv6 networks; public time sources can use IPv6 addresses (e.g., 2001:4860:4806:8::8).

Dynamic poll interval : Automatically adjusts sync interval based on network stability (default 4-1024s), reducing useless requests and resource consumption in weak networks.

Practical Deployment: chrony Server and K8s Node Clients

3.1 Pre-deployment Requirements

OS: CentOS Stream 9 / Ubuntu 22.04+ (2025 mainstream stable K8s node OS, kernel ≥5.15).

Privileges: root or sudo required; avoid containerized deployment (time sync needs system-level privileges, containerization easily causes drift).

Network: Open UDP port 123 (NTP default) between chrony servers and K8s nodes.

3.2 Server Deployment (Intra-cluster Unified Time Source)

Servers connect to public time sources and provide stable time for K8s nodes; deploy 2 nodes for redundancy.

Install chrony

# CentOS/RHEL (dnf replaces yum)
dnf install -y chrony
# Ubuntu/Debian
apt update && apt install -y chrony

Production-grade configuration (/etc/chrony.conf) Key config must include public sources, cluster CIDR authorization, local-stratum fallback, logging:

# 1. Upstream public time sources (pick 2-3 stable, iburst speeds first sync)
server ntp.aliyun.com iburst minpoll 4 maxpoll 6  # Aliyun, poll 8-64s
server time.cloudflare.com iburst minpoll 4 maxpoll 6  # Cloudflare, IPv6 support
server 2001:4860:4806:8::8 iburst minpoll 4 maxpoll 6  # Google IPv6 (redundancy)

# 2. Allow K8s node CIDRs (replace with actual cluster CIDRs, Pod CIDR + node CIDR)
allow 10.244.0.0/16  # K8s Pod CIDR
allow 192.168.1.0/24  # K8s node CIDR

# 3. Fallback to local time when external sources unavailable (stratum 8, below public 1-3)
local stratum 8

# 4. Logging (production needs persistence for troubleshooting)
logdir /var/log/chrony  # create beforehand (mkdir -p /var/log/chrony)
log measurements statistics tracking  # record measurements, stats, sync trajectory

# 5. Disable hardware clock adjustment (avoid conflict with other K8s time services)
noclockredir
# 6. Ignore client hardware clock adjustment requests
nochronyc

Start service and enable on boot

# Start service
systemctl start chronyd
# Enable on boot (ensure auto-recovery after node reboot)
systemctl enable --now chronyd
# Verify status (must show active (running))
systemctl status chronyd | grep "active (running)"

3.3 K8s Node Client Deployment

All K8s Master/Worker nodes act as chrony clients, syncing only from intra-cluster servers (avoiding public-source fluctuation drift).

Install chrony (same as server step 1).

Client configuration (/etc/chrony.conf) Simplified config pointing only to intra-cluster servers:

# 1. Sync only from intra-cluster chrony servers (replace with actual server IP/hostname, 2 for redundancy)
server chrony-server-01 iburst minpoll 3 maxpoll 5  # server1, poll 8-32s
server chrony-server-02 iburst minpoll 3 maxpoll 5  # server2, redundancy

# 2. Allow only localhost (127.0.0.1) via chronyc management
allow 127.0.0.1

# 3. Logging and hardware clock config
logdir /var/log/chrony
log measurements statistics
noclockredir

Start service and verify basic sync

systemctl enable --now chronyd
# Check time source connection status (S=sync success, ^*=current source)
chronyc sources -v

Cluster-Level Verification: Ensuring Full-Stack K8s Time Consistency

Deployment alone is insufficient; verify from node → pod → monitoring three layers to guarantee full-stack consistency.

4.1 Node-Level Verification

Validate K8s node time sync status; key metrics: sync status, offset, stratum.

View sync details

chronyc tracking
# Key output interpretation:
# - Leap status: Normal (no leap second issue)
# - Stratum: 9 (should be 1 higher than server; server=8 → client=9)
# - System time: 0.000123 seconds fast of NTP time (offset, recommend <10ms)
# - Last offset: +0.000045 seconds (last offset, smaller = more stable)

Verify system time vs hardware time consistency Node reboot may cause hardware clock (RTC) drift from system time; ensure both sync:

# View system time vs hardware time relationship
timedatectl show -p LocalRTC,TimeNTP,NTPSynchronized
# Normal output:
# LocalRTC=no (not using hardware time as system time)
# TimeNTP=yes (using NTP sync)
# NTPSynchronized=yes (sync successful)

4.2 K8s Pod-Level Verification

Pod time drift usually stems from containers not mounting node time config; verify pod-node time consistency.

Compare pod and node time

# 1. Get node time (format: YYYY-MM-DD HH:MM:SS)
NODE_TIME=$(date +"%Y-%m-%d %H:%M:%S.%N" | cut -c1-23)
# 2. Enter pod to view time (example nginx pod, replace with actual pod name)
POD_TIME=$(kubectl exec -it nginx-demo -- date +"%Y-%m-%d %H:%M:%S.%N" | cut -c1-23)
# 3. Output comparison (normal drift <100ms)
echo "Node time: $NODE_TIME"
echo "Pod time:  $POD_TIME"

Verify container runtime config (containerd example) Containers must mount node's /etc/localtime (timezone) and /etc/timezone (timezone config), otherwise time drift occurs:

# Check pod mount config (replace with actual pod name)
kubectl describe pod nginx-demo | grep -A3 "Volumes:" | grep "localtime\|timezone"
# Normal output should include:
# localtime:
#   Type:          HostPath (bare host directory volume)
#   Path:          /etc/localtime

4.3 Monitoring Integration: Prometheus + Grafana Alerting

Production clusters need real-time chrony monitoring to detect sync anomalies; integrate Prometheus+Grafana.

Enable chrony metrics endpoint Modify client/server /etc/chrony.conf , add metrics config:

# Enable metrics endpoint, listen on node IP (0.0.0.0 = all interfaces), port 8080
metrics port 8080 allow 10.244.0.0/16  # only allow cluster CIDR access

Restart chronyd to apply: systemctl restart chronyd Prometheus scrape configuration Add scrape job in prometheus.yml :

scrape_configs:
  - job_name: "chrony"
    static_configs:
      - targets: ["k8s-master-01:8080", "k8s-worker-01:8080"]  # all K8s nodes
    scrape_interval: 15s  # 15s scrape, balance real-time vs resource

Key monitoring metrics and alert thresholds

chrony_sync_status : sync status (1=normal, 0=abnormal). Normal: 1. Alert: 0.

chrony_offset_seconds : time offset (seconds). Normal: <0.01s (10ms). Alert: >0.05s (50ms).

chrony_stratum : time stratum. Normal: <12. Alert: >15.

Best Practices and Exception Fixes

5.1 Production Best Practices

Architecture redundancy : Deploy 2 chrony servers; clients configure both server addresses to avoid single point of failure; servers connect to different public sources (Aliyun + Cloudflare) for upstream stability.

Configuration optimization :

Client minpoll=3 (8s), maxpoll=5 (32s): shorten sync interval, adapt to K8s dynamic network.

Server local stratum=8: stratum must be lower than public sources (1-3) but higher than clients (9-10), ensuring correct priority.

Security hardening :

Firewall restriction: only allow cluster CIDR access to UDP 123 (NTP) and TCP 8080 (metrics):

# CentOS firewall config
firewall-cmd --add-rich-rule='rule family="ipv4" source address="10.244.0.0/16" port port="123" protocol="udp" accept' --permanent
firewall-cmd --add-rich-rule='rule family="ipv4" source address="10.244.0.0/16" port port="8080" protocol="tcp" accept' --permanent
firewall-cmd --reload

No public exposure: servers do not expose to internet, only serve intra-cluster nodes.

Regular maintenance : Weekly run chronyc sourcestats to check public source stability; if any source offset persistently grows (>50ms), replace with another source.

5.2 Common Exceptions and Fixes

chronyd fails to start

Troubleshoot: journalctl -u chronyd -e check logs; check /etc/chrony.conf syntax.

Fix: Correct config errors (invalid server address, missing logdir); create log directory: mkdir -p /var/log/chrony.

Client chronyc sources shows "?"

Troubleshoot: ping chrony-server-01 check connectivity; telnet chrony-server-01 123 verify port.

Fix: Repair inter-node network (e.g., Calico/Flannel network policy allow UDP 123); adjust firewall rules to open port.

Pod-node time drift >1s

Troubleshoot: Check if pod mounts /etc/localtime; inspect containerd runtime config.

Fix: Add mount in pod YAML:

volumes:
  - name: localtime
    hostPath:
      path: /etc/localtime
volumeMounts:
  - name: localtime
    mountPath: /etc/localtime

Restart containerd: systemctl restart containerd.

Server local mode not effective

Troubleshoot: chronyc tracking check if stratum matches configured value; check config for local keyword.

Fix: Ensure config has local stratum 8 uncommented; restart chronyd: systemctl restart chronyd; run chronyc makestep to force sync.

Summary

With fast sync, low resource usage, and strong fault tolerance, chrony perfectly fits self-hosted K8s cluster time synchronization needs, becoming the optimal solution for distributed architectures in 2025. Deployment core lies in: server redundancy for availability, clients precisely targeting internal sources, full-stack verification (node→pod→monitoring) ensuring consistency .

Future K8s cluster dynamism will further automate chrony management — e.g., via K8s Operator for dynamic config rollout and rolling updates, combined with cluster network policies to auto-optimize time source selection — ultimately achieving "deploy-and-work, self-healing anomalies" operational goals, further reducing distributed cluster time sync management overhead.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

operationsKubernetesprometheustroubleshootinggrafanaNTPtime synchronizationchrony
Cilantro Loves Linux
Written by

Cilantro Loves Linux

Focused on Linux system operations and maintenance technology exchange, regularly publishing in-depth technical articles covering core areas such as system architecture, performance optimization, and security hardening. Whether you are a beginner or a seasoned expert, you can find valuable resources to enhance your skills and solve problems here. Follow us to explore the mysteries of Linux operations!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.