Docker 还须要学习吗?

去年中在 Hacker News 上有篇热帖 “Goodbye Docker: Purging is Such Sweet Sorrow” 这篇文章内容其实很常规,无非是本身使用 Docker 的时候遇到了一些问题,最后切换到了 Podman buildahSkopeo 组合的工具集,以做为 Docker 的一种替代方案。git

这样的文章在近一年左右应该算是比较常见了,但为什么会成为 Hacker News 上的热帖呢?主要有两方面的缘由: 1. 仍是由于此文的做者是 Ian Miell 他是 Docker in Practice (中译本叫作 《Docker 实践》)的做者,一个 Docker 相关技术书籍的做者将本身的 Docker 给替换掉,会让人比较好奇;2. 替换 Docker 貌似是一种方向,多数人也想要了解这种技术变迁会带来什么优点或者有什么样的坑。github

背景介绍完了,咱们来开始正文。docker

容器时代

Docker 在 2013 年 PyCon 上首次亮相,随后开源。因为其简单易用,以及切实解决了因环境不一致致使的问题,迅速得到到一大批粉丝。shell

接下来的几年中,Docker 改变了软件的交付方式,更多的人为之着迷。随之而来的是 Docker 生态的蓬勃发展。json

Docker 在大多数人眼中几乎是容器(container)的代名词,即便是如今我也会常听到有人说“我有几个 docker 跑 xx 服务” 相似这样的话,无疑 Docker 引领了容器的时代安全

容器是什么

一直在提容器,咱们不如深刻点先来探究下容器究竟是什么?bash

Docker 官网上对容器的描述是: “A standardized unit of software” -- 软件的标准单元,并无什么更详细的内容了。多数人对容器的见解也都停留在很浅显的认识:认为容器是轻量级的虚拟机,因此后来也就有一段时间有人推“富容器”技术。网络

这里我想更深刻一些,可能会涉及一些容器的历史,但我认为这些内容有助于读者理解个人角度和观点。架构

说白了 容器实际上是在某台机器上的“一组”进程,固然这组进程可能只有一个;它们有相同的特性,固然所受的限制也是相同的;既然叫作容器,很天然的咱们认为它们与外界能够进行隔离/应该有一个分界线。工具

谈完了它的基本特性,那咱们来看看如何来建立一个容器。

chroot

若是你对 Linux 相对熟悉的话,你可能知道 chroot ,咱们有时会使用 chroot 改变某进程的根目录,而且它不能访问该目录以外的其余目录。

这个和咱们在一个容器内的感受很像了。事实上在几年前确实有人用一百多行的 bash shell 利用 chroot 写了一个模拟 Docker 建立容器的实现,称之为 bocker , 有兴趣的朋友能够去看看该项目的代码。

接下来咱们使用 chroot 建立一个隔离环境:

首先它须要有一个 rootfs 的根文件系统,咱们能够很简单的使用 Docker 得到咱们须要的内容。

(MoeLove) ➜  ~ mkdir chroot-dir
(MoeLove) ➜  ~ cd chroot-dir
(MoeLove) ➜  chroot-dir docker save -o debian.tar debian:buster                                          
(MoeLove) ➜  chroot-dir ls
debian.tar
(MoeLove) ➜  chroot-dir tar -xf debian.tar 
(MoeLove) ➜  chroot-dir ls
098963abf3c3b87b8114ff67d164097dfac2d5659e39f9beb5604db91585f375.json  debian.tar     repositories
0f28619fe69181d3af529d56692f1362b7a7c8a6bf8dc9ab0d6d4f9ef9b0004d       manifest.json
(MoeLove) ➜  chroot-dir mkdir -p debian
(MoeLove) ➜  chroot-dir tar -C debian -xf 0f28619fe69181d3af529d56692f1362b7a7c8a6bf8dc9ab0d6d4f9ef9b0004d/layer.tar  
(MoeLove) ➜  chroot-dir ls debian
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
复制代码

能够看到咱们经过对镜像文件的解压,获得了咱们所需的 rootfs 的所有内容,接下来咱们看看 chroot 的能力:

(MoeLove) ➜  chroot sudo chroot debian /bin/bash -i
[sudo] tao 的密码:
root@localhost:/# whoami 
root
root@localhost:/# cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
复制代码

咱们目前就已经在一个 "容器" 内了,咱们来看下使用这个容器咱们能作些什么。

首先咱们看看当前“容器”内的路由表:

root@localhost:/# mkdir -p /sys
root@localhost:/# mount -t sysfs sys /sys
root@localhost:/# ip r
default via 192.168.0.1 dev wlp2s0 proto dhcp metric 600 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.0.0/24 dev wlp2s0 proto kernel scope link src 192.168.0.108 metric 600 
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
复制代码

很天然的,咱们发现它能够访问全部的网络设备

root@localhost:/# ls /sys/class/net/
docker0  enp1s0  lo  vboxnet0  virbr0  virbr0-nic  wlp2s0
复制代码

另外,咱们还能够将 /proc 也挂载进去

root@localhost:/# mkdir -p /proc
root@localhost:/# mount -t proc proc /proc
root@localhost:/# ls -al /proc/31730/ns/pid
lrwxrwxrwx. 1 1000 1000 0 Jul 29 16:47 /proc/31730/ns/pid -> 'pid:[4026531836]'
复制代码

能够看到咱们在这个“容器”内,能够访问到主机上的进程及网络等信息,这表示没有任何的进程或者网络隔离,这带来的危险是咱们甚至能够在容器内杀掉容器外的进程,或者经过容器来攻击主机。

为了能更好的解决这个问题,接下来出现了另外一个技术:namespace 。

namespace

Namespace 是在 2002 年由 Linux 2.4.19 开始加入内核的特性,它主要的做用就作了一层抽象和隔离,使得在 namespace 中的进程/进程组能够看起来拥有本身的独立资源,具体的“资源”表现形式取决于给它赋予了哪些 namespace 。

随着 2013 年 Linux 3.8 中 user namespace 的引入,对于咱们如今所熟知的容器所需的所有 namespace 就都实现了:mnt pid net ipc uts usercgroup 对于这些 namespace 和 Docker 的关系,咱们稍后会逐步来看,这里先对 namespace 作下介绍,而后继续前面"容器"的内容。

咱们能够经过三个系统调用直接操做 namespace ,这三个系统调用分别是:

  • clone: 能够经过传递不一样 namespace 的标志来为新的(子)进程指定其所属的 namespace;
  • unshare: 容许一个进程(或线程)取消当前与其余进程(或线程)共享的执行上下文;
  • setns: 进入文件描述符指定的 namespace;

知道了这些基础知识后,咱们回到前面"容器"的内容中。

咱们在前面 chroot 的例子中看到没能作到进程或网络隔离,如今咱们来试试看用 namespace 完成该需求。

(MoeLove) ➜  ~ sudo unshare -fp --mount-proc -n
[sudo] tao 的密码:
[root@localhost]/home/tao# ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@localhost]/home/tao# ps -a
  PID TTY          TIME CMD
    1 pts/15   00:00:00 zsh
   33 pts/15   00:00:00 ps
[root@localhost]/home/tao# 
复制代码

这里很明显,咱们当前所在进程的 PID 为 1 而且看不到宿主机上的其余进程,咱们达到了基础的隔离效果。网络也一样,如今只包含一个 lo 接口。关于网络部分的内容其实能聊的东西还有不少,我在专栏《Docker 核心知识必知必会》中,用 8 节内容来深刻的聊了聊 Docker 容器网络相关的内容,感兴趣的读者能够看看。

小结

上面聊了这么多,无非是想说明容器的发展历程,以及它是什么。它其实就是用了各类 Linux 内核提供的特性/功能协同实现的对进程组的资源隔离。用 Docker 或者 namespace 或者 chroot 之类的均可以造出来一个 “容器” ,那咱们为何要用 Docker 呢?

Docker 崛起之路

想必你们对 cgroups 不会太陌生,它在 2008 年进入 Linux 2.6.24 后,基于它而且瞄准容器世界的一个项目诞生了。

Linux Container (LXC)结合了 namespace 和 cgroups 等技术,目标就是要创造出运行在 Linux 系统中,而且隔离性良好的容器环境。

LXC 的事情发生在 2008 年,但值得注意的是 cgroups 最初是由 Google 的工程师开发的,最先的记录是在 2006 年,事实上当时 Google 确实也在作相似的容器化项目。

时间一晃而过,就到了 2013 年的 PyCon 上,在此次大会上 Docker 正式面世。而它当时其实也只是构建在 LXC 之上的一个工具,屏蔽掉了 LXC 的使用细节,让用户能够一句 docker run 便建立出本身的容器环境。 同时,它容许用户将容器环境打包成为一个 Docker 镜像进行分发,这也大大下降了用户使用的门槛。 Docker 镜像分发能够说是 Docker 成功的一个关键要素了。

2014 年 Docker 发布 1.0 正式进入生产就绪的状态。在此以前它也将 LXC 逐步从它的底层移除,换成了本身实现的 libcontainer ,幸运的是我也在 0.9 版本时开始了个人 Docker 之路。

此后 Docker 便成为了风靡技术界的新热潮。

了解了 Docker 的发展背景后,咱们来看看前面咱们提到的问题:为何要用 Docker 呢?

2008 年到 2013 年这之间大约 5 年左右的时间,以 LXC 为首的容器技术并无获得相似 Docker 出现后那么普遍的普及,我在以前的线下演讲中也提到过这个点,最主要的缘由在于 LXC 太偏向技术了,使用 LXC 有必定的门槛,致使了好多人的退却。

Docker 则提供了简单易用的 CLI , 优雅灵活的容器生命周期管理,以及镜像的构建,分发等配套设施,这为后期的推动提供了不少的便利。

再加上 Docker 的策略很好,以及在 Docker 公司内部也有大量的实践经验,因此这些事情作起来也都很顺畅了。(关于 Docker 公司的实践经验能够说是信息量巨大了,之后再看机会分享吧)

使用 Docker 面临的问题

前面分别聊了容器的发展历程,以及 Docker 的发展历程。咱们要正视 Docker 是一个已经 7 岁的项目,自它 1.0 发布以来已经 6 年之久了,它在生产环境中已经获得了大量的实践和验证。固然不可避免的它也会存在一些历史遗留问题或者是软件 bug 。

尤为是随着 K8S 的发展,国内外大量公司都在落地 K8S。其中有超过半数的公司都是使用 Docker 做为容器运行的。

当发生故障或者异常时,有些人可能会一筹莫展,或是只从上层进行问题的排查,却不知问题极可能发生在底层的容器运行时。比较典型的例子:好比说容器 Hang 住,或者是没法建立容器等。

或者有人因为对 Docker 网络相关的方面不了解,因此在学习和使用 K8S 时,也会走很多的弯路。比较典型的例子:不理解 K8S 中数据包的流向。

最后一个很重要的方面就是安全相关的了。在企业中使用 Docker 尤为须要注意。这里包含着不少信息:好比容器运行时安全,镜像安全,容器策略安全等,我在专栏的安全篇也都进行了介绍。

总结

Docker 的上手使用很是简单,这是 Docker 的一大优点,旨在下降开发者的使用门槛。尤为是 Docker Desktop 提供了交互式 UI,用户能够经过鼠标点击就完成容器的相关操做和管理。

可是 Docker 做为一个正在被大量使用,且日后会被应用更多的技术,若是要用在生产环境中,我建议读者去更深刻的学习和掌握它。

以便在遇到问题时,能够更快速的定位和解决;构建镜像时,能够更加高效;使用 Docker 时,能为企业提供更适宜的安全策略。

固然,还有一个最重要的,当你对 Docker 的了解越深刻时,你也会越开心,你能接触到更多有趣的知识和技能。


推荐我耗时6个月完成的专栏: 《Docker 核心知识必知必会》, 从容器,镜像,Docker 架构,安全,网络等方面深刻介绍 Docker 核心知识及应用实践。

相关文章
相关标签/搜索