Docker之Docker概念,网络,数据卷-元一软件
1.1 简介Docker最初是dotCloud公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目于 2013 年 3 月以Apache 2.0授权协议开源主要项目代码在GitHub上进行维护。Docker 使用 Google 公司推出的 Go 语言 进行开发实现。docker是linux容器的一种封装提供简单易用的容器使用接口。它是最流行的Linux容器解决方案。docker的接口相当简单用户可以方便的创建、销毁容器。docker将应用程序与程序的依赖打包在一个文件里面。运行这个文件就会生成一个虚拟容器。程序运行在虚拟容器里如同在真实物理机上运行一样有了docker就不用担心环境问题了1.2 Docker架构Docker包括三个基本概念:镜像ImageDocker镜像Image就相当于是一个root文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。通过镜像启动一个容器一个镜像是一个可执行的包其中包括运行应用程序所需要的所有内容包含代码运行时间库、环境变量、和配置文件。Docker镜像也是一个压缩包只是这个压缩包不只是可执行文件环境部署脚本它还包含了完整的操作系统。因为大部分的镜像都是基于某个操作系统来构建所以很轻松的就可以构建本地和远端一样的环境这也是Docker镜像的精髓。容器Container镜像Image和容器Container的关系就像是面向对象程序设计中的类和实例一样镜像是静态的定义容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。所创建的每一个容器都是相互隔离、互不可见以保证平台的安全性。可以把容器看做是一个简易版的linux环境包括root用户权限、镜像空间、用户空间和网络空间等和运行在其中的应用程序仓库Repository仓库可看成一个代码控制中心用来保存镜像。仓库注册服务器上往往存放着多个仓库每个仓库中包含了多个镜像每个镜像有不同标签tag仓库分为公开仓库Public和私有仓库Private两种形式。最大的公开仓库是Docker Hub:https://hub.docker.com存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等Docker使用客户端-服务器 (C/S) 架构模式使用远程API来管理和创建Docker容器Docker容器通过Docker镜像来创建在这里插入图片描述概念说明Docker 镜像(Images)Docker 镜像是用于创建 Docker 容器的模板比如 Ubuntu 系统。Docker 容器(Container)容器是独立运行的一个或一组应用是镜像运行时的实体Docker 客户端(Client)Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。Docker 主机(Host)一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。Docker RegistryDocker 仓库用来保存镜像可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。一个 Docker Registry 中可以包含多个仓库Repository每个仓库可以包含多个标签Tag每个标签对应一个镜像。通常一个仓库会包含同一个软件不同版本的镜像而标签就常用于对应该软件的各个版本。我们可以通过仓库名:标签的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签将以latest 作为默认标签Docker MachineDocker Machine是一个简化Docker安装的命令行工具通过一个简单的命令行即可在相应的平台上安装Docker比如VirtualBox、 Digital Ocean、Microsoft Azure1.3 Docker与虚拟机的区别虚拟机通过添加Hypervisor层虚拟化中间层虚拟出网卡、内存、CPU等虚拟硬件再在其上建立虚拟机每个虚拟机都有自己的系统内核。而Docker容器则是通过隔离namesapce的方式将文件系统、进程、设备、网络等资源进行隔离再对权限、CPU资源等进行控制cgroup最终让容器之间互不影响容器无法影响宿主机。与虚拟机相比容器资源损耗要少。同样的宿主机下能够建立容器的数量要比虚拟机多但是虚拟机的安全性要比容器稍好而docker容器与宿主机共享内核、文件系统等资源更有可能对其他容器、宿主机产生影响特性容器虚拟机启动秒级分钟级硬盘使用一般为MB一般为GB性能接近原生弱于系统支持量单机支持上千个容器一般是几十个1.4 Docker 如何工作Docker技术使用Linux内核和内核功能例如 Cgroups 和 namespaces来分隔进程以便各进程相互独立运行。这种独立性正是采用容器的目的所在它可以独立运行多种进程、多个应用程序更加充分地发挥基础设施的作用同时保持各个独立系统的安全性。容器工具包括 Docker可提供基于镜像的部署模式。这使得它能够轻松跨多种环境与其依赖程序共享应用或服务组。Docker还可在这一容器环境中自动部署应用程序或者合并多种流程以构建单个应用程序。此外由于这些工具基于Linux容器构建使得Docker既易于使用又别具一格 —— 它可为用户提供前所未有的高度应用程访问权限、快速部署以及版本控制和分发能力。1.5 Docker技术是否与传统的Linux容器相同答案否Docker技术最初是基于LXC技术构建大多数人都会将这一技术与传统的 Linux容器联系在一起但后来它逐渐摆脱了对这种技术的依赖。就轻量级虚拟化这一功能来看LXC非常有用但它无法提供出色的开发人员或用户体验。除了运行容器之外Docker技术还具备其他多项功能包括简化用于构建容器、传输镜像以及控制镜像版本的流程。传统的Linux容器使用init系统来管理多种进程。这意味着所有应用程序都作为一个整体运行。与此相反Docker技术鼓励应用程序各自独立运行其进程并提供相应工具以实现这一功能。这种精细化运作模式自有其优势。1.5.1 什么是 Linux 容器Linux容器是与系统其他部分隔离开的一系列进程从另一个镜像运行并由该镜像提供支持进程所需的全部文件。容器提供的镜像包含了应用的所有依赖项因而在从开发到测试再到生产的整个过程中它都具有可移植性和一致性。在这里插入图片描述更加详细地来说假定在开发一个应用。使用的是一台笔记本电脑而开发环境具有特定的配置。其他开发人员身处的环境配置可能稍有不同。正在开发的应用依赖于当前的配置还要依赖于某些特定文件。与此同时企业还拥有标准化的测试和生产环境且具有自身的配置和一系列支持文件。你希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销。因此你要如何确保应用能够在这些环境中运行和通过质量检测并且在部署过程中不出现令人头疼的问题也无需重新编写代码和进行故障修复答案就是使用容器。容器可以确保你的应用拥有必需的配置和文件使得这些应用能够在从开发到测试、再到生产的整个流程中顺利运行而不出现任何不良问题。虽然这只是简化的示例但在需要很高的可移植性、可配置性和隔离的情况下我们可以利用Linux容器通过很多方式解决难题。无论基础架构是在企业内部还是在云端或者混合使用两者容器都能满足你的需求。1.5.2 容器是虚拟化吗是但也不竟然。我们用一种简单方式来思考一下虚拟化使得许多操作系统可同时在单个系统上运行。容器则可共享同一个操作系统内核将应用进程与系统其他部分隔离开。在这里插入图片描述这意味着什么首先让多个操作系统在单个虚拟机监控程序上运行以实现虚拟化并不能达成和使用容器同等的轻量级效果。事实上在仅拥有容量有限的有限资源时需要能够可以进行密集部署的轻量级应用。Linux容器可从单个操作系统运行在所有容器中共享该操作系统因此应用和服务能够保持轻量级并行快速运行1.6 Docker中的镜像分层参考文档http://www.maiziedu.com/wiki/cloud/dockerimageDocker支持通过扩展现有镜像创建新的镜像。实际上Docker Hub中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。在这里插入图片描述从上图可以看到新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件就在现有镜像的基础上增加一层。1.6.1 Docker镜像为什么分层镜像分层最大的一个好处就是共享资源比如说有多个镜像都从相同的 base 镜像构建而来那么 Docker Host 只需在磁盘上保存一份 base 镜像同时内存中也只需加载一份 base 镜像就可以为所有容器服务了。而且镜像的每一层都可以被共享。如果多个容器共享一份基础镜像当某个容器修改了基础镜像的内容比如 /etc 下的文件这时其他容器的 /etc 是不会被修改的修改只会被限制在单个容器内。这就是容器Copy-on-Write特性。1.6.2 可写的容器层当容器启动时一个新的可写层被加载到镜像的顶部。这一层通常被称作容器层容器层之下的都叫镜像层。在这里插入图片描述所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的容器层下面的所有镜像层都是只读的。1.6.3 容器层的细节说明镜像层数量可能会很多所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件比如 /a上层的 /a 会覆盖下层的 /a也就是说用户只能访问到上层中的文件 /a。在容器层中用户看到的是一个叠加之后的文件系统。文件操作说明文件操作说明添加文件在容器中创建文件时新文件被添加到容器层中读取文件在容器中读取某个文件时Docker 会从上往下依次在各镜像层中查找此文件。一旦找到立即将其复制到容器层然后打开并读入内存修改文件在容器中修改已存在的文件时Docker 会从上往下依次在各镜像层中查找此文件。一旦找到立即将其复制到容器层然后修改之删除文件在容器中删除文件时Docker 也是从上往下依次在镜像层中查找此文件。找到后会在容器层中记录下此删除操作。只是记录删除操作只有当需要修改时才复制一份数据这种特性被称作Copy-on-Write。可见容器层保存的是镜像变化的部分不会对镜像本身进行任何修改。这样就解释了我们前面提出的问题容器层记录对镜像的修改所有镜像层都是只读的不会被容器修改所以镜像可以被多个容器共享。1.7 Docker网络类型在这里插入图片描述1.7.1 docker的网络类型类型说明None不为容器配置任何网络功能没有网络--netnoneContainer与另一个运行中的容器共享Network Namespace--netcontainer:containerIDHost与主机共享Network Namespace--nethostBridgeDocker设计的NAT网络模型默认类型Bridge默认docker网络隔离基于网络命名空间在物理机上创建docker容器时会为每一个docker容器分配网络命名空间并且把容器IP桥接到物理机的虚拟网桥上。1.7.1.1 host模式host模式 使用--nethost指定相当于VMware中的桥接模式与宿主机在同一个网络中但是没有独立IP地址Docker使用了Linux的Namespace技术来进行资源隔离如PID Namespace隔离进程Mount Namespace隔离文件系统Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境包括网卡路由iptable规则等都与其他Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace但是如果启动容器的时候使用host模式那么这个容器将不会获得一个独立的Network Namespace而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡配置自己的IP等而是使用宿主机的IP和端口。此时容器不再拥有隔离的、独立的网络栈。不拥有所有端口资源在这里插入图片描述1.7.1.2 container模式container模式使用--netcontainer:contatiner:NAME_or_ID指定这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace而不是和宿主机共享。新创建的容器不会创建自己的网卡配置自己的IP而是和一个指定的容器共享IP端口范围等。 可以在一定程度上节省网络资源容器内部依然不会拥有所有端口。同样两个容器除了网络方面其他的如文件系统进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信在这里插入图片描述1.7.1.3 none 模式none模式:使用--netnone指定使用none模式docker容器有自己的network Namespace但是并不为Docker容器进行任何网络配置。也就是说这个Docker容器没有网卡ip 路由等信息。这种网络模式下容器只有lo回环网络没有其他网卡。这种类型没有办法联网但是封闭的网络能很好的保证容器的安全性该容器将完全独立于网络用户可以根据需要为容器添加网卡。此模式拥有所有端口。none网络模式配置网络特殊情况下才会用到一般不用1.7.1.4 bridge模式相当于Vmware中的nat模式容器使用独立network Namespace并连接到docker0虚拟网卡。通过docker0网桥以及iptables nat表配置与宿主机通信此模式会为每一个容器分配Network Namespace、设置IP等并将一个主机上的Docker容器连接到一个虚拟网桥上。当Docker进程启动时会在主机上创建一个名为docker0的虚拟网桥此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似这样主机上的所有容器就通过交换机连在了一个二层网络中。从docker0子网中分配一个IP给容器使用并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的它们组成了一个数据的通道数据从一个设备进入就会从另一个设备出来。因此veth设备常用来连接两个网络设备。Docker将veth pair设备的一端放在新创建的容器中并命名为eth0容器的网卡另一端放在主机中 以veth*这样类似的名字命名并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。容器之间通过veth pair进行访问使用docker run -p时docker实际是在iptables做了DNAT规则实现端口转发功能。可以使用iptables -t nat -vnL查看在这里插入图片描述转载于https://mp.weixin.qq.com/s/VkRQyvXfo-dLem5Bv0094w1.7.2 不为容器配置网络功能此模式下创建容器是不会为容器配置任何网络参数的如容器网卡、IP、通信路由等全部需要自己去配置。[rootdocker01 ~]# docker run -it --network none busybox:latest /bin/sh/# ip a1: lo:LOOPBACK,UP,LOWER_UPmtu65536qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet127.0.0.1/8 scopehostlo valid_lft forever preferred_lft forever1.7.3 与其他容器共享网络配置(Container此模式和host模式很类似只是此模式创建容器共享的是其他容器的IP和端口而不是物理机此模式容器自身是不会配置网络和端口创建此模式容器进去后你会发现里边的IP是你所指定的那个容器IP并且端口也是共享的而且其它还是互相隔离的如进程等。[rootdocker01 ~]# docker run -it --network container:mywordpress_db_1 busybox:latest /bin/sh/# ip a1: lo:LOOPBACK,UP,LOWER_UPmtu65536qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet127.0.0.1/8 scopehostlo valid_lft forever preferred_lft forever105: eth0if106:BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWNmtu1500qdisc noqueue link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff inet172.18.0.3/16 brd172.18.255.255 scope global eth0 valid_lft forever preferred_lft forever1.7.4 使用宿主机网络此模式创建的容器没有自己独立的网络命名空间是和物理机共享一个Network Namespace并且共享物理机的所有端口与IP并且这个模式认为是不安全的。[rootdocker01 ~]# docker run -it --network host busybox:latest /bin/sh1.7.5 查看网络列表[rootdocker01 ~]# docker network listNETWORK ID NAME DRIVER SCOPE b15e8a720d3b bridge bridgelocal345d65b4c2a0hosthostlocalbc5e2a32bb55 mywordpress_default bridgelocalebf76eea91bb none nulllocal1.7.6 用PIPEWORK为docker容器配置独立IP参考文档http://blog.csdn.net/design321/article/details/48264825官方网站https://github.com/jpetazzo/pipework宿主环境centos7.21、安装pipeworkwgethttps://github.com/jpetazzo/pipework/archive/master.zipunzipmaster.zipcppipework-master/pipework /usr/local/bin/chmodx /usr/local/bin/pipework2、配置桥接网卡安装桥接工具yuminstallbridge-utils.x86\_64-y修改网卡配置实现桥接# 修改eth0配置让br0实现桥接[rootdocker01 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0TYPEEthernetBOOTPROTOstaticNAMEeth0DEVICEeth0ONBOOTyesBRIDGEbr0[rootdocker01 ~]# cat /etc/sysconfig/network-scripts/ifcfg-br0TYPEBridgeBOOTPROTOstaticNAMEbr0DEVICEbr0ONBOOTyesIPADDR10.0.0.100NETMASK255.255.255.0GATEWAY10.0.0.254DNS1223.5.5.5# 重启网络[rootdocker01 ~]# /etc/init.d/network restart3、运行一个容器镜像测试pipework br0\$\(docker run-d-it-p6880:80--namehttpd\_pw httpd\)10.0.0.220/24\10.0.0.254在其他主机上测试端口及连通性[rootdocker01 ~]# curl 10.0.0.220htmlbodyh1It works!/h1/body/html[rootdocker01 ~]# ping 10.0.0.220 -c 1PING10.0.0.220(10.0.0.220)56(84)bytes of data.64bytes from10.0.0.220:icmp_seq1ttl64time0.043ms4、再运行一个容器设置网路类型为nonepipework br0$(dockerrun-d-it--netnone--nametesthttpd:2.4)10.0.0.221/2410.0.0.254进行访问测试[rootdocker01 ~]# curl 10.0.0.221htmlbodyh1It works!/h1/body/html5、重启容器后需要再次指定pipework br0 testduliip172.16.146.113/24\172.16.146.1 pipework br0 testduliip01172.16.146.112/24\172.16.146.1Docker跨主机通信之overlay可以参考http://www.cnblogs.com/CloudMan6/p/7270551.html1.7.7 Docker跨主机通信之macvlan创建网络[rootdocker01 ~]# docker network create --driver macvlan --subnet 10.1.0.0/24 --gateway 10.1.0.254 -o parenteth0 macvlan_133a1f41dcc074f91b5bd45e7dfedabfb2b8ec82db16542f05213839a119b62ca设置网卡为混杂模式iplinkseteth0 promisc on创建使用macvlan网络容器[rootdocker02 ~]# docker run -it --network macvlan_1 --ip10.1.0.222 busybox /bin/sh1.8 数据卷挂载卷映射1.8.1 定义数据卷是一个供容器使用的特殊目录位于容器中。可将宿主机的目录挂载到数据卷上对数据卷的修改操作立刻可见并且更新数据不会影响镜像从而实现数据在宿主机与容器之间的迁移。数据卷的使用类似于Linux下对目录进行的mount操作。如果需要在容器之间共享一些数据最简单的方法就是使用数据卷容器。数据卷容器是一个普通的容器专门提供数据卷给其他容器挂载使用。容器互联是通过容器的名称在容器间建立一条专门的网络通信隧道。简单点说就是会在源容器和接收容器之间建立一条隧道接收容器可以看到源容器指定的信息1.8.2 挂载时创建挂载目录-v[rootdocker01 ~]# docker run -d -p 80:80 -v /data:/usr/share/nginx/html nginx:latest079786c1e297b5c5031e7a841160c74e91d4ad06516505043c60dbb78a259d09容器内站点目录: /usr/share/nginx/html在宿主机写入数据查看[rootdocker01 ~]# echo http://www.nmtui.com /data/index.html[rootdocker01 ~]# curl 10.0.0.100http://www.nmtui.com设置共享目录使用同一个卷启动一个新的容器[rootdocker01 ~]# docker run -d -p 8080:80 -v /data:/usr/share/nginx/html nginx:latest351f0bd78d273604bd0971b186979aa0f3cbf45247274493d2490527babb4e42[rootdocker01 ~]# curl 10.0.0.100:8080http://www.nmtui.com查看卷列表docker volume ls[rootdocker01 ~]# docker volume lsDRIVER VOLUME NAME1.8.3 创建卷后挂载创建一个卷docker volume create xxx[rootdocker01 ~]# docker volume createf3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521[rootdocker01 ~]# docker volume lsDRIVER VOLUME NAMElocalf3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521指定卷名[rootdocker01 ~]# docker volume lsDRIVER VOLUME NAMElocalclsnlocalf3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521查看卷路径docker volume inspect clsn[rootdocker01 ~]# docker volume inspect clsn[{CreatedAt:2018-02-01T00:39:2508:00,Driver:local,Labels:{},Mountpoint:/var/lib/docker/volumes/clsn/_data,Name:clsn,Options:{},Scope:local}]使用卷创建[rootdocker01 ~]# docker run -d -p 9000:80 -v clsn:/usr/share/nginx/html nginx:latest1434559cff996162da7ce71820ed8f5937fb7c02113bbc84e965845c219d3503# 宿主机测试[rootdocker01 ~]# echo blog.nmtui.com /var/lib/docker/volumes/clsn/_data/index.html[rootdocker01 ~]# curl 10.0.0.100:9000blog.nmtui.com设置卷[rootdocker01 ~]# docker run -d -P --volumes-from 079786c1e297 nginx:latestb54b9c9930b417ab3257c6e4a8280b54fae57043c0b76b9dc60b4788e92369fb1.8.4 卷映射1.8.4.1 简介与目录挂载区别目录挂载Bind Mounts由用户管理需要指定宿主机上的具体目录。它们直接映射到宿主机的文件系统允许宿主机上的用户或程序直接访问和修改容器内的数据。因此目录挂载完全依赖于宿主机的文件系统和目录结构。卷映射Volumes由Docker自动管理和维护。它们存储在Docker主机的一个特定目录中通常是/var/lib/docker/volumes/与宿主机的文件系统相隔离提供了更好的安全性和隔离性。Docker卷的生命周期可以独立于容器即使容器被删除卷中的数据也会保留。与目录挂载最大区别是-v后不以/开始在启动时即使没有文件也会以容器内文件为主而目录挂载没有文件就是空的示例命令卷映射docker run -v volume_name:/path/in/container my_image目录挂载docker run -v /path/on/host:/path/in/container my_image如何确定是具名挂载还是匿名挂载还是指定路径挂载-v容器路径 匿名挂载-v卷名:容器内路径 具名挂载-v/宿主机路径:容器内路径 指定路径挂载1.8.4.2 映射没有冒号没有冒号时比如docker run -v /app/shared_data my_image的路径含义如果只提供一个路径即-v /app/shared_data这里的路径是容器内的挂载路径。Docker会自动为该路径创建一个匿名卷宿主机由Docker管理的目录。匿名卷存储在宿主机的默认卷存储路径中例如/var/lib/docker/volumes但这个宿主机路径是由Docker自动管理的并不是用户直接指定的宿主路径。容器路径的作用-v /app/shared_data表示要在容器内部的/app/shared_data挂载一个存储。如果不指定宿主路径则 Docker 会在宿主机创建一个匿名卷并将其挂载到容器内部的 /app/shared_data。匿名卷的存储位置宿主机的匿名卷位置是Docker默认管理的路径如/var/lib/docker/volumes/{volume_id}/_data。