zoukankan      html  css  js  c++  java
  • Docker容器到底是什么?

    Docker是一个开源的应用容器引擎,是近些年最火的技术之一,Docker公司从Docker项目开源之后发家致富把公司商标改为了Docker,收购了fit项目,整合为了docker-compose,前景一片大好,但是据说Docker在社区中话语权过于强硬,得罪了不少公司,google与rethub等牵头发起了kubernetes项目,虽说让Docker在市场上损失很大,但为相信Docker未来前景会很好,k8s虽然强大,但主流也是采用了Docker的容器规范,只会更好。

    Docker创建一个容器的方式很简单,使用docker run命令。

    docker run -it busybox /bin/sh
    

    -it 参数告诉了Docker项目在启动容器后,需要给我们分配一个文本输入输出环境,也就是TTY,跟容器的标准输入相关联,这样我们就可以和这个Docker容器进行交互了。

    /bin/sh 就是我们要在 Docker 容器里运行的程序。

    所以,上面这条指令翻译过来就是:请帮我启动一个容器,在容器里执行/bin/sh,并且给我分配一个命令行终端跟这个容器交互。

    在这之前,我们先看看宿主机存在的进程。

    image.png

    可以看到当前管理员下sudo的PID为4987,在此基础上我们bash的PID为4988,bash中执行了ps命令,PID与PPID的关系很明显。

    此时运行我们的docker容器。

    在容器中查看该容器中的进程,出去这个ps,只有一个PID为1的/bin/sh进程。

    image.png

    再来从我们的宿主机查看一下进程信息,排除掉sudo,bash和ps的进程,只有一个sh,可以与容器中的/bin/sh容器作联系,可以看到他的进程PID是7355,父进程是7332。

    image.png

    找到PPID为7332的这个进程,可以知道是一个叫containerd-shim的进程创建了该容器。

    image.png

    先不考虑containerd-shim进程的事,你应该已经注意到容器是什么了,其实就是一个“特殊”的进程。

    特殊在何处?

    我们宿主机ps查看进程是可以看到容器进程的,而在容器中使用ps命令查看却只能看到容器他本身,一个PID为1的进程,PID为1在Linux有着特殊的意义。

    PID为0的进程是idle进程,是由系统自动创建,运行在内核态,创建第一个用户进程,也就是PID为1的init进程,init进程是linux系统中其它所有用户进程的祖先进程,主要作用是处理僵尸进程。

    当某个父进程比子进程提前消亡时,父进程会给子进程重新寻找“养父进程”,一般就是init进程,由init进程负责处理该子进程的消亡。

    在容器中的进程居然是一个PID为1的进程,他可以管理他所能看到的进程生死,而看不到外面的宿主机进程,一叶障目,不见泰山。

    这时候引入一张Docker商标图片最为合适了。

    image.png

    鲸鱼背上的一个个集装箱就是所谓的容器,完全封闭起来,我们可以看到它,在它里面却不知道我们。

    这就是Docker的容器封闭技术了。

    他的实现其实很简单,Linux的进程创建函数clone,可以接受一个参数CLONE_NEWPID,这样创建的这个进程将会“看到”一个全新的进程空间,在这个进程空间里,它的PID是1。之所以说“看到”,是因为这只是一个“障眼法”,在宿主机真实的进程空间里,这个进程的 PID 还是真实的数值。

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

    这种技术叫做NameSpace,上面说的是PID NameSpace,当然Linux其他的东西也可以有NameSpace,比如说Mount、UTS、IPC、Network和User。

    Mount NameSpace可以让被隔离进程只看到当前Namespace里的挂载点信息,
    Network Namespace,用于让被隔离进程看到当前Namespace里的网络设备和配置。

    综上,可以发现容器其实就是一个带了NameSpace参数特殊创建的进程。

    这暴露出了几个问题,第一是所有容器都是共用了同一个宿主机的操作系统内核,第二是如果我们在低版本的Linux上使用高版本的容器,是会出现问题的,还有第三是这种NameSpace的隔离并不如虚拟化技术隔离的彻底。

    Linux中其实有很多资源都是无法做到隔离的,比如说时间,如果你的容器中尝试使用修改时间的系统调用,就会影响到宿主机的时间。

    所以说,Docker的容器其实还是有未知的隐患存在。

    容器虽然觉得自己是PID为1的init进程,但是对于我们宿主机来说,他是一个正常的进程,和其它进程一样,竞争着系统中的资源。

    为此,Linux内核诞生了一个新特性,Linux Cgroups,可以为进程设置资源限制指标。

    Linux Control Group,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。

    Cgroups的使用接口是利用文件系统开放的,你可以在这个目录下发现很多以资源命名的文件。

    image.png

    image.png

    在创建容器的时候你可以指定资源分配的参数,如下。

    docker run -it --cpu-period=100000 --cpu-quota=20000 busybox /bin/sh
    

    使用该命令创建运行容器之后,可以看到运行的容器id为f0c0f3e5d0d2。

    image.png

    image.png

    此时你可以在之前所说的sys/fs/cgroup下找到一个docker目录,其中就有一个f0c0f3e5d0d2开头的目录。

    image.png

    在这个目录中就可以找到容器f0c0f3e5d0d2的资源分配参数,提供给Cgroups,来为Docker容器分配资源。

    image.png

    还有一个重要地方,是关于容器本身的文件系统,当然和PID NameSpace一样,是利用Mount NameSpace实现的,独立出一片独立空间的文件系统。

    虽说是这样,但有一点细节需要知道,Mount NameSpace创建一片独立空间时是会继承宿主机本身的已挂载空间的,所以在创建之后需要重新挂载所有节点。

    此处细节,可以看左耳朵耗子的这篇博客,非常详细。

    https://coolshell.cn/articles/17010.html

  • 相关阅读:
    jsp学习之——关于请求转发和重定向的形象理解
    java多线程学习之——多线程中几种释放锁和不释放锁的操作
    DBUtils学习之——使用ResultSetHandler接口的各个实现类实现数据库的增删改查
    java网络编程学习之——构建基于多线程的网络通信模型1
    web后端学习过程中技巧总结(持续更新。。。)
    关于表单form元素中onsubmit事件处理机制的认识
    Java小案例——使用双重for循环实现杨辉三角的输出
    Android工具大杂烩
    基于上一篇AS项目依赖库问题的优化解决方案
    Gradle脚本打包AndroidStudio依赖库的问题
  • 原文地址:https://www.cnblogs.com/LexMoon/p/docker1.html
Copyright © 2011-2022 走看看