k8s环境下处理容器时间问题的多种姿势
介绍 Kubernetes 环境中容器时间不一致问题的排查与处理方法。

背景概述
在Linux环境下,默认安装操作系统时都需要正确设置系统的时区为当前所在的时区
在容器环境下,除了业务镜像外,我们有很多情况都是使用的官方镜像或第三方镜像,而这些镜像一般都不是国人制作。因此使用这些镜像的时候,自然会有一个问题,即容器镜像的默认时区不正确
简而言之,在容器环境中需要处理时间(时区)问题的原因一般有
- 时间不对,和正确的(例如北京时间)有偏差
- 时区不对,镜像默认时区和当前时区不符合
- 某些特殊业务需要临时修改时间。例如电商秒杀业务,将时间设置超前或滞后,在内部测试业务的时间控制功能
硬件时钟和系统时间
先来看看操作系统以及容器是如何获取时间的
时钟一般分为硬件时钟(RTC,Real Time Clock)和操作系统时钟(OS,System Clock)
硬件时钟跟运行在cpu上的程序是独立不相关的,甚至在服务器关机之后仍然可以正常运行,这就保证了服务器时间的正常运行,硬件时间也有着各种各样的称呼,例如:hardware clock, real time clock, RTC, BIOS clock以及CMOS clock等,在目前主流的服务器都采用RTC芯片实现
操作系统时间称为系统时钟或者系统时间,这就是平时在系统中经常接触到的时间,也是应用程序在执行与时间相关的操作会用到的时间,它只是在系统运行时存在,其记录形式为UTC时间(the number of seconds since 00:00:00 January 1, 1970 UTC)
硬件时钟和系统时间的关系
硬件时钟是用来保证在操作系统关机之后仍然可以正常计时的必要硬件,而系统时间是我们在日常操作中才会经常使用到的时间,仅仅在操作系统初始化时,操作系统才会去RTC芯片中拿到硬件时钟的值,之后便是独立运行和独立计时
时钟的运作机制如下

Linux中修改时间
时间依赖时间标准,时间的表示有两个标准:localtime和UTC(Coordinated Universal Time)
- UTC 是与时区无关的全球时间标准。尽管概念上有差别,UTC 和 GMT (格林威治时间) 是一样的
- localtime 标准则依赖于当前时区
时间标准由操作系统设定,Windows默认使用localtime,Mac OS默认使用UTC而UNIX系列的操作系统两者都有。使用Linux时,最好将硬件 时钟设置为UTC标准,并在所有操作系统中使用。这样Linux系统就可以自动调整夏令时设置,而如果使用localtime标准那么系统时间不会根据夏令时自动调整
通过如下命令可以检查当前设置,终端执行
timedatectl status | grep local
硬件时间可以用 hwclock 命令设置,将硬件时间设置为localtime
timedatectl set-local-rtc 1
硬件时间设置成UTC,终端执行
timedatectl set-local-rtc 0
上述命令会自动生成/etc/adjtime,无需单独设置
在日常使用中,修改时间一般通过date修改日期时 间,通过hwclock校准硬件时钟
这里提到了夏令时,再分享一个有意思的事情,可能大多数人还不知道,我国在解放后是实行过夏令时的

尝试在容器中修改时间
在容器中能否通过date修改日期时间,通过hwclock校准硬件时钟?
事实上是不可以的,在容器内部通过默认权限修改时间会报错

这是因为容器的隔离是基于Linux的Capability机制实现的,可以通过给容器添加--privileged或--cap-add SYS_TIME来实现目的,但并不推荐,因为这样会直接影响到容器所在主机的时间
Linux内核中将timekeeper设置为全局变量,所以只要去修改系统时间,这个影响就是内核层面的,所以在docker的实现中默认是禁止在容器内修改时间的,因为容器与虚拟化的区别就在于是否共享内核,这就意味着一旦在容器中修改了时间,这个影响就是全局性的