Dockerfile

Dockerfile是啥

Dockerfile 是一个文本文件,其中包含了构建docker 镜像的所有指令

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明

通过定义一系列命令和参数,Dockerfile 指导 Docker 构建一个自定义的镜像

docker build

docker build 会根据Dockerfile中的指令,依次执行并构建镜像,指令如下:

docker build (参数) [image_name]:[tag] [Path]

参数说明:
-f 指定Dockerfile的位置(如果不在当前目录下)
-t 设置镜像名称和可选的标签
--build-arg 设置构建时变量
--no-cache 不使用构建缓存

[Path]:Dockerfile 的位置,一般为 . ,表示在当前目录下寻找

注意:路径下有且仅能有一个Dockerfile文件
注意:以前是只能Dockerfile,现在dockerfile也可以了

dockerfile指令

Dockerfile 指令		说明

FROM				指定基础镜像,用于后续的指令构建。
MAINTAINER			指定Dockerfile的作者/维护者。(已弃用,推荐使用LABEL指令)
LABEL				添加镜像的元数据,使用键值对的形式。
RUN					在构建过程中在镜像中执行命令。
CMD					指定容器创建时的默认命令。(可以被覆盖)
ENTRYPOINT			设置容器创建时的主要命令。(不可被覆盖)
EXPOSE				声明容器运行时监听的特定网络端口。
ENV					在容器内部设置环境变量。
ADD					将文件、目录或远程URL复制到镜像中。
COPY				将文件或目录复制到镜像中。
VOLUME				为容器创建挂载点或声明卷。
WORKDIR				设置后续指令的工作目录。
USER				指定后续指令的用户上下文。
ARG					定义在构建过程中传递给构建器的变量,可使用 "docker build" 命令设置。
ONBUILD				当该镜像被用作另一个构建过程的基础时,添加触发器。
STOPSIGNAL			设置发送给容器以退出的系统调用信号。
HEALTHCHECK			定义周期性检查容器健康状态的命令。
SHELL				覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。

接下来,会稍微细致的讲一下常用的指令

FROM 指令:往往是Dockerfile 的第一条,指定base image

FROM ubuntu # 指定ubuntu:latest作为基础镜像

MAINTAINER chenjx12 # 指定该dockerfile的作者为chenjx12(更换为LABEL)

LABEL maintainer=“chenjx12@foxmail.com” # 指定作者chenjx12 LABEL =

RUN echo ‘这是一个本地构建的镜像’ > /tmp/1.txt

用于执行后面跟着的命令行命令,RUN 有两种格式:

  • shell格式

    RUN <终端命令行命令>

  • exec格式

    RUN [“可执行文件”, “参数1”, “参数2”]

    例如:

    RUN ["./test.php", “dev”, “offline”] 等价于 RUN ./test.php dev offline

注意:Dockerfile每次执行都会在docker镜像上新建一层,过多无意义的层会造成镜像膨胀过大

多个RUN指令可以 && 符号连接命令,这样执行后,只会创建 1 层镜像

CMD /bin/bash == CMD [“/bin/bash”]

即运行镜像时默认执行/bin/bash,但是可以更改

比如:docker run -it ubuntu:latest cat /tmp/1.txt

这条指令运行容器时就会执行 cat /tmp/1.txt

ENTRYPOINT 同理,但是有细微的区别,后面会讲

EXPOSE 80

声明容器运行暴露听的特定的网络 80 端口,这样就可以访问在 80 端口运行的服务

仅仅只是声明端口

作用:

  • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口

ENV NAME = “chenjx12”

指定环境变量NAME为chenjx12

ARG 同理,也有细微区别,后讲

ADD Path/file

将 Path路径下的file文件/目录复制到镜像中

也可以使用 url

COPY 同,细微区别,后讲

WORKDIR /opt

指定工作目录为 /opt

也就是进入容器后的目录为 /opt

指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在

USER root

指定工作用户为root

也就是进入容器后的身份为root

用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)

只能用于切换用户,如果需要添加用户还需要再RUN 一条指令添加用户

VOLUME /data

挂载容器内的/data文件夹,在容器运行时,该目录自动挂载为匿名卷,任何向该目录写入数据的操作都不会被容器记录

保证容器存储层的无状态理念

定义匿名数据卷,在启动容器时忘记挂载数据卷,会自动挂载到匿名卷

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的
  • 避免容器不断变大

ONBUILD

用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

格式:

ONBUILD <其它指令>

CMD & ENTRYPOINT

首先这俩都可以使用shell格式和exec格式,都没差

主要区别在于,一个是默认执行(可以更改);一个是主要执行(不允许更改)

并且它们在传参上也有所不同

比如现在,我想运行一个ubuntu容器,让它帮我用curl命令查看一下本机的ip

先看看直接拉取一个ubuntu下来,怎么执行

# docker pull ubuntu:20.04
# docker run -it <id> /bin/bash

# apt-get update
# apt-get install -y curl

# curl -s http://ipinfo.io/ip
root@c20b356e8ec0:/# curl -s http://ipinfo.io/ip
47.120.34.132

这里就会回显你的ip地址,如果我加上 -i 参数,则会输出HTTP响应头的详细信息

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# curl -s http://ipinfo.io/ip -i
HTTP/1.1 200 OK
date: Thu, 03 Oct 2024 09:59:20 GMT
content-type: text/plain; charset=utf-8
Content-Length: 13
access-control-allow-origin: *
via: 1.1 google
strict-transport-security: max-age=2592000; includeSubDomains

那么我们自行构建镜像呢

# 咱接着来看看 ENTRYPOINT 版的,dockerfile_1
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
ENTRYPOINT ["curl", "-s", "http://ipinfo.io/ip"]

这样就行了,build一下运行容器试试

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker build -t curl_ip:test .
[+] Building 0.1s (6/6) FINISHED                                                                                                      docker:default
 => [internal] load build definition from dockerfile                                                                                            0.0s
 => => transferring dockerfile: 142B                                                                                                            0.0s
 => [internal] load metadata for docker.io/library/ubuntu:20.04                                                                                 0.0s
 => [internal] load .dockerignore                                                                                                               0.0s
 => => transferring context: 2B                                                                                                                 0.0s
 => [1/2] FROM docker.io/library/ubuntu:20.04                                                                                                   0.0s
 => CACHED [2/2] RUN apt update && apt install curl -y                                                                                          0.0s
 => exporting to image                                                                                                                          0.0s
 => => exporting layers                                                                                                                         0.0s
 => => writing image sha256:050f1c97507ca5e73b95c5659b4c9f23e249289e71bdad2bf101b2593e6116ba                                                    0.0s
 => => naming to docker.io/library/curl_ip:test                                                                                                 0.0s
[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker images
REPOSITORY    TAG        IMAGE ID       CREATED        SIZE
flask_web     latest     69ce5c1bb4e0   20 hours ago   866MB
curl_ip       test       050f1c97507c   30 hours ago   143MB
test_blog     latest     4deac4dd6f5b   41 hours ago   141MB
nginx         latest     605c77e624dd   2 years ago    141MB
ubuntu        20.04      ba6acccedd29   2 years ago    72.8MB
hello-world   latest     feb5d9fea6a5   3 years ago    13.3kB
centos        7.8.2003   afb6fca791e0   4 years ago    203MB

# 那个curl_ip就是构建出来的了,接着使用容器运行它

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker run -it 050
47.120.34.132

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker run curl_ip:test -i
HTTP/1.1 200 OK
date: Thu, 03 Oct 2024 10:09:24 GMT
content-type: text/plain; charset=utf-8
Content-Length: 13
access-control-allow-origin: *
via: 1.1 google
strict-transport-security: max-age=2592000; includeSubDomains

47.120.34.132

# 可以发现两个都可以

那么CMD呢,把dockerfile改一下就好了

FROM ubuntu:20.04
RUN apt update && apt install curl -y
CMD ["curl", "-s", "http://ipinfo.io/ip"]

接着构建,运行

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker run -it f28 
47.120.34.132

# 显然直接运行出一个容器是没问题的

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker run curl_ip:test -i
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "-i": executable file not found in $PATH: unknown.

# 加上 -i 参数,发现报错了

[root@iZf8z1f6vdo3jdz69xilspZ curl_ip]# docker run curl_ip:test curl -s http://ipinfo.io/ip -i
HTTP/1.1 200 OK
date: Fri, 04 Oct 2024 00:27:08 GMT
content-type: text/plain; charset=utf-8
Content-Length: 13
access-control-allow-origin: *
via: 1.1 google
strict-transport-security: max-age=2592000; includeSubDomains

# 当你自己在后面把完整的命令输入之后才行

这就是为什么说,CMD 指令可以被覆盖,是默认执行的命令

当你运行时的 COMMAND 有了其他指令,docker 就会优先处理其他指令,而CMD 就被覆盖了

而 ENTRYPOINT 指令不可被覆盖,是主要执行的命令

这时就不需要在运行容器时加其他指令,但是可以加参数

下面是来自[菜鸟教程的原文](Docker Dockerfile | 菜鸟教程 (runoob.com))

CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

格式:

CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 –entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式:

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

示例:

假设已通过 Dockerfile 构建了 nginx:test 镜像:

FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参 

1、不传参运行

docker run  nginx:test

容器内会默认运行以下命令,启动主进程。

nginx -c /etc/nginx/nginx.conf

2、传参运行

docker run  nginx:test -c /etc/nginx/new.conf

容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

nginx -c /etc/nginx/new.conf

ADD & COPY

ADD

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

COPY

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

格式:

COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

ENV & ARG

ENV

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

格式:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:

ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

ARG

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 –build-arg <参数名>=<值> 来覆盖。

格式:

ARG <参数名>[=<默认值>]