Docker 特性与原理详细介绍与解析

Docker 特性与原理

首先看看Docker提供了哪些特性:

  1. 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上,例如运行一个一次性交互shell
  2. 文件系统隔离:每个进程容器运行在完全独立的根文件系统里
  3. 写时复制:采用写时复制方式创建根文件系统,这让部署变得极其快捷,并且节省内存和硬盘空间
  4. 资源隔离:可以使用cgroup为每个进程容器分配不同的系统资源
  5. 网络隔离:每个进程容器运行在自己的网络命名空间里,拥有自己的虚拟接口和IP地址
  6. 日志记录:Docker将会收集和记录每个进程容器的标准流(stdout/stderr/stdin),用于实时检索或批量检索
  7. 变更管理:容器文件系统的变更可以提交到新的映像中,并可重复使用以创建更多的容器。无需使用模板或手动配置

从以上特性分别看实现原理

1. 交互式Shell

首先我们允许一个交互式的容器

$docker run -i -t <image name> /bin/bash

这样就建立了一个到容器内的交互式连接,看到的是如下的命令行:

root@df3880b17407:/#

这里我们启动了一个容器,以bash作为其根进程.

root@df3880b17407:/# ps aux
USER    PID %CPU %MEM  VSZ  RSS TTY   STAT START  TIME COMMAND
root     1 0.0 0.0 18164 2020 ?    S  06:06  0:00 /bin/bash

可以看到,在这个容器中,bash 的 PID为 1,而实体机平常情况下,是这样的:

root@ubuntu:~# ps aux
USER    PID %CPU %MEM  VSZ  RSS TTY   STAT START  TIME COMMAND
root     1 0.0 0.1 24716 2612 ?    Ss  Sep04  0:01 /sbin/init

大家都知道,所有进程的共同祖先都是 PID=1的进程

所以在容器中,所有以后创建的进程都是通过/bin/bash 创建的,PID=1的 bash是容器中所有进程的祖先理解了这点后,对容器的理解就很简单了.

2. 文件系统隔离

对于一个正在运行的容器,其文件系统都是一个从根目录开始的虚拟文件系统,在容器中看到的是这样的:

root@df3880b17407:/# ll /
total 68
drwxr-xr-x  2 root root 4096 Jul 22 22:51 bin
drwxr-xr-x  2 root root 4096 Apr 10 22:12 boot
drwxr-xr-x  3 root root 4096 Jul 22 22:49 dev
drwxr-xr-x 85 root root 4096 Sep 5 06:49 etc
drwxr-xr-x  2 root root 4096 Apr 10 22:12 home
drwxr-xr-x 16 root root 4096 Jul 22 22:50 lib
drwxr-xr-x  2 root root 4096 Aug 12 03:30 lib64
drwxr-xr-x  2 root root 4096 Jul 22 22:48 media
drwxr-xr-x  2 root root 4096 Apr 10 22:12 mnt
drwxr-xr-x  2 root root 4096 Jul 22 22:48 opt
dr-xr-xr-x 356 root root  0 Sep 5 06:06 proc
drwx------  2 root root 4096 Jul 22 22:51 root
drwxr-xr-x  7 root root 4096 Sep 5 07:23 run
drwxr-xr-x  2 root root 4096 Aug 12 03:30 sbin
drwxr-xr-x  2 root root 4096 Jul 22 22:48 srv
dr-xr-xr-x 13 root root  0 Sep 5 06:06 sys
drwxrwxrwt  2 root root 4096 Sep 5 06:55 tmp
drwxr-xr-x 20 root root 4096 Sep 5 06:11 usr
drwxr-xr-x 19 root root 4096 Sep 5 06:11 var

其实真是情况是这样的,容器中的文件系统都是挂载到了真是系统中的一个目录下面.

/var/lib/docker/containers/<image-long-id>/rootfs

这个配置是怎么来的呢,其实所有容器的管理都是通过lxc来管理的,lxc的配置文件放在

/var/lib/docker/containers/<image-long-id>/config.lxc

文件中有字段表示容器挂载到哪个文件目录, 比如我的是这样的:

lxc.rootfs = /var/lib/docker/containers/df3880b17407575cd642a6b7da3c7e417a55fad5bbd63152f89921925626d2b6/rootfs

打开看一下,一目了然:

root@ubuntu:/var/lib/docker/containers/df3880b17407575cd642a6b7da3c7e417a55fad5bbd63152f89921925626d2b6/rootfs# ll
total 84
drwxr-xr-x 53 root root 4096 Sep 5 00:23 ./
drwx------ 4 root root 4096 Sep 5 00:53 ../
drwxr-xr-x 2 root root 4096 Jul 22 15:51 bin/
drwxr-xr-x 2 root root 4096 Apr 10 15:12 boot/
drwxr-xr-x 3 root root 4096 Jul 22 15:49 dev/
drwxr-xr-x 85 root root 4096 Sep 4 23:49 etc/
drwxr-xr-x 2 root root 4096 Apr 10 15:12 home/
drwxr-xr-x 16 root root 4096 Jul 22 15:50 lib/
drwxr-xr-x 2 root root 4096 Aug 11 20:30 lib64/
drwxr-xr-x 2 root root 4096 Jul 22 15:48 media/
drwxr-xr-x 2 root root 4096 Apr 10 15:12 mnt/
drwxr-xr-x 2 root root 4096 Jul 22 15:48 opt/
drwxr-xr-x 2 root root 4096 Apr 10 15:12 proc/
drwx------ 2 root root 4096 Jul 22 15:51 root/
drwxr-xr-x 7 root root 4096 Sep 5 00:23 run/
drwxr-xr-x 2 root root 4096 Aug 11 20:30 sbin/
drwxr-xr-x 2 root root 4096 Jul 22 15:48 srv/
drwxr-xr-x 2 root root 4096 Mar 12 18:41 sys/
drwxrwxrwt 2 root root 4096 Sep 4 23:55 tmp/
drwxr-xr-x 20 root root 4096 Sep 4 23:11 usr/
drwxr-xr-x 19 root root 4096 Sep 4 23:11 var/

这些就是容器中的真实目录了,容器中对于目录的操作都是操作了这个host机器的真实目录。
对于不同的容器,挂载点是不一样的,而容器不能穿越根目录上一级去访问, 所以这里对每一个容器都做到了文件系统隔离。

3. 写时复制

我们把每一个

/var/lib/docker/containers/<image-long-id>

看做是一个容器的配置目录的话,可以看到在配置目录下面有一个 rw/目录,打开看有些什么

total 36
drwxr-xr-x 9 root root 4096 Sep 5 00:23 ./
drwx------ 4 root root 4096 Sep 5 00:53 ../
drwxr-xr-x 6 root root 4096 Sep 4 23:49 etc/
drwxr-xr-x 2 root root 4096 Sep 5 00:23 run/
drwxrwxrwt 2 root root 4096 Sep 4 23:55 tmp/
drwxr-xr-x 7 root root 4096 Sep 4 23:11 usr/
drwxr-xr-x 5 root root 4096 Sep 4 23:11 var/
-r--r--r-- 1 root root  0 Sep 4 23:06 .wh..wh.aufs
drwx------ 2 root root 4096 Sep 4 23:06 .wh..wh.orph/
drwx------ 2 root root 4096 Sep 4 23:11 .wh..wh.plnk/

里面是一些不完整的根目录,这不能说明什么,但是我们在container中写入文件后,看看其中的变化

在容器中执行以下命令

root@df3880b17407:/# touch /opt/x

在 /opt 下我们生成了一个文件

再看看

root@ubuntu:/var/lib/docker/containers/df3880b17407575cd642a6b7da3c7e417a55fad5bbd63152f89921925626d2b6/rw# ll
total 40
drwxr-xr-x 10 root root 4096 Sep 5 01:00 ./
drwx------ 4 root root 4096 Sep 5 00:53 ../
drwxr-xr-x 6 root root 4096 Sep 4 23:49 etc/
drwxr-xr-x 2 root root 4096 Sep 5 01:00 opt/
drwxr-xr-x 2 root root 4096 Sep 5 00:23 run/
drwxrwxrwt 2 root root 4096 Sep 4 23:55 tmp/
drwxr-xr-x 7 root root 4096 Sep 4 23:11 usr/
drwxr-xr-x 5 root root 4096 Sep 4 23:11 var/
-r--r--r-- 1 root root  0 Sep 4 23:06 .wh..wh.aufs
drwx------ 2 root root 4096 Sep 4 23:06 .wh..wh.orph/
drwx------ 2 root root 4096 Sep 4 23:11 .wh..wh.plnk/

是的,在host机器上新生成了 opt/目录,这里做到了容器的写时复制

4. 资源隔离

以系统的三大进程间通信的消息队列来看

初始状态在 ghost, ipcs -q 查看消息队列

root@ubuntu:~/codes/msq# ipcs -q

------ Message Queues --------
key    msqid   owner   perms   used-bytes  messages

在ghost 创建一个, 这里楼主自己写的代码创建的消息队列:

root@ubuntu:~/codes/msq# ./main 1
Create msq with key id 65536root@ubuntu:~/codes/msq# ipcs -q

------ Message Queues --------
key    msqid   owner   perms   used-bytes  messages
0x00000001 65536   root    666    0      0

然后在容器中查看 ipcs -q

root@df3880b17407:/# ipcs -q

------ Message Queues --------
key    msqid   owner   perms   used-bytes  messages

可以看到系统资源是隔离的,这里只是说了一部分,其实还包括了可以通过cgoup对其做CPU和Memory的Quota管理.
默认情况下是使用了所有CPU和内存的,但是可以在config.lxc增加如下配置设置CPU等,具体可以参考lxc的文档

lxc.cgroup.cpu.shares=512 lxc.cgroup.cpuset.cpus=1.2

资源隔离的原理就在于利用cgroup,将不同进程的使用隔离开,假设每个容器都是以bash启动的,那么在容器内部,每个子进程都只能使用当前bash下面的资源,对于其他的系统资源是隔离的.子进程的访问权限由父进程决定

5.网络隔离

在安装好docker后,会默认初始化一个 docker0的网桥

docker0  Link encap:Ethernet HWaddr ee:8c:1f:8b:d7:59
     inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0
     ...

在host机器上,会为每一个容器生成一个默认的网卡类似这样的 vethdBVa1H veth*

这个网卡的一端连接在容器的eth0,一端连接到docker0.这样就实现了每个容器有一个单独的IP.

这里如果需要容器访问外网,需要将eth0设置为混杂模式:

$ifconfig eth0 promisc

这样看来,容器会从172.17.0.0/24 这个网段选择一个IP作为eth0的IP,这样,容器就可以和外部通过 docker0网桥通信了.

在容器内部监听一个端口 python -m SimpleHTTPServer 80 >> /tmp/log.log &
从ghost访问 telnet 172.17.0.2 80
在容器中看到如下:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address      Foreign Address     State    PID/Program name
tcp    0   0 0.0.0.0:80       0.0.0.0:*        LISTEN   2823/python
tcp    0   0 172.17.0.2:80      172.17.42.1:46142    TIME_WAIT  -

在host上看到

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address      Foreign Address     State    PID/Program name
tcp    0   0 172.17.42.1:46142    172.17.0.2:80      ESTABLISHED 10244/telnet

如果需要外部能够访问容器,需要做端口映射规则,和配置虚拟机一样的道理, 只不过这里可以看到的是,80端口并没有占用了本地端口,而是在容器内部做了监听,外部是通过docker0 桥接过去的,每个容器间也做到了端口和网络隔离.

6.日志记录

不多说,在 /var/lib/docker/containers/<image-long-id>.log 下

7.变更管理

Docker的变更管理看做是git的版本管理好了。

生成镜像的时候,未做改动的部分就是上一个版本的镜像的引用,如果做了改动,就是一个新的文件。

将刚才操作的容器做成镜像

docker commit <image-id> <REPOSITORY>

此时的镜像多出来的部分,比如我在这个镜像中安装了Python,那么多出来的部分作为新文件,其他部分任然是上一个版本的引用。

你可以搭建自己的镜像服务器,push到自己的镜像服务器,从其他机器拉下来后直接可以运行。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Docker 命令教程(附中文解释)

    Docker 命令教程 Docker自从诞生以来就一直备受追捧,学习Docker是一件很炫酷.很有意思的事情.我希望通过这篇文章能够让大家快速地入门Docker,并有一些学习成果来激发自己的学习兴趣.我也只是一个在Docker这条巨鲸上玩耍的小孩,全文如有不明确.不正确的地方,还请斧正. # docker --help Usage: docker [OPTIONS] COMMAND [arg...] docker daemon [ --help | ... ] docker [ -h | --h

  • Docker 常用命令整理并介绍

    什么是Docker? Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器.开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机).bare metal.OpenStack 集群和其他的基础应用平台. Docker通常用于如下场景: web应用的自动化打包和发布: 自动化测试和持续集成.发布: 在服务型环境中部署和调整数据库或其他的后台应用: 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的P

  • 简单谈谈Docker镜像的使用方法

    在上篇文章(在Docker中搭建Nginx服务器)中,我们已经介绍了如何快速地搭建一个实用的Nginx服务器.这次我们将围绕Docker镜像(Docker Image),介绍其使用方法.包括三部分: 从Docker Hub或者其他镜像源安装Docker镜像 从Image file安装Docker镜像 从Docker file制作Docker镜像 查找Docker镜像 安装Docker镜像的第一步,是查找你需要的Docker镜像列表,键入: docker search mysql 如果出现权限问题

  • pipework docker无法使用ip netns命令解决办法

    在通过pipework 给docker容器分配IP的时候, 系统报出以下错误: Object "netns" is unknown, try "ip help". 该错误是由于系统版本暂时不支持namespaces, 可通过更新内核或者iproute的方法获取对namespaces的支持.更新方法如下: 方法一: 添加yum源: [cce] cat /etc/yum.repos.d/rdo.repo [openstack-kilo] name=OpenStack K

  • Docker学习之常用的基础命令总结

    前言 Docker是一个用了一种新颖方式实现的超轻量虚拟机,在实现的原理和应用上还是和VM有巨大差别,专业的叫法是应用容器(Application Container).下面这篇文章将简单介绍一下Docker常用的一些基础命令. # 在ubuntu中安装docker $ sudo apt-get install docker.io # 查看docker的版本信息 $ docker version # 查看安装docker的信息 $ docker info # 查看本机Docker中存在哪些镜像

  • docker kubernetes dashboard安装部署详细介绍

    docker之kubernetes dashboard部署 1. 环境说明: 1). 架构: 注: 本次实验服务器环境均采用centos 7. 服务安装均采用yum install. 192.168.3.7 master 192.168.3.16 node 2).使用的软件包: master: docker kubernetes-master etcd flannel nodes: docker kubernetes-node flannel 3). 软件版本: docker: 1.10.3 k

  • Docker 清理命令集锦

    杀死所有正在运行的容器 复制代码 代码如下: docker kill $(docker ps -a -q) 删除所有已经停止的容器 复制代码 代码如下: docker rm $(docker ps -a -q) 删除所有未打 dangling 标签的镜像 复制代码 代码如下: docker rmi $(docker images -q -f dangling=true) 删除所有镜像 复制代码 代码如下: docker rmi $(docker images -q) 为这些命令创建别名 复制代码

  • Docker 特性与原理详细介绍与解析

    Docker 特性与原理 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上,例如运行一个一次性交互shell 文件系统隔离:每个进程容器运行在完全独立的根文件系统里 写时复制:采用写时复制方式创建根文件系统,这让部署变得极其快捷,并且节省内存和硬盘空间 资源隔离:可以使用cgroup为每个进程容器分配不同的系统资源 网络隔离:每个进程容器运行在自己的网络命名空间里,拥有自己的虚拟接口和IP地址 日志记录:Docker将会收集和记

  • C++通信新特性协程详细介绍

    目录 一.关于协程 二.协程的好处 三.协程得用法 四.与线程的区别 五.协程示例 一.关于协程 从 1.54.0 版本开始,Boost.Asio 支持协程.虽然您可以直接使用 Boost.Coroutine,但 Boost.Asio 中对协程的显式支持使得使用它们变得更加容易. 协程让您创建一个反映实际程序逻辑的结构.异步操作不会拆分函数,因为没有处理程序来定义异步操作完成时应该发生什么.程序可以使用顺序结构,而不是让处理程序相互调用. 二.协程的好处 考虑多任务协作的场景. 如果是线程的并发

  • Vue高级特性概念原理详细分析

    目录 1. 自定义v-model 2. $nextTick 3. slot 插槽 4. Vue 动态组件 5. Vue 异步组件 6. 使用 keep-alive 缓存组件 7. mixin 混入 1. 自定义v-model Vue中的自定义v-model指的是在自定义组件中使用v-model语法糖来实现双向绑定.在Vue中,通过v-model指令可以将表单元素的值与组件实例的数据进行双向绑定.但是对于自定义组件,如果要实现v-model的双向绑定,就需要自定义v-model的实现方式. 自定义

  • Docker中的镜像详细介绍

    Docker中的镜像详细介绍 Docker镜像可以理解为运行在Docker容器中的一个组件,本节将会带领大家学会Docker镜像的下载.搜索.查看.添加删除等一系列操作.Docker在运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker会尝试从默认的远程仓库Docker Hub下载. 一.获取镜像:  1.Docker从网络上下载镜像的格式为: docker pull [NAME]:[TAG] 其中TAG即为标签,如果不指定标签的话,那么默认会下载最新版本的镜像. 比如从Docke

  • Java 对象序列化 NIO NIO2详细介绍及解析

    Java 对象序列化 NIO NIO2详细介绍及解析 概要: 对象序列化 对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而可以保存到磁盘或者进行网络传输,其它程序获得这个二进制流后可以将其恢复成原来的Java对象. 序列化机制可以使对象可以脱离程序的运行而对立存在 序列化的含义和意义 序列化 序列化机制可以使对象可以脱离程序的运行而对立存在 序列化(Serialize)指将一个java对象写入IO流中,与此对应的是,对象的反序列化(Deserialize)则指从IO流中恢

  • Android组件化原理详细介绍

    目录 什么是组件化? 为什么使用组件化? 一步步搭建组件化 1.新建模块 2.统一Gradle版本号 3.创建基础库 4.组件模式和集成模式转换 5.AndroidManifest的切换 6.*业务Application切换 组件之间的跳转 1.添加依赖 2.初始化ARouter 3.添加跳转 组件之间的数据传递 1.定义接口 2.实现接口 组件Application的动态切换 1.定义抽象类 BaseApplication 继承 Application 2.所有的组件的 Application

  • Docker 教程之数据管理详细介绍

    Docker 数据管理 用户在使用 Docker 的过程中,往往需要能查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多个容器之间进行数据的共享,这必然涉及容器的数据管理操作.容器中管理数据主要有两种方式:数据卷(Data Volumes),数据卷容器(Data Volume Containers). 数据卷 数据卷是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性: 1.    数据卷可以在容器之间共享和重用. 2.    对数据卷的更改会立即生效. 3.   

  • Java 什么是注解及注解原理详细介绍

    Java 注解的原理 java : 注解使用,http://www.jb51.net/article/101747.htm 什么是注解 注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包.类.接口.字段.方法参数.局部变量等进行注解.它主要的作用有以下四方面: 生成文档,通过代码里标识的元数据生成javadoc文档. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证. 编译时动态处理,编译

  • Docker 多主机网络通信详细介绍

    最近做项目是关于Docker 的网络通信,需要多个主机进行链接通信,这里记录下,以后便于项目开发,大家需要的话也可以看下,少走些弯路. Docker多主机网络通信详解              Docker支持多主机网络通信功能,可以通过命令行建立多主机通信网络.本文使用Docker machine和Consul服务发现工具来讲解这一点. 前提是需要先安装Docker工具箱. 1.Docker Multi-Host Networking 作为一个示例,我们会在VirtualBox虚拟机上使用do

  • Web Service 运行原理详细介绍

    利用清明小假期,温习了一遍Web Service的相关内容,对其工作原理进行了简要总结.以供有需求的朋友和自己日后参考.文章若有不当之处,敬请朋友们提出宝贵建议,以求共勉. Web服务中,我们应该首先了解相关的术语含义:WSDL.UDDI....相关术语方面的介绍在此不再赘述,重点放在原理上. 在Web服务中,存在三个角色:服务提供者.服务请求者和服务中介,三者之间的关系如图1-1所示 实现一个完整的Web服务包括以下步骤: ◆ Web服务提供者设计实现Web服务,并将调试正确后的Web服务通过

随机推荐