现在云原生技术能够说是互联网最火的概念之一,作为云原生技术的重要基石 —“容器”技术,想必从业者早有学习并大量的使用在工作和日常开发中。
如果有人问你“什么是容器?”,你的答案是什么?
容器就是 Docker 吗?它和虚拟化技术有什么区别和分割?它的实现原理又是什么?
这篇文章次要通过 容器和虚拟化技术的比照,容器和 Linux Namespace 的关系,以及 Namespace 的初体验 三个模块让大家更理解什么是容器,以及容器实现的底层技术撑持 — Liunx Namespace。
容器和虚拟化
有 Linux 学习教训的同学,想必有应用 VMware 和 VirtualBox 这种软件来装置 Linux 虚拟机的经验。个别是这样的流程,咱们先在 windows 机器上装置 VMware 或者 VirtualBox 这类 Hypervisor 虚拟化软件,而后应用这类虚拟化软件将 Linux 的某种发行版镜像装置成为一个 Linux 虚拟机,这样咱们就领有了一个 Linux 操作系统。
这种虚拟化软件是一种运行在根底物理服务器和操作系统之间的两头软件层,可容许多个操作系统和利用共享硬件,通过这种形式咱们失去的虚拟机软硬件、内核一应俱全,能够说是性能齐备的操作系统,它和宿主机隔离,咱们在操作系统里能够放心大胆的做任何的批改,而不必放心对宿主机造成侵害,这中资源的隔离型和性能的相似性,和容器表象上能够说是特地的类似。
刚开始接触 Linux 容器的我,习惯性的拿容器和虚拟机比照,认为容器就是一种轻量级的虚拟化技术,只是少了硬件资源的虚拟化,外加运行应用程序所必须的最小的文件系统。不晓得有多少人也曾有过和我一样的了解,不得不说这种了解的确对刚接触容器时,了解容器有不少的帮忙,然而这种说法却是不妥的,因为容器的实现机制和 Hypervisor 这种虚拟化技术是有实质却别的。
容器和 Namespace
容器实质上只是 Linux 上运行的非凡的过程,之所以说非凡,是因为它和操作系统上的其余过程环境进行了隔离,就像一个集装箱一样,站在容器外面,只能看到它自身。容器技术的根底是 Linux namespace 和 cgroups 内核个性。
隔离的个性确保了不同容器过程互不烦扰,领有各自独立的运行环境。咱们晓得,在操作系统上 PID 这种货色是惟一的,咱们无奈领有一个 PID = 1 的过程 A,同时领有一个 PID = 1 的过程 B;端口号也是惟一的,咱们无奈让多个过程同时监听同一个端口(fork 子过程的非凡形式除外)。咱们也无奈让操作系统 hostname 为 server1, 同时让 hostname 为 server2。然而咱们能够在容器 A 里存在一个过程他的 PID = 1,同时容器 B 里也存在一个过程他的 PID 也等于 1,这就是容器隔离性的体现,它本人就是本人的全世界,开拓任意的空间(本人的文件系统),领有本人的网络设备,领有本人的 hostname。
这种隔离性依附的就是内核个性 — Namespace,Namespace 能够让一个过程运行在独立的命名空间中,命名空间里的过程和零碎过程互相隔离,不同命名空间中的过程之间互相隔离,所以咱们须要理解容器,就须要先理解下 Namespace.
namespace 是内核 2.4 开始有的个性,namespace 一共有如下几种:
Namespace | 隔离的资源 |
---|---|
Cgroup | Cgroup root directory |
IPC | System V IPC,POSIX message queues |
Network | 网络设备,端口 |
Mount | 挂载点 |
PID | 过程 ID |
Time | 时钟 |
User | 用户和用户组 ID |
UTS | 主机名、域名 |
须要留神的是以上的类型的 namespace 是从内核 2.4 开始逐渐退出的,2.4 实现了 mount,2.6 退出了 IPC、Network、PID、和 UTS,User 则是从 2.6 开始呈现,但到了 3.8 才发表实现,Cgroup 则是在 4.6 中才有
下面咱们提到的,不同容器中能够领有雷同的 PID,就是因为 PID 这种隔离个性的 namespace, User 和 UTS 这种隔离个性的 namespace 则能够让同一台主机上的不同容器具备雷同的 user id、group id 和 主机名。Namespace 这种内核个性,就是让一组过程运行在一个独立的空间中,让其和操作系统上的过程隔离。
namespace 初体验
置信很多敌人第一次接触 namespace 也是一脸懵逼,没关系,咱们能够间接上手对 namespace 来个直观的感触
以下的操作在 ubuntu16.03,内核被动降级到了 5.8.12-050812-generic 的零碎中实现的
开始之前,须要阐明的是 Linux 提供了诸如 clone、setns、unshare、ioctl 这些零碎调用 API 来实现对 Namespace 和其过程的操作,不过这是后话,本次初体验咱们是应用 Linux 中一个和零碎调用函数 unshare 同名的命令行工具(它实际上是调用了零碎调用 unshare)
看看它的用法:
# unshare -h
Usage:
unshare [options] [<program> [<argument>...]]
Run a program with some namespaces unshared from the parent.
Options:
-m, --mount[=<file>] unshare mounts namespace
-u, --uts[=<file>] unshare UTS namespace (hostname etc)
-i, --ipc[=<file>] unshare System V IPC namespace
-n, --net[=<file>] unshare network namespace
-p, --pid[=<file>] unshare pid namespace
-U, --user[=<file>] unshare user namespace
-C, --cgroup[=<file>] unshare cgroup namespace
-f, --fork fork before launching <program>
--mount-proc[=<dir>] mount proc filesystem first (implies --mount)
-r, --map-root-user map current user to root (implies --user)
--propagation slave|shared|private|unchanged
modify mount propagation in mount namespace
-s, --setgroups allow|deny control the setgroups syscall in user namespaces
-h, --help display this help
-V, --version display version
UTS Namespace 初体验
UTS Namespace 能够实现 hostname 和 domainname 的隔离
# hostname # 查看以后的 hostname 是 server0
server0
# unshare -u /bin/sh # 创立一个新的过程,并处于 UTS namespace 中
# hostname new-hostname # 批改 hostname
# hostname # 以后过程的 hostname 曾经扭转
new-hostname
# exit # 退出 UTS namespace
# hostname # 能够确定本来的 hostname 并未扭转
server0
通过下面的例子咱们能够看到 UTS Namespace 的隔离个性,它能够让过程领有本人独立的 hostname,位于 UTS Namespace 中的过程,批改 hostname 不会影响到主机本来的 hostname.
PID Namespace 初体验
PID Namespace 能够实现 PID 的隔离
# ps -aux |wc -l # 零碎本来的过程数
146
# unshare -u /bin/sh # 没有应用 -p 参数
# ps -aux |wc -l # UTS Namespace 没有 pid 的隔离
147
# exit
# unshare -fp --mount-proc /bin/bash # PID Namespace, --mount-proc 选项以确保外壳程序将在新创建的名称空间中取得 PID 1, - f 标记从 unshare 以下地位派生 shell
# ps -aux |wc -l
4
通过下面的例子,咱们特意比照了 UTS 和 PID 两种 namespace,能够更加直观的理解到 PID Namespace 的隔离个性。位于 PID Namespace 中的过程,它能够领有和主机 PID 反复的过程。
Network Namespace 初体验
Network 能够进行网络的隔离
# ifconfig -a |grep flags # 本来主机的网卡数量
br-0218842a6d4f: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
br-9a4009fc0074: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
br-ccc197daa6b7: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
vethb960cbb3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
vethe6e2d00a: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
# unshare -n /bin/bash # 启动 Network Namespace
# ifconfig -a # 启动 Network Namespace 只有 lo 这种网络设备
lo: flags=8<LOOPBACK> mtu 65536
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
通过上例咱们能够看到,主机的网卡有多个,而位于 Network Namespace 中的过程只有一个 lo 网卡,网络设备和主机的网路设施进行了隔离。
通过以上的例子,置信大家对 Namespace 的隔离个性有了很直观的理解,容器技术的隔离性质正是基于 Namespace 这种内核个性实现,是一种内核级别的个性。
下面通过 unshare 指令咱们对 Namespace 有了初步的体验,置信大家对容器的隔离实现也会有了不同的意识。下一个章节,我会用代码的形式操作 Namespace, 让大家对 Namespace 有更加深刻的理解,感兴趣的敌人,请关注公众号“思图邦”,及时接管更新。
当然咱们晓得容器除了资源的隔离,还有一大个性是资源的限度,其资源的限度则是依赖于 cgroups 这一内核个性,之后的章节我也会对 cgroups 开展做一个阐明,感激关注。
公众号:思图邦
感激大家浏览,如果有问题或须要补充的,请在评论区留言斧正。