docker镜像与容器概念认识
本⽂从⼀种使⽤场景来引出docker,并讨论了什么是镜像,容器,仓库,以及docker的相关概念。
试想⼀种使⽤场景:
我的wordpress 博客⽹站现在部署在阿⾥云服务器上,但是在后期的使⽤中我有可能有这样⼀种需求,阿⾥云太贵,我可能实在付不起每⽉⽉租,想把我的服务迁到其他的云服务上,⽽⼜想完整的将我的wordpress服务和数据从阿⾥云迁出,并将其部署到另外的云服务上。那么我怎样解决这个问题?
第⼀种⽅案:在新主机上部署⼀套环境,然后将项⽬⽬录和mysql服务倒出,这种⽅法⽐较费⼒,⽽且由于相关依赖包⽐价复杂,容易出错。
第⼆种⽅案:在主机上安装⼀个KVM之类的虚拟机,可以将wordpress服务部署在KVM的虚拟机上,当我想迁出服务时我就将虚拟机的相关⽂件倒出,但是由于云服务主机的配置不是很⾼,才1核1G。这样就会耗费我很多的资源,我的wordpress明明可以跑在宿主机上,但是现在却要跑在虚拟机⾥。那么有没有什么⽅法可以解决我的问题?
⽅案四:使⽤Docker:Build, Ship and Run Any App, Anywhere!
什么是 Docker与Docker官⽅相关技术简介(可略读):
Docker是⼀套轻量级操作系统虚拟化解决⽅案,它由go语⾔编写。它基于Linux容器技术(LXC),Namespace,Cgroup,UnionFS(联合⽂件系统)等技术。
namespace(命名空间):命名空间是 Linux 内核⼀个强⼤的特性。每个容器都有⾃⼰单独的名字空间,运⾏在其中的应⽤都像是在独⽴的操作系统中运⾏⼀样。名字空间保证了容器之间彼此互不影响。docker实际上⼀个进程容器,它通过namespace实现了进程和进程所使⽤的资源的隔离。使不同的进程之间彼此不可见。我们可以把Docker容器想像成进程+操作系统除内核之外的⼀套软件。
cgroup(控制组):是 Linux 内核的⼀个特性,主要⽤来对共享资源进⾏隔离、限制、审计等。只有能控制分配到容器的资源,才能避免当多个容器同时运⾏时的对系统资源的竞争。控制组技术最早是由 Google 的程序员 2006 年起提出,Linux 内核⾃ 2.6.24 开始⽀持。控制组可以提供对容器的内存、CPU、磁盘 IO 等资源的限制和审计管理。
UnionFS(联合⽂件系统):Union⽂件系统(UnionFS)是⼀种分层、轻量级并且⾼性能的⽂件系统,它⽀持对⽂件系统的修改作为⼀次提交来⼀层层的叠加,同时可以将不同⽬录挂载到同⼀个虚拟⽂件系统下(unite several directories into a single virtual filesystem)。Union ⽂件系统是 Docker 镜像的基础。镜像可以通过分层来进⾏继承,基于基础镜像(没有⽗镜像),可以制作各种具体的应⽤镜像。另外,不同 Docker 容器就可以共享⼀些基础的⽂件系统层,同时再加上⾃⼰独有的改动层,⼤⼤提⾼了存储的效率。Docker 中使⽤的
AUFS(AnotherUnionFS)就是⼀种 Union FS。 AUFS ⽀持为每⼀个成员⽬录(类似 Git 的分⽀)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限, 同时 AUFS ⾥有⼀个类似分层的概念, 对只读权限的分⽀可以逻辑上进⾏增量地修改(不影响只读部分的)。
Docker ⽬前⽀持的 Union ⽂件系统种类包括 AUFS, btrfs, vfs 和 DeviceMapper。
谈谈我的理解
1.Docker的⽣命周期:
Docker的⽣命周期包含三个部分,镜像,容器,仓库,我们可以把镜像,容器想像成java的类和对象,即容器是由镜像实例化⽽来的。也就是说我们想使⽤装有相关软件的镜像,⾸先要把镜像创建成容器。现在是不是对镜像,容器,仓库这些概念还存在⼀些困惑?那么让我们动⼿体验⼀下docker的神奇,然后慢慢理解这些概念。
2.Docker的安装:
由于docker要使⽤LXC,namespace,cgroup等Linux内核相关技术,⽽mac⼜是基于unix的,所以要使⽤boot2docker来使⽤
docker,boot2docker实际上是⼀个Linux的轻量级发⾏版,⼀共24M⼤⼩,完全运⾏于内存中。但有⼀个问题,那就是下载它需要
FQ,so…已将其安装⽂件放到了百度云盘中,想下载的可以点击下载。另外boot2docker需要运⾏在virtual Box上,所以请先下载virtualBox。安装之后直接点击如下图标:
此时会出现⼀个终端,并进⾏⼀些初始化操作,待期完成后,运⾏如下命令:
1. bash−3.2$ boot2docker ssh
既可以登录到boot2docker,boot2docker已经为我们预安装好docker相关环境。我们可以运⾏如下命令查看docker相关版本信息:
1. docker@boot2docker:~$ docker version
2. Client version: 1.
3.1
3. Client API version: 1.15
4. Go version (client): go1.3.3
5. Git commit (client): 4e9bbfa
6. OS/Arch (client): linux/amd64
7. Server version: 1.3.1
8. Server API version: 1.15
9. Go version (server): go1.3.3
10. Git commit (server): 4e9bbfa
下⾯让我们在boot2docker中下载第⼀个docker镜像:我们下载镜像可以从docker官⽅为我们提供的dockerHub上下载,但由于dockerHub的下载速度⾮常慢,所以这⾥推荐个不错的国内源:。我们可以使⽤docker  pull命令来从此源下载镜像:
1. docker pull docker/docker/centos:centos6
3.理解Docker的⽣命周期中的镜像:
在下载的过程中我们可以看到docker的镜像好像是在⼀层⼀层的在下载,如下图:
1. docker@boot2docker:~$ docker pull docker/docker/centos:centos6
2. Pulling repository docker/docker/centos
3. 70441cac1ed5: Download complete
4. 511136ea3c5a: Download complete
5. 5b12ef8fd570: Download complete
6. Status: Image is up to date for docker/docker/centos:centos6
那么docker的镜像到底是什么呢?我们来解释⼀下docker的镜像的概念。
docker的镜像实际上由⼀层⼀层的⽂件系统组成,这种层级的⽂件系统就是上⽂说到的UnionFS。在Docker镜像的最底层是bootfs。这⼀层与我们典型的Linux/Unix系统是⼀样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使⽤权已由bootfs转交给内核,此时系统也会卸载bootfs。Docker在bootfs之上的⼀层是rootfs(根⽂件系统)。rootfs就是各种不同的操作系统发⾏版,⽐如Ubuntu,Centos等等。
docker镜像的层级结构图:
那么docker的rootfs与传统意义的rootfs不同之处到底是什么呢?
传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统⾃检之后将rootfs从read-only改为read-write。然后我们就可以在rootfs上进⾏写和读的操作了。但docker的镜像却不是这样,他在bootfs⾃检完毕之后并不会把rootfs的read-only改为read-write。⽽是利⽤union mount(UnionFS的⼀种挂载机制)将⼀个或多个read-only的rootfs加载到之前的read-only 的rootfs层之上。并在加载了这么多层的rootfs之后,仍然让它看起来只像⼀个⽂件系统,在docker的体系⾥把union mount的这些read-only层的rootfs叫做docker的镜像(image)。请注意,此时的每⼀层rootfs都是read-only的,也就是说我们此时还不能对其进⾏操作,那么我们怎样对其进⾏读写操作呢?
答案是将docker镜像进⾏实例化,就是上⽂说的从镜像(image)变成容器(container)的过程,当镜像被实例化为容器之后,系统会为在⼀层或是多层的read-only的rootfs之上分配⼀层空的read-write的rootfs。⽽这个分配的动作是由docker  run命令发起的,我们可以⽤如下命令创建⼀个容器:
1. docker run −t −i docker/docker/centos:centos6 /bin/bash
此时我们已经进⼊到容器,此处要说明⼀下,我们使⽤的的镜像实际上是在dockerHub提供的镜像之
上进⾏了⼀些修改,来使我们可以更轻松的使⽤镜像,⽐如会把/etc/skel/.b* ⽂件拷贝到/root ⽬录之下等等,也就是说docker是在空的那层rootfs(read-write)对底层的rootfs(read-only)进⾏了修改,那么问题来了,上⽂不是说底层的rootfs是read-only的吗?那为什么docker可以对它进⾏修改?并且还能把修改后的镜像保存起来?这看起来好像⾮常的⽭盾。
其实这并不⽭盾,当我们将⼀个镜像实例化为⼀个容器之后,docker会在read-only 的rootfs之上分配⼀层空的read-write的rootfs,我们对⽂件系统的改变实际上是在空的这层rootfs(read-write)上发⽣的。打个⽐⽅,如果你想修改⼀个⽂件,系统实际上是将这个在read-only层的rootfs的⽂件拷贝到read-write层的rootfs之中,然后对它进⾏修改,但read-only层的⽂件并不会被修改,依然存在于read-only层之中,只不过是在read-write层下被隐藏了。这种模式被称为copy on write。这是unionFS的特性。也是docker的强⼤之处,为什么说强⼤呢?它允许镜像被继承,也就是说我们想⽣成⼀套虚拟环境不⽤从零开始了,⽽只要在⼀个相对完善的基础环境之上来创建我们的虚拟环境就可以了,⽐如我们想⽣成⼀个具有tomcat环境的镜像,只要在⼀个装有java环境的镜像之上来创建就可以了。这也是docker便捷性的体现。
4.何为容器
上⽂已经解释了什么是docker的镜像。那么什么⼜是容器呢?我们刚才使⽤docker run命令已经创建了⼀个docker容器,我们先来解释⼀下刚才那条命令:
1. docker run −t −i docker/docker/centos:centos6 /bin/bash
docker run:将镜像实例化成⼀个容器,有点像java中的new。
docker run的两个参数:
-i参数:是以交互模式启动容器
-t参数:分配⼀个tty终端
后⾯还跟了⼀条命令 /bin/bash,指要在容器中运⾏的命令
我们现在在容器中运⾏top命令:
1. top − 20:16:27 up 16:38, 0 users, load average: 0.00, 0.01, 0.05
2. Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie
3. Cpu(s): 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
4. Mem: 2056668k total, 336912k used, 1719756k free, 46280k buffers
5. Swap: 1438672k total, 0k used, 1438672k free, 234792k cached
6.
7. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
docker进入容器
8. 1 root 20 0 14784 3056 2704 S 0.0 0.1 0:00.06 bash
9. 15 root 20 0 14952 1944 1744 R 0.0 0.1 0:00.14 top
然后我们会看到docker容器中只运⾏了两个进程:这与我们常看到的Linux系统进程貌似不⼀样,它并没有init进程,然后我们在保持原有终端不变的情况下,再开⼀个终端,然后登录到boot2docker中(使⽤命令:boot2docker ssh),然后运⾏如下命令:
1. docker@boot2docker:~$ ps −ef | grep top
2. 2010 root top
3. 2052 docker grep top
此时我们可能会⽐较奇怪,我们并没有在boot2docker中运⾏top,那么这个top进程是哪⾥来的呢?
这个top进程就是我们在容器中运⾏的top,这时你可能已经明⽩了,其实docker容器中运⾏的进程实际上就是宿主机上的进程。docker实际上使⽤了命名空间(namespace)来对进程进⾏隔离,使不同namespace的进程彼此不可见,同时使⽤cgroup来对彼此隔离的进程的资源进⾏限制,docker的容器(container)其实就是⼀个进程的容器,⽽并不是⼀个全虚拟化的操作系统,所以他不会有什么init进程。docker 将进程、进程所需要的操作系统、运⾏环境称为容器。所以它⽐传统的基于hypervisor的虚拟机拥有更⾼的效率,并使⽤更低的资源。它实际上是⼀个内核级别的虚拟化技术,容器还是在使⽤宿主机的内核,为了证实上述内容,我们可以在容器中⽤如下命令查看docker的内核版本:
1. [root@5b3a545077e0 /]# uname −a
2. Linux 5b3a545077e0
3.16.4−tinycore64 #1 SMP Thu Oct 23 16:14:24 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
你会发现docker容器使⽤的内核版本与宿主机相同,然后运⾏如下命令:
1. [root@5b3a545077e0 /]# cat /etc/redhat−release
2. CentOS release 6.6 (Final)
你会发现他的发⾏版是centos6.6。
5.什么是镜像仓库
很简单:它就是⼀个存储和共享镜像⽂件的地⽅。
6.附录
附录1.docker与传统虚拟机的对⽐:
特性容器虚拟机
启动秒级分钟级
硬盘使⽤⼀般为MB⼀般为GB
性能接近原⽣弱于
系统⽀持量单机⽀持上千个容器⼀般⼏⼗个
附录2.docker使⽤命名空间(namespace)详解:
名字空间
名字空间是 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, 也就是说可以在容器内⽤容器内部的⽤户执⾏程序⽽⾮主机上的⽤户。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。