在点击蓝字关注我们
大家好,我是杰哥
通过前两篇Docker的理论与实战,我们了解到Docker的核心概念。包括什么是Docker,Docker的出现解决了哪些问题,Docker的命令使用方式等等
我们知道,Docker的出现,极大地简化了从开发到运维的交付过程,因为它可以使得开发交给运维的是软件程序以及软件程序所依赖的环境,即docker镜像,而不仅仅是软件程序
那么,该如何使用Docker构建一个镜像呢?要想回答这个问题,我们就得认识今天的主角:Dockerfile
一 初识
Dockerfile是什么?
1 Dockerfile的角色
我们知道,若要将本地的服务端程序发布到linux服务器上运行。首先会通过maven或其他工具将其构建为一个jar或者war包,然后修改相关配置,使用服务器启动
即总得来说,若要运行一个服务端程序,项目只需要被构建为jar包或者war包,然后运行即可
而Docker的生命周期也很类似,分为以下三个步骤
编写Dockerfile-》构建(docker build) -》运行(docker run)
由Dockerfile构建为Docker镜像,运行这个Docker镜像,就变成了容器
三者的角色分别可以描述为:
Dockerfile:软件的原材料
Docker镜像:软件的交付品
Dcoker容器:软件的运行态
那么,也就是说,Dockerfile,实际上是用来构建镜像的一个构建文件
2 一个例子
先来看看官网上centos的Dockerfile文件,来初步认识一下Dockerfile
FROM scratchADD centos-8-x86_64.tar.xz /LABEL org.label-schema.schema-version="1.0" org.label-schema.name="CentOS Base Image" org.label-schema.vendor="CentOS" org.label-schema.license="GPLv2" org.label-schema.build-date="20201204"CMD ["/bin/bash"]
可以试着读一下,如果你是第一次见到Dockerfile文件,那么不理解很正常
我来简单解读一下,你就会有一个大致的印象了
首先,我们看到,这个文件中包含了Dockerfile的四个保留字指令:
FROM ADD LABEL以及CMD
FROM 表示基于哪个镜像进行构建,类似于java中的extends的含义
ADD 表示将一个压缩文件解压复制到目标路径
LABEL 为该镜像添加元数据标签信息
CMD 指定容器启动进程的启动命令
而这些指令都是按照顺序执行的
也就是说,centos的镜像的逻辑分为如下几步:
首先声明它自己继承自scratch这个基础镜像。你可以理解为java中的Object类,即基类
然后,将安装包centos-8-x86_64.tar.xz解压后拷贝到当前目录下(即进入容器之后的路径,一般为/bin/bash)
接着,添加一些标签信息,包括名称、构建时间、构建版本以及供应商信息等
最后,进入容器的/bin/bash目录
验证一下,通过查看本地下载好的最新版本的centos的信息(执行docker inspect centos)
可以看到,这些信息是已生效的
3 回答:Dockerfile是什么?
前面通过类比maven项目以及对一个centos的解读,你一定理解了Dockerfile到底是什么了。没错,用一句话总结:
Dockerfile由一系列命令和参数构成,是用来构建镜像的构建文件
二 发育
Dockerfile保留字学习
有了一点点印象,我们来看看Dockerfile的基本语法,也就是说各个常用构建指令的含义
1 FROM 指定基础镜像
前面已经看到了,用来指定基础镜像
在Dockerfile中,FROM是必备指令,并且必须是第一条指令
2 RUN 执行命令行命令
用于执行命令行命令
RUN <命令>RUN ["可执行文件", "参数1", "参数2"]
3 WORKDIR 指定工作目录
用于指定工作目录(或者称为当前目录)
WORKDIR <工作目录路径>
使用它实现的效果是:
若使用了WORKDIR testdir1指令,以后各层都是在testdir1目录下进行操作
是不是让你想到了cd命令,没错,的确可以理解为linux中的cd命令。不同点在于,即使该目录不存在,WORKDIR也会帮你建立
4 COPY 拷贝文件
拷贝文件,将从构建上下文目录中<源路径>的文件/目录复制到容器内的<目标路径>位置
COPY <源路径>... <目标路径>COPY ["<源路径1>",... "<目标路径>"]
例子1
COPY /home/file1 /testdir1
该指令,表示将主机上的/home/file1目录下的所有文件拷贝至容器中的testdir1目录
需要注意的是,源路径可以是多个,甚至可以是通配符,看看例子2
例子2
COPY /home/file1/test* /testdir1
该指令,表示拷贝/home/file1目录下,以test开头的所有文件至容器的testdir1目录下
第3,目标路径既可以是容器内的绝对路径,也可以是相对于工作目录的相对路径,也就是说文件在前方有使用WORKDIR命令声明了当前目录
例子3
WORKDIR /testdir1COPY /home/file1/test* .
在这里,目标路径不需要事先创建。如果目录不存在,会在复制文件前先行创建缺失目录
4 ADD 更高级的拷贝
ADD <源路径>... <目标路径>ADD ["<源路径1>",... "<目标路径>"]
为什么说它是更高级的拷贝呢?
ADD指令和COPY的格式和性质基本一致,均是拷贝的功能
不同点在于,当源路径的文件为一个压缩文件(tar, gzip, bzip2, etc)时,
若使用COPY指令,则只是将其由源路径拷贝到目标路径,并不会做任何处理
使用ADD指令,则是先解压,再拷贝其解压后的文件到目标路径
看看下面这个例子你就知道了
a 将centos-8-x86_64.tar.xz拷贝到容器根目录
COPY centos-8-x86_64.tar.xz /
b 将centos-8-x86_64.tar.xz先解压,再拷贝到容器根目录
ADD centos-8-x86_64.tar.xz /
总结
ADD=[解压缩]+COPY
5 CMD 容器启动命令
CMD <命令>CMD ["可执行文件", "参数1", "参数2"...]CMD ["参数1", "参数2"...]
我们知道,Docker容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD指令就是用于指定默认的容器主进程的启动命令的
一般情况,在Dockerfile中,CMD指令会被覆盖。即当存在多条CMD指令,则只有最后一条指令会被运行;如果用户启动容器时指定了运行的命令,也会覆盖CMD指定的命令
6 ENTRYPOINT 容器启动命令
与CMD命令一样,指定容器启动程序及参数,在Dockerfile中指定了多个时也是最后一个生效
ENTRYPOINT ['executable', 'param1', 'param2']ENTRYPOINT command param1 param2
唯一不同的点在于,在运行时不会被指定的参数替代
7 ENV 设置环境变量
ENV <key> <value>
效果相当于在代码中指定一个全局变量的值,后续的代码都可以使用这个全局变量
例子1
ENV mypath /tmpWORKDIR $mypath
该指令的意思为:将/tmp目录设置为当前目录
8 ARG 构建参数
ARG <参数名>[=<默认值>]
Dockerfile中的ARG指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令docker build中用--build-arg <参数名>=<值>来覆盖
需要注意的是,ARG 定义的变量只在建立 image 时有效,建立完成后变量就失效消失。也就是说在运行期间,再去指定这个参数的值是不会生效的
9 VOLUME 定义容器数据卷
VOLUME ['/data']
创建一个可以从本地主机或其他容器挂载的挂载点,用于数据保存和持久化工作
比如,指令
VOLUME ["/var/test1", "/var/log/test2", "/etc/test3"]
表示在容器的根目录下创建三个目录,用来挂载数据。并与本地主机上的某个地址映射共享(虽然没有指定,但是docker会根据本地主机的类型分别默认指定三个映射目录)
当然,在运行时可以通过-v 来覆盖这个挂载设置。比如:
docker run -d -v mydata:/data xxx
就使用了mydata这个命名卷挂载到了/data这个位置,替代了Dockerfile中定义的匿名卷的挂载配置
温馨提示1
如果现在有点难以理解这块的描述,那么可以移步至实战一,通过实战例子进行理解
10 EXPOSE声明端口
EXPOSE <端口1> [<端口2>...]
EXPOSE指令是声明运行时容器提供服务端口
当然这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。只有在运行时使用-p <宿主端口>:<容器端口>进行宿主机和容器端口的映射,才会向客户端开启这个端口
三 实战
使用Dockerfile构建镜像
既然熟悉了Dockerfile的基本命令,我们趁热打铁,进入实战环节
我将通过两个个镜像构建的实战,带领大家实际应用Dockerfile中的部分常用指令,并进一步熟悉Dockerfile构建镜像的过程
实战一 创建挂载目录
1 创建Dockerfile
FROM centosVOLUME ["/var/test1", "/var/log/test2", "/etc/test3"]CMD echo "hello,the three volumes created!"CDM /bin/bash
2 构建
执行如下命令进行构建
docker build -f Dockerfile -t mycentos .
构建过程如下:
我们看到,构建过程中,实际就是在顺序执行Dockerfile中的每行命令
3 验证
1)查看镜像
运行docker images,查看本地镜像列表
从图中看到,mycentos已成功构建
2)查看挂载目录是否已创建
a 运行镜像,并进入容器
我们看到,docker已在该容器的根目录下创建了三个挂载目录
b 查看匿名目录
通过运行命令
docker inspect 000dc6727d06
查看当前的容器信息。你会看到,docker实际上也为这三个目录分别创建了三个对应的匿名目录
4 运行时指定挂载目录映射地址
1) 启动容器
运行如下命令,指定/test1目录对应的本地主机绑定地址
docker run -it -v /Users/wangjie/Downloads/documents/mydocker:/test1 mycentos
并在容器中的test1目录下创建test1.txt文件
2)查看绑定目录信息
通过运行命令
docker inspect ba0e700b7e50
查看当前容器信息
3)验证是否真正绑定
查看对应主机目录,该目录下的确存在了test1.txt文件
说明,Dockerfile中通过VOLUMN关键字指定的挂载目录,在运行时可以通过-v 来覆盖这个挂载设置
小结
1 Dcoker镜像的构建过程分为Dockerfile文件编写、docker build构建两步
2 Dcoerkfile中的VOLUMN指令,用来指定Docker容器的挂载目录
3 在运行时,可以通过-v 来覆盖Dockerfile文件中的挂载设置
那么,接下来,进入是实战二
实战二 构建Spring Boot项目并运行
1 创建Dockerfile文件
FROM openjdk:8-jdk-alpineVOLUME /tmpARG JAR_FILE COPY ${JAR_FILE} app.jarENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar ${0} ${@}"]
该Dockerfile表示:在构建镜像过程中,分别执行:
a 在jdk8的基础镜像的基础上
b 创建挂载目录/tmp
c 声明参数JAR_FILE
d 将我们打好的软件包拷贝到根目录下,并重命名为app.jar
e 指定在运行时,执行java -jar app.jar命令
2 构建镜像
docker build --build-arg=target/*.jar -t test1/app .
使用docker build 命令构建镜像,指定镜像中定义的JAR_FILE的值:target/*.jar,指定该镜像名称为test1/app
3 启动镜像
docker run -p 8888:9999 -e "JAVA_OPTS=-Ddebug -Xmx128m" test1/app --server.port=9999
启动命令,指定其JAVA_OPTS参数的值:-Ddebug -Xmx128m,并指定运行端口为9999,通过-p将其端口映射为8888
若服务启动成功,访问本地ip的8888端口服务,即可实现该服务的访问
温馨提示2
此时再回过头浏览一下那些命令,学习效果会更佳哦~
三 总结
总而言之
好了,Dockerfile实战篇圆满结束~
本篇内容,我们分别总结了概述了Dockerfile是什么,了解了10个Dockerfile的主要命令,并通过两个镜像构建的实战过程,真正使用到了其中几个常用命令,相信大家已经对Dockerfile掌握的差不多了~
其他的命令大家可以自己尝试一下
如果你已经按奈不住激动的心,想要尝试一把的话。建议你可以考虑一下你们当前项目中使用的项目要是直接通过镜像交付,该如何编写Dockerfile
想好了就真正实现一把,有问题或者经验分享,也可以随时与我讨论哦~
嗯,就这样。每天学习一点,时间会见证你的强大~
下期预告:
敬请期待~
往期精彩回顾
SpringCloud篇章
Spring Cloud(十三):Feign居然这么强大?
Spring Cloud(十):消息中心篇-Kafka经典面试题,你都会吗?
Spring Cloud(九):注册中心选型篇-四种注册中心特点超全总结
Spring Cloud(四):公司内部,关于Eureka和zookeeper的一场辩论赛
Spring Cloud(一):我与导师的对话:你真的了解zookeeper吗?
..........
Spring Boot篇章
Spring Boot(八):Spring Boot的监控法宝:Actuator
Spring Boot(七):你不能不知道的Mybatis缓存机制!
Spring Boot(六):那些好用的数据库连接池们
Spring Boot(四):让人又爱又恨的JPA
SpringBoot(一):特性概览
..........
翻译
如何成为谷歌开发专家(GDE)— 实用指南
使用 CSS 提升页面渲染速度
WebTransport 会在不久的将来取代 WebRTC 吗?
.........
职业、生活感悟
你有没有想过,旅行的意义是什么?
程序员的职业规划
让程序员崩溃的十个瞬间!第6个简直不能忍!
聊聊这次换工作经历
欢迎大家关注们的公众号,一起持续性学习吧~