Docker 从入门到实践系列五 - Dockerfile文件
Posted ThinkWon
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker 从入门到实践系列五 - Dockerfile文件相关的知识,希望对你有一定的参考价值。
文章目录
什么是DockerFile文件
DockerFile是一个文本格式的配置文件,用户可以使用DockerFile来快速创建自定义的镜像。
DockerFile基本结构
Dockerfile分为四部分:基础镜像信息、维护者信息、 镜像操作指令和容器启动时执行指令
比如:
# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu:18.04
MAINTAINER docker_user docker_user@email.com
COPY . /app
RUN make /app
CMD python /app/app.py
每条指令创建一层:
-
FROM : 指明所基于的镜像名称,从ubuntu:18.04Docker镜像创建一个图层。
-
MAINTAINER:维护者信息,docker_user维护(可以不写)
-
COPY : 从Docker客户端的当前目录添加文件,镜像操作指令
-
RUN : 构建镜像时执行make命令,每运行一条RUN指令,镜像就添加新的一层,并提交(添加可写层)
-
CMD : 指定在容器中运行什么命令,用来指定运行容器时的操作命令
Docker镜像由只读层组成,每个只读层代表一个Dockerfile指令。这些层是堆叠的,每个层都是上一层的变化的增量
Docker可以通过读取Dockerfile指令来自动构建镜像
完整的小例子:
#在一个空目录下,新建一个名为 Dockerfile 文件
mkdir /usr/dockerfile -p
vim dockerfile-demo
#编辑 dockerfile
FROM nginx:latest
# 维护者 可以省略
MAINTAINER jourwon jourwon@docker.com
#启动容器
RUN mkdir /usr/share/nginx/html/ -p
RUN echo Hello DockerFile! > /usr/share/nginx/html/demo.html
#构建镜像 . : 根据当前上下文环境构建
docker build -f dockerfile-demo -t jourwon/nginx:v1 .
#运行
docker run --rm -d -it --network host jourwon/nginx:v1
浏览器访问
DockerFile指令详解
D ockerfile命令官方文档 常见命令详解:
1. FROM
指定所创建镜像的基础镜像,如果本地不存在,则默认会去Docker Hub下载指定镜像。命令格式如下:
FROM [--platform=<platform>] <image> [AS <name>]
Or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
Or
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
平时不用这么复杂的,只需如下即可
FROM <image>[:<tag>]
FROM centos:7.6.1810
任何Dockerfile中的第一条指令必须为FROM指令。并且,如果在同 一个Dockerfile中创建多个镜像,可以使用多个FROM指令(每个镜像一 次)。
2. MAINTAINER
指定维护者信息,格式为MAINTAINER。可以不写
MAINTAINER jourwon jourwon@docker.com
该信息会写入生成镜像的Author属性域中
3. RUN
容器构建时需要运行的命令
1、RUN
默认将在shell终端中运行命令,即/bin/sh-c
2、RUN [“executable”,“param1”,“param2”]。指令会被解析 为Json数组,因此必须用双引号。exec执行,不会启动shell环境
指定使用其他终端类型可以通过此方式实现,例如
RUN ["/bin/bash","-c","echo hello"]
每条RUN指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用\\来换行
在shell形式中,可以使用\\(反斜杠)将一条RUN指令继续到下一行。例如,考虑以下两行:
RUN /bin/bash -c 'source $HOME/.bashrc; \\
echo $HOME'
它们在一起等效于以下这一行:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
要使用’/bin/sh’以外的其他shell ,请使用exec 形式传入所需的shell 。例如:
RUN ["/bin/bash", "-c", "echo hello"]
注意 在JSON格式中,必须转义反斜杠。在Windows中,反斜杠是路径分隔符,这一点尤其重要。由于无效的JSON,以下行否则将被视为shell形式,并以意外的方式失败:
RUN ["c:\\windows\\system32\\tasklist.exe"]
此示例的正确语法为:
RUN ["c:\\\\windows\\\\system32\\\\tasklist.exe"]
4. CMD
CMD指令用来指定启动容器时默认执行的命令。它支持三种格式:
CMD 指令的格式和 RUN 相似:
- shell 格式:
CMD <命令>
- exec 格式(推荐):
CMD ["可执行文件","参数 1","参数 2"...]
- 参数列表格式:
CMD ["参数 1","参数 2"...]
。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体参数
注:DockerFile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换。
5. COPY
复制本地主机的下的内容到镜像中的路径下。目标路径不存在时,会自动创建格式如下:
COPY
src:为Dockerfile所在目录的相对路径、文件或目录dest:镜像中的目标路径,相对和绝对都可以
支持通配符和正则
COPY hom* /mydir/
COPY hom?.txt mydir/
当使用本地目录为源目录时,推荐使用COPY
6. ENV
用来在构建镜像过程中设置环境变量。这个其实就是用一个别名指定一个目录地址,然后后续RUN指令可以通过 $别名 的方式来使用这个目录地址。
格式为ENV或ENV=… ,ENV 指令有两种形式
第一种形式ENV 会将一个变量设置为一个值。第一个空格之后的整个字符串将被视为 -包括空格字符。该值将为其他环境变量解释,因此如果不对引号字符进行转义,则将其删除。
第二种形式ENV = … 允许一次设置多个变量。请注意,第二种形式在语法中使用等号= ,而第一种形式则不使用等号= 。与命令行解析一样,引号和反斜杠可用于在值中包含空格。
例如:
ENV myName="John Doe" myDog=Rex\\ The\\ Dog \\
myCat=fluffy
和
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
是一样的
ENV 从结果镜像运行容器时,使用设置的环境变量将保留。可以使用查看值docker inspect , 并使用更改它们docker run --env = 。
docker run --env<key>=<value> built_image
7. ADD
将宿主机目录下的文件拷贝到镜像且 ADD
命令会自动处理 URL
和解压 tar
压缩包,格式为
ADD hom* /mydir/
ADD hom?.txt /mydir/
ADD test.txt /absoluteDir/
ADD test.txt relativeDir/
其中可以是Dockerfile所在目录的一个相对路径(文件或目录),也可以是一个URL,还可以是一 个tar文件(如果为tar文件,会自 动解压到路径下)。可以是镜像内的绝对路径,或者相对于工作目录(WORKDIR)的相对路径
与COPY的区别
1、Dockerfile中的COPY指令和ADD指令都可以将主机上的资源复制或加入到容器镜像中,都是在 构建镜像的过程中完成的。
2、COPY指令和ADD指令的区别在于是否支持从远程URL获取资源。COPY指令只能从执行docker build所在的主机上读取资源并复制到镜像中。而ADD指令还支持通过URL从远程服务器读取资源并复制到镜像中。
3、满足同等功能的情况下,推荐使用COPY指令。ADD指令更擅长读取本地tar文件并解压缩
4、当要读取URL远程资源的时候,并不推荐使用ADD指令,而是建议使用RUN指令,在RUN指令 中执行wget 或curl命令
8. ENTRYPOINT
指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的 参数
ENTRYPOINT有两种形式:
-
在EXEC的形式,这是优选的形式
ENTRYPOINT ["executable", "param1", "param2"]
,这种执行的是executable param1 param2
-
带壳形式:
ENTRYPOINT command param1 param2
。这中其实执行的是/bin/sh -c command param1 param2
多个ENTRYPOINT 只有最后一条生效
ENTRYPOINT ["echo", "4"]
ENTRYPOINT ["echo", "5"]
ENTRYPOINT ["echo", "6"]
9. VOLUME
容器数据卷,用于数据保存和持久化工作。
创建一个数据卷挂载点,格式为VOLUME ["/data"]
FROM centos:7.6.1810
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
RUN echo "hello" > /myvol/greeting
可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保存的数据等
10. WORKDIR
指定在创建容器后,终端默认登陆进来的工作目录,即一个落脚点。
为后续的RUN、CMD和ENTRYPOINT指令配置工作目录格式为WORKDIR /path/to/workdir
可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则 会基于之前命令指定的路径
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最终的输出pwd命令这Dockerfile 将是/a/b/c
11. EXPOSE
声明镜像内服务所监听的端口格式为EXPOSE[…]
EXPOSE 指令通知Docker 容器在运行时监听指定的网络端口。可以指定端口是侦听TCP 还是
UDP ,如果未指定协议,则默认值为TCP 。
EXPOSE 22 80 8443
默认情况下, EXPOSE 假定为TCP 。还可以指定UDP :
EXPOSE 80/udp
要同时在TCP和UDP上公开,请包括以下两行:
EXPOSE 80/tcp
EXPOSE 80/udp
无论EXPOSE
设置如何,都可以在运行时使用该-p
标志覆盖它们。例如
docker run -p 80:80/tcp
DockerFile创建镜像
编写完成Dockerfile之后,可以通过docker build命令来创建镜像。
基本的格式为docker build[选项]内容路径,该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下的所有内容发送给Docker服务端,由服务端来创建镜像
如果使用非内容路径下的Dockerfile,可以通过-f选项来指定其路径
$ docker build -f /path/to/a/Dockerfile .
要指定生成镜像的标签信息,可以使用-t选项
docker build -t jourwon/ubuntu:v1 .
docker build 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录
镜像管理
Docker镜像由一系列层组成。每层代表镜像的Dockerfile中的一条指令。除最后一层外的每一层都是只 读的。考虑以下Dockerfile:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
该Dockerfile包含四个命令,每个命令创建一个层。该 FROM语句首先从ubuntu:18.04镜像创建只读层。
该COPY命令从Docker客户端的当前目录添加一些文件。
该RUN命令使用命令构建您的应用程序make。
最后一层指定在容器中运行什么命令。 运行镜像并生成容器时,可以在基础层之上添加一个新的可写层
(“容器层”)。对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写 入此可写容器层。
容器和镜像之间的主要区别是可写顶层。在容器中添加新数据或修改现有数据的所有写操作都存储在此 可写层中。删除容器后,可写层也会被删除。基础镜像保持不变。
因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对 同一基础镜像的访问,但具有自己的数据状态。下图显示了多个共享相同Ubuntu 18.04镜像的容器
查看镜像的分层信息
docker history 镜像ID
磁盘上容器大小
要查看正在运行的容器的大致大小,可以使用以下docker ps -s 命令。有两个不同的列与大小有关。
size :用于每个容器的可写层的数据量(在磁盘上)。
virtual size :容器使用的只读图像数据加上容器的可写层使用的数据量size。多个容器可以共享部分或全部只读图像数据。从同一图像开始的两个容器共享100%的只读数据,而具有不同图像 的两个容器(具有相同的层)共享这些公共层。因此,您不能只对虚拟大小进行总计。这高估了总 磁盘使用量,可能是一笔不小的数目。
我们可以通过Docker仓库来传输我们的镜像,也可以通过文件模式
docker save 镜像ID -o xxxx.tar 或(docker save xxxx > xxxx.tar)
docker load -i xxxx.tar 或docker (docker load < xxxx.tar)
docker diff 容器ID
docker commit 容器ID svendowideit/testimage:version4 # 直接保存容器
docker commit --change='CMD ["apachectl", "-DFOREGROUND"]' -c "EXPOSE 80" 容器ID
svendowideit/testimage:version4 # 将正在运行的容器添加几个层之后再保存】
DockerFile模版
编写建议:
-
从适当的基础镜像开始。例如,如果需要JDK镜像基于正式openjdk 镜像
-
使用多阶段构建。例如,可以使用该maven 镜像来构建Java应用程序,然后将其重置为该tomcat镜像并将Java构件复制到正确的位置以部署我们的应用程序,所有这些操作都在同一Dockerfile中。这意味着的最终镜像不包括构建所引入的所有库和依赖项,而仅包括运行它们所需的工件和环境。
-
通过最小化Dockerfile中RUN 单独命令的数量来减少镜像中的层数。可以通过将多个命令合并为RUN 一行并使用Shell的机制将它们组合在一起来实现此目的。虑以下两个片段。第一层在镜像中创建两层,而第二层仅创建一层。
#在镜像中创建两层
RUN apt-get -y update
RUN apt-get install -y python
#在镜像中创建一层
RUN apt-get -y update && apt-get install -y python
- 如果有多个具有很多共同点的图像,请考虑使用共享的组件创建自己的基础镜像,Docker只需要加载一次公共层,然后将它们缓存。这意味着我们的派生镜像将更有效地使用Docker主机上的内存,并更快地加载。
JDK镜像模版
dockerfile-jdk
FROM jourwon/centos/7.6/centos
ENV JAVA_HOME="/apps/jdk"
ENV PATH=${JAVA_HOME}/bin:$PATH
ADD ./jdk-8u251-linux-x64.tar.gz /apps/
RUN ln -s /apps/jdk1.8.0_251 /apps/jdk
ADD ./UnlimitedJCEPolicyJDK8/US_export_policy.jar /apps/jdk/jre/lib/security/
ADD ./UnlimitedJCEPolicyJDK8/local_policy.jar /apps/jdk/jre/lib/security/
ADD ./msyhbd.ttf /apps/jdk/jre/lib/fonts/
ADD ./msyh.ttf /apps/jdk/jre/lib/fonts/
CMD ["/bin/bash"]
构建命令:
#下载jce_policy-8.zip并解压到当前目录
wget http://pkgs-linux.cvimer.com/jdk/1.8/jce_policy-8.zip
unzip jce_policy-8.zip
#下载jdk-8u251-linux-x64.tar.gz、msyh.ttf、msyhbd.ttf
wget http://pkgs-linux.cvimer.com/jdk/1.8/jdk-8u251-linux-x64.tar.gz
wget http://pkgs-linux.cvimer.com/fonts/msyh.ttf
wget http://pkgs-linux.cvimer.com/fonts/msyhbd.ttf
#构建
docker build -f dockerfile-jdk -t jourwon/centos/7.6/jdk/1.8/jdk .
Docker数据持久化
- 创建一个卷,待后边使用
docker volume create test_volume
- 分别启动2个容器挂载上卷
# 在2个终端窗口启动2个容器
docker run -it --rm -v test_volume:/test nginx:latest /bin/bash
docker run -it --rm -v test_volume:/test nginx:latest /bin/bash
cd /test;
touch a.txt
ls /test
# 在两个容器中我们均可以看到我们创建的文件,这样我们就可以做到了在多个容器之间实现数据共享
挂载在容器/test 目录内创建。Docker 不支持容器内安装点的相对路径。 多个容器可以在同一时间段内使用相同的卷。如果两个容器需要访问共享数据,这将很有用。例如,如果一个容器写入而另一个容器读取数据。 卷名在驱动程序test必须唯一。这意味着我们不能将相同的卷名与两个不同的驱动程序一起使用。 如果们指定了当前test_volume程序上已在使用的卷名,则Docker会假定我们要重用现有卷,并且不会返回错误。如果开始无test_volume 则会创建这个卷
当然除了使用卷,也可以使用将宿主机的文件映射到容器的卷,命令类似,只不过不用提前创建卷,而且数据会映射到宿主机上
docker run -it --rm -v /root/test_volume:/test centos /bin/bash
注意如果宿主机上的目录可以不存在,会在启动容器的时候创建
构建过程解析
构建步骤
- 编写Dockerfile文件
- docker build
- docker run
基础知识
- 每条保留字指定都必须大写字母且后面要至少跟随一个参数
- 指定按照从上到下,顺序执行
#
表示注释- 每条指令都会创建一个新的镜像层,并对镜像进行提交
Dockerfile 执行流程
Docker 在执行 DockerFile 时有以下几个流程:
- Docker 从基础镜像运行一个容器,以这个基础镜像为开始,一层一层的加入新的内容。
- 执行类似
docker commit
的操作提交一个新的镜像层。 - Docker 再基于刚提交的镜像运行一个新容器。
- 执行 DockerFile 中的下一条指令直到所有指令都执行完成。
小结
从应用软件的角度来看,Dockerfile、Docker镜像和Docker容器分别代表软件的三个阶段:
- Dockerfile:软件的原材料。Dockerfile 定义了进程需要的一切东西。Dockerfile 设计的内容包括执行代码或者文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版本、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道时,需要考虑如何设计 namespace 的权限控制)等。
- Docker 镜像:软件的交付品。在用 Dockerfile 定义一个文件后,docker build 会产生一个 Docker 镜像。
- Docker 容器:软件的运行状态。当运行 Docker 镜像时生成容器,容器是直接提供服务的。
以上是关于Docker 从入门到实践系列五 - Dockerfile文件的主要内容,如果未能解决你的问题,请参考以下文章
Docker 从入门到实践系列五 - Dockerfile文件
Docker 从入门到实践系列四 - Docker 容器编排利器 Docker Compose