【组件云原生】docker原理浅析

简介: Docker已经成为容器技术的代名词,但实际上,Docker只是linux 容器技术的一种封装,那么为什么会有容器技术的出现,容器技术解决了什么问题?为什么又会有Docker的出现,Docker又解决了什么问题?Docker核心原理是什么。接下来这篇文章,我们将浅析Docker的出现和原理。

前言

Docker已经成为容器技术的代名词,但实际上,Docker只是linux 容器技术的一种封装,那么为什么会有容器技术的出现,容器技术解决了什么问题?为什么又会有Docker的出现,Docker又解决了什么问题?Docker核心原理是什么。接下来这篇文章,我们将浅析Docker的出现和原理。

一、为什么会有Docker

1、虚拟化技术的出现

在软件工程里,环境配置问题是一个永远解决不完的难题,每台计算机环境都不一样,开发者经常会说:“It works on my machine(他可以在我的电脑上运行了)”,但实际上,换一台电脑,很可能运行失败。怎么保证软件能在不同计算机中按照预期运行也是程序开发者必须要考虑的一个问题。


为什么会出现这样的问题?

软件工程是一个精细的工程,软件工程依赖计算机系统上各个系统的协调工作,大到操作系统版本,小到编译环境,数据库版本等,而不同版本的软件之间会有不兼容问题,即使是相同版本,不同配置的软件,也有可能不能正常兼容运行。

那有办法解决吗?

解决这个问题的办法就是虚拟化,虚拟化技术的一种实现就是虚拟机技术,虚拟机技术可以在计算机上模拟其他计算机完整的系统和环境配置,比如在Windows上可以运行Linux虚拟机,同时在Linux中可以安装对应版本的软件,对运行的程序来说就是运行在Linux操作系统上,程序是无感知的,因此程序可以在虚拟机中正常运行,虚拟机对于计算机来说就是一个文件,不需要可以直接删掉,也可以打包进行迁移,使用虚拟机技术可以很好的解决“It doesn't work on your machine(他无法在你的机器上运行)”的问题。

2、虚拟化技术的问题

看起来虚拟机似乎完美的解决了程序运行环境的问题,越来越多的人使用虚拟机安装调试程序,但同时也有越来越多的问题被发现。

问题1: 资源利用率低

虚拟机解决了环境问题,但是虚拟机里跑了一个完整的操作系统,操作系统本身就需要庞大的资源运行,一个完整的操作系统包含着用户管理,内置一些软件等,这些都是大多数程序运行所不需要的。

例如图中所示,一个16核32G内存的机器,由于操作系统自身需要一定资源运行,因此可以运行3个4核8G的虚拟机,每个虚拟机运行3个1核2G内存的程序进程,那么1台机器只能运行9个程序进程,但是如果直接在操作系统上运行,可以轻松运行12个以上的程序进程,说明采用虚拟机解决了环境问题,但是资源的利用率很低。

问题2:程序启动慢

由于虚拟机运行了完整的操作系统,操作系统启动总是要花费很多时间,甚至一些系统级的操作步骤无法跳过,这导致虚拟机运行程序显得十分冗余,甚至程序自身启动花费的时间远远小于虚拟机操作系统的时间,冗余且启动时间长,这让程序也显得十分冗余,在快速迭代时期,甚至会成为开发的瓶颈。

3、Linux Containter

随着这些问题的越来约突出,linux发展出了另一种虚拟化技术,Linux 容器(LXC)

Linux容器共享同一个操作系统内核,将应用进程与系统其他部分隔离开。容器可以确保应用程序运行拥有必需的库、依赖项和文件,相当于对程序包了一个壳子,让程序以为自己运行在一个完整的操作系统上。

解决问题1:只加载程序需要使用的环境资源,共用底层系统

Linux容器不需要像虚拟机那样启动一个完整的操作系统,只包含了程序运行依赖的库,多个容器可以共享底层操作系统,因此容器的占用资源少,体积小,同时容器中可以打包程序依赖的环境。容器解决了环境配置问题,在一个linux容器中,可以运行多个程序,每个程序都像运行在一个完整的操作系统上一样,也可以运行多个容器,容器和容器间隔离。

解决问题2:只启动一个进程,不包含操作系统

Linux容器对操作系统来说只是一个进程,程序运行在容器中,启动容器就是启动程序的过程,不需要操作完整操作系统冗余的启动步骤,因此可以快速启动。


二、Docker是什么

1、Docker

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。

  • Docker是世界领先的软件容器平台。
  • Docker使用Google公司推出的Go语言进行开发实现,基于Linux内核的Cgroup,Namespace,以及AUFS类的UnionFS等技术(文章第三节会对Cgroup,Namespace做解释),对进程进行封装隔离,属于操作系统层面的虚拟化技术。 由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。Docker最初实现是基于LXC。
  • Docker能够自动执行重复性任务,例如搭建和配置开发环境,从而解放了开发人员以便他们专注在真正重要的事情上:构建杰出的软件。
  • 用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

2、Docker的特点

  • 轻量,在一台机器上运行的多个Docker容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。
  • 标准,Docker容器基于开放式标准,能够在所有主流Linux版本、Microsoft Windows以及包括VM、裸机服务器和云在内的任何基础设施上运行。
  • 安全,Docker赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker默认提供最强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台机器。

3、为什么要用Docker

  • Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现“这段代码在我机器上没问题啊”这类问题;——一致的运行环境
  • 可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。——更快速的启动时间
  • 避免公用的服务器,资源会容易受到其他用户的影响。——隔离性
  • 善于处理集中爆发的服务器使用压力;——弹性伸缩,快速扩展
  • 可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。——迁移方便
  • 使用Docker可以通过定制应用镜像来实现持续集成、持续交付、部署。——持续交付和部署

三、docker核心原理:怎么让程序看起来独享系统资源

操作系统只有一个,一个进程所需要的资源,如何对资源进行隔离,给不同的进程使用,这里的资源我们指进程运行需要的基本资源,CPU,内存,磁盘,网络。在Linux里,有一个功能是Cgroups,可以对资源进行隔离。

程序的运行不光需要基本资源,还需要使用系统内核的很多功能,比如分配进程号,用户组等,在Linux中,Namespace可以对内核资源进行分区,分区内可以让进程看起来使用了一个完整的全局资源。

补充说明:使用 Cgroups 和 Namespace并不是Docker特有的,Cgroups和Namespace可以说是容器化技术的基石,了解Cgroups和Namespace更有助于了解容器化的实现。

1、Cgroups:对资源进行限制

Cgroups 是Linux内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 CPU,内存等资源实现精细化的控制。如图所示,Cgroups是有一个层级结构的,Cgroup可以基于Cgroup数结构对子系统做资源限制,Cgroup可形成一颗树形结构的资源限制树。cgroup的子系统需要attach到cgroup的层级结构中实现对子系统的资源限制。我们简单来看一下cgroup的源代码结构体定义。

structcgroup{

   unsignedlongflags; /* 用于标识当前 cgroup 的状态 */

   atomic_tcount; /*引用计数器,表示有多少个进程在使用这个 cgroup */

   

   /*

   由于 cgroup 是通过 层级 来进行管理的,

   这三个字段就把同一个 层级 的所有 cgroup 连接成一棵树。

   parent 指向当前 cgroup 的父节点,

   sibling 连接着所有兄弟节点,

   而 children 连接着当前 cgroup 的所有子节点

   */

   structlist_headsibling;

   structlist_headchildren;

   structcgroup* parent;

   

   

   structdentry* dentry; /* 由于 cgroup 是通过 虚拟文件系统 来进行管理的,该字段描述cgroup层级中目录 */

   

   /*

   子系统 能够附加到 层级 ,

   而附加到 层级 的 子系统 都有其限制进程组使用资源的算法和统计数据。

   所以 subsys 字段就是提供给各个 子系统 存放其限制进程组使用资源的统计数据。

   我们可以看到 subsys 字段是一个数组,

   而数组中的每一个元素都代表了一个 子系统 相关的统计数据。

   从实现来看, cgroup 只是把多个进程组织成控制进程组,而真正限制资源使用的是各个 子系统

   */

   structcgroup_subsys_state* subsys[ CGROUP_SUBSYS_COUNT];

   

   /*

   用于保存 层级 的一些数据,

   比如: 层级 的根节点,附加到 层级 的 子系统 列表(因为一个 层级 可以附加多个 子系统 ),

   还有这个 层级 有多少个 cgroup 节点等。

   */

   structcgroupfs_root* root;

   

   /*

   层级 的根节点(根cgroup)

   */

   structcgroup* top_cgroup;

   structlist_headcss_sets;

   structlist_headrelease_list;

   

};


接下来我们看下内核是如何把进程与 cgroups 层级结构联系起来的。

在创建了 cgroups 层级结构中的节点(cgroup 结构体)之后,可以把进程加入到某一个节点的控制任务列表中,一个节点的控制列表中的所有进程都会受到当前节点的资源限制。同时某一个进程也可以被加入到不同的 cgroups 层级结构的节点中,因为不同的 cgroups 层级结构可以负责不同的系统资源。


由于一个进程可以同时添加到不同的 cgroup 中(前提是这些 cgroup 属于不同的 层级 )进行资源控制,而这些 cgroup 附加了不同的资源控制 子系统 。所以需要使用一个结构把这些 子系统 的资源控制统计信息收集起来,方便进程通过 子系统ID 快速查找到对应的 子系统 资源控制统计信息,而 css_set 结构体就是用来做这件事情。我们来简单来看下css_set的结构体定义,帮助我们更好的理解css_set工作原理。

structcss_set{  

   structkrefref;/*引用计数器,用于计算有多少个进程在使用此 css_set */

   structlist_headlist;/*用于连接所有 css_set*/

   structlist_headtasks;/*由于可能存在多个进程同时受到相同的 cgroup 控制,所以用此字段把所有使用此 css_set 的进程连接起来*/

   structlist_headcg_links;

   structcgroup_subsys_state* subsys[ CGROUP_SUBSYS_COUNT];/*用于收集各种 子系统 的统计信息结构*/

   

};


2、 Namespace: 对资源进行分区

namespace是对全局系统资源的一种封装隔离。这样可以让不同namespace的进程拥有独立的全局系统资源。这样改变一个namespace的系统资源只会影响当前namespace中的进程,对其它namespace中的资源没有影响

namespace可以分为很多种,可以对不同的资源进行分区

namespace名称

使用的标识 - Flag

控制内容

Cgroup

CLONE_NEWCGROUP

Cgroup root directory cgroup 根目录

IPC

CLONE_NEWIPC

System V IPC, POSIX message queues信号量,消息队列

Network

CLONE_NEWNET

Network devices, stacks, ports, etc.网络设备,协议栈,端口等等

Mount

CLONE_NEWNS

Mount points挂载点

PID

CLONE_NEWPID

Process IDs进程号

Time

CLONE_NEWTIME

时钟

User

CLONE_NEWUSER

用户和组 ID

UTS

CLONE_NEWUTS

系统主机名和 NIS(Network Information Service) 主机名(有时称为域名)

简单来说:Docker底层原理也是使用了Linux Cgroups 和 Namespace 让进程隔离并对资源进行封装,Cgroups的主要作用是限制和分配资源,Namespace的主要作用是抽象封装隔离系统资源。

四、总结

这篇文章我们简单的介绍了docker出现的背景以及要解决的问题,了解了docker具体是做什么的,docker有什么优势,最后我们简单介绍了docker最核心的原理Cgroups和Namesapce,Cgroups和Namesapce是linux虚拟化技术的基石,了解了linux内核的能力更有助于我们理解docker原理。

相关文章
|
1月前
|
缓存 Java API
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
|
1月前
|
Kubernetes Cloud Native 开发工具
带你读《云原生应用开发:Operator原理与实践》精品文章合集
带你读《云原生应用开发:Operator原理与实践》精品文章合集
|
3月前
|
Ubuntu Cloud Native 测试技术
百度搜索:蓝易云【云原生之使用Docker部署ubuntu测试环境】
请注意,以上步骤仅是简单的使用Docker部署Ubuntu测试环境的示例。在实际应用中,您可能需要根据测试需求和应用场景进行更多的配置和优化。同时,在退出容器时,容器并未删除,您可以使用 `docker rm <容器名称>`命令来删除容器。在进行测试时,请谨慎操作,并确保对Docker的使用有一定的了解,以避免不必要的问题。
41 2
|
3月前
|
Cloud Native NoSQL Redis
云原生 Docker Dockerfile 构建应用
【1月更文挑战第9天】云原生 Docker Dockerfile 构建应用
|
3月前
|
分布式计算 Java Linux
【深入浅出Docker原理及实战】「原理实战体系」零基础+全方位带你学习探索Docker容器开发实战指南(Dockerfile使用手册)
Docker 是一套构建在 Linux 内核之上的高级工具,旨在帮助开发人员和运维人员更轻松地交付应用程序和依赖关系,实现跨系统和跨主机的部署。使用安全且轻量级的容器环境来实现这一目标。容器可以手动创建,也可以通过编写 Dockerfile 自动创建。开发人员和运维人员可以将应用程序及其依赖打包到容器中,实现应用程序的可移植性和环境一致性。
119 5
【深入浅出Docker原理及实战】「原理实战体系」零基础+全方位带你学习探索Docker容器开发实战指南(Dockerfile使用手册)
|
27天前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
39 0
|
27天前
|
关系型数据库 MySQL Nacos
【深入浅出Nacos原理及调优】「实战开发专题」采用Docker容器进行部署和搭建Nacos服务以及“坑点”
【深入浅出Nacos原理及调优】「实战开发专题」采用Docker容器进行部署和搭建Nacos服务以及“坑点”
48 1
|
1月前
|
消息中间件 存储 Cloud Native
【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程
【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程
|
2月前
|
IDE Cloud Native 开发工具
云原生之在Docker环境下部署Atheos云IDE平台
【2月更文挑战第3天】云原生之在Docker环境下部署Atheos云IDE平台
365 2
|
2月前
|
Java Linux 虚拟化
浅谈Docker底层原理
浅谈Docker底层原理
28 0

热门文章

最新文章