前言

使用第三方镜像肯定不是学习Docker的最终目的,最想要的还是自己构建镜像;将自己的程序、文件、环境等构建成自己想要的应用镜像,方便后续部署、启动和维护;而Dockerfile就是专门做这个事的,通过类似简单编码的形式,最终就可以构建出属于自己的镜像,所以必须学起来。

正文

1. Dockerfile简介

在日常开发过程中,需要编写对应的程序文件,最后通过编译打包生成对应的可执行文件或是类库;这里的Dockerfile文件就好比平时我们编写的程序文件,但内部的语法和关键字并没有程序那么复杂和繁多,相对来说还是很简单的,最后通过docker build命令就可以将对应的程序、文件、环境等构建成镜像啦。

在第一篇文章最后就简单使用了Dockerfile构建了一个镜像,这里重新认识下这个Dockerfile文件,如下图:

Dockerfile就是一个文本文件,但不需要指定后缀类型; 文件内容中FROM、WORKDIR、COPY等就是关键字,按照规则写好之后,就可以将指定的文件构建为镜像啦。

构建操作统一由Docker daemon进行,它会先对文件内容语法进行初步验证(语法不对就会返回错误信息),然后逐一运行指令,每次生成一个新的镜像层,直到执行完所有指令,就构建出最终的镜像。 Dockerfile、镜像、容器的关系如下:

总结一下Dockerfile的知识点;

  • 构建时,指令从上到下逐一执行;
  • 每条指令都会创建一个新的镜像层,每一层都是前一层变化的增量;
  • 使用#号进行注释;
  • 关键字约定都是大写,后面至少跟一个参数;

2. Dockerfile关键字

2.1 FROM 关键字

指定基础镜像, 就是新镜像是基于哪个镜像构建的。

比如建房子,可以在一块空地开始,也可以在别人打好的基石基础上开始, 甚至可以在别人弄好的毛坯房基础上装修即可。

如果要建房的话,可以FROM 空地,或者FROM 打好的基石,或者 FROM 毛坯房, 反正最后建好房就行;

这里需要注意的是,不管咋样,空地是少不了的;构建镜像也一样,最底层肯定有一个最基础的镜像

建议使用官方的镜像作为基础镜像,推荐使用Alpine这种类型,因为它是严格控制的,而且体积很小。

用法如下:

 # FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
ARG CODE_VERSION=latest # 定义变量
FROM base:${CODE_VERSION} # 指定基础镜像
2.2 MAINTAINER/LABEL 关键字

MAINTAINER 指定维护者的相关信息,也就是构建的镜像是由谁构建的,他的邮箱是什么

LABLE 就是用于给镜像打标签,以键值对的方式进行指定,相对MAINTAINER 来说比较灵活,可以使用LABLE替代MAINTAINER。

用法如下:

 # LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
2.3 RUN 关键字

构建过程中需要运行的命令, 比如在构建过程中需要执行一条命令下载对应的包,这里就需要用到RUN关键字;

用法如下:

 # 两种命令方式都可以
# RUN <command>
# RUN ["executable", "param1", "param2"]
# 执行命令,Linux支持的相关命令
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]
2.4 WORKDIR 关键字

根据镜像启动容器时,通常需要进入到容器内部;则可以通过WORKDIR指定进入容器时的目录

用法如下:

 WORKDIR /path # 指定路径
2.5 ENV 关键字

可以在构建过程中设置环境变量; 就好比平时我们安装完程序,需要配置环境变量,方便访问; ENV关键字就是根据需求可以设置对应的环境变量;

用法如下:

 # ENV <key>=<value> ...
# 指定环境变量
ENV PATH=/usr/local/postgres-$PG_MAJOR/bin:$PATH
2.6 ADD 关键字

将宿主机的资源拷贝进镜像中,会自动解压缩,而且还能从远程宿主机中读取资源并拷贝到镜像中

用法如下:

 # 两种命令方式都可以
# ADD [--chown=<user>:<group>] <src>... <dest>
# ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
ADD https://example.com/big.tar.xz /usr/src/things/
2.7 COPY 关键字

将宿主机的资源拷贝到镜像中,只支持读取构建所在宿主机的资源。相对于ADD关键字来说更加透明,操作什么就是什么。

用法如下:

 # 拷贝资源到容器,两种命令格式都行
# COPY [--chown=<user>:<group>] <源地址>... <目标地址>
# COPY [--chown=<user>:<group>] ["<源地址>",... "<目标地址>"]
COPY requirements.txt /tmp/
2.8 VOLUME 关键字

挂载数据卷,之前在常用命令那说到通过命令的方式进行数据卷挂载,在Dockerfile中使用VOLUME指定挂载路径即可,根据构建出来的镜像运行容器时,默认就有构建时挂载的信息。

用法如下:

 # 挂载数据卷
VOLUME ["/data"]
VOLUME /myvol
2.9 EXPOSE 关键字

指定运行容器时对外暴露的端口;即根据镜像启动容器时,容器向外暴露端口。

用法如下:

 # EXPOSE <port> [<port>/<protocol>...]
EXPOSE 80/tcp # 暴露端口
EXPOSE 80/udp
2.10 CMD 关键字

指定启动容器时要执行的命令,只有最后一个会生效;即根据镜像启动容器时,容器需要执行啥命令。

用法如下:

 # 两种格式都行
# CMD ["param1","param2"]
# CMD command param1 param2
# 执行命令统计 行数、字数、字节数
CMD echo "This is a test." | wc -
# 执行wc --help命令
CMD ["/usr/bin/wc","--help"]
2.11 ENTRYPOINT 关键字

指定根据镜像启动容器时要执行的命令,可以追加命令;执行时机同CMD。

用法如下:

 # ENTRYPOINT ["executable", "param1", "param2"]
# ENTRYPOINT command param1 param2
ENTRYPOINT ["top", "-b"]
2.12 ARG 关键字

通过ARG指令定义了一个变量;和写代码时定义的变量一样,根据需要,定义就行啦。

用法如下:

 # ARG <name>[=<default value>]
ARG user1=someuser
ARG buildno=1
2.13 ONBUILD 关键字

基于父镜像构建新的镜像时,父镜像的OBUILD会被触发。

3. 实战演示

这里还是以.NetCore项目构建镜像为例,其他编程语言的项目同理;这次咱们一步一步的来,搞清楚每个命令的使用。

以下关于项目创建和发布的具体细节在第一篇最后就分享了,小伙伴可以参考,这里主要演示Dockerfile关键字。

3.1 准备项目和Dockerfile文件

新建一个项目,啥都不需要改,就用默认的接口演示,如下:

Dockerfile内容如下:

 # 指定基础镜像,在此基础上构建自己的项目镜像
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
# 指定自己的工作目录,进入容器时目录
WORKDIR /myApp
# 将构建上下文目录下的文件拷贝到容器的当前工作目录中,即/myApp
COPY . .
# 容器向外暴露端口,项目以什么端口启动就暴露对应的端口
EXPOSE 80
# 执行命令,这里默认是以80端口启动的
#就类似于在Linux系统的项目目录下执行 dotnet DockerfileDemo.dll 是一样的
ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]

记得右键Dockerfile,选择属性,然后设置Dockerfile为始终复制,这样后续更新变动,发布时就会自动拷贝到对应的发布目录。

3.2 以文件的形式发布项目,并连同Dockerfile拷贝到安装好Docker的机器上进行构建(这里还是用我的云服务器);

docker build -t myimage:v1.0 .解析

  • -t:指定镜像的名字及标签,通常 name:tag 或者 name 格式,myimage就是镜像名字,v1.0就是tag;
  • -f :指定要使用的Dockerfile路径,这里由于Dockerfile在当前路径,所以不用指定;
  • 最后面的点官方称为构建上下文,点表示指定为当前目录。 会把指定的这个目录下的文件发送给docker daemon进行构建,所以千万不要指定/(斜杠代表根目录,有很多文件的)。
  • 其他选项参数小伙伴可以根据需要使用,以上是比较重要的。
3.3 根据构建出来的镜像启动容器,看Dockerfile中的命令效果;

启动容器如下:

ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]这行代码就等同于的项目目录下直接执行 dotnet DockerfileDemo.dll是一样的,目的就是启动我们的项目

通过docker logs可以查看容器内部的日志,如下:

3.4 丰富化Dockefile文件内容并查看构建之后的细节

文件内容如下:

 # 指定基础镜像,在此基础上构建自己的项目镜像
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
# 指定维护人
MAINTAINER CodeZYQ<1137533407@qq.com>
# 打标签
LABEL createname="CodeZYQ"
# 指定自己的工作目录,进入容器时目录 app
WORKDIR /myapp
# 将构建上下文目录下的文件拷贝到容器中的工作目录中
COPY . .
# 定义变量
ARG myPort=8080
# 使用环境变量方式改变启动端口,拼接用到了定义的变量
ENV ASPNETCORE_URLS=http://+:$myPort
# 通过RUN 执行相关命令,根据需要执行相关命令
RUN mkdir testDir
# 挂载数据卷,这里模拟挂载日志目录
VOLUME /Logs
# 容器向外暴露端口,项目以什么端口启动就暴露对应的端口
EXPOSE $myPort
# 执行命令,这里默认是以80端口启动的
# 就类似于在Linux系统的项目目录下执行 dotnet DockerfileDemo.dll 是一样的
ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]

执行如下命令,构建新的镜像:

 # 这里没有显示指定tag 默认就latest
docker build -t newimage .

通过docker logs看看容器日志,如下:

看看数据卷挂载是否成功,进入容器,看根目录下就会多了Logs目录,也可以通过docker inspect 容器 看容器详细信息,如下:

标签也打成功了:

也可以通过docker inspect 镜像查看镜像内部的详细信息,执行命令docker inspect newimage如下:

关于步骤和效果,在Dockerfile注释和图表中已经详细描述。

3.5 CMD和ENTRYPOINT的区别

两个命令都是启动容器时指定执行命令和对应的参数,但两者稍有不同,如下:

  • CMD:只能最后一个命令会生效,命令会被docker run之后的参数替换掉;
  • ENTRYPOINT:可以追加命令,比如增加参数;

上面构建出来的newimage镜像用到的是ENTRYPOINT,所以我们先来测试一下ENTRYPOINT,如下:

docker run启动容器时指定了参数 --urls="http://+:9999",容器正常启动,并且参数还能生效,等同于在当前目录直接执行如下命令:

 dotnet DockerfileDemo.dll --urls="http://+:9999"

现在把ENTRYPOINT换成CMD试试,如下:

 # 在以上的Dockerfile中
# 将ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]换成CMD,如下:
CMD ["dotnet", "DockerfileDemo.dll"]

然后重新构建一个镜像试试,测试如下:

如上图,对于CMD而言,如果在运行容器时,后面指定参数,这个参数就会把CMD命令替换掉,不能拼接,导致命令不对,所以报错;但这样就可以执行,如下:

如果在当前构建的上下文目录中不想要一些文件参与构建,可以通过在.dockerignore文件中进行配置,这个和git中的.gitignore一个道理,编写也比较简单,这里就不演示了。

对了,.NetCore的镜像列表可以参照这个地址:https://hub.docker.com/_/microsoft-dotnet-aspnet/,每个镜像都有对应的Dockerfile,感兴趣的小伙伴可以点进去看看,参考参考。

总结

关于Dockerfile的演示就先说那么多,小伙伴们一定要举一反三,上面演示只是一个小例子而已,在正式项目中可以根据需要,编辑出属于符合需求的Dockefile文件,最终构建出方便、好用的镜像,这样开发和运维就和谐了(嘿嘿嘿)。

Docker之前文章目录:

  1. Docker小白到实战之开篇概述
  2. Docker小白到实战之常用命令演示,通俗易懂
  3. Docker小白到实战之容器数据卷,整理的明明白白

好了,下次聊聊Docker中的网络应用,关注“Code综艺圈”,和我一起学习吧;

Docker小白到实战之Dockerfile解析及实战演示,果然顺手的更多相关文章

  1. docker学习笔记-05:DockerFile解析

    一.DockerFile是什么 1.DockerFile是用来构建docker镜像的构建文件,是由一系列参数和命令构成的脚本. 2.构建三步骤: 手动编写一个dockerfile文件,然后直接dock ...

  2. Docker小白到实战之Docker网络简单了解一下

    前言 现在对于Docker容器的隔离性都有所了解了,但对容器IP地址的分配.容器间的访问等还是有点小疑问,如果容器的IP由于新启动导致变动,那又怎么才能保证原有业务不会被影响,这就和网络有挂钩了,接下 ...

  3. Docker小白到实战之容器数据卷,整理的明明白白

    前言 上一篇把常用命令演示了一遍,其中也提到容器的隔离性,默认情况下,容器内应用产生的数据都是由容器本身独有,如果容器被删除,对应的数据文件就会跟着消失.从隔离性的角度来看,数据就应该和容器共存亡:但 ...

  4. Docker 实战—使用 Dockerfile 构建镜像

    Dockerfile 指令详解请访问:https://www.cnblogs.com/cloudfloating/p/11737447.html 使用 Alpine Linux 作为基础镜像 Alpi ...

  5. Docker系列(24)- 实战:DockerFile制作tomcat镜像

    实战:DockerFile制作tomcat镜像 step-1 准备镜像文件 tomcat压缩包,jdk压缩包! step-2 编写dockerfile文件,官方命名Dockerfile,build会自 ...

  6. Docker实战-编写Dockerfile

    一.编译镜像 1. 编译镜像 Dockerfile类似于Makfile,用户使用docker build就可以编译镜像,使用该命令可以设置编译镜像时使用的CPU数量.内存大小.文件路径等 语法:doc ...

  7. Dockerfile解析(八)

    一.Dockerfile是什么 Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本. 1. 构建的步骤 运行容器:docker run 构建新的镜像:docker ...

  8. 6、DockerFile解析:三步走、保留字指令

    1.dockerfiel是什么 1.是什么 Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本. 2.构建三步骤 编写Dockerfile文件 docker bu ...

  9. Docker学习(六)Dockerfile构建自定义镜像

    Docker学习(六)Dockerfile构建自定义镜像 前言 通过前面一篇文章可以知道怎么去使用一个镜像搭建服务,但是,如何构造自己的一个镜像呢,docker提供了dockerfile可以让我们自己 ...

随机推荐

  1. Couchdb 垂直权限绕过漏洞(CVE-2017-12635)

    影响版本:小于 1.7.0 以及 小于 2.1.1 首先,发送如下数据包: 修改数据包 { "type": "user", "name": ...

  2. 重返MySQL之MySQL基础

    重返MySQL之MySQL基础 本章详细介绍了,什么是数据库,常见的关系型数据库有哪些,什么是MySQL,及MySQL中DDL操作表,DML操作表记录. 1.0 数据库概述 1.1 数据存储的方式 目 ...

  3. rsa加密初探

    RSA加密算法初探 RSA加密算法是早期的非对称加密,公钥和私钥分离,公开公钥,通过确保私钥的安全来保证加密内容的安全.由麻省理工学院的罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi ...

  4. 实现 pow 函数

    1 ////实现pow函数 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 double power(double,int) ; ...

  5. Spring Cloud Alibaba - Spring Cloud Stream 整合 RocketMQ

    Spring Cloud Stream 简介 在微服务的开发过程中,可能会经常用到消息中间件,通过消息中间件在服务与服务之间传递消息,不管你使用的是哪款消息中间件,比如RabbitMQ.Kafka和R ...

  6. 玩转Java8日期工具类-基础

    内容基于的是 Java8官方文档,以及Java时间类总结 的总结.BTW:其实具体方法的使用直接在IDEA中看源码更方便直接. 1.老一辈:Java.util.Date Java.sql.Date J ...

  7. Java 横向技术 Spring框架【笔记】

    Java横向技术 spring框架[笔记] Spring 的两大特性是什么? AOP(Aspect Oriented Programming,面向切面编程)与 IOC(Inverse of Contr ...

  8. S3C2440—12.按键中断

    文章目录 一. 总体 二. CPSR设置 三. 中断源设置 四. 中断控制器设置 五. C中断处理函数 六. 汇编IRQ异常处理程序 七. 源码 一. 总体 要驱动按键中断控制LED亮灭,程序要进行如 ...

  9. join控制线程的执行循序 T1 -> T2 -> T3

    /** * 控制线程的执行循序 T1 -> T2 -> T3 * join实现 */ public static void join(){ Thread t1 = new Thread(( ...

  10. 【springcloud】Eureka 常用配置解析

    转自:https://www.cnblogs.com/zyon/p/11023750.html 1. 配置项解析 1.1 通用配置 # 应用名称,将会显示在Eureka界面的应用名称列 spring. ...