Docker虚拟化

 一、虚拟化简介
 1、虚拟化概述
        虚拟化是一种资源管理技术,能够把物理资源转变为逻辑上可以管理的资源,如服务器、网络、内存及存储资源,虚拟化可以打破物理结构内的壁垒,计算元件运行在虚拟的基础上而非真实的基础上,可以扩大硬件的容量,简化软件的重新配置过程,从而让用户可以用比原本的组太更好的方式来应用和管理这些资源。
        虚拟化技术允许一个平台同时运行多个操作系统,并且应用程序可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率,它是一个为了简化管理、优化资源的解决方案。
        目前企业主流的虚拟化技术包括:KVM、Xen、VMware Esxi、VirtualBox、Docker,虚拟化技术越来越广泛的应用到互联网企业。
 2、虚拟化原理
        虚拟化解决方案的底部是要进行虚拟化的物理机器,该物理机器可以直接支持虚拟化,或间接支持虚拟化,那么就需要虚拟机管理程序的支持。虚拟机管理程序(virtual machine monitor,VMM),可以看作是平台硬件和操作系统之间的抽象化或者中间件。
        VMM为每个虚拟机分配一套数据结构来管理它们的状态,包括虚拟处理器的全套寄存器、物理内存的使用情况、网络设备状态、虚拟设备状态等。
        VMM可以对底层(HostOS)硬件资源(物理CPU、内存、磁盘、网卡、显卡等)进行封装、隔离,抽象为另一种形式的逻辑资源,再提供给上层(GuestOS)虚拟机使用,通过虚拟化技术实现的虚拟机被称为GuestOS,而作为GuestOS载体的物理主机称为HostOS(宿主)。
 3、虚拟化技术实现的区别
  • 完全虚拟化技术,实际上是通过软件实现对操作系统的资源再分配,比较成熟,如KVM、VirtualBox等;
  • 半虚拟化技术,则是通过代码修改已有的系统,形成一种新的可虚拟化的系统,调用硬件资源去安装多个系统,整体速度上相对高一点。代表产品有Xen;
  • 轻量级虚拟化,介于完全虚拟化和半虚拟化之间,典型代表有Docker。
  
 二、Docker 虚拟化简介
 1、概述
        Docker 是一个开源的应用窗口引擎,开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux 机器上,进而实现虚拟化。容器是完全使用沙箱机制的,而且相互之间不会有任何接口,几乎没有性能开销,可以很容易地在机器和数据中心中运行,最重要的是,它们不依赖于任何语言、框架或包括系统。
        Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案,Docker 的基础是Linux 容器(Linux container,LXC)等技术,Docker 在LXC技术的基础上进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便,用户操作Dockeer 的容器就像操作一个快速轻量级的虚拟机一样简单。
        Docker 虚拟化和传统虚拟化(KVM、Xen等)方式的不同之处在于Docker 虚拟化可以在操作系统层面上直接实现App或者应用虚拟化,即直接在HostOS 上基于VMM启动各种App。直接复用本地主机的操作系统,而传统方式则在硬件的基础上,虚拟GuestOS操作系统,然后在GuestOS 操作系统,然后在GuestOS 操作系统上部署相关的App应用。
 ## Docker 虚拟化实施有以下三个概念:
  • Docker 镜像,Docker 镜像是一个静态模板,与常见的ISO镜像类似,是一个样板,不能直接修改,可以通过封装生成;
  • Docker 容器,基于Docker 镜像运行启动的应用或系统,称之为一个Docker 容器或者Docker 虚拟机;
  • Docker 仓库,Docker 仓库是存放Docker 镜像的地方,常见分为公开仓库(public)和私有仓库(private)两种形式。
 2、Docker LXC 之 Cgroup 资源控制
        cgroups(control groups)是Linux内核提供的一种机制,这种机制可以根据特定的行为,把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。通俗的来说,cgroups可以限制、记录、隔离进程组所使用的物理资源(包括:CPU、memory、IO等),为容器实现虚拟化提供了基本保证,是构建Docker等一系列虚拟化管理工具的基石。
        实现cgroups的主要目的是为不同用户层面的资源管理,提供一个统一化的接口。从单个进程的资源控制到操作系统层面的虚拟化。
 ## Cgroups提供了以下四大功能:
  • 资源限制(Resource Limitation):cgroups可以对进程组使用的资源总额进行限制。如设定应用运行时使用内存的上限,一旦超过这个配额就发出OOM(Out of Memory)。
  • 优先级分配(Prioritization):通过分配的CPU时间片数量及硬盘IO带宽大小,实际上就相当于控制了进程运行的优先级。
  • 资源统计(Accounting): cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等等,这个功能非常适用于计费。
  • 进程控制(Control):cgroups可以对进程组执行挂起、恢复等操作。
 3、Docker LXC 之 Namespace 访问隔离
        Namespace又称为命名空间,它主要做访问隔离。其原理是针对一类资源进行抽象,并将其封装在一起提供给一个容器使用,对于这类资源,因为每个容器都有自己的抽象,而他们彼此之间是不可见的,所以就可以做到访问隔离。
 ## Linux提供如下Namespace:
 Namespace           Constant                          Isolates
  • Cgroup           CLONE_NEWCGROUP           Cgroup root directory
  • IPC              CLONE_NEWIPC              System V IPC, POSIX message queues
  • Network          CLONE_NEWNET              Network devices, stacks, ports, etc.
  • Mount            CLONE_NEWNS               Mount points
  • PID              CLONE_NEWPID              Process IDs
  • User             CLONE_NEWUSER             User and group IDs
  • UTS              CLONE_NEWUTS              Hostname and NIS domain name
 以上Namespace分别对进程的 Cgroup root、进程间通信、网络、文件系统挂载点、进程ID、用户和组、主机名域名等进行隔离。
 ## 创建容器(进程)主要用到三个系统调用:
  • clone() – 实现线程的系统调用,用来创建一个新的进程,并可以通过上述参数达到隔离
  • unshare() – 使某进程脱离某个namespace
  • setns() – 把某进程加入到某个namespace
 4、Docker LXC 之 Chroot 文件隔离
       Chroot 全称是change to root : 其中root 是根目录的意思,也就是改变(linux 根目录是/,也可以理解为设置)一个程序运行时参考的根目录的位置。
 ## 根目录的参考
 linux 系统(原始的方案)  | 引入chroot 机制
 /                                 /lxc
 /usr                            /lxc/usr
 /bin                            /lxc/bin
 /sys                            /lxc/sys
 如上我们一旦使用了chroot ,用户的中心就不是linux 系统的根目录,而是我们指定的/lxc (这个目录可以任意指定),所以chroot 确实可以修改根目录。
 ## chroot 机制的意义:
  • 增强系统的安全行
  • 指定程序访问的根目录,防止用攻击者可以通过程序的漏洞获取其他目录的读写权限;比如/etc/passwd 比如/ 下所有的权限
 ## chroot 机制在虚拟化中的作用:
 chroot 机制因为安全问题才被引入的,但是在LXC 中却启动了举足轻重的作用,因为chroot 机制可以指定虚拟根目录,让不同的容器在不同的根目录下工作。
  • 不同的容器进程参考的根目录不同,相互直接不影响
  • 不同的容器因为共享linux 底层的文件系统,所以容器集成os的功能,实现轻量级!
  
 三、Docker 虚拟化特点
 ## Docker 虚拟化跟传统虚拟化相比,有发下优点:
  • 操作启动快,运行时的性能可以获取极大提升,管理操作(启动、停止、开始、重启等)都是以秒或毫秒为单位的;
  • 轻量级虚拟化,用户会拥有足够的“操作系统”,仅需添加或减少镜像即可,单台服务器上可以部署100~1000个containers 容器,而传统虚拟化能虚拟10~20个虚拟机就非常不错了;
  • 开源免费,成本低,由现代Linux 内核支持并驱动;
  • 前景及云支持,正在越来越受欢迎,各大主流公司都在推动Docker 的快速发展,性能有很大的优势;
  • 更快速地交付和部署,Docker 在整个开发周期都可以完美的辅助用户实现快速交付;
  • 更快速地创建及迭代,Docker 能够快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的;
  • 高效的部署和扩容,Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等;
  • 更简单的管理,使用Docker,只需要小小的修改,就可以替代以往大量的更新工作,所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。
  
 四、Docker 虚拟化原理
        Docker 虚拟化中啊核心的部分分为Docker 引擎,Docker 引擎是一个C/S(client/server)结构的应用。
        Docker server 是一个常驻进程,rest API实现了client 和server 间的交互协议,CLI 实现容器和镜像的管理,为用户提供统一的操作界面。Docker 使用C/S架构,client 通过接口与server 进程通信实现容器的构建、运行和发布,client和server可以运行在同一台集群,也可以通过跨主机实现远程通信。
        完整的Docker 镜像可以支撑一个Docker 容器的运行,在Dock er 容器运行过程中主要提供文件系统数据支撑。Docker 镜像作为Docker 中最基本的概念,有以下特性:
  • 镜像分层,每个镜像都由一个或多个镜像层组成;
  • 可通过在某个镜像上加上一定的镜像层得到新镜像(此过程可通过编写DockerFile或基于容器commit实现);
  • 每个镜像层拥有唯一镜像ID;
  • 镜像在存储和使用时共享相同的镜像层(根据ID),所以在pull镜像时,已有的镜像层会自动跳过下载;
  • 每个镜像层都是只读的,即使启动成容器,也无法对其真正的修改,修改只会作用于最上层的容器层。
        Docker 容器,可以理解为一个或多个运行进程,而这些运行进程将占有相应的内存、相应的CPU计算资源、相应的虚拟网络设备以及相应的文件系统资源。而Docker 容器所占用的文件系统资源,则通过Docker 镜像的镜像层文件来提供。
        基于每个镜像的json文件,Docker 可以通过解析Docker 镜像的json文件,获知应该在这这个镜像之上运行什么样的进程,应该为进程配置怎样的环境变量,Docker 守护进程实现了静态向动态的转变。
  
 五、Docker 安装配置
      CentOS 6.X系统安装Docker 软件,首先要关闭SELinux,然后需要安装相应的epel源,安装代码如下:
 # sed -i '/SELINUX/s/enforcing/disable/g' /etc/selinux/config
 # setenfoce 0
 # wget http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
 # rpm -ivh epel-release-6-8.noarch.rpm
 # yum install lxc libcgroup device-mapper-event-libs
 # yum install docker -io
 # yum install device-mapper* -y
 Docker 安装完毕后,启动docker 进程/etc/init.d/docker start,并且查看docker 进程:# ps -ef|grep docker
      CentOS 7.X系统安装Docker 软件,首先要关闭SELinux,然后需要安装相应的epel源,安装代码如下:
 # sed -i '/SELINUX/s/enforcing/disable/g' /etc/selinux/config
 # setenfoce 0
  • 卸载旧版本
 较旧版本的Docker被称为docker或docker-engine。如果已安装这些,请卸载它们以及相关的依赖项。
 # yum remove docker docker-client \
                   docker-client-latest \
                   docker-common \
                   docker-latest \
                   docker-latest-logrotate \
                   docker-logrotate \
                   docker-selinux \
                   docker-engine-selinux \
                   docker-engine
  • 设置官方存储库安装
 # yum install -y yum-utils device-mapper-persistent-data lvm2
 # yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  • 启用边缘和测试存储库(默认是禁用的)
 # yum-config-manager --enable docker-ce-edge
 # yum-config-manager --enable docker-ce-test
 # yum-config-manager --disable docker-ce-edge     #使用disable禁用
  • 安装DOCKER CE 社区版(CE)
 # yum install docker-ce
  • 列出仓库中可用的版本
 # yum list docker-ce --showduplicates | sort -r
 docker-ce.x86_64            18.03.1.ce-1.el7.centos             docker-ce-stable
 docker-ce.x86_64            18.03.0.ce-1.el7.centos             docker-ce-stable
 docker-ce.x86_64            17.12.1.ce-1.el7.centos             docker-ce-stable
 docker-ce.x86_64            17.12.0.ce-1.el7.centos             docker-ce-stable
 docker-ce.x86_64            17.09.1.ce-1.el7.centos             docker-ce-stable
 docker-ce.x86_64            17.09.0.ce-1.el7.centos             docker-ce-stable
  • 安装特定的版本example: docker-ce-18.03.1.ce
 # yum install docker-ce-<VERSION STRING>
  • 启动并测试
 # systemctl start docker
 # docker run hello-world
  • 升级DOCKER CE
 要升级Docker CE,请按照仓库版本列表选择要安装的新版本。
  • 从包安装
 如果无法使用Docker的存储库来安装Docker,则可以下载.rpm适用于您的发行版的 文件并手动安装。每次要升级Docker时都需要下载新文件。
 转到 https://download.docker.com/linux/centos/7/x86_64/stable/Packages/ 并下载.rpm要安装的Docker版本的文件。
 注意:要安装边缘 包,stable请将上述URL中的单词更改 为edge。
 # yum install docker-ce-18.03.1.ce-1.el7.centos.x86_64.rpm
  • 使用脚本安装
 # curl -fsSL https://get.docker.com -o get-docker.sh
 # /bin/sh get-docker.sh
  
 六、Docker 必备命令
      对Docker 技术的深入学习,需要构建Docker 基础环境,熟练使用Docker 各种语法命令,要模拟Docker 虚拟化环境,需下载Docker 镜像,通过命令在宿主机服务器上直接下载Docker 公共仓库的镜像,具体步骤如下:
      公共仓库Nginx 和CentOS 镜像下载以及本地导入CentOS 镜像,执行如下命令:
 # docker pull nginx                #Docker 下载Nginx镜像
 # docker pull centos                #Docker 下载CentOS镜像
 # cat centos68.tar |docker import - centos        #本地导入CentOS镜像
      对Docker 的管理除了可以下载镜像、导入镜像之外,还要掌握如下命令:
  • docker version                #查看Docker 版本
  • docker search CentOS                #搜索可用的Docker 镜像
  • docker images                #查看当前Docker 所有镜像
  • docker pull CentOS                #下载CentOS镜像
  • cat xxx|docker import - newname         #本地导入Docker 镜像
  • docker export container_id >cenos6.tar                #Docker 导出镜像
  • docker run CentOS echo "hello world"                #在Docker 容器中运行hello world
  • docker run CentOS yum install ntpdate                #在容器中安装ntpdate的程序
  • docker ps -l                        #获得最后一个容器的ID
  • docker ps -a                         #查看所有的容器
  • docker commit 87e2313132 CentOS:v1                #提交刚修改的容器
  • docker run -i -t -d CentOS /bin/bash                #启动Docker 镜像,-d 表示后台启动,-t 表示打开终端,-i 表示交互输入
  • docker stop id                 #关闭Docker 容器
  • docker start id                #启动Docker 容器
  • docker rm id                        #删除Docker 容器
  • docker rmi images                #删除Docker 镜像
  • docker run -d -p 80:80 -p 8022:22 CentOS:v2        #-p 表示指定Docker 容器端口映射,前面是宿主主机本地端口,后面是Docker 窗口中端口。
  • docker exec -it docker_id /bin/bash                #进入Docker 容器shell终端
  • docker exec docker_id df -h                        #查看Docker 容器内部磁盘分区
  
 七、Docker 网络详解
 1、基于Docker run 创建Docker 容器时,可以使用--net 选项指定容器的网络模式,Docker 默认有以下4种模式:
  • host 模式,使用--net=host 指定;
  • container 模式,使用--net=container:NAME_OR_ID 指定;
  • none 模式,使用--net=none 指定;
  • bridge 模式,使用--net=bridge 指定,默认设置
 2、Docker 4 种网络模式详解如下:
  • host 模式详解:
 基于host模式,容器将不会获得一个独立的network namespace,而是与宿主机共用一个network namespace,容器将不会虚拟出自己的网卡、配置自己的IP等,而是使用宿主机的IP和端口。
  • container 模式详解:
 理解host 模式后,container 模式也非常好理解,container 模式指定新创建的容器和已经存在的一个容器共享一个network namespace,而不是和宿主机共享共享。即新创建的容器不会创建自己的网卡、配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样两个容器除了网络方面相同之外,其他的如文件系统、进程列表等还是隔离的。
  • none 模式详解:
 Docker 容器拥有自己的network namespace,但是并不为Docker 容器进行任何网络配置。该Docker 容器没有网卡、IP、路由等信息,需要手工为Docker 容器添加网卡、配置IP等,典型pipework 工具为Docker 容器指定IP等信息。
  • bridge 桥接模式详解:
 bridge 模式是Docker 默认的网络模式,该模式会为每一个容器分配network namespace、设置IP、路由等配置,默认会将Docker 容器连接到一个虚拟网桥交换机docker0上。
 3、默认使用Docker 创建Docker 容器网络为bridge 模式,以下为创建Docker bridge创建过程:
  • 启动Docker 容器,首先会在Docker 宿主机上创建一对虚拟网卡 veth pair 设备,veth 设备总是成对出现的,组成了一条数据通道,数据从一端设备进入,就会从另一端设备出来,veth 设备常用来连接两个网络设备;
  • Docker 将veth pair 设备的一端放在新创建的容器中。并命名为eth0,然后将另一端放在宿主机中,以vethxxx 这样类似的名字命名,并将这个网络设备加入到docker0网桥中;
  • 从docker0 子网中分配一个IP给容器使用,并设置docker0 IP地址为窗口默认网关;
  • 此时,容器IP与宿主机能够通信,宿主机也可访问容器中的IP地址,在bridge 模式下,连在同一网桥上的容器之间可以相互通信,同时容器也可以访问外网,但是其他宿主机不能访问Docker 容器IP,需要通过NAT 将容器IP的port 映射为宿主机的IP和port,方可使用。
  
 八、Docker 桥接模式
      Docker 容器默认使用docker0桥接网络,IP地址会自动分配,每个容器都是连接到docker0网桥上的。如果想让容器与宿主机同一网段的其他宿主机之间能访问,须在启动Docker 的时候将Docker 容器的某个端口映射到该宿主机的端口,其他宿主机连接Docker 宿主机的IP和port 即可。
      在生产环境中可以自定义Docker 桥接网卡,好处是可以设置Docker 容器的IP与宿主机同网段,无须NAT映射端口访问,更加方便、快捷,同时也可以基于pipework脚本为Docker 容器指定静态IP地址,以下为Docker 自定义桥接网络的配置方法,执行代码如下:
 # yum install bridge-utils                        #安装bridge相关库支持
 # /etc/init.d/docker stop                        #停止Docker 服务
 # ifconfig docker0 down                        #关掉docker0
 # brctl delbr docker0                        #删除docker0
 # brctl addbr br0                        #创建br0网桥
 # ip link set dev br0 up                        #开户br0网桥
 # ip addr add 192.168.1.6/24 dev br0        #为br0分配物理网络中的IP地址
 # ip addr del 192.168.1.6/24 /dev ens0        #将宿主机网卡的IP清空
 # brctl addif br0 eht0                        #将宿主机eth0网卡挂到br0上
 # ip route del default                        #删除原路由
 # ip route add default via 192.168.1.6 dev br0        #为br0设置路由
 如果Docker 宿主机操作系统为CentOS 6.X,Docker 启用br0设置如下:
 # vim /etc/sysconfig/docker
 other_args="-b=br0"
 上述配置方法比较烦琐,生产环境建议直接通过创建网桥br0配置文件实现桥接,在/etc/sysconfig/network-scripts/下,修改原ifcfg-eth0网卡配置,同时增加ifcfg-eth0桥接网卡配置,操作步骤如下:
 1、vim ifcfg-eth0 内容修改如下:
 DEVICE=eth0
 BOOTPROTO=none
 NM_CONTROLLED=no
 ONBOOT=yes
 TYPE=Ethernet
 BRIDGE="br0"
 IPADDR=192.168.1.6
 NETMASK=255.255.255.0
 GATEWAY=192.168.1.254
 USERCTL=no
 2、vim ifcfg-br0 内容修改如下:
 DEVICE="br0"
 BOOTPROTO=none
 IPV6INIT=no
 NM_CONTROLLED=no
 ONBOOT=yes
 TYPE="Bridge"
 IPADDR=192.168.1.6
 NETMASK=255.255.255.0
 GATEWAY=192.168.1.254
 USERCTL=no
 3、Docker 桥接网卡配置完毕,直接重启network 服务即可。
 # /etc/init.d/network restart
 4、修改Docker 桥接网卡为br0。如果Docker 宿主机操作系统为CentOS 7.X,Docker 启用br0 设置如下,然后重启Docker 服务即可。
 # vim /etc/sysconfig/docker-network
 DOCKER_NETWORK_OPTIONS="-b=br0"
 启动新的Docker 容器,使用命令docker attach容器ID或docker exec -it 容器ID /bin/bash 进入容器,会自动分配 192.168.1.x 网段的IP地址。
      通过配置br0桥接网卡,快速实现Docker 容器快速获取动态IP地址,生产环境服务器的IP均为静态IP,基于pipework工具为Docker 容器指定静态IP地址,以下为pipework工具配置Docker 容器静态IP地址的方法,通过pipework指定的静态IP,当容器重启之后,静态IP会丢失,所以启动容器之前需重新绑定该IP,也可以通过shell脚本自动配置IP,代码如下:
 #安装pipework工具
 git clone https://github.com/jpetazzo/pipework
 cp ~/pipework/pipework /usr/local/bin/
 docker run -itd --net=none --name=lamp2 CentOS 7 /bin/bash
 #基于pipework 设置Docker 容器IP为192.168.1.11,网关为192.168.1.6,Docker 容器IP子网掩码为255.255.255.0
 pipework br0 lamp2 192.168.1.11/24@192.168.1.6
 查看Docker 容器IP地址,执行如下代码:
 # docker exec lamp2 ifconfig
  
 九、Docker 桥接模式
      DockFile 是一种能被Docker 程序解释的脚本,DockerFile 由多条指令组成,每条指令对应Linux 系统中不同的命令,基于DockerFile 可以自定义创建生产环境所需的Docker 镜像,通过镜像可以启动所需的Docker 容器。
      Docker 程序将这些DockerFile 指令翻译为真正的Linux 命令,DockerFile 有特定的书写格式和支持的命令,Docker 程序解决这些命令间的依赖关系,类似于Linux系统中编译软件所使用的MakeFile 文件。
      Docker 程序可以读取DockerFile 文件,根据指令生成定制的image,需要定制自己额外的需求时,只需在DockerFile 上添加或修改指令,重新生成image即可,省去了敲命令的麻烦,以下为DockerFile 镜像制作常用的命令详解:
 FROM<image>:<tag>                FROM指令表示指定一个基本的镜像源,或从公共库拉取一个镜像源,DockerFile文件第一行必须指定FROM基础镜像源
 MAINTAINER                        设置DockerFile 编写人或维护者的信息
 LABEL<key>=<value>                设置标签、采用键值对的形式
 RUN<command>                核心命令,表示运行的Linux 指令,每条RUN指令在当前基础镜像上执行,并且提交成为新的镜像
 EXPOSE<port>[<port>]                用来指定Docker 容器中监听的端口,用于外界宿主机互联访问,启动Docker 时,可以通过-P,主机会自动分配一个端口号转发到指定的端口
 ENV<key>=<value>                设置环境变量,执行RUN指令及Docker 启动时被引用
 WORKDIR /path/to/workdir        设置工作目录,执行RUN、ADD、COPY、ENV指令时的基础路径
 COPY<src><dest>和ADD<src><dest>        Linux 系统增加及复制文件,ADD在和COPY相同的基础上,ADD允许<src>是一个URL,同时ADD的<src>是一个压缩格式文档,<src>将会解压缩复制
 CMD和ENTRYPOINT                配置Docker 容器启动后执行的命令,每个DockerFile 至少指定一个CMD命令或ENTRYPOINT,两者都可以指定shell或exec函数调用的方式执行命令,默认DockerFile run启动镜像之后便会退出容器,需要一个长时间运行的命令,使得容器一直执行
  • CMD和ENTRYPOINT的详解如下:
 CMD ["executable","param1","param2"]                运行一个可执行的文件并提供参数
 CMD ["param1","param2"]                        为ENTRYPOINT指定参数
 CMD command param1 param2                        以/bin/sh -c 的方法执行的命令
 ENTRYPOINT ["executable","param1","param2"]        首选执行形式
 ENTRYPOINT command param1 param2                以/bin/sh -c 的方法执行的命令
  • CMD和ENTRYPOINT的区别如下:
 每个 DockerFile 只能有一个CMD/ENTRYPOINT指令,超过一个CMD只有最后一个生效;
 CMD在运行时会被Docker run command 指定命令覆盖,而ENTRYPOINT 不会被运行时Docker run command 覆盖;
 DockeFile 中同时设置CMD和EINTRYPOINT,Docker 在build 过程中会将CMD中指定的内容作为ENTRYPOINT 的参数;
      如果Docker 启动需运行多个启动命令,彼此之间可以使用&&分开,最后一个命令必须为无限运行的命令,否则启动的容器将会被退出。
 VOLUME [DIR]                设置本地挂载目录,用于存放数据库和需要保持的数据
 USER daemon                指定Docker 运行时的用户名或UID,后续的RUN也会使用指定用户
 ONBUILD [INSTRUCTION]        配置当前所创建的镜像作为其他新创建镜像的基础镜像时,所执行的操作指令
  
 十、DockerFile 企事业案例
      (一)基于DockerFile 相关指令,可以在Docker 宿主机上编写DockerFile 文件,本案例为实现Docker 容器运行,并对外开启22端口,DockerFile 代码如下:
 #设置基本的镜像,后续命令都以这个镜像为基础
 FROM CentOS_lamp:v1
 #作者信息
 MAINTAINER TANG.COM
 #RUN 命令会在上面指定的镜像里执行任何命令
 RUN yum install passwd openssl openssh-server -y
 RUN echo '123456'|passwd --stdin root
 RUN sed -i '/^session\s\+required\s\+pam_loginuid.so/s/^/#/' /etc/pam.d/sshd
 RUN mkdir -p /root/.ssh&&chown root.root /root&&chmod 700 /root/.ssh
 RUN mkdir /var/run/sshd
 #暴露ssh端口22
 EXPOSE 22
 #设定运行镜像时的默认命令并且打印Docker IP地址,以daemon 方式启动sshd
 CMD ip addr ls eth0 |awk '{print $2}' |egrep -o '([0-9]+\.){3}[0-9]+';/usr/sbin/sshd -D
      (二)基于DockerFile 相关指令,可以在Docker 宿主机上编写DockerFile 文件,本案例为实现Docker 容器运行,并对外开启80端口,DockerFile 代码如下:
 #设置基本的镜像,后续命令都以这个镜像为基础
 FROM CentOS_lamp:v1
 #作者信息
 MAINTAINER TANG.COM
 #RUN 命令会在上面指定的镜像里执行任何命令
 RUN yum install pcre-devel -y
 RUN yum install httpd httpd-devel -y
 RUN echo "<h1>The Test Page TANG</h1>" >>/var/www/html/index.html
 #暴露ssh端口22
 EXPOSE 22
 #启动httpd
 CMD ["/usr/sbin/apachectl","-D","FOREGROUND"]
      (三)基于DockerFile 相关指令,可以在Docker 宿主机上编写DockerFile 文件,本案例为实现Docker 容器运行,并对外开户3306端口,DockerFile 代码如下:
 FROM CentOS:v1
 RUN groupadd -r mysql && useradd -r -g msyql mysql
 RUN install -y gcc zlib-devel gd-devel
 ENV MSYQL_MAJOR 5.6
 ENV MSYQL_VERSION 5.6.20
 RUN
         && curl -SL "http://dev.mysql.com/get/Downloads/MySQL-$MYSQL_MAJOR/mysql-$MYSQL_VERSION-linux-glibc2.5-x86_64.tar.gz" -o msyql.tar.gz\
         && curl -SL "http://mysql.he.net/Downloads/MySQL-$MYSQL_MAJOR/mysql-$MYSQL_VERSION-linux-glibc2.5-x86_64.tar.gz.asc" -o msyql.tar.gz.asc\
         && mkdir /usr/local/mysql \
         && tar -xzf msyql.tar.gz -C /usr/local/msyql \
         && rm mysql.tar.gz* \
 ENV PATH $PATH:/usr/local/mysql/bin:/usr/local/mysql/scripts
 WORKDIR /usr/local/mysql
 VOLUME /var/lib/mysql
 EXPOSE 3306
 CMD ["mysqld","--datadir=/var/lib/mysql","--user=mysql"]
      (五)基于DockerFile 相关指令,可以在Docker 宿主机上编写DockerFile 文件,本案例为实现Docker 容器运行,并对外开启8080端口,DockerFile 代码如下:
 FROM CentOS:v1
 #设置DockerFile 运行工作目录
 WORKDIR /tmp
 #安装JAVA JDK
 RUN wget --no-cookies --no-check-certificate --header "Cookie:gpw_e24=http%3a%2f%2fwww.oracle.com%2ftechnetwork%2fjava%2fjavase%2fdownloads%2fjdk7-downloads-1880260.html;oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-x64.tar.gz
 RUN tar -zxf jdk-7u70-linux-x64.tar.gz
 RUN mkdir -p /usr/java/
 RUN mv jdk1.7.0_79 /usr/java/
 #配置环境变量
 ENV JAVA_HOME /usr/java/jdk1.7.0_79/
 ENV JRE_HOME $JAVA_HOME/jre
 ENV CLASSPATH.:$JAVA_HOME/lib:$JRE_HOME/lib
 ENV PATH $PATH:$JAVA_HOME/bin
 #安装配置tomcat服务
 RUN wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-7/v7.0.62/bin/apache-tomcat-7.0.62.tar.gz
 RUN tar xvf apache-tomcat-7.0.62.tar.gz
 RUN mv apache-tomcat-7.0.62 /usr/local/tomcat/
 #配置tomcat 环境变量
 ENV CATALINA_HOME /usr/local/tomcat/
 EXPOSE 8080
 #设置tomcat 自启动
 CMD ["/usr/local/tomcat/bin/catalina.sh","run"]
  
 十一、Docker 磁盘扩容
      device mapper是Linux2.6内核中提供的一种从逻辑设备到物理设备的映射框架机制,device mapper driver默认会创建一个100GB的存储文件,主要用于存储镜像和容器,每一个容器都被限制在10GB大小的卷内,也可以基于loopback 自动创建稀疏文件,具体为用/var/lib/docker/devicemapper/devicemapper下的data和metadata实现动态磁盘扩容,默认创建的100GB存储总空间和Docker 容器10GB是无法满足生产环境应用的,需要扩大Docker 总容量和Docker 容器的rootfs 根系统大小。
      Docker 服务在启动的时候可以配置device mapper 的启动参数,docker -d --storage-opt dm.foo=bar,常见参数如下:
 dm.basesize                默认为10GB,限制容器和镜像的大小
 dm.loopdatasize                存储池大小。默认为100GB
 dm.datadev                存储池设备,/var/lib/docker/devicemapper/devicemapper/data
 dm.loopmetadatasize        元数据大小,默认为2GB
 dm.metadatasize        无数据设备,/var/lib/docker/devicemapper/devicemapper/metadata
 dm.fs                        文件系统,置认为ext4
 dm.blocksize blocksize        默认为64KB
 dm.blkdiscard                默认为true
      将Docker 置信存储池从100GB扩大到2TB,存储池元数据从2GB扩大到10GB,执行命令如下:
 rm -rf /var/lib/docker/devicemapper/devicemapper
 mkdir -p /var/lib/docker/devicemapper/devicemapper
 dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/data bs=1G count=0 seek=2000
 dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/metadata bs=1G count=0 seek=10
      还可以通过配置文件直接添加以下代码实现将Docker 默认存储池从100GB扩大到2TB,存储池元数据从2GB扩大到10GB,修改配置文件/etc/sysconfig/docker-storage,加入如下代码:
 DOCKER_STORAGE_OPTIONS="--storage-opt dm.loopdatasize=2000G --storage -opt dm.loopmetadatasize=10G --storage-opt dm.fs=ext4"
      上述配置完毕后,重启Docker 服务即可,新生成的Docker 容器磁盘大小即可生效,如果不在配置文件中指定,还可以基于以下命令直接启动,生产环境推荐修改配置文件,而不推荐命令行方式直接启动,执行命令如下:
 docker -d --storage-opt dm.loopdatasize=2000G --storage-opt dm.loopmetadatasize=10G --storage-opt dm.fs=ext4
      以上方法只适用于新容器生成,并且修改后需要重启Docker ,无法做到动态地给正运行的容器指定大小,基于现有容器在线扩容,宿主机文件系统类型支持ext2、ext3、ext4,不支持XFS。
 Docker 在线扩容方法:
  • 查看原容器的磁盘空间大小
 # df -h 
  • 查看Docker 存储device mapper 设备名称
 # ls -l /dav/mapper/docker-*
  • 查看Docker mapper 卷信息表查看该设备所占用的多少扇区
 # dmsetup table docker-******************************
  • 计算扩容大小
 扩容大小是以扇区为单位的,例如10GB有扇区近 20971520个左右,装空间扩容为15GB,计算15GB的空间所需扇区的大小:
 echo $((15*1024*1024*1024/512))
 31457280
 然后修改Dokcer 容器卷信息表、激活并且验证,使用echo 命令将新的扇区大小写入,
 注意只是改变20971520 的数字为31457280 ,其他数字不变,通过命令dmsetup resume 将修改后的容器文件激活,通过命令dmsetup table 查看最新Docker 扇区信息。
 # echo 0 31457280 thin 253:0 30 |dmsetup load docker-*****************************
 # dmsetup resume docker-******************************
 # dmsetup table docker-*******************************
  • 修改文件系统大小,命令为resize2fs
 # resize2fs /dev/mapper/docker-******************************
  • 验证Docker rootfs 磁盘大小
 # df -h 
      通过上述步骤成功地将Docker 容器的10GB空间扩容为15GB,还可以将上述步骤写成shell脚本,基于脚本参数快速扩容。给Docker 磁盘扩容除了采用上述方法外,还可以使用挂载目录方法,基于-v参数,在启动Docker 容器时指定。
  
 十二、Docker 构建私有仓库
      Docker 镜像默认存放在仓库中,Docker 仓库分为公共仓库和私有仓库,随着公司业务的发展,Docker 镜像的种类也非常繁多,为了统一管理,可以基于registry 搭建本地私有仓库。
 1、使用Docker 私有仓库有以下优点:
  • 节省网络带宽,针对每个镜像不用去Docker 官网仓库下载;
  • Docker 镜像从本地私有仓库中下载;
  • 构建公司内部私有仓库,方便各部门使用,服务器管理更加统一;
  • 可以基于GIT或SVN、Jenkins更新本地Docker 私有仓库镜像版本。
 2、使用Docker registry 构建本地私有仓库的方法及步骤:
  • 下载Docker registry 镜像
 # docker pull registry
  • 启动私有仓库容器
 # mkdir -p /data/registry/
 # docker run -itd -p 5000:5000 -v /data/registry:/tmp/registry docker.io/registry
 默认情况下,会将仓库存放于容器内的/tmp/registry目录下。这样如果容器被删除,则存放于容器中的镜像也会丢失,所以一般情况下会指定本地/data/registry 目录挂载到容器内的/tmp/registry下。
  • 上传镜像至本地私有仓库。客户端上传镜像至本地私有仓库,以busybox 镜像为例,将busybox上传至私有仓库服务器,命名如下:
 # docker pull busybox
 # docker tag busybox 192.168.1.123:5000/busybox
 # docker push 192.168.1.123:5000/busybox
  • 检测本地私有仓库:
 # curl -XGET http://192.168.1.123:5000/v2/_catalog
 # curl -XGET http://192.168.1.123:5000/v2/busybox/tags/list
  • 客户端本地私有仓库。在客户端Docker 配置文件/etc/sysconfig/docker 中添加以下代码,同时重启Docker 服务,获取本地私有仓库。
 OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false --insecure-registry 192.168.1.123:5000'
 ADD_REGISTRY='--add-registry 192.168.1.123:5000'
      至此,Dokcer 本地私有仓库部署完毕,可以向仓库中添加、更新Docker 镜像,或查看、删除Docker 仓库相关的镜像,操作命令如下:
 # curl -XGET http://192.168.1.123:5000/v2/_catalog
 # curl -XGET http://192.168.1.123:5000/v2/image_name/tags/list
 # curl -X DELETE http://192.168.1.123:5000/v1/repositories/镜像名称
 #v2 版本,官网不建议删除私有仓库中的镜像,可以基于delete-docker-registry-image工具
 #删除Dokcer 镜像
 # curl https://raw.githubusercontent.com/burnettk/delete-docker-registry-image/master/delete_docker_registry_image.py|sudo tee /usr/local/bin/delete_docker_registry_image >/dev/null
 # chmod a+x /usr/local/bin/delete_docker_registry_image
 # export REGISTRY_DATA_DIR=/data/registry/v2
 # delete_docker_registry_image --image centos:v1
  
 十三、Docker 自动化部署(一)
      想批量应用于生产环境,需要编写能够实现自动安装并配置Docker 虚拟化及桥接网络的脚本,同时使用pipework 这个软件来配置容器IP,能够实现容器简单的管理,以下为CentOS 6.X Linux 系统一键安装、配置、管理Docker 的shell 脚本,脚本代码如下:
 #!/bin/bash
 #auto install docker and create VM
 #By tang.com 2018
 #Define PATH Varablies
 IPADDR=`ifconfig|grep "Bcast"|awk '{print $2}'|cut -d: -f2|grep "192.168"|head -1`
 GATEWAY=`route -n|grep "UG"|awk '{print $2}'|grep "192.168"|head -1`
 DOCKER_IPADDR=$1
 IPADDR_NET=`ifconfig|grep "Bcast"|awk '{print $2}'|cut -d: -f2|grep "192.168"|head -1|awk -F. '{print $1"."$2"."$3".""xxx"}'`
 NETWORK=(
         HWADDR=`ifconfig eht0 |egrep "HWaddr|Bcast"|tr "\n" " "|awk '{print $5,$7,$NF}'|sed -e 's/addr://g' -e 's/Mask://g'|awk '{print $1}'`
         IPADDR=`ifconfig eht0 |egrep "HWaddr|Bcast"|tr "\n" " "|awk '{print $5,$7,$NF}'|sed -e 's/addr://g' -e 's/Mask://g'|awk '{print $2}'`
         NETMASK=`ifconfig eht0 |egrep "HWaddr|Bcast"|tr "\n" " "|awk '{print $5,$7,$NF}'|sed -e 's/addr://g' -e 's/Mask://g'|awk '{print $3}'`
         GATEWAY=`route -n|grep "UG"|awk '{print #2}'`
 )
 if [ -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" ];then
         echo -e "\033[32m------------------------------------\033[0m"
         echo -e "\033[32mPlease exec $0 IPADDR CPU(C) MEM(G) DISK(G),example $0 $IPADDR_NET 16 32 50\033[0m"
         exit 0
 fi
 CPU=`expr $2 -1`
 if [ ! -e /usr/bin/bc ];then
         yum install bc -y >>/dev/null 2>&1
 fi
 MEM_F=`echo $3 \*1024|bc`
 MEM=`printf "%.0f\n" $MEM_F`
 DISK=$4
 USER=$5
 REMARK=$6
 ping $DOCKER_IPADDR -c 1 >>/dev/null 2>&1
 if [ $? -eq 0 ];then
         echo -e "\033[32m-------------------\033[0m"
         echo -e "\033[32mThe IP address to be used,Please change other IP,exit.\033[0m"
         exit 0
 fi
 if [ ! -e /etc/init.d/docker ];then
         rmp -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
         yum install docker-io -y
         yum install device-mapper* -y
         /etc/init.d/docker start
         if [ $? -ne 0 ];then
                 echo "Docker install error,please check."
                 exit
         fi
 fi
 cd /etc/sysconfig/network-scripts/
 mkdir -p /data/backup/`data +%Y%m%d-%H%M`
 yes|cp ifcfg-eth* /data/backup/`data +%Y%m%d-%H%M`/
 if [ -e /etc/sysconfig/network-scripts/ifcfg-br0 ];then
         echo
 else
         cat>ifcfg-eth0<<EOF
         DEVICE=eth0
         BOOTPROTO=none
         ${NETWORK[0]}
         NM_CONTROLLED=no
         ONBOOT=yes
         TYPE=Ethernet
         BRIDGE="br0"
         ${NETWORK[1]}
         ${NETWORK[2]}
         ${NETWORK[3]}
         URERCTL=no
 EOF
         cat>ifcfg-br0<<EOF
         DEVICE="br0"
         BOOTPROTO=none
         ${NETWORK[0]}
         IPV6INIT=no
         ONBOOT=yes
         TYPE="Bridge"
         ${NETWORK[1]}
         ${NETWORK[2]}
         ${NETWORK[3]}
         USERCTL=no
 EOF
         /etc/init.d/network restart
 fi
 echo "Your can restart Ethernet Service:/etc/init.d/network restart !"
 echo '---------------------------------------------------------------'
 cd -
 #######create docker container
 service docker status >>/dev/null
 if [ $? -ne 0 ];then
         /etc/init.d/docker restart
 fi
 NAME="Docker_`echo $DOCKER_IPADDR|awk -F"." '{print $(NF-1)"_"$NF}'`"
 IMAGES=`docker images|grep -v "REPOSITORY"|grep -v "none"|head -1|awk '{print $1}'`
 CID=$(docker run -itd --cpuset-cpus=0-$CPU -m ${MEM}m --net=none --name=$NAME $IMAGES /bin/bash)
 if [ -z $IMAGES ];then
         echo "Please Download Docker CentOS Images,you can to be use docker search CentOS,and docker pull CentOS6.5-ssh,exit 0"
         exit 0
 fi
 if [ ! -f /usr/local/bin/pipework ];then
         you install wget unzip zip -y
         wget https://github.com/jpetazzo/pipework/archive/master.zip
         unzip master
         cp pipework-master/pipework /usr/local/bin/
         chmod +x /usr/local/bin/pipework
         rm -rf master
 fi
 ip netns >> /dev/null
 if [ $? -ne 0 ];then
         rpm -e iproute --nodeps
         rpm -ivh https://repos.fedorapeople.org/openstack/EOL/openstack-grizzly/epel-6/iproute-2.6.32-120.el6ost.netns.2.x86_64.rpm
 fi
 pipework br0 $NAME $DOCKER_IPADDR/24@ $IPADDR
 docker ps -a |grep "$NAME"
 DEV=$(basename $(echo /dev/mapper/docker-*-$CID))
 dmsetup table $DEV |sed "s/0 [0-9]* thin/0 $((${DISK}*1024*1024*1024/512)) thin/"|dmsetup load $DEV
 dmsetup resume $DEV
 resize2fs /dev/mapper/$DEV
 docker start $CID
 docker logs $CID
 LIST="docker_vmlist.csv"
 if [ ! -e $LIST ];then
         echo "编号,容器ID,容器名称,CPU,内存,硬盘,容器IP,宿主机IP,使用人中,备注">$LIST
 fi
 ######################################
 NUM=`cat docker_vmlist.csv|grep -v CPU|tail -1|awk -F, '{print $1}'`
 if [[ $NUM -eq "" ]];then
         NUM="1"
 else
         NUM=`expr $NUM + 1`
 fi
 ######################################
 echo -e "\033[32mCreate virtual client Successfully.\n$NUM `echo $CID|cut -b 1-12` $NAME $2C ${MEM}M ${DISK}G $DOCKER_IPADDR $IPADDR $USER $REMARK\033[0m"
 if [ -z $USER ];then
         USER="NULL"
         REMARK="NULL"
 fi
 echo $NUM,`echo $CID|cut -b 1-12`,$NAME,${2}C,${MEM}M,${DISK}G,$DOCKER_IPADDR,$IPADDR,$USER,$REMARK>>$LIST
 rm -rf docker_vmlist_*
 iconv -c -f utf-8 -t gb2312 docker_vmlist.csv -o docker_vmlist_`date +%H%M`.csv
  
 十四、Docker 自动化部署(二)
      目前越来越多的企业开始使用CentOS7系统,以下为CentOS7.X Linux 系统一键安装、配置Docker的shell脚本:
 #!/bin/bash
 #auto install docker and create VM
 #by tang.com 2018
 #Define PATH Varablies
 IPADDR=`ifconfig|grep -E "\<inet\>"|awk '{print $2}'|grep "192.168"|head -1`
 GATEWAY=`route -n |grep "UG"|awk '{print $2}'|grep "192.168"|head -1`
 IPADDR_NET=`ifconfig|grep -E "\<inet\>"|awk '{print $2}'|grep "192.168"|head -1|awk -F. '{print $1"."$2"."$3"."}'`
 LIST="/root/docker_vmlist.csv"
 if [ ! -f /usr/sbin/ifconfig ];then
         yum install net-tools -y
 fi
 for i in `seq 1 253`;do ping -c 1 ${IPADDR_NET} ${i};[ $? -ne 0 ]$$DOCKER_IPADDR="${IPADDR_NET}${i}"$$break;done>>/dev/null 2>$1
 echo "#########################################"
 echo -e "Dynamic get docker IP,The Docker IP address\n\n$DOCKER_IPADDR"
 NETWORK=(
         HWADDR=`ifconfig eht0 |grep ether|awk '{pirnt $2}'`
         IPADDR=`ifconfig eht0 |grep -E "\<inet\>"|awk '{print $2}'`
         NETMASK=`ifconfig eht0 |grep -E "\<inet\>"|awk '{print $4}'`
         GATEWAY=`route -n|grep "UG"|awk '{print $2}'`
 )
 if [ -z "$1" -o -z "$2" ];then
         echo -e "\033[32m---------------------------\033[0m"
         echo -e "\033[32mPlease exec $0 CPU(C) MEM(G),example $0 4 8\033[0m"
         exit 0
 fi
 #CPU=`expr $2 - 1`
 if [ ! -e /usr/bin/cb ];then
         yum install bc -y >>/dev/null 2>&1
 fi
 CPU_ALL=`cat /proc/cpuinfo |grep processor|wc -l`
 if [ ! -f $LIST ];then
         CPU_COUNT=$1
         CPU_1="0"
         CPU1=`expr $CPU_1 + 0`
         CPU2=`expr $CPU1 + $CPU_COUNT - 1`
         if [ $CPU2 -gt $CPU_ALL ];then
                 echo -e "\033[32mThe System CPU count is $CPU_ALL,not more than it.\033[0m"
                 exit
         fi
 else
         CPU_COUNT=$1
         CPU_1=`cat $LIST|tail -1|awk -F"," '{print $4}'|awk -F"-" '{print $2}'`
         CPU1=`expr $CPU_1 + 1`
         CPU2=`expr $CPU1 + $CPU_COUNT - 1`
         if [ $CPU2 -gt $CPU_ALL ];then
                 echo -e "\033[32mThe System CPU count is $CPU_ALL,not more than it.\033[0m"
                 exit
         fi
 fi
 MEM_F=`echo $2 \* 1024|bc`
 MEM=`printf "%.0f\n" $MEM_F`
 DISK=20
 USER=$3
 REMARK=$4
 ping $DOCKER_IPADDR -c 1>>/dev/null 2>&1
 if [ $? -eq 0 ];then
         echo -e "\033[32m------------------\033[0m"
         echo -e "\033[32mThe IP address to be used,Please change other IP,exit.\033[0m"
         exit 0
 fi
 if [ ! -e /usr/bin/docker ];then
         yum install docker* device-mapper* lxc -y
         mkdir -p /export/docker/
         cd /var/lib/ ;rm -rf docker;ln -s /export/docker/ .
         mkdir -p /var/lib/docker/devicemapper/devicemapper
         dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/data bs=1G count=0 seek=2000
         service docker start
         if [ $? -ne 0 ];then
                 echo "Docker install error,please check."
                 exit
         fi
 fi
 cd /etc/sysconfig/network-scripts/
         mkdir -p /data/backup/`date +%Y%m%d-%H%M`
         yes|cp ifcfg-eth* /data/backup/`data +%Y%m%d-%H%M`/
 if [ -e /etc/sysconfig/network-scripts/ifcfg-br0 ];then
         echo
 else
         cat>ifcfg-eth0<<EOF
         DEVICE=eth0
         BOOTPROTO=none
         ${NETWORK[0]}
         NM_CONTROLLED=no
         ONBOOT=yes
         TYPE=Ethernet
         BRIDGE="br0"
         ${NETWORK[1]}
         ${NETWORK[2]}
         ${NETWORK[3]}
         URERCTL=no
 EOF
         cat>ifcfg-br0<<EOF
         DEVICE="br0"
         BOOTPROTO=none
         ${NETWORK[0]}
         IPV6INIT=no
         NM_CONTROLLED=no
         ONBOOT=yes
         TYPE="Bridge"
         ${NETWORK[1]}
         ${NETWORK[2]}
         ${NETWORK[3]}
         USERCTL=no
 EOF
         /etc/init.d/network restart
 fi
 echo "Your can restart Ethernet Service:/etc/init.d/network restart !"
 echo '---------------------------------------------------------------'
 cd -
 #######create docker container
 NAME="Docker_`echo $DOCKER_IPADDR|awk -F"." '{print $(NF-1)"_"$NF}'`"
 IMAGES=`docker images|grep -v "REPOSITORY"|grep -v "none"|grep "CentOS"|head -1|awk '{print $1}'`
 if [ -z $IMAGES ];then
         echo "Please Download Docker CentOS Images,you can to be use docker search CentOS,and docker pull CentOS6.5-ssh,exit 0"
         if [ ! -f tang_CentOS68.tar ];then
                 echo "Please upload tang_CentOS68.tar for docker server."
                 exit
         fi
         cat tang_CentOS68.tar|docker import - tang_CentOS6.8
 fi
 IMAGES=`docker images|grep -v "REPOSITORY"|grep -v "none"|grep "CentOS"|head -1|awk '{print $1}'`
 CID=$(docker run -itd --privileged --cpuset-cpus=${CPU1}-${CPU2} -m ${MEM}m --net=none --name=$NAME $IMAGES /bin/bash)
 echo $CID
 docker ps -a |grep "$NAME"
 pipework br0 $NAME $DOCKER_IPADDR/24@ $IPADDR
 docker exec $NAME /etc/init.d/sshd start
 if [ ! -e $LIST ];then
                 echo "编号,容器ID,容器名称,CPU,内存,硬盘,容器IP,宿主机IP,使用人中,备注">$LIST
 fi
 ######################################
 NUM=`cat $LIST|grep -v CPU|tail -1|awk -F, '{print $1}'`
 if [[ $NUM -eq "" ]];then
         NUM="1"
 else
         NUM=`expr $NUM + 1`
 fi
 ###################################### 
 echo -e "\033[32mCreate virtual client Successfully.\n$NUM `echo $CID|cut -b 1-12` $NAME $CPU1-$CPU2 ${MEM}M ${DISK}G $DOCKER_IPADDR $IPADDR $USER $REMARK\033[0m"
 if [ -z $USER ];then
         USER="NULL"
         REMARK="NULL"
 fi
 echo $NUM,`echo $CID|cut -b 1-12`,$NAME,$CPU1-$CPU2,${MEM}M,${DISK}G,$DOCKER_IPADDR,$IPADDR,$USER,$REMARK>>$LIST
 rm -rf /root/docker_vmlist_*
 iconv -c -f utf-8 -t gb2312 $LIST -o /root/docker_vmlist_`date +%H%M`.csv