一、构建三步骤
Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本
- 编写Dockerfile文件
- docker build
- docker run
二、Dockerfile 构建过程解析
Dockerfile 内容基础知识
- 每条保留字指令都必须大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
#
表示注释- 每条指令都会创建一个新的镜像层,并对镜像进行提交
Docker执行Dockerfile的大致流程
- docker从基础镜像运行一个容器
- 执行一条指令并对容器作出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
三、Dockerfile 体系结构
- FROM 基础镜像,当前新镜像是基于哪个镜像的
- MAINTAINER 镜像维护者的姓名和邮箱地址
- RUN 容器构建时需要运行的命令
- EXPOSE 当前容器对外暴露出的端口
- WORKDIR 指定在创建容器后,终端默认登录的进来工作目录,一个落脚点
- ENV 用来在构建镜像过程中设置环境变量
- ADD 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
- COPY 类似ADD,拷贝文件和目录到镜像中,将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置
- VOLUMN 容器数据卷,用于数据保存和持久化工作
- CMD 指定一个容器启动时要运行的命令,- Dockerfile中可以有多个CMD命令,但只有最后一个生效,CMD会被docker run之后的参数替换
- ENTRYPOINT 指定一个容器启动时要运行的命令,其目的的CMD一样,都是在指定容器启动程序及参数
- ONBUILD 当构建一个被继承的Dockerfile时运行命令,父镜像在在继承后父镜像的onbuild被触发
每一次 RUN 命令都会在镜像上增加一层,每一层都会占用磁盘空间。因此,为了减少镜像大小起见,所有文件相关的操作,比如删除,释放和移动等,都需要尽可能地放在一个 RUN 指令中进行。
四、自定义镜像
1.编写 Dockerfile 文件
创建 Dockerfile 文件
# FROM 指定了一个基础镜像
FROM busybox:1.36.0
# MAINTAINER 指定维护者信息,已经过时,可以使用LABEL maintainer=<xxx>来替代。
LABEL maintainer="example@example.com"
# RUN 运行命令
RUN mkdir /app
# WORKDIR 配置工作目录,为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录
WORKDIR /app
# ENV 指定环境变量,可以在 docker run 的时候使用 -e 改变;会被固化到 image 的 config 里面
ENV APP_ENV=production
# ARG 指定镜像构建使用的参数(如版本号等),可以在build的时候,使用--build-args改变
ARG VERSION=1.0
RUN echo $VERSION
# ADD 制指定的 src 路径下的内容到镜像中的 dest 路径下,src 可以为 url 会自动下载,可以为 tar 文件,会自动解压
ADD https://mirrors.aliyun.com/apache/tomcat/tomcat-9/v9.0.95/src/apache-tomcat-9.0.95-src.tar.gz /app/down/
# COPY 复制本地主机的 src 路径下的内容到镜像中的 dest 路径下,但不会自动解压等
COPY t1.txt /app
# LABEL 指定生成镜像的元数据标签信息
LABEL version="1.0"
LABEL description="This is an example image"
# VOLUME 创建数据卷挂载点
VOLUME ["/data"]
# USER 指定运行容器时的用户名或UID
USER appuser
# EXPOSE 声明镜像内服务监听的端口
EXPOSE 80
# CMD 指定启动容器时默认的命令
CMD ["localhost"]
# ENTRYPOINT 指定镜像的默认入口
ENTRYPOINT ["/bin/ping","-c","3"]
2.构建
使用 docker build 命令构建镜像
# -f,--file 指定 dockerfile 路径
# --no-cache 构建镜像时不使用缓存
# -t,--tag 指定构建的镜像名和 tag
# --label 设置镜像的元数据
docker build -f Dockerfile --build-arg VERSION=2.0 --label type=abc --no-cache -t demo:1.0 .
3.运行
使用docker run命令运行镜像
docker run --name demo_1 -u root -e APP_ENV=development --add-host test.abc:1.2.3.4 --entrypoint sh -it demo:1.0
五、CMD 和 ENTRYPOINT
1.覆盖
在写 Dockerfile 时,ENTRYPOINT 或者 CMD 命令会自动覆盖之前的 ENTRYPOINT 或者 CMD 命令。
在docker镜像运行时,用户也可以在命令指定具体命令,覆盖在 Dockerfile 里的命令。
例如:
docker run --rm demo:1.0 hostname
,其中 hostname
命令 覆盖 Dockerfile 里的 CMD 命令
docker run --rm --entrypoint hostname demo:1.0
,其中 hostname
命令 覆盖 Dockerfile 里的 ENTRYPOINT 命令
2.shell 表示法和 exec 表示法
ENTRYPOINT 和 CMD 指令支持2种不同的写法: shell表示法和exec表示法
The CMD instruction has three forms:
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
ENTRYPOINT has two forms:
ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)
1) shell表示法:CMD executable param1 param2
如果 CMD /bin/sh
,docker ps 可以看到 /bin/sh -c /bin/sh
如果 CMD ping localhost
,docker ps 可以看到 /bin/sh -c 'ping localhost'
可以看出如果你的镜像里面没有 /bin/sh,docker 容器就不能运行
2) 一个更好的选择是用exec表示法:CMD ["executable","param1","param2"]
CMD指令后面用了类似于JSON的语法表示要执行的命令. 这种用法告诉docker不需要调用/bin/sh执行命令
如果CMD ["/bin/ping","localhost"]
,docker ps 可以看到 /bin/ping localhost
3.ENTRYPOINT 和 CMD 组合使用
组合使用 ENTRYPOINT 和 CMD,ENTRYPOINT 指定默认的运行命令,CMD 指定默认的运行参数.
CMD 给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行的命令。重点就是这个“默认”。意味着,如果 docker run 没有指定任何的执行命令或者 dockerfile 里面也没有 ENTRYPOINT,那么,就会使用 CMD 指定的默认的执行命令执行。同时也从侧面说明了 ENTRYPOINT 的含义,它才是真正的容器启动以后要执行命令。
FROM busybox:1.36.0
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
或:
FROM busybox:1.36.0
CMD ["localhost"]
ENTRYPOINT ["/bin/ping","-c","3"]
上面执行的命令是 ENTRYPOINT 和 CMD 指令拼接而成,ENTRYPOINT 和 CMD 同时存在时,docker 把 CMD 的命令拼接到 ENTRYPOINT 命令之后,拼接后的命令才是最终执行的命令。
因为 docker run 命令执行时,可以覆盖 CMD 指令的值,如果你希望这个 docker 镜像启动后不是 ping localhost,而是ping其他服务器,, 可以这样执行 docker run demo:1.0 docker.io
组合使用 ENTRYPOINT 和 CMD 命令式,确保你一定用的是Exec表示法
例如:
ENTRYPOINT /bin/ping -c 3
CMD localhost
# 实际执行的:/bin/sh -c '/bin/ping -c 3' /bin/sh -c localhost
ENTRYPOINT ["/bin/ping","-c","3"]
CMD localhost
# 实际执行的:/bin/ping -c 3 /bin/sh -c localhost
ENTRYPOINT /bin/ping -c 3
CMD ["localhost"]"
# 实际执行的:/bin/sh -c '/bin/ping -c 3' localhost
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
# 实际执行的:/bin/ping -c 3 localhost
六、COPY 和 ADD
作用:Dockerfile 中的 COPY 指令和 ADD 指令都可以将主机上的资源复制或加入到容器镜像中,都是在构建镜像的过程中完成的
区别:COPY 指令和 ADD 指令的唯一区别在于是否支持从远程 URL 获取资源。COPY 指令只能从执行 docker build 所在的主机上读取资源并复制到镜像中。而 ADD 指令还支持通过 URL 从远程服务器读取资源并复制到镜像中
满足同等功能的情况下,推荐使用COPY指令。ADD指令更擅长读取本地tar文件并解压缩
在设置了 WORKDIR 命令后,接下来的 COPY 和 ADD 命令中的相对路径就是相对于 WORKDIR 指定的路径
1. COPY 指令
COPY指令能够将构建命令所在的主机本地的文件或目录,复制到镜像文件系统
exec格式:COPY ["<src>",... "<dest>"]
shell格式:COPY <src>... <dest>
COPY 命令区别于 ADD 命令的一个用法是在 multistage 场景下
在 multistage 的用法中,可以使用 COPY 命令把前一阶段构建的产物拷贝到另一个镜像中,比如:
FROM busybox:1.36.0
WORKDIR /opt/
COPY app.txt .
FROM busybox:1.36.0
WORKDIR /app/
COPY --from=0 /opt/app.txt .
CMD ["echo", "app.txt"]
2. ADD 指令
ADD 除了不能用在 multistage 的场景下,ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类的功能:
- 解压压缩文件并把它们添加到镜像中
- 从 url 拷贝文件到镜像中
exec格式:ADD ["<src>",... "<dest>"]
shell格式:ADD <src>... <dest>
说明,对于从远程URL获取资源的情况,由于ADD指令不支持认证,如果从远程获取资源需要认证,则只能使用 RUN wget 或 RUN curl 替代
另外,如果源路径的资源发生变化,则该 ADD 指令将使 Docker Cache 失效,Dockerfile 中后续的所有指令都不能使用缓存。因此尽量将 ADD 指令放在 Dockerfile 的后面
事实上,当要读取URL远程资源的时候,并不推荐使用ADD指令,而是建议使用RUN指令,在RUN指令中执行wget或curl命令