Dockerfile学习
Dockerfile学习
下一篇Docker文章:Docker Compose学习
什么是Dockerfile
Dockerfile是一种用于定义Docker镜像构建过程的文本文件,通过一系列指令和参数指导Docker引擎执行操作。它提供了可重复、可自动化的方式来构建镜像,包括安装软件包、复制文件、设置环境变量等。Dockerfile具有版本控制、可移植性和自动化构建等优势,使得应用程序的部署和交付更加简单和可靠。通过编写清晰、可维护的Dockerfile,开发人员可以轻松构建、交付和部署应用程序,提高团队协作效率和应用程序的可靠性。
简单来说,Dockerfile就是一个安装指令集合,通过这个指令集合,将指定目录下的文件打包成一个Docker镜像;因此我们可以通过这个文件构建我们想要的镜像。
Dockerfile指令详解
FROM
构建镜像的基础镜像,解析来的一切操作都在这个基础镜像上进行。
什么是基础镜像呢?就拿
FROM ubuntu:latest
来说,它使用ubuntu作为基础镜像,这里的ubuntu是指包含linux内核和ubuntu特征的小型操作系统。我们要知道,一个服务的运行必须依赖一个操作系统,Docker已经隔离了宿主机操作系统,那么每一个镜像就是一个封装了操作系统的微型环境,这个环境里面运行着我们需要的服务,这个操作系统是非常轻量化的,只包含Linux内核和一些工具类,包管理器等;我们也可以根据构建需求进行调整。像Ubuntu、Alpine、CentOS这样的基础镜像叫做基础操作系统镜像,也有特定环境的镜像,如:Java、Node.js、Python环境的镜像,这种镜像除了操作系统本身外,就只安装了运行对应语言的环境。
因此在选择镜像的时候也有一些讲究:
- 尽可能小的镜像:选择尽可能小的基础镜像可以减少镜像的大小,提高构建速度和部署效率。较小的镜像通常只包含必需的组件,不包含多余的软件包和依赖项。
- 官方支持和维护:选择官方提供的基础镜像可以获得更好的支持和维护,确保镜像的安全性和稳定性。官方镜像经过广泛测试和验证,有活跃的社区支持。
- 特定用途的镜像:如果应用程序有特定的需求或依赖项,可以选择特定用途的基础镜像。例如,如果应用程序使用Java开发,可以选择官方提供的Java镜像作为基础镜像。
通过上面我们发现镜像的构建其实可以一层又一层:我们先用基础镜像a构建生成镜像b,再用镜像b构建生成镜像c。
MAINTAINER(已弃用)/ LABEL
MAINTAINER
用于指定镜像作者信息,LABEL
用于给容器指定元数据信息在较早的Docker版本中,使用
MAINTAINER
指令来指定镜像的作者信息。然而,自Docker 1.13版本开始,MAINTAINER
指令已被标记为废弃(deprecated),并建议使用LABEL
指令来替代。例如:
# 指定容器作者
MAINTAINER 张三
# LABEL的使用方式
LABEL maintainer="张三"
LABEL version="1.0"
LABEL description="这是一段描述"
RUN
用于在容器中运行指令,因为上文说了,容器就是一个小型Linux系统,如果我在构建镜像的时候需要在这个Linux系统上运行一些指令,就需要用到RUN了,例如:
# 在该容器中更新apt包和安装net-tools
RUN apt-get update && apt-get install -y net-tools**注意:**在使用RUN指令的时候,不要一个命令就使用一个RUN指令,这样会增加容器层数;因为每使用一次RUN指令的时候,容器在构建过程中就会产生一个中间层,一是会减慢容器的构建速度,二是增加容器大小;对于有多个命令的情况,可以像上面一样,将多个命令用&&拼接在一起,对于换行的命令,可以使用\
CMD
CMD指令用于设置容器在启动是运行的命令,在镜像构建阶段CMD是不会执行的,当我们把镜像运行成容器,在容器启动阶段,CMD指定的命令就会运行。
CMD命令的两种编写形式:
Exec形式(推荐):
CMD ["可执行文件", "参数1", "参数2", ...]
# 例如执行nodejs服务,执行node app.js
CMD ["node","app.js"]
# 命令以json数组的形式Shell形式:
CMD 可执行脚本
# 例如执行nodejs服务
CMD node app.js
# 适用需要使用到shell变量的形式注意:
CMD
指令可以在 Dockerfile 中只出现一次。如果多次出现,只有最后一条CMD
指令会生效,指定的命令将成为容器启动时的默认命令。如果在运行容器时提供了命令行参数,它们将覆盖CMD
中指定的默认命令。
ENTRYPOINT
ENTRYPOINT指令的用法以及效果跟CMD类似,还是有一些区别的:
- CMD命令会被运行容器是外部指定的参数所替代,就是我在运行容器的时候,给它设置了运行参数,将覆盖CMD指令后的参数;而ENTRYPOINT不会覆盖,而是替换。
- 一个镜像内可以有多个CMD指令,但只能有一个ENTRYPOINT指令
例如:
FROM ubuntu
CMD ["echo", "Hello, Docker!"]结果输出:Hello, Docker!
FROM ubuntu
ENTRYPOINT ["echo", "Hello, Docker!"]结果输出:echo Hello, Docker!
在容器中也是更建议使用CMD命令
EXPOSE
申明容器内监听的端口,多个端口用逗号拼接;什么叫申明监听端口呢,指的是我给这个端口做一个标记,但是我不会去监听这个端口,只是做一个标记,当创建容器时可以使用-p参数将该端口与宿主机进行映射,比如
# 表明我申明了80端口,在创建容器的时候可以使用-p 80:80进行端口转发
EXPOSE 80
# 申明多个端口
EXPOSE 80,90,100
ENV
设置一个环境变量,既可以在镜像内当作变量使用,也可以在容器内部当作环境变量使用,此外,也可以在容器创建时通过-e参数给容器设置环境变量
# 设置一个环境变量app_id=123456789
ENV app_id=123456789
通过创建容器时指定环境变量
docker --name myapp -e app_id=123456789 -d app
WORKDIR
指定容器的工作目录,这将是后续命令的默认目录
# 指定根目录下app目录为工作目录
WORKDIR /app
# 指定当前路径下app目录为工作目录
WORKDIR app**注意:**WORKDIR可以设置多次,每次设置都会更改工作目录。
ADD
用于将文件、目录或远程 URL 添加到容器内指定目录中,如果是压缩文件,将会自动解压
# 基本用法
ADD <源路径> <目标路径>
# 将Dockerfile当前目录下所有文件和文件夹拷贝到容器内的工作目录,如果没有工作目录,就是容器根目录,也就是Linux根目录
ADD . .
# 将指定文件拷贝到指定目录
ADD /usr/local/app.json /home/
# 在指定url上下载文件,拷贝到指定目录
ADD xxx.xxx.com/user.json /home/user/
COPY
用于将文件、目录或远程 URL 拷贝到容器内指定目录中,但是如果是压缩文件,它是不会自动解压的;用法的话和ADD一样,但是不支持远程url。
建议:如果只是简单的拷贝需求,不涉及文件解压的话,建议使用COPY,如果涉及文件解压这样的,就用ADD。
VOLUME
于在容器中创建一个或多个挂载点(卷),挂载点是容器内的目录,可以与主机上的目录或其他容器共享数据。在设置挂载点后,在创建容器时,使用 -v参数将挂载点与宿主机关联起来。
使用方式:
# 将/usr/local/myapp目录设置为挂载点,多个挂载点可以编写多个VOLUME指令,或者在一个VOLUME指令中多个挂载点用空格分开
VOLUME /usr/local/myapp映射方式:
在创建容器时,将宿主机目录与容器挂载点关联起来
docker --name myapp -v /usr/local/app/myapp:/usr/local/myapp -d app-images
这样的话容器访问容器内/usr/local/myapp目录相当于访问宿主机/usr/local/app/myapp目录
便于容器数据的外挂,配置文件的外挂,日志的外挂,便于容器数据安全
USER
用于指定在容器中运行的进程的用户或用户组身份,它可以用于设置容器中运行进程的用户权限和安全性。
# 使用示例
USER <用户名>[:用户组]
# 给容器设置用户为fei
USER fei
# 给容器设置用户组为fg
USER :fg
# 给容器设置用户和用户组
USER fei:fg**注意:**USER指令指的是切换到USER所指的用户来运行命令,所有要么在镜像构建指令一开始就设置用户,要么在创建容器时使用 -u参数设置用
docker --name myapp -u fei:fg -d myadd-image
ARG
定义容器中的变量,用法和ENV类似,但是ENV的作用范围可以到镜像外面,给运行起来的容器设置变量,而ARG只作用在镜像内部,充当变量使用,当时可以在创建容器时使用–build-arg对参数进行修改。
所以:当不需要将变量注入到环境变量时,推荐使用ARG,如果要使用到环境变量,推荐使用ENV。
ONBUILD
ONBUILD
是 Dockerfile 中的一个指令,用于定义构建触发器,当一个镜像被用作另一个镜像的基础镜像时,基础镜像中的ONBUILD
指令将会在子镜像的构建过程中触发执行。什么意思呢,就是在a镜像中设置ONBUILD RUN apt-get update时,然后b镜像使用a镜像作为其基础镜像,当b镜像在构建时,ONBUILD RUN apt-get update这条指令便会被运行,相当于在b镜像中运行RUN apt-get update,ONBUILD这条指令后面可以运行任何Dockerfile指令。
这个指令非常有作用的,在镜像构建时被触发,这样就可以在基础镜像中写一些未来将要触发的命令。
常见应用构建成Dockerfile
使用Centos7基础镜像构建java8环境
# 设置centos7作为基础镜像 |
- 在Dockerfile根目录执行命令构建镜像:docker build -t my-java8 .
- 通过上面构建的镜像我们发现一些问题,一是使用centos7作为基础镜像的话,centos7的很多功能我们用不到,白白占用了空间;二是我们在Docker容器中一般都是运行编译打包好的代码,以Java项目为例,我们只需要使用到jre;所以这些就是可以优化的地方,下面我使用一个完整的前后端分离项目进行实操,如何通过多阶段构建将项目打包部署到Docker容器中。
前后端部署到Docker+镜像构建优化(实战)
- 项目下载地址
- 注意:以下只是打包,如果需要运行需要更改一些数据源地址,aliyun相关key等,自行抉择
- 分别在after-end、client、manage目录下编写Dockerfile文件
编写after-end后端的Dockerfile
- 进入after-end目录下,编写Dockerfile
# 构建after_end,SpringBoot后端代码 |
- 运行脚本打包脚本:docker build -t after-end .
编写front-client前端用户端Dockerfile
- 进入client目录下,编写Dockerfile
# 构建front-clien,Vue前端前台代码 |
- 运行脚本打包脚本:docker build -t front-client .
编写front-manage前端管理端Dockerfile
- 进入manage目录下,编写Dockerfile
# 构建front-clien,Vue前端前台代码 |
- 运行脚本打包脚本:docker build -t front-manage .