Docker容器中的时间与主机不同步

6

我正在尝试通过我的SpringBoot应用连接到CosmosDB。如果我使用Spring或通过Intellij运行应用程序,所有这些都可以正常工作。但是,当我在Docker中运行应用程序时,我会收到以下错误消息:

com.azure.data.cosmos.CosmosClientException: The authorization token is not valid at the current time.
Please create another token and retry
(token start time: Thu, 26 Mar 2020 04:32:10 GMT, 
token expiry time: Thu, 26 Mar 2020 04:47:10 GMT, current server time: Tue, 31 Mar 2020 20:12:42 GMT).

请注意,以上错误信息中的当前服务器时间是正确的,但其他时间落后了5天。
有趣的是,我只在Docker容器中收到这个错误。
FROM {copy of zulu-jdk11}

ARG JAR_FILE

#.crt file in the same folder as your Dockerfile
ARG CERT="cosmos.cer"
ARG ALIAS="cosmos2"

#import cert into java
COPY $CERT /
RUN chmod +x /$CERT
WORKDIR $JAVA_HOME/lib/security
RUN keytool -importcert -file /$CERT -alias $ALIAS -cacerts -storepass changeit -noprompt

WORKDIR /
COPY /target/${JAR_FILE} app.jar
COPY run-java.sh /
RUN chmod +x /run-java.sh

ENV JAVA_OPTIONS "-Duser.timezone=UTC"
ENV JAVA_APP_JAR "/app.jar"

# run as non-root to mitigate some security risks
RUN addgroup -S pcc && adduser -S nonroot -G nonroot
USER nonroot:nonroot

ENTRYPOINT ["/run-java.sh"]

需要注意的一点是ENV JAVA_OPTIONS "-Duser.timezone=UTC",但是删除这个并没有帮助我解决问题。

基本上我从IntelliJ中运行相同的步骤,没有任何问题,但在docker中,到期日期似乎比实际时间早5天。

version: "3.7"
services:
  orchestration-agent:
    image: {image-name}
    ports:
      - "8080:8080"
    network_mode: host
    environment:
      - COSMOSDB_URI=https://host.docker.internal:8081/
      - COSMOSDB_KEY={key}
      - COSMOSDB_DATABASE={database}
      - COSMOSDB_POPULATEQUERYMETRICS=true
      - COSMOSDB_ITEMLEVELTTL=60

我认为还应该提到,我将 network_mode 更改为 host。并且我还将 CosmosDB 的 URI 从 https://localhost:8081 更改为 https://host.docker.internal:8081/

我还想提一下,我使用以下链接来帮助构建我的 dockerfile

将自签名证书导入Docker的JRE cacert证书库未被服务识别

如何在 Dockerfile 中为 Jenkins 添加 SSL 自签名证书以进行 LDAPS?


感觉你的容器(镜像)没有正确地获取|更新时间。如果它有一个 shell,你可以运行 docker run --interactive --tty {image-name} /bin/sh -c 'date' 命令。如果你的客户端依赖于镜像的时间,而镜像的时间不正确,这可能会解释问题。我假设“当前服务器时间”是由 CosmosDB 服务返回的。 - DazWilkin
@DazWilkin 我应该看到什么输出?我看到的还是通常的错误。 - Archmede
抱歉,您可能需要调整入口点而不是使用 docker run --interactive --tty --entrypoint=/bin/sh {image-name} date 命令来获取容器中的日期。显然,这个日期与您主机上的日期不匹配,因此需要进行调整。 - DazWilkin
@DazWilkin 我在你的解决方案中遇到了“/bin/sh: can't open 'date': No such file or directory”的问题。我使用的是Windows系统。 - Archmede
@DazWilkin 是的,我会重新措辞问题并在今天晚些时候发布答案。感谢您的帮助。 - Archmede
显示剩余3条评论
3个回答

12

Docker容器没有单独的时钟,它与Linux主机的时钟相同,因为时间不是命名空间的值。这也是为什么Docker删除了在容器内更改时间的权限,因为那会影响主机和其他容器,打破隔离模型。

然而,在Docker桌面版中,docker运行在虚拟机中(允许您在非Linux台式机上运行Linux容器),当笔记本电脑挂起时,该虚拟机的时间可能会失步。目前这个问题正在github上跟踪,您可以关注以下链接以查看进展情况:https://github.com/docker/for-win/issues/4526

潜在的解决方案包括重新启动计算机、重新启动docker的虚拟机、将NTP作为特权容器运行或使用以下PowerShell重置Windows虚拟机中的时间同步:

Get-VMIntegrationService -VMName DockerDesktopVM -Name "Time Synchronization" | Disable-VMIntegrationService
Get-VMIntegrationService -VMName DockerDesktopVM -Name "Time Synchronization" | Enable-VMIntegrationService

使用WSL 2,重新启动虚拟机涉及以下步骤:

wsl --shutdown
wsl

5

-1
这是一个在 Windows 上使用 WSL2 和 Docker Desktop 的替代方法:
由于无法在 Docker 容器内设置日期,我只需在 WSL2 中打开 Ubuntu 并运行以下命令以同步时钟:
sudo date -s "$(wget -qSO- --max-redirect=0 google.com 2>&1 | grep Date: | cut -d' ' -f5-8)Z"

它工作得很好,所以我在我的根用户的 crontab 中添加了以下行:

# Edit root user's crontab
sudo crontab -e

# Add the following line to run it every minute of every day:
* * * * * sudo date -s "$(wget -qSO- --max-redirect=0 google.com 2>&1 | grep Date: | cut -d' ' -f5-8)Z"

之后,我只需重新启动Docker容器,日期就是正确的,因为它们似乎使用了WSL2 Ubuntu的日期。

之前的日期(不正确):

date
Thu Feb  4 21:50:35 UTC 2021

日期后(正确):

date
Fri Feb  5 19:01:05 UTC 2021

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接