尽管之前久闻Docker的大名了,但是天资愚钝,对其到底是个啥东西一直摸不清,最近花了一段时间整理了一下,算是整理出一点头绪来。

官网的介绍是这样的:

Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications....

其实看完这句话还是不明白究竟是啥的,下面就慢慢解释。不过长话短说的话,把他想象成一个用了一种新颖方式实现的超轻量虚拟机,在大概效果上也是正确的。当然在实现的原理和应用上还是和VM有巨大差别的,并且专业的叫法是应用容器(Application Container)。

为啥要用容器?

那么应用容器长什么样子呢,一个做好的应用容器长得就好像一个装好了一组特定应用的虚拟机一样。比如我现在想用Mysql那我就找个装好Mysql的容器,运行起来,那么我就可以使用 Mysql 了。

那么我直接装个 Mysql 不就好了,何必还需要这个容器这么诡异的概念?话是这么说,可是你要真装Mysql的话可能要再装一堆依赖库,根据你的操作系统平台和版本进行设置,有时候还要从源代码编译报出一堆莫名其妙的错误,可不是这么好装。而且万一你机器挂了,所有的东西都要重新来,可能还要把配置在重新弄一遍。但是有了容器,你就相当于有了一个可以运行起来的虚拟机,只要你能运行容器,Mysql 的配置就全省了。而且一旦你想换台机器,直接把这个容器端起来,再放到另一个机器就好了。硬件,操作系统,运行环境什么的都不需要考虑了。

在公司中的一个很大的用途就是可以保证线下的开发环境、测试环境和线上的生产环境一致。当年在 Baidu 经常碰到这样的事情,开发把东西做好了给测试去测,一般会给一坨代码和一个介绍上线步骤的上线单。结果代码在测试机跑不起来,开发就跑来跑去看问题,一会儿啊这个配置文件忘了提交了,一会儿啊这个上线命令写错了。找到了一个 bug 提上去,开发一看,啊我怎么又忘了把这个命令写在上线单上了。类似的事情在上线的时候还会发生,变成啊你这个软件的版本和我机器上的不一样……在 Amazon 的时候,由于一个开发直接担任上述三个职位,而且有一套自动化部署的机制所以问题会少一点,但是上线的时候大家还是胆战心惊。

若果利用容器的话,那么开发直接在容器里开发,提测的时候把整个容器给测试,测好了把改动改在容器里再上线就好了。通过容器,整个开发、测试和生产环境可以保持高度的一致。

此外容器也和VM一样具有着一定的隔离性,各个容器之间的数据和内存空间相互隔离,可以保证一定的安全性。

那为啥不用VM?

那么既然容器和 VM 这么类似为啥不直接用 VM 还要整出个容器这么个概念来呢?Docker 容器相对于 VM 有以下几个优点:

  • 启动速度快,容器通常在一秒内可以启动,而 VM 通常要更久

  • 资源利用率高,一台普通 PC 可以跑上千个容器,你跑上千个 VM 试试

  • 性能开销小, VM 通常需要额外的 CPU 和内存来完成 OS 的功能,这一部分占据了额外的资源

为啥相似的功能在性能上会有如此巨大的差距呢,其实这和他们的设计的理念是相关的。 VM 和 docker的设计图如下:

 

VM 的 Hypervisor 需要实现对硬件的虚拟化,并且还要搭载自己的操作系统,自然在启动速度和资源利用率以及性能上有比较大的开销

Docker 几乎就没有什么虚拟化的东西,并且直接复用了 Host 主机的 OS,在 Docker Engine 层面实现了调度和隔离重量一下子就降低了好几个档次。 Docker 的容器利用了 LXC,管理利用了 namespaces 来做权限的控制和隔离, cgroups 来进行资源的配置,并且还通过 aufs 来进一步提高文件系统的资源利用率。

其中的 aufs 是个很有意思的东西,是 UnionFS 的一种。他的思想和 git 有些类似,可以把对文件系统的改动当成一次 commit 一层层的叠加。这样的话多个容器之间就可以共享他们的文件系统层次,每个容器下面都是共享的文件系统层次,上面再是各自对文件系统改动的层次,这样的话极大的节省了对存储的需求,并且也能加速容器的启动。


Docker 是 用 Go 语言编写的,源代码托管在 github 而且居然只有 1W 行就完成了这些功能。


镜像和容器的关系

类似ISO文件或者虚拟机快照和虚拟机系统之间的关系


链接必须暴露端口,否则没有端口可用


docker怎么实现资源隔离?

cgroup

这是Linux内核功能,它让两件事情变成可能:

    • 限制Linux进程组的资源占用(内存、CPU)
    • 为进程组制作 PID、UTS、IPC、网络、用户及装载命名空间

docker的文件系统

docker使用 union 文件系统

Union文件系统允许通过union装载变化的分层叠加。在union文件系统里,文件系统可以被装载在其他文件系统之上,其结果就是一个变化的分层集合。每个装载的文件系统表示前一个文件系统之后的变化集合,就像是一个diff

当你下载一个镜像,修改它,然后保存成新版本,你只是创建了加载在包裹基础镜像的初始层上的一个新的union文件系统。这使得Docker镜像非常轻,比如:你的DB、Nginx和Syslog镜像都可以共享同一个Ubuntu基础,每一个镜像保存的只是在基础之上工作需要的变化


镜像和容器的本质面目

postgresql的一个镜像

[{
"AppArmorProfile": "",
"Args": [
"postgres"
],
"Config": {
"AttachStderr": true,
"AttachStdin": false,
"AttachStdout": true,
"Cmd": [
"postgres"
],
"CpuShares": ,
"Cpuset": "",
"Domainname": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"Env": [
"PATH=/usr/lib/postgresql/9.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=en_US.utf8",
"PG_MAJOR=9.3",
"PG_VERSION=9.3.5-1.pgdg70 1",
"PGDATA=/var/lib/postgresql/data"
],
"ExposedPorts": {
"5432/tcp": {}
},
"Hostname": "6334a2022f21",
"Image": "postgres",
"MacAddress": "",
"Memory": ,
"MemorySwap": ,
"NetworkDisabled": false,
"OnBuild": null,
"OpenStdin": false,
"PortSpecs": null,
"StdinOnce": false,
"Tty": false,
"User": "",
"Volumes": {
"/var/lib/postgresql/data": {}
},
"WorkingDir": ""
},
"Created": "2015-01-03T23:56:12.354896658Z",
"Driver": "devicemapper",
"ExecDriver": "native-0.2",
"HostConfig": {
"Binds": null,
"CapAdd": null,
"CapDrop": null,
"ContainerIDFile": "",
"Devices": null,
"Dns": null,
"DnsSearch": null,
"ExtraHosts": null,
"IpcMode": "",
"Links": null,
"LxcConf": null,
"NetworkMode": "",
"PortBindings": null,
"Privileged": false,
"PublishAllPorts": false,
"RestartPolicy": {
"MaximumRetryCount": ,
"Name": ""
},
"SecurityOpt": null,
"VolumesFrom": [
"bestwebappever.dev.db-data"
]
},
"HostnamePath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hostname",
"HostsPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hosts",
"Id": "6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da",
"Image": "aaab661c1e3e8da2d9fc6872986cbd7b9ec835dcd3886d37722f1133baa3d2db",
"MountLabel": "",
"Name": "/bestwebappever.dev.db",
"NetworkSettings": {
"Bridge": "docker0",
"Gateway": "172.17.42.1",
"IPAddress": "172.17.0.176",
"IPPrefixLen": ,
"MacAddress": "02:42:ac:11:00:b0",
"PortMapping": null,
"Ports": {
"5432/tcp": null
}
},
"Path": "/docker-entrypoint.sh",
"ProcessLabel": "",
"ResolvConfPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/resolv.conf",
"State": {
"Error": "",
"ExitCode": ,
"FinishedAt": "0001-01-01T00:00:00Z",
"OOMKilled": false,
"Paused": false,
"Pid": ,
"Restarting": false,
"Running": true,
"StartedAt": "2015-01-03T23:56:42.003405983Z"
},
"Volumes": {
"/var/lib/postgresql/data": "/mnt/docker/vfs/dir/5ac73c52ca86600a82e61279346dac0cb3e173b067ba9b219ea044023ca67561",
"postgresql_data": "/mnt/docker/vfs/dir/abace588b890e9f4adb604f633c280b9b5bed7d20285aac9cc81a84a2f556034"
},
"VolumesRW": {
"/var/lib/postgresql/data": true,
"postgresql_data": true
}
}
]

bestwebappever的容器

[{
"AppArmorProfile": "",
"Args": [],
"Config": {
"AttachStderr": true,
"AttachStdin": false,
"AttachStdout": true,
"Cmd": [
"/sbin/my_init"
],
"CpuShares": ,
"Cpuset": "",
"Domainname": "",
"Entrypoint": null,
"Env": [
"DJANGO_CONFIGURATION=Local",
"HOME=/root",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TALPOR_ENVIRONMENT=local",
"TALPOR_DIR=/opt/bestwebappever"
],
"ExposedPorts": {
"80/tcp": {}
},
"Hostname": "44a87fdaf870",
"Image": "talpor/bestwebappever:dev",
"MacAddress": "",
"Memory": ,
"MemorySwap": ,
"NetworkDisabled": false,
"OnBuild": null,
"OpenStdin": false,
"PortSpecs": null,
"StdinOnce": false,
"Tty": false,
"User": "",
"Volumes": {
"/opt/bestwebappever": {}
},
"WorkingDir": "/opt/bestwebappever"
},
"Created": "2015-01-03T23:56:15.378511619Z",
"Driver": "devicemapper",
"ExecDriver": "native-0.2",
"HostConfig": {
"Binds": [
"/home/german/bestwebappever/:/opt/bestwebappever:rw"
],
"CapAdd": null,
"CapDrop": null,
"ContainerIDFile": "",
"Devices": null,
"Dns": null,
"DnsSearch": null,
"ExtraHosts": null,
"IpcMode": "",
"Links": [
"/bestwebappever.dev.db:/bestwebappever.dev.app/db",
"/bestwebappever.dev.redis:/bestwebappever.dev.app/redis"
],
"LxcConf": null,
"NetworkMode": "",
"PortBindings": {
"80/tcp": [
{
"HostIp": "",
"HostPort": ""
}
]
},
"Privileged": false,
"PublishAllPorts": false,
"RestartPolicy": {
"MaximumRetryCount": ,
"Name": ""
},
"SecurityOpt": null,
"VolumesFrom": [
"bestwebappever.dev.requirements-data"
]
},
"HostnamePath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hostname",
"HostsPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hosts",
"Id": "44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4",
"Image": "b84804fac17b61fe8f344359285186f1a63cd8c0017930897a078cd09d61bb60",
"MountLabel": "",
"Name": "/bestwebappever.dev.app",
"NetworkSettings": {
"Bridge": "docker0",
"Gateway": "172.17.42.1",
"IPAddress": "172.17.0.179",
"IPPrefixLen": ,
"MacAddress": "02:42:ac:11:00:b3",
"PortMapping": null,
"Ports": {
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": ""
}
]
}
},
"Path": "/sbin/my_init",
"ProcessLabel": "",
"ResolvConfPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/resolv.conf",
"State": {
"Error": "",
"ExitCode": ,
"FinishedAt": "0001-01-01T00:00:00Z",
"OOMKilled": false,
"Paused": false,
"Pid": ,
"Restarting": false,
"Running": true,
"StartedAt": "2015-01-03T23:56:47.537259546Z"
},
"Volumes": {
"/opt/bestwebappever": "/home/german/bestwebappever",
"requirements_data": "/mnt/docker/vfs/dir/bc14bec26ca311d5ed9f2a83eebef872a879c9e2f1d932470e0fd853fe8be336"
},
"VolumesRW": {
"/opt/bestwebappever": true,
"requirements_data": true
}
}
]

镜像和容器的本质就是一个json。

镜像指定了从该镜像运行的容器的特性,union装载点保存在哪里,要暴露什么端口等等

当从镜像上创建一个容器,Docker会创建一个空白的union文件系统加载在与该镜像关联的union文件系统之上

镜像是只读的,容器在启动的时候创建一层可写层作为最上层(layer)


docker 相比 VM 众多的有点造成了 docker 的迅速兴起

首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。 其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。

容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。

具体说来,Docker 在如下几个方面具有较大的优势。

更快速的交付和部署

对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。

更高效的虚拟化

Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。

更轻松的迁移和扩展

Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

更简单的管理

使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。

对比传统虚拟机总结

特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个

what is docker的更多相关文章

  1. docker——容器安装tomcat

    写在前面: 继续docker的学习,学习了docker的基本常用命令之后,我在docker上安装jdk,tomcat两个基本的java web工具,这里对操作流程记录一下. 软件准备: 1.jdk-7 ...

  2. Docker笔记一:基于Docker容器构建并运行 nginx + php + mysql ( mariadb ) 服务环境

    首先为什么要自己编写Dockerfile来构建 nginx.php.mariadb这三个镜像呢?一是希望更深入了解Dockerfile的使用,也就能初步了解docker镜像是如何被构建的:二是希望将来 ...

  3. Docker 第一篇--初识docker

    已经多年不写博客, 看完<晓松奇谈>最后一期猛然觉醒, 决定仔细梳理下自己这几年的知识脉络. 既然决定写, 那么首先就从最近2年热门的开源项目Docker开始.Docker 这两年在国内很 ...

  4. 在docker中运行ASP.NET Core Web API应用程序(附AWS Windows Server 2016 widt Container实战案例)

    环境准备 1.亚马逊EC2 Windows Server 2016 with Container 2.Visual Studio 2015 Enterprise(Profresianal要装Updat ...

  5. docker for mac 学习记录

    docker基本命令 docker run -d -p 80:80 --name webserver nginx 运行容器并起别名 docker ps 展示目前启动的容器 docker ps -a 展 ...

  6. scrapy爬虫docker部署

    spider_docker 接我上篇博客,为爬虫引用创建container,包括的模块:scrapy, mongo, celery, rabbitmq,连接https://github.com/Liu ...

  7. [原][Docker]特性与原理解析

    Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...

  8. 开发者的利器:Docker 理解与使用

    困扰写代码的机器难免会被我们安装上各种各样的开发工具.语言运行环境和引用库等一大堆的东西,长久以来不仅机器乱七八糟,而且有些相同的软件还有可能会安装不同的版本,这样又会导致一个项目正常运行了,却不小心 ...

  9. 使用python自动生成docker nginx反向代理配置

    由于在测试环境上用docker部署了多个应用,而且他们的端口有的相同,有的又不相同,数量也比较多,在使用jenkins发版本的时候,不好配置,于是想要写一个脚本,能在docker 容器创建.停止的时候 ...

  10. 微服务与Docker介绍

    什么是微服务 微服务应用的一个最大的优点是,它们往往比传统的应用程序更有效地利用计算资源.这是因为它们通过扩展组件来处理功能瓶颈问题.这样一来,开发人员只需要为额外的组件部署计算资源,而不需要部署一个 ...

随机推荐

  1. T-SQL游标

    游标是面向行的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能. 在性能上,游标会迟更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量. 用一个比喻来说明为什么游标会占 ...

  2. NSLog中的%@

    [NSLog中的%@] There is one additional substitution token available in Objective-C, %@, used to denote ...

  3. Oracle用户的单张表的读写权限控制

    在oracle数据库的用户下,一张表需要做读写控制,只能读和写,不能删除和修改.开发人员开始想从用户权限上去实现. 经过一番讨论,判读从权限上去实现该需求是不合适的. 这个用户下很多表,根本不会被一个 ...

  4. csu oj 1811: Tree Intersection (启发式合并)

    题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1811 给你一棵树,每个节点有一个颜色.问删除一条边形成两棵子树,两棵子树有多少种颜色是有 ...

  5. CodeForces 534B Covered Path (水题)

    题意:给定两个速度,一个一初速度,一个末速度,然后给定 t 秒时间,还每秒速度最多变化多少,让你求最长距离. 析:其实这个题很水的,看一遍就知道怎么做了,很明显就是先从末速度开始算起,然后倒着推. 代 ...

  6. Linux下查看文件和文件夹大小的df和du命令

        转自:http://www.yayu.org/look.php?id=162 当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择. df可以查看一级文件夹大小.使用比 ...

  7. [转]C/C++中的memset

    http://blog.csdn.net/songuooo/article/details/7819790 1. 需要的头文件 C中为<memory.h> 或 <string.h&g ...

  8. IPv4&IPv6双重协议栈

    IPV4 TCP客户与IPV6服务器之间的通信: 1 启动IPV6服务器,创建套接监听口,绑定通配地址 2 IPV4调用gethostbyname找到该服务器对应的A记录 3 调用connect,向服 ...

  9. Ext.grid.Panel 数据动态改变后刷新grid

    gridPanel中加载的数据分为两种:一种是本地数据加载,那另一种就是后台数据加载. 在表格中增.删.改.查 是必不可少的. 那么数据动态改变后怎样刷新表格中的数据呢. 一.后台取数据 var gr ...

  10. TextView 实现复制文本功能

    Android api 11 以后可以直接设置 android:textIsSelectable="true" <TextView android:layout_width= ...