【Docker学习笔记-2】Docker File
Docker File 简介
官方仓库: https://hub.docker.com
官方文档: https://docs.docker.com/engine/reference/builder/#from
Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。
它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile从FROM命令开始,紧接着跟随者各种方法,命令和参数。
其产出为一个新的可以用于创建容器的镜像。
构建命令:
语法: docker build -t image-name-tag context-dir
选项:
-t 指定构建完成后的镜像名称
-f 指定dokerfile的路径, 如果省略此选项, docker会在context-dir目录中查找名称为"Dockerfile"的文件进行构建
Dockerfile指令
FROM: 指定基础镜像 *必备指令
语法: FROM <image name>
所有Dockerfile都必须以FROM命令开始。 FROM命令会指定镜像基于哪个基础镜像创建。
在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如nginx 、 redis 、 mongo 、mysql 等;也
有一些方便开发、构建、运行各种语言应用的镜像,如 node 、 openjdk 、 python 等。
可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制
MAINTAINER: 设置该镜像的作者。
语法: MAINTAINER
RUN: 在shell或者exec的环境下执行的命令。
语法:
RUN command arg1 arg2 ... #由shell启动,类似于直接在命令行输入命令. Linux默认为 /bin/sh -c
RUN ["命令","arg1","arg2"] #类似于函数调用的形式
由于命令行的强大能力, RUN 指令在定制镜像时是最常用的指令之一
RUN命令用于创建镜像(在之前commit的层之上形成新的层)
每一个 RUN 的行为都会新建立一层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像
为了不让我们的镜像产生许多不必要的层,建议多条命令用命令连接符连接起来,而不是写多个RUN指令.
另外,每一层只保留真正需要的东西,类似于安装包,暂存目录等不必要的东西都建议清理掉.
很多人初学 Dockerfile 制作出了很臃肿的镜像的原因之,就是忘记了每一层构建的最后一定要清理掉无关文件或是写了多个RUN指令
COPY: 复制文件
语法:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
COPY 指令将从构建上下文目录中 <源路径> 的文件或目录复制到新的一层的镜像内的 <目标路径> 位置。
<源路径> 可以是多个,甚至可以是通配符
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。
目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录
使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。
ADD: 复制文件指令。它有两个参数和。
语法: ADD <src> <dest>
<src>必须是相对于源文件夹的一个文件或目录,也可以是一个远程的url,<dest>是目标容器中的绝对路径。
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
如果<src>是一个远程url,下载后的文件权限自动设置为 600 ,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用
如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip , bzip2 以及 xz 的情况下, ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去.这也是使用ADD最多的场景.
PS:
需要注意的是, ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
CMD: 容器启动时调用的命令
语法:
CMD command arg1 arg2 #执行shell内部命令 sh -c
CMD ["executable","arg1","arg2"] #执行可执行文件,优先
CMD ["arg1","arg2"] #设置了ENTRYPOINT,则直接调用
ENTRYPOINT添加参数
容器实际上是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。 CMD 指令就是用于指定默认的容器主进程的启动命令的。
在运行时可以指定新的命令来替代镜像设置中的这个默认命令,如centos镜像默认CMD是/bin/bash,我们可以在启动时使用其他命令来替换它.
CMD命令可被docker run中的命令覆盖,如果有多个CMD命令,则最后一条生效
对于容器而言,其启动程序就是容器应用进程. 容器仅为前台主进程而存在,前台主进程退出,容器就失去了存在的意义,从而退出
例: 对于构建nginx镜像,,其CMD不应该写成service nginx start 或 systemctl start nginx,,而应该写成 nginx -g "daemon off;"
apache httpd -D FOREGROUND
ENTRYPOINT:配置容器启动后执行的命令
语法:
ENTRYPOINT application "arg1", "arg2", ..
ENTRYPOINT ["application","arg1", "arg2"]
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。
ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数--entrypoint 来指定。
Dockerfile 中如果有多个 ENTRYPOINT,只有最后一个生效。
如果你结合CMD命令和ENTRYPOINT命令,你可以从CMD命令中移除“application”而仅仅保留参数,参数将传递给ENTRYPOINT命令。
也可以从命令行中捕获执行时需要的参数
**EXPOSE**: 声明端口
语法: EXPOSE port1 [port2]
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。
在 Dockerfile 中写入这样的声明有两个好处:
一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;
另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射到 EXPOSE 的端口
ENV:设置环境变量
语法:
ENV
ENV = [= …]
设置环境变量,无论是后面的其它指令,如 RUN ,还是运行时的应用,都可以直接使用这里定义的环境变量。
ADD 、 COPY 、 ENV 、 EXPOSE 、 LABEL 、 USER 、 WORKDIR 、 VOLUME 、 STOPSIGNAL 、 ONBUILD等指令都可以用到ENV
通过环境变量,我们可以让一份 Dockerfile 制作更多的镜像,只需使用不同的环境变量即可。
VOLUME: 挂载匿名卷
语法:
VOLUME <dir>
VOLUME ["dir1","dir2"]
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,
这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。常用于需要持久数据的应用
VOLUME设置的挂载可被运行时设置的挂载所覆盖
可通过 docker inspect -f {{.Mount}} container查询所挂载的目录
WORKDIR: CMD命令运行的工作目录
语法: WORKDIR <path>
使用 WORKDIR 指令可以来指定工作目录(当前目录),如该目录不存在, WORKDIR 会自动创建目录
USER: 用于设定容器的运行用户名或UID
语法: USER user/uid
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。
WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份。
和 WORKDIR 一样, USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换
HEALTHCHECK: 健康检查
语法:
HEALTHCHECK [选项] CMD <命令> :设置检查容器健康状况的命令
HEALTHCHECK NONE :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令
在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,
但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检
测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。
当指定了 HEALTHCHECK 指令后,启动容器时的初始状态为 starting ,检查成功后变为 healthy ,如果连续一定次数失败,则会变为 unhealthy
HEALTHCHECK 支持下列选项:
interval=<间隔> :两次健康检查的间隔,默认为 30 秒;
timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3 次。
和 CMD , ENTRYPOINT 一样, HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。
HEALTHCHECK [选项] CMD 后面的命令格式和 ENTRYPOINT 一样,分为 shell 格式和 exec 格式。命令的返回值决定了健康检查的成功与否:
0 - 成功;
1 - 失败;
2 - 保留 不要使用
案例
给centos7 镜像添加 vim+ifconfig+jdk8
FROM centos:7
MAINTAINER 1654246022@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u181-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_181
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
构建build
成功
Network
查看网络
创建&删除
网络模式
bridge模式
IPADDRESS不一样
删除u2开一台新的u3
我们发现ip与删除的u2一样
这告诉我们docker容器内ip是会发生变动的
开启两台实例
[root@docker01 test]# docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
[root@docker01 test]# docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
[root@docker01 test]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6ab5454a945a billygoo/tomcat8-jdk8 "catalina.sh run" 4 seconds ago Up 3 seconds 0.0.0.0:8082->8080/tcp, :::8082->8080/tcp tomcat82
5396ff3c1ba5 billygoo/tomcat8-jdk8 "catalina.sh run" 2 minutes ago Up 2 minutes 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp tomcat81
查看ip发现每个container都有对应的veth
进入tomcat81 ip a
我们发现他的eth0网卡就是前面vth0的部分,如下图,形成了插槽和插口的结构
host模式
开启8083
[root@docker01 test]# docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
WARNING: Published ports are discarded when using host network mode
e8ecd0e3330201569d1a1454c8899caf554558663358a61c7d1ecc9bac5fadd5
模式特点:端口映射没有意义了(端口号以主机端口号为主,重复时递增)
对比bridge与inspect
tomcat81(bridge)
tomcat83(host)
进入tomcat83,我们发现网络地址与主机一致
none模式
inspect
进去看
Container模式
不合适
换个镜像
关闭1,2就没有了
自定义网络模式
互相ping发现能ping
按照服务ping不行
为了解决服务不能ping的问题,引入自定义网络
新建自定义网络
吧81,82加入新建的网络
这样服务就能ping了
工作上建议锁死服务名而不是ip