Docker镜像、容器剖析
我们通常所说的docker是什么?
在这里英文本意为“搬运工”这里指的的docker搬运点的是集装箱,集装箱装的是够任意类型的APP,开发者通过Docker可以将app变成一种标准化,可移植的、自管理的组件、可以在任意主流的系统开发,调试和运行
简单的来说,docker是一种用了新颖的方式实现的轻量级的虚拟化,类似于VM,但是在原理和应用上和VM还是有很大的区别,其专业的名称是应用容器;
所谓应用容器,就比如将Nginx,Mysql等软件程序将其封装成一组特定的虚拟机一样,如果我们想用Nginx这些应用的话,我们直接运行这个容器就好了,这就相当于运行起来的虚拟机一样,只要能运行,这些配置都省下来了,如果说系统层面的宿主机出现了异常需要迁移,那么我们将这个容器转移平台就可以的
Docker基于Go语言开发的,代码托管在Github上,docker容器可以封装任何有效负载,计划可以在任何服务器之间进行一致性运行,也就是说,开发者构建的应用,只需要一次构建即可多平台运行,运营人员只需要配置服务,即可运行所有应用,
若是利用容器的话,那么开发直接在容器里开发,测试的时候整个同期给测试,测好了把测试后容器再上线就好了,通过容器,整个开发,测试和生产环境保持高度一致;
此外容器也有和VM一样具有一定的隔离性,各个容器之间的数据和内存之间相互隔离,可以保证一定的安全性
Hyper-V、KVM和XEN等虚拟机管理程序都是“”基于虚拟化硬件仿真机制”,这就意味着,它们对系统硬件要求很高,然而,容器确实使用共享的操纵系统,它们在使用系统资源方面比虚拟机管理程序要高效很多,容器不是对硬件要求进行的虚拟化的处理,而是驻留在一个linux实例上,Docker可以解决虚拟机能够解决的问题,同时也能解决虚拟机由于资源要求过高而无法解决的问题
为什么使用Docke?或者是docker有哪方面的优势
1)快速的交付应用程序
开发者使用一个标准image来构建开发容器,开发完成之后,系统管理员就可以使用这个容器来部署代码
docker可以快速的创建容器,快速的迭代应用程序,并让整个过程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的
docker容器轻量级,启动很快,启动的时候是次秒级的,节约开发,测试,部署时间
2)更容易部署和扩展
docker容器可以在几乎所有环境中运行,物理机,虚拟机,公有云,私有云,服务器等等
docker容器兼容很多平台,这样就可以把一个应用程序从一个平台上迁移到到另一个
3)效率高
Docker容器不需要hypervisor,他是内核级虚拟化
4)快速的部署也以为这更简单的管理
通常是需要小小的改变就可以替代以往巨型和大量更新工作
【Docker常用的案列】
自动打包和部署应用
创建轻量,私有的Paas环境
自动化测试和持续集成/部署
部署并扩展WEB应用/数据库和后端服务器
【VM的选用】
docker容器相对于VM还是有很多优点的,
1)启动速度快,容器通常在一秒内可以启动,而VM要很久
2)资源利用率小,一台普通服务器可以跑上个容器
3)性能开销小,VM需要额外的CPU和内存来完成OS的功能,这一部分占据了额外的资源
下图所示:对比了docker和传统虚拟化(KVM,XEN等)方式的不同之处,Docker容器是在操作系统层面上实现了虚拟化,直接复用本地主机操作系统,而传统方式则是在硬件基础上,虚拟出自己的系统,再在系统上部署相关的APP应用
传统虚拟化方案:Nginx(图一)
Docker虚拟化解决方案:Nginx镜像-nginx容器-对外访问
Docker虚拟化三个概念解析:镜像,容器,仓库
镜像:Docker的镜像其实就是模板,跟我们常见的ISO镜像类似,是一个样板
容器:使用镜像常见的应用或者系统,称之为一个容器
仓库:仓库是存放镜像的地方,分为公开仓库(Public)和私有仓库(Private)两种形式
Docker最早为LXC+AUFS组合,Docker0.9.0版本开始引入libcontainer,可以视作LXC的替代品,其中LXC负责资源管理,AUFS负责镜像管理;而LXC包括Cgroup,namespace,chroot等组件,并通过cgroup进行资源管理
从资产管理来看,Docker,LXC,Cgroup三者的关系是:cgroup在最底层落实资源管理,LXC在cgroup上封装了一层,Docker又在LXC封装了一层
Cgroup是linux内核提供的一种可以限制,记录,隔离进程组所使用的物理资源(如CPU,Memory,IO等)的机制,Cgroups也是LXC为实现虚拟化所使用的资源管理手段,可以说没有Cgroups也就没有LXC,也就没有Docker
Cgroup最初的目的地为资源管理提供一个统一的框架,即整合现有的Cgroup等子系统,也为未来开发新的子系统提供接口,现在的Cgroup使用与多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化
LXC容器可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性,容器有效的将单个系统管理的资源划分到独立的组中,以便更好的在独立的组之间平衡有冲突的资源使用需求
【对比传统的虚拟机总结】
【Docker的体系结构】
docker使用C/S架构,docker daemon作为server端接受clinet的请求,并处理(创建,运行,分发容器)他们可以运行在一个机器上,也通过socket或者去RESTful API通信
Docker daemon一般在宿主机后台运行,docker clinet以系统命令形式存在,用户用docker命令与docker daemon交互;
docker守护进程(docker daemon)
如图上所示,docker守护进程运行在一台主机上,用户并不直接和守护进行交互,而通过docker客户端间接和其通信
Docker客户端(Docker client)
Docker客户端实际上是docker的二进制程序,是用户与docker交互方式,它接受用户指令并且与背后的docker守护进程通信;
Docker 内部:
要理解 Docker 内部构建,需要理解以下三种部件:
Docker 镜像 - Docker images
docker镜像是docker容器运行时的只读模板,镜像可以用来创建docker容器。每个镜像由一系列层的(layers)组成,Docker使用的UFS(联合文件系统)来讲这些层联合到单独的镜像中,
UFS允许独立文件系统中的文件和文件夹被透明覆盖,形成一个单独连贯的文件系统,正因为有了这些镜像层的存在,docker是如此的轻量,当你改变一个docker镜像,比如说升级到某个程序最新版本,这样一个新的层会被创建,不需要替换或者是说重新创建,只需要升级,层使得分发docker镜像变得简单和快速;
每个docker都有很多层次构成,docker使用 union file systems 将这些不同的层结合到一个image 中去。
例如:centos镜像中安装nginx,就成了nginx镜像”,其实在此时Docker镜像的层级概念就体现出来了。底层一个centos操作系统镜像,上面叠加一个ngnx层,就完成了一个nginx镜像的构建。层级概念就不难理解,此时我们一般centos操作系统镜像称为nginx镜像层的父镜像。
Docker 仓库 - Docker registeries
docker仓库用来保存镜像,可以理解为代码控制中的代码仓库,同样的,docker仓库也有共公有和私有的概念,公有Docker仓库名字是docker hub,docker hub提供了庞大的镜像集合供使用,这些镜像可以是自己创建的,或者在比人的镜像基础上创建
仓库是集中存放镜像文件的场所,有时候会把仓库 和仓库注册服务器(Regitry)混为一谈,
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。国内的公开仓库包括 Docker Pool等,可以提供大陆用户更稳定快速的访问。
当然,用户也可以在本地网络内创建一个私有仓库。
当用户创建了自己的镜像之后就可以使用push命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上pull下来就可以了。
*注:Docker 仓库的概念跟Git类似,注册服务器可以理解为 GitHub 这样的托管服务。
Docker 容器 - Docker containers
Docker 利用容器来运行应用,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个 Docker 容器都是从 Docker 镜像创建的。Docker 容器可以运行、开始、停止、移动和删除。每一个 Docker 容器都是独立和安全的应用平台。
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
可以把容器看做是一个简易版的Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
*注:镜像是只读的,容器在启动的时候创建一层可写层作为最上层
【总结】到此,我们来总结一下Docker的优势和缺点
优势:1)快速的部署:短时间类可以部署成百上千个应用,快速的交付到线上;
2)高效的虚拟化:传统的虚拟化是基于硬件层面上实现的虚拟化,而我们的docker是在系统层面上实现的,不需要基于Hyprevisor的支持,想比较虚拟机大幅度提高性能和效率
3)节省开支:更好的提高服务器的利用率;
4)简化配置:将运行的环境打包至容器。使用时直接启动即可
5)快速的迁移扩展:我们可以快速将容器进行打包迁移到需要的平台上应用,拥有良好的兼容性;
ps:这里Hyprevisor可以理解基础物理服务器和操作系统中间的软件层,可以协调硬件资源的访问
缺点:1)隔离性:虽然docker提供了Namespace隔离系统资源,但是应用之间的隔离还不如虚拟机
2)安全性:目前docker server无法判断容器是由哪个用户启动的,比如说,删除的时候不会做用户权限审核;
3)稳定性以及功能兼容性:因为docker版本还在不断的更新迭代中,很多功能都依赖于高版本的linux系统内核,因此兼容性还是存在一些问题
进入正在运行的容器
进入正在使用的容器有三种方式,分别为:
1)docker attach 【容器名称】:attach类似于VNC,操作会在各个容器界面显示。所以进入容器的操作都是同步显示,但是呢,exit退出容器,,容器也随停止掉,故不推荐使用
2)docker exec -it 【容器名称/id】:执行单次命令进入容器,不是很推荐此方式,虽然exit退出后容器不会停止
3)nsenter -t 【容器pid】-m -u -i -n -p:通过这种方式进入容器,前提是必须要知道容器的PID号,才能执行,可以通过docker inspect -f {{.State .Pid}} 【容器名称】来获取容器的PID号
拓展一下哈,我们可以写成一个脚本工具,通过这个脚本来进入容器
shell# vim docker- in # !/bin/ bash docker - in (){ NAME =$ 1 PIDNAME =$(docker inspect -f " {{.State.Pid}} " ${NAME}) nsenter -t ${PIDNAME} -m -u -i -n - p } docker - in $ 1 # chmod +x docker- in
# docker-in centos7
[root@f3d64292da7f /]# ls
bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
【创建自己的镜像】
一:使用docker commit 来扩展一个image
1.1:先下载一个容器
docker pull training/sinatra
1.2用容器启动这个镜像
docker run -t -i training/sinatra /bin/bash
1.3:接下来给使用中的容器,添加自己需要的工具,组装自己的运行环境
1.4:结束后,我们使用exit来退出,现在我们的容器已经改变了,我们使用docker commint命令来提交相应的副本
docker commit -m “Added json gem” -a “xiaoyu” 0b2616b0e5a8 ouruser/sinatra:v2
其中, -m 来指定提交的说明信息,跟我们使用的版本控制工具一样; -a 可以指定更新的用户信息;之后是用来创建镜像的容器的ID;最后指定目标镜像的仓库名和tag 信息。创建成功后会返回这个镜像的ID信息。
二:从dockerfile来创建image
使用docker commit 来扩展一个image 比较简单,但它不容易在一个团队中分享它。我们使用docker build 来创建一个新的image 。为此,我们需要创建一个dockerfile,包含一些如何创建我们的image 的指令。现在,我们来创建一个目录和一个dockerfile
Dockerfile基本的语法是
使用#来注释
FROM指令告诉Docker 使用哪个镜像作为基础(docker使用哪个image 源)
MAINTAINER是维护者的信息
RUN开头的指令会在创建中运行,比如安装一个软件包,在这里使用yum来安装了一些软件
编写完成Dockerfile后可以使用docker build 来生成镜像。
2.1、创建镜像所在的文件夹和Dockerfile文件
命令:
1、mkdir sinatra
2、cd sinatra
3、touch Dockerfile
2.2、在Dockerfile文件中写入指令,没一条指令都会更新镜像的信息例如:
# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith ksmith@example.com
RUN apt-get update && apt-get install -y ruby ruby-dev
RUN gem install sinatra
格式说明:
每行命令都是以 INSTRUCTION statement 形式,就是命令+ 清单的模式。命令要大写,“#”是注解。
FROM 命令是告诉docker 我们的镜像什么。
MAINTAINER 是描述 镜像的创建人。
RUN 命令是在镜像内部执行。就是说他后面的命令应该是针对镜像可以运行的命令。
2.3、创建镜像
命令:docker build -t ouruser/sinatra:v2 .
docker build 是docker创建镜像的命令
-t 是标识新建的镜像属于 ouruser的
sinatra是仓库的名称
:v2 是tag
“.”是用来指明 我们的使用的Dockerfile文件当前目录的
2.4、创建完成后,从镜像创建容器
docker run -t -i ouruser/sinatra:v2 /bin/bash
其中 -t 标记来添加 tag,指定新的镜像的用户信息。“.”是Dockerfile所在的路径(当前目录),也可以替换为一个具体的Dockerfile的路径。
可以看到 build 进程在执行操作。它要做的第一件事情就是上传这个Dockerfile内容,因为所有的操作都要依据Dockerfile来进行。然后,Dockfile中的指令被一条一条的执行。每一步都创建了一个新的容器,在容器中执行指令并提交修改(就跟之前介绍过的docker commit 一样)。当所有的指令都执行完毕之后,返回了最终的镜像 id。所有的中间步骤所产生的容器都被删除和清理了。
*注意一个镜像不能超过 127 层
拓展:
进入容器
在使用 -d 参数时,容器启动后会进入后台。某些时候需要进入容器进行操作,有很多种方法,包括使用docker attach 命令或nsenter命令。
使用docker attach进入容器
1.docker attach 允许我们进入后台进程.
2.--sig-proxy=false 不使用容器转发信号,允许我们使用 ctrl -c 来退出,执行dockerps查看在后台运行
但是使用 attach 命令有时候并不方便。当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口也无法执行操作了。
也可以执行docker exec进入运行的容器
docker exec -it 容器ID/名称 /bin/bash
以上命令返回一个命令界面,exec代表直接在容器中运行命令
容器导入和导出
导出容器
docker export [容器 id] > [导出文件]
如果要导出本地某个容器,可以使用docker export 命令。
导入容器
可以使用docker import 从容器快照文件中再导入为镜像
# cat centos6.tar | docker import – centos6:test
#docker images