在使用 docker 之前我一般都认为容器的技术应该和虚拟机应该差不多,和虚拟机的技术类似,但是事实上容器和虚拟机根本不是一回事。

虚拟机是将虚拟硬件、内核(即操作系统)以及用户空间打包在新虚拟机当中,虚拟机能够利用“虚拟机管理程序”运行在物理设备之上。虚拟机依赖于 hypervisor,其通常被安装在“裸金属”系统硬件之上,这导致hypervisor在某些方面被认为是一种操作系统。一旦 hypervisor 安装完成, 就可以从系统可用计算资源当中分配虚拟机实例了,每台虚拟机都能够获得唯一的操作系统和负载(应用程序)。简言之,虚拟机先需要虚拟一个物理环境,然后构建一个完整的操作系统,再搭建一层Runtime,然后供应用程序运行。

容器其实就是在系统中,利用 namespace 和 cgroup 原本的 linux 特性,将进程隔离在一个封闭的环境中,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”,再通过 veth 让进程的网络环境相隔离,利用 docker 的网桥来进行容器进程之间的相互通信。

这里要说的是容器并没有使用什么很高深的技术,而是将上面所说的 linux 相关的技术进行融合形成容器技术,所以有句话非常正确:

容器本身没有价值,有价值的是“容器编排”。

接下来会从三个方向说一下容器底层所使用到的技术:隔离,限制还有互通。

  

隔离-Namespace机制

首先先创建一个简单的容器:

$ docker run -it busybox /bin/sh
/ #

这里是简单起了一个 busybox 的容器,用 -it 交互的方式来进行 shell

此时在容器内 ps

/ # ps
PID USER TIME COMMAND
1 root 0:00 /bin/sh
10 root 0:00 ps

可以看到,我们在 Docker 里最开始执行的 /bin/sh,就是这个容器内部的第 1 号进程(PID=1),而这个容器里一共只有两个进程在运行。这就意味着,前面执行的 /bin/sh,以及我们刚刚执行的 ps,已经被 Docker 隔离在了一个跟宿主机完全不同的世界当中。

这里的 pid=1 只是 namespace 机制中的映射,实际在宿主机器中

$ ps -ef | grep docker
root 7067 2417 0 16:02 pts/0 00:00:00 docker run -it busybox /bin/sh

这里的 pid=7067

这种技术,就是 Linux 里面的 Namespace 机制。而 Namespace 的使用方式也非常有意思:它其实只是 Linux 创建新进程的一个可选参数。我们知道,在 Linux 系统中创建进程的系统调用是 clone(),比如:

int pid = clone(main_function, stack_size, SIGCHLD, NULL); 

这个系统调用就会为我们创建一个新的进程,并且返回它的进程号 pid。而当我们用 clone() 系统调用创建一个新进程时,就可以在参数中指定 CLONE_NEWPID 参数,比如:

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL); 

这时,新创建的这个进程将会“看到”一个全新的进程空间,在这个进程空间里,它的 PID 是 1。

之所以说“看到”,是因为这只是一个“障眼法”,在宿主机真实的进程空间里,这个进程的 PID 还是真实的数值,比如 100。当然,我们还可以多次执行上面的 clone() 调用,这样就会创建多个 PID Namespace,而每个 Namespace 里的应用进程,都会认为自己是当前容器里的第 1 号进程,它们既看不到宿主机里真正的进程空间,也看不到其他 PID Namespace 里的具体情况。而除了我们刚刚用到的 PID Namespace,Linux 操作系统还提供了 Mount、UTS、IPC、Network 和 User 这些 Namespace,用来对各种不同的进程上下文进行“障眼法”操作。比如,Mount Namespace,用于让被隔离进程只看到当前 Namespace 里的挂载点信息;Network Namespace,用于让被隔离进程看到当前 Namespace 里的网络设备和配置。这,就是 Linux 容器最基本的实现原理了。

userns-remap

在这里还想说一下关于 docker 中使用 userns-remap 做用户命名空间

下面的步骤我是参考 https://blog.csdn.net/zyy247796143/article/details/114386484

使用普通用户运行docker容器,在docker中,在容器内创建的文件在从主机检查时往往具有不可预测的所有权。默认情况下,卷上文件的所有者是root(uid 0),但只要非root用户帐户涉及容器并写入文件系统,所有者就会从主机角度变得或多或少随机 。需要使用调用docker命令的同一用户帐户从主机访问卷数据时,这是一个问题.

典型的解决方法是:

  • 在Dockerfiles中创建时强制用户uID(非可移植)
  • 将主机用户的UID作为环境变量传递给docker run命令,然后在入口点脚本中的卷上运行一些chown 命令
  • 用户命名空间( user namespace)是这个问题的最终解决方案.
    通过 user namespace 技术,把宿主机中的一个普通用户(只有普通权限的用户)映射到容器中的 root 用户。在容器中,该用户在自己的 user namespace 中认为自己就是 root,也具有 root 的各种权限,但是对于宿主机上的资源,它只有很有限的访问权限(普通用户)。

Kernel 内核启用 namespace

grubby --args="namespace.unpriv_enable=1 user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
echo "user.max_user_namespaces=15076" >> /etc/sysctl.conf

重启系统

reboot

创建用户

useradd -u 5000 dku
groupadd -g 500000 dku-root
useradd -u 500000 -g dku-root dku-root

在/etc/subuid 和 /etc/subgid 文件中修改自动生成的从属ID范围:

dku:500000:65536

修改 /etc/docker/daemon.json,新增 "userns-remap"

[root@izm5e37rlunev9ij58ixy9z ~]# cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://registry.docker-cn.com"],
"userns-remap": "dku"
}

访问数据卷文件

假如,在容器里面创建一个 UID 为1000 的 test 用户,他能读取挂载在容器里面的什么权限的文件呢?
测试文件权限:在系统的 /data/test 目录上,创建几个不同的测试文件
1. 一个属主和属组都为 501000 的文件,权限为600,文件名为:501000.txt 。
3. 一个属主和属组都为 dku-root (UID为500000)的文件,权限为600,文件名为:dku-root.txt 。
4. 一个在系统上权限为 root (UID为0)的文件,权限为600,文件名为:root.txt 。
5. 一个在系统上权限为 root (UID为0)的文件,权限为666,文件名为:test.txt 。

6.启动一个centos 7.9 的容器,并在里面创建一个 UID 为 1000 的test 用户,进入挂载的 /test 目录。
查看权限如下:

结果是只有 501000.txt 和 test.txt 这两个文件可以看到里面内容。剩下两个均没有权限。其中501000.txt 有权限,是因为在系统上这个文件的属主和属组都是 501000,其映射在容器里面的属主和属组就是1000,所以UID 为 1000 的 test 用户可以查看里面的内容。

test.txt 能查看,因为文件的权限是666,other 位的权限是 rw,所以其他用户都有权限查看。

这里的实验步骤要复现的话,还需要考虑linux的内核版本,如果低版本的运行 docker 的用户空间会出现这种问题:

docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:319: getting the final child's pid from pipe caused \"EOF\"": unknown.

我用 centos7.6 的是肯定会出现的,貌似这个 error 在 github 上有很多 issues

https://github.com/moby/moby/issues/40835

Docker容器基础入门认知-Namespce的更多相关文章

  1. Docker容器基础入门认知-网络篇

    这篇文章中,会从 docker 中的单机中的 netns 到 veth,再到单机多个容器之间的 bridge 网络交互,最后到跨主机容器之间的 nat 和 vxlan 通信过程,让大家对 docker ...

  2. docker容器基础

    一.docker容器基础6种名称空间:UTS.MOunt.IPC.PID.User.Net (1) Linux Namespaces:namespace 系统调用参数 隔离内容 内核版本  UTS   ...

  3. Docker容器基础认知

    ## 一. [docker](http://www.itxdm.me/archives/tag/docker/)概念 [Docker](http://www.itxdm.me/wp-content/p ...

  4. Docker容器使用 (入门到精通)

    Docker容器 CentOS安装Docker Docker 分为 CE 和 EE 两大版本.CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月. D ...

  5. Docker容器基础知识学习

    Docker作为操作系统层面的轻量级的虚拟化技术,凭借简易的使用.快速的部署以及灵活敏捷的集成等优势,迅速发展目前最为火热的技术. 1.云计算服务是一种资源管理的资源服务,该模式可以实现随时随地.便捷 ...

  6. Kubernetes 普及系列:容器基础入门

    随着云原生时代的来临,云以及分布式计算已经是时下最受欢迎的技术之一了.其中 Docker 作为最知名的容器平台,到底有着怎样的魅力来让其无人不知无人不晓?废话不多说,让我们开始逐层掀开容器技术的神秘面 ...

  7. Docker容器技术入门

    独立容器分开的条件: 1.Namespace命名空间 1.主机名和域名,UTS 2.文件系统,mount 3.进程间通信,IPC 4.独立的进程树PID 5.独立的用户User 6.独立的IP地址tc ...

  8. Docker容器入门到精通

    Docker 容器 快速入门 第一章:Docker容器 第二章:Dockerfile指令与Docker-compose容器编排-搭建docker私有仓库 h1 { color: rgba(0, 60, ...

  9. 1.Docker容器学习之新生入门必备基础知识

    0x00 Docker 快速入门 1.基础介绍 描述:Docker [ˈdɑ:kə(r)] 是一个基于Go语言开发实现的遵循Apache 2.0协议开源项目,目标是实现轻量级的操作系统虚拟化解决方案: ...

  10. docker容器技术基础入门

    目录 docker容器技术基础入门 容器(Container) 传统虚拟化与容器的区别 Linux容器技术 Linux Namespaces CGroups LXC docker基本概念 docker ...

随机推荐

  1. What?构造的查询语句会导致堆栈溢出

    摘要:本文归纳了Neo4j和Nebula两个开源图数据库的两个pull request修复的堆栈溢出问题,并试着写写通过阅读pr中的问题而得到的一些启发 本文分享自华为云社区<巧妙构造的查询语句 ...

  2. 云小课|CDN第5课 CDN入门之—我的网站可以用CDN加速吗?

    摘要:CDN(Content Delivery Network,内容分发网络)通过将源站资源缓存到遍布各地的边缘节点服务器上,用户可以就近获取资源,从而达到加速的效果. 本文分享自华为云社区<[ ...

  3. AI新手语音入门:认识词错率WER与字错率CER

    摘要:本文介绍了词错率WER和字错率CER的概念,引入了编辑距离的概念与计算方法,从而推导得到词错率或字错率的计算方法. 本文分享自华为云社区<新手语音入门(一):认识词错率WER与字错率CER ...

  4. TML转义字符:xss攻击与HTML字符的转义和反转义

    xss与转义符 xss简单来说: XSS 攻击是页面被注入了恶意的代码 XSS 漏洞是 可以让攻击者注入恶意代码可执行的漏洞 具体参看之前写的: web开发前端安全问题总结--web前端安全问题汇总  ...

  5. Java 网络编程 —— 异步通道和异步运算结果

    从 JDK7 开始,引入了表示异步通道的 AsynchronousSockerChannel 类和 AsynchronousServerSocketChannel 类,这两个类的作用与 SocketC ...

  6. sqlalchemy 查询已存在表(无model定义)

    已存在的表信息如下,该表不是由sqlalchemy创建.想要对该表做CRUD,可以通过sqlalchemy提供的一个解析方法完成. 示例代码: from sqlalchemy import creat ...

  7. 初始环境(centos7)

    #!/bin/bash yum -y install wget #更换阿里源 下载epel源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d ...

  8. SE11/SE16N修改表数据

    1.SE11修改方法 首先修改显示格式 选择SE16标准列表 双击这条数据 输入/H,回车,再回车 修改CODE为EDIT,F8 此时,数据已经可以修改了 2.SE16N修改方法 2.1断点修改 输入 ...

  9. 备忘 springboot 整合ehcache,注入CacheManager时提示 required a bean of type 'org.springframework.cache.CacheManager' that could not be found

    问题因人而异,此处仅做备忘 整合过程: 1.添加maven依赖 <dependency> <groupId>net.sf.ehcache</groupId> < ...

  10. 【教程】步兵 cocos2dx 加密和混淆

    文章目录 摘要 引言 正文代码加密具体步骤代码加密具体步骤测试和配置阶段IPA 重签名操作步骤 总结 参考资料 摘要 本篇博客介绍了针对 iOS 应用中的 Lua 代码进行加密和混淆的相关技术.通过对 ...