代码改变世界

docker内时间与北京时间相差8小时解决

2024-01-22 18:32  youxin  阅读(1565)  评论(0编辑  收藏  举报

/etc/localtime是用来描述本机时间
/etc/timezone是用来描述本机所属的时区

在linux中,有一些程序会自己计算时间,不会直接采用带有时区的本机时间格式,会根据UTC时间和本机所属的时区等计算出当前的时间。

比如jdk应用,时区为“Etc/UTC”,本机时间改为北京时间,通过java代码中new 出来的时间还是utc时间,所以必须得修正本机的时区。

在使用docker容器查看日志的过程中,发现容器内的时间与实际时间晚了8小时,其实那个是UTC时间(标准时间),而我们所处的是CST时间(东八区时间)

 

总结,最佳做法:

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
ENV TZ=Asia/Shanghai  
RUN apk add -U tzdata &&   cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 
 
注意,不要删除apk del tzdata,不然时间还是utc时间。

创建容器查看日志

  启动一个容器

1
docker run --3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql3306 mysql:5.7

   查看日志

1
docker logs mysql3306

   可以看到日志比系统时候少8小时

一,进入容器内修改(已经实践过的)
(1)更改容器时间
也就是操作的文件夹是容器内的文件夹,不再是宿主机的
首先是从宿主机进入容器,输入以下命令(containerID就是我们在宿主机通过docker ps -a获取到的容器id值):

docker exec -it containerID /bin/bash

进入容器后,我们ls即可看到已经进入容器内部,容器内部的文件目录和我们的linux目录是一样的(退出容器是exit),我们输入以下命令进行修改:

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

我们此使输入date即可查看到时区已经进行了更改,即便是重启也会保存此次更改


(2)更改java相关程序获取到的时间
虽然系统时间有改为CST时区,但是java中的new Date()获取到的时间仍然是UTC时间,这是因为:
LINUX下:jre是从/etc/sysconfig/clock这个文件中获取时区信息的,修改次文件即可,没有的话添加一个。
我们使用以下命令在sysconfig目录添加一个clock文件(如果目录也不存在,那么目录也添加上)

touch clock

clock文件创建好了,我发现没有办法使用vim去编辑clock文件,可能容器内没有装vim,所以我们分别执行下边两条命令进行安装:

apt update
apt install vim

然后编辑clock文件放入以下内容,然后:wq保存退出,

ZONE="Asia/Shanghai"
UTC=false
ARC=false

就可以发现与java相关的系统日志时间已经是CST的了,我操作时间就是14:10


下边这两种方法是在网上找的另外两种,但是如果要是java相关程序获取到CST时间,仍然要使用上边(2)做法

二,在宿主机内对容器进行修改
直接执行以下命令,不需要进入容器内

docker cp /etc/localtime [容器ID或者NAME]:/etc/localtime

三,通过dockerfile对容器进行修改
这个方法的好处是不用每次启动新的容器都要进行修改,上边两种方法修改后只能在同一容器中生效,通过更改dockerfile,进行创建新的容器时就已经是修改过的了,我们需要在对应镜像的dockerfile中添加如下内容:

ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

 

容器生成前(进入dockerfile里面设置)

 

FROM ${docker.registry.public}/jdk:8u252       

 ENV TZ=Asia/Shanghai                 

   RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

WORKDIR /

COPY ${project.build.finalName}.jar ${project.artifactId}.jar

ENTRYPOINT ["java","-Duser.timezone=GMT+08", "-jar", "${project.artifactId}.jar"]

上面指定时区TZ,时间-Duser.timezone

 

-s  --symbolic比较容易,有-s时表示创建软连接,没有-s时,表示创建硬链接

-f  --force 强行删除任何已存在的目标文件

-n  --no-dereference 把符号连接的目的目录视为一般文件

 注意alpine不带 timezone,需要先安装timezone,看文章下面

 

jdk读取linux系统时间步骤:

0. 先找vm参数 :  -Duser.timezone=Asia /Shanghai

1.先找“TZ”变量,没有,到2,

2.读/etc/timezone,没有到3,

3.比较/etc/localtime文件与"/usr/share/zoneinfo目录下所有时区文件,如果有一致的,就为该时区,如果没有,到4,

4.默认为标准GMT

(参考:https://blog.csdn.net/weixin_34072159/article/details/88031424

JVM加载TimeZone读取文件优先级实战分析

 
或者:

在dockerfile中加入:

RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \

    && echo 'Asia/Shanghai' >/etc/timezone \

把时间改成东八区就可以了

 



链接:https://www.jianshu.com/p/387959fc2fae
 


参考:https://blog.csdn.net/Jarbein/article/details/103631718

在Compose中修改时区

在docker-compose.yml文件中

volumes:
  - /etc/timezone:/etc/timezone
  - /etc/localtime:/etc/localtime


链接:https://www.jianshu.com/p/43e5d72b0f63
 
 

alpine 是精简版镜像,默认不带 timezone,需要我们处理一下,步骤如下:
1、安装 timezone 数据包

apk add -U tzdata
ls /usr/share/zoneinfo

为了防止添加失败,加上-U 参数,更新仓储缓存。
列出安装的时区文件,验证是否下载成功。

2、拷贝需要的时区文件到localtime,国内需要的是Asia/Shanghai:

cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

3、验证时区

date
CST 即为 中国标准时间。

 

命令合并

apk add -U tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 

官方网址
————————————————
原文链接:https://blog.csdn.net/catoop/article/details/105199019

 

mysql时间

 

其他系统例如 Docker 官方提供的 mysql

https://github.com/docker-library/mysql/tree/master/5.6

可以先进系统看时区,以及是否存在时区的配置文件,存在的情况下,直接修改即可。示例如下:

FROM 10.10.1.243:5000/mysql:5.6.43

RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone

原文链接:https://blog.csdn.net/isea533/article/details/87261764

 如果用docke compose,可以这么设置:

services:
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: example
      TZ: Asia/Shanghai

 

-e TZ=Asia/Shanghai

 
改完后执行:
SELECT NOW();
 
show variables like '%time_zone%';
 
 

alpine 镜像时区调为 Asia/Shanghai 后因 TZ 与 apk del tzdata 导致不生效
现象:
设置 TZ 环境变量,并且最后删除 tzdata,创建出来的镜像运行时执行 ​​date​​ 后显示日期还是 UTC 时间。

FROM alpine AS runtime
ENV TZ Asia/Shanghai
RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone && apk del tzdata

解决方案

不删除 tzdata

FROM alpine AS runtime
ENV TZ Asia/Shanghai
RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone
 

不设置 TZ 环境变量

FROM alpine AS runtime
RUN apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo Asia/Shanghai > /etc/timezone && apk del tzdata
 

用官方脚本设置(suggest)

在​ ​alpine 官方文档​​​ 可以看到 timezone 是根据 setup-timezone 指定的,而在 alpine 镜像上可以发现​ ​没有这个命令​​​,所以可以用 ​​alpine-conf​​ 安装。

FROM alpine AS runtime
ENV TZ Asia/Shanghai
RUN apk add alpine-conf && \
/sbin/setup-timezone -z Asia/Shanghai && \
apk del alpine-conf
 
现在可以看到是 CST 时间了(又熬夜的一天)

# date
Sun Jan 16 04:44:07 CST 2022
 
推测
推测是 date 在有 TZ 环境变量时会去 /usr/share/zoneinfo/ 目录下取时区信息,而我们删掉的 tzdata 就是这个目录,导致找不到信息就 fallback 到 UTC 时间了。没有 TZ 时大概就直接从 /etc/timezone 取时区,再从 /etc/localtime 读时区信息。而 alpine 本身可能没有 TZ 指定环境变量的机制,像 debian 镜像就自带 /usr/share/zoneinfo/,就不会有这个问题。

相关链接
​ ​https://github.com/gliderlabs/docker-alpine/issues/136​
-----------------------------------
alpine 镜像时区调为 Asia/Shanghai 后因 TZ 与 apk del tzdata 导致不生效
https://blog.51cto.com/u_15310543/5010044