docker主机时运行docker容器的核心组件,但docker自身并不是容器,容器是使用内核提供的,docker本身仅是一个容器管理器,docker事实上运行在用户空间的守护进程
一方面守护运行在本机上的各容器状态
二,守护着也能接收客户端发送的请求,随意响应客户端请求,
docker的守护进程还有另外的作用,包括设置网络环境,比如提供docker0网络桥,虽然可以自定义桥的地址,但是桥的使用方式的确是由docker守护进程来完成的
如果运行容器,这些容器就运行在当前主机之上,让当前主机的docker守护进程来负责监控
除此之外还需要有一个空间,来存储本地的,镜像文件
这些镜像文件是只读的,所以可以启动多个容器,不需要把镜像文件复制,只需要为此容器添加一个读写层,因为底层都是只读的
还需要使用DM device mapper来实现联合挂载,ubuntu是unionfs(升级为AUFS高级联合文件系统)
其他的也可以支持分层存储,完成联合挂载,btrfs overlayfs2
这是在本地存储镜像文件的所谓的系统,支持联合挂载的文件系统
好在这种文件系统是基于grub来进行定义的,用docker自行管理,无需做其他多余的设定,一开始按照好,本地的镜像仓库可能是空的,,需要去获取镜像的时候通常可以使用dockerpull到远程的registry主机上,下载和获取,一个下载请求,是由daemon发出的,发出以后,获取,存储到本地,
远程的仓库,也可以不存镜像,支持多种驱动方式,或者多种远程存储的驱动方式,把数据存储在不同的存储设备上,、可以存在本地,也可以存在后端存储驱动(NFS,NOS,swift这种所谓的存储系统)
当用户是由docker search发出请求,这个search请求的操作,会发给index,index在索引当中,获取指定镜像文件的访问路径,而后基于这个路径,把数据取出来,响应给docker daemon,
docker同一个镜像文件,大家都可以用,每一个docker容器修改不是修改镜像文件,而是修改最上层的读写层,
而对这个容器运行来讲,对于容器的自身来讲,最起码,
(关闭容器,容器的只读层依然在仓库中,但读写层就由容器自身做保存,容器停止,还在,只是并不运行而已,可以理解为是容器自己的读写层,以后拿这个读写层和定义联合底层文件系统,照样能启动一模一样的容器)
每一个容器一般从创建开始,到停止结束。类似于此前docker - -RM选项,一停止自动删除,读写层数据就没了
为了让数据持久存储,要使用卷,这样容器仅仅当作一个程序来跑了
为了让数据持久存储,要使用卷,这样容器仅仅当作一个程序来跑了,在docker内每一个容器通常只是跑一个程序的,一个进程和子进程,通常只是用来跑一个程序及其相关的所有进程
容器代表了生命周期,进程不运行,容器随之结束,在容器内部,如果只是为了运行单个进程,即便不是单个进程,容器要想运行,这个进程必须running,这样docker的进程才能捕捉到,一旦运行到后台去,就认为这个进程中止了,就会把对于的容器关掉,删除了,这是比较尴尬的
对于此前经常以守护进程运行的服务来讲,此时都应该运行在前台,这个容器只代表了程序和它代表的所依赖环境的最小化,而后打包运行起来了,不依赖整个操作系统多余的环境了,跟操作系统真正运行的交互式环境没有任何关系
它只是为了自己的运行 而创建,为了自运行而终止,不影响任何外面的内容
只需要在本地留下一些探测点,生成 的数据,这些数据通过卷来保存,而卷通常有两种形式 ,
docker 卷
绑定卷 自己指定主机上的路径,和容器中的卷路径, 而docker管理的卷则只需要指定容器中的卷路径,在主机上的路径一般而言,默认在/var/lib/docker/volumes的目录下,这就是为了持久的,持久的好处,任何卷在启动时容器内部的目录就存在, 而这里面有数据,启动后会立即自动把里面数据复制到卷上来,以确保能持久,
如果容器原来目录的就有数据,就把它指定为卷,显然它的数据要立即反应到主机目录上,所以容器启动会自动把数据复制到主机目录一份,
在容器内做的任何操作都保存在主机目录上,通过主机目录所做的任何操作,也都直接反应到容器中
部署网页站点的时候,不用部署到容器内,仍然部署到主机目录下即可
加入将来更新一个tomcat版本,把这里的镜像所启动的tomcat,在基于这个新镜像启动一个容器,更新里面的目录就是了,或者再启动另外一个卷就是,另外一个容器启动时直接复制这个容器的卷
如果要发布自己对应的内容,就是我们的页面资源的话,可能依然需要我们蓝绿发布,一半一半
或者做灰度
对docker而言是如何实现通信,内部的每一个容器启动起来后,就可以相当于一台虚拟机来使用了,
但docker由于使用名称空间方式,灵活度比此前的kvm多了好几项,
docker的容器模型,有4中
1.封闭式容器,closed container没有网卡,只有本地回环接口 ,不需要网络通信
2.共享主机网络名称空间 open container开放式容器,很危险
3.bridged container 桥接式容器,与kvm桥没什么本质上的不同,nat 桥,仅主机桥,隔离桥。物理桥
4.joined container 联盟式容器 相当于让两个容器共享一个网络名称空间 ,这两个容器的程序可以通过lo接口通信了,可以高效通信,mysql和httpd,虽然把容器隔离成一个个进程了,彼此也能用joined container完成交换通信的,
K8S的时候,大概是两种方式联合起来使用的,
如果创建容器没创建,默认的使用docker0桥,nat桥,能够访问网络中的主机,而访问方式是通过SNAT进行的,
所以内部的容器是无法被外部的主机访问的,除非暴露出去,把对应端口expose暴露出去,所谓暴露,在我们宿主机上做了一个DNAT规则
相当于把宿主机对应的网络接口上的某一端口,映射到容器的指向端口上,而并不一定是对等映射的,端口有可能不一样,而暴露的方式,有可能启动容器时,可以使用-p选项,来定义
-p container port (cport)随机暴露
如果要自己指定容器端口,又要固定在宿主机上,就需要 -p hport(host port 主机端口):cport(容器端口)
要固定ip地址 就需要-p hip::cport 但是端口随机的
端口也不想随机 -p hip:hport:cport
docker用的NAT网络,对真正物理机的网络性能影响还是不可忽视的,以至于K8S就完全没使用这样的通信方式,用overlaynetwork addlaynetwork来完成
如果两个docker主机上的容器需要通信,用NAT很麻烦,第一个容器需要先SNAT出去,第二个容器需要通过DNAT接收进来,
镜像文件创建有几种方式
1.基于容器创建,基于读写层做成镜像,commit就可以制作成镜像
2.基于dockerfile,来自动构建
如果镜像想要分发使用,就需要放到registry上
dockerfile镜像制作指令文件,由docker build命令,基于此镜像的指令,一层一层构建出镜像来,构建出镜像可能有N层组成,你有几个指令,就有多少层,所以尽量减少指令 的数量,这样才能确保层数少,层数越多,将来影响的性能也越大,所以docker容器在性能上还是有一些缺陷的,联合挂载使得真正的删除不是真正的删除,修改不是真修改,
如何制作docker镜像仓库,已完成私有镜像文件分发
先从制作dockerfile开始
能够通过dockerfile读取的指令自动去build构建出一个镜像,dockerfile无非是一个文本 文件,里面所包含的也无非就是,dockerfile文件如何构建镜像文件的指令,
写好dockerfile文件就可以用docker build命令就可以构建镜像了
这个dockerfile的格式也非常简单,只有两类内容
1.#注释符
2.非#开头,都是构建指令,每一个指令都要全大写字符,虽然说并不区分字符大小写,但约定俗成,不同的指令拥有不同的参数,这个是需要精心安排的,这些指令的次序至关重要,因为build命令就是按照次序去构建的,所以正常的业务逻辑要通过命令给出次序来反应第一个指令必须是from,构建镜像不能是凭空构建的,而要基于某个基础镜像来构建的,base image,base image是手工构建的,但是docker hub中,基础镜像来做我们所需要的镜像,
dockerfile第一行必须是from
**如果某一个值多次引用,可以用变量,改的时候一起改,甚至可以启动镜像时传递给镜像,镜像启动完成,变量替换,从而变量能够适用多种场景
环境变量,使用ENV指令,还支持几种bash的修饰符
**
这个变量有值就是有值,没有值就是word,相当于给变量设置默认值
如果这个变量自身有值用word没有值就没有值
基于所谓的dockerfile做一个镜像的时候,通常需要一个所谓的工作目录
.dockerignore 这个文件就是想要忽略的文件,这个文件信息可以使用global通配符
.dockerignore可以放构建上下文的根目录
如果这个文件存在,build命令构建的时候先查这个文件,,从这个文件内容排除当前目录下的指定 文件,把其他文件在打包进去,
FROM 指定出基础镜像
ɝ FROM指令是最重的一个且必须为Dockerfile文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境
ɝ 实践中,基准镜像可以是任何可用镜像文件,默认情况下,
docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件
ɰ 如果找不到指定的镜像文件,docker build会返回一个错误信息
ɝ Syntax
ɰ FROM [:] 不指明标签默认 latest 或
ɰ FROM @容器的id名称就是镜像文件的校验码
l :指定作为base image的名称;
l :base image的标签,为可选项,省略时默认为latest;
容器的id名称就是镜像文件的校验码,后面的镜像没有生成ID号所以是none
让dockerfile作者提供本人详细信息
指明哪个文件打包放置在构建的目标镜像文件中,必须是从build上下文路径中的文件,不能是其父目录中的文件
因为构建命令会把当前目录当作构建时使用的跟,所以是看不见父目录的
ɰ <src必须是build上下文中的路径,不能是其父目录中的文件
ɰ 如果<src是目录,则其内部文件或子目录会被递归复制,但<src目录自身不会被复制
ɰ 如果指定了多个<src,或在<src中使用了通配符,则<dest必须是一个目录,且必须以/结尾
ɰ 如果<dest事先不存在,它将会被自动创建,这包括其父目录路径
在dockerfile制作时并不要求镜像文件要存在
类似docker volume的时候挂载点可以不存在,并不要求原来定的基础镜像中一定要有这个目录,因为会自动创建,
目标目录不存在,无论是最底层的目录或者是对于父目录的每一级都会自动创建,但是如果你的源有多个,目标路径必须以斜线结尾,因为需要知道你是哪个目录的,不以斜线结尾就麻烦了
如果<src是目录,则其内部文件或子目录会被递归复制,但<src目录自身不会被复制,是直接复制里面的文件
如果指定了多个<src,或在<src中使用了通配符,则<dest必须是一个目录,无论存在不存在,都必须以/结尾,不存在会自动创建
如果<dest事先不存在,它将会被自动创建,这包括其父目录路径
举个例子,以运行httpd的bbox为例
把这写网页打包到目标镜像文件,放在?data/html
docker文件后缀名是固定的
一定要加斜线/,不加斜线就创建/data/html这个文件了。html不是目录了
就叫dockerfile,构建docker文件的上下文,就相当于根目录
有三步,每一个指令都会构建一个镜像层的,最终构成的是由三层构成的,所以指令越少越好,每一层 都带一个ID
使用dockerfile build命令完成构建镜像,可以直接加上tag,所属仓库的标签和镜像文件名
如果不给tag,就是none
打一个标签,先写镜像ID 后写标签
这就是如何把一个文件,打包到一个新镜像里
打包也可以用另外的方式,比如有目录,把文件放到当前目录下,现在bbox下又目录而没有这个文件了,打算把data中的内容,制定到dockerfile所定义的目标镜像中
现在需要把data目录复制到,目标系统的data目录中,
因为当你指定的源是一个目录的时候,会把目录中的文件,递归复制到目录下,但不包含目录本身
当你去复制目录的时候,不会复制目录本身,但会复制目录下的文件,注意目录层次结构不要出错
复制文件的指令除了copy还有add
add多出一个功能,支持复制tar文件,支持复制url为文件路径
操作准则
ɰ 同COPY指令
ɰ 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为;如果以/结尾,则文件名URL指定的文件将被直接下载并保存为/(下载下来以后改名)
ɰ 如果是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于“tar -x”命令;然而,通过URL获取到的tar文件将不会自动展开;(tar必须是本地文件系统不能是url)
ɰ 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则其被视作一个普通文件,的内容将被直接写入到;
编辑一个本地url试试
把这个文件下载扔到repos.d
现在就下载成功了
可以直接跑起来看看
这就是如何add文件到docker镜像的
workdir工作目录的,这个并不是你的构建工作目录,而是给build,copy和add设定工作目录的
当某些命令执行之前先cd到目录中去,这个目录应该构建下的子目录在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过,其是相对此前一个WORKDIR指令指定的路径
l 另外,WORKDIR也可调用由ENV指定定义的变量
ɰ 例如
WORKDIR /var/log
l WORKDIR $STATEPATH
镜像启动以后,默认就有一个卷,之前需要用-v指定卷,想要自动拥有一个卷,由docker管理的卷,构建镜像的时候,自动把网页路径关联到主机卷上
VOLUME 直接给挂载路径
或 ɰ VOLUME [""]放在一个中括号中,多个卷用逗号隔开
镜像中指明挂载点,不能确保每个人运行镜像都在这个目录**
把/data.html当做卷的挂载点**
再次构建
run新构建出的镜像
但是这个文件的内容却映射到
是的这样的镜像起来后直接拥有卷,但是这种是docker管理的卷
expose指定要暴露的端口,不指协议,默认是tcp
再去构建一个镜像
根据镜像启动容器
**定义了expose但是并没有暴露出去,可以暴露但是并没有真正做到暴露
**
要想暴露,需要-P选项,代表所有expose指定端口都暴露出来
现在就可以看到暴露端口了,镜像中想要暴露的端口,必须要加-P
expose可以根据自己需要来定义
定义环境变量的
Syntax
ENV <key变量名> 空格 变量值(一次定义一个变量)
或 ɰ ENV = 空格 = …(申明多个变量,)
尽量能一条指令完成的就完成,因为一条指令一层,层次多了,性能就下降
第一种格式中,之后的所有内容均会被视作其的组成部分,因此,一次只能设置一个变量;
ɝ 第二种格式可用一次设置多个变量,每个变量为一个"="的键值对,如果中包含空格,可以以反斜线()进行转义,也可通过对加引号进行标识;另外,反斜线也可用于续行;
ɝ 定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能
定义好env可以在copy和add,以及commit,run等指令中引用
再次重新构建
基于这个镜像跑起来,repo文件也在
run表示运行命令的,cmd也表示执行命令,但是又本质的不同,
如果想要目标镜像安装某个程序包或者添加一个用户账号之类的,
启动之后执行完了
应该构建的时候执行,需要用run
镜像启动以后的命令cmd,指定镜像启动以后,默认执行什么程序
RUN
ɝ 用于指定docker build过程中运行的程序,其可以是任何命令
dockerile中的任何一个指令都会在一个新的image文件层上执行,因此,当需要运行多个命令行,建议将其定义在同一个RUN指令时可以减少image文件的层数
run cmd1 &&cmd2 &&cmd3 第一个成功执行第二个,指定命令的格式有两种
ɝ Syntax
ɰ RUN 或 运行多个命令
ɰ RUN ["", “”, “”] 命令有参数
ɝ 第一种格式中,通常是一个shell命令,且以“/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop 命令停止容器时,此进程接收不到SIGTERM信号;(dockerfile运行的时候不会运行命令本身,而是以bash作为 解释器来运行 ,等于shell的子命令,可以利用shell命令展开,通配符替换。如果不用shell子命令这些功能都不支持)
ɝ 第二种语法格式中的参数是一个JSON格式的数组,其中为要运行的命令,后面的为传递给命令的选项或参数;然而,此种格式指定的命令不会以“/bin/sh -c”来发起(不会以shell的子进程来运行),因此常见的shell操作如变量替换以及通配符(?,* 等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。(可以把executable写成shell)
RUN ["/bin/bash", “-c”, “”, “”] 以这个shell作为父进程来运行此程序
希望镜像运行过程中自动添加用户账号
当构建镜像后,就用这个添加用户
写在不同的行中需要些续航符
基于新的镜像来启动容器