處理Docker容器化部署Tomcat服務碰到的時區問題

用Docker工具容器化部署服務以後,碰到的問題比較多。
其中比較特出的問題是這個時區問題。java

1 問題現象:

舉個例子,有個小功能,須要記錄計劃開始時間。
前臺輸入的時間是:2018.4.24 00:00:00。
後臺記錄的計劃開始時間倒是:2018.4.23 16:00:00。
能夠看出,整整提早了8個小時。web

2 問題處理過程:

多筆數據都是剛恰好差別8個小時,那通常都是時區問題了。
加上我是用docker容器化部署的,因此,我開始估計是Docker的時區沒弄正確致使的問題。sql

驗證問題:
Docker宿主查看時間:docker

[apiuser@xygerp-api software]$ date
2018年 04月 24日 星期二 15:00:05 CST

說明宿主的時區是木有問題的。數據庫

UTC是世界時區,CST是東八區時間
CST應該是指(China Shanghai Time,東八區時間)
UTC應該是指(Coordinated Universal Time,標準時間)

接着,exec進入容器,查看容器的時間有沒有問題:apache

備註:爲方便測試,我新建了一個docker容器(timezone-test)。
sudo docker run --name timezone-test -p 8800:8080 -v /etc/localtime:/etc/localtime chaimm/tomcat:1.1

進入容器,查看容器的時間:segmentfault

[apiuser@xygerp-api software]$ sudo docker exec -it timezone-test /bin/sh
sh-4.2$ date
Tue Apr 24 15:05:14 CST 2018

能夠看出,時區是沒問題的,可是爲何運行在該容器的Tomcat服務的時區(不管是操做DB的時間,仍是Tomcat的Log的時間)都是UTC?
例以下面的日誌,log時間是:24-Apr-2018 07:03:55.381
應該是:24-Apr-2018 15:03:55.381纔對。api

24-Apr-2018 07:03:55.381 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/host-manager
24-Apr-2018 07:03:55.410 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/host-manager has finished in 29 ms
24-Apr-2018 07:03:55.410 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/manager
24-Apr-2018 07:03:55.431 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/manager has finished in 21 ms
24-Apr-2018 07:03:55.437 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler [http-nio-8080]
24-Apr-2018 07:03:55.447 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler [ajp-nio-8009]
24-Apr-2018 07:03:55.450 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 886 ms

這個問題困擾了我蠻久,最後通過找一些資料,發現原來是Tomcat啓動的時候,時區實際是java啓動的默認時區。
這樣子,實際上不少時區:tomcat

時區功能 時區說明
Java JVM啓動時候的時區。若是不指定,則默認取系統時區,就是:System.getProperty("user.timezone")
MySQL 數據庫有時區,直接用:select now()能夠查看確認。可是,Java的JDBC直接新增的數據是不會參考Mysql自己的時區。舉個例子:INSERT什麼時間就是什麼時間!
Docker宿主 其實是操做系統OS的時區。若是安裝系統的時候選擇中國上海時區,那通常是CST(中國時區)。
Docker容器 容器自己也是一個操做系統(虛擬系統)。當容器時區和宿主時區不一致,能夠修改。若是有疑問能夠直接exec到容器裏面查看容器時區。
Tomcat服務 Tomcat是運行Java的Web應用服務器,因此,它的時區實際上就是JVM的時區。

從上面的表格能夠看出,實際上,在Java中處理的時區,說到底仍是以JVM啓動的時候的時區爲主!
只不過,大部分狀況下,JVM啓動若是沒指定時區,那默認=操做系統的時區。
問題來了,如何確認JVM默認啓動的時區是哪一個?
這裏有個小辦法:服務器

sh-4.2$ vi TestTimezone.java
public class TestTimezone{
   public static void main(String[] args){
      System.out.println(new java.util.Date());
      System.out.println(System.getProperty("user.timezone"));
   }
}

而後再編譯而且運行它,看結果就知道JVM默認啓動的時區是哪一個了:

sh-4.2$ javac TestTimezone.java
sh-4.2$ java TestTimezone
Tue Apr 24 07:27:30 UTC 2018
UTC

從上面能夠看出,就算我在Docker容器裏面的系統時間已是CST時區,可是,System.getProperty("user.timezone")是UTC,致使Java啓動的時候,以UTC時區啓動。
因此就出現了我碰到的問題!
這個纔是根本緣由。

備註: 一樣的辦法,在宿主執行,發現宿主就能抓取正確的時區。
[apiuser@xygerp-api software]$ java TestTimezone
Tue Apr 24 15:31:03 CST 2018
Asia/Shanghai

3 問題解決方案

既然發現了問題,是在Docker容器環境下,System.getProperty("user.timezone")抓的值不是CST時區致使的問題。那其中一個簡單的解決辦法是,在Tomcat啓動的 時候,指定時區便可。
具體辦法:

[apiuser@xygerp-api software]$ sudo docker exec -it timezone-test /bin/sh
sh-4.2$ ls
LICENSE        TestTimezone.class        bin   scripts
NOTICE           TestTimezone.java        conf  temp
RELEASE-NOTES  apache-tomcat-7.0.82        lib   webapps
RUNNING.txt    apache-tomcat-7.0.82.tar.gz  logs  work
sh-4.2$ cd bin/
sh-4.2$ vi catalina.sh
修改這裏:
JAVA_OPTS="$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Duser.timezone=GMT+08"

這裏寫圖片描述

保存您的修改,再重啓啓動docker:

[apiuser@xygerp-api software]$ sudo docker restart timezone-test
timezone-test
[apiuser@xygerp-api software]$

再查看timezone-test容器的log:

[apiuser@xygerp-api ~]$ sudo docker logs -f --tail 1000 timezone-test
24-Apr-2018 15:41:27.436 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
24-Apr-2018 15:41:27.629 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
24-Apr-2018 15:41:27.647 INFO [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
24-Apr-2018 15:41:27.650 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["ajp-nio-8009"]
24-Apr-2018 15:41:27.652 INFO [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
24-Apr-2018 15:41:27.652 INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 809 ms
24-Apr-2018 15:41:27.683 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service Catalina
24-Apr-2018 15:41:27.683 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.5.9
24-Apr-2018 15:41:27.697 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/ROOT
24-Apr-2018 15:41:28.153 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/ROOT has finished in 456 ms
24-Apr-2018 15:41:28.154 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/docs
24-Apr-2018 15:41:28.179 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/docs has finished in 25 ms
24-Apr-2018 15:41:28.179 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/examples
24-Apr-2018 15:41:28.492 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/examples has finished in 312 ms
24-Apr-2018 15:41:28.492 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/host-manager
24-Apr-2018 15:41:28.524 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/host-manager has finished in 32 ms
24-Apr-2018 15:41:28.525 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory /opt/tomcat/webapps/manager
24-Apr-2018 15:41:28.548 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory /opt/tomcat/webapps/manager has finished in 23 ms
24-Apr-2018 15:41:28.555 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler [http-nio-8080]
24-Apr-2018 15:41:28.565 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler [ajp-nio-8009]
24-Apr-2018 15:41:28.567 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 914 ms

能夠看到,裏面的日誌的時間已經正確啦!

備註:上面的只是其中一個解決辦法,也能夠經過調整Docker容器的System.getProperty("user.timezone")的值,來讓JVM在啓動的時候能正確找時區!
仍是那句話:出現問題的話,必需要找到問題出錯的地方,纔好解決問題啊!我就是繞了許多的彎路才找到問題的緣由。一開始一直覺得是Docker容器虛擬機的時區問題,因此困擾了好久!

更新:
另一個作法,也能夠解決這個問題:
其實就是啓動容器的時候,指定時區便可。

sudo docker run --name timezone-test2 -p 8800:8080 -e TZ="Asia/Shanghai" -v /etc/localtime:/etc/localtime chaimm/tomcat:1.1

參考連接:
https://segmentfault.com/a/11...
https://www.jianshu.com/p/5b1...

相關文章
相關標籤/搜索