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 <参数名>[=<默认值>]