您的位置:首页 > 运维架构 > Docker

虚拟化与Docker容器

2017-06-13 14:36 253 查看
1、虚拟化

虚拟化技术即是对资源的抽象。

如果把x86平台的cpu,内存和外设作为资源,那对应的虚拟化技术就是平台虚拟化,在同一个x86平台上面,可以虚拟多个x86平台,每个平台可以运行自己独立完整的操作系统。

如果把操作系统以及提供的系统调用作为资源,那虚拟化就表现为操作系统虚拟化,例如Linux容器虚拟化技术就是在同一个Linux操作系统之上虚拟多个同样的操作系统,每个应用程序都认为自己运行在一个独立的os上,例如LXL,Docker。操作系统虚拟化允许操作系统内核拥有彼此隔离和分割的多用户空间实例instance
,这些实例也称之为容器,基于Linux内核中的namespace 、cgroup、LXC 、Docker。



2、Docker

Docker 是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker 提供了一个可以运行你的应用程序的封套,或者说容器。Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。



Docker扩展了Linux 容器(Linux Containers),通过一个高层次的API为进程单独提供了一个轻量级的虚拟环境Docker 利用了 LXC, cgroups 和 Linux 自己的内核。和传统的虚拟机不同的是,一个 Docker 容器并不包含一个单独的操作系统,而是基于已有的基础设施中操作系统提供的功能来运行的。

Docker类似虚拟机的概念,但是与虚拟化技术的不同点在于下面几点:

1)、虚拟化技术依赖物理CPU和内存,是硬件级别的;而docker构建在操作系统上,利用操作系统的containerization技术,所以docker甚至可以在虚拟机上运行。

2)、虚拟化系统一般都是指操作系统镜像,比较复杂,称为“系统”;而docker开源而且轻量,称为“容器”,单个容器适合部署少量应用,比如部署一个redis、一个memcached。

3)、传统的虚拟化技术在构建系统的时候较为复杂,需要大量的人力;而docker可以通过Dockfile来构建整个容器,重启和构建速度很快。更重要的是Dockfile可以手动编写,这样应用程序开发人员可以通过发布Dockfile来指导系统环境和依赖,这样对于持续交付有利。

4)、Dockerfile可以基于已经构建好的容器镜像,创建新容器。Dockerfile可以通过社区分享和下载,有利于该技术的推广。

Docker会像一个可移植的容器引擎那样工作它把应用程序及所有程序的依赖环境打包到一个虚拟容器中,这个虚拟容器可以运行在任何一种 Linux 服务器上。这大大地提高了程序运行的灵活性和可移植性,无论需不需要许可、是在公共云还是私密云、是不是裸机环境等等。

同时,Docker也是一个云计算平台,它利用Linux的LXC、AUFU、Go语言、cgroup实现了资源的独立,可以很轻松的实现文件、资源、网络等隔离,其最终的目标是实现类似PaaS平台的应用隔离。

Docker 采用了 C/S架构,包括客户端和服务端。 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。客户端和服务端既可以运行在一个机器上,也可通过socket或者RESTful API来进行通信。



我们知道,在操作系统中,包括内核、文件系统、网络、PID、UID、IPC、内存、硬盘、CPU 等等,所有的资源都是应用进程直接共享的。 要想实现虚拟化,除了要实现对内存、CPU、网络IO、硬盘IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC等等的相互隔离。 前者相对容易实现一些,后者则需要宿主机系统的深入支持。

随着 Linux 系统对于命名空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的命名空间中运行。大家虽然都共用一个内核和某些运行时环境(例如一些系统命令和系统库),但是彼此却看不到,都以为系统中只有自己的存在。这种机制就是容器(Container),利用命名空间来做权限的隔离控制,利用 cgroups 来做资源分配。

命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。

pid命名空间:不同用户的进程就是通过 pid 命名空间隔离开的,且不同命名空间中可以有相同 pid。所有的 LXC 进程在 Docker 中的父进程为Docker进程,每个 LXC 进程具有不同的命名空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。

net 命名空间:有了 pid 命名空间, 每个命名空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的 网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。Docker
默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。

ipc 命名空间:容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在
IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。

mnt 命名空间:类似 chroot,将一个进程放到一个特定的目录执行。mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的
mount point。

uts 命名空间:UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。

user 命名空间:每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

而控制组(cgroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时的对系统资源的竞争。控制组技术最早是由
Google 的程序员 2006 年起提出,Linux 内核自 2.6.24 开始支持,可以提供对容器的内存、CPU、磁盘 IO 等资源的限制和审计管理。

这样的产品设计架构,体现了Docker 的优点:

1)、简化程序:

Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是 Docker的最大优势,过去需要用数天乃至数周的    任务,在Docker容器的处理下,只需要数秒就能完成。

2)、避免选择恐惧症:

如果你有选择恐惧症,还是资深患者。Docker 帮你    打包你的纠结!比如 Docker 镜像;Docker 镜像中包含了运行环境和配置,所以 Docker 可以简化部署多种应用实例工作。比如 Web 应用、后台应用、数据库应用、大数据应用比如 Hadoop 集群、消息队列等等都可以打包成一个镜像部署。

3)、节省开支:

一方面,云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker 改变了高性能必然高价格的思维定势。Docker 与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理的问题,也改变了虚拟化的方式。

3、Docker的生命周期

Docker 包括三个基本概念:镜像(Image)、容器(Container)、仓库(Repository)。

这三部分组成了Docker的整个生命周期,如下图所示,容器是由镜像实例化而来的,这和我们学习的面向对象的概念十分相似,我们可以把镜像想象成类,把容器想象成类经过实例化后的对象,这样就非常好理解镜像和容器的关系了。

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。

Docker 容器通过 Docker 镜像来创建。

容器与镜像的关系类似于面向对象编程中的对象与类。



Docker的镜像概念类似于虚拟机里的镜像,是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了Mysql或用户需要的其它应用程序。

Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统被称为UnionFS。镜像可以基于Dockerfile构建,Dockerfile是一个描述文件,里面包含若干条命令,每条命令都会对基础文件系统创建新的层次结构。Docker
提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。注:镜像是只读的,可以理解为静态文件。

Docker容器:Docker 利用容器来运行应用。Docker容器是由Docker镜像创建的运行实例。Docker容器类似虚拟机,可以支持的操作包括启动,停止,删除等。每个容器间是相互隔离的,容器中会运行特定的应用,包含特定应用的代码及所需的依赖文件。可以把容器看做是一个简易版的
Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。注:相对于镜像来说容器是动态的,容器在启动的时候创建一层可写层作为最上层。

Docker仓库:如果你使用过git和github就很容易理解Docker的仓库概念。Docker 仓库的概念跟Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。Docker
仓库是用来包含镜像的位置,Docker提供一个注册服务器(Register)来保存多个仓库,每个仓库又可以包含多个具备不同tag的镜像。Docker运行中使用的默认仓库是 Docker Hub 公共仓库。

仓库支持的操作类似git,当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了。.

4、Docker简单命令

基本语法:Docker 命令有两大类,客户端命令和服务端命令。前者是主要的操作接口,后者用来启动 Docker daemon。

客户端命令:基本命令格式为 docker [OPTIONS] COMMAND [arg...];

服务端命令:基本命令格式为 docker daemon [OPTIONS]。

可以通过 man docker 或 docker help 来查看这些命令。

1)、安装docker:

[root@NameNode ~]#  yum -y install docker-io

2)、查看docker版本和信息:

[root@NameNode ~]# docker version

Client version: 1.7.1

Client API version: 1.19

Go version (client): go1.4.2

Git commit (client): 786b29d/1.7.1

OS/Arch (client): linux/amd64

Server version: 1.7.1

Server API version: 1.19

Go version (server): go1.4.2

Git commit (server): 786b29d/1.7.1

OS/Arch (server): linux/amd64

[root@NameNode~]# docker info

Containers: 0

Images: 0

Storage Driver: devicemapper

Pool Name: docker-252:1-1687722-pool

Pool Blocksize: 65.54 kB

Backing Filesystem: extfs

Data file: /dev/loop0

Metadata file: /dev/loop1

Data Space Used: 305.7 MB

Data Space Total: 107.4 GB

Data Space Available: 38.33 GB

Metadata Space Used: 729.1 kB

Metadata Space Total: 2.147 GB

Metadata Space Available: 2.147 GB

Udev Sync Supported: true

Deferred Removal Enabled: false

Data loop file: /var/lib/docker/devicemapper/devicemapper/data

Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata

Library Version: 1.02.117-RHEL6 (2016-08-15)

Execution Driver: native-0.2

Logging Driver: json-file

Kernel Version: 2.6.32-642.15.1.el6.x86_64

Operating System: <unknown>

CPUs: 1

Total Memory: 996.1 MiB

Name: wenzhiyi

ID: KAJD:B23V:MBIX:43LO:GENE:EIXT:UI3V:A2W5:E2UI:OAYB:VFEJ:FQLR




3)、Hub仓库查找和下载自己想要的镜像:

[root@NameNode~]# docker  search tensorflow

[root@NameNode~]# docker  pull   tensorflow/tensorflow

4)、启动一个容器nginx:

[root@NameNode ~]# docker run -it -v /root/tensorflow:/root/tensorflow tensorflow/tensorflow bash

-t:在新容器内指定一个伪终端或终端。

-i:允许你对容器内的标准输入 (STDIN) 进行交互。

这样在容器启动后,容器内会自动创建/root.tensorflow的目录。通过这种方式,我们可以明确一点,即-v参数中,冒号前面的目录是宿主机目录,冒号后面的目录是容器内目录。

在使用docker容器时,有时候里边没有安装vim,敲vim命令时提示说:vim: command not found,这个时候就需要安装vim,可是当你敲apt-get install vim命令时,提示:  

Reading package lists... Done  

Building dependency tree        

Reading state information... Done  

E: Unable to locate package vim  

这时候需要敲:apt-get update,这个命令的作用是:同步 /etc/apt/sources.list 和 /etc/apt/sources.list.d 中列出的源的索引,这样才能获取到最新的软件包。  

等更新完毕以后再敲命令:apt-get install vim命令即可。  








5)、保存容器修改:

保存对容器的修改,当你对某一个容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次可以从保存后的最新状态运行该容器。docker中保存状态的过程称之为committing,它保存的新旧状态之间的区别,从而产生一个新的版本。

运行docker commit,可以查看该命令的参数列表。你需要指定要提交保存容器的ID,通过docker ps -l 命令获得,无需拷贝完整的id,通常来讲最开始的三至四个字母即可区分。(译者按:非常类似git里面的版本号)

[root@NameNode ~]# docker commit 20d6bf258be3  tensorflow/tensorflow

b289f2a691c384e2d6d815ea20ea03ebcfb5010ad5d95f439548f21c2af38bb5

然后,docker images查看有没有保存成功

[root@NameNode ~]# docker images

REPOSITORY              TAG                IMAGE ID            CREATED            VIRTUAL SIZE

tensorflow/tensorflow  latest              b289f2a691c3        2 minutes ago      1.32 GB

hello-world            latest              690d80202531        6 weeks ago        1.848 kB

查看容器的详细信息:

[root@TensorFlow ~]#  docker inspect tensorflow/tensorflow

[
{
"Id": "b289f2a691c384e2d6d815ea20ea03ebcfb5010ad5d95f439548f21c2af38bb5",
"Parent": "dc688d70399a43a05103e144f1b3056530b5cc044b48499e7503e8b100fd26ac",
"Comment": "",
"Created": "2018-01-05T01:05:05.951493913Z",
"Container": "20d6bf258be303755f692be72289e8b959f789cf979f8a4d33d40f5f40ca1e6c",
"ContainerConfig": {
"Hostname": "20d6bf258be3",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"PortSpecs": null,
"ExposedPorts": {
"6006/tcp": {},
"8888/tcp": {}
},
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"bash"
],
"Image": "tensorflow/tensorflow",
"Volumes": null,
"VolumeDriver": "",
"WorkingDir": "/notebooks",
"Entrypoint": null,
"NetworkDisabled": false,
"MacAddress": "",
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "1.7.1",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"PortSpecs": null,
"ExposedPorts": {
"6006/tcp": {},
"8888/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"bash"
],
"Image": "",
"Volumes": null,
"VolumeDriver": "",
"WorkingDir": "/notebooks",
"Entrypoint": null,
"NetworkDisabled": false,
"MacAddress": "",
"OnBuild": null,
"Labels": {}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 73972013,
"VirtualSize": 1319690723
}
]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: