Docker入门

【本文为根据资料整理总结得到,性能分析部分为引用他人结果。】

Docker介绍

1、容器

公用一个内核和某些运行时环境(一些系统命令和系统库),但却彼此看不到,都以为系统中只有自己存在,这种机制就是容器。容器不是虚拟机,而是进程。

2、docker

Docker是一个能够把开发的应用程序部署到容器的开源引擎,可以轻松的为任何应用创建一个标准化、轻量级、可移植、自给自足的容器。它能把开发环境直接封装成镜像,然后直接部署到生产环境,速度快且不容易出问题。
Docker容器借鉴了“集装箱”的设计理念,Docker在英文中的意思是“码头工人“,码头工人的工作是使用集装箱搬运货物,Docker则是使用容器搬运各种应用程序。
docker容器可以实现虚拟机隔离应用环境的功能,但开销比虚拟机小。同传统的虚拟化及版虚拟技术相比,容器运行不需要模拟层(emulation layer)和管理层(hypervisor layer),而是使用操作系统的系统调用接口。
传统使用虚拟机的虚拟化
Docker的虚拟化

3、Docker的作用

Docker解决了应用隔离、移植、服务器资源充分利用及高效运维等问题。它的slogan是「一次构建,到处交付」(Docker - Build, Ship, and Run Any App, Anywhere)。
Docker容器为一个软件打包了一个完整的文件系统,该文件系统包含了该软件运行所需的全部可安装内容:代码、运行时环境、系统工具、动态链接库。这样就保证了软件可以忽略它所处的外部系统,始终运行在一致的环境中。
Docker的作用包括:
- Docker使开发运维过程标准化。现有的部署系统从业务程序包的维度操作,开发和运维之间缺乏通用的标准,部署系统往往都与具体的业务绑定。开发、测试、运维之间的交付物没有标准,运行环境没有标准,部署本身也没有标准。
- 很多个应用不隔离直接装在服务器上不但容易出现资源冲突,如依赖环境不同、端口冲突、内存资源抢占等问题,也可能出现不可知的安全问题;如果在一台服务器上装多个虚拟机来部署不同应用,虽能避免问题,但又太耗资源,而Docker在应用隔离安全性与资源使用效率方面取得了较好的平衡。
- Docker还可以屏蔽多种(不同,但相似,如Centos和Ubuntu)操作系统之间的差异性,避免部署问题。
- 支持自动化测试和持续集成、发布;

Docker本身并没有多大的技术创新,它使用的一些技术,比如aufs/dm、cgroup、capability、namespace都是已经存在了很久的技术,它最大的创新就在于它让互联网软件的所有环节都标准化起来。只要你的应用程序遵循这个标准,就可以在任何地方运行,这也是Docker的初衷。docker并不能部署的工作「减少为0」,比较好的情况下是「基本减少为1」

Docker组件和一些重要概念

客户端和服务器

Docker系统有两个程序:docker服务端(又称Docker引擎)和docker客户端。其中docker服务端是一个服务进程,管理着所有的容器。docker客户端则扮演着docker服务端的远程控制器,可以用来控制docker的服务端进程。大部分情况下,docker服务端和客户端运行在一台机器上。
Docker引擎提供了一组REST API供客户端与之交互,各种docker功能的实现都是以远程调用的形式在服务端Docker引擎完成的。
Docker系统框图

Docker镜像(Union文件系统)

Doker镜像是由多个文件系统叠加而成的。一个镜像可以放到另一个镜像的顶部,位于下面的镜像称为父镜像,最底部的镜像称为基础镜像。(union mount,一次同时加载多个文件系统,但是外面看起来只能看到一个文件系统,该文件系统包含所有底层的文件和目录。)

  1. 最低端是一个引导文件系统,即bootfs,在容器启动后,bootfs被卸载,以节省空间。
  2. 第二层是root文件系统rootfs,它位于bootfs上,可以是一种或多种操作系统的文件系统,只读。
  3. Docker利用联合加载技术会在rootfs上增加更多的只读文件系统。

当从一个镜像启动时,Docker会在该镜像的最顶部加载一个读写文件系统。每一层构建完的镜像不再改变,增删改操作都只在顶层读写层标记或复制完成,且原始文件会一直跟随镜像。
当Docker第一次启动容器时,初始的读写层是空的,当文件系统发生变化时,这些变化都会应用到这一层(利用写时复制机制,底层镜像当中的文件依然只读,而且被隐藏)。当容器消亡时,读写层的内容也会随之消失。当运行多个容器时,他们可以公用某些基础镜像,以减小容器占用内存。
按照Docker最佳实践的要求,容器不应该向其存储层内写入任何数据,容器的存储层要保持无状态化,所有文件写入操作都应该使用数据卷(Volume)或绑定宿主目录,在这些位置的读写会调过容器的存储层,直接对宿主或网络存储发生读写,性能和稳定性更高。
Docker镜像组成

Docker仓库(Registry)

Registry来保存用户构建的镜像,分为公共和私有两种。Docker公司有DockerHub,允许用户注册分享自己的镜像(类似GitHub),仓库也可以保存私有镜像,但需要付费购买空间。个人和机构也可以建设私有Registry,官方有提供的仓库镜像。

Docker容器

镜像是Docker声明周期中的构建或打包阶段,而容器则是启动和执行阶段。镜像是静态的定义,容器是镜像运行时的实体。容器里的镜像可以进行创建、启动、关闭、重启、销毁,Docker执行上述操作时并不关心容器内部是什么。

Docker相关的其他概念

数据卷

卷是在一个或多个容器内被选定的目录,可以绕过分层的联合文件系统(Uinon File System),为Docker提供持久数据或者是共享数据。对卷的修改会直接生效,并且绕过镜像。当提交或创建镜像时,卷不被包含在镜像里。卷可以在容器间共享,即便容器停止,卷里的内容依然存在。
数据卷的使用,类似Linux下对目录或文件进行mount,镜像中被指定为挂载点的目录中的文件会被隐藏掉,能显式看到的是挂在的数据卷。

删除容器时删除数据卷:docker rm -v

卷的用途:
1. 希望同时对代码做开发和测试
2. 代码改动很频繁,不想在开发过程中重构镜像
3. 希望在多个容器间共享代码

数据卷容器

如果有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。数据卷容器其实就是一个正常的容器,专门用来提供数据卷供其他容挂载。
可以利用数据卷容器对数据进行备份、回复、迁移。

使用Docker

构建Docker镜像

一般来说,我们并不是真正完全创建新镜像,而是基于一个已有的基础镜像,如ubuntu或fedora等,构建自己的镜像而已。创建Docker镜像有多种方式,下面主要介绍两种,推荐使用第2种dockerfile方式

1.docker Commit方式

docker commit提交镜像是将容器的存储层保存下来成为镜像,即在原有镜像的基础上,再叠加上容器的存储层,生成一个新的只读文件系统。
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
Docker commit的创建过程:

  1. 启动一个容器,然后在其中进行修改,修改完成后使用exit命令退出。
  2. 修改完成后,运行docker commit提交修改。
    docker commit 容器ID 目标镜像仓库/镜像名

这种方式的缺点

  1. 保存大量的无关内容,导致软件臃肿。
  2. 所有操作是黑箱操作,不便于追溯。
  3. 对镜像的删、改只在读写层标记,实际文件依然存在,造成臃肿。
    Docker commit命令除了帮助理解镜像的原理意外,还可以应用在一些特殊场合,如被入侵后保存现场。

2. Dockerfile+Docker build

dokerfile+docker build创建镜像是一种更强大、灵活的镜像构建方式。
Dockerfile是一个文本文件,其中包含了一条条指令,每一条指令会构建一层(commit一次,有最大层数限制,比如aufs限制127层,因此docker指令行数不能过多),因此每一条指令的内容就是描述该层应当如何构建。
Dockerfile示例

FROM docker.io/centos   #FROM是必备指令,且必须是第一条,用来指定基础镜像
RUN yum install wget
RUN echo 'I am vacing'
RUN echo "hello world " > ./a.txt
COPY ./a.txt /home/oicq/

Docker build的工作原理:
Docker build命令构建镜像,并非在本地构建,而是在服务端。client在发起build请求时携带用户指定的环境文件(上下文)上传给Docker引擎,Docker引擎在收到上下文包时就会获得镜像所需的一切文件,并根据其中的dockerfile进行镜像构建。
Docker命令只能使用上下文中的文件,也只能引用相对路径引用这些文件。使用.dockerignore文件可以指定哪些文件不作为上下文传递到Docker引擎。
在Dockerfile中连续两行的命令,在两个不同的容器当中,执行环境完全不同。因此,上一条命令指定的目录,对下一条无用,这时需要使用WORKDIR来指定工作目录。

提交Docker镜像到仓库

docker push

安装Docker镜像

docker search
docker pull

启动容器

docker run  -ti --name=centos_2 centos /bin/bash
                #-t让Docker分配一个伪中端并绑定到容器的标准输入
                #-i让容器的标准输入保持打开
                #--name可以为容器指定一个名称,允许字符[a-zA-Z0-9_.-]
                #/bin/bash是指要在ubuntu容器中执行的命令
docker -ps -a   #查看当前系统中的容器列表,默认只能看到正在运行的容器,-a表示看到全部
docker start 指定容器;  # 重新启动已经停止的容器
                        #三种方式可以唯一指定容器:端UUID、长UUID、名称。
docker attach 指定容器; # 附着到容器会话
docker logs --tail -0 -f 容器; # 查看容器的stdout日志

当利用docker run来创建容器时,Docker后台的标注难操作包括:
- 检查本地是否存在指定的镜像,不存在就从共有仓库下载
- 利用镜像启动并创建一个容器
- 分配一个文件系统,并在只读的竞相层外面挂在一层可读写层。
- 从诉诸主机配置的网桥接口中接一个虚拟接口到容器中去
- 从地址池配置一个IP地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必须的,除此之外没有其他资源。

docker exec 命令是从Docker 1.3引入的,早期Docker参考nsenter命令。
容器内运行的进程有两种类型:后台任务和交互式任务。后台任务在容器内运行且没有交互需求,交互式任务则保持 在前台运行。

docker exec -d 容器 命令;           #在容器内运行命令,-d标识后台任务
docker exec -t -i 容器 命令;        #容器内运行交互式任务命令
docker exec -ti zhiyun /bin/bash;   #退出该shell不会影响容器运行。
docker stop/kill 容器;                #停止容器,stop发送SIGTERM信号,kill发送SIGKILL信号。
docker run --restart=alway...       #重启容器
docker inspect                      #获取更多容器信息
docker rm 容器                        #删除容器,运行中的docker容器需要先停止
docker rm `docker ps -a -q`         # 一次删除全部容器
docker images -f dangling=true      #显示虚悬镜像

Docker编配:

在Docker的世界里,编配用描述一组时间过程,这个过程会管理多个运行在多个Docker容器里的应用,而这些应用有可能运行在多个宿主机上。

应用程序Docker化

1、界定应用程序包的内容及其依赖

前者比较简单,重点是依赖。通常有以下几种依赖:

  1. 操作系统环境
    首先是操作系统版本。虽然都是Linux, 都遵循POSIX,但不同的操作系统版本,仍然有很多可能被应用程序使用到的差别。
    • 系统程序和配置文件的目录结构不一样。
      比如网络配置文件,在Debian/Ubuntu下是/etc/network/interface文件, 而在RH/CentOS中是/etc/sysconfig/network-scripts目录。
    • 同样的系统程序,行为不一致。
      比如adduser命令,在Debian/Ubuntu下是个perl脚本,里面调用了useradd, 而在RH/CentOS中直接是useradd的软连接。造成这个命令在不同的系统中的参数语法和错误提示完全不同。还比如同是CentOS,但是bash版本不同,bash4.2支持关联数组,bash3.2中就不支持。
    • 系统程序的集合不一样
      基础库的版本不一样。比如glibc的版本问题。
  2. 应用程序依赖的中间件和库
    如Java运行时环境,特定版本的python,某个特定的库。
  3. 应用程序的特定依赖
    比如有的程序代码中写死需要往某个目录读写文件。

2、撰写Dockerfile

选择一个目录,创建名为Dockerfile的文本文件。
1. 选定或构建base image
官方的Docker Registry有很多base image,比如centos6/centos7/ubuntu14.04等。这些image里面,包含了对应版本的系统程序,配置文件,库等内容,体积一般一百到两百兆。由于同样的内容只需分发一次,一般来说,为了提高我们的image的分发效率,还是尽量选择这些最常见系统版本的base image。特殊情况下,也可以自己创建特殊的base image。如何从头创建base image
比如我们选择centos7作为base image,那么在Dockerfile中写下第一行:
FROM docker.oa.com:8080/library/centos7:latest
2. 安装其他依赖包
例如GAIA依赖Java开发环境,需要在我们的Docker image中安装对应的包。
在Dockerfile总增加:

RUN yum -y install java-1.6.0-openjdk-devel.x86_64 wget
ENV JAVA_HOME /usr/lib/jvm/java
ENV PATH $JAVA_HOME/bin:$PATH

其中RUN指令会在docker build过程中下载docker.oa.com:8080/library/centos7:latest这个image, 启动一个container, 在container中运行RUN指令后面的命令,然后执行docker commit保存这些指令所产生的变化为一个新的layer。
EVN定义的环境变量,不仅在docker build过程中有效,在构建出来的Docker image以后运行起来后也有效。
3.安装设置私有依赖环境
比如我们需要把hadoop-2.4.1.tar.gz解压放到固定的目录/gaia, 然后修改其中的一个文件。在Dockerfile中增加:

ADD hadoop-2.4.1.tar.gz /gaia/
ENV HADOOP_YARN_HOME /gaia/hadoop-2.4.1
RUN rm -rf $HADOOP_YARN_HOME/etc && sed -i "s/null &/null/g" $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  

3、构建Docker image

命令为:docker build -t docker.oa.com:8080/library/gaia-base .
要指定的参数是新构建出来的image的名字(tag), 还有Dockerfile所在的路径。
这里有一点要注意的是:Dockerfile中的ADD指令的源如果是本地文件,这个文件必须事先拷贝到Dockerfile所在目录或者其下级子目录,而不能在Dockerfile所在目录之外的其他地方,也不能是软链接。

4、把新创建的image推送到docker registry

docker push docker.oa.com:8080/library/gaia-base

5、在其他机器上部署这个image

docker pull docker.oa.com:8080/library/gaia-base

至此,我们完成了把一个应用程序及其依赖打包为一个Docker image的全部过程,从而可以非常方便的部署我们的应用程序了。只要一台机器支持docker,并能访问我们的dokcer registry (docker.oa.com:8080),无论它上面的操作系统是Suse, CentOS,无论这台机器上面有没有安装Java运行环境,也无论已经安装的Java运行环境是什么版本,都不影响我们的应用程序(Java)在这台机器上直接运行。因为通过打包为Docker image,我们的应用程序为自己带盐,不再依赖机器上现有的软件环境。

性能

CPU

测试工具:Linpack,参考这里
工具介绍:测试单位时间CPU浮点计算能力
测试方法:./xlinpack_xeon64 lininput_xeon64
图1 CPU性能对比,单位:10亿次双精度浮点运算(GFLOPs)/秒
CPU性能对比

内存

测试工具:STREAM,参考这里
工具介绍:
测试内存带宽,4种操作:COPY,SCALE,SUM,TRIAD。

NameKernelBytes/IteratioFLOPS/Iteration
COPYa(i) = b(i)160
SCALEa(i) = q*b(i)161
SUMa(i) = b(i) + c(i)241
TRIADa(i) = b(i) + q*c(i)242

测试方法:
#gcc -DSTREAM_ARRAY_SIZE=64000000 -O3 -o stream3 stream.c
执行stream 三次,取平均值。
图2 内存性能对比,单位:MB/秒
这里写图片描述

网络

测试工具:netperf
工具介绍:测试单位时间内传输包的数量
测试方法:
B6运行netserver,4台C1运行netperf,每台C1 100个进程。
bin/netperf -H 10.193.x.x -p 12865 -l 300 -t TCP_RR -- -r 1,1 -P 0,$port
图3 TCP_RR对比,单位:Trans Rate/秒
这里写图片描述

IO

测试工具 fio
测试方法
A5 Raid1+0
fio --filename=/data1/fio.dat --direct=1 --thread --rw=randrw --rwmixread=70 --ioengine=libaio --runtime=300 --iodepth=64 --size=4G --numjobs=1 -name=test_rw --group_reporting --bs=8k --time_base

这里写图片描述

性能测试总结

可以看到,容器基本可以达到物理机的性能。

评价

Docker在容器的基础上进行了进一步的封装,使镜像成为了一种像集装箱那样的标准货件,极大的简化了容器的创建和维护。Docker的产生对开发的影响不大,但对于部署来说,是场革命。可以极大的减少部署的时间成本和人力成本。
基于一件核心事物「标准化」,可以做更多的事情,比如集装箱的机械自动搬运等。

优势

  • 更高效的利用系统资源(相比与虚拟机)
  • 更快速的启动时间(秒级、毫秒级)
  • 一致的运行环境
  • 持续交付和部署(结合持续集成)
  • 更轻松的迁移
  • 更轻松的维护和扩展

限制

  • 由于“客居”与操作系统,容器只能运行与底层宿主机相同或相似的操作系统。例如可以在ubuntu中运行RedHat Enterprise Linux,但却无法运行Windows。
  • 容器的隔离性比虚拟机要差。
  • 在Docker应用中,我们面临的最大挑战就是网络。
  • 安装先决条件:
    ● 64位CPu架构,不支持32位
    ● 3.8或更高版本内核。有些低版本也可以运行,但是有时会出异常。
    ● 支持一种合适的存储驱动(storage driver),例如Device Manager(默认)/AUFS/vfs/btrfs
    ● 内置支持并开启cgroups、namespace功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值