Docker容器基础介绍和操作
1.容器简介
1.什么是linux容器
Linux容器是与系统其他部分隔离开的⼀系列进程,从另⼀个镜像运⾏,并由该镜像提供⽀持进程所需的全部⽂件。容器提供的镜像包含了应⽤的所有依赖项,因⽽在从开发到测
试再到⽣产的整个过程中,它都具有可移植性和⼀致性。
更加详细地来说,请您假定您在开发⼀个应⽤。您使⽤的是⼀台笔记本电脑,⽽且您的开发环境具有特定的配置。其他开发⼈员⾝处的环境配置可能稍有不同。
您正在开发的应⽤依赖于您当前的配置,还要依赖于某些特定⽂件。与此同时,您的企业还拥有标准化的测试和⽣产环境,且具有⾃⾝的配置和⼀系列⽀持⽂件。
您希望尽可能多在本地模拟这些环境,⽽不产⽣重新创建服务器环境的开销。
因此,您要如何确保应⽤能够在这些环境中运⾏和通过质量检测,并且在部署过程中不出现令⼈头疼的问题,也⽆需重新编写代码和进⾏故障修复?答案就是使⽤容器。
容器可以确保您的应⽤拥有必需的配置和⽂件,使得这些应⽤能够在从开发到测试、再到⽣产的整个流程中顺利运⾏,⽽不出现任何不良问题。这样可以避免危机,做到皆⼤欢喜。
虽然这只是简化的⽰例,但在需要很⾼的可移植性、可配置性和隔离的情况下,我们可以利⽤Linux容器通过很多⽅式解决难题。⽆论基础架构是在企业内部还是在云端,或
者混合使⽤两者,容器都能满⾜您的需求。
2.容器不就是虚拟化吗
是,但也不竟然。我们⽤⼀种简单的⽅式来思考⼀下:
虚拟化使得许多操作系统可同时在单个系统上运⾏。
容器则可共享同⼀个操作系统内核,将应⽤程序与系统其他部分隔离开。
图-普通虚拟化技术和Docker的对⽐
这意味着什么?⾸先,让多个操作系统在单个虚拟机监控程序上运⾏以实现虚拟化,并不能达成和使⽤容器同等的轻量级效果。事实上,在仅拥有容量有限的有限资源时,您需要能够可以进⾏密集部署
的轻量级应⽤。Linux容器可从单个操作系统运⾏,在所有容器中共享该操作系统,因此应⽤和服务能够保持轻量级,并⾏快速运⾏。
docker与Openstack的对⽐
3.容器发展简史
我们现在称为容器技术的概念最初出现在2000年,当时称为FreeBSDjail,这种技术可将FreeBSD系统分区为多个⼦系统(也称为Jail)。Jail是作为安全环境⽽开发
的,系统管理员可与企业内部或外部的多个⽤户共享这些Jail。
Jail的⽬的是让进程在经过修改的chroot(更改某个进程所能看到的根⽬录,即将某进程限制在指定⽬录中,保证该进程只能对该⽬录及其⼦⽬录的⽂件有所动作,从⽽保证
整个服务器的安全。)环境中创建,⽽不会脱离和影响整个系统—在chroot环境中,对⽂件系统、⽹络和⽤户的访问都实现了虚拟化。尽管Jail在实施⽅⾯存在局限性,但最
终⼈们找到了脱离这种隔离环境的⽅法。
但这个概念⾮常有吸引⼒。
2001年,通过JacquesGélinas的VServer项⽬,隔离环境的实施进⼊了Linux领域。正如Gélinas所说,这项⼯作的⽬的是“在⾼度独⽴且安全的单⼀环境中运⾏多个通
⽤Linux服务器[sic]。”在完成了这项针对Linux中多个受控制⽤户空间的基础性⼯作后,Linux容器开始逐渐成形并最终发展成了现在的模样。
简介
1.什么是docker
Docker是PaaS提供商dotCloud开源的⼀个基于LXC(LXC是Linux原⽣⽀持的容器技术,可以提供轻量化的虚拟化,docker提供LXC的⾼级封装,发展标准的配置⽅法)的⾼
级容器引擎,源代码托管在Github上,基于go语⾔并遵从Apache2.0协议开源。Docker是通过内核虚拟化技术(namespace以及cgroups等)来提供容器的资源隔离与安全保
障。由于Docker通过操作系统层的虚拟化实现隔离,所以Docker容器在运⾏时,不需要类似虚拟机(VM)额外的操作系统开销,提⾼资源利⽤率。
Docker是使⽤Go语⾔编写的⼀个程序运⾏、测试、交付的开放平台,Docker被设计为能够使你快速地交付应⽤。在Docker中,你可以将你的程序分为不同的基础部分,对于每
⼀个基础部分都可以当做⼀个应⽤程序来管理。Docker能够帮助你快速地测试、快速地编码、快速地交付,并且缩短你从编码到运⾏应⽤的周期。Docker使⽤轻量级的容器虚拟
化平台,并且结合⼯作流和⼯具,来帮助你管理、部署你的应⽤程序。Docker在其核⼼,Docker实现了让⼏乎任何程序都可以在⼀个安全、隔离的容器中运⾏。安全和隔离可以
使你可以同时在机器上运⾏多个容器。Docker容器轻量级的特性,意味着可以得到更多的硬件性能。
的组成和架构详解
Docker主机(Host):⼀个物理机或虚拟机,⽤于运⾏Docker服务进程和容器。
Docker服务端(Server):Docker守护进程,运⾏docker容器。
Docker客户端(Client):客户端使⽤docker命令或其他⼯具调⽤dockerAPI。
Docker仓库(Registry):保存镜像的仓库,类似于git或svn这样的版本控制系统。
Docker镜像(Images):镜像可以理解为创建实例使⽤的模板。
Docker容器(Container):容器是从镜像⽣成对外提供服务的⼀个或⼀组服务。
Docker原理:建⽴-->传送-->运⾏
通过DockerHub或者⾃⼰的Docker仓库分享Docker镜像,从Docker镜像创建Docker容器,在容器⾥运⾏应⽤程序。
Docker组件:镜像(Image)、容器(Container)、仓库(Repository)
Docker架构:C/S架构
->Docker使⽤客户端-服务器(client-rver)架构模式。
->Docker客户端会与Docker守护进程进⾏通信。Docker守护进程会处理复杂繁重的任务,例如建⽴、运⾏、发布你的Docker容器。
->Docker客户端和守护进程可以运⾏在同⼀个系统上,当然也可以使⽤Docker客户端去连接⼀个远程的Docker守护进程。
->Docker客户端和守护进程之间通过socket或者RESTfulAPI进⾏通信。
Docker守护进程
如上图所⽰,Docker守护进程运⾏在⼀台主机上。⽤户并不直接和守护进程进⾏交互,⽽是通过Docker客户端间接和其通信。
Docker客户端
Docker客户端,实际上是docker的⼆进制程序,是主要的⽤户与Docker交互⽅式。它接收⽤户指令并且与背后的Docker守护进程通信,如此来回往复。
Docker内部
要理解Docker内部构建,需要理解以下三种部件:
Docker镜像-DockerImages
Docker仓库-DockerRegistry
Docker容器-DockerContainers
Docker镜像是Docker容器运⾏时的只读模板,每⼀个镜像由⼀系列的层(layers)组成。Docker使⽤UnionFS来将这些层联合到单独的镜像中。UnionFS允许独⽴⽂件系统中的
⽂件和⽂件夹(称之为分⽀)被透明覆盖,形成⼀个单独连贯的⽂件系统。正因为有了这些层的存在,Docker是如此的轻量。当你改变了⼀个Docker镜像,⽐如升级到某个程序
到新的版本,⼀个新的层会被创建。因此,不⽤替换整个原先的镜像或者重新建⽴(在使⽤虚拟机的时候你可能会这么做),只是⼀个新的层被添加或升级了。现在你不⽤重新发
布整个镜像,只需要升级,层使得分发Docker镜像变得简单和快速。
Docker仓库⽤来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker仓库也有公有和私有的概念。公有的Docker仓库名字是DockerHub。DockerHub提供了庞⼤
的镜像集合供使⽤。这些镜像可以是⾃⼰创建,或者在别⼈的镜像基础上创建。Docker仓库是Docker的分发部分。
Docker容器和⽂件夹很类似,⼀个Docker容器包含了所有的某个应⽤运⾏所需要的环境。每⼀个Docker容器都是从Docker镜像创建的。Docker容器可以运⾏、开始、停⽌、
移动和删除。每⼀个Docker容器都是独⽴和安全的应⽤平台,Docker容器是Docker的运⾏部分。
libcontainer
Docker从0.9版本开始使⽤libcontainer替代lxc,libcontainer和Linux系统的交互图如下:
镜像是如何⼯作的
Docker镜像是Docker容器运⾏时的只读模板,每⼀个镜像由⼀系列的层(layers)组成;Docker使⽤UnionFS(联合⽂件系统)来将这些层联合到单独镜像中,UnionFS⽂件系统允许
独⽴⽂件系统中的⽂件和⽂件夹(称之为分⽀)被透明覆盖,形成⼀个单独连贯的⽂件系统。
正因为有了这些层(layers)的存在,Docker才会如此的轻量。当你改变了⼀个Docker镜像,⽐如升级到某个程序到新的版本,⼀个新的层会被创建。因此,不⽤替换整个原先的
镜像或者重新建⽴(在使⽤虚拟机的时候你可能会这么做),只是⼀个新的层被添加或升级了。所以你不⽤重新发布整个镜像,只需要升级。层使得分发Docker镜像变得简单和快
速。
每个镜像都是从⼀个基础的镜像开始的,⽐如ubuntu,⼀个基础的Ubuntu镜像,或者是Centos,⼀个基础的Centos镜像。你可以使⽤你⾃⼰的镜像作为新镜像的基础,例如你
有⼀个基础的安装了Nginx的镜像,你可以使⽤该镜像来建⽴你的Web应⽤程序镜像。(Docker通常从DockerHub获取基础镜像)
Docker镜像从这些基础的镜像创建,通过⼀种简单、具有描述性的步骤,我们称之为指令(instructions)。每⼀个指令会在镜像中创建⼀个新的层,指令可以包含这些动作:
->运⾏⼀个命令。
->增加⽂件或者⽂件夹。
->创建⼀个环境变量。
->当运⾏容器的时候哪些程序会运⾏。
这些指令存储在Dockerfile⽂件中。当你需要建⽴镜像的时候,Docker可以从Dockerfile中读取这些指令并且运⾏,然后返回⼀个最终的镜像。
仓库的⽤处
Docker仓库是Docker镜像的存储仓库。可以推送镜像到Docker仓库中,然后在Docker客户端,可以从Docker仓库中搜索和拉取镜像。
容器是如何⼯作的
⼀个Docker容器包含了⼀个操作系统、⽤户添加的⽂件和元数据(meta-data)。每个容器都是从镜像建⽴的,镜像告诉Docker容器内包含了什么,当容器启动时运⾏什么程序,还
有许多配置数据。Docker镜像是只读的,当Docker运⾏⼀个从镜像建⽴的容器,它会在镜像顶部添加⼀个可读写的层,应⽤程序可以在这⾥运⾏。
技术是否与传统的Linux容器相同?
不相同。Docker技术最初是基于LXC技术构建(⼤多数⼈都会将这⼀技术与“传统的”Linux容器联系在⼀起),但后来它逐渐摆脱了对这种技术的依赖。
就轻量级虚拟化这⼀功能来看,LXC⾮常有⽤,但它⽆法提供出⾊的开发⼈员或⽤户体验。除了运⾏容器之外,Docker技术还具备其他多项功能,包括简化⽤于构建容器、传
输镜像以及控制镜像版本的流程。
传统的Linux容器使⽤init系统来管理多种进程。这意味着,所有应⽤程序都作为⼀个整体运⾏。与此相反,Docker技术⿎励应⽤程序各⾃独⽴运⾏其进程,并提供相应⼯具
以实现这⼀功能。这种精细化运作模式⾃有其优势。
容器运⾏时会做哪些事情?
使⽤docker命令时,Docker客户端都告诉Docker守护进程运⾏⼀个容器。
#dockerrun-i-tubuntu/bin/bash
可以来分析这个命令,Docker客户端使⽤docker命令来运⾏,run参数表明客户端要运⾏⼀个新的容器。
Docker客户端要运⾏⼀个容器需要告诉Docker守护进程的最⼩参数信息是:
->这个容器从哪个镜像创建,这⾥是ubuntu,基础的Ubuntu镜像。
->在容器中要运⾏的命令,这⾥是/bin/bash,在容器中运⾏Bashshell。
那么运⾏这个命令之后在底层发⽣了什么呢?按照顺序,Docker做了这些事情:
->拉取ubuntu镜像:Docker检查ubuntu镜像是否存在,如果在本地没有该镜像,Docker会从DockerHub下载。如果镜像已经存在,Docker会使⽤它来创建新的容器。
->创建新的容器:当Docker有了这个镜像之后,Docker会⽤它来创建⼀个新的容器。
->分配⽂件系统并且挂载⼀个可读写的层:容器会在这个⽂件系统中创建,并且⼀个可读写的层被添加到镜像中。
->分配⽹络/桥接接⼝:创建⼀个允许容器与本地主机通信的⽹络接⼝。
->设置⼀个IP地址:从池中寻找⼀个可⽤的IP地址并且服加到容器上。
->运⾏你指定的程序:运⾏指定的程序。
->捕获并且提供应⽤输出:连接并且记录标准输出、输⼊和错误让你可以看到你的程序是如何运⾏的。
由此就可以拥有⼀个运⾏着的Docker容器了!从这⾥开始你可以管理你的容器,与应⽤交互,应⽤完成之后,可以停⽌或者删除你的容器。
的⽬标和⽤途
Docker⽤途:简单配置、代码流⽔线管理、开发效率、应⽤隔离、服务器整合、调试能⼒、多租户、快速部署
Docker可以快速交付应⽤程序
Docker可以为你的开发过程提供完美的帮助。Docker允许开发者在本地包含了应⽤程序和服务的容器进⾏开发,之后可以集成到连续的⼀体化和部署⼯作流中。
举个例⼦,开发者们在本地编写代码并且使⽤Docker和同事分享其开发栈。当开发者们准备好了之后,他们可以将代码和开发栈推送到测试环境中,在该环境进⾏⼀切所需要的
测试。从测试环境中,你可以将Docker镜像推送到服务器上进⾏部署。
Docker可以让开发和拓展更加简单
Docker的以容器为基础的平台允许⾼度可移植的⼯作。Docker容器可以在开发者机器上运⾏,也可以在实体或者虚拟机上运⾏,也可以在云平台上运⾏。Docker的可移植、轻
量特性同样让动态地管理负载更加简单。你可以⽤Docker快速地增加应⽤规模或者关闭应⽤程序和服务。Docker的快速意味着变动⼏乎是实时的
Docker可以达到⾼密度和更多负载
Docker轻巧快速,它提供了⼀个可⾏的、符合成本效益的替代基于虚拟机管理程序的虚拟机。这在⾼密度的环境下尤其有⽤。例如,构建你⾃⼰的云平台或者PaaS,在中⼩的
部署环境下同样可以获取到更多的资源性能。
Docker改变了什么?
->⾯向产品:产品交付
->⾯向开发:简化环境配置
->⾯向测试:多版本测试
->⾯向运维:环境⼀致性
->⾯向架构:⾃动化扩容
底层技术介绍
命名空间[Namespaces]
什么是namespaces?
namespace是linux内核提供的特性,为虚拟化⽽⽣,是Linux内核⽤来隔离内核资源的⽅式。通过namespace可以让⼀些进程只能看到与⾃⼰相关的⼀部分资源,⽽另外⼀
些进程也只能看到与它们⾃⼰相关的资源,这两拨进程根本就感觉不到对⽅的存在。具体的实现⽅式是把⼀个或多个进程的相关资源指定在同⼀个namespace中。那么内核资
源有哪些呢,或者说docker容器化技术⽤了哪些namespaces技术呢,下⾯我来⼀⼀介绍下。
=>pidnamespace:使⽤在进程隔离(ProcessID)
不同⽤户的进程就是通过pidnamespace隔离开的,且不同namespace中可以有相同PID。
具有以下特征:
->每个namespace中的pid是有⾃⼰的pid=1的进程(类似/sbin/init进程,⼀般是内核完成初始化之后的第⼀个进程init。)
->每个namespace中的进程只能影响⾃⼰的同⼀个namespace或⼦namespace中的进程
->因为/proc包含正在运⾏的进程,因此在container中的pudo-filesystem(伪⽂件系统)的/proc⽬录只能看到⾃⼰namespace中的进程
->因为namespace允许嵌套,⽗namespace可以影响⼦namespace的进程,所以⼦namespace的进程可以在⽗namespace中看到,但是具有不同的pid
=>mntnamespace:使⽤在管理挂载点(Mount)
类似chroot(更改root⽬录,就是更改根⽬录),将⼀个进程放到⼀个特定的⽬录执⾏。mntnamespace允许不同namespace的进程看到的⽂件结构不同,这样每个namespace
中的进程所看到的⽂件⽬录就被隔离开了。同chroot不同,每个namespace中的container在/proc/mounts的信息只包含所在namespace的mountpoint。
=>netnamespace:使⽤在进程⽹络接⼝(Networking)
⽹络隔离是通过netnamespace实现的,每个netnamespace有独⽴的networkdevices,IPaddress,IProutingtables,/proc/net⽬录。这样每个container的⽹络就能隔离开
来。docker默认采⽤veth的⽅式将container中的虚拟⽹卡同host上的⼀个dockerbridge连接在⼀起。
=>utsnamespace:使⽤在隔离内核和版本标识(UnixTimesharingSystem)
UTS("UNIXTime-sharingSystem")namespace允许每个container拥有独⽴的hostname和domainname,使其在⽹络上可以被视作⼀个独⽴的节点⽽⾮Host上的⼀个进程。
=>ipcnamespace:使⽤在管理进程间通信资源(InterProcessCommunication)
container中进程交互还是采⽤Linux常见的进程间交互⽅法(interprocesscommunication-IPC),包括常见的信号量、消息队列和共享内存。然⽽同VM不同,container的进程
间交互实际上还是host上具有相同pidnamespace中的进程间交互,因此需要在IPC资源申请时加⼊namespace信息-每个IPC资源有⼀个唯⼀的32bitID。
=>urnamespace:使⽤在管理空户空间
每个container可以有不同的ur和groupid,也就是说可以以container内部的⽤户在container内部执⾏程序⽽⾮Host上的⽤户。
有了以上6种namespace从进程、⽹络、IPC、⽂件系统、UTS和⽤户⾓度的隔离,⼀个container就可以对外展现出⼀个独⽴计算机的能⼒,并且不同container从OS层⾯实现
了隔离。然⽽不同namespace之间资源还是相互竞争的,仍然需要类似ulimit来管理每个container所能使⽤的资源。
资源配额[cgroups]
Docker还使⽤到了cgroups技术来管理群组。使应⽤隔离运⾏的关键是让它们只使⽤你想要的资源。这样可以确保在机器上运⾏的容器都是良民(goodmulti-tenantcitizens)。群
组控制允许Docker分享或者限制容器使⽤硬件资源。例如,限制指定的容器的内容使⽤。
cgroups实现了对资源的配额和度量。cgroups的使⽤⾮常简单,提供类似⽂件的接⼝,在/cgroup⽬录下新建⼀个⽂件夹即可新建⼀个group,在此⽂件夹中新建task⽂件,
并将pid写⼊该⽂件,即可实现对该进程的资源控制。具体的资源配置选项可以在该⽂件夹中新建⼦subsystem,{⼦系统前缀}.{资源项}是典型的配置⽅法,如
nbytes就定义了该group在subsystemmemory中的⼀个内存限制选项。另外,cgroups中的subsystem可以随意组合,⼀个subsystem可以在不同的group
中,也可以⼀个group包含多个subsystem-也就是说⼀个subsystem。
=>memory
内存相关的限制
=>cpu
在cgroup中,并不能像硬件虚拟化⽅案⼀样能够定义CPU能⼒,但是能够定义CPU轮转的优先级,因此具有较⾼CPU优先级的进程会更可能得到CPU运算。通过将参数
写⼊,即可定义改cgroup的CPU优先级-这⾥是⼀个相对权重,⽽⾮绝对值
=>blkio
blockIO相关的统计和限制,byte/operation统计和限制(IOPS等),读写速度限制等,但是这⾥主要统计的都是同步IO
=>devices
设备权限限制
Docker联合⽂件系统
联合⽂件系统(UnionFS)是⽤来操作创建层的,使它们轻巧快速。Docker使⽤UnionFS提供容器的构造块。Docker可以使⽤很多种类的UnionFS包括AUFS,btrfs,vfs,and
DeviceMapper。
Docker容器格式
Docker连接这些组建到⼀个包装中,称为⼀个containerformat(容器格式)。默认的容器格式是libcontainer。Docker同样⽀持传统的Linux容器使⽤LXC。在未来,Docker也许会⽀持其它的
容器格式,例如与BSDJails或SolarisZone集成。
3.安装Docker
环境说明
#我们这⾥准备两台节点进⾏测试
[root@linux-test-no~]#cat/etc/redhat-relea
CentOSLinuxrelea7.4.1708(Core)
[root@linux-test-no~]#uname-r
7.x86_64
[root@linux-test-no~]#hostname-I
172.31.46.38
[root@centos2-no~]#hostname-I
172.31.46.78
在两个节点上都进⾏操作
[root@centos2-no~]#wget-O/etc/.d/tps:///docker-ce/linux/centos/
[root@centos2-no~]#d-i's##/docker-ce#g'/etc/.d/
[root@centos2-no~]#yuminstalldocker-ce-y
修改在docker01配置:
#修改启动⽂件,监听远程端⼝
[root@linux-test-no~]#vim/usr/lib/systemd/system/e
[root@linux-test-no~]#systemctldaemon-reload
[root@linux-test-no~]#e
Createdsymlinkfrom/etc/systemd/system//eto/usr/lib/systemd/system/e.
[root@linux-test-no~]#e
#检查是否启动
[root@linux-test-no~]#ps-ef
在docker2进⾏测试
[root@centos2-no~]#docker-H172.31.46.38info
Client:
DebugMode:fal
Server:
Containers:0
Running:0
Paud:0
Stopped:0
Images:0
ServerVersion:19.03.12
StorageDriver:overlay2
基础命令操作
查看docker相关信息
[root@linux-test-no~]#dockerversion
Client:DockerEngine-Community
Version:19.03.12
APIversion:1.40
Goversion:go1.13.10
Gitcommit:48a66213fe
Built:MonJun2215:46:542020
OS/Arch:linux/amd64
Experimental:fal
Server:DockerEngine-Community
Engine:
Version:19.03.12
APIversion:1.40(minimumversion1.12)
Goversion:go1.13.10
Gitcommit:48a66213fe
Built:MonJun2215:45:282020
OS/Arch:linux/amd64
Experimental:fal
containerd:
Version:1.2.13
GitCommit:7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version:1.0.0-rc10
GitCommit:dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version:0.18.0
GitCommit:fec3683
配置docker镜像加速
vi/etc/docker/
{
"registry-mirrors":[""]
}
2.启动第⼀个容器
[root@linux-test-no~]#dockerrun-d-p80:80nginx
参数说明
run创建并运⾏⼀个容器
-d放⼊后台
-p端⼝映射
nginx镜像名称
镜像⽣命周期
镜像相关操作
1.搜索官⽅仓库镜像
[root@linux-test-no~]#dockerarchcentos
NAMEDESCRIPTIONSTARSOFFICIALAUTOMATED
centosTheofficialbuildofCentOS.6104[OK]
ansible/centos7-ansibleAnsibleonCentos7132[OK]
consol/centos-xfce-vncCentoscontainerwith"headless"VNCssion…117[OK]
jdeathe/centos-sshOpenSSH/Supervisor/EPEL/IUS/SCLRepos-…115[OK]
centos/systemdsystemdenabledbacontainer.86[OK]
列表说明
NAME镜像名称
DESCRIPTION镜像说明
STARS点赞数量
OFFICIAL是否是官⽅的
AUTOMATED是否是⾃动构建的
2.获取镜像
根据镜像名称拉取镜像
[root@linux-test-no~]#dockerpullcentos
Usingdefaulttag:latest
latest:Pullingfromlibrary/centos
6910e5a164f7:Pullcomplete
Digest:sha256:4062bbdd1bb0801b0aa38e0f83dece70fb7a5e9bce223423a68de2d8b784b43b
Status:Downloadednewerimageforcentos:latest
/library/centos:latest
查看当前主机镜像列表
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
nginxlatest8cf1bfb43ff55daysago132MB
centoslatest831691599b885weeksago215MB
列表参数说明
REPOSITORY镜像仓库
TAG标签
IMAGEID镜像ID
CREATED创建时间
VIRTUALSIZE镜像⼤⼩
拉第三⽅镜像⽅法
/tenxcloud/httpd
dockerpull仓库服务器:端⼝/项⽬名称/镜像名称:tag(版本)号
3.导出镜像
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
nginxlatest8cf1bfb43ff55daysago132MB
centoslatest831691599b885weeksago215MB
#导出镜像
[root@linux-test-no~]#dockerimagesavecentos>
[root@linux-test-no~]#ll
total443940
-rw-r--r--1rootroot478Jul1318:
-rw-------.
-rw-r--r--1rootroot342Jul1318:
-rw-r--r--1rootroot868Jul1317:
-rw-r--r--1rootroot120Jul1316:
-rw-r--r--1rootroot686Jul1515:
-rw-r--r--1rootroot222584320Jul2711:
4.删除镜像
[root@linux-test-no~]#dockerimagermcentos:latest#红⾊字体为我们要删除的镜像,centos为镜像名称,latest为镜像标签。
Untagged:centos:latest
Untagged:centos@sha256:4062bbdd1bb0801b0aa38e0f83dece70fb7a5e9bce223423a68de2d8b784b43b
Deleted:sha256:831691599b88ad6cc2a4abbd0e89661a121aff14cfa289ad840fd3946f274f1f
Deleted:sha256:eb29745b8228e1e97c01b1d5c2554a319c00a94d8dd5746a3904222ad65a13f8
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
nginxlatest8cf1bfb43ff55daysago132MB
5.导⼊镜像
[root@linux-test-no~]##红⾊字体为你要导⼊的镜像
eb29745b8228:Loadinglayer[==================================================>]222.6MB/222.6MB
Loadedimage:centos:latest
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
nginxlatest8cf1bfb43ff55daysago132MB
centoslatest831691599b885weeksago215MB
6.查看镜像的详细信息
[root@linux-test-no~]#dockerimageinspectcentos#红⾊字体为要查看的镜像名称
7.⼿动将容器保存为镜像
#我们这⾥基于docker官⽅centos6.8镜像进⾏测试,默认我们从官⽅的下载的os镜像没有安装任何服务,就连最基础的sshd服务都不会有。所以我们⽆法从外部通过ssh直接连接容器。
[root@linux-test-no~]#dockerpullcentos:6.8
[root@linux-test-no~]#dockerps-a
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
#启动⼀个centos6.8的镜像,在容器中安装sshd服务,并修改系统密码
[root@linux-test-no~]#dockerrun-it-p1022:22centos:6.8/bin/bash
[root@f94fe91f659e/]#yuminstallopenssh-rver-y
#chpasswd是批量修改⽤户密码的命令,使⽤格式如下,我们这⾥把root密码修改为123456
[root@f94fe91f659e/]#echo"root:123456"|chpasswd
#启动ssd服务
[root@f94fe91f659e/]#/etc/init.d/sshdstart
#查看容器的ip地址,我们看到地址为172.17.0.2
[root@f94fe91f659e/]#ifconfig
eth0Linkencap:EthernetHWaddr02:42:AC:11:00:02
inetaddr:172.17.0.2Bcast:172.17.255.255Mask:255.255.0.0
UPBROADCASTRUNNINGMULTICASTMTU:1500Metric:1
RXpackets:16583errors:0dropped:0overruns:0frame:0
TXpackets:14133errors:0dropped:0overruns:0carrier:0
collisions:0txqueuelen:0
RXbytes:40026232(38.1MiB)TXbytes:970789(948.0KiB)
loLinkencap:LocalLoopback
inetaddr:127.0.0.1Mask:255.0.0.0
UPLOOPBACKRUNNINGMTU:65536Metric:1
RXpackets:0errors:0dropped:0overruns:0frame:0
TXpackets:0errors:0dropped:0overruns:0carrier:0
collisions:0txqueuelen:1
RXbytes:0(0.0b)TXbytes:0(0.0b)
#我们这个时候新开⼀个终端,在宿主机本地测试通过ssh连接容器,输⼊密码,发现测试成功
[root@linux-test-no~]#ssh172.17.0.2
Theauthenticityofhost'172.17.0.2(172.17.0.2)'can'tbeestablished.
RSAkeyfingerprintisSHA256:bsUbjHkMxqvOdix+HKXAcemrm3ImCvxYs8Ozwq5bXI4.
RSAkeyfingerprintisMD5:6e:f5:d2:b2:27:81:7d:7f:1d:73:1a:2f:e4:68:14:df.
Areyousureyouwanttocontinueconnecting(yes/no)?yes
Warning:Permanentlyadded'172.17.0.2'(RSA)tothelistofknownhosts.
root@172.17.0.2'spassword:
[root@f94fe91f659e~]
##这⾥⾯如果希望不从宿主机上连接此容器,从别的机器连接此容器,只需连接的时候指定宿主机的地址和容器上的22端⼝映射到宿主机的端⼝就⾏了。
将容器提交为镜像
[root@linux-test-no~]#dockercommit-p-a"qingbai"-m"centos6.8-sshd"f94fe91f659ecentos6-ssh
#容器提交的基本语法为dockercommit[选项]容器名或容器ID[镜像名[:标签]]
上⾯-p的作⽤是在提交的时候,将容器暂停。
-a的作⽤是指明提交镜像的作者
-m的作⽤是提交时的说明⽂字
所以上⾯的命令的作⽤就是以容器ID为f94fe91f659e的容器为基础,提交为镜像centos6-ssh,我这⾥在设镜像名的时候没有指定标签,默认就会帮我们设为latest,如果要设标签的话,
只需要在容器名后⾯加上冒号加标签,如(centos6-ssh:v1)并在提交过程中暂停容器,指明提交作者为qingbai,并附上镜像说明centos6.8-sshd
#我们查看本地镜像仓库,发现镜像已经⽣成
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
centos6-sshlatest58486de0367220condsago342MB
nginxlatest8cf1bfb43ff510daysago132MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
使⽤新的镜像启动容器
#sshd-D表⽰已后台后台守护进程⽅式运⾏sshd服务
[root@linux-test-no~]#dockerrun-d-p1122:22centos6-ssh:latest/usr/sbin/sshd-D
bbb78fad8aba3a62447e834020e42d6d524e14d1ce05180548b2c8f01d184841
对新镜像启动的容器进⾏测试,看是否⽣效
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
bbb78fad8abacentos6-ssh:latest"/usr/sbin/sshd-D"22condsagoUp22conds0.0.0.0:1122->22/tcpstrange_franklin
[root@linux-test-no~]#dockerexec-itbbb78fad8aba/bin/bash
[root@bbb78fad8aba/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
86:eth0@if87:
link/ether02:42:ac:11:00:02brdff:ff:ff:ff:ff:ff
inet172.17.0.2/16brd172.17.255.255scopeglobaleth0
valid_lftforeverpreferred_lftforever
[root@bbb78fad8aba/]#exit
#测试发现,新镜像⽣成的容器中默认加了sshd服务。
[root@linux-test-no~]#ssh172.17.0.2
root@172.17.0.2'spassword:
[root@bbb78fad8aba~]
#5.容器的⽇常管理
1.容器的起/停和查看
最简单的运⾏⼀个容器
[root@linux-test-no~]#dockerrunnginx
创建容器,两步⾛(不常⽤)
[root@linux-test-no~]#dockercreatecentos:latest/bin/bash#这条语句的意思就是通过centos:latest镜像创建容器,并不运⾏
8d9578c38a6f2b8c4bebc4f81c56d3670067a5824b26db1da9444b78237d913d
[root@linux-test-no~]#dockerps-a#这条语句的意思是查看显⽰所有的容器,包括未运⾏的
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
8d9578c38a6fcentos:latest"/bin/bash"3condsagoCreatedwonderful_saha
[root@linux-test-no~]#dockerstart8d9578c38a6f#启动⼀个容器,后⾯跟的是上⾯容器的CONTAINERID
8d9578c38a6f
[root@linux-test-no~]#dockerps-a
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
8d9578c38a6fcentos:latest"/bin/bash"51condsagoExited(0)7condsago
快速启动容器的⽅法
[root@linux-test-no~]#dockerruncentos:latest/usr/bin/sleep20#这条语句的意思是直接通过centos:latest镜像创建容器并运⾏,
并执⾏/usr/bin/sleep20这个命令
现在基本上通过dockerrun来创建运⾏容器,它背后其实包含独⽴的两步,⼀步是dockercreate创建容器,另⼀步是dockerstart启动容器。
通过上⾯的步骤,我们发现dockerstart启动容器后,dockerps去看,容器显⽰⼏秒前退出,并不在运⾏,这是因为,容器内的第⼀个进程必须
⼀直处于运⾏的状态,否则这个容器,就会处于退出状态!
查看正在运⾏的容器(下⾯两条命令都⾏)
[root@linux-test-no~]#dockercontainerls
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
abc685cb2007centos:latest"/usr/bin/sleep50"4condsagoUp3condssleepy_jones
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
abc685cb2007centos:latest"/usr/bin/sleep50"7condsagoUp7condssleepy_jones
查看你容器详细信息/ip
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
988a3716e9bbcentos:latest"/usr/bin/sleep200"4condsagoUp3condskeen_mcnulty
[root@linux-test-no~]#dockercontainerinspect988a3716e9bb#通过这条命令就可以看到容器的详细信息
[
{
"Id":"988a3716e9bbeaa0f23d4fbe43d17c3189641026be5a13a98989a14f0cb457b1",
"Created":"2020-07-27T07:51:22.925441099Z",
"Path":"/usr/bin/sleep",
"Args":[
"200"
.....
查看你所有的容器(包括未运⾏的)
[root@linux-test-no~]#dockerps-a
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
988a3716e9bbcentos:latest"/usr/bin/sleep200"26minutesagoExited(0)22minutesagokeen_mcnulty
abc685cb2007centos:latest"/usr/bin/sleep50"28minutesagoExited(0)27minutesagosleepy_jones
35bdf2a164b0centos:latest"/usr/bin/sleep40"29minutesagoExited(0)28minutesagorecursing_burnell
2eb97fa49c18centos:latest"/usr/bin/sleep20"AboutanhouragoExited(0)Aboutanhouragoawesome_rhodes
71fdafc25dc6centos:latest"/bin/bash"AboutanhouragoExited(0)Aboutanhourago
停⽌容器(下⾯两个命令都⾏)
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
0caba1282af5centos:latest"/usr/bin/sleep200"10condsagoUp9condsromantic_mestorf
[root@linux-test-no~]#dockerstop0caba1282af5
0caba1282af5
或
[root@linux-test-no~]#dockercontainerkill0caba1282af5
2.进⼊容器的⽅法
启动时进去⽅法
[root@linux-test-no~]#dockerrun-itnginx:latest/bin/bash#参数:-it可交互终端
root@7b960ee8d35b:/
#退出/离开容器
同时按住ctrl,p,q三个按键可以做到退出容器但不关闭,dockerps查看有
同时按住ctrl+d退出容器且关闭容器,dockerps查看⽆
启动后进⼊容器的⽅法
启动⼀个docker
[root@linux-test-no~]#dockerrun-itcentos:latest
[root@1961edb8d554/]#ps-ef
UIDPIDPPIDCSTIMETTYTIMECMD
root10009:21pts/000:00:00/bin/bash
root141009:21pts/000:00:00ps-ef
1.使⽤attach命令可以进⼊正在运⾏的容器,使⽤此⽅法类似于vnc,操作会在各个容器界⾯显⽰。如下
进⼊容器⽅法(推荐使⽤)
[root@linux-test-no~]#dockerexec-it7b960ee8d35b/bin/bash#-it参数是创建⼀个新的终端,此⽅法表述在已运⾏的容器id为7b960ee8d35b的容器中开启⼀个交互式的终端
root@7b960ee8d35b:/
#exec⽅法进⼊已运⾏的容器和attach⽅法不⼀样,会单独建⽴⼀个终端,就是不会像attach⼀样,所有操作会在所有使⽤attach进⼊的界⾯同步显⽰。
此⽅法进⼊按ctrl+d键退出,只会退出终端,并不会同步关闭容器,⽽attach通过ctrl+d键退出终端,会同步关闭容器。
3.删除容器
删除单个容器:
[root@linux-node2~]#dockerrmID/名称
加-f强制删除,包括正在运⾏中的容器
删除所有容器:
[root@linux-test-no~]#dockerrm-f`dockerps-a-q`#-f表⽰强制删除容器,包括正在运⾏的容器,要删除单独的容器,把后⾯的红体字部分改成容器id.
4.启动时进⾏端⼝映射
-p参数端⼝映射
[root@linux-test-no~]#dockerrun-d-p88:80nginx:latest#-d参数表⽰后台运⾏容器,并返回容器ID;这条命令的意思表⽰本地的88端⼝映射到容器的80端⼝。
不同指定映射⽅法
-phostPort:containerPort端⼝映射-p8080:80
-pip:hostPort:containerPort
配置监听地址-p10.0.0.100:8080:80(本地指定ip
和指定端⼝映射到容器指定端⼝)
-pip::containerPort
随机分配端⼝-p10.0.0.100::80(本地指定IP和本
地随机端⼝映射到容器指定端⼝)
-phostPort:containerPort:udp
指定协议-p8080:80:tcp(本地指定端⼝和容器指定
端⼝和指定协议,默认为tcp)
-p81:80–p443:443⼀次性映射多个端⼝
随机映射
dockerrun-P(⼤P)#需要镜像⽀持
查看指定容器已映射的端⼝
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
16d0a0cfe9b9nginx:latest"/docker-entrypoint.…"15minutesagoUp15minutes0.0.0.0:88->80/tcpcharming_joliot
[root@linux-test-no~]#dockerport16d0a0cfe9b9
80/tcp->0.0.0.0:88
5.⾃定义容器名称
[root@linux-test-no~]#dockerrun-it--namenginx-testnginx:latest#--name参数为通过镜像启动容器时指定容器名称,nginx-test就是指定的容器名,指定
容器名称的时候要唯⼀,不能起两个同样名称的容器
6.容器⽇志的查看
命令格式:
dockerlogs[选项]容器ID
选项:
--details显⽰提供给⽇志的其他详细信息
-f,--follow跟随⽇志输出
--sincestring显⽰⾃时间戳记以来的⽇志(例如2013-01-02T13:23:37Z)或相对时间(例如42m持续42分钟)
-n,--tailstring从⽇志末尾开始显⽰的⾏数(默认为“all”)
-t,--timestamps显⽰时间戳
--untilstring在时间戳(例如2013-01-02T13:23:37Z)或相对(例如42m持续42分钟)之前显⽰⽇志
⽤法如下:
查看指定时间后的⽇志,只显⽰最后100⾏
dockerlogs-f-t--since="2020-12-17"--tail=100CONTAINER_ID
查看某时间段⽇志:(下⾯的表⽰显⽰2020年2⽉16号之后到2020年2⽉17号之前之间的⽇志,并加上时间戳显⽰)
dockerlogs-t--since="2020-02-16T13:23:37"--until"2020-02-17T12:23:37"CONTAINER_ID
数据卷的管理
Docker数据持久化:
容器在运⾏期间产⽣的数据是不会写在镜像⾥⾯的,重新⽤此镜像启动新的容器就会初始化镜像,会加⼀个全新的读写⼊层来保存数据。如果想做到数据持久化,Docker提供数
据卷(Datavolume)或者数据容器卷来解决问题,另外还可以通过commit提交⼀个新的镜像来保存产⽣的数据。
DockerVolume数据卷可以实现:
->绕过“拷贝写”系统,以达到本地磁盘IO的性能,(⽐如运⾏⼀个容器,在容器中对数据卷修改内容,会直接改变宿主机上的数据卷中的内容,所以是本地磁盘IO的性能,⽽不
是先在容器中写⼀份,最后还要将容器中的修改的内容拷贝出来进⾏同步。)
->绕过“拷贝写”系统,有些⽂件不需要在dockercommit打包进镜像⽂件。
->数据卷可以在容器间共享和重⽤数据
->数据卷可以在宿主和容器间共享数据
->数据卷数据改变是直接修改的
->数据卷是持续性的,直到没有容器使⽤它们。即便是初始的数据卷容器或中间层的数据卷容器删除了,只要还有其他的容器使⽤数据卷,那么⾥⾯的数据都不会丢失。
docker数据卷的使⽤场景
从上⾯的介绍我们看出,docker内部数据持久化,做的不太好,⼀般容器内产⽣的数据会随着容器的的消失⽽消失。所以⼀般我们为了保存⼀些重要数据,会使⽤docker数据
卷。docker数据卷⼀般会在以下场景使⽤。
1.⽇志输出
2.静态web页⾯
3.应⽤配置⽂件
4.多容器间⽬录或⽂件共享
1.挂载本地指定⽬录
#下⾯命令的意思,通过nginx:latest镜像创建容器,并在容器内部创建数据卷/usr/share/nginx/html,此⽬录可存在,可不存在,如不存在则会⾃动创建,然后指定此数据卷挂载宿主
机⽬录/iflytek/data1下⾯。此⽬录也可不存在,届时会⾃动创建。如果不指定宿主机⽬录,则数据卷会默认挂载宿主机的⼀个⽬录。默认在/var/lib/docker/volumes⽬录下数据卷后
⾯跟的ro表⽰,创建的数据卷设为容器内只读,如果不指定,默认是读写。
[root@linux-test-no~]#dockerrun-d-p80:80-v/iflytek/data1:/usr/share/nginx/html:ronginx:latest
容器内站点⽬录:/usr/share/nginx/html
在宿主机写⼊数据,查看
[root@linux-test-no~]#echo"test1">/iflytek/data1/
[root@linux-test-no~]#curl172.31.46.38:80
test1
进⼊容器,尝试往数据卷内写⼊数据
[root@linux-test-no~]#dockerexec-it2984edf77b32/bin/bash
root@2984edf77b32:/#cd/usr/share/nginx/html/
root@2984edf77b32:/usr/share/nginx/html#ls
root@2984edf77b32:/usr/share/nginx/html##这⾥我们看到数据卷和宿主机的⽂件已完成同步。
test1
root@2984edf77b32:/usr/share/nginx/html#echo"test1.1">#我们尝试往数据卷内写⼊数据,发现写不进去,这是因为我们开通创建数据卷的时候设为只读了。
bash::Read-onlyfilesystem
⼀个宿主机⽬录可以同时挂载多个容器内的数据卷,这就启到了共享卷的作⽤。
2.创建数据卷容器,并挂载它
#这⾥我没指定宿主机的⽬录,所以这⾥只会在容器内创建⼀个数据卷,如果你想创建多个数据卷,你只需在命令⾥,再加⼀个-v后⾯跟你要创建的数据卷名称就可以了,这个时候此容器就可以
被理解为数据卷容器,数据卷会默认在宿主机⽣成⼀个⽬录。⼀般即便是删除了初始的数据卷容器,或是删除挂载了初始化数据卷容器的容器,但只要是有容器在使⽤该数据卷,那么它⾥⾯的数
据就不会丢失!(除⾮是没有容器在使⽤它们)
[root@linux-test-no~]#dockerrun-d-p8081:80-v/usr/share/nginx/htmlnginx:latest
360a4cbff9e19ca681372d403ab930a2b6a66a8e6b499ed0176f0b076e3216d5
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
360a4cbff9e1nginx:latest"/docker-entrypoint.…"22condsagoUp22conds0.0.0.0:8081->80/tcpzealous_archimedes
2984edf77b32nginx:latest"/docker-entrypoint.…"36minutesagoUp36minutes0.0.0.0:80->80/tcpsuspicious_rubin
#这⾥inspect表⽰显⽰容器的详细信息,我们会在详细信息⾥看到如下⼀段,其中source后⾯为我们创建数据卷的挂载的宿主机⽬录,Destination后⾯就是数据卷的地址
[root@linux-test-no~]#dockerinspect360a4cbff9e1
。。。。。。
"Mounts":[
{
"Type":"volume",
"Name":"ba4c873f3990856dfa9d64bea2669ca16f9e20879d9214da5a96021b05b2f435",
"Source":"/var/lib/docker/volumes/ba4c873f3990856dfa9d64bea2669ca16f9e20879d9214da5a96021b05b2f435/_data",
"Destination":"/usr/share/nginx/html",
"Driver":"local",
"Mode":"",
"RW":true,
"Propagation":""
}
],
。。。。。。
#⼀般如果没有改数据卷的默认⽬录的话,也可以通过下⾯命令直接查到,这后⾯的360a4cbff9e1是你的容器ID
[root@linux-test-no~]#dockerinspect360a4cbff9e1|grep/var/lib/docker/volumes
"Source":"/var/lib/docker/volumes/ba4c873f3990856dfa9d64bea2669ca16f9e20879d9214da5a96021b05b2f435/_data",
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
360a4cbff9e1nginx:latest"/docker-entrypoint.…"35minutesagoUp35minutes0.0.0.0:8081->80/tcpzealous_archimedes
2984edf77b32nginx:latest"/docker-entrypoint.…"AboutanhouragoUpAboutanhour0.0.0.0:80->80/tcpsuspicious_rubin
#往数据卷内写⼊测试数据,测试其他容器挂载数据卷容器中的数据卷会不会⽣效。
[root@linux-test-no~]#echo"test1.1">/var/lib/docker/volumes/ba4c873f3990856dfa9d64bea2669ca16f9e20879d9214da5a96021b05b2f435/_data/
#下⾯的命令意思是创建容器nginx-test1.1,挂载360a4cbff9e1容器中的数据卷,--volumes-from后⾯填的是你要挂载的数据卷容器的ID或者容器名,--rm参数的作⽤是退出容器后,删除
容器。
[root@linux-test-no~]#dockerrun-it-p8082:80--rm--volumes-from360a4cbff9e1--namenginx-test1.1nginx:latest/bin/bash
#现在我们进到容器⾥来,我们看到360a4cbff9e1容器中的数据卷已被挂载,数据已同步过来了。
root@013f376bef15:/#cat/usr/share/nginx/html/
test1.1
3.创建卷后挂载
Docker新版本中引⼊了dockervolume命令来管理Dockervolume。
使⽤默认的'local'driver创建⼀个volume数据卷
#下⾯的命令就是查看容器上已有的数据卷
[root@linux-test-no~]#dockervolumels
DRIVERVOLUMENAME
#创建数据卷的命令如下,--name后⾯跟你要创建的数据卷名,他会默认在数据卷⽬录下创建⼀个你指定的数据卷名称,就是他会在/var/lib/docker/volumes下⽣成⼀个数据卷test1。
[root@linux-test-no~]#dockervolumecreate--nametest1
test1
[root@linux-test-no~]#dockervolumels
DRIVERVOLUMENAME
localtest1
[root@linux-test-no~]#ls/var/lib/docker/volumes/
1
#下⾯的命令⾥中⽤户挂载了我们刚刚创建的数据卷test1,挂载已有数据卷的操作就是-v跟的是你要挂载的数据卷名称和你容器中的⽬录,这样就挂载成功了。其实如果你-v后⾯的宿主机⽬录
填写的是⼀个不存在的相对路径,⽽不是绝对路径的话,他也会默认的数据卷⽬录下帮你⾃动创建⼀个数据卷并命名为你填写的相对路径,就算此数据卷默认不存在。例如如果-v后⾯跟的是
test3:/volune这⾥test3数据卷不存在,系统会默认帮你在默认的数据卷⽬录下创建⼀个test3数据卷。就是相当于省去了dockervolumecreate步骤。
[root@linux-test-no~]#dockerrun-d-p8081:80--nametest1-vtest1:/volumenginx:latest
3de69cb9aa75a06d4cca8bb0dc78e435514381b0049a8f16301e027c173fe62c
[root@linux-test-no~]#dockerinspecttest1|grep/var/lib/docker/volumes
"Source":"/var/lib/docker/volumes/test1/_data",
#上⾯的命令结果就是test1数据卷对应在本机上的⽬录/var/lib/docker/volumes/test1/_data挂载给容器内的/volume⽬录
4.备份数据卷和恢复
#我们先创建⼀个容器test1,包含⼀个数据卷/test1
[root@linux-test-noiflytek]#dockerrun-it-p8081:80--nametest1-v/test1nginx:latest/bin/bash
root@d0d32aa5ab4b:/#cd/test1
#测试往数据卷⾥写⼊数据
root@d0d32aa5ab4b:/test1#mkdirtest1.1
root@d0d32aa5ab4b:/test1#ls
test1.1
#开始进⾏数据卷的备份操作,启动⼀个新的容器并且从你要备份的数据卷容器中挂载卷,然后挂载当前⽬录到容器中为backup,并备份test1卷中所有的数据为,执⾏完成之后删除
容器--rm,此时备份就在当前的⽬录下,名为。
[root@linux-test-noiflytek]#dockerrun--rm--volumes-fromtest1-v$(pwd):/backupnginx:latesttarcvf/backup//test1
tar:Removingleading`/'frommembernames
/test1/
/test1/test1.1/
[root@linux-test-noiflytek]#ls
data1data2data3data4lost+
#总结就是备份数据卷就是先创建⼀个容器,并挂载要备份的容器数据卷,再挂载数据卷(pwd):/backup⽬录到容器/bakcup,在容器中执⾏要备份的数据⽬录(我这⾥是test1)到/backup,
也就是备份到宿主机$(pwd):/backup⽬录。
#下⾯我们来演⽰怎么恢复数据卷,恢复给另外的容器
[root@linux-test-noiflytek]#dockerps-a
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
d0d32aa5ab4bnginx:latest"/docker-entrypoint.…"2hoursagoExited(0)2hoursagotest1
#我们这⾥把之前的test1容器删除掉
[root@linux-test-noiflytek]#dockerrm-fd0d32aa5ab4b
d0d32aa5ab4b
[root@linux-test-noiflytek]#dockerps-a
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAM
#我们这⾥启动⼀个test2容器,并创建test1数据卷。新容器创建时挂载的数据卷路径最好是和之前备份的数据卷路径⼀致,要不然只能恢复⼀部分数据。列如⽼的数据卷容器⾥有
test1和test2数据卷,然后我们启动新容器创建数据卷的时候,只创建了test1数据卷,那么恢复的时候也只会恢复test1,不会恢复test2。不过这也不是绝对的,这个看你备份
的时候备份哪些卷,⽐如我们在⽼数据卷容器中只备份了test1卷没有备份test2卷,那新容器就算挂载了test2卷,恢复也没数据。
[root@linux-test-noiflytek]#dockerrun-p8081:80-it-v/test1--nametest2nginx:latest/bin/bash
#这⾥我们发现现在新创建的test1数据卷⾥没有任何东西
root@f5164f9124b2:/#ls/test1
root@f5164f9124b2:/
##这个时候我们可以在开⼀个终端来做恢复数据卷的操作,我们需要先cd到存放我们备份⽂件所在的⽬录,然后执⾏恢复操作,如下,下⾯的命令的意思是创建⼀个新的临时容器并挂载我们要恢复数据卷
的数据卷容器test2,这个时候新建的容器中就会有挂载test2中的数据卷test1,然后在把宿主机本地的⽬录挂载临时容器中的backup数据卷,然后在本地⽬录下的备份⽂件也就是backup下⾯的
备份⽂件进⾏解压缩到根下⾯,因为⼀开始我们备份的时候是直接备份/test1的,数据解压缩的话,就会把数据恢复到test1下⾯,正好之前我们挂载了test2中的数据卷test1。所以数据就恢复到test
2数据卷容器中的test1下⾯去了,这就达到了恢复数据卷的效果
[root@linux-test-no~]#cd/iflytek
[root@linux-test-noiflytek]#dockerrun--rm--volumes-fromtest2-v$(pwd):/backupnginx:latesttarxvf/backup/-C/
test1/
test1/test1.1/
#这个时候我们再新创建的数据卷容器test2⾥,查看数据卷有没有恢复,我们发现数据卷已恢复
root@f5164f9124b2:/#ls/test1
test1.1
root@f5164f9124b2:/
##上⾯我们演⽰的是恢复到其他容器,恢复到同⼀个容器其实意思⼀样,我们只需要改--volumes-from后⾯跟的值就可以了。
5.容器数据卷的管理
数据卷是被设计⽤来持久化数据的,它的⽣命周期独⽴于容器,Docker不会在容器被删除后⾃动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引⽤的数据
卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使⽤dockerrm-v这个命令。⽆主的数据卷可能会占据很多空间,要清理会很⿇烦
1.列出所有数据卷
[root@linux-test-noiflytek]#dockervolumels
DRIVERVOLUMENAME
local5fe072dd48041e24d2424e75f6281a4ccd9ca925d62ec74be83326eb6a1918f7
local6c1bc17941dfa0f924f64ec67eac02dd4208625ebfebadf46304403f641d5620
local58b1e859ee8bb76f92de82b9a2f8b72cbbf018777291e741fb47bc66882a5d19
使⽤--filterdangling=true过滤不在使⽤的数据卷
[root@linux-test-noiflytek]#dockervolumels--filterdangling=true
DRIVERVOLUMENAME
local6c1bc17941dfa0f924f64ec67eac02dd4208625ebfebadf46304403f641d5620
local58b1e859ee8bb76f92de82b9a2f8b72cbbf018777291e741fb47bc66882a5d19
2.创建⼀个数据卷,可以设置,--name,--path,--mode。也可以不⽤
拥有⼀切⾃动⽣成的参数
[root@linux-test-noiflytek]#dockervolumecreate
3.删除⼀个数据卷
[root@linux-test-noiflytek]#dockervolumerm后⾯跟VOLUMENAME(卷名)
4.严禁把容器正在使⽤的数据卷和绑定挂载的数据卷删除(删除数据卷的时候可以通过下⾯命令进⾏删除)
dockervolumels--filterdangling=true#这个是过滤掉不在使⽤的数据卷,然后你可以选择性删除
dockervolumels--filterdangling=true|greplocal|awk'{print$2}'|xargsdockervolumerm#这个是删除所有不在使⽤的数据卷
6.容器数据卷的总结
通过上⾯的介绍我们知道什么是数据卷,什么是数据卷容器,但有的同学可能会问,这上⾯的挂载本地指定⽬录,创建数据卷容器并挂载它,还有创建卷后挂载都有什么区别
呢。下⾯我来介绍⼀下
1.挂载本地指定⽬录,顾名思义,就是我们可以指定数据卷的的宿主机挂载⽬录,这⼀般在下⾯场景中会⽤到,就是现在⽣成⼀个⼤⽂件,我发现默认的数据卷⽬录空间不太够
了,所以这个时候我们就需要指定数据卷的宿主机挂载⽬录,这个有⼀个⼩缺点就是宿主机挂载⽬录不唯⼀,以后管理起来可能不太⽅便
2.创建数据卷容器并挂载它,主要起的是保证这个数据卷可以⼀直提供服务,不会造成数据丢失。但是如果⼀个数据卷被创建出来后,如果没有任何容器去使⽤他,那么此数据
卷就会丢失,我所说的丢失不是讲数据真的没了。只是当我们再次创建同名数据卷的时候,并不会找回以前的数据卷⾥的数据,但这个也不是绝对的。其实造成这个现状的原因
是当我们通过-v参数创建数据卷的时候,默认会在默认的数据卷⽬录/var/lib/docker/volumes/创建⼀个⽬录,不过这个⽬录名是不规则的(为了好解释,我们之间取这个⽬录名为
123),在容器内部如果我们创建数据卷的时候没有指定宿主机⽬录,那么容器的内部逻辑就会把你创建的数据卷默认挂这个123。所以只要有容器⽤这个数据卷,数据卷就会⼀
直挂着这个123。但是如果没有容器在使⽤这个数据卷了,那挂载就会⾃动断开。因为这个⽬录名是不规则的,所以我们在数据卷层⾯就会认为此数据卷丢失了,当我们在创建
⼀个同名的数据名时候,内部不会在挂载123,可能去挂载另⼀个456了。但是123⽬录会还在,只要你不删除它。所以我上⾯讲的找不会数据不是绝对的,就是因为你记性好,真
的记住了这个不规则的⽬录名,所以当我们要找回数据的时候,只需指定数据卷挂载宿主机上这个不规则的⽬录名就⾏了。
3.因为有上⾯2的情况,所以我们可以先创建数据卷,然后在进⾏挂载。通俗易懂就是我把上⾯的不规则⽬录名规范化了,就算后⾯数据卷没容器使⽤,数据也能再轻易找回来。
其实上⾯使⽤数据卷的三种⽅法,总结起来就是挂载本地指定⽬录可以防⽌空间不⾜,缺点是挂载不在⼀个⽬录下,管理起来不太⽅便。然后创建卷后挂载是创建数据卷容器并
挂载它的升级版,更推荐使⽤,优点是挂载都在⼀个⽬录下,管理起来更⽅便点。缺点是可能造成空间压⼒。
4.容器数据卷如果我们挂载的时候,宿主机⽬录是空的,然后关联到⼀个容器的⽬录,如果容器内的⽬录下默认是有⽂件的,那么启动的容器,会默认已宿主机⽬录为准,就是
会清空
容器内被关联的⽬录下的⽂件或⽬录。
file⾃动构建docker镜像
DockerFile可以说是⼀种可以被Docker程序解释的脚本,DockerFile是由⼀条
条的命令组成的,每条命令对应linux下⾯的⼀条命令,Docker程序将这些
DockerFile指令再翻译成真正的linux命令,其有⾃⼰的书写⽅式和⽀持的命
令,Docker程序读取DockerFile并根据指令⽣成Docker镜像,相⽐⼿动制作镜
像的⽅式,DockerFile更能直观的展⽰镜像是怎么产⽣的,有了DockerFile,当
后期有额外的需求时,只要在之前的DockerFile添加或者修改响应的命令即可
重新⽣成新的Docke镜像,避免了重复⼿动制作镜像的⿇烦
file指令集
dockerfile主要组成部分:
基础镜像信息FROMcentos:6.8
制作镜像操作指令RUNyuminsatllopenssh-rver-y
容器启动时执⾏指令CMD["/bin/bash"]
dockerfile常⽤指令:
FROM这个镜像的妈妈是谁?(指定基础镜像)
MAINTAINER告诉别⼈,谁负责养它?(指定维护者信息,可以没有)
RUN你想让它⼲啥(在命令前⾯加上RUN即可)
ADD给它点创业资⾦(COPY⽂件,会⾃动解压)
WORKDIR我是cd,今天刚化了妆(设置当前⼯作⽬录)
VOLUME给它⼀个存放⾏李的地⽅(设置卷,挂载主机⽬录)
EXPOSE它要打开的门是啥(指定对外的端⼝)
CMD奔跑吧,兄弟!(指定容器启动后的要⼲的事情)
2.创建⼀个Dockerfile
[root@linux-test-no~]#cd/opt/
#这⾥建议⽬录结构按照业务类型或系统类型等⽅式划分,⽅便后期镜像⽐较多的时候
进⾏分类。不过只是建议,具体你想把dockerfile⽂件放到哪⾥看你⾃⼰
[root@linux-test-noopt]#mkdirdockerfile/{web/{nginx,tomcat,jdk,apache},system/{centos,ubuntu,redhat}}-pv
[root@linux-test-noopt]#cddockerfile/system/centos/
[root@linux-test-nocentos]#pwd
/opt/dockerfile/system/centos
#编写dockerfile,⽣成的镜像的时候会在执⾏命令的当前⽬录查找Dockerfile⽂件,所以名称不可写错,⽽且D必须⼤写
Dockerfile⽂件中第⼀⾏,必须是Fromxxx(xxx是基础镜像),下⾯每⼀⾏指定有什么⽤,上⾯已经讲过了,不⽤再解释了。
[root@linux-test-nocentos]#vimDockerfile
FROMcentos:6.8
RUNyuminstallopenssh-rver-y
RUNecho"root:123456"|chpasswd
RUN/etc/init.d/sshdstart
CMD["/usr/sbin/sshd","-D"]
3.通过dockerfile⽂件构建docker镜像
[root@linux-test-nocentos]#dockerimagels
REPOSITORYTAGIMAGEIDCREATEDSIZE
centos6-sshlatest58486de0367218hoursago342MB
nginxlatest8cf1bfb43ff510daysago132MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
#下⾯命令中,-t为镜像标签打标签并指定镜像名,.表⽰当前路径。因为我们要构建镜像的Dockerfile在本地⽬录下,所以我们⽤了.如果需要的dockerfile⽂件不在本地⽬录下,⽽是
在其他⽬录下,我们则需要把.换成指定的其他⽬录。
[root@linux-test-nocentos]#dockerimagebuild-tcentos6.8-ssh:test1.
#测试发现我们通过Dockerfile⽂件构建的docker镜像成功。
[root@linux-test-no~]#dockerimagels
REPOSITORYTAGIMAGEIDCREATEDSIZE
centos6.8-sshtest1922e349f2760Aboutanhourago342MB
centos6-sshlatest58486de0367219hoursago342MB
nginxlatest8cf1bfb43ff511daysago132MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
#测试使⽤⾃构建的镜像启动成功
[root@linux-test-no~]#dockerrun-d-p2022:22centos6.8-ssh:test1
2e2a32d93d8bd82e88eb83fa93ae1fc2841d46885b25c6027bf5d58b4604dd44
中的镜像分层
Docker⽀持通过扩展现有镜像,创建新的镜像。实际上,DockerHub中99%的镜像都是通过在ba镜像中安装和配置需要的软件构建出来的。
从上图可以看到,新镜像是从ba镜像⼀层⼀层叠加⽣成的。每安装⼀个软件,就在现有镜像的基础上增加⼀层。
镜像为什么分层
镜像分层最⼤的⼀个好处就是共享资源。
⽐如说有多个镜像都从相同的ba镜像构建⽽来,那么DockerHost只需在磁盘上保存⼀份ba镜像;同时内存中也只需加载⼀份ba镜像,就可以为所有容器服务了。⽽
且镜像的每⼀层都可以被共享。
如果多个容器共享⼀份基础镜像,当某个容器修改了基础镜像的内容,⽐如/etc下的⽂件,这时其他容器的/etc是不会被修改的,修改只会被限制在单个容器内。这就是容器
Copy-on-Write特性。
2.可写的容器层
当容器启动时,⼀个新的可写层被加载到镜像的顶部。这⼀层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动-⽆论添加、删除、还是修改⽂件都只会发⽣在容器层中。只有容器层是可写的,容器层下⾯的所有镜像层都是只读的。
3.容器层的细节说明
镜像层数量可能会很多,所有镜像层会联合在⼀起组成⼀个统⼀的⽂件系统。如果不同层中有⼀个相同路径的⽂件,⽐如/a,上层的/a会覆盖下层的/a,也就是说⽤户只能访问
到上层中的⽂件/a。在容器层中,⽤户看到的是⼀个叠加之后的⽂件系统。
⽂件操作的
添加⽂件在容器中创建⽂件时,新⽂件被添加到容器层中。
读取⽂件在容器中读取某个⽂件时,Docker会从上往下依次在各镜像层中查找此⽂件。⼀旦找到,⽴即将其复制到容器层,然后打开并读⼊内存。
修改⽂件在容器中修改已存在的⽂件时,Docker会从上往下依次在各镜像层中查找此⽂件。⼀旦找到,⽴即将其复制到容器层,然后修改之。
删除⽂件
在容器中删除⽂件时,Docker也是从上往下依次在镜像层中查找此⽂件。找到后,会在容器层中记录下此删除操作。(只是记录删除操
作)
只有当需要修改时才复制⼀份数据,这种特性被称作Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本⾝进⾏任何修改。
这样就解释了我们前⾯提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
私有仓库(registry)
DockerRegistry作为Docker的核⼼组件之⼀负责镜像内容的存储与分发,客户
端的dockerpull以及push命令都将直接与registry进⾏交互,最初版本的registry
由Python实现,由于设计初期在安全性,性能以及API的设计上有着诸多的缺陷,
该版本在0.9之后停⽌了开发,由新的项⽬distribution(新的dockerregister被称为
Distribution)来重新设计并开发下⼀代registry,新的项⽬由go语⾔开发,所有的
API,底层存储⽅式,系统架构都进⾏了全⾯的重新设计已解决上⼀代registry中存在
的问题,2016年4⽉份rgistry2.0正式发布,docker1.6版本开始⽀持registry2.0,
⽽⼋⽉份随着docker1.8发布,dockerhub正式启⽤2.1版本registry全⾯替代之
前版本registry,新版registry对镜像存储格式进⾏了重新设计并和旧版不兼容,
docker1.5和之前的版本⽆法读取2.0的镜像,另外,Registry2.4版本之后⽀持了
回收站机制,也就是可以删除镜像了,在2.4版本之前是⽆法⽀持删除镜像的,所以
如果你要使⽤最好是⼤于Registry2.4版本的。
本部分将介绍通过官⽅提供的dockerregistry镜像来简单搭建⼀套本地私有仓库环境。
这种容器私服有点像nexus,就是maven私服,你理解了maven私服就理解了容器私服。
1.创建⼀个普通的仓库
#查看本地镜像中是否有registry镜像,这⾥我们看没有
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
centos6.8-sshtest1922e349f276022hoursago342MB
centos6-sshlatest58486de0367240hoursago342MB
nginxlatest8cf1bfb43ff511daysago132MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
#下载registry镜像。
[root@linux-test-no~]#dockerpullregistry
#基于registry镜像创建容器私有仓库,--restart=always的作⽤是让此容器随着docker服务的重启⽽重启,这样的启动⽅式适合与⼀些监控程序,跟着机器⼀起启动,出问题也会⾃动重启。
在上⾯我们介绍数据卷的时候我们讲过,容器内的数据⼀般会随着容器的消失⽽消失,所以我们为了让私库内的镜像不消失,所以⼀般我们会通过-v参数挂载⼀个宿主机⽬录,/var/lib/registry
⽬录是registry容器内存放镜像的默认⽬录。
[root@linux-test-no~]#dockerrun-d-p5000:5000--restart=always--nameqingbai_registry-v/iflytek/my_registry:/var/lib/registryregistry:latest
7d1aba329d0cdc6a084efd646c8e3e59930e50ba93dca3ec40ed43f31554db24
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
7d1aba329d0cregistry:latest"//etc…"45condsagoUp45conds0.0.0.0:5000->5000/tcpqingbai_registry
4a7a77ebc5e7centos:6.8"/bin/bash"13hoursagoUp12hourstest2
655769f1e67fcentos:6.8"/bin/bash"13hoursagoUp13hours
#Docker从1.3.X之后,与dockerregistry交互默认使⽤的是https,然⽽此处搭建的私有仓库只提供http服务,所以我们需要在docker的客户机(即上传镜像到私有仓库⾥或从私有仓库下载镜像的客户机)
上修改以下配置⽂件。使其⽀持http服务,不然我们后续使⽤的时候会报错。
[root@linux-test-no~]#vim/etc/docker/
{
"registry-mirrors":[""],
"incure-registries":["172.31.46.38:5000"]这⾥是我们要加的⾏,⾥⾯地址填的是私有仓库所在服务器的地址。
}
[root@linux-test-no~]#e
#修改完配置后,重启docker服务,让修改⽣效,我们发现我们上⾯--restart=always参数起了作⽤,registry随着docker服务的重启⽽重启了。
不过这⾥⽤--restart=always参数的时候我们需要注意⼀点,如果我们不想要这个容器了,我们不能通过平常的dockerrm和dockerstop命令,因为通过这些命令删除
不掉容器,还可能出现其他问题,⽽是需要更新restart的状态。例如:dockerupdate--restart=nonginx:latest这样之后,这个容器就不会在⾃动启动了。最
后我们就可以通过传统⽅式删除容器。
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
7d1aba329d0cregistry:latest"//etc…"9minutesagoUp3minutes0.0.0.0:5000->5000/tcpqingbai_registry
测试上传镜像到私库和从私库上拉镜像
#查看本地现有镜像
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
centos6.8-sshtest1922e349f276025hoursago342MB
centos6-sshlatest58486de0367243hoursago342MB
nginxlatest8cf1bfb43ff512daysago132MB
registrylatest2d4f4b5309b16weeksago26.2MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
#开始上传镜像到私有仓库,我们这⾥需要先给要上传的镜像打个tag,格式如下,centos6.8-ssh:test1为我们要上传的镜像名和tag号,172.31.46.38:5000为我们要传的私库地址,
/后⾯是你要在私库⾥把这个镜像重新取的名字和tag号。
[root@linux-test-no~]#dockertagcentos6.8-ssh:test1172.31.46.38:5000/centos6.8-ssh:test1
#我们看到本地多了⼀个镜像172.31.46.38:5000/centos6.8-ssh这个就是我们打的tag
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
172.31.46.38:5000/centos6.8-sshtest1922e349f276025hoursago342MB
centos6.8-sshtest1922e349f276025hoursago342MB
centos6-sshlatest58486de0367243hoursago342MB
nginxlatest8cf1bfb43ff512daysago132MB
registrylatest2d4f4b5309b16weeksago26.2MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
#开始上传镜像,push后⾯跟的就是我们的上⾯打的tag
[root@linux-test-no~]#dockerpush172.31.46.38:5000/centos6.8-ssh
#我们通过下⾯的命令查看私库⾥现在有哪些镜像
[root@linux-test-no~]#curl-XGET172.31.46.38:5000/v2/_catalog
{"repositories":["centos6.8-ssh"]}
#我们这⾥测试把本地刚刚上传的镜像删除,然后再从私库⾥下载回来
[root@linux-test-no~]#dockerimagermcentos6.8-ssh:test1
Untagged:centos6.8-ssh:test1
[root@linux-test-no~]#dockerimagerm172.31.46.38:5000/centos6.8-ssh:test1
#我们这⾥看到刚刚打的tag镜像和源镜像已删除。
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
centos6-sshlatest58486de0367243hoursago342MB
nginxlatest8cf1bfb43ff512daysago132MB
registrylatest2d4f4b5309b16weeksago26.2MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
#我们现在测试从私库⾥拉我们刚刚上传的镜像,
[root@linux-test-no~]#dockerpull172.31.46.38:5000/centos6.8-ssh:test1
#测试成功,我们在本地看到了此镜像。这样,我们就完成了私库的搭建,也就可以在同⼀局域⽹内的其他机器上,从该私有仓库中pull下来该镜像。
[root@linux-test-no~]#dockerimagelist
REPOSITORYTAGIMAGEIDCREATEDSIZE
172.31.46.38:5000/centos6.8-sshtest1922e349f276026hoursago342MB
centos6-sshlatest58486de0367244hoursago342MB
nginxlatest8cf1bfb43ff512daysago132MB
registrylatest2d4f4b5309b16weeksago26.2MB
centoslatest831691599b886weeksago215MB
centos6.882f3b5f3c58f16monthsago195MB
2.带basic(⽤户名和密码)认证的仓库
上⾯我们创建的仓库,有⼀个安全风险,就是任何⼈都可以上传镜像和拉取镜像。所以为了安全,容器仓库会设为basic认证的仓库。那么带basic认证的仓库
怎么配置呢,如下。
1.安装加密⼯具
[root@linux-test-no~]#yuminstallhttpd-tools-y
2.设置认证密码
[root@linux-test-no~]#mkdir/opt/registry-var/auth/-p
[root@linux-test-no~]#htpasswd-Bbnclsn123456>/opt/registry-var/auth/htpasswd
3.启动容器,在启动时传⼊认证参数
#这⾥我们基于上⾯的普通仓库进⾏操作
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
7d1aba329d0cregistry:latest"//etc…"7hoursagoUp7hours0.0.0.0:5000->5000/tcpqingbai_registry
#这⾥我们需要先把容器的restart状态改变,让其不随docker服务的重启⽽重启
[root@linux-test-no~]#dockerupdate--restart=noqingbai_registry
qingbai_registry
#然后我们重启docker进⾏验证,关闭上⾯已在运⾏的仓库。
[root@linux-test-no~]#systemctlrestartdocker
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
#开始启动带basic认证的仓库,下⾯前⼀⾯⼀个-v是把我们在宿主机上创建的认证⽬录和容器内的认证⽬录关联,让仓库运⾏的时候可以读取我们在宿主机上创建的⽤户名和密码并进⾏验证。
下⼀个-v是把仓库容器中的镜像存放⽬录映射到宿主机上,-eREGISTRY_AUTH_HTPASSWD_PATH后⾯填认证⽂件所在的⽬录。因为我上⾯⽤户名和密码放在htpasswd⽂件下,所以我这⾥填写
/auth/htpasswd
[root@linux-test-no~]#dockerrun-d-p5000:5000-v/opt/registry-var/auth/:/auth/-v/iflytek/my_registry:/var/lib/registry-e"REGISTRY_AUTH=htpasswd"-e"REGISTRY_AUTH_HTPASSWD_REALM=RegistryRealm"-eREGISTRY_AUTH_HTP
996e778baea4c6b913397c4a902f520355c4ad6bc62beb1cead22bfb201032cd
4.使⽤验证⽤户测试
#登陆⽤户
[root@linux-test-no~]#dockerlogin172.31.46.38:5000
Urname:clsn
Password:
WARNING!Yourpasswordwillbestoredunencryptedin/root/.docker/.
/engine/reference/commandline/login/#credentials-store
LoginSucceeded
#推送镜像到仓库
[root@linux-test-no~]#dockerpush172.31.46.38:5000/centos:6.8
Thepushreferstorepository[172.31.46.38:5000/centos]
ad337ac82f03:Layeralreadyexists
6.8:digest:sha256:3e472cabf40e9beee56affc1fdce0e897dadc4e6063c00cd16bcbdbd3ba96864size:529
#认证⽂件保存位置
[root@linux-test-no~]#/
{
"auths":{
"172.31.46.38:5000":{
"auth":"Y2xzbjoxMjM0NTY="
}
},
"HttpHeaders":{
"Ur-Agent":"Docker-Client/19.03.12(linux)"
}
}
-compo容器容器编排⼯具编排⼯具
当在宿主机启动较多的容器时候,如果都是⼿动操作会觉得⽐较⿇烦⽽且容器出错,这个时候推荐使⽤docker单机编排⼯具dockercompo,DockerCompo
是docker容器的⼀种编排服务,dockercompo是⼀个管理多个容器的⼯具,⽐如可以解决容器之间的依赖关系,就像启动⼀个web就必须得先把数据库服务
先启动⼀样,dockercompo完全可以替代dockerrun启动容器。
1.安装docker-compo
#下载pip软件
[root@linux-test-no~]#yuminstall-ypython2-pip
#下载docker-compo
[root@linux-test-no~]#pipinstall--upgradepip
[root@linux-test-no~]#pipinstalldocker-compo
#下载docker-compo之前建议开启国内pip下载,另下载过程中可能会报错,报错解决⽅法参考下⾯博客
#查看docker-compo版本
[root@linux-test-no~]#docker-compoversion
/usr/lib64/python2.7/site-packages/cryptography/__init__.py:39:CryptographyDeprecationWarning:tforitisnowdeprecatedincryptography,andwillberemovedinafuturerelea
CryptographyDeprecationWarning,
docker-compoversion1.26.2,buildunknown
docker-pyversion:4.2.2
CPythonversion:2.7.5
OpenSSLversion:OpenSSL1.0.1e-fips11Feb2013
[root@linux-test-no~]#mkdir~/.pip/
[root@linux-test-no~]#cat>~/.pip/<<'EOF'
>[global]
>index-url=/pypi/simple/
>[install]
>trusted-host=
>EOF
2.编排启动容器
1.创建⽂件⽬录
#⽬录可以在任意⽬录,推荐放在有意义的位置。
[root@linux-test-no~]#mkdir/opt/my_wordpress/
[root@linux-test-no~]#cd/opt/my_wordpress/
[root@linux-test-nomy_wordpress]
#2.编写编排⽂件启动单个容器
#编排⽂件是⼀个yml格式的配置⽂件,因此要严格注意前后的缩进和格式,不然通过编排⽂件启动容器的时候会有各种问题。
[root@linux-test-nomy_wordpress]#
web1:#这⾥是填容器的id,每⼀个容器都可以设⾃⼰的id
image:nginx:latest#这⾥写镜像名,表⽰从哪个镜像启动,如果镜像本地不存在,会⾃动去仓库拉
expo:#这⾥表⽰开放什么端⼝,这⾥可写可不写,对应下⾯的prots。
-80
-443
volumes:#这⾥表⽰挂载数据卷
-/iflytek/data1:/usr/share/nginx/html
restart:always
container_name:nginx-web1#这⾥指定启动后的容器名
ports:#这⾥表⽰映射的端⼝号
-"80:80"
-"443:443"
[root@linux-test-nomy_wordpress]#cat/iflytek/data1/#这⾥准备测试web界⾯
test1
[root@linux-test-nomy_wordpress]#docker-compoup#通过docker-compo⽂件前台启动容器,如果想后台的话,只需执⾏docker-compoup-d,还有启动命令⼀定要在
docker-compo⽂件所在⽬录执⾏
#刚刚因为我们是前台启动的,所以我们这⾥重新开⼀个端⼝看容器是否启动和测试web能否访问,结果如下,我们发现docker-compo启动成功
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
1f2919530ecenginx:latest"/docker-entrypoint.…"14minutesagoUp14minutes0.0.0.0:80->80/tcp,0.0.0.0:443->443/tcpnginx-web1
996e778baea4registry"//etc…"28hoursagoUp28hours0.0.0.0:5000->5000/tcpmusing_yalow
[root@linux-test-no~]#curl172.31.46.38:80
test1
3.编写编排⽂件启动多个容器
#编排⽂件如下,通过下⾯我们看出,启动⼏个容器,就写⼏个容器ID名就⾏了,然后在容器ID下⾯写上你⾃⼰启动时要配置的参数
[root@linux-test-nomy_wordpress]#
web1:
image:nginx:latest
expo:
-80
-443
volumes:
-/iflytek/data1:/usr/share/nginx/html
restart:always
container_name:nginx-web1
ports:
-"80:80"
-"443:443"
web2:
image:nginx:latest
expo:
-80
-443
volumes:
-/iflytek/data2:/usr/share/nginx/html
restart:always
container_name:nginx-web2
ports:
-"81:80"
-"4443:443"
[root@linux-test-nomy_wordpress]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
996e778baea4registry"//etc…"28hoursagoUp28hours
[root@linux-test-nomy_wordpress]#cat/iflytek/data2/
2
#通过docker-compo编排⽂件后台启动容器
[root@linux-test-nomy_wordpress]#docker-compoup-d
#测试发现docker-compo编排⽂件启动多容器成功。
[root@linux-test-nomy_wordpress]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
0b55534de6danginx:latest"/docker-entrypoint.…"44condsagoUp42conds0.0.0.0:81->80/tcp,0.0.0.0:4443->443/tcpnginx-web2
187305da8f7anginx:latest"/docker-entrypoint.…"44condsagoUp42conds0.0.0.0:80->80/tcp,0.0.0.0:443->443/tcpnginx-web1
996e778baea4registry"//etc…"29hoursagoUp29hours0.0.0.0:5000->5000/tcpmusing_yalow
[root@linux-test-nomy_wordpress]#curl172.31.46.38:80
test1
[root@linux-test-nomy_wordpress]#curl172.31.46.38:81
2
镜像加速配置
国内下载国外的镜像有时候会很慢,因此可以更改docker配置⽂件添加⼀个加
速器,可以通过加速器达到加速下载镜像的⽬的。
1.获取加速地址
2.⽣成配置⽂件
按阿⾥官⽅的操作⼿册,我们在本地⽣成配置⽂件
[root@gitlab~]#mkdir-p/etc/docker
[root@gitlab~]#sudotee/etc/docker/<<-'EOF'
>{
>"registry-mirrors":[""]
>}
>EOF
{
"registry-mirrors":[""]
}
[root@gitlab~]#cat/etc/docker/
{
"registry-mirrors":[""]
}
3.重启docker服务
[root@gitlab~]#systemctldaemon-reload
[root@gitlab~]#systemctlrestartdocker
⽹络类型
docker的默认逻辑⽹络图如下
Docker服务安装完成之后,默认在每个宿主机会⽣成⼀个名称为docker0的⽹卡
其IP地址都是172.17.0.1/16,并且会⽣成三种不能类型的⽹络,如下
[root@gitlab~]#ifconfigdocker0
docker0:flags=4099
inet172.17.0.1netmask255.255.0.0broadcast172.17.255.255
ether02:42:4b:88:44:4etxqueuelen0(Ethernet)
RXpackets0bytes0(0.0B)
RXerrors0dropped0overruns0frame0
TXpackets0bytes0(0.0B)
TXerrors0dropped0overruns0carrier0collisions0
#这个命令是看容器的⽹络列表的
[root@gitlab~]#dockernetworkls
NETWORKIDNAMEDRIVERSCOPE
17d7ac9c1b55bridgebridgelocal
8391899dee5dhosthostlocal
3aa485e206ffnonenulllocal
的⽹络类型
Bridge默认docker⽹络隔离基于⽹络命名空间,在物理机上创建docker容器时会为每⼀个docker容器分配⽹络命名空间,并且把容器IP桥接到物理机的虚拟⽹桥上。
Network
其实在docker1.9版本之后,新增了⼀个⽹络类型network(⽤户⾃定义⽹络),可以使⽤dockernetworkcreate创建
允许容器使⽤第三⽅的⽹络实现或者创建单独的bridge⽹络,提供⽹络隔离能⼒。这个我们有需要的时候讲解。
这些⽹络模式在相互⽹络通信⽅⾯的对⽐如下所⽰:
南北向通信指容器与宿主机外界的访问机制,东西向流量指同⼀宿主机上,与其他容器相互访问的机制。
2.为容器配置none类型⽹络(就是不为容器配置⽹络功能)
此模式下创建容器是不会为容器配置任何⽹络参数的,如:容器⽹卡、IP、通信路由等,全部需要⾃⼰去配置,所以极少使⽤。
#指定容器启动时⽤的⽹络类型是需在启动参数⾥加上--net=后⾯跟你要指定的类型⽹络。
[root@linux-test-no~]#dockerrun-it--namenet_none--net=nonecentos:latest/bin/bash
#验证,我们刚刚指定了none类型⽹络,我们现在进容器发现容器中并没有配置任何⽹络功能。
[root@b61605f1ceb5/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
3.为容器配置Container类型⽹络(就是与其他容器共享⽹络配置)
逻辑图如下:
此模式和host模式很类似,只是此模式创建容器共享的是其他容器的IP和端⼝⽽不是物理机,此模式容器⾃⾝是不会配置⽹络和端⼝,创建此模式容器进去后,你会发现⾥边
的IP是你所指定的那个容器IP并且端⼝也是共享的,因此这个容器的端⼝不能和被指定容器的端⼝冲突,但是新容器其它还是互相隔离的,如进程等。
#起⼀个测试的被共享⽹络的测试容器。
[root@linux-test-no~]#dockerrun-it-d--nametest1centos:latest/bin/bash
556135e167bd6849ff98ee23e2eab079245a1f0baeb16d76a67cf98970461a38
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
556135e167bdcentos:latest"/bin/bash"8condsagoUp7condstest1
[root@linux-test-no~]#dockerexec-ittest1/bin/bash
#查看⽹络配置
[root@556135e167bd/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
122:eth0@if123:
link/ether02:42:ac:11:00:02brdff:ff:ff:ff:ff:fflink-netnsid0
inet172.17.0.2/16brd172.17.255.255scopeglobaleth0
valid_lftforeverpreferred_lftforever
#起⼀个Container类型⽹络的容器,格式为--net=container:后⾯跟你要指定的容器名或容器ID
[root@linux-test-no~]#dockerrun-it-d--namenet_container--net=container:test1centos:latest/bin/bash
3868b33234b30acdde241abe2fe48990d187813e4239f38cc58e9c79c292b5ff
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
3868b33234b3centos:latest"/bin/bash"3condsagoUp3condsnet_container
556135e167bdcentos:latest"/bin/bash"9minutesagoUp9minutestest1
[root@linux-test-no~]#dockerexec-itnet_container/bin/bash
#查看⽹络配置是否和测试容器⼀样
[root@556135e167bd/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
122:eth0@if123:
link/ether02:42:ac:11:00:02brdff:ff:ff:ff:ff:fflink-netnsid0
inet172.17.0.2/16brd172.17.255.255scopeglobaleth0
valid_lftforeverpreferred_lftforever
类型⽹络(使⽤宿主机⽹络)
⽹络逻辑图如下:
启动的容器如果指定了使⽤host模式,那么新创建的容器不会创建⾃⼰的虚拟
⽹卡,⽽是直接使⽤宿主机的⽹卡和IP地址,因此在容器⾥⾯查看到的IP信息
就是宿主机的信息,访问容器的时候直接使⽤宿主机IP+容器端⼝即可,不过容
器的其他资源们必须⽂件系统、系统进程等还是和宿主机保持隔离。
此模式的⽹络性能最⾼,但是各容器之间端⼝不能相同,适⽤于运⾏容器端⼝⽐
较固定的业务。
这个模式认为是不安全的。
#我们先看⼀下宿主机的⽹络配置
[root@linux-test-no~]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
inet6::1/128scopehost
valid_lftforeverpreferred_lftforever
2:eth0:
link/etherfa:16:3e:75:0f:bebrdff:ff:ff:ff:ff:ff
inet172.31.46.38/24brd172.31.46.255scopeglobaldynamiceth0
valid_lft39286cpreferred_lft39286c
inet6fe80::f816:3eff:fe75:fbe/64scopelink
valid_lftforeverpreferred_lftforever
3:docker0:
link/ether02:42:62:93:42:75brdff:ff:ff:ff:ff:ff
inet172.17.0.1/16brd172.17.255.255scopeglobaldocker0
valid_lftforeverpreferred_lftforever
inet6fe80::42:62ff:fe93:4275/64scopelink
valid_lftforeverpreferred_lftforever
123:veth38cafa1@if122:
link/ether86:33:44:5f:8f:bbbrdff:ff:ff:ff:ff:fflink-netnsid0
inet6fe80::8433:44ff:fe5f:8fbb/64scopelink
valid_lftforeverpreferred_lftforever
#看下宿主机的系统,等下有⽤
[root@linux-test-no~]#cat/etc/redhat-relea
CentOSLinuxrelea7.4.1708(Core)
#启动⼀个host类型⽹络的容器,启动格式为加⼀个启动参数--net=host
[root@linux-test-no~]#dockerrun-it--namenet_host--net=hostcentos:latest/bin/bash
#我们现在已经进容器了,看⽹络配置,和宿主机⼀样。这⾥可能会有⼈问,你这明明在宿主机上,怎么讲进容器了,我们看下⾯
[root@linux-test-no/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
inet6::1/128scopehost
valid_lftforeverpreferred_lftforever
2:eth0:
link/etherfa:16:3e:75:0f:bebrdff:ff:ff:ff:ff:ff
inet172.31.46.38/24brd172.31.46.255scopeglobaldynamiceth0
valid_lft39175cpreferred_lft39175c
inet6fe80::f816:3eff:fe75:fbe/64scopelink
valid_lftforeverpreferred_lftforever
3:docker0:
link/ether02:42:62:93:42:75brdff:ff:ff:ff:ff:ff
inet172.17.0.1/16brd172.17.255.255scopeglobaldocker0
valid_lftforeverpreferred_lftforever
inet6fe80::42:62ff:fe93:4275/64scopelink
valid_lftforeverpreferred_lftforever
123:veth38cafa1@if122:
link/ether86:33:44:5f:8f:bbbrdff:ff:ff:ff:ff:fflink-netnsid0
inet6fe80::8433:44ff:fe5f:8fbb/64scopelink
valid_lftforeverpreferred_lftforever
#看到这⾥我们发现,我们是进到容器⾥了,因为宿主机系统和容器系统不⼀样。出现这样的原因是我们启动的时候指定的是host类型⽹络,这是host类型⽹络的特性
[root@linux-test-no/]#cat/etc/redhat-relea
CentOSLinuxrelea8.2.2004(Core)
13.容器间的互联
容器的连接(linking)系统是除了端⼝映射外,另⼀种跟容器中应⽤交互的⽅式。
该系统会在源和接收容器之间创建⼀个隧道,接收容器可以看到源容器指定的信息。
容器间的互联分为单主机容器上的相互通信,和跨主机的容器相互通信。
1.同宿主机下不同容器间的容器名互联
同宿主机下不同容器间的互联,很多博客采⽤dockerrun--link的⽅式进⾏,下⾯我来进⾏演⽰。
dockerrun--link可以⽤来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以通过容器名或者别名通信,并且接收容器可以获取源容器的⼀
些数据,如源容器的环境变量。
--link的格式:
--link
其中,name和id是源容器的name和id,alias是源容器在link下的别名。
别名的作⽤就是⾃定义的容器名称可能后期会发⽣变化,那么⼀旦名称发⽣变化,程序之间也要随之发⽣变化,⽐如程序通过容器名称进⾏服务调⽤,但是容器名称发⽣变化之
后再使⽤之前的名称肯定是⽆法成功调⽤,每次都进⾏更改的话⼜⽐较⿇烦,因此可以使⽤⾃定义别名的⽅式解决,即容器名称可以随意更改,只要不更改别名即可
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
#创建并启动测试源容器test1
[root@linux-test-no~]#dockerrun-d-it--nametest1centos:6.8
655769f1e67fb292111837508f798beec2d07926e5cb65d0048420a3566e028c
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
655769f1e67fcentos:6.8"/bin/bash"6minutesagoUp6minutestest1
#创建并启动名为test2的接收容器,并把该容器和名为test1,别名设为test1.1的容器链接起来。其中test1是源容器名,test1.1是我们为源容器设的别名。
接收容器可以通过源容器名或者源容器的别名进⾏链接
[root@linux-test-no~]#dockerrun-t-i--nametest2--linktest1:test1.1centos:6.8/bin/bash
#测试发现接收容器可以通过源容器名进⾏链接。
[root@4a7a77ebc5e7/]#pingtest1
PINGtest1.1(172.17.0.2)56(84)bytesofdata.
64bytesfromtest1.1(172.17.0.2):icmp_q=1ttl=64time=0.484ms
64bytesfromtest1.1(172.17.0.2):icmp_q=2ttl=64time=0.060ms
64bytesfromtest1.1(172.17.0.2):icmp_q=3ttl=64time=0.078ms
64bytesfromtest1.1(172.17.0.2):icmp_q=4ttl=64time=0.070ms
^C
---test1.1pingstatistics---
4packetstransmitted,4received,0%packetloss,time3167ms
rttmin/avg/max/mdev=0.060/0.173/0.484/0.179ms
#测试发现接收容器可以通过别名进⾏链接。
[root@4a7a77ebc5e7/]#pingtest1.1
PINGtest1.1(172.17.0.2)56(84)bytesofdata.
64bytesfromtest1.1(172.17.0.2):icmp_q=1ttl=64time=0.087ms
64bytesfromtest1.1(172.17.0.2):icmp_q=2ttl=64time=0.085ms
#查看接收容器的地址为172.17.0.3
[root@4a7a77ebc5e7/]#ifconfig
eth0Linkencap:EthernetHWaddr02:42:AC:11:00:03
inetaddr:172.17.0.3Bcast:172.17.255.255Mask:255.255.0.0
UPBROADCASTRUNNINGMULTICASTMTU:1500Metric:1
RXpackets:8errors:0dropped:0overruns:0frame:0
TXpackets:0errors:0dropped:0overruns:0carrier:0
collisions:0txqueuelen:0
RXbytes:648(648.0b)TXbytes:0(0.0b)
loLinkencap:LocalLoopback
inetaddr:127.0.0.1Mask:255.0.0.0
UPLOOPBACKRUNNINGMTU:65536Metric:1
RXpackets:0errors:0dropped:0overruns:0frame:0
TXpackets:0errors:0dropped:0overruns:0carrier:0
collisions:0txqueuelen:1
RXbytes:0(0.0b)TXbytes:0(0.0b)
[root@linux-test-no~]#dockerexec-ittest1/bin/bash
#查看源容器的为172.17.0.2,由此确认,通过link链接后,接收容器可以通过容器名和别名链接源容器
[root@655769f1e67f/]#ifconfig
eth0Linkencap:EthernetHWaddr02:42:AC:11:00:02
inetaddr:172.17.0.2Bcast:172.17.255.255Mask:255.255.0.0
UPBROADCASTRUNNINGMULTICASTMTU:1500Metric:1
RXpackets:17errors:0dropped:0overruns:0frame:0
TXpackets:8errors:0dropped:0overruns:0carrier:0
collisions:0txqueuelen:0
RXbytes:1362(1.3KiB)TXbytes:672(672.0b)
loLinkencap:LocalLoopback
inetaddr:127.0.0.1Mask:255.0.0.0
UPLOOPBACKRUNNINGMTU:65536Metric:1
RXpackets:0errors:0dropped:0overruns:0frame:0
TXpackets:0errors:0dropped:0overruns:0carrier:0
collisions:0txqueuelen:1
RXbytes:0(0.0b)TXbytes:0(0.0b)
#从下⾯的结果我们看出,源容器⽆法通过容器名去连接接收容器。
[root@655769f1e67f/]#pingtest2
ping:unknownhosttest2
#源容器可以通过地址和接收容器链接
[root@655769f1e67f/]#ping172.17.0.3
PING172.17.0.3(172.17.0.3)56(84)bytesofdata.
64bytesfrom172.17.0.3:icmp_q=1ttl=64time=0.476ms
64bytesfrom172.17.0.3:icmp_q=2ttl=64time=0.054ms
64bytesfrom172.17.0.3:icmp_q=3ttl=64time=0.061ms
从上⾯我们的测试,我们看出在--link标签下,接收容器就是通过设置环境变量和更新接收容器的/etc/hosts⽂件来获取源容器的信息,并与之建⽴通信和传递数据的。
也就是--link其实就是替我们做了⼀个host映射。
但是这个有⼀个弊端,就是通过--link,实现的容器名连接,只是单向的,⽽不是双向的(但是如果你两边都执⾏⼀下--link也能实现双向容器名连接),但是这个⽐较
繁琐,与其说这是--link的弊端,不如讲是默认⽹络类型bridge(docker0)的⼀个弊端。如果我们想实现容器名双向连接,可以参考下⾯的⾃定义⽹络(同宿主机下不同
容器间的互联)
2.⾃定义bridge类型⽹络(同宿主机下不同容器之间容器名互联)
操作如下。
[root@linux-test-no~]#dockernetworkls
NETWORKIDNAMEDRIVERSCOPE
76f868577929bridgebridgelocal
79232f7835dbhosthostlocal
58d353d474cdnonenulllocal
#使⽤下⾯的命令在宿主机上创建名为mynet的bridge⽹络。--drivwe指定创建的⽹络类型为什么,也可以直接写成-d,类似于加了⼀个什么⽹络驱动。
--subnet指定mynet⽹络所在的⽹络,⼀般填写宿主机所在的⽹络,不强制,--gateway指定⽹关,⼀般为宿主机⽹关,不强制。
[root@linux-test-no~]#dockernetworkcreate--driverbridge--subnet192.168.0.0/16--gateway192.168.0.1mynet
f63927707f46915214e4eec31b728703fa20fcacc4fbf3c485e9b0a2a58b87d9
#验证⼀下⽹络是不是创建成功。
[root@linux-test-no~]#dockernetworkls
NETWORKIDNAMEDRIVERSCOPE
76f868577929bridgebridgelocal
79232f7835dbhosthostlocal
f63927707f46mynetbridgelocal
58d353d474cdnonenulllocal
以上我们就解决了,我们上⾯讲的弊端(默认bridge⽹络,⽆法实现同宿主机下容器间通过容器名的互联的问题)。下⾯我们来验证⼀下
#通过--net选项可以指定我们创建的容器使⽤什么⽹络,默认我们不指定的话,这⾥是指定bridge。
[root@linux-test-no~]#dockerrun-d-it--nametest1--netmynetcentos
221e5c7e687cf9e44d8ed48048616bdbb4bc10476195e8c648dce00512fb7b61
[root@linux-test-no~]#dockerrun-d-it--nametest2--netmynetcentos
9c7fbe8951240df4a7f0975c5a19961ed001aea347e417c727078c31e08c1c22
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
9c7fbe895124centos"/bin/bash"10condsagoUp10condstest2
221e5c7e687ccentos"/bin/bash"17condsagoUp16condstest1
[root@linux-test-no~]#dockerexec-ittest1/bin/bash
[root@221e5c7e687c/]#pingtest2
PINGtest2(192.168.0.3)56(84)bytesofdata.
(192.168.0.3):icmp_q=1ttl=64time=0.101ms
(192.168.0.3):icmp_q=2ttl=64time=0.079ms
^C
---test2pingstatistics---
2packetstransmitted,2received,0%packetloss,time1ms
rttmin/avg/max/mdev=0.079/0.090/0.101/0.011ms
[root@221e5c7e687c/]#exit
exit
[root@linux-test-no~]#dockerexec-ittest2/bin/bash
[root@9c7fbe895124/]#pingtest1
PINGtest1(192.168.0.2)56(84)bytesofdata.
(192.168.0.2):icmp_q=1ttl=64time=0.048ms
(192.168.0.2):icmp_q=2ttl=64time=0.085ms
^C
---test1pingstatistics---
2packetstransmitted,2received,0%packetloss,time1000ms
rttmin/avg/max/mdev=0.048/0.066/0.085/0.020ms
跨主机通信之macvlan(⼀般⽤的⽐较少)
macvlan的逻辑图如下
我们这⾥测试在两台宿主机上进⾏容器通过macvlan的链接的测试
#以下操作是在宿主机1上做的
#使⽤下⾯的命令在宿主机上创建名为mcv1的macvlan⽹络。-d指定创建的⽹络类型为什么,类似于加了⼀个什么⽹络驱动。
--subnet指定macvlan⽹络所在的⽹络,⼀般填写宿主机所在的⽹络,--gateway指定⽹关,⼀般为宿主机⽹关。-oparent指定⽤来分配macvlan⽹络的物理⽹卡,因为macvlan创建的时候,
需要关联⼀个物理⽹卡才⾏。
[root@linux-test-no~]#dockernetworkcreate-dmacvlan--subnet172.31.46.0/24--gateway172.31.46.1-oparent=eth0mcv1
ceede34b513e7e1e5376e7f0b4ac90ef591467558a188d1035d8dd1060bc777f
#查看我们的docker⽹络类型,发现⽣成了⼀个macvlan类型⽹络的macv1。
[root@linux-test-no~]#dockernetworkls
NETWORKIDNAMEDRIVERSCOPE
e6d919586ae3bridgebridgelocal
79232f7835dbhosthostlocal
ceede34b513emcv1macvlanlocal
58d353d474cdnonenulllocal
#这⾥我们还需要把我们macvlan关联的物理⽹卡开启混杂模式,不然可能出现ping不通的情况。
混杂模式(PromiscuousMode)是指⼀台机器能够接收所有经过它的数据流,⽽不论其⽬的地址是否是他
[root@linux-test-no~]#iplinkteth0promiscon
#启动⼀个容器c1,通过--network指定使⽤什么类型⽹络,我们这⾥是名为mcv1的macvlan类型⽹络。--ip指定容器内的地址,防⽌容器地址⾃动分配,导致冲突
[root@linux-test-no~]#dockerrun-itd--namec1--ip=172.31.46.101--networkmcv1centos:latest
489ad6f309b9772d7327ccf732f3c3575a43390ac02a0a18ac1be12c76faae77
[root@linux-test-no~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
489ad6f309b9centos:latest"/bin/bash"43condsagoUp42condsc1
3868b33234b3centos:latest"/bin/bash"39hoursagoUp39hoursnet_container
556135e167bdcentos:latest"/bin/bash"39hoursagoUp39hourstest1
#我们进到我们刚刚创建的容器中,查看地址是否是我们指定的
[root@linux-test-no~]#dockerexec-itc1/bin/bash
[root@489ad6f309b9/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
124:eth0@if2:
link/ether02:42:ac:1f:2e:65brdff:ff:ff:ff:ff:fflink-netnsid0
inet172.31.46.101/24brd172.31.46.255scopeglobaleth0
valid_lftforeverpreferred_lftforever
#以下操作是在宿主机2上进⾏的操作
#这⾥执⾏的步骤和宿主机1上⼀样,也是类似起⼀个macvlan的驱动。驱动名要和宿主机1⼀样都指定成mcv1
[root@gitlab~]#dockernetworkcreate-dmacvlan--subnet172.31.46.0/24--gateway172.31.46.1-oparent=eth0mcv1
f0e34200bed181a1a626bc3a214f86111ab87fbb8d5ed37f6948a1685d4190f8
[root@gitlab~]#dockernetworkls
NETWORKIDNAMEDRIVERSCOPE
b25338243647bridgebridgelocal
8391899dee5dhosthostlocal
f0e34200bed1mcv1macvlanlocal
3aa485e206ffnonenulllocal
#然后我们这⾥把我们在宿主机2上配置了macvlan驱动的物理⽹卡设为混杂模式,从上⾯我们看出,我们起macvlan驱动的时候,指定的是eth0⽹卡
[root@gitlab~]#iplinkteth0promiscon
#启动⼀个容器C2,指定我们要使⽤的名为mcv1的macvlan类型⽹络,这⾥因为我们是要和宿主机1下的c1容器通信,所以我们要在⼀个macvlan通道macv1下。
[root@gitlab~]#dockerrun-itd--namec2--ip=172.31.46.102--networkmcv1centos:latest
5659b8fd85155a1d35f423597eb90658c5fe6e6ebab737980894babca336219a
[root@gitlab~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
5659b8fd8515centos:latest"/bin/bash"2condsagoUp1condc2
[root@gitlab~]#dockerexec-itc2/bin/bash
[root@5659b8fd8515/]#ipa
1:lo:
link/loopback00:00:00:00:00:00brd00:00:00:00:00:00
inet127.0.0.1/8scopehostlo
valid_lftforeverpreferred_lftforever
5:eth0@if2:
link/ether02:42:ac:1f:2e:66brdff:ff:ff:ff:ff:fflink-netnsid0
inet172.31.46.102/24brd172.31.46.255scopeglobaleth0
valid_lftforeverpreferred_lftforever
本文发布于:2023-01-03 12:16:26,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/90/84369.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |