前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并非每个容器内部都能包含一个操作系统

并非每个容器内部都能包含一个操作系统

作者头像
用户5166556
发布2020-11-05 14:25:26
8660
发布2020-11-05 14:25:26
举报

在没有探讨是否包含操作系统之前,大家一起来看两张图片,如下所示:

很容易可以辨别出,图一docker引擎画在了应用底部,类似于虚拟机的位置,docker虚拟化技术替代了虚拟机,更轻量级,看上去更容易理解和接受;

图二docker引擎画在了应用的侧边栏,从图片上看,进程则是直接运行在虚拟机上,docker容器更多的是进行旁路式的辅助和管理;其它没什么区别,其中图一也是PPT和网上常见的作图方式,这种方式真的正确吗?下文揭晓。

容器底层技术实现

docker底层主要是通过cgroup和namespace两种技术实现,cgroup实现资源限额, namespace实现资源隔离。

这两个概念看起来非常抽象,其实一起来看个例子,一切就明白了,执行docker exec -it 5080b69f08c4 /bin/bash

代码语言:javascript
复制
[root@tomcat-7c5857b68f-fzrbr /]# ps aux|grep tomcat
root         1 79.1 23.4 10168216 3788616 ?    Ssl  Oct22 8559:52 /usr/java/jre/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
root       109  0.0  0.0   9052   668 pts/0    S+   19:24   0:00 grep --color=auto tomcat

在容器内部执行ps之后出现两个进程在运行,一个是我的服务,另外一个是我刚刚执行的ps,已经看到容器内部进程已经跟我们的宿主机中的服务完全隔离开了。这其实就是对被隔离应用的进程空间做了隔离,使得这些进程空间下的进程只能看到重新计算之后的进程编号,这就是linux的namespace机制,linux底层是通过clone()函数实现,而当在docker中创建进程时就可以通过指定参数返回一个全新的进程空间,这样的话的就做到了pid为1的目的,其实你到宿主机上查看下,这个docker中运行的服务,在宿主机上存在着同样的进程,只不过这个进程的pid是真实的pid。

除了刚才我们提到的pid namespace隔离技术,Linux还为我们提供了Mount、UTS、IPC、Network、User等隔离机制,其效果都是为进程上下文起到隔离作用,使我们只能看到指定的内容。

上面刚刚说了,namespace只是对进程做了隔离,使其在容器内部看不到宿主机的进程,但是对于宿主机来说,还是能够看到这些被隔离的进程。换句话说,这些被隔离的进程跟宿主机上的其它进程完全没什么区别。所以说上述例子中的tomcat依然可以随意占用宿主机的资源?其实docker利用了linux底层的Cgroup进行了资源限制。

Linux Cgroups 的全称是 Linux Control Group。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等.

进入容器内部/sys/fs/cgroup/文件夹下面,里面包含了很多子目录,通过这些目录中的文件内容就可以实现对各种资源的限制。

看到这里,再品一下,容器就是一个进程而已。

什么叫一个进程?你刚才上面举的例子,不是在tomcat容器中执行了一个ps,这明明是两个进程,在说了,我也可以在这个容器中运行其它服务,这些也都是正常运行的进程,这怎么能说是一个进程呢?不信的话,你进到看看nginx容器中ps看下,它的进程更多。

代码语言:javascript
复制
root         1  0.0  0.0  54952  4416 ?        Ss   Oct29   0:00 nginx: master process /usr/xshj/openresty/nginx/sbin/nginx -g daemon off;
xshj         6  0.0  0.1  76164 23380 ?        S    Oct29   0:00 nginx: worker process
xshj         7  0.0  0.1  76164 23380 ?        S    Oct29   0:00 nginx: worker process
xshj         8  0.0  0.1  76164 23392 ?        S    Oct29   0:00 nginx: worker process
xshj         9  0.0  0.1  76300 23392 ?        S    Oct29   0:00 nginx: worker process
root        25  0.0  0.0  12484   992 pts/0    S+   18:47   0:00 grep --color=auto nginx

其实我说的一个进程指的是只有一个进程是受docker控制的,其它进程虽然也在运行,但是他们不受docker的控制,它们都是野进程,如果主的挂了,其它的都得跟着玩完。这也是为什么在编写Dockerfile的时候,CMD中的命令不能后台运行的原因,简单来说,docker仅在它的1号进程(PID为1)运行时,会保持运行。如果1号进程退出了,Docker容器也就退出了。这个问题也是新手或者没有从事过docker相关开发的同学经常碰到的问题,我的容器刚刚启动,怎么就exit了呢?因为它需要一个前台进程把它hang住。比如在执行nginx命令的时候,我们会使用ENTRYPOINT [ "/usr/sbin/nginx", "-g", "daemon off;" ]

容器单进程并不是指容器里只能运行"一个"进程而是指容器没有管理多进程的能力。这是因为容器里PID=1的进程就是应用本身,其他的进程都是PID=1进程的子进程。

再说了,Pod是k8s调度的最小单位,为什么不能是容器,而需要搞出一个Pod的概念?因为容器单进程模式,而Pod则是进程组。通过进程组的概念,Pod能够把容器 "有原则的" 组织到一起运行,从而能够进行每个容器的管理。而k8s需要做的工作就是将 "进程组" 的概念映射到容器技术中。

容器技术深入理解

其实docker的核心技术可以总结为:为待创建进程的容器开启namespace配置、指定Cgroup参数、切换容器根目录。本质上来说也就是运行在宿主机上的普通进程而已。

表示不服,容器技术从诞生至今一直反复强调的特性就是一致性,你这说的和普通进程没什么区别,如何保证一致性?答案很简单,docker镜像不仅能够打包应用,还能打包整个操作系统的文件和目录,记住是操作系统的文件和目录。通过这种方式docker就把一个应用所有的依赖库包括操作系统中的文件和目录都被打包到镜像中。docker正是通过打包操作系统级别的方式,解决了开发到线上环境的一致性。

搞明白容器的本质之后,你就可以解决很多现实问题,比如:我有一个老项目,两个服务之间是紧密耦合的,如果按照k8s中Pod的编排思想,改动成本非常高。这个时候,就可以考虑把这个两个服务打包到一个镜像中,并且其中一个进程作为主进程启动,另外一个后台运行。但后台运行的进程就需要你自己管理了,说白了,服务挂了也没人知道。这也是为什么说k8s Pod能够把多个紧密关联的进程有组织的管理在一起的原因。

说了半天,容器就是运行在宿主机上的一个资源被限制、视图被隔离的进程;那一个宿主机操作系统只有一个内核,也就是说,所有的容器都依赖这一个内核了?比如我现在有一个需求,我的两个容器运行在同一台宿主机上,但是依赖的内核版本不一样,或者需要配置的内核参数不一样,怎么解决呢?解决不了,这也是容器化技术相比于虚拟机的主要缺陷之一。

说好的打包操作系统呢?

网上搜索下,到处充斥着docker能够打包操作系统的言论,这句话其实是不准确的,而且容易让人误解docker的能力。下面先来看一组概念内核,操作系统和发行版之间的区别。

  • Linux内核是Linux操作系统的核心部分。这就是Linus最初写的。
  • Linux操作系统是内核和用户域(库,GNU实用程序,配置文件等)的组合。
  • Linux发行版是Linux操作系统的特定版本,例如Debian,CentOS或Alpine。

大多数在编写Dockerfile时都会显式或隐式地依赖于运行在容器中的某种Linux操作系统的特定发行版本,比如:

代码语言:javascript
复制
$ cat <<EOF > Dockerfile
FROM alpine:3.7
RUN apk add --no-cache mysql-client
ENTRYPOINT ["mysql"]
EOF

$ docker build -t mysql-alpine .
$ docker run mysql-alpine

对于刚刚开始学习容器技术的同学来说,这可能导致一种错误的印象,docker是操作系统级别的隔离,而且总是基于众所周知的和广泛分布的Linux发行版本debian,centos或alpine等。结合上面所说的docker容器启动后,只是运行在宿主机上的一个进程,理所当然依赖于宿主机的内核。这里又打包了一个完整的操作系统,怎么回事呢?

其实linux操作系统中代码包含两部分,一部分是文件目录和配置,另外一部分是内核,这两部分是分开存放的,系统只有在宿主机开机启动时才会加载内核模块。说白了,即使镜像中包含了内核也不会被加载。说到最后,原来镜像只是包含了操作系统的躯干(文件系统),并没有包含操作系统的灵魂(内核)。

说到这里,相信不用多说,你也应该知道为什么要把docker引擎放在应用的侧边栏,而不是底部。

总结

本文主要结合本人的理解和实践对docker引擎作用提出了质疑,然后通过介绍docker技术的本质,最后介绍了镜像构建基础-操作系统。如有说的不对的地方,还请指正!欢迎关注公众号、加我微信。一起讨论!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-10-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 容器底层技术实现
  • 容器技术深入理解
  • 说好的打包操作系统呢?
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档