需求

客户提供的数据有时间限制,需要把数据制定到一个特定的时间点去测试,如何在 修改容器时间 而不影响 宿主机时间

宿主机操作系统:CentOS Linux release 7.7.1908 (Core)

实现逻辑

首先,运行容器尝试直接修改容器内时间,如下:

运行一个容器:
docker run -d -p 80:80 nginx:alpine
进入容器修改时间:
docker exec -it d0d1cc9d5 sh
/ # date -R
Fri, 25 Nov 2022 15:48:40 +0000
/ # date -s '2000-11-25 15:48:51'
date: can't set date: Operation not permitted
Fri Nov 25 15:48:51 UTC 2022

通过上面的操作,确认无法通过 date 命令来修改容器内时间。这是由于docker容器的隔离是基于Linux的Capability机制实现的,Linux的Capability 机制允许你将超级管理员相关的最高权限划分为不同的小单元。而要修改系统时间需要有 SYS_TIME 权限。使用 --cap-add--cap-drop 可以添加或禁止特定的权限。--privileged 参数也可以达到开放权限的作用,与 --cap-add 的区别就是,--privileged 是将所有权开放给容器。

docker 使用 --privileged , --cap-add--cap-drop 来对容器本身的能力进行开放或限制。

使用 --privileged 尝试修改时间:

docker run --name ngx -d -p 80:80 --privileged nginx:alpine
root@k8s-master(192.168.1.11)/root> docker exec -it ngx sh
/ # date
Fri Nov 25 15:57:09 UTC 2022
/ # date -s '2000-11-25 15:57:14'
Sat Nov 25 15:57:14 UTC 2000
/ # date -R
Sat, 25 Nov 2000 15:57:17 +0000

容器内时间修改成功,查看宿主机时间有没有被修改。

date
Sat Nov 25 23:58:19 CST 2000

可以发现, 一旦添加了 --privileged 容器就拥有了宿主机超级用户的权限,修改容器内时间连通宿主机时间一起修改了。

这种方式是不可取的,我们的需求仅仅是修改这一个容器的时间,而非宿主机的时间。如果该宿主机运行其他容器,则连同所有容器的时间都会发生变动。

libfaketime的使用

Github 有一个开源的 libfaketime 项目,就能解决仅修改一个容器的问题。

项目地址:https://github.com/wolfcw/libfaketime

安装过程

下载项目到宿主机:

git clone https://github.com/wolfcw/libfaketime.git
cd libfaketime

# 安装编译环境
yum install -y gcc gcc-c++ locate

# 编译安装
make && make install 

查看依赖库路径

updatedb
locate libfaketime.so.1
/root/libfaketime/src/libfaketime.so.1
/usr/local/lib/faketime/libfaketime.so.1

测试1 – 时间是否会递增

因为是在 centos 系统上编译的 libfaketime,首先使用 centos 镜像运行为容器进行测试:

#注意这里需要将本地库文件映射到容器内
docker run -it --rm -v /usr/local/lib/faketime/libfaketime.so.1:/usr/local/lib/faketime/libfaketime.so.1 centos:7 /bin/bash
>export LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1
>FAKETIME='2000-11-25 16:19:24' date
Sat Nov 25 16:19:24 UTC 2000
>date
Fri Nov 25 08:19:28 UTC 2022

到这里,容器内修改时间已经实现了, 接下来如何结合程序呢?这里通过 dockerfile 使用一个shell脚本进行测试:

Dockerfile

FROM centos:7
ENV LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 \
    FAKETIME="2000-01-01 1:00:00"
COPY /usr/local/lib/faketime/libfaketime.so.1 /usr/local/lib/faketime/libfaketime.so.1
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]


### entrypoint.sh
#!/bin/bash
# Author:hukey
while sleep 1; do
  date
done

### 将libfaketime.so.1拷贝到当前目录下
cp -a /usr/local/lib/faketime/libfaketime.so.1 ./

基于dockerfile 生成镜像

docker build -t time:centos ./
...
Successfully built a7ca0542553a
Successfully tagged time:centos

运行镜像为容器

docker run time:centos

image-20221125163621781

通过在 Dockerfile中定义环境变量的方式,修改的时间是不会发生变化的。

接下来,通过手动进入容器修改时间,查看时间会不会递增

# 启动容器
docker run -it --rm -v /usr/local/lib/faketime/libfaketime.so.1:/usr/local/lib/faketime/libfaketime.so.1 centos:7 /bin/bash

# 创建测试脚本
vi test.sh
#!/bin/bash
while sleep 1; do
  date
done
# 赋权
chmod +x test.sh

# 开始测试
export LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1

FAKETIME='2000-01-01 1:00:00' ./test.sh
Sat Jan  1 01:00:00 UTC 2000
Sat Jan  1 01:00:00 UTC 2000
Sat Jan  1 01:00:00 UTC 2000
Sat Jan  1 01:00:00 UTC 2000
Sat Jan  1 01:00:00 UTC 2000

可以看到,就算手动触发,时间也不会递增。因此,使用 FAKETIME 定义时间后,时间不会发生任何递增,始终保持设定的时间。

测试2 – 多平台是否适用

宿主机为 centos 系统,容器采用 centos 无问题,接下来采用 alpine 进行测试。

# 创建容器
docker run --name ngx  -d --rm -p 80:80 -v /usr/local/lib/faketime/libfaketime.so.1:/usr/local/lib/faketime/libfaketime.so.1 nginx:alpine

# 进入容器
docker exec -it ngx sh

# 定义变量
export LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1

# 更改时间
/ # FAKETIME='2000-01-01 1:00:00' date
Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by /usr/local/lib/faketime/libfaketime.so.1)
Error relocating /usr/local/lib/faketime/libfaketime.so.1: dlvsym: symbol not found
Error relocating /usr/local/lib/faketime/libfaketime.so.1: gnu_get_libc_version: symbol not found

这里发生了报错,因此 如果宿主机是redhat系列的系统,则容器也仅仅支持redhat系列系统。如果容器为其他系统,例如:alpine 则需要重新编译 libfaketime

测试3 – jdk版本兼容性

这个测试是在进行业务需求时遇到的,这里有必要记录下。

研发要求:修改时间+jdk1.8

为了使用faketime 修改时间,我这里采用 centos:7 作为基础镜像,自己制作 jdk镜像。

首先尝试不用修改时间的 jdk1.8 镜像制作

vim Dockerfile
FROM centos:7
ENV JAVA_HOME=/usr/local/jdk1.8.0_77
ENV PATH=$PATH:$JAVA_HOME/bin \
    CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ADD jdk-8u77-linux-x64.tar.gz /usr/local/
CMD java -version

# 生成镜像
docker build -t jdk:centos ./

# 运行为容器
docker run --rm jdk:centos
java version "1.8.0_77"
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)

ok,基于centos基础镜像的 jdk1.8 制作完成了,接下来添加修改时间功能。

vim Dockerfile
FROM centos:7
ENV JAVA_HOME=/usr/local/jdk1.8.0_77 \
    LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1
ENV PATH=$PATH:$JAVA_HOME/bin \
    CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar \
    FAKETIME="2000-01-01 1:00:00"
COPY ./libfaketime.so.1 /usr/local/lib/faketime/libfaketime.so.1
ADD jdk-8u77-linux-x64.tar.gz /usr/local/

# 生成镜像
docker build -t jdk:centos ./

# 运行容器并进入容器
docker run -it  --rm jdk:centos /bin/bash

# 更改时间
[root@c02302587352 /]# date
Sat Jan  1 01:00:00 UTC 2000
[root@c02302587352 /]# java

这里更改时间没问题,但是更改时间后,直接运行 java 1.8版本 命令是没办法执行,一直处于挂起状态!

再次,采用 jdk16 进行尝试

创建 dockerfile

FROM centos:7
ENV JAVA_HOME=/usr/local/jdk-16.0.2 \
    LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1
ENV PATH=$PATH:$JAVA_HOME/bin \
    CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar \
    FAKETIME="2000-01-01 1:00:00"
COPY ./libfaketime.so.1 /usr/local/lib/faketime/libfaketime.so.1
ADD jdk-16.0.2_linux-x64_bin.tar.gz /usr/local/

运行容器

docker run -it  --rm jdk:centos /bin/bash

执行java命令

>java -version
java version "16.0.2" 2021-07-20
Java(TM) SE Runtime Environment (build 16.0.2+7-67)
Java HotSpot(TM) 64-Bit Server VM (build 16.0.2+7-67, mixed mode, sharing)

>date
Sat Jan  1 01:00:00 UTC 2000

总结

对于 jdk来说,jdk1.8 无法兼容 faketime修改时间,而 jdk16则可以兼容。


— EOF —

原文地址:http://www.cnblogs.com/hukey/p/16926000.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性