Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 4 万 6 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
下面的图片比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。
Docker 包括三个基本概念
理解了这三个概念,就理解了 Docker 的整个生命周期
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资
源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境
变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂
载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于
是一个 root 文件系统。
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在
Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中
的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被
创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的
独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、
自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环
境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容
器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学
Docker 时常常会把容器和虚拟机搞混。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为
基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而
准备的存储层为容器存储层。
镜像构建完成后,可以很容易的在当前宿主上运行,但是,如果需要在其它服务器
上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry
就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多
个标签(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的
各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版
本的镜像。如果不给出标签,将以 latest 作为默认标签。
以 Ubuntu 镜像 为例,ubuntu是仓库的名字,其内包含有不同的版本标签,如, 14.04 , 16.04 。我们可以通过 ubuntu:14.04 ,或者 ubuntu:16.04
来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu ,那将视为ubuntu:latest 。
Docker做的事情就是将的软件隔离起来,让它们即使出了问题也不会互相影响。这并不是什么横空出世的新思想。
你很可能会说内核控制的进程不就这样玩么?每一个进程都有自己的内存空间,并且在一个进程自身看来,内存
空间与所处在的计算机的内存空间是一样的。然而内核欺骗了进程,在背后将内存地址重新映射到了真实的内存
空间中。想想今天高速运转处理器,任何地方见到的系统都能同时运行多个进程。今天的文明社会比人类历史任
何一个时间点制造的谎言数量级都要很多的量级。
Docker将进程的隔离模型的进行了扩展,让隔离性变得更强。Docker是在Linux内核的基础上打造的一系列工具。
整个文件系统被抽象了,网络被虚拟化了,其它进程被隐藏了,并且从理论上,不可能逃脱容器去对在一个机器
上的其他进程搞破坏。实际中,每个人对于怎么才能逃脱容器,至少去收集一点运行容器的机器的相关信息,持
开放的态度。跟虚拟机比较起来,容器的隔离性还是较弱。
但换个角度看,进程比容器性能更好,容器性能比虚拟机性能更佳。原因很简单,隔离性更高,在每一个上下文
中就需要运行更多的东西,从而拖慢速度。选择一个隔离性的过程,实际就是决定你对要运行进程的信任有多少
的过程 - 它会不会去干扰其他的进程?如运行的进程都是自己的亲儿子,那你对他们会有一个很高的信任度,对
他们用最少的隔离,运行在一个进程中就行了。如果是SAP,那么你很可能需要尽可能高的隔离性:将电脑装在封
存在箱子里,绑在火箭上发射到月球。
Docker另一个很好的特性是,容器可以作为一个整体交付。他们不会像虚拟机那么臃肿。这大大的提高了部署的简易度。
Docker 底层的核心技术包括 Linux 上的命名空间(Namespaces)、控制组
(Control groups)、Union 文件系统(Union file systems)和容器格式
(Container format)
镜像是一种层叠的只读文件系统。容器中程序的执行仍然是使用本机操作系统的,容器并不自己构建操作系统,而是以某种隔离的方式依赖本机操作系统工作。这就是Docker和虚拟机的本质区别。
什么是容器
依托于linux内核的虚拟化技术
什么是Docker
能够把应用程序自动部署到容器的开源引擎
Docker的目标
创建软件程序可移植的轻量容器,让其可以在任何安装了Docker的机器上运行,而不用关心底层操作系统,类似于船舶使用的集装箱.
对 Docker 应用最广泛的三个领域分别是:Test/QA 应用;Web 应用;大数据,企业应用。
命名空间(Namespace):Docker 有五个命名空间:进程、网络、挂
载、宿主和共享内存,为了隔离有问题的应用,Docker 运用 Namespace 将进
程隔离,为进程或进程组创建已隔离的运行空间,为进程提供不同的命名空间视
图。这样,每一个隔离出来的进程组,对外就表现为一个 container(容器)。需
要注意的是,Docker 让用户误以为自己占据了全部资源,但这并不是”虚拟机”
内核 namesapce 从内核 2.6.15 之后被引入。
Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,
然后发布到任何流行的 Linux 机器上,便可以实现虚拟化
1 | Ubuntu 14.04 LTS部署 (不同版本见部署方式略有差别) |
1 | git clone https://github.com/docker/docker.git |
#安装好配置镜像加速器(阿里云)
1 | vi /etc/default/docker |
#查看仓库中镜像
1 | docker search ubuntu |
#从官方仓库中下载image
1 | docker pull centos |
#查看本地可用的image
1 | docker images |
#启动一个ubuntu的dokcer容器,直接进入bash
1 | docker run -it ubuntu bash |
#后台启动一个ubuntu的dokcer容器
1 | docke run -d -it ubuntu |
#后台启动一个ubuntu的dokcer容器,监听80端口,映射到容器里的8080端口
1 | docke run -p 80:8080 -d -it ubuntu |
docker run 参数
–name 标记可以为容器自定义命名。
–rm 标记,则容器在终止后会立刻删
除。注意, –rm 和 -d 参数不能同时使用。
#查看正在运行的docker容器
1 | docker ps |
#查看所有docker容器
1 | docker ps -a |
#获取所有容器ID
1 | docker ps -a -q |
#然后停掉所有的容器
1 | docker kill $(docker ps -a -q) |
#在运行状态的容器里启动一个bash(进入正在运行的容器)
1 | docker exec -it $container_id bash |
#启动/关闭/重启容器
1 | docker start/stop/restart $container_id |
#Docker 容器镜像删除,停止所有的container,这样才能够删除其中的images
1 | docker stop $(docker ps -a -q) |
#删除容器
1 | docker rm $container_id |
#如果想要删除所有container的话再加一个指令
1 | docker rm $(docker ps -a -q) |
#删除images,通过image的id来指定删除谁
1 | docker rmi <image id> |
#想要删除untagged images,也就是那些id为
1 | docker rmi $(docker images | grep "^<none>" | awk "{print $3}") |
#要删除全部image的话
1 | docker rmi $(docker images -q) |
#查看悬挂镜像
1 | docker images -f dangling=true |
#查看容器运行前后不同
1 | docker diff webserver |
#容器与主机之间复制文件
1 | docker cp foo.txt mycontainer:/foo.txt |
#在主机里使用以下命令可以查看指定容器的信息
1 | docker inspect container |
#获取容器的log信息
1 | sudo docker logs [container ID or NAMES] |
#创建自定义镜像,打包应用
1 | mkdir test_image |
#创建Dockerfile
1 | vi Dockerfile |
#自定义image
#继承一个已有的镜像,这里用nginx1.8.1作为基础
1 | FROM nginx:1.8.1 |
#打包镜像,镜像名为web,版本为1.0
1 | docker build -t web:v1 . |
#启动镜像,并启动web,把本机的8080端口映射到容器的80端口
1 | docker run --name web -d -p 8080:80 web:v1 |
#push到docker hub,先登录,打标签,push
1 | #docker login |
#导出镜像,导入镜像(直接通过文件来分发镜像到其他机器)
1 | docker save -o myweb1.0.image myweb:v1 |
Run anywhere
Docker:Build once,Run anywhere
附录:客户端命令
可以通过 man docker-COMMAND 或 docker help COMMAND 来查看这些命令的具体用法。
1 | attach:依附到一个正在运行的容器中; |
https://docs.docker-cn.com/get-started/
https://yeasy.gitbooks.io/docker_practice/content/basic_concept/
https://github.com/widuu/chinese_docker/blob/master/SUMMARY.md
https://docs-cn.docker.octowhale.com/get_started/001.Orientation.html
https://github.com/DeanXu/Docker-introduce/blob/master/README.md
https://www.zhihu.com/question/28300645
https://blog.lab99.org/post/docker-2016-07-14-faq.html
http://www.csdn.net/article/2014-07-02/2820497-what's-docker
https://docs.docker-cn.com/get-started/
https://linux.cn/article-8192-1.html
http://www.jianshu.com/p/420c66d4d361