SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor

วิธีแก้ปัญหานาฬิกา VPS ไม่ตรงและเวลาคลาดเคลื่อน

สาเหตุที่นาฬิกา VPS เดินไม่ตรงเกิดจากการขาดการซิงค์เวลาผ่าน NTP เรียนรู้วิธีตรวจสอบสถานะด้วยคำสั่ง chronyc และ timedatectl เพื่อแก้ไขปัญหา 2FA ล็อกอินไม่ได้อย่างถาวร

เหตุใดนาฬิกาของ VPS จึงคลาดเคลื่อน

นาฬิกาของ VPS คลาดเคลื่อนเนื่องจากไม่มีกระบวนการปรับเทียบเวลา เคอร์เนลจะนับเวลาจากตัวนับฮาร์ดแวร์ซึ่งอาจเดินเร็วหรือช้ากว่าปกติเล็กน้อย และเมื่อไม่มีไคลเอนต์สำหรับซิงค์เวลาทำงานอยู่ ข้อผิดพลาดเล็กน้อยนั้นจะสะสมเพิ่มขึ้นทุกชั่วโมง ภายในเครื่องเสมือนยังมีสาเหตุประการที่สองคือ เกสต์ของคุณต้องใช้ CPU ร่วมกับเกสต์อื่น ดังนั้นช่วงเวลาที่เกสต์ไม่ถูกจัดตารางการทำงานจึงเป็นช่วงเวลาที่เกสต์ไม่สามารถนับเวลาได้

บนเกสต์ KVM ในปัจจุบัน ตัวนับเวลาเองมักไม่ใช่ปัญหาหลัก แหล่งข้อมูล kvm-clock แบบ paravirtual จะอ่านค่าที่โฮสต์เป็นผู้ดูแล ดังนั้นเกสต์ที่มีสถานะปกติจะติดตามเวลาของโฮสต์ได้อย่างใกล้ชิด นาฬิกาที่แสดงเวลาผิดปกติมักเกิดจากสาเหตุที่เรียบง่ายกว่านั้น เช่น ไม่มี daemon สำหรับซิงค์เวลาทำงานอยู่, มี daemon สองตัวทำงานทับซ้อนกัน หรือพอร์ต UDP 123 สำหรับขาออกถูกบล็อกไม่ให้ออกจากเครือข่ายของผู้ให้บริการ เกสต์จะรับการควบคุมเวลาจากโฮสต์หรือจาก NTP (network time protocol) ไม่ใช่จากออสซิลเลเตอร์ของตัวมันเอง

ผลกระทบที่แท้จริงเมื่อนาฬิกาของระบบไม่ตรง

  • รหัสผ่านชั้นที่สองแบบ TOTP (time-based one-time password) จะไม่ตรงกัน ทำให้คุณไม่สามารถเข้าสู่เซิร์ฟเวอร์ได้แม้ว่ารหัสผ่านและคีย์จะถูกต้องก็ตาม
  • ใบรับรองที่เพิ่งออกให้เมื่อนาทีที่แล้วจะถูกปฏิเสธ และ curl จะแสดงข้อความ SSL certificate problem: certificate is not yet valid
  • apt update จะปฏิเสธ repository พร้อมข้อความ E: Release file for ... is not valid yet (invalid for another 1d 2h 3min 4s)
  • งานที่ตั้งเวลาไว้จะทำงานผิดเวลา และนาฬิกาที่กระโดดอาจทำให้งานหนึ่งรันซ้ำสองครั้งในขณะที่อีกงานถูกข้ามไป
  • log จากเซิร์ฟเวอร์สองเครื่องไม่สามารถนำมาเรียงลำดับกันได้ ทำให้การสร้างลำดับเหตุการณ์ของปัญหาต้องอาศัยการคาดเดา

ค่าความคลาดเคลื่อนที่ยอมรับได้นั้นน้อยกว่าที่คนส่วนใหญ่คาดคิด ตัวเลขด้านล่างนี้คือค่าเริ่มต้นตามเอกสาร ไม่ใช่ค่าที่วัดจากการทดสอบ

ChartHow far the clock can be off before something breaks (documented defaults)
The data behind this chart
[
  {
    "label": "TLS certificate boundary",
    "tolerance_seconds": 0,
    "documented_in": "RFC 5280"
  },
  {
    "label": "chrony steps instead of slewing",
    "tolerance_seconds": 1,
    "documented_in": "Debian and Ubuntu chrony.conf"
  },
  {
    "label": "TOTP code, one time step",
    "tolerance_seconds": 30,
    "documented_in": "RFC 6238"
  },
  {
    "label": "Kerberos clock skew",
    "tolerance_seconds": 300,
    "documented_in": "MIT krb5 default"
  }
]

รหัส TOTP ถูกคำนวณจากตัวนับที่จะเพิ่มขึ้นทุกๆ 30 วินาที และระบบตรวจสอบส่วนใหญ่จะยอมรับความคลาดเคลื่อนได้หนึ่งช่วงเวลาในแต่ละฝั่ง ดังนั้นงบประมาณความคลาดเคลื่อนทั้งหมดจึงอยู่ที่ครึ่งนาทีในแต่ละทิศทาง Kerberos ยอมรับความคลาดเคลื่อนได้มากกว่ามาก โดยมีค่าเริ่มต้นอยู่ที่ 300 วินาที แต่ใบรับรองนั้นไม่มีความยืดหยุ่นเลย โดยจะถูกตรวจสอบเทียบกับช่วงเวลาที่กำหนดไว้โดยมีระยะเวลาผ่อนผันเพียง 0 วินาที ดังนั้นนาฬิกาที่เร็วไปเพียงหนึ่งวินาทีก็จะปฏิเสธใบรับรองที่ถูกต้องสมบูรณ์ทันที

นาฬิกาทั้งสามประเภทและสิ่งที่สำคัญจริง

System clock คือนาฬิกาที่สำคัญที่สุด มันคือ CLOCK_REALTIME ของเคอร์เนล ซึ่งนับจำนวนวินาทีนับจากวันที่ 1 มกราคม 1970 UTC โดยเก็บไว้ในหน่วยความจำและถูกอ่านโดยทุกกระบวนการที่ต้องการประทับเวลา ไม่ว่าจะเป็นบรรทัดใน log, การตรวจสอบ certificate, รหัส TOTP หรือเวลาแก้ไขไฟล์ ทั้งหมดล้วนอ้างอิงจากนาฬิกานี้ เมื่อมีคนกล่าวว่าเวลาของเซิร์ฟเวอร์ไม่ถูกต้อง นั่นหมายถึงนาฬิกานี้

Hardware clock หรือที่เรียกว่า RTC (real time clock) คือตัวนับแยกต่างหากที่ยังคงทำงานอยู่แม้ในขณะที่เครื่องปิดอยู่ ในเครื่องเซิร์ฟเวอร์จริง มันคือชิปที่มีแบตเตอรี่สำรอง ในขณะที่เครื่อง guest มันจะถูกจำลองโดย hypervisor ดังนั้นมันจึงเป็นเพียงสิ่งที่โฮสต์สร้างขึ้นมา Linux จะอ่านค่าจากมันเพียงครั้งเดียวตอนบูตเพื่อใช้เป็นค่าเริ่มต้น จากนั้นจะนับเวลาด้วยตัวเอง timedatectl จะแสดงค่านี้ในบรรทัด RTC time อย่าใช้บรรทัดนี้ในการ debug บน VPS เพราะมันบอกถึงเวลาในมุมมองของโฮสต์ ไม่ใช่สถานะการซิงค์ของ system clock ของคุณ ส่วนใน container มักจะไม่มี /dev/rtc อยู่เลย ดังนั้น hwclock --show จึงล้มเหลวด้วยข้อผิดพลาด hwclock: Cannot access the Hardware Clock via any known method.

Clocksource คือสิ่งที่เคอร์เนลใช้ในการนับเวลาระหว่างการอ่านค่าแต่ละครั้ง คุณสามารถตรวจสอบได้ว่าเคอร์เนลของคุณเลือกใช้อันไหน:

cat /sys/devices/system/clocksource/clocksource0/current_clocksource
cat /sys/devices/system/clocksource/clocksource0/available_clocksource

บน KVM คุณมักจะเห็น kvm-clock ซึ่งจะอ่านค่าที่โฮสต์เป็นผู้ดูแล นี่คือเหตุผลว่าทำไม KVM guest ที่ไม่มี NTP client เลยยังคงรักษาเวลาได้ค่อนข้างแม่นยำในระยะเวลาหนึ่ง tsc คือตัวนับของ CPU เอง ส่วน Xen guest จะรายงานเป็น xen และ Hyper-V guest จะรายงานเป็นแหล่งข้อมูล hyperv อย่าแก้ไขการตั้งค่านี้เว้นแต่คุณจะมีเหตุผลที่วัดผลได้ เพราะเคอร์เนลได้เลือกแหล่งข้อมูลที่ดีที่สุดที่เชื่อถือได้สำหรับฮาร์ดแวร์นั้นไว้แล้ว

โฮสต์บางแห่งอาจส่งอุปกรณ์ PTP (precision time protocol) มาให้ guest ซึ่งช่วยให้ chrony สามารถอ่านเวลาจากโฮสต์ได้โดยตรงแทนที่จะผ่านเครือข่าย ควรตรวจสอบดู ซึ่งมักจะไม่สามารถใช้งานได้บน VPS แบบแชร์:

sudo modprobe ptp_kvm
ls /dev/ptp*
cat /sys/class/ptp/ptp0/clock_name

หาก modprobe ล้มเหลวหรือไม่พบอุปกรณ์ แสดงว่าโฮสต์ของคุณไม่มีบริการนี้ และการใช้ NTP ผ่านเครือข่ายคือคำตอบของคุณ หาก clock_name ระบุว่าเป็น KVM virtual clock คุณสามารถให้ chrony ใช้งานได้ด้วยบรรทัด refclock PHC /dev/ptp0 poll 2 ในไฟล์ config ของมัน

อ่านสถานะเวลาบนเครื่องของคุณ

เริ่มต้นด้วยคำสั่งเดียว คำสั่งนี้จะตอบคำถามว่า "มีสิ่งใดคอยปรับเวลาให้ถูกต้องอยู่หรือไม่" ได้ภายในหน้าจอเดียว

timedatectl

ให้อ่านบรรทัดเหล่านี้แทนการเชื่อตัวเลขที่คุณจำได้:

  • Local time และ Universal time คือเวลาเดียวกันที่แสดงในโซนเวลาของคุณและในรูปแบบ UTC หากทั้งสองค่าเหมือนกัน แสดงว่าเครื่องตั้งค่าเป็น UTC อยู่แล้ว
  • RTC time คือนาฬิกาฮาร์ดแวร์ตามที่อธิบายไว้ข้างต้น สำหรับ VPS ให้ละเว้นค่านี้
  • Time zone คือสิ่งที่ระบบใช้ในการจัดรูปแบบเวลาท้องถิ่น
  • System clock synchronized คือแฟล็กของเคอร์เนลเอง โดย daemon ด้านเวลาจะตั้งค่านี้เมื่อมันเชื่อถือแหล่งข้อมูลของมันแล้ว ดังนั้น no หมายความว่าไม่มีสิ่งใดเข้ามาควบคุมนาฬิกานี้เลยตั้งแต่บูตเครื่อง
  • NTP service รายงานสถานะของ systemd-timesyncd โดยเฉพาะ n/a เป็นเรื่องปกติบนเครื่องที่รัน chrony เนื่องจากไม่ได้ติดตั้ง timesyncd ไว้ System clock synchronized: yes ร่วมกับ NTP service: n/a หมายความว่า chrony กำลังทำงานอยู่และเคอร์เนลยอมรับค่าจากมัน

ถัดไป ให้ตรวจสอบว่าเวลาของคุณคลาดเคลื่อนไปเท่าใด อย่ากะประมาณด้วยสายตาเทียบกับโทรศัพท์ของคุณ หาก chrony กำลังทำงานอยู่:

chronyc tracking
chronyc sources -v

chronyc tracking จะแสดงตัวเลขที่ตอบคำถามนี้ System time คือค่าความคลาดเคลื่อนปัจจุบันจากเวลา NTP ตามด้วยคำว่า fast หรือ slow ส่วน Last offset คือขนาดของการแก้ไขล่าสุด Frequency คือข้อผิดพลาดของอัตราที่ chrony วัดได้ในนาฬิกาของคุณและกำลังชดเชยให้อยู่ Leap status ควรแสดงค่าเป็น Normal หากแสดงเป็น Not synchronised และ Reference ID เป็น 00000000 () แสดงว่า chrony ยังไม่ได้เลือกแหล่งข้อมูลที่แน่นอน

chronyc sources -v จะพิมพ์คำอธิบายสัญลักษณ์ไว้เหนือรายการ เพื่อให้คุณไม่ต้องจำสัญลักษณ์เหล่านั้น โดยสองคอลัมน์จะสื่อความหมายหลัก ตัวอักษรสถานะที่จุดเริ่มต้นของแต่ละบรรทัดคือวิธีที่ chrony ประเมินแหล่งข้อมูลนั้น โดย * ระบุแหล่งข้อมูลที่กำลังใช้งานอยู่ และ ? ในทุกบรรทัดหมายความว่าไม่มีการตอบกลับ Reach คือประวัติการตอบกลับของการสำรวจ 8 ครั้งล่าสุดในรูปแบบเลขฐานแปด: 377 หมายความว่ามีการตอบกลับครบทั้ง 8 ครั้ง ส่วน 0 หมายความว่าไม่มีการตอบกลับเลย

หาก systemd-timesyncd เป็นผู้ดูแลแทน:

timedatectl timesync-status
timedatectl show-timesync --all

timesync-status จะแสดงเซิร์ฟเวอร์ที่กำลังติดต่ออยู่ ช่วงเวลาการสำรวจ และค่า Offset หากคำสั่งส่งคืนข้อผิดพลาดเกี่ยวกับ service แทนที่จะแสดงสถานะ แสดงว่า timesyncd ไม่ใช่ daemon ที่ดูแลเครื่องนี้ ซึ่งนั่นก็เป็นคำตอบสำหรับคำถามของคุณแล้ว

สำหรับการตรวจสอบคร่าวๆ กับโลกภายนอกโดยไม่ต้องใช้เครื่องมือเพิ่มเติม ให้เปรียบเทียบนาฬิกาของคุณกับ HTTP Date header สาธารณะ ซึ่งจะแสดงในรูปแบบ GMT ที่มีความละเอียดระดับหนึ่งวินาที:

date -u
curl -sI https://www.cloudflare.com | grep -i '^date:'

ความแตกต่างระดับหนึ่งหรือสองวินาทีถือเป็นเรื่องปกติและไม่มีนัยสำคัญ แต่หากแตกต่างกันถึงหนึ่งนาที แสดงว่าเกิดข้อผิดพลาดขึ้นแล้ว

chrony หรือ systemd-timesyncd บน VPS

Ubuntu และ Debian ติดตั้ง systemd-timesyncd มาให้เป็นค่าเริ่มต้น ซึ่งเป็นไคลเอนต์ SNTP (simple network time protocol) ที่จะสอบถามเซิร์ฟเวอร์ทีละแห่งและปรับเวลาให้ตรงตามนั้น สำหรับเครื่องที่ออนไลน์อยู่ตลอดและมีเวลาเริ่มต้นที่คลาดเคลื่อนไม่มากนัก วิธีนี้เพียงพอและใช้ทรัพยากรน้อยมาก

chrony เป็นการใช้งาน NTP แบบเต็มรูปแบบและเป็นตัวเลือกเริ่มต้นที่ดีกว่าสำหรับเครื่องเสมือน (virtual machine) โดยดูได้จากผลลัพธ์การทำงานของมัน chrony จะสอบถามแหล่งเวลาหลายแห่งพร้อมกันและคัดแหล่งที่ให้ข้อมูลไม่ตรงกันออก นอกจากนี้ยังวัดอัตราความคลาดเคลื่อนของนาฬิกาและบันทึกลงใน drift file เพื่อแก้ไขแนวโน้มความคลาดเคลื่อนแทนที่จะไล่ปรับตามค่าที่ได้รับมาทีละครั้ง chrony ยังฟื้นตัวได้อย่างรวดเร็วจากสองสถานการณ์ที่ VM มักพบแต่เครื่องจริงไม่พบ นั่นคือการถูกหยุดการทำงาน (pause) โดยโฮสต์ และการถูกย้ายไปยังโฮสต์อื่นขณะที่ยังทำงานอยู่ และเมื่อมีการจัดเตรียมอุปกรณ์ PTP จากโฮสต์ chrony ก็เป็นซอฟต์แวร์ที่ทำหน้าที่อ่านค่าดังกล่าว

sudo apt update && sudo apt install -y chrony
systemctl status chrony --no-pager
chronyc sources -v
chronyc tracking

ให้สังเกตผลลัพธ์ของ apt ขณะติดตั้ง บน Debian และ Ubuntu ทั้งแพ็กเกจ chrony และ systemd-timesyncd ต่างให้ time-daemon ดังนั้น apt จะลบ timesyncd ออกเมื่อติดตั้ง chrony ซึ่งเป็นสิ่งที่ถูกต้องและควรจะเป็น ห้ามรันทั้งสองตัวพร้อมกันเนื่องจาก daemon สองตัวที่พยายามตั้งค่านาฬิกาเดียวกันจะขัดแย้งกันเอง และค่า offset ที่รายงานออกมาจะไม่สามารถเชื่อถือได้ในระหว่างนั้น สำหรับ Rocky และ AlmaLinux ให้ติดตั้งด้วย sudo dnf install -y chrony โดย unit จะใช้ชื่อว่า chronyd แทนที่จะเป็น chrony

ไฟล์คอนฟิกคือ /etc/chrony/chrony.conf บน Debian และ Ubuntu และ /etc/chrony.conf บน Rocky และ Alma ค่าเริ่มต้นที่มากับ distribution นั้นเหมาะสมสำหรับ VPS อยู่แล้ว จึงควรแก้ไขก็ต่อเมื่อมีเหตุผลจำเป็นเท่านั้น มีคำสั่งสองประเภทที่ควรทำความเข้าใจ:

  • บรรทัด pool และ server ใช้ระบุแหล่งเวลา การเพิ่ม iburst จะสั่งให้ chrony ส่งข้อมูลแบบ burst อย่างรวดเร็วเมื่อเริ่มทำงาน เพื่อให้การซิงค์ครั้งแรกเกิดขึ้นภายในไม่กี่วินาทีแทนที่จะเป็นนาที
  • makestep เป็นตัวกำหนดว่าเมื่อใดที่ chrony จะใช้วิธีปรับเวลาแบบกระโดด (jump) แทนการค่อยๆ ปรับ (ease) ให้ตรวจสอบค่าของคุณด้วย grep -n makestep /etc/chrony/chrony.conf ค่าเริ่มต้นของ Debian และ Ubuntu คือ makestep 1 3 ซึ่งหมายความว่า: ในช่วงการอัปเดตสามครั้งแรกหลังจาก chronyd เริ่มทำงาน ให้ปรับเวลาแบบกระโดดหากเวลาคลาดเคลื่อนเกินหนึ่งวินาที และหลังจากนั้นให้ใช้วิธีค่อยๆ ปรับเวลาเท่านั้น

หากคุณต้องการให้ทราฟฟิกของเวลาได้รับการตรวจสอบความถูกต้องเพื่อป้องกันการแก้ไขระหว่างทาง chrony เวอร์ชัน 4 ขึ้นไปรองรับ NTS (network time security) ให้ยืนยันเวอร์ชันของคุณด้วย chronyd -v ก่อน และโปรดทราบว่า NTS จำเป็นต้องเปิดพอร์ต TCP 4460 ขาออก นอกเหนือจาก UDP 123:

server time.cloudflare.com iburst nts

ให้รีสตาร์ทและตรวจสอบความถูกต้องก่อนเริ่มใช้งานจริง หากคอนฟิกมีข้อผิดพลาดจนไม่สามารถอ่านค่าได้ คุณจะไม่มี time daemon ทำงานอยู่เลย และนาฬิกาจะไม่แจ้งเตือนคุณเกี่ยวกับเรื่องนี้

sudo systemctl restart chrony
chronyc sources -v
chronyc tracking

เหตุใดนาฬิกาที่เวลาคลาดเคลื่อนหลายนาทีจึงยังคงไม่ตรง

time daemon มีสองวิธีในการแก้ไขค่าเวลาที่คลาดเคลื่อน วิธีการ slewing จะทำการเร่งหรือหน่วงเวลาของนาฬิกาจนกว่าค่าความคลาดเคลื่อนจะหมดไป ซึ่งช่วยให้เวลาเดินไปข้างหน้าอย่างต่อเนื่องโดยไม่มีการซ้ำหรือข้าม timestamp ส่วนวิธีการ stepping จะเป็นการปรับเวลาให้ตรงทันที ซึ่งรวดเร็วแต่อาจทำให้เวลาถอยหลังได้ การที่เวลาถอยหลังเป็นอันตรายต่อกระบวนการใดก็ตามที่วัดระยะเวลาจาก wall clock ดังนั้น daemon ทั้งสองประเภทจึงนิยมใช้วิธี slewing มากกว่า

ความนิยมในวิธีนี้เป็นเหตุผลว่าทำไมนาฬิกาที่เวลาผิดพลาดมากจึงอาจไม่ตรงอยู่เป็นเวลานาน chrony จะทำการ step เวลาเฉพาะภายในช่วงที่ makestep อนุญาตเท่านั้น ซึ่งโดยปกติจะเป็นช่วงการอัปเดตไม่กี่ครั้งแรกหลังจาก daemon เริ่มทำงาน หาก chronyd ทำงานมาแล้วหนึ่งสัปดาห์แล้วพบว่าเวลาคลาดเคลื่อนไป 40 วินาที มันจะใช้วิธี slewing ซึ่งการ slewing เวลา 40 วินาทีนั้นใช้เวลานานกว่าที่คุณต้องการรอ คุณควรบังคับให้มันปรับเวลาหนึ่งครั้งด้วยความตั้งใจในช่วงเวลาที่มีการใช้งานน้อย:

sudo chronyc makestep
chronyc tracking

ขณะนี้ chronyc tracking ควรรายงานค่า System time offset ที่ใกล้ศูนย์ และ Last offset ควรแสดงขนาดของเวลาที่เพิ่งได้รับการแก้ไขไป โปรดพิจารณาให้รอบคอบก่อนดำเนินการนี้บนโฮสต์ฐานข้อมูลที่มีการใช้งานสูง เพราะนาฬิกาที่กระโดดถอยหลังอาจทำให้ซอฟต์แวร์ที่ตั้งสมมติฐานว่าเวลาเดินไปข้างหน้าเพียงอย่างเดียวเกิดความสับสนได้ การรีสตาร์ท daemon เป็นวิธีแก้ไขที่นุ่มนวลกว่า เนื่องจากช่วงเวลา makestep จะเปิดให้ใช้งานอีกครั้งเมื่อเริ่มต้นระบบใหม่

คอนเทนเนอร์ใช้เวลาจากนาฬิกาของโฮสต์ร่วมกัน

คอนเทนเนอร์ไม่มีนาฬิกาของตนเอง จึงไม่มีสิ่งใดให้ซิงค์ภายในนั้น Linux time namespaces ทำหน้าที่จำลองเฉพาะ monotonic clock และ boot-time clock เท่านั้น ส่วน CLOCK_REALTIME ไม่ได้ถูกจำลองขึ้นมา ซึ่งหมายความว่าคอนเทนเนอร์จะอ่านค่าจากนาฬิกาของระบบชุดเดียวกับโฮสต์ที่มันทำงานอยู่ การแก้ไขเวลาบนโฮสต์จะส่งผลให้ทุกคอนเทนเนอร์บนโฮสต์นั้นได้รับการแก้ไขเวลาไปพร้อมกันในทันที

ผลที่ตามมามีอยู่สองสามประการ ห้ามติดตั้ง chrony หรือ ntpd ลงในอิมเมจ เพราะอย่างดีที่สุดมันก็ไม่ทำงาน การตั้งค่าวันที่ภายในคอนเทนเนอร์ที่ไม่มีสิทธิ์พิเศษ (unprivileged container) จะล้มเหลวด้วย date: cannot set date: Operation not permitted เนื่องจากเคอร์เนลต้องการสิทธิ์ CAP_SYS_TIME สำหรับการเรียกใช้งานดังกล่าว การให้สิทธิ์ CAP_SYS_TIME ไม่ได้ทำให้คอนเทนเนอร์มีนาฬิกาแยกส่วนตัว แต่เป็นการอนุญาตให้คอนเทนเนอร์เปลี่ยนเวลาของโฮสต์ ซึ่งจะส่งผลกระทบต่อเวลาของคอนเทนเนอร์อื่นทั้งหมดด้วย

การตั้งค่าเขตเวลา (time zone) ภายในคอนเทนเนอร์ที่แตกต่างกันไม่ใช่ปัญหาเรื่องนาฬิกา อิมเมจที่มีไฟล์ /etc/localtime ของตนเองจะแสดงเวลาขณะเดียวกันในรูปแบบของเขตเวลาอื่น ดังนั้น date จึงดูเหมือนผิดทั้งที่นาฬิกายังคงถูกต้อง ให้ตั้งค่า TZ=UTC ในสภาพแวดล้อมของคอนเทนเนอร์แล้วความสับสนจะหมดไป รันไทม์ที่คุณเลือกไม่ได้เปลี่ยนแปลงสิ่งนี้ และ การเปรียบเทียบ Podman และ Docker แบบ rootless ได้ครอบคลุมถึงสิ่งที่รันไทม์เหล่านั้นเปลี่ยนแปลงจริงไว้แล้ว

เขตเวลา: UTC บนเซิร์ฟเวอร์ เวลาท้องถิ่นสำหรับผู้ใช้งาน

ตั้งค่าเครื่องให้เป็น UTC และคงไว้เช่นนั้น

timedatectl list-timezones | grep -i utc
sudo timedatectl set-timezone UTC
timedatectl

UTC ไม่มีการปรับเวลาตามฤดูกาล (daylight saving) และนั่นคือเหตุผลสำคัญทั้งหมด งานที่ตั้งเวลาไว้ตอน 02:30 ในเขตเวลาที่มีการปรับเวลาตามฤดูกาลจะทำงานสองครั้งในวันที่นาฬิกาถอยหลัง และจะไม่ทำงานเลยในวันที่นาฬิกาเดินหน้า man 8 cron ได้ระบุถึงการจัดการพิเศษสำหรับการเลื่อนเวลาที่น้อยกว่าสามชั่วโมงไว้ว่า: งานที่ถูกข้ามไปจากการเลื่อนเวลาไปข้างหน้าจะถูกเรียกทำงานทันทีหลังจากมีการเปลี่ยนแปลง และงานที่อยู่ในช่วงเวลาที่ซ้ำกันจากการเลื่อนเวลาถอยหลังจะไม่ถูกเรียกทำงานซ้ำเป็นครั้งที่สอง พฤติกรรมดังกล่าวถือว่าสมเหตุสมผล แต่ก็ยังเป็นพฤติกรรมที่คุณไม่ควรต้องมานั่งกังวลในช่วงเวลา 03:00 น. ภายใต้ UTC งานจะทำงานวันละหนึ่งครั้ง ทุกวันตลอดทั้งปี หากงานของคุณหายไปเลยแทนที่จะทำงานในเวลาที่ผิดปกติ สาเหตุที่ cron job ไม่ทำงานโดยไม่มีการแจ้งเตือน คือคำอธิบายที่เป็นไปได้มากกว่า

เหตุผลเดียวกันนี้ใช้กับการอ่าน log journalctl จะจัดรูปแบบ timestamp ตามเขตเวลาของระบบ และ journalctl --utc จะบังคับให้ใช้ UTC เซิร์ฟเวอร์สองเครื่องในสองเขตเวลาจะทำให้ทุกเหตุการณ์กลายเป็นการแปลงเวลา และการแปลงเวลาภายใต้ความกดดันคือสาเหตุที่ทำให้คนอ่านลำดับเวลาผิดพลาด ให้คงระบบไว้ที่ UTC จัดเก็บ timestamp ในรูปแบบ UTC และแปลงเวลาเพียงครั้งเดียวที่จุดที่มนุษย์อ่านข้อมูล หากใครต้องการอ่านเวลาท้องถิ่นสำหรับคำสั่งใดคำสั่งหนึ่ง ก็สามารถเรียกดูได้โดยไม่ต้องเปลี่ยนเวลาของเครื่อง:

TZ=Europe/Berlin date

อีกหนึ่งบรรทัดในผลลัพธ์ของ timedatectl ที่เกี่ยวข้องกับส่วนนี้ RTC in local TZ ควรแสดงเป็น no การตั้งค่าเป็น yes เป็นเพียงวิธีแก้ปัญหาชั่วคราวสำหรับการทำ dual-boot Windows บนแล็ปท็อป แต่บนเซิร์ฟเวอร์มันจะเพิ่มค่า offset ที่อาจทำให้เกิดปัญหาในภายหลัง เมื่อตั้งค่านี้ไว้ timedatectl จะแสดงคำเตือนว่าระบบถูกกำหนดค่าให้อ่านเวลา RTC ในเขตเวลาท้องถิ่น

การแก้ไขปัญหาตามอาการ

รหัสยืนยันตัวตนสองชั้น (2FA) ถูกปฏิเสธบนเซิร์ฟเวอร์ ให้ตรวจสอบเวลาของระบบก่อนสิ่งอื่นใด รหัสนี้มาจากตัวนับที่เปลี่ยนทุก 30 วินาที หากเซิร์ฟเวอร์เวลาช้าไป 90 วินาที เซิร์ฟเวอร์จะคำนวณรหัสจากขั้นตอนที่โทรศัพท์ของคุณผ่านไปแล้ว timedatectl จะแสดง System clock synchronized: no หรือ chronyc tracking จะรายงานค่า System time offset ที่สูง ความผิดพลาดนี้ต่างจากการที่คีย์ถูกปฏิเสธโดยตรง ซึ่งจะมีข้อความแจ้งเตือนเฉพาะและอธิบายไว้ใน คู่มือการแก้ไขปัญหาการยืนยันตัวตนด้วย publickey

apt update แจ้งว่าไฟล์ Release ยังไม่ถูกต้อง ข้อความเต็มจะระบุชื่อ repository และระยะเวลาที่ไฟล์จะยังไม่ถูกต้อง เช่น is not valid yet (invalid for another 1d 2h 3min 4s) แสดงว่าเวลาในระบบของคุณช้ากว่าวันที่ระบุในไฟล์ Release ของ repository นั้น และระยะเวลาที่แจ้งคือค่าความต่างของเวลาที่ชัดเจน ให้แก้ไขเวลาของระบบ อย่าปิดการตรวจสอบวันที่ของ apt เพื่อข้ามปัญหานี้ เพราะการตรวจสอบดังกล่าวช่วยป้องกันไม่ให้ผู้อื่นส่งดัชนีแพ็กเกจที่ล้าสมัยมาให้คุณ

ทุกบรรทัดของ source แสดงสถานะ unreachable และ Reach เป็น 0 ไม่มีสิ่งใดตอบสนอง ให้ตรวจสอบการเชื่อมต่อขาออก (egress) แทนการตรวจสอบ config ของคุณ NTP ใช้พอร์ต UDP 123 ขาออก ซึ่งบางเครือข่ายอาจมีการกรองหรือเปลี่ยนเส้นทาง sudo chronyc ntpdata จะแสดงตัวนับแยกตามแหล่งที่มา รวมถึง Total TX และ Total RX หากค่า TX เพิ่มขึ้นในขณะที่ RX ยังคงเป็นศูนย์ หมายความว่าแพ็กเกจของคุณถูกส่งออกไปแต่ไม่มีการตอบกลับ ซึ่งบ่งชี้ว่ามีไฟร์วอลล์กั้นอยู่ระหว่างคุณกับแหล่งที่มา

เวลาเคยถูกต้องแต่เกิดการกระโดด เหตุการณ์บนโฮสต์อาจทำให้เกิดปัญหานี้ได้ เช่น การกู้คืน snapshot, การหยุดพัก guest ชั่วคราว หรือการย้าย guest ไปยังโฮสต์อื่น (live migration) อาจทำให้เวลาของ guest ล้าหลังกว่าเวลาจริง chrony จะตรวจพบในการตรวจสอบครั้งถัดไปและแก้ไขให้ถูกต้อง ส่วน systemd-timesyncd อาจรอจนกว่าจะครบช่วงเวลาการตรวจสอบที่ยาวนาน ให้ยืนยันว่า daemon เริ่มทำงานตอนบูตด้วย systemctl is-enabled chrony เพราะ daemon ที่เริ่มด้วยมือจะหายไปหลังการรีบูตครั้งถัดไป

ค่า offset มีขนาดเล็กแต่ไม่เคยนิ่ง ให้ตรวจสอบ CPU steal หาก guest ไม่ได้รับการจัดสรรเวลาประมวลผลเมื่อถึงเวลาขัดจังหวะของตัวจับเวลา (timer interrupt) การเก็บตัวอย่างเวลาจะล่าช้า ทำให้ค่า offset แกว่งไปมาแทนที่จะคงที่ top จะแสดงค่านี้เป็นตัวเลข st ในบรรทัด CPU การอ่านค่า CPU steal time บนโฮสต์ที่ใช้งานร่วมกัน อธิบายความหมายของตัวเลขนี้และสิ่งที่คุณสามารถทำได้

ใบรับรอง (certificate) ที่เพิ่งออกให้ถูกปฏิเสธว่ายังไม่ถึงเวลาใช้งาน curl จะพิมพ์ SSL certificate problem: certificate is not yet valid และเบราว์เซอร์จะแจ้งเตือนในลักษณะเดียวกัน ใบรับรองนั้นถูกต้อง แต่เวลาของเครื่องที่ตรวจสอบนั้นช้าเกินไป เครื่องใดเครื่องหนึ่งอาจเป็นสาเหตุ ให้ตรวจสอบทั้งฝั่งไคลเอนต์และเซิร์ฟเวอร์ หากเซิร์ฟเวอร์ที่ออกใบรับรองมีเวลาไม่ถูกต้อง คู่มือการใช้ certbot และใบรับรอง nginx จะครอบคลุมถึงด้านการต่ออายุในสถานการณ์เดียวกันนี้

เพิ่มรายการนี้เข้าไปในการตรวจสอบที่คุณทำอยู่เป็นประจำ

การตั้งค่าเวลาให้ตรงกัน (Time sync) เป็นการตั้งค่าตอนบูตระบบที่อาจล้มเหลวโดยไม่มีการแจ้งเตือนหลังจากผ่านไปหลายเดือน ซึ่งเป็นสิ่งที่การตรวจสอบตามรอบจะช่วยตรวจพบได้ แต่ความจำของคุณอาจมองข้ามไป timedatectl และ chronyc tracking ใช้เวลาอ่านผลลัพธ์เพียงสองวินาทีเท่านั้น ให้รันคำสั่งเหล่านี้เป็นส่วนหนึ่งของ สิบนาทีแรกบน VPS ใหม่ และรันอีกครั้งเมื่อคุณทำตาม รายการตรวจสอบการบำรุงรักษาเซิร์ฟเวอร์ Linux ตามปกติ หากคุณต้องการให้ระบบตรวจสอบด้วยตนเองและแจ้งเตือนเมื่อค่าความคลาดเคลื่อน (offset) เพิ่มสูงขึ้น การเขียน systemd service และ timer จะอธิบายรูปแบบการสร้าง unit ขนาดเล็กเพื่อรายงานผลตามกำหนดเวลาที่ตั้งไว้

FAQ

ฉันจะตรวจสอบได้อย่างไรว่านาฬิกาของ VPS ตรงกับเวลามาตรฐานหรือไม่?

ให้รันคำสั่ง timedatectl แล้วอ่านบรรทัด System clock synchronized ซึ่งเป็นสถานะของเคอร์เนลที่ถูกกำหนดโดย daemon ที่ทำหน้าที่ปรับเทียบเวลา ดังนั้นการเห็น yes ควบคู่กับ NTP service: n/a จึงถือเป็นสถานะปกติและถูกต้องสำหรับเครื่องที่ใช้ chrony หากต้องการทราบขนาดของความคลาดเคลื่อน ให้รัน chronyc tracking แล้วอ่านค่า System time หรือรัน timedatectl timesync-status แล้วอ่านค่า Offset หากเครื่องใช้ systemd-timesyncd ในการจัดการเวลา สำหรับการตรวจสอบเทียบกับแหล่งภายนอก ให้เปรียบเทียบ date -u กับส่วนหัว Date ที่ได้รับจากเว็บไซต์ HTTPS ใดก็ได้

ฉันควรใช้ chrony หรือ systemd-timesyncd บน VPS?

ให้ใช้ chrony กับงานที่สำคัญทุกประเภท systemd-timesyncd เป็นเพียง SNTP client ที่อ้างอิงเวลาจากเซิร์ฟเวอร์เดียว ซึ่งเพียงพอสำหรับเครื่องที่ออนไลน์ตลอดเวลาและมีเวลาเริ่มต้นที่ใกล้เคียงกับความเป็นจริงอยู่แล้ว แต่ chrony จะดึงข้อมูลจากหลายแหล่ง ตัดแหล่งข้อมูลที่ไม่น่าเชื่อถือออก เรียนรู้ค่าความคลาดเคลื่อนของนาฬิกาเครื่องคุณ และฟื้นฟูเวลาให้ตรงอย่างรวดเร็วหลังจากเครื่องหยุดทำงานหรือผ่านการทำ live migration การติดตั้ง chrony บน Debian หรือ Ubuntu จะลบ systemd-timesyncd ออกโดยอัตโนมัติ เนื่องจากทั้งสองแพ็กเกจมี time-daemon เหมือนกัน ห้ามรัน time daemon สองตัวพร้อมกันเด็ดขาด

ทำไมรหัส TOTP ถึงใช้งานไม่ได้บนเซิร์ฟเวอร์เครื่องหนึ่ง แต่ใช้ได้กับเครื่องอื่นทั้งหมด?

เพราะรหัส TOTP ขึ้นอยู่กับเวลาปัจจุบัน รหัสจะถูกสร้างจากตัวนับที่เปลี่ยนทุก 30 วินาที ดังนั้นเซิร์ฟเวอร์และโทรศัพท์ของคุณต้องมีเวลาที่ตรงกัน ระบบตรวจสอบส่วนใหญ่จะยอมรับความคลาดเคลื่อนได้ไม่เกินหนึ่งช่วงเวลา (ประมาณครึ่งนาที) ให้ตรวจสอบ timedatectl บนเซิร์ฟเวอร์เครื่องนั้น หาก System clock synchronized แสดงค่า no ให้แก้ไขการซิงค์เวลา แล้วรหัสจะกลับมาใช้งานได้โดยไม่ต้องเปลี่ยน shared secret ใดๆ

ฉันสามารถตั้งเวลาภายใน Docker container ได้หรือไม่?

ไม่ได้ และคุณไม่จำเป็นต้องทำเช่นนั้น container จะใช้ CLOCK_REALTIME ร่วมกับโฮสต์ เนื่องจาก Linux time namespace จะจำลองเฉพาะ monotonic clock และ boot-time clock เท่านั้น container ที่ไม่มีสิทธิ์พิเศษจะได้รับ date: cannot set date: Operation not permitted และการเพิ่ม CAP_SYS_TIME จะทำให้ container เปลี่ยนเวลาของโฮสต์ได้แทนที่จะเป็นการตั้งเวลาของตัวเอง ให้ซิงค์เวลาที่โฮสต์แทน หากต้องการแสดงเวลาท้องถิ่นที่ต่างออกไปภายใน container ให้ตั้งค่า TZ ใน environment ของ container นั้น

เซิร์ฟเวอร์ควรใช้เวลา UTC หรือเวลาท้องถิ่น?

ควรใช้ UTC และให้แสดงผลเป็นเวลาท้องถิ่นเฉพาะตอนที่ผู้ใช้เรียกดูข้อมูลเท่านั้น UTC ไม่มีการปรับเวลาตามฤดูกาล (daylight saving) ทำให้งานที่ตั้งเวลาไว้ทำงานได้สม่ำเสมอตลอดทั้งปี และทำให้ timestamp จากเซิร์ฟเวอร์หลายเครื่องเรียงลำดับกันได้โดยไม่ต้องแปลงค่า ให้ตั้งค่าด้วย sudo timedatectl set-timezone UTC หากใครต้องการอ่านเวลาท้องถิ่น สามารถใช้คำสั่งนำหน้าได้ เช่น TZ=America/New_York date ซึ่งจะไม่ส่งผลกระทบใดๆ ต่อนาฬิกาของระบบ