共计 5148 个字符,预计需要花费 13 分钟才能阅读完成。
摘要:Docker 为什么火,靠的就是 Docker 镜像。他打包了应用程序的所有依赖,彻底解决了环境的一致性问题,从新定义了软件的交付形式,进步了生产效率。
本文分享自华为云社区《意识容器,咱们从它的历史开始聊起》,作者:技术火炬手。
对于容器的历史、倒退以及技术实质,在互联网上曾经有十分多的文章了。这里旨在联合本身的工作教训和了解,通过一系列的文章,讲清楚这项技术。
容器的历史和倒退
1、前世
讲到容器,就不得不提 LXC(Linux Container),他是 Docker 的前生,或者说 Docker 是 LXC 的使用者。残缺的 LXC 能力在 2008 年合入 Linux 主线,所以容器的概念在 2008 年就根本定型了,并不是前面 Docker 造出来的。对于 LXC 的介绍很多,大体都会说“LXC 是 Linux 内核提供的容器技术,能提供轻量级的虚拟化能力,能隔离过程和资源”,但总结起来,无外乎就两大知识点 Cgroups(Linux Control Group)和 Linux Namespace。搞清楚他俩,容器技术就根本把握了。
- Cgroups:重点在“限度”。限度资源的应用,包含 CPU、内存、磁盘的应用,体现出对资源的治理能力。
- Namespace:重点在“隔离”。隔离过程看到的 Linux 视图。说大白话就是,容器和容器之间不要相互影响,容器和宿主机之间不要相互影响。
2、少年期起步艰巨
2009 年,Cloud Foundry 基于 LXC 实现了对容器的操作,该我的项目取名为 Warden。2010 年,dotCloud 公司同样基于 LXC 技术,应用 Go 语言实现了一款容器引擎,也就是当初的 Docker。那时,dotCloud 公司还是个小公司,出世低微的 Docker 没什么热度,活得相当艰巨。
3、成长为巨无霸
2013 年,dotCloud 公司决定将 Docker 开源。开源后,我的项目忽然就火了。从大的说,火的起因就是 Docker 的这句口号“Build once,Run AnyWhere”。呵呵,是不是似曾相识?对的,和 Java 的 Write Once,Run AnyWhere 一个情理。对于一个程序员来说,程序写完后打包成镜像就能够随处部署和运行,开发、测试和生产环境完全一致,这是如许大一个引诱。程序员再也不必去定位因环境差别导致的各种坑爹问题。
Docker 开源我的项目的异样火爆,间接驱动 dotCloud 公司在 2013 年更名为 Docker 公司。Docker 也疾速成长,干掉了 CoreOS 公司的 rkt 容器和 Google 的 lmctfy 容器,间接变成了容器的事实标准。也就有了起初人一提到容器就认为是 Docker。
总结起来,Docker 为什么火,靠的就是 Docker 镜像。他打包了应用程序的所有依赖,彻底解决了环境的一致性问题,从新定义了软件的交付形式,进步了生产效率。
4、被列强鲸吞
Docker 在容器畛域疾速成长,野心天然也变大了。2014 年推出了容器云产品 Swarm(K8s 的同类产品),想扩张事业幅员。同时 Docker 在开源社区领有相对话语权,相当强势。这种走本人的路,让他人无路可走的行为,让容器畛域的其余大厂玩家很是不爽,为了不让 Docker 一家独大,决定要干他。
2015 年 6 月,在 Google、Redhat 等大厂的“运作”下,Linux 基金会成立了 OCI(Open Container Initiative)组织,旨在围绕容器格局和运行时制订一个凋谢的工业化规范,也就是咱们常说的 OCI 规范。同时,Docker 公司将 Libcontainer 模块捐给 CNCF 社区,作为 OCI 规范的实现,这就是当初的 RunC 我的项目。说白了,就是当初这块儿有个规范了,大家一起玩儿,不被某个特定我的项目的绑定。
讲到 Docker,就得说说 Google 家的 Kubernetes,他作为容器云平台的事实标准,现在已被宽泛应用,俨然已成为大厂标配。Kubernetes 原生反对 Docker,让 Docker 的市场占有率始终居高不下。如图是 2019 年容器运行时的市场占有率。
但在 2020 年,Kubernetes 忽然发表在 1.20 版本当前,也就是 2021 年当前,不再反对 Docker 作为默认的容器运行时,将在代码骨干中去除 dockershim。
如图所示,K8s 本身定义了规范的容器运行时接口 CRI(Container Runtime Interface),目标是能对接任何实现了 CRI 接口的容器运行时。在初期,Docker 是容器运行时不容置疑的王者,K8s 便内置了对 Docker 的反对,通过 dockershim 来实现规范 CRI 接口到 Docker 接口的适配,以此取得更多的用户。随着开源的容器运行时 Containerd(实现了 CRI 接口,同样由 Docker 捐给 CNCF)的成熟,K8s 不再保护 dockershim,仅负责保护规范的 CRI,解除与某特定容器运行时的绑定。当然,也不是 K8s 不反对 Docker 了,只是 dockershim 谁保护的问题。随着 K8s 态度的变动,预计将会有越来越多的开发者抉择间接与开源的 Containerd 对接,Docker 公司和 Docker 开源我的项目(现已改名为 moby)将来将会产生什么样的变动,谁也说不好。
讲到这里,不晓得大家有没有留神到,Docker 公司其实是募捐了 Containerd 和 runC。这俩到底是啥货色。简略的说,runC 是 OCI 规范的实现,也叫 OCI 运行时,是真正负责操作容器的。Containerd 对外提供接口,治理、管制着 runC。所以下面的图,真正应该长这样。
Docker 公司是一个典型的小公司因一个爆款我的项目火起来的案例,不论是技术层面、公司经营层面以及如何跟大厂缠斗,不论是好的方面还是坏的方面,都值得咱们去学习和理解其背地的故事。
什么是容器
按国际惯例,在介绍一个新概念的时候,都得从大家相熟的货色说起。幸好容器这个概念还算好了解,喝水的杯子,洗脚的桶,养鱼的缸都是容器。容器技术外面的“容器”也是相似概念,只是装的货色不同罢了,他装的是应用软件自身以及软件运行起来须要的依赖。用鱼缸来类比,鱼缸这个容器外面装的应用软件就是鱼,装的依赖就是鱼食和水。这样大家就能了解 docker 的 logo 了。大海就是宿主机,docker 就是那条鲸鱼,鲸鱼背上的集装箱就是容器,咱们的应用程序就装在集装箱外面。
在讲容器的时候肯定绕不开容器镜像,这里先简略的把容器镜像了解为是一个压缩包。压缩包里蕴含利用的可执行程序以及程序依赖的文件(例如:配置文件和须要调用的动静库等),接下来通过实际操作来看看容器到底是个啥。
一、宿主机视角看容器:
1、首先,咱们启动容器。
docker run -d --name="aimar-1-container" euleros_arm:2.0SP8SPC306 /bin/sh -c "while true; do echo aimar-1-container; sleep 1; done"
这是 Docker 的规范命令。意思是应用 euleros_arm:2.0SP8SPC306 镜像(镜像名: 版本号)创立一个新的名字为 ”aimar-1-container” 的容器,并在容器中执行 shell 命令:每秒打印一次“aimar-1-container”。
参数阐明:
-d:应用后盾运行模式启动容器,并返回容器 ID。
–name:为容器指定一个名字。
docker run -d --name="aimar-1-container" euleros_arm:2.0SP8SPC306 /bin/sh -c "while true; do echo aimar-1-container; sleep 1; done"
207b7c0cbd811791f7006cd56e17033eb430ec656f05b6cd172c77cf45ad093c
从输入中,咱们看到一串长字符 207b7c0cbd811791f7006cd56e17033eb430ec656f05b6cd172c77cf45ad093c。他就是容器 ID,能惟一标识一个容器。当然在应用的时候,不须要应用全 id,间接应用缩写 id 即可(全 id 的前几位)。例如下图中,通过 docker ps 查问到的容器 id 为 207b7c0cbd81
aimar-1-container 容器启动胜利后,咱们在宿主机上应用 ps 进行查看。这时能够发现方才启动的容器就是个过程,PID 为 12280。
咱们尝试着再启动 2 个容器,并再次在宿主机进行查看,你会发现又新增了 2 个过程,PID 别离为 20049 和 21097。
所以,咱们能够失去一个论断。从宿主机的视角看,容器就是过程。
2、接下来,咱们进入这个容器。
docker exec -it 207b7c0cbd81 /bin/bash
docker exec 也是 Docker 的规范命令,用于进入某个容器。意思是进入容器 id 为 207b7c0cbd81 的容器,进入后执行 /bin/bash 命令,开启命令交互。
参数阐明:
-it 其实是 - i 和 - t 两个参数,意思是容器启动后,要调配一个输出 / 输入终端,不便咱们跟容器进行交互,实现跟容器的“对话”能力。
从 hostname 从 kwephispra09909 变动为 207b7c0cbd81,阐明咱们曾经进入到容器外面了。在容器中,咱们尝试着启动一个新的过程。
[root@207b7c0cbd81 /]# /bin/sh -c "while true; do echo aimar-1-container-embed; sleep 1; done" &
再次回到宿主机进行 ps 查看,你会发现 不论是间接启动容器,还是在容器中启动新的过程,从宿主机的角度看,他们都是过程。
二、容器视角看容器:
后面咱们曾经进入容器外面,并启动了新的过程。然而咱们并没有在容器里查看过程的状况。在容器中执行 ps,会发现失去的后果和宿主机上执行 ps 的后果齐全不一样。下图是容器中的执行后果。
在 Container1 容器中只能看见刚起启动的 shell 过程(container1 和 container1-embed),看不到宿主机上的其余过程,也看不到 Container2 和 Container3 外面的过程。这些过程像被关进了一个盒子外面,齐全感知不到外界,甚至认为咱们执行的 container1 是 1 号过程(1 号过程也叫 init 过程,是零碎中所有其余用户过程的先人过程)。所以,从容器的视角,容器感觉“我就是天,我就是地,欢送来到我的世界”。
但难堪的是,在宿主机上,他们却是一般得不能再一般的过程。留神,雷同的过程,在容器里看到的过程 ID 和在宿主机上看到的过程 ID 是不一样的。容器中的过程 ID 别离是 1 和 1859,宿主机上对应的过程 ID 别离是 12280 和 9775(见上图)。
三、总结
通过下面的试验,对容器的定义就须要再加上一个定语。容器就是过程 => 容器是与零碎其余局部隔离开的过程。这个时候咱们再看下图就更容易了解,容器是跑在宿主机 OS(虚机容器的宿主机 OS 就是 Guest OS)上的过程,容器间以及容器和宿主机间存在隔离性,例如:过程号的隔离。
在容器内和宿主机上,同一个过程的过程 ID 不同。例如:Container1 在容器内 PID 是 1,在宿主机上是 12280。那么该过程真正的 PID 是什么呢?当然是 12280!那为什么会造成在容器内看到的 PID 是 1 呢,造成这种幻象的,正是 Linux Namespace。
Linux Namespace 是 Linux 内核用来隔离资源的形式。每个 Namespace 下的资源对于其余 Namespace 都是不通明,不可见的。
Namespace 按隔离的资源进行分类:
后面提到的容器内外,看到的过程 ID 不同,正是应用了 PID Namespace。那么这个 Namespace 在哪呢?在 Linux 上所有皆文件。是的,这个 Namespace 就在文件里。在宿主机上的 proc 文件中(/proc/ 过程号 /ns)变记录了某个过程对应的 Namespace 信息。如下图,其中的数字(例如:pid:[4026534312])则示意一个 Namespace。
对于 Container1、Container2、Container3 这 3 个容器,咱们能够看到,他们的 PID Namespace 是不一样的。阐明他们 3 个容器中的 PID 互相隔离,也就是说,这 3 个容器外面能够同时领有 PID 号雷同的过程,例如:都有 PID= 1 的过程。
在一个命名空间中,那这俩过程就互相可见,只是 PID 与宿主机上看到的不同而已。
至此,咱们能够对容器的定义再细化一层。容器是与零碎其余局部隔离开的过程 =》容器是应用 Linux Namespace 实现与零碎其余局部隔离开的过程。
点击关注,第一工夫理解华为云陈腐技术~