上一篇文章链接:Docker容器技术基础用法(二)
目录
镜像的导入docker load和导出docker save
一、回顾
上篇文章的最后我们讲了docker的容器的状态转换和相关各种常用命令
1.1docker容器的状态
docker容器会处于一下状态中的某一种:
- created:处于创建状态的容器
- running:处于运行状态的容器
- paused:处于暂停状态的容器
- stopped:处于停止状态的容器
- deleted:不能算是一种状态了。因为一个容器已删除,我们就无法查看他的状态了。
1.2docker容器管理时常用的命令
关于docker attach命令:一个docker容器处于stop以后为了能够使得其在启动以后再关联到它的终端上,那么我们使用了docker start命令,那么attach就是如果我们对一个处于运行状态的有终端的容器处于剥离了,那么我们也可以给他重新连上去就用attach
docker attach :连接到正在运行中的容器。
语法
docker attach [OPTIONS] CONTAINER
要attach上去的容器必须正在运行,可以同时连接上同一个container来共享屏幕(与screen命令的attach类似)。
官方文档中说attach后可以通过CTRL-C来detach,但实际上经过我的测试,如果container当前在运行bash,CTRL-C自然是当前行的输入,没有退出;如果container当前正在前台运行进程,如输出nginx的access.log日志,CTRL-C不仅会导致退出容器,而且还stop了。这不是我们想要的,detach的意思按理应该是脱离容器终端,但容器依然运行。好在attach是可以带上--sig-proxy=false来确保CTRL-D或CTRL-C不会关闭容器。
1.3docker的架构形式
如果我们只考虑单机之上所运行的容器的话,那么docker的整体架构形式是
docker容器所能运行的主机叫Docker Host,所谓Docker Host就是运行的docker Daemon守护进程的主机,也成Docker Server端
接收客户端请求是通过http或https协议与客户端交互,客户端可以是远程主机的docker客户端,此前我们用的docker create,docker run等等命令都是客户端命令,而后Docker Daemon接收到创建或启动容器的命令后,将在本地创建或启动容器,注意:在一个docker Host之上可以启动多个容器,所以此处我们可能运行的是分别属于运行各种各样不同程序的容器,而容器的启动是要基于镜像来实现,如果镜像本地没有,那么docker Daemon会自动连到Docker Registry上,(Docker Registry是统称,docker Hub是其最知名的,是默认的Registry)而后从中获取镜像以后,存储在本地,一个能够专门存储住这种所谓的镜像的存储空间中(这个空间要求是特殊而且是专用的文件系统,比如我们安装的docker是1.18的版本那么他使用的是overlay2这种存储驱动),镜像本身是只读的,而且镜像在镜像Registry上放的时候仓库名通常就是应用程序名,而仓库内可以放多个镜像,而且这些镜像通常是属于同一个应用程序的不同版本,我们用标签来识别这些镜像。docker这个单词是码头工人的意思。
以前我们去安装部署应用程序的时候:应该都是散装的。以后我们应用程序在部署的时候是靠镜像,镜像在理解的时候可以是应用程序的集装箱。因此叫docker码头工人,他来负责装卸集装箱的。
所以docker镜像是启动docker容器非常重要的组件。接下来我们就了解一下docker镜像是由什么构建的!
二、About Docker Images
2.1 关于docker镜像的作用及架构剖析
我们之前启动过busybox容器。
可以看到蓝色的完整意义上的根文件系统。容器启动就是靠镜像实现,如果我们创建一个容器后,没有做任何修改,那么我们看到的内容应该是底层镜像所提供的内容,所以镜像含有启动容器所需要的文件系统及其内容。因此主要作用就在于创建并启动docker容器的。
docker镜像采用分层构建机制,这种分层大体分为两部分:最底层为bootfs(引导文件系统)和真正让用户拿来构建用户空间并运行进程的容器的我们成为rootfs。
2.2 docker镜像的层结构剖析
Apache镜像就是运行httpd的镜像,在底层有一个纯净的最小化的Debian,centos或者是suse系统。在上层我们添加了一个编辑器emac相当于vim一样,当然你也可以添加vim,除此之外添加一个httpd。每添加一个软件就是一个独立的层次。上图是三层,最底下的bootfs层在容器启动时,一旦被引导完了就会被卸载并移除的(这里的移除并不是删除文件,而是从内存中移除),因此真正的用户空间运行的只有add Apache,add emacs,Debian这三层,而这三层的层级关系是:最底层的被称为Base Image(通常用来构建一个系统的基本组成,就是上面我们看到的bin,sbin,usr等这样的目录,但是他是最小化的,没有你所依赖的应用程序,如果我们要用到某个额外的应用工具的话,需要在上面执行安装操作)。比如我们要最小化安装一个centos,接下来我们要用vim,那么就执行yum install vim就完了。但是对于我们的镜像来讲,底层的镜像本来就是个最小安装的镜像,他是不会动的,你去额外安装一个vim,他会在这个系统之上新生成一个层次。这个层次可以理解为vim层,只包含vim。如果我们再yum install nginx就又生成一个nginx层。这就是三层。随后我们要启动nginx需要启动,要把这三个层都启动起来,首先先启动最底层,挂载最底层。在最底层的基础之上挂载第二层然后在挂载第三层,他们是叠加在一起挂载的。所以我们称为联合挂载。而后者三层都是只读的。容器启动完成以后,如果某一个进程需要创建一些临时文件之类的,他应该放在tmp目录下对吧?事实上tmp所在的底层 基础镜像是不允许编辑的,如果我们要想进行编辑或者是支持编辑的,只能是最上层就是图中写着writable container的。这一层才是容器自有的,底下那层都是可供多个基于一个镜像启动的容器所共享的层次。
因此对一个容器来讲,他的所有写操作,仅能够在最上面这个writable层次上来实现。而且如果删除了容器,这个writable也会被删除的。docker rm或者docker container rm来删除docker容器,当我们删除容器以后,这个容器自有的可写层一定会被删除的。
2.3 docker的文件系统
docker镜像的这种分层构建和联合挂载依赖于专有文件系统的支撑才能实现,而早起用到的专有文件系统叫做Aufs
- Aufs
因为这个Aufs代码很烂,几万行,这个作者每次写完都去申请加入内核中,但是一直被拒绝,后来一气之下就不在申请了。所以Aufs一直都不是内核中自有的文件系统,想用需要自己去向内核打补丁并且编译使用它, 很遗憾的是centos是红帽发行的,一般不会干这种出格的事,因为他是是以保守稳定著称的。因此在centos系列的系统上,想使用Aufs这种联合挂载文件系统是不可能的。而Ubuntu是略微激进点的,Ubuntu Server是很早一批就把Aufs打包进内核当中去的。随着他的发行版直接提供补丁过的内核。因此早起我们要使用docker没有很好的选择,要使用UbuntuServer。
Aufs的竞争产品叫overlayfs,overlay的中文意思是叠加,从3.18版本已经被合并到Linux内核
但是问题是早期centos不支持Aufs和overlayfs时候怎么办呢?所以除了Aufs外还支持btrfs,devicemapper(简称是dm,是红帽牵头来维护一个内核模块)和vfs文件系统等。
LVM2用的一定是dm,包括他的多路径功能用的都是dm模块。因此docker支持dm,那么红帽就应该在他的系统上推动使用dm来作为解决方案。所以在早些时候centos7使用的就是dm,但是现在我们新版的docker能够启用overlay2。有人做过overlay2和dm的测试,dm在使用联合挂载方面性能很差,因为他使用了叫超重的方式来实现, 而且非常不稳定。
- Devicemapper
dm我们就不详细介绍了。那么我们需要知道有这么个东西就行
同时镜像要实现分层构建联合挂载就需要支持overlay2
overlay2是一种抽象的二级文件系统,他需要建构于本地文件系统之上。后端:xfs。前段:overlay2
2.4 Docker Registry
2.4.1 Registry的概述
Registry如果没有特别的指定,通常就是指docker Hub,如果要指向别的Registry,我们必须在镜像的访问路径当中给明服务器地址,如果没给服务器地址,只是指明了Tag,那么这个镜像一定是Docker Hub上的镜像。
后面我们将介绍一个非常著名的开源的docker Registry是由vmware提供的叫harbor
2.4.2 Registry分类
- Sponsor Registry:捐助者
- Mirror Registry:例如docker cn加速,阿里云的docker加速器,也可以称为镜像Registry
- Vendor Registry:服务商的Registry,比如红帽的Registry,但是并不需开放使用,一般提供给买了红帽操作系统服务的。
- Private Registry:自建Registry,好处是不消耗互联网带宽。而且Docker Hub上的很多镜像我们都用不了。因为要修改一些配置文件,当然不仅仅是为了改配置文件。后面再说吧。
2.4.3 Registry 的组成部分
2.4.4 Docker Registry中的镜像来源
如上图所示,不同的环境中的镜像里面的配置还是不同的。所以我们还有不同配置的同一个镜像,如果配置信息通过制作镜像的方式来做,将会非常头疼
因此现在有一个术语:“云原生”。比如我们要写C程序要针对C环境进行开发。云原生就是面向云环境去运行一个程序,而调云系统本身既有的功能而开发应用程序。他就是为云计算环境而生的。
因此我们把那些单机程序托管到容器云上去运行,会有很多的问题和不便,最大的不便就是改配置文件很麻烦。
那些云原生开发的程序,会使得对云计算这么一个应用程序怎么配置更方便,用那种配置接口提供所谓的配置逻辑。
而容器本来就加了一层外壳,我们操作里面的数据是不方便的。所以后来人们就想:向容器启动时,传环境变量来传信息。而后你的配置文件应该是容器启动时,从环境变量中加载,自动注入到配置文件中,而生成的。这是早起常用的解决方案。也就是说我们通过环境变量来配置容器的启动。
但是nginx表示我们很少能接收到环境变量后自动的将她注入到配置文件中。所以nginx不是云原生。
而云原生都是通过向环境变量获取数据来配置文件的。而不是自己非要修改配置文件参数来配置的。
多数情况下我们做镜像应该基于被人已存在的一个应用的基础镜像来实现的,我们把其成为base image
而这个基础镜像从何而来呢?一般是docker Hub工作人员制作的。
2.4.5 docker Hub
在docker Hub之上如果你登陆后在里面会发现下图所示的几个标签
这几个红色标签分别表示了他们的不同功能:
- Image Repositories:镜像仓库
- Automated Builds:自动构建,dockerfile是构建镜像的文件,后面重点讲
- Webhooks:是自动构建功能的一种特性,特征。和上面是有关联性的
- Organizations:组织。可以创建一个工作组,大家协同工作
- GitHub and Bitbucket Integration:可以于GitHub 和Bitbucket进行整合
2.4.6 Registry的拉取
拉取镜像使用docker pull命令
docker pull <registry>[:<port>]/[<namespace>/]<name>:<tag>
其中registry、port、namespace可省略
registry:是一个仓库的web地址,如果不使用默认的docker hub仓库,需要指明仓库地址
port:是仓库web地址的端口号,默认使用443
namespace:代表是哪个用户的仓库,如果镜像是顶级仓库,namespace就是可省略的,namespace分为三类
name:仓库的名字,和tag一起标识了一个唯一的镜像
tag:镜像的标签,没有就代表是一个镜像的最新版
其中,nginx为顶层仓库,jwilder/nginx-proxy为用户的仓库
例如:
docker pull nginx:1.14 # 没有指仓库地址默认从docker hub上下载nginx1.14版本到本地 docker pull quay.io/coreos/flannel:v0.10.0-amd64 # 从quay上拉取镜
世界上除了docker Hub还有很多可用的仓库服务器,比如著名的quay.io这个站点,这是由QuayOS所维护的第三方的镜像仓库,非常著名!!!!
然后我们搜索flannel
这是非常重要的,将来可能在k8s上要用到的镜像
但是他不是默认的镜像仓库,我们要下载必须要指定这种格式的命令,如下图红色框内所示的命令。
其中的quay.io是服务器地址,端口没指定默认就是443,因为是通过https协议来获取的
coreos指的是:用户名,就是所谓名称空间中的名字,不是顶层仓库
flannel指的是仓库名,如果不加Tag默认就是latest,查看标签请看下图所示。
报错说没找到lastet,所以我们要加上Tag的。
下载好的镜像即带服务器地址又带名称空间还有仓库自己的名称,除非来自docker Hub我们不需要标注,否则默认都被docker识别为docker Hub之上。
2.5 镜像相关的操作
2.5.1镜像制作的两种方式
docker镜像的制作有两种方式:
- 用Dockerfile我们自己使用一个命令叫docker build命令,这是专门做镜像的
- 基于容器来做:比如某个容器我们已经启动并处于运行当中,我们使用docker commit命令,会把容器最上面的可写层单独创建成一个镜像层
- 在Docker Hub上有一个automated builds(其实还是基于dockerfile来实现的
2.5.2 基于容器制作镜像
先启动一个容器并在容器中做好你打算做的修改,比如我们启动一个简单centos,在centos之上使用yum install 安装nginx,而后把安装生成nginx文件的这生成的writable可写层单独做成镜像使用docker commit命令来做就行了。
案例:把busybox加上一个html目录并创建一个index的网页,把这个结果做成镜像
1.先启动busybox容器
2.创建html目录并在这个目录下创建一个网页
加入下面的内容
3.下面我们要保存我们所做的改变,说以不要关闭容器,让容器处于运行状态,因为一关闭下次在启动就没有了。
使用docker commit 命令制作镜像
CONTAINER:基于哪个容器做镜像
[REPOSITORY[:TAG]]:做完这个镜像属于哪个仓库并拥有什么标签,这个是可以省略的,如果省略就表示做出来的镜像是本地的裸镜像不属于任何仓库也没有任何标签
当我们使用docker commit命令的时候 应用程序还在运行还在不断的生成新文件很有可能保存下来得文件是一半的,所以为了避免这种情况,可以使用-p选项让其暂停(做镜像的过程尽可能的让容器暂停下来)
4.查看结果
可以看出他的IMAGE ID跟上上图中的sha256是其开头的部分
给镜像打标签:
5.为了方便管理我们还是给其加上标签
使用docker tag命令,是标签管理命令可以给哪个镜像打上哪个标签,这个标签可以是他所属的仓库名和标签名,如果是引用的镜像,他本身已经有标签了,我们可以在其基础上再打一个标签。一个镜像是可以有多个标签的。如果这个镜像一个标签都没有,那么我们只能使用ID来引用他
假设我们github上有个mageedu,我们期待把它上传到github上的mageedu下的叫做httpd的仓库中,打个标签叫v0.1-1
我们还可以给他在打个标签
其ID实一模一样的
删除一个标签,如果还有其他标签的时候,这个镜像是不会被真删的,只是删除了一个引用
其实我们在做镜像的时候可以同时打标签的。
这次我们重新做个镜像
之前我们讲过镜像定义了基于此镜像启动容器时默认要运行的程序
其中的cmd表示基于此镜像启动容器默认要运行的什么命令
所以上面就表明了为什么要运行shell的原因
我们在看一下nginx的默认命令
表示让nginx运行在前台
我们之前解释过,因为这个容器只运行单个程序,所以这个程序必须要运行在前台,否则一启动就到后台去,那么就认为这个程序就结束了。容器也就自动转为停止状态了。
如果我们期望在创建镜像时,改变原来镜像中的默认运行命令:
下面我们来看一下我们自己做的镜像:
所以说我们改变的东西都还在,但是现在没有改变默认要运行的命令
改变容器启动时候默认要运行的命令:
假如我们不想让容器启动的时候运行shell,而是默认运行httpd
httpd有一个对应的选项 -f 表示运行在前台,-h则指明他的网页文件的根目录,不指定就是当前目录
我们需要修改的只是docker commit的时候额外加一项 -c 意思就是修改原有基础镜像要运行的命令也称Dockerfile指令
我们这里简单一点,只是修改里面的CMD。所以方法很简单,还可以加上作者-a
注意CMD一定要大写的。加上我们要给的指令,我们这里给他一个列表用 [ 括起来的列表
这里我们先看一下httpd在哪
-f 表示运行在前台
-h 则指明他的网页文件的根目录
-p表示制作时先让容器处于暂停状态
而后我们要基于b1来做
做完以后镜像直接取名叫mageedu/httpd:v0.2
下面我们基于v0.2启动一个容器看一下效果:
可以看到没有什么信息,因为它不是交互式接口,因为默认的shell被我们改成httpd了
此时我们换个终端
可以看到地址,然后我们使用curl直接访问这个地址
到这里我们就完成了基于busybox做一个默认就运行很微型的web服务器的镜像就这么完成了!
把制作好的镜像推到docker Hub中:
我们做好了,想共享给别人用我们可以放到我们的仓库上,比如docker Hub,当然要确保有docker Hub的账号
我们要确保我们有一个刚打了标签的叫httpd的仓库,这里我们新建一个仓库
如果是私有,那么只有我们登陆之后自己才能访问,这里我们选择public
到此仓库就创建完成了。我们要把镜像推到这里来
注意:本地的标签一定要跟远程仓库的名称一定要保持一致,才能推上来
推镜像我们使用docker pull命令
我们要指定标签,如果不指定标签也可以使用该命令的-a,把该镜像的所有标签都推上来
但是在推之前有可能让你进行登陆,不登录对着仓库没有访问权限
所以我们使用docker login这个命令
所以我们先登陆docker Hub,如果是别的服务器要指定server
返回Login Succeeded表示登陆成功
下面我们开始推送镜像到docker Hub(注意如果你不是推送到docker Hub,必须!要跟上服务器地址,下面还有个案例是讲阿里云的docker仓库)
这样就推送成功了
下面是笔记的截图部分:
建议国内大家使用阿里云的docker Hub比较快
注意重启docker才会生效
把制作好的镜像推到aliyun Hub中:
之前我们使用的docker CN的加速器,现在我们把阿里云的配置进去
登陆阿里云是一个账号,但是仓库的前缀(即命名空间)是可以自己定义的
上图表示镜像从哪来,来自于我们的本地仓库。
选择其他的话就表示要基于这些代码仓库的dockerfile自动触发自动构建的。
上图不是我们的操作步骤
然后我们点击“管理”
我们要pull的时候选择下图的标签前缀来使用:
注意我们的阿里云账号的密码不是我们使用aliyun镜像仓库的密码,需要独立设置的。这里不多说
现在我们开始推送镜像到aliyun,注意标签前缀要跟对方提供给我们的仓库名称和对应的仓库访问路径一致才行
到这里就推送ok了。出现下图即为成功!!!
可以扫面一下看看有没有问题,因为前一段时间有人想docke Hub上传了很多镜像,里面有挖矿代码,所以很多人使用它们的镜像后被人拿去挖矿了。所以不要随意下不熟悉用户上传的镜像,要使用官方的。
镜像的导入docker load和导出docker save
假设我们有两个服务器node01和node02
假设02想用刚才自己做的镜像怎么办?
第一我们把这个镜像推到一个公共的位置,第二node02使用docker pull拖下来就能用了
但是这样还是很麻烦,假设我们是测试用的。我在01上做完,我想在02上跑一下
这里还有个更简单的办法,我们可以在已有镜像组件的基础上把镜像打包,打包成一个压缩文件,到另外一个主机上再把它装了就行了。
docker save命令叫保存打包文件
docker load命令 可以从打包文件给他直接装入
而且一次打包出来可以打包多个文件
下面开始打包
-o 表示输出到何处
在当前路径下就有刚打包好的文件了。咱们可以通过SCP或者其他的文件共享方式到node02主机上就ok了
因为没有docker所以我们按照下图快速安装一个docker
这时候我们去node02,快速安装一下docker
先在node02上建好docker配置文件目录
再把配置文件发给node02
然后我们在node02执行下面命令启动docker服务
下面我们使用docker load命令
-i 指明从哪个文件进行load
这是属于镜像分发的另外一种方式:直接使用save打包。用文件共享的方式共享过去,让别人执行load。
但是这种方式有个缺陷,当我们使用docker run的时候,如果没有镜像还是要到服务器上去进行下载,因此像这种方式我们必须要先准备好本地镜像
否则他在使用docker pull的时候,一定还是到镜像应该本身所属的位置去执行pull操作的。
下一篇文章链接:docker容器技术之虚拟化网络概述(四)