• 对于注定会优秀的人来说,他所需要的,只是时间----博主
  • 手懒得,必受贫穷,手勤的,必得富足----《圣经》
  • 帮助别人,成就自己。愿君在本站能真正有所收获!
  • 如果你在本站中发现任何问题,欢迎留言指正!
  • 宝剑锋从磨砺出,梅花香自苦寒来!
  • 本站开启了防爆破关小黑屋机制,如果您是正常登录但被关进小黑屋,请联系站长解除!

<九>docker学习笔记–从docker容器时间问题探究到Namespace问题

docker eryajf 5个月前 (06-18) 483°C 已收录 9个评论
本文预计阅读时间 14 分钟

1,容器的时间问题。

在测试环境已经全面使用k8s部署了,今天突然有一个测试同学,因为特殊场景,希望更改一下服务对应的容器时间,当时我心想,这不是挺简单的。

于是就来到容器当中,执行如下命令进行更改:

[root@a-admin-f478dbd55-ddv7x /]# date
Tue Jun 18 14:01:34 CST 2019

[root@a-admin-f478dbd55-ddv7x /]# date -s 05/28
date: cannot set date: Operation not permitted
Tue May 28 00:00:00 CST 2019

[root@a-admin-f478dbd55-ddv7x /]# date
Tue Jun 18 14:01:48 CST 2019

报了一个date: cannot set date: Operation not permitted的错误,而且也没有更改成功。当时还不知道一些深层次的原因,于是开始了搜索之旅。

最先搜索到的,是解决date命令报错的问题:需要在启动容器的时候,加上--cap-add SYS_TIME的参数。

docker run -it --cap-add SYS_TIME --name centos centos /bin/bash

简单说明一下这里添加的参数,深入的后边探讨。

在添加这些特殊权限的时候,可以有如下几种操作:

  • --cap-add:给容器添加某个权限。
  • --cap-drop:去掉容器的某个权限。
  • --privileged:将所有的权限添加给容器。
  • --cap-drop ALL:去掉所有权限。

然后再进入到容器里,重复执行上边改时间的操作,就能成功了。

因为目前使用rancher进行k8s的管理,因此挂载的方式如下,可点击pod升级,在高级选项的最后,有增加内核的选项,选中SYS_TIME,然后点击升级即可。

事实上可能问题到这里,就已经没有问题了,但是接着测试同学发现,时间会自动又变成现在的时间了,以至于无法进行测试,我当时心想,难道说,k8s自己有这样的时间修正机制么,,,

后来想到会不会是定时任务的因素,于是乎,来到了刚刚那个容器所在的主机之上,一看,果然有时间校准的定时任务,每分钟执行一次,于是注释掉,再次更改容器时间,这次才真正的生效,与此同时,发现了另外一个重大问题,那就是容器时间变更为5月28日之后,宿主机的时间也跟着变更了,让我一阵儿惶恐,尽管这是在测试环境,宿主机的时间更改可也不是闹着玩的呀

到后来才知道,原来上边操作的 --cap-add SYS_TIME是为了将宿主机的内核时间挂载进来与容器共享,因此容器时间更改了,宿主机时间也会跟着更改。说到底,还是对docker理解不深的缘故,但是借着这个机缘,倒是静下心来,看了如下的一些资料,让自己对容器技术有了新的一层认识。

2,docker之Namespace

  • 内核为容器提供了两种技术cgroupnamespace,分别对容器进行资源限制资源隔离
    • 容器本质是进程,cgroup用来限制容器的资源使用量,避免单个容器耗尽系统资源。
    • namespace用来隔离容器与宿主机,以及不同的容器。

这里,就先针对namespace这个概念进行一下探析。

Namespace 系统调用参数 隔离内容 内核版本
UTS CLONE_NEWUTS 主机名与域名 2.6.19
IPC CLONE_NEWIPC 信号量,消息队列和共享内存 2.6.19
PID CLONE_NEWPID 进程编号 2.6.24
Network CLONE_NEWNET 网络设备,网络栈,端口等 2.6.29
Mount CLONE_NEWNS 挂载点(文件系统) 2.4.19
User CLONE_NEWUSER 用户和用户组 3.8

如上列出的这六项,是目前构建一个容器所默认赋予的基础能力,可以有别于原宿主机而在容器当中单独存在(当然还是基于宿主机),这个时候就能够理解,上边改时间的操作之所以失败,就是因为默认情况下,容器是没有Time(时间)这个Namespace的,通过--cap-add SYS_TIME挂载也是引用了系统的内核时间,因此改了容器时间之后,系统时间跟着变化,也就不稀奇了。

1,UTS namespace

UTS(UNIX Time-sharing System)namespace提供了主机名与域名的隔离,这样每个docke容器就可以拥有独立的主机名和域名了,在网络上可以被视为一个独立的节点,而非宿主机上的一个进程。

docker中,每个镜像基本都以自身所提供的服务名称来命名镜像的hostname,且不会对宿主机产生任何影响,其原理就是使用了UTS namespace。

2,IPC namespace

进程间通信(Inter-Process Communication,IPC)涉及的IPC资源包括常见的信号量、消息队列和共享内存。申请IPC资源就申请了一个全局唯一的32位ID,所以IPC namespace中实际上包含了系统IPC标识符以及实现POSIX消息队列的文件系统。在同一个IPC namespace下的进程彼此可见,不同IPC namespace下的进程则互相不可见。

目前使用IPC namespace机制的系统不多,其中比较有名的有PostgreSQL。Docker当前也使用IPC namespace实现了容器与宿主机、容器与容器之间的IPC隔离。

3,PID namespace

PID namespace隔离非常实用,它对进程PID重新标号,即两个不同namespace下的进程可以有相同的PID。每个PID namespace都有自己的计数程序。内核为所有的PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,被称为root namespace,它创建的心PID namespace被称为child namespace(树的子节点),而原先的PID namespace就是新创建的PID namespace的parent namespace(树的父节点)。通过这种方式,不同的PID namespace会形成一个层级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响。反过来,子节点却不能看到父节点PID namespace中的任何内容,由此产生如下结论。

  • 每个PID namespace中的第一个进程“PID 1”,都会像全通Linux中的init进程一样拥有特权,其特殊作用。
  • 一个namespace中的进程,不可能通过kill或ptrace影响父节点或者兄弟节点中的进程,因为其他几点的PID在这个namespace没有任何意义。
  • 如果你在新的PID namespace中重新挂载/proc文件系统,会发现其下只显示同属一个PID namespace中的其他进程。
  • 在root namespace中看到所有的进程,并且递归包含所有子节点中的进程。到这里,读者可能已经联想到了一种在Docker外部监控运行程序的方法了,就是监控Docker daemon所在的PID namespace下的所有进程及子进程,在进行筛选即可。

4,mount namespace

mount namespace通过隔离文件系统挂载点对隔离文件系统提供支持,它是历史上第一个Linux namespace,所以标示位比较特殊,就是CLONE_NEWNS。隔离后,不同的mount namespace中的文件结构发生变化也互不影响。也可以通过/proc/[pid]/mounts查看到所有挂载在当前namespace中的文件系统,还可以通过/proc/[pid]/mountstats看到mount namespace中文件设备的统计信息,包括挂载文件的名字、文件系统的类型、挂载位置等。

进程在创建mount namespace时,会把当前的文件结构复制给新的namespace。新namespace中的所有mount操作都只影响自身的文件系统,对外界不会产生任何影响。这种做法非常严格的实现了隔离,但对某些状况可能并不适用。比如父节点namespace中的进程挂载了一张CD-ROM,这时子节点namespace复制的目录结构是无法自动挂载上这张CD-ROM的,因为这种操作会影响到父节点的文件系统。

一个挂载状态可能为以下一种:

  • 共享挂载
  • 从属挂载
  • 共享/从属挂载
  • 私有挂载
  • 不可绑定挂载

传播事件的挂载对象称为共享挂载;接收传播事件的挂载对象称为从属挂载;同时兼有前述两者特征的挂载对象为共享/从属挂载;既不传播也不接受事件的挂载对象称为私有挂载;另一种特殊的挂载对象称为不可绑定挂载,它们与私有挂载相似,但不允许执行绑定挂载,即创建mount namespace时这块文件对象不可被复制。

5,netword namespace

network namespace主要提供了关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表、防火墙、/proc/net目录、/sys/class/net目录、socket等。一个物理的网络设备最多存在于一个network namespace中,可以通过创建veth pair(虚拟网络设备对:有两端,类似管道,如果数据从一端传入另一端也能接受,反之亦然)在不同的network namespace间创建通道,以达到通信目的。

也许你会好奇,在建立起veth pair之前,新旧namespace该如何通信呢?答案是pipe(管道)。以Docker daemon启动容器的过程为例,假设容器内初始化的进程称为init。Docker daemon在宿主机上负责创建这个veth pair,把一段绑定到docker0网桥上,另一端介入新建的network namespace进程中。这个过程执行期间,Docker daemon和init就通过pipe进行通信。具体来说,就是在Docker deamon完成veth pair的创建之前,init在管道的另一端循环等待,直到管道另一端传来Docker daemon关于veth设备的信息,并关闭管道。init才结束等待的过程,并把它的“eth0”启动起来。

与其他namespace类似,对network namespace的使用其实就是在创建的时候添加CLONE_NEWNET标识符位。

6,user namespace

user namespace主要隔离了安全相关的标识符(identifier)和属性(attribute),包括用户ID、用户组ID、root目录、key(指密钥)以及特殊权限。通俗地讲,一个普通用户的进程通过clone()创建的新进程在新user namespace中可以拥有不同的用户和用户组。这意味着一个进程在容器外属于一个没有特权的普通用户,但是它创建的容器进程却属于拥有所有权限的超级用户,这个技术为容器提供了极大的自由。

user namespace时目前的6个namespace中最后一个支持的,并且直到linux内核3.8版本的时候还未完全实现(还有部分文件系统不支持)。user namespace实际上并不算完全成熟,很多发行版担心安全问题,在编译内核的时候并未开启USER_NS。Docker在1.10版本中对user namespace进行了支持。只要用户在启动Docker daemon的时候制定了–user-remap,那么当用户运行容器时,容器内部的root用户并不等于宿主机的root用户,而是映射到宿主机上的普通用户。

Docker不仅使用了user namespace,还使用了在user namespace中涉及的Capability机制。从内核2.2版本开始,Linux把原来和超级用户相关的高级权限分为不同的单元,称为Capability。这样管理员就可以独立的对特定的Capability进行使用或禁止。Docker同时使用namespace和Capability,这很大程度上加强了容器的安全性。

这些似乎都是docker相关的基础性概念,过去也许并非没有看过,但都一带而过没有留下印象,若非今日时间问题这个机缘,恐怕自己也还一直不懂得这些原理,有时候学习就是如此,要看缘分。重要的是,如果缘分来了,不要错失!

3,延伸

1,参考地址

2,扩展阅读


weinxin
扫码订阅本站,第一时间获得更新
微信扫描二维码,订阅我们网站的动态,另外不定时发送WordPress小技巧,你可以随时退订,欢迎订阅哦~

二丫讲梵 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明<九>docker学习笔记–从docker容器时间问题探究到Namespace问题
喜欢 (1)
[如果想支持本站,可支付宝赞助]
分享 (0)
eryajf
关于作者:
学无止境,我愿意无止境学。书山有路,我愿意举身投火,淬炼成金!

您必须 登录 才能发表评论!

(9)个小伙伴在吐槽
  1. chengkanghua
    时间问题怎么解决的啊?
    chengkanghua2019-06-19 09:54 Windows 10 | Chrome 75.0.3770.100
    • eryajf
      嗯哼,文中不是可已经给出方案了么,你说的怎么解决指的是?
      eryajf2019-06-19 09:55 Windows 7 | Chrome 74.0.3729.169
      • chengkanghua
        后面不是不是容器时间更改了,宿主机时间也会变更吗? 之后的内容看不太明白,太理论了
        chengkanghua2019-06-19 10:07 Windows 10 | Chrome 75.0.3770.100
        • eryajf
          你可以先跟着操作一下,然后慢慢体会,其实之前我看到这类理论的东西,也都是头疼的,但是结合实际场景之后,理解起来会好很多
          eryajf2019-06-19 10:08 Windows 7 | Chrome 74.0.3729.169
  2. chengkanghua
    仅修改docker容器中的时间,而不影响宿主机的时间https://www.cnblogs.com/chengkanghua/p/11058660.html
    chengkanghua2019-06-20 14:46 Windows 10 | Firefox浏览器 67.0
    • eryajf
      尝试了这种方案,发现退出容器之后,时间就又跑了。另,看到你那里搬运了我的压缩那篇文章,但没看到有搬运连接啊老哥
      eryajf2019-06-20 15:52 Windows 7 | Chrome 74.0.3729.169
      • chengkanghua
        我加上
        chengkanghua2019-06-20 19:29 Windows 10 | Firefox浏览器 67.0
      • chengkanghua
        容器是一直放在后台运行的,我测试没发现时间跑的情况呢
        chengkanghua2019-06-20 19:32 Windows 10 | Firefox浏览器 67.0
        • eryajf
          放在后台是什么意思呢,不是很理解,我猜想应该是这样,你是把改时间的指令放进了Dockerfile里边了吧,这样容器在启动的时候就加载了这条配置,而我是在一个正常的容器中,执行了这条指令,然后退出容器,再次进入,时间就又与当前同步了
          eryajf2019-06-21 09:47 Windows 7 | Chrome 74.0.3729.169