1、前言
嘿!大家好!今天我们聊聊一个非常酷的工具——Dockerfile。小伙伴们如果是一个开发人员或者经常需要操作Linux,你肯定会对它感兴趣。
Dockerfile是Docker的一个重要组成部分,它极大地简化了应用程序的部署过程。相信大家都经历过因为环境配置不一致而导致应用程序无法正常运行的痛点吧?别担心,Dockerfile可以解决这个问题!
2、Dockerfile是什么?
Dockerfile是一个文本文件,内容是一条一条的指令,每条指令构建一层,因此每条指令的内容,就是描述该层应当如何构建。(这是抄的 😁)
有了Dockerfile,我们只需要简单地运行一个命令,就可以一键构建出整个容器,包括操作系统、库、环境变量等各种配置。
3、使用Dockerfile有什么优点
(1) 快速构建和部署容器化应用
(2) 实现应用的版本控制和回滚
(3) 复用和共享已有的镜像和层
(4) 提高应用的安全性和可移植性
4、适用人群
开发人员、运维人员以及对Linux喜爱的小伙伴
5、Dockerfile的语法
5.1 FROM 基础镜像
FROM 表示设置要制作的镜像基于哪个镜像,FROM指令必须是整个Dockerfile的第一个指令,如果指定的镜像不存在默认会自动从Docker Hub上下载。
指定我们的基础镜像是node,latest表示版本是最新
格式:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
例如:
FROM node:latest
5.2 MAINTAINER 作者信息
格式:
MAINTAINER <name>
例如:
MAINTAINER zhangsan shuaige@example.com
5.3 RUN 构建镜像执行的命令
RUN命令有两种格式:
1、shell格式:
RUN <命令行命令>
<命令行命令> 指的就是 在终端输入的shell指令。
例如:
RUN mkdir -p ./index
这执行的结果就是在当前目录创建一个index的文件夹。
2、exec格式:
RUN ["可执行文件", "参数1", "参数2"]
这里可以把 ”可执行文件“ 理解成一个可接受参数的执行文件,”参数1“、”参数2“ 顾名思义是给执行文件的具体传参。
例如:
RUN ["/bin/bash", "-c", "echo hello"]
Tips:
Dockerfile中每个指令都会新建立一层,多个RUN指令就构建多少层镜像,这就会导致镜像冗余,增加部署的时间不说,还容易出错。
当我们需要多个RUN指令的时候,可以将指令进行合并,例如:
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
从例子中可以看出,需要书写多行命令时,命令与命令之间用\ 换行 &&
来进行拼接。这样执行之后,只会产生一层镜像。
5.4 COPY 复制指令
将上下文目录中的文件都copy到container(运行此镜像的容器)文件系统的文件夹下
格式:
COPY <源路径>... <目标路径>
或
COPY ["<源路径>",... "<目标路径>"]
源路径:源文件或源目录。
目标路径:容器内指定路径,路径不存在会被自动创建
例如:
COPY ./index /crawler_node/index
这里源路径为./index
,目标路径为/crawler_node/index
。
5.5 ADD 将本地文件添加到容器
ADD的功能与COPY类似,都是将本地文件复制到容器中,都会自动创建容器目录
不同点在于:
- ADD可以将tar类型(压缩格式为gzip、bzip2以及xz)的文件自动解压;而COPY不会
- ADD可以加载网络资源,但不会自动解压;COPY不能访问网络资源
例如:
ADD ./bin/* /usr/bin
将当前目录下的bin文件夹内所有不带后缀的文件拷贝到/usr/bin
目录下
5.6 CMD 当容器启动所运行的命令
这时候小伙伴可能有疑问了,RUN不也是用于运行程序的指令吗,CMD和RUN有啥区别?
区别在于 CMD是启动容器时用到的指令,而RUN是构建镜像需要的指令。换句话说,先用RUN命令构建镜像,镜像构建成功后,需要用CMD来将新构建的镜像进行启动,有点类似于npm run serve
。
CMD格式与RUN相类似,也是两种格式(下面代码块内容引自 docker文档):
shell 格式:CMD <命令>
exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
例如:
CMD ["node", "index.js"]
5.7 ENV 设置环境变量
格式:
ENV <key> <value>
定义了环境变量,后续就可以使用这个环境变量的值。
例如:
ENV HOST 0.0.0.0
ENV PORT 8081
后续使用$HOST
就能引用到0.0.0.0
的值
5.8 EXPOSE 监听端口
用于为容器开放要监听的端口,以实现与外部的通信。
例如:
EXPOSE 8081
这样会将端口号8081暴露出来
5.9 ENTRYPOINT 容器启动时执行的命令
ENTRYPOINT
命令和CMD
很像,ENTRYPOINT
也有两种格式,shell格式和exec格式,它们的用法与CMD相同。
如果dockerfile中有多个ENTRYPOINT
指令,只有最后一个会生效。如果docker run
命令中指定了--entrypoint
选项,那么dockerfile中的ENTRYPOINT
会被覆盖。
CMD和ENTRYPOINT
可以结合使用,这样CMD的内容会作为ENTRYPOINT
的参数传递。例如,如果dockerfile中有如下指令:
ENTRYPOINT ["ping"]
CMD ["www.baidu.com"]
那么容器启动时会执行ping www.baidu.com
这个命令。
如果docker run
命令中指定了其他参数,如docker run myimage www.google.com
,那么容器启动时会执行ping www.google.com
这个命令,覆盖了dockerfile中的CMD。
如果dockerfile中有多个CMD指令,那么只有最后一个会生效。
5.10 VOLUME 设置挂载点
格式:
VOLUME ["/data"]
VOLUME 命令会创建一个可以从本地主机或其他容器挂载的挂载点,与-v
选项一样。
5.11 LABEL 元数据添加
LABEL
和MAINTAINER
相类似,也可以添加一些作者的信息。通过LABEL
可以为镜像添加一些元数据,格式如下
LABEL <key>=<value> <key>=<value> ...
一组key、value与另一组之间要用空格隔开。一个镜像可以有多个标签,也可以在一行指定多个标签。
例如:
LABEL version="1.0"
LABEL author="萧瑟"
LABEL description="这是一个demo"
5.12 WORKDIR 指定工作区
格式为:
WORKDIR <工作区目录>
WORKDIR
指令的作用就是用来指定工作目录,如果目录不存在,该命令会自动创建目录。
6、镜像的构建
6.1 准备Dockerfile
下面是我写的一个简单的Dockerfile示例:
# FROM 表示设置要制作的镜像基于哪个镜像,FROM指令必须是整个Dockerfile的第一个指令,如果指定的镜像不存在默认会自动从Docker Hub上下载。
# 指定我们的基础镜像是node,latest表示版本是最新
FROM node:latest
LABEL author="萧瑟"
# 执行命令,创建文件夹
RUN mkdir -p /www/crawler_node/index
# 将根目录下的文件都copy到container(运行此镜像的容器)文件系统的文件夹下
COPY ./index /www/crawler_node/index
# WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次,如果使用相对路径则为相对于WORKDIR上一次的值,
# 例如WORKDIR /data,WORKDIR logs,RUN pwd最终输出的当前目录是/data/logs。
# cd到 /www/crawler_node
WORKDIR /www/crawler_node/index
# 安装项目依赖包
RUN npm install
RUN npm build
# 配置环境变量
ENV HOST 0.0.0.0
ENV PORT 8081
# 容器对外暴露的端口号(笔者的nestjs运行的端口号是8081)
EXPOSE 8081
# 容器启动时执行的命令,类似npm run start
CMD ["node", "index.js"]
6.2 目录层级
Dockerfile的文件名即为Dockerfile,并且没有后缀名,放置位置与项目目录同级。
|-- Dockerfile
|-- index
|-- node_modules
|-- index.js
|-- package.json
|-- README.md
|-- package-lock.json
6.3 构建镜像
首先我们要进入到Dockerfile所在的目录
然后运行build命令
docker build .
这样我们的镜像就构建完成啦。不过这时候我们的镜像名是随机的,如果我们想要自定义镜像名怎么办呢,构建镜像的指令可以用下边这个。
docker build -t crawler_node .
-t
就是给镜像命名一个tag的意思,后边的crawler_node就是我们的镜像名,千万不要漏掉最后的点
执行构建命令之后,docker就会进行构建
[root@master env]# docker build -t crawler_node .
Sending build context to Docker daemon 3.584kB
Step 1/3 : FROM FROM node:latest
---> dc3bacd8b5ea
Step 2/3 : LABEL author=萧瑟
---> Running in 0ff7462c023b
Removing intermediate container 0ff7462c023b
---> bae66ec32f55
Removing intermediate container 2a3701bc64fc
---> f75b03f5ec1f
Successfully built f75b03f5ec1f
Successfully tagged crawler_node:0.0.1
7、注意事项
- 不要忘记build命令最后的.
- build命令中的镜像名不能出现大写字符
- 基础镜像的标签最好不要用latest,基础镜像版本更新有可能会导致项目报错
- 合理调整COPY与RUN的顺序
- 将多个RUN指令合并为一个
- 设置默认的环境变量,映射端口和数据卷
8、结尾
祝大家docker构建镜像的时候一切顺利,有问题可以去github提issues,也欢迎在评论区交流探讨。
同时也欢迎有能力构建镜像能方便大家工作学习生活的同学,多多参与到将镜像推送至仓库的活动中来。
最后感谢各位开发人员的付出,能让我们有docker这么好用的工具。😁
9、参考资料
1、https://developer.aliyun.com/article/484262
2、https://www.cnblogs.com/panwenbin-logs/p/8007348.html
3、https://www.runoob.com/docker/docker-dockerfile.html
评论区