Linux-Dockerfile
Dockerfile介绍
Dockerfile 是一个文本文件
包含了一条条的指令
每一条指令构建一层,基于基础镜像,最终构建出一个新的镜像
对于开发人员:可以为开发团队提供一个完全一致的开发环境
对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了
对于运维人员:在部署时,可以实现应用的无缝移植
镜像的定制实际上就是定制每一层所添加的配置、文件。我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,这个脚本就是Dockerfile。
Dockerfile是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
各位想必应该记得,我们此前如果安装一个nginx的话,安装完以后,我们说过很多次了,通常不会运行在默认配置下,那因此,我们通常需要去改一改它的配置文件或者定义模块化配置文件,然后启动服务。那为什么,nginx的默认配置不符合我们的需要呢?很显然,不同的生产场景所需要用到的配置参数各个相同,因此,对方只能用一个默认的,认为适用于大多数普遍场景情形的或者适用于较小主机资源情况下的那么一个设定来启动服务,同样的逻辑,各位试想一下,如果我们从docker hub下拖下来一个nginx的镜像,去启动nginx容器的时候,请问这个镜像内的nginx容器内的配置文件一定就会符合我们的需要吗?应该说叫一定不会,基本上几乎不能完全适合我们的需要,此时我们就必须去要修改配置,那怎么改呢?
之前做法:启动容器docker 然后exec连进容器,在内部执行vi,修改后 再reload重启 docker
另外一种方式:假设我们把它对应的那个配置文件的路径做存储卷,从我们宿主机上加载文件,在宿主机上进行编辑,也能让它立即生效,启动容器之前先把它编辑好(容器启动之前,我们事先找一目录把配置文件准备好,然后启动容器时,把容器内的应用程序默认加载配置文件的路径与宿主机上的目录进行建立关联关系,然后去启动容器,也能加载到在宿主机上定制的配置文件)
缺点:我们在宿主上做的编辑,能不能让它立即生效呢?比如我们启动以后发现,有些参数还是需要改,改完以后依然需要重载才能生效。
还有一种方式:自制镜像
基于容器:先启动起来,交互式连入进来,做修改,改完以后,我们把容器保存在一个新镜像中,而后,我们再去创建容器时,根据我们自己所创建的镜像来使用。
缺点:做的镜像也是直接把文件直接备进镜像中的,直接写死在镜像中的就是,如果我们想在改,还是改不了,运行过程当中去修改配置的需求可能对于做运维来讲,变更不就是日常操作吗?很多时候,也有可能需要随时进行修改。那依然解决不了问题。而且这种进镜像的设计方式,最悲惨的地方在于:一次更新,维护复杂 。环境简单可以使用。
基于dockerfile
dockerfile,相当于是一个文档,客户可以基于dockerfile生成新的容器
dockerfile仅仅是用来制作镜像的源码文件,是构建容器过程中的指令,docker能够读取dockerfile的指定进行自动构建容器,基于dockerfile制作镜像,每一个指令都会创建一个镜像层,即镜像都是多层叠加而成,因此,层越多,效率越低,创建镜像,层越少越好。因此能在一个指令完成的动作尽量通过一个指令定义。
docker镜像制作的工作逻辑
首先需要有一个制作镜像的目录,该目录下有个文件,名称为Dockerfile(名称随意),Dockerfile文件内规则有指定的格式,#号开头为注释,,指定默认用大写字母来表示,以区分指令和参数,docker build读取Dockerfile是按顺序依次Dockerfile里的配置,且第一条非注释指令必须是FROM 开头,表示基于哪个基础镜像来构建新镜像。可以根据已存在的任意镜像来制作新镜像。
Dockerfile语法
#号开头为注释
Dockerfile 有以常用指令选项 还有些指令这就不介绍了 用的不多 而且我们如果自己写Dockerfile了解下面就够了 没必要搞得那么复杂
-
FROM
-
MAINTAINER
-
ENV
-
VOLUME
-
EXPOSE
-
COPY
-
ADD
-
WORKDIR
-
RUN
-
CMD
我们下面就对上面的指令一一讲解 先不要急 着创建Dockerfile文件 先把语法弄明白后在说
一定要注意执行Dockerfile 的时候是从上到下执行的 也就是 说上一条命令可以影响到下一条命令
FROM(必须)
FROM指令是最重要的一个且必须为 Dockerfile文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境 .
实践中,基准镜像可以是任何可用镜像文件,默认情况下, docker build会在 本地docker上查找指定的镜像文件,在其不存在时,则会从 Docker Hub Registry上拉取所需的镜像文件 如果找不到指定的镜像文件, docker build会返回一个错误信息
FROM 语法
FROM repository
FROM repository:tag
repository:指定作为base image的名称
tag image的标签(版本号),为可选项,省略时默认为 latest(最新版本);
MAINTAINER(必须)
MAINTAINER=作者 也就是 谁开发的
一般把MAINTAINER放在FROM后面
语法:
MAINTAINER HuAnmin 可是任何文本信息,但约定俗成地使用作者名称或邮件地址
ENV(可选)
ENV用于为镜像定义所需的环境变量,并可被 Dockerfile文件中位于其后的其它指令(如 ENV、ADD、COPY等)所调用 ,即先定义后调用 (在简单来说就是 java中的成员变量 可以在方法中被调用)
语法:
ENV key value
这格式中, key之后的所有内容均会被视作其 value的组成部分,因此一次只能设置一个变量
ENV key=value
这格式中 可用一次设置多个变量,每个变量为一个“key=value的键值对,如果value包含空格,可以以反斜线(\)进行转义,也可通过对value加引号进行标识另外反斜线也可以用于续行 定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能
调用格式为 k e y 或 key 或 key或{key}
列:
ENV DOC_ROOt=/data/web/html/
换行写法 使用 \
ENV DOC_ROOt=/data/web/html/ \
WEB=“nginx-1.15.5”
调用
COPY index.html ${DOC_ROOT}
VOLUME(可选)
器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中
Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。这领我们想起docker的run命令中的 "-v"参数的作用
那么Dockerfile中的VOLUME指令实际使用中是不是就是跟docker run中的-v参数一样是将宿主机的一个目录绑定到容器中的目录以达到共享目录的作用呢?
并不然,其实VOLUME指令只是起到了声明了容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能。
但是当我们生成镜像的Dockerfile中以Volume声明了匿名卷,并且我们以这个镜像run了一个容器的时候,docker会在安装目录下的指定目录下面生成一个目录来绑定容器的匿名卷(这个指定目录不同版本的docker会有所不同)
比如我的 默认绑定容器的匿名卷的目录是: /var/lib/docker/volumes
那么当Dockerfile中声明了匿名卷,但是run的时候没有使用-v
绑定匿名卷的话,那么docker就会在/var/lib/docker/volumes这个目录下创建一个目录来绑定匿名卷。
语法
VOLUME mountpoint
列
VOLUME /data
在运行容器的时候可以使用-v 将匿名卷覆盖掉
比如
docker run -id --name=centos_test -v ~/mydata:/data centos:7
将 ~/mydata 挂载到容器/data 目录 代了Dockerfile中定义的匿名卷的挂载配置。
如果不使用-v 那么就会在/var/lib/docker/volumes这个目录下创建一个目录来绑定匿名卷。
使用 docker inspect 容器名称
在内容中找到 "Mounts"节点 下的Source 查看匿名卷的目录位置 Destination是绑定容器的那个目录
EXPOSE(可选)
EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应该就会开启这个端口的服务。
在Dockerfile中写入这样的声明有两个好处:
是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;在运行是使随机端口映射时,也就是docker run -P(大写)时,会自动随机映射EXPOSE端口。
语法
EXPOSE 80/tcp
也可以指定多个 会自动随机选
EXPOSE 80/tcp 81/tcp 82/tcp
列
docker run -id --name=tinyweb1 -P xxxx
使用docker port 容器名称参绑定情况
COPY(可选)
COPY=用于从 Docker主机复制文件至创建的新映像文件
语法
COPY src dest
src:要复制的源文件或目录,支持使用通配符
dest:目标路径,即正在创建的 image的文件系统路径;建议 dest使用绝对路径,dest绝对路径为镜像中的路径,而不是宿主机的路径。否则, COPY以 WORKDIR(终端进入的落脚点)为其起始路径
注意:在路径中有空白字符时,通常使用第二种格式
文件复制准则
src 必须是build上下文中的路径,即只能放在workshop这个工作目录下,不能是其父目录中的文件
如果src是目录,其内部文件或者子目录会被递归复制,但src目录自身不会被复制
如果指定了多个src,或在src中使用了通配符,则dest必须是一个目录,且dest目录必须以/结尾
如果dest事先不存在,它将会被自动创建,这包括其父目录路径
例子
COPY index.html /data/web/html/
把当前本机目录下的 index.html 拷贝到镜像目录下的 /data/web/html/里 目录不存在自动创建
ADD(可选)
ADD指令类似于 COPY指令, ADD支持使用 tar压缩包和 URL(网络)路径
语法
ADD src dest
src (源路径) dest(目标路径) 更多细节参考上面的COPY
ADD指令和COPY的格式和性质基本一致。但是在COPY基础上增加了一些功能。比如<源路径>可以是一个URL,这种情况下,Docker引擎会试图去下载这个链接的文件放到<目标路径>去。
在Docker官方的Dockerfile最佳实践文档中要求,尽可能的使用COPY,因此COPY的语义很明确,就是复制文件而已,而ADD则包含了更复杂的功能,其行为也不一定很清晰。最适合使用ADD的场合,就是所提及的需要自动解压缩的场合。
因此在COPY和ADD指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY指令,仅在需要自动解压缩的场合使用ADD。
列:
ADD nginx-1.15.5.tar.gz /usr/local/src/
WORKDIR(可选)
使用WORKDIR指令可以来指定工作目录(或者称为当前目录),以后的(RUN、CMD、ENTRYPOINT、COPY和 ADD…) 这些命令就会在该目录下操作
在Dockerfile文件中, WORKDIR指令可出现多次,其路径也可以为相对路径,不过,其是相对此前一个 WORKDIR指令指定的路径
另外, WORKDIR也可调用由 ENV指定定义的变量 .例如
ENV STATEPATH=/data/web/html/
WORKDIR $STATEPATH
语法
WORKDIR dirpath
列:
指定workdir为/usr/local,相当于是容器启动后,会把工作目录切换到/usr/local这个workdir路径下,而不是默认的根目录
相对路径 ./xxx
绝对路径 /xx/xx
WORKDIR /usr/local
RUN (可选)
RUN用于指定 docker build过程中运行的程序,其可以是任何命令,但是这里有个限定,一般为基础镜像可以运行的命令,如基础镜像为centos,安装软件命令为yum 而不是ubuntu里的apt-get命令
RUN和CMD都可以改变容器运行的命令程序,但是运行的时间节点有区别使用次数也有区别,RUN表示在docker build运行的命令 不限制使用次数,而CMD是将镜像启动为容器运行的命令。因为一个容器正常只用来运行一个程序,因此CMD指定在 Dockerfile 中只能使用一次 具体的会在CMD里详细介绍
语法
RUN command
command 通常是一个shell命令 刚拉取下来的镜像可能很多 工具都没有也就代表很多命令无法使用需要你下载 所以你悠着点
列:
&& 执行多个命令(从左到右 如果左边有命令失败那么后面的命令将不会执行) \ 是 换行 (避免一大堆命令都堆积在一行 )
RUN cd /usr/local/src &&
tar xf ${WEB_SERVER_PACKAGE}
也可以这样 向文件里写内容
RUN echo’<h1>Hello,Docker~</h1>’ > /usr/share/nginx/html/index.html
反正拉取下来的镜像 只要是能使用的shell 命令 都能在RUN中 使用
这里建议 尽量能一次RUN 完 别2次 因为每一个指令都会创建一个镜像层 层越多,效率越低
CMD (可选)
类似于 RUN指令, CMD指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同 . RUN指令运行于映像文件构建过程中,而 CMD指令运行于基于 Dockerfile构建出的新映像文件启动一个容器时 CMD指令的首要目的在于为启动的容器指定默认要运行的程序, CMD指定的命令其可以被 docker run的命令行选项所覆盖
在Dockerfile中只能使用一次CMD 如果配置了多个,那么就只有最后一个会有效
语法
CMD command
列:
CMD java–jar app.jar
在启动容器的时候自动启动 java–jar app.jar 条命令
CMD service nginx start
在启动容器的时候自动启动 nginx服务
Dockerfile案例
在你将Dockerfile 语法都看明白了后我们来做几个案例 将上面的命令 大概演示一遍 没有演示到的命令 看语法只要不是傻子应该都懂
写的很明白了 一般FROM 在一行 CMD在最后 其他的指令你随意 看你自己要干嘛了
然我们在来说说 怎么运行Dockerfile文件的
语法: docker build xxx .
**注意: ** 结尾 有一个点 这个是当前目录为Docker引擎的上下文路径 意思就是在文件内 通过 COPY
指令、ADD
指令等 路径是按照上下文来找的而不是按照本地路径来找的 这也是初学者经常会问的为什么? COPY /xxxx
无法工作的原因
因为这些路径已经超出了上下文的范围,Docker引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去 也就是你创建Dockerfile文件的目录里 必须 在Dockerfile文件目录里在 运行docker buildxx
只有这样你Dockerfile内的路径才不会错误
常用参数:
-
-f :指定要使用的Dockerfile路径
-
-t : 镜像的名字和标签(版本) 通常 name:tag 或者 name 格式 (默认标签为latest)
至于其他的指令 都不重要
第一个案例:
需求: 自定义centos7镜像 使用exec进入容器命令行 默认路径为 /usr 并且可以使用vim
-
在一个空目录下创建一个文件为centos-dockerfile
mkdir -p ~/dockerfile vi ~/dockerfile/centos-dockerfile
centos-dockerfile内容 如果要复制的话 先 按下i 进入编译模式在复制 否则有可能复制不全
FROM centos:7 MAINTAINER HuAnmin RUN yum install -y vim WORKDIR /usr
解析:
- 拉取centos:7镜像
- 作者
- 下载vim
- 将操作路径改为/usr
-
运行Dockerfile文件
cd ~/dockerfile/centos-dockerfile docker build -f ~/dockerfile/centos-dockerfile -t centos:1.1 .
一定要注意最后的 . 别忘了 否则创建失败
-
查询是否生成镜像
docker images
可以看到我们的Centos:1.1 比从官网上拉取下来的centos:7要大这是为什?
因为我们在上面Dockerfile文件里写的要安装了 vim啊
-
创建容器
docker run -id --name=centos-test centos:1.1 docker ps
-
测试
先看看进入容器后 路径是不是在/user
docker exec -it centos-test /bin/bash
然后看看 vim 可以使用吗
vim
这个案例下来 估计你稍微有点感觉了 但是还是有点迷糊 不要紧我们在来
第二个案例
需求: 定义dockerfile,发布springboot项目
都知道springboot里自带Tomcat 插件 也就是说打包成 是jar 而不是war 既然是jar 那么我们需要JDK来运行
这里不用你写Springboot了 我这里提供一个最基础 测试项目 访问路径 /Hell
链接:https://pan.baidu.com/s/1c3FXPfcu6UEYnfhN7k4PDg
提取码:1234
现在本地cmd里 进入到 jar文件所在的目录 然后 输入jara -jar 文件名.jar
然后我们在游览器上访问 http://localhost:8080/Hello 记住不能是IE 现在IE 都不更新了 太多BUG了 使用火狐或者谷歌 …
出现这个效果那么 就代表没什么问题 然后我们到Centos7里 利用Docker 容器将Springboot项目启动
-
创建一个空目录 将springBoot-docker.jar放入到这个目录下 你可以利用宝塔终端将文件放入或者其他的远程工具
mkdir -p ~/dockerfile
-
在一个空目录下 创建一个java-dockerfile文件
vi java-dockerfile
java-dockerfile内容
FROM java:8 MAINTAINER HuAnmin COPY ./springBoot-docker.jar /spring/springBoot-docker.jar WORKDIR /spring CMD java -jar springBoot-docker.jar
解析:
- 拉取java:8镜像
- 作者
- 将上下文中 ./springBoot-docker.jar 复制到容器 /spring/下
- 将操作路径改为/spring
- 在/spring路径下 启动 java -jar命令运行 springBoot-docker.jar文件
-
运行Dockerfile文件
cd ~/dockerfile docker build -f ~/dockerfile/java-dockerfile -t java:1.1 .
一定要注意最后的 . 别忘了 否则创建失败
-
创建容器
docker run -id --name=springBoot-test -p9000:8080 java:1.1 docker ps
-
测试
http://192.168.93.137:9000/Hello 把 192.168.93.137换成你的ip