k8s学习笔记


容器技术概念入门篇

05 | 白话容器基础(一):从进程说开去

  • 容器其实是一种沙盒技术。 顾名思义,沙盒就是能够像一个集装箱一样,把你的应用“装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去。
  • 容器本身没有价值,有价值的是”容器编排
  • 镜像。比如我们要做一个加法程序,由于计算机只认识0和1,所以无论用哪种语音编写这段代码,最后都需要翻译成二进制文件,才能在计算机操作系统跑起来。为了让这些代码正常运行,还需要提供数据,比如这个加法程序,将数据和代码本身的二进制文件,放在磁盘上,就是平常我们所说的程序,也叫代码的可执行镜像
  • 进程。对于进程来说,它的静态表现就是程序,平常都安安静静地待在磁盘上;而一旦运行起来,它就变成了计算机里的数据和状态的综合,这就是它的动态表现
    • 输入数据
    • 寄存器的值变化
    • 堆桟中的指令变化
    • 被打开的文件
    • 各种设备的状态信息变化
  • 容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个边界
    • Ggroups技术是用来制造约束的主要手段
    • Namespace技术则是用来修改进程视图的主要方法
  • 跟真实存在的虚拟机不同,在使用docker的时候并密友一个真正的”docker容器”运行在宿主机里面。docker项目帮助用户启动的还是原来的引用进程,只不过在创建这些进程时,docker为它们加上了各种各样的Namespace参数
    • 这时这些进程就会觉得自己是各自PID Namespace里的第1号进程
    • 只能看到各自Mount Namespace里挂载的目录和文件
    • 只能访问到各自Network Namespace里的网络设备
    • 彷佛运行在一个个”容器”里面,与世隔绝
  • 小结
    • 程序:静态状态是文件,动态状态是进程
    • Cgroups限制资源,Namespace隔离资源
    • 容器:其实就是操作系统在启动进程时通过设置一些参数实现了隔离不相关资源后的一个特殊进程
      • 实际上没有一个真正的容器运行在宿主机里面(只是Namespace造成了这一个个进程的错觉)

06 | 白话容器基础(二):隔离与限制

  • Namespace技术实际上修改了应用进程看待整个计算机”视图”,即它的”视线”被操作系统做了限制,只能”看到”某些指定的内容。
  • 虚拟机容器的区别
    • 虚拟机是真实存在的(需要有一套完成的GuestOS造成资源消耗约如100~200MB),而容器只是一个宿主机上的普通进程(占用资源忽略不计)
    • 虚拟机对宿主机操作系统调用会有性能损耗(尤其是计算资源、网络和磁盘I/O),而容器并不存在性能损耗
  • 既然容器是宿主机上一个特殊进程,那么多个容器之间用的是同一个宿主机的操作系统内核(共享内核导致隔离不彻底)
  • Cgroups全称是Control Group,用来限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等
  • 容器是一个”单进程”模型
  • 小结
    • 虚拟机需要借助GuestOS才能运行应用,容器实际上就是OS的进程,一组文件和运行环境,更加简单。
    • 虚拟化较容器化有更大的资源损耗
    • 容器相较于虚拟机最大的优势是敏捷高性能
    • 多个容器共享内核导致隔离不彻底所以引入了Cgroups限制资源
    • 具体Cgroups相关指令操作参考相关文档…
    • 一个正在运行的docker容器,其实就是启用了多个Namespace的应用进程,而这个进程能够使用的资源量,则受Cgroups配置的限制
    • Cgroups问题:如果在容器中执行top指令,将会显示宿主机的CPU和内存数据,而不是当前容器的数据(原因是/proc文件系统并不知道用户通过Cgroups对这个容器做了怎样的资源限制)
      • lxcfs可解决该问题

07 | 白话容器基础(三):深入理解容器镜像

1.容器里的进程看到的文件系统是什么样子的呢?
2.用户希望每次创建一个新容器时都是看到一个独立的隔离环境

  • 即使开启了Mount Namespace,容器进程看到的文件系统也跟宿主机完全一致

    • 原因:Mount Namespace修改的是容器进程对于文件系统”挂载点”的认知
    • 所以:只有”挂载”后,进程的视图才会被改变。而在此之前,新创建的容器会直接继承宿主机的各个挂载点
    • 修改前:继承宿主机的挂载点
    • 修改后:可以修改挂载点
  • 可以在容器进程启动之前,让容器以tmpfs(内存盘)格式,重新挂载/xxx目录

  • Mount Namespace只有挂载(mount)之后才能生效

  • 我们一般会在容器的根目录下挂载一个完成操作系统的文件系统,比如Ubuntu 16.04的ISO。这样容器启动之后,我们在容器里通过执行ls /查看根目录下的内容,就是Ubuntu 16.4的所有目录和文件

    • 容器镜像:挂载在容器根目录上,用来为容器进程提供隔离后执行环境的文件系统。也叫rootfs(根文件系统)
    • rootfs只是一个操作系统包含的文件、配置和目录,并不包括操作系统内核(Linux中这两部分是分开存放的,操作系统只有在开机启动时才会加载指定版本的内核镜像)
  • Docker项目最核心原理就是为待创建的用户进程

    • 1.启用Linux Namespace配置
    • 2.设置指定的Cgroups参数
    • 3.切换进程的根目录chroot(Change Root) //会优先使用pivot_root系统调用(如果不支持采用chroot)
  • 同一台机器上的所有容器,都共享宿主机操作系统的内核

    • 意味着是全局变量,牵一发而动全身
    • 相比虚拟机的主要缺陷之一:虚拟机拥有一个完整的Guest OS(不存在影响其他应用)
  • 一致性

    • 镜像打包后,无论在本地、云端等地方,只要解压打包好的容器镜像,那么这个应用所需要的完整的执行环境就会被重现出来
    • 打通了应用在本地开发和远端执行环境之间难以逾越的鸿沟

1.但是否每开发/升级一个应用,都需要重复制作一次rootfs吗
2.比如我在rootfs安装了Java环境来部署我的Java应用,这时候其他同事也需要部署Java应用,那显然希望是能直接用我安装了Java环境的rootfs,而不是重复这个流程

  • 层(layer)

    • 用户制作镜像的每一步操作,都会生成一个层,也就是一个增量rootfs
    • UnionFS:目的是将多个不同位置的目录联合挂载(union mount)到同一个目录下。
      挂载前
      执行
      mkdir C
      mount -t aufs -o dirs=./A:./B none ./C
      挂载后
      可以发现原来A目录下有a、x,B目录下有b、x,合并后的C中只有a、b、x(x被去重合并了)
    • 层分为只读层+可读写层+Init层
  • 总结

    • rootfs是一个操作系统的所有文件和目录,并不包含内核。而传统虚拟机的镜像大多是一个磁盘的”快照”,磁盘有多大,镜像就至少有多大
    • 通过结合Mount Namespacerootfs,容器就能为进程构建一个完善的文件隔离环境(依赖chrootpivot_root这两个系统调用切换根目录的能力)
    • docker公司提出了使用多个增量rootfs联合挂载一个完整rootfs的方案,这就是容器镜像中“”的概念
      • 共享层的存在,使得所有这些容器镜像需要的总空间,也比每个镜像的综合要小
    • 强一致性:一旦一个镜像被发布,那么任何地方下载这个镜像得到的内容都完全一致,可以完全复现这个镜像制作者当初的完整环境
    • 容器镜像将会变成未来软件的主流发布方式

文章作者: GaryLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GaryLee !
  目录