preface
docker这种时髦的技术我接触的比较晚,如果不是公司在使用这项技术,估计还得会更晚接触。好了,说下我司现在docker使用的情况。docker在我司是用来跑web服务的,里面的web软件是jexus(c#下的web框架),目前同事准备着开始把较多的web服务移动到docker里面,因为我司的业务比较特殊,经常打包带走,所以就得利用docker这样的轻量级的虚拟化技术。
好了,废话不多说,赶紧上手docker吧。
Docker‘s Concept
玩docker,就必须指定docker是包含三个重要概念的:
- 镜像(image)
- 容器(Container)
- Repository(仓库)
下面就分别说说这三个概念
镜像 Image
Docker的镜像是只读的模版,是由文件系统叠加而成的,最底端是一个引导文件系统,即bootfs,这很像典型的Linux、unix的因为文件系统。Docker用户几乎永远不会和引导文件系统有什么交互。实际上,当一个容器启动后,它将会被移植到内存中,而引导文件系统则会被卸载(umount),以留出更多的内存供initrd磁盘镜像使用。
so far,Docker看起来还像一个典型的Linux虚拟化栈。实际上,docker镜像的第二层是root文件系统rootfs,它位于引导文件系统纸上,rootfs可以是一种或者多种操作系统,如ubuntu。
在传统的linux引导过程中,root文件系统会最先以只读的方式挂载,当引导结束并完成了完整性检验后,它才会切换为读写模式。但是在Docker里面,root文件系统永远只读的,并且被Docker利用联合加载(union mount)技术又会在root文件系统层上加载更多的只读文件系统。联合加载指的是一次同时加载多个文件系统,但是外面看起来只能看到一个文件系统。联合加载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层的文件和目录。
Docker将这样的文件系统成为镜像。一个镜像可以放到另一个镜像的顶部。位于下面的镜像称为父镜像(parent image),可以依此类推,直到镜像栈的最底部,最底部的镜像称为基础镜像(base Image)。最后,当从一个镜像启动容器时,Docker会在镜像的最顶层加载一个读写文件系统。我们想在docker中运行的程序就是在这个读写层中执行的。
如下图所示:
当Docker第一次启动一个容器的时候,初始的读写层是空的。当文件系统发生变化时,这些变化都会应用到这一层上。比如,如果想修改一个文件,这个文件首先会从读写层下面的只读层复制到该读写层。该文件的只读版本依然存在,但是已经被读写层中的该文件副本所隐藏。
通常这种机制称为写时复制(copy on write),这也是Docker如此强大的技术之一。每个只读镜像层都是只读的,并且以后永远不会变化。当创建一个新的容器,docker会构建出一个镜像栈,并在栈的最顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。每个容器是可以修改的,他们都有自己的状态,并且是可以启动和停止的。容器这种特点加上镜像分层框架(image-layering framework),使我们可以快速构建并运行包含我们自己的应用程序和服务的容器。
例如:一个镜像可以包含完整的ubuntu系统,里面仅安装了Apache或者其他的应用。
镜像可以用来创建 Docker 容器。
Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。
容器Container
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
*注:镜像是只读的,容器在启动的时候创建一层可写层作为最上层。
仓库repository
仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool等,可以提供大陆用户更稳定快速的访问。
当然,用户也可以在本地网络内创建一个私有仓库。
当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了。
*注:Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。
镜像是构建Docker世界的基石,用户基于镜像来运行自己的容器。
镜像是基于联合文件系统的一种层式的结构,由一系统指令一步一步构建出来的。例如:
添加一个文件,执行一个命令,打开一个端口。
镜像是Docker 生命周期中的构建或打包阶段,而容器是启动或者执行阶段的。
说道这里,我们把Docker和OpenStack来比较下:
Docker和Openstack 的对比
对比类别 | Docker | Openstack(KVM位列) |
---|---|---|
虚拟化级别 | 内核虚拟化-->是linux内核支持的 | kvm-->硬件虚拟化-->是CPU支持的 |
部署难度 | 非常简单 | 组建多,部署复杂 |
启动速度 | 秒级 | 分钟级 |
执行性能 | 和物理系统几乎一致 | VM会占用一些资源,大约占用6% |
镜像体积 | M级 | G级 |
管理效率 | 管理简单 | 组件相互依赖,管理复杂 |
隔离性 | 隔离性高 | 彻底隔离 |
可管理性 | 单进程,不建议启动SSH | 完整的系统管理 |
网络连接 | 比较弱 | 借助Neutron可以灵活组建各类网络架构 |
开始部署Docker
Docker安装方式可以参考官网:https://docs.docker.com/engine/installation/
CentOs6
Docker 支持 CentOS6 及以后的版本。我这里使用的CentOs6.6的系统,系统默认内核是可以支持docker的。所以不需要升级内核,linux kernel version is 2.6.32-642.11.1.el6.x86_64。
对于 CentOS6,可以使用 EPEL 库安装 Docker,命令如下
- 32位的
$ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm
- 64位的:
yum install http://mirrors.yun-idc.com/epel/6/x86_64/epel-release-6-8.noarch.rpm
安装好yum源后:
$ sudo yum install docker-io
CentOS7
CentOS7 系统 CentOS-Extras 库中已带 Docker,可以直接安装:
rpm -Uvh http://dl.Fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
$ sudo yum install docker
安装之后启动 Docker 服务,并让它随系统启动自动加载。
$ sudo service docker start
$ sudo chkconfig docker on
启动Docker
可以直接通过service命令来启动
[root@salt docker_pratice]# service docker start
[root@salt ~]# docker info
daemon下绑定端口
[root@salt docker_pratice]# docker -d -H tcp://0.0.0.0:2375
- -d 是表示放入后台运行,daemon模式
这样linux就开启了一个2375端口,哈哈,我也没搞明白这个命令有啥用。
下载镜像以及启动第一个容器:
Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker 会从镜像仓库下载(默认是Docker Hub 公共注册服务器中的仓库)。
- 从仓库获取镜像
- 管理本地主机的镜像
- 介绍镜像实现的基本原理
查看本地的镜像
[root@salt ~]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
搜索公共的镜像列表
[root@salt ~]# docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
centos The official build of CentOS. 2526 [OK]
jdeathe/centos-ssh CentOS-6 6.8 x86_64 / CentOS-7 7.2.1511 x8... 27 [OK]
nimmis/java-centos This is docker images of CentOS 7 with dif... 13 [OK]
million12/centos-supervisor Base CentOS-7 with supervisord launcher, h... 12 [OK]
。。。。。。
拉取某个公共镜像
[root@salt docker_pratice]# docker pull jamtur01/puppetmaster # 时间挺长的
下载公共镜像
[root@salt ~]# docker run -i -t centos /bin/bash
Unable to find image 'centos:latest' locally
latest: Pulling from centos
3690474eb5b4: Pull complete
b200b2d33d98: Pull complete
3fbd5972aaac: Pull complete
d83a55af4e75: Pull complete
Digest: sha256:79aacacabba31540d665dec70afbf9a6d85a7200a008c0a6f9e6ba6ba5f4c988
Status: Downloaded newer image for centos:latest
参数解释:
- -i: 标志保证容器中的stdin是开启的,尽管我们没有附着到容器中。持久的标准输入是交互式shell的半边天
- -t:标志则是另外的"半边天",它告诉docker要为创建的容器分配一个伪tty中断。这样,新创建的容器才能提供一个交互式的shell。若要在命令行下创建一个我们能与之进行交互的容器,而不是一个运行后台的服务器,则这2个参数是最基本的参数了。
- centos:表示基于什么镜像来创建容器。首先检测本地有没有这个镜像,如果没有就连接到docker的公共镜像下载。
当镜像下载完后,那么就会直接进入到命令shell交互模式下,如下所示:
[root@7d124faef7df /]# hostname
7d124faef7df
[root@7d124faef7df /]# date
Thu Aug 11 03:05:42 UTC 2016
退出容器
[root@7d124faef7df /]# exit
exit
当我们输入exit结束的时候,我们就返回到了linux-CentOs的宿主机了,这个容器已经停止运行了,只有在指定的/bin/bash命令处于运行状态的时候,我们的容器也会相应的处于运行状态,一旦退出容器,/bin/bash命令也结束了,容器也随之停止了运行了。
查看退出容器后,容器的运行状态
[root@salt ~]# docker ps -a
[root@salt ~]# docker ps -l
操作容器:
1 为容器取别名
[root@salt ~]# docker run --name test001 -i -t centos /bin/bash
不能够为容器取取同样名字的
2 启动容器:
[root@salt ~]# docker start test01 针对别名启动
[root@salt ~]# docker start d83a55af4e75 使用容器ID启动容器
3 启动后进入容器:
[root@salt ~]# docker attach test01
4 创建守护式容器
[root@salt ~]# docker run --name test1 -d centos /bin/sh -c "while true;do echo hello world;sleep 1;done"
df78ec137bc2c53d293bad140e2d5f8b89f828ee7a1340ac5b30478a18887da1
参数解释:
- -d 参数来使其这个容器作为守护进程放到后台,
- -c 参数是能够接受shell命令的
下面的一串乱码其实是docker返回来一个容器ID
通过docker ps查看到有一个正在运行的容器。
[root@salt ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df78ec137bc2 centos "/bin/sh -c 'while t 15 seconds ago Up 14 seconds test1
5 what happend in docker's inside ?
[root@salt ~]# docker logs test1
hello world
hello world
hello world
.......#一堆hello world
[root@salt ~]# docker logs -f test1 #也可以使用这条命令(等同于tailf ):
[root@salt ~]# docker logs --tail 10 test1 # 这条也可以
hello world
hello world
hello world
6 watch docker's processes:
通过top命令来查看docker内部运行的进程
[root@salt ~]# docker top test1
7 execute a process in docker
进程的执行分为交互式和前台运行:
交互式: 保持前台运行
后台式:在容器内部运行没有交互需求。
后台直接创建
[root@salt ~]# docker exec -t -i t3 touch /tmp/xxx.txt
前台运行:
[root@salt ~]# docker exec -t -i t3 /bin/bash
8 restart container by himself
我们可以让docker在遇到错误而停止运行的时候自己重启启动该容器。--restart 标志会检查容器的退出代码,并由此来决定是否重启容器,默认的行为是docker不会重启容器的。
[root@salt docker_pratice]# docker run --restart=always --name t4 -d centos
--restart 标志为always的时候,无论容器退出的代码是什么,Docker都会自动重启该容器,除了always,我们还可以将这个标志设为on-failure. 这样只有当容器的退出代码为非0 的时候,才会自动重启,也可以指明重启次数。
[root@salt docker_pratice]# docker run --restart=on-failure --name t5 -d centos
[root@salt docker_pratice]# docker run --restart=on-failure:5 --name t5 -d centos
9 return container's info
返回详细信息:
[root@salt docker_pratice]# docker inspect test1
返回指定容器的运行状态
[root@salt docker_pratice]# docker inspect --format='{{ .State.Running}}' t3
false
[root@salt docker_pratice]# docker inspect --format='{{ .State.Running}}' t4
true
10 delete this container
删除多个容器的时候就在rm后面接多个容器名
[root@salt docker_pratice]# docker rm test001 t1 t2 t3 t4
删除所有的容器:
docker ps -a -q能够把所有docker 的container id 获取到。
[root@salt docker_pratice]# docker rm `docker ps -a -q`