在Docker容器中更改日期是否可能?

80

我有一个在tomcat内运行的容器。我需要仅更改该容器内的日期并测试程序行为。我的逻辑与时间相关,有时需要查看几天或几个月后会发生什么。 在Docker中可行吗?我读到如果我更改容器中的日期,日期将会在主机系统上改变。但这对我来说是一个坏主意。我需要在一个服务器上拥有几个应用程序实例,并且能够为每个实例设置不同的时间。

但是,当我尝试在容器内更改日期时,我收到以下错误:

sudo date 04101812
date: cannot set date: Operation not permitted
Fri Apr 10 18:12:00 UTC 2015

2
如果你需要更改小于24小时的日期,请查看我的评论 https://dev59.com/XYnca4cB1Zd3GeqP9lr-#29435396 - user2915097
这个问题似乎是与https://dev59.com/_F4b5IYBdhLWcg3wiiR4重复了。 - Dragan Nikolic
9个回答

77

非常有可能在Docker容器内动态更改时间,而不影响主机操作系统。

解决方案是模拟时间。 这个库 拦截程序用来检索当前时间和日期的所有系统调用。

实现很简单。根据需要在Dockerfile中添加功能:

WORKDIR /
RUN git clone https://github.com/wolfcw/libfaketime.git
WORKDIR /libfaketime/src
RUN make install
记得在运行希望应用程序应用伪造时间之前设置环境变量LD_PRELOAD

示例:


(注意:此翻译保留了原文中的HTML标签)

CMD ["/bin/sh", "-c", "LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 FAKETIME_NO_CACHE=1 python /srv/intercept/manage.py runserver 0.0.0.0:3000]

现在您可以动态更改服务器时间:

示例:

def set_time(request):
    import os
    import datetime
    print(datetime.datetime.today())
    os.environ["FAKETIME"] = "2020-01-01"  #  string must be "YYYY-MM-DD hh:mm:ss" or "+15d"
    print(datetime.today())

2
这可以在Docker容器内工作。我通过docker exec安装了库并使用export设置了变量。 - roman
5
对我来说,这个解决方案也非常有效。我按照Roman上面建议的做法,进入容器,使用git检出代码,运行make install,然后将我的更改提交到镜像中,并使用以下方法重新启动容器: docker run -it --rm [各种选项省略] spooforbrains/image1 /bin/bash -c "export LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1; export FAKETIME_NO_CACHE=1; export FAKETIME=\"2016-05-01 12:00:00\"; node nodeimage" - SpooForBrains
1
啊,所以一旦在 Dockerfile 中使用那个 CMD 加载了库,你只需要改变 FAKETIME 环境变量,时间就会调整?真巧妙。 - chrismanderson
4
这对于使用golang应用程序和其他静态链接的内容无法正常工作。 - Sentinel
4
对于Debian和Ubuntu,存储库中也有一个libfaketime软件包可用。因此,您无需下载源代码并自行编译。例如,对于Debian Buster,请参见此处:https://packages.debian.org/buster/libfaketime - Erik Nellessen
显示剩余4条评论

20

使用Docker是不可能实现这个的。Docker使用与外部内核相同的时钟。你需要的是完全虚拟化,模拟完整的个人电脑。

sudo失败是因为它只会使你成为容器内虚拟环境的root用户。这个用户与主机系统的真正root用户无关(除了名称和UID),并且不能执行真正的root用户可以执行的操作。

如果你使用Python或Java等高级语言,通常可以使用挂钩来模拟某个系统时间进行测试,或者编写代码包装“从系统获取当前时间”的功能,并返回测试所需的结果。

特别是对于Java,使用joda-time。你可以使用DateTimeUtils.setCurrentMillis*()来注入自己的时间源。


Docker使用与外部内核相同的时钟。这是否意味着更改主机日期和时间就可以了? - otong
1
@otong 是的,但它会影响所有正在运行的容器和主机系统本身。 - Aaron Digulla
3
请参考Vingtoft的答案,了解如何使用Docker实现此操作-https://dev59.com/bV0b5IYBdhLWcg3wJ-Qy#41548434 - Taylor D. Edmiston

10
我创建了一个包含libfaketime的Docker镜像,供Alpine使用,但可以在其他发行版上进行。以下是使用Groovy为例的Java使用示例,但也可以使用Tomcat。
FROM groovy:alpine
COPY --from=trajano/alpine-libfaketime  /faketime.so /lib/faketime.so
ENV LD_PRELOAD=/lib/faketime.so \
    DONT_FAKE_MONOTONIC=1

然后在运行docker时,例如通过构建和传递FAKETIME环境变量

docker build -f fakedemo-java.Dockerfile . -t fakedemo
docker run --rm -e FAKETIME=+15d fakedemo groovy -e "print new Date();"

源码在 trajano / alpine-libfaketime | Github,而Docker镜像在 trajano/alpine-libfaketime | dockerhub

我还基于Ubuntu创建了一个变体:trajano / ubuntu-faketime | Github


4
对我来说,实际上我需要设置测试的真实日期。 我发现以下选项在Mac上可行,但您必须意识到,因为您正在更改Docker用于其所有容器的基础Alpine VM的日期,您将更改所有容器的日期。
选项1:更改主机机器的日期并重新启动docker
使用此选项时:
- 您可以重新启动docker。 - 您可以更改主机机器的日期
步骤:
1.停止容器。 2.通过“日期和时间首选项”更改机器的日期。 3.重新启动 Docker。 4.开始您的容器。
运行此序列以再次返回正确的日期和时间。
选项2:更改 Alpine VM 的日期
使用此选项时:
- 无法重新启动 docker。 - 无法设置主机机器的日期。
步骤:
1.打开终端(Terminal),输入命令screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty 2.在屏幕中按 Enter 几次。 3.输入命令date -s [hh:mm]。 现在,Docker 所有容器都将使用新时间。 您也可以使用其他格式,请查看关于“busybox date”的文档,因为它与其他日期实现不完全相同。 4.按下control-a :并键入d退出。
重置时间:
1.输入命令screen -r 2.输入命令ntpd -q,此步骤将使用 /etc/ntp.conf 中定义的服务器(看起来像返回到主机时钟的神奇桥)。 3.按下control-a :并键入quit以退出。

2
这对我有用,也许你可以尝试一下:

dpkg-reconfigure tzdata

编辑:在您遇到问题的容器内执行它。一个界面会出现。在那里,您可以编辑时区和本地时间,然后正确设置,这解决了我的问题,与您的问题相同。
祝你好运!

@zwolin,如果您使用的是Debian基础镜像,则需要在容器中安装“debconf”包。对于其他发行版,请查阅其文档。 - Aaron Digulla
25
这是用于配置时区的内容,这并没有回答问题。 - FAjir
3
14个人如何为一个没有回答问题的答案点赞? - Graham Nicholls

1

我的解决方案:我通过Vagrant在VirtualBox虚拟机上运行Docker。

这并没有听起来那么复杂。下面的Vagrantfile将虚拟机的日期时间重置为特定时间,在虚拟机启动之前。相应的docker-compose provisioner会自动运行docker-compose.yml

Vagrantfile:

Vagrant.configure("2") do |config|
  config.vm.define "ubuntuvm" do |ubuntuvm|
    config.vm.box = "ubuntu/focal64"
    config.vm.provision :docker

    ubuntuvm.trigger.before :up do |trigger|
      trigger.info = "changing time"
      trigger.ruby do |env,machine|
        require 'time'
        machineTime = "2022/07/02 00:20:00"
        offset = DateTime.parse(machineTime).strftime("%Q").to_i - DateTime.now.strftime("%Q").to_i
        puts "Updating machine time to: #{machineTime} by shifting biossystemtime #{offset} ms"
        puts `VBoxManage setextradata #{machine.id} "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 1`
        puts `VBoxManage modifyvm #{machine.id} --biossystemtimeoffset #{offset}`
      end
    end
  end


  config.vm.hostname = "ubuntuvm"
  # config.vm.network "forwarded_port", guest: 80 host: 8080
  # config.vm.synched_folter "../data"", "/vagrant_data"

  config.vm.provider "virtualbox" do |vb|
      # vb.memory = "1024"
  end
  config.vm.provision :docker_compose, yml: "/vagrant/docker-compose.yml", rebuild: true, run: "always"
end

然后,我只需要做以下几步:
# start the machine and run the docker-compose
vagrant up

每次我启动机器时都会看到以下信息:
ubuntuvm: Updating machine time to: 2022/07/02 00:20:00 by shifting biossystemtime -212710744 ms
ubuntuvm: Running docker-compose up...

其他命令:

# get inside the machine with ssh to check its time and also check the container's time (docker commands already exist inside there due to the provisioner).
vagrant ssh ubuntuvm
# stop the machine
vagrant halt
# destroy the machine
vagrant destroy

前提条件

已知问题:

仅在第一次运行时,vagrant up将无法更新时间,因为由于某种原因,在创建虚拟机之前即执行了before-up触发器。在所有其他执行中,虚拟机已经存在,因此时间会按预期重置。


0

docker exec -it [Container Id] /bin/bash(进入容器)

rm /etc/localtime(查看时区)

ln -s /usr/share/zoneinfo/Asia/Karachi /etc/localtime(设置新的时区)


4
请给你的代码添加描述,说明你的代码实际在做什么。 - Mustafa Poya
2
原始问题是设置日期而不是时区。 - Jarad Duersch
可能是基于Alpine构建的Docker容器没有任何时区数据。 使用apk add tzdata进行安装。https://wiki.alpinelinux.org/wiki/Setting_the_timezone - EsmaeelE

0
如果您想更改容器的日期,您需要在启动时向容器添加--cap-add SYS_TIME标志来放宽容器的安全性。
例如:
docker run --rm -it --cap-add SYS_TIME alpine date -s 2020-08-25

这将为您提供更多容器权限,而无需成为root用户。
这里有一个链接供参考。
您也可以在Kubernetes中执行此操作。
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
spec:
  containers:
  - name: friendly-container
    image: "alpine:3.4"
    command: ["/bin/echo", "hello", "world", "date -s 2020-08-25"]
    securityContext:
      capabilities:
        add:
        - SYS_TIME

-2

我在我的Jenkins Docker实例中遇到了同样的问题,以下步骤解决了我的问题:

  1. 进入容器

    docker exec -it 9d41c699a8f4 /bin/bash

  2. 查看时区 cat /etc/timezone:输出Etc/UTC

  3. 使用nano设置新的时区:Asia/Colombo(在此处输入您的时区)

  4. 重新启动容器


2
原始问题是设置日期而不是时区。 - Jarad Duersch
我得到了:bash: /etc/timezone: 权限被拒绝 - Ryan w

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