Dockerfile 指令详解与实战指南

Dockerfile 相关指令

dockerfile 文件中的常见指令

bash 复制代码
ADD
COPY
ENV
EXPOSE
FROM
LABEL
VOLUME
USER
VOLUME
WORKDIR

FROM:指定基础镜像

定制镜像,需要先有一个基础镜像,在这个基础镜像上进行定制。
FROM 就是指定基础镜像,此指令通常必需放在Dockerfile文件第一个非注释行。后续的指令都是运行于此基准镜像所提供的运行环境。

基础镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件.如果找不到指定的镜像文件, docker build会返回一个错误信息。
如何选择合适的镜像呢?

对于不同的软件官方都提供了相关的docker镜像,比如:nginx、redis、mysql、httpd、tomcat等服务类的镜像,也有操作系统类,如:centos、ubuntu、debian等。建议使用官方镜像,比较安全。

格式

bash 复制代码
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

#说明:  
--platform 指定镜像的平台,比如:  linux/amd64, linux/arm64, or windows/amd64
tag 和 digest是可选项,如果不指定,默认为latest

说明:关于scratch镜像

bash 复制代码
FROM scratch
参考链接:
https://hub.docker.com/_/scratch?tab=description
https://docs.docker.com/develop/develop-images/baseimages/
该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像
该镜像在构建基础镜像(例如debian和busybox)或超最小镜像(仅包含一个二进制文件及其所需内容,例
如:hello-world)的上下文中最有用。

示例

bash 复制代码
FROM scratch #所有镜像的起源镜像,相当于JAVA中Object类
FROM ubuntu
FROM ubuntu:bionic
FROM debian:buster-slim

LABEL:指定镜像元数据

可以指定镜像元数据,如:镜像作者等。

bash 复制代码
LABEL <key>=<value> <key>=<value> <key>=<value> 

示例

bash 复制代码
LABEL maintainer="caoge <ywxe@aliyun.com>"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL org.opencontainers.image.authors="caoge"

一个镜像可以有多个label,还可以写在一行中,即多标签写法,可以减少镜像的的大小。

多标签写法

bash 复制代码
#一行格式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
#多行格式
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

docker inspect 命令可以查看LABEL

示例

bash 复制代码
"Labels": {
                "LABEL": "org.opencontainers.image.authors=caoge",
                "maintainer": "caoge",
                "org.opencontainers.image.authors": "caoge",
                "org.opencontainers.image.ref.name": "ubuntu",
                "org.opencontainers.image.version": "24.04"
            }

RUN:执行 shell 命令

RUN 指令用来在构建镜像阶段需要执行 FROM 指定镜像所支持的Shell命令。

通常各种基础镜像一般都支持丰富的shell命令。

注意:RUN 可以写多个,每一个RUN指令都会建立一个镜像层,所以尽可能合并成一条指令,比如将多个 shell命令通过 && 连接一起成为在一条指令。

每个RUN都是独立运行的,和前一个RUN无关。

bash 复制代码
#shell 格式: 相当于 /bin/sh -c  <命令>  此种形式支持环境变量
RUN <命令>

#exec 格式: 此种形式不支持环境变量,注意:是双引号,不能是单引号
RUN ["executable","param1","param2"...]

#exec格式可以指定其它shell
RUN ["/bin/bash","-c","echo hello wang"]

说明

bash 复制代码
shell格式中,<command>通常是一个shell命令,且以"/bin/sh -c" 来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号。

exec格式中的参数是一个JSON格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指定的命令不会以"/bin/sh -c"来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/bash", "-c", "<executable>", "<param1>"]

示例

bash 复制代码
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
RUN ["/bin/bash", "-c", "echo hello world"]
RUN yum -y install epel-release \
      && yum -y install nginx \
      && rm -rf /usr/share/nginx/html/*
      &&echo "<h1> docker test nginx </h1>" > /usr/share/nginx/html/index.html
RUN sed -i 's|mirrorlist=http://mirrorlist.centos.org|#mirrorlist=http://mirrorlist.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo 
&& sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://mirrors.aliyun.com|g' /etc/yum.repos.d/CentOS-Base.repo

多个前后RUN命令无法独立和shell命令不同

bash 复制代码
#world.txt并不存放在/app内
RUN cd /app
RUN echo "hello" > world.txt

ENV:设置环境变量

ENV 可以定义环境变量和值,会被后续指令(如:ENV,ADD,COPY,RUN等)通过 K E Y 或 KEY或 KEY或{KEY}进行引用,并在容器运行时保持。

bash 复制代码
#变量赋值格式1
ENV <key> <value>   #此格式只能对一个key赋值,<key>之后的所有内容均会被视作其<value>的组成部分

#变量赋值格式2
ENV <key1>=<value1> <key2>=<value2> <key3>=<value3>\  #此格式可以支持多个key赋值,定义多个变量建议使用,减少镜像层   

#如果<value>中包含空格,可以以反斜线\进行转义,也可通过对<value>加引号进行标识;另外,反斜线也可用于续行

#只使用一次变量
RUN <key>=<value> <command>

#引用变量
RUN $key .....

#变量支持高级赋值格式
${key:-word}
${key:+word}

如果运行容器时如果需要修改变量,可以执行下面通过基于 exec 机制实现。

注意:下面方式只影响容器运行时环境,而不影响构建镜像的过程,即只能覆盖docker run时的环境变量,而不会影响docker build时环境变量的值。

格式

bash 复制代码
[root@ubuntu2404 ubuntu]#docker run --help
  -e, --env list                         Set environment variables
      --env-file list                    Read in a file of environment variables

两种格式功能相同

bash 复制代码
#格式1
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy

#格式2
ENV myName John Doe
ENV myDog Rex The  Dog
ENV myCat fluffy

#示例
ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && ...
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

示例

bash 复制代码
[root@ubuntu2404 debian]#vim Dockerfile
FROM ubuntu:24.04
LABEL authors="caoge"
ENV NAME caoge ge

[root@ubuntu2404 debian]#docker build -t ubuntu-env:v1 .
[root@ubuntu2404 debian]#docker run -it --rm ubuntu-env:v1 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=df8caa690279
TERM=xterm
NAME=caoge ge
HOME=/root
[root@ubuntu2404 debian]#docker run -it --rm -e NAME=jiege ubuntu-env:v1 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=5fc2bc5a4cb8
TERM=xterm
NAME=jiege
HOME=/root

[root@ubuntu2404 debian]#vim env.txt
NAME=cao
TITLE=cto

[root@ubuntu2404 debian]#docker run -it --rm --env-file env.txt ubuntu-env:v1 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=40a56f540933
TERM=xterm
NAME=cao
TITLE=cto
HOME=/root

COPY:复制文本

复制本地宿主机的到容器中的。

bash 复制代码
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] #路径中有空白字符时,建议使用此格式

说明

  • 可以是多个,可以使用通配符,通配符规则满足Go的filepath.Match 规则filepath.Match 参考链接:https://golang.org/pkg/path/filepath/#Match
  • 必须是build上下文中的路径(为 Dockerfile 所在目录的相对路径),不能是其父目录中的文件
  • 如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制
  • 如果指定了多个,或在中使用了通配符,则必须是一个目录,且必须以 / 结尾
  • 可以是绝对路径或者是 WORKDIR 指定的相对路径
  • 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
  • 如果事先不存在,它将会被自动创建,这包括其父目录路径,即递归创建目录

示例

bash 复制代码
COPY hom* /mydir/    
COPY hom?.txt /mydir/

ADD:复制和解包文件

该命令可认为是增强版的COPY,不仅支持COPY,还支持自动解压缩。可以将复制指定的到容器中的。

格式

bash 复制代码
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] 

说明

  • 可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压)
  • 可以是绝对路径或者是 WORKDIR 指定的相对路径
  • 如果是目录,只复制目录中的内容,而非目录本身
  • 如果是一个 URL ,下载后的文件权限自动设置为600
  • 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为,如果以 / 结尾,则文件名URL指定的文件将被直接下载并保存为/< filename>
  • 如果是一个本地文件系统上的打包文件,如:gz, bz2 ,xz ,它将被解包 ,其行为类似于"tar -x"命令,但是通过URL获取到的tar文件将不会自动展开
  • 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则其被视作一个普通文件的内容将被直接写入

示例

bash 复制代码
ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # adds "test" to /absoluteDir/
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /

CMD:容器启动命令

一个容器中需要持续运行的进程一般只有一个,CMD 用来指定启动容器时默认执行的一个命令,且其运行结束后,容器也会停止,所以一般CMD 指定的命令为持续运行且为前台命令。

  • 如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT命令,那么开启容器时就会使用执行CMD指定的默认的命令。
  • 前面介绍过的 RUN 命令是在构建镜像时执行的命令,注意二者的不同之处。
  • 每个 Dockerfile 只能有一条 CMD 命令。如指定了多条,只有最后一条被执行。
  • 如果用户启动容器时用 docker run xxx 指定运行的命令,则会覆盖 CMD 指定的命令。
    格式
bash 复制代码
# 使用 exec 执行,推荐方式,第一个参数必须是命令的全路径,此种形式不支持环境变量
CMD ["executable","param1","param2"]

#在 /bin/sh 中执行,提供给需要交互的应用;此种形式支持环境变量
CMD command param1 param2

#提供给 ENTRYPOINT 命令的默认参数
CMD ["param1","param2"]

#示例
CMD ["nginx", "-g", "daemon off;"]

示例

bash 复制代码
[root@ubuntu2404 1.26]#cat Dockerfile
FROM centos:7
LABEL maintainer="caoge.com<ywxr@aliyun.com>"
RUN sed -i 's|mirrorlist=http://mirrorlist.centos.org|#mirrorlist=http://mirrorlist.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://mirrors.aliyun.com|g' /etc/yum.repos.d/CentOS-Base.repo && yum install -y gcc gcc-c++  pcre pcre-devel zlib zlib-devel openssl openssl-devel && useradd -r -s /sbin/nologin nginx && yum clean all
ADD nginx-1.26.3.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.26.3 && ./configure --prefix=/apps/nginx && make && make install && rm -rf /usr/local/src/nginx*
COPY index.html /apps/nginx/html/
RUN ln -s /apps/nginx/sbin/nginx /usr/sbin/nginx
EXPOSE 80 443
CMD ["nginx","-g","daemon off;"]
[root@ubuntu2404 1.26]#cat build.sh 
#!/bin/bash
docker build -t nginx-centos7:v1.2 .
[root@ubuntu2404 1.26]#./build.sh
[root@ubuntu2404 1.26]#docker images
REPOSITORY      TAG             IMAGE ID       CREATED        SIZE
nginx-centos7   v1.2            d36538f56f06   2 hours ago    346MB

ENTRYPOINT:入口点

功能类似于CMD,配置容器启动后执行的命令及参数。

bash 复制代码
# 使用 exec 执行
ENTRYPOINT ["executable", "param1", "param2"...]

# shell中执行
ENTRYPOINT command param1 param2
  • ENTRYPOINT 不能被 docker run 提供的参数覆盖,而是追加,即如果docker run 命令有参数,那么参数全部都会作为ENTRYPOINT的参数。
  • 如果docker run 后面没有额外参数,但是dockerfile中有CMD命令(即上面CMD的第三种用法),即Dockerfile中即有CMD也有ENTRYPOINT,那么CMD的全部内容会作为ENTRYPOINT的参数。
  • 如果docker run 后面有额外参数,同时Dockerfile中即有CMD也有ENTRYPOINT,那么docker run 后面的参数覆盖掉CMD参数内容,最终作为ENTRYPOINT的参数。
  • 可以通过docker run --entrypoint string 参数在运行时替换,注意string不要加空格
  • 使用CMD要在运行时重新写命令本身,然后在后面才能追加运行参数,ENTRYPOINT则可以运行时无需重写命令就可以直接接受新参数。
  • 每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效。
  • 通常会利用ENTRYPOINT指令配合脚本,可以为CMD指令提供环境配置。

示例

bash 复制代码
[root@ubuntu2404 ~]# cat Dockerfile
FROM centos:centos7.9-v10.0
LABEL maintainer="caoge"
ENV version=1.18.0
ADD nginx-$version.tar.gz /usr/local/
RUN cd /usr/local/nginx-$version && ./configure --prefix=/apps/nginx && make &&
make install && rm -rf /usr/local/nginx* && sed -i 's/.*nobody.*/user nginx;/'
/apps/nginx/conf/nginx.conf && useradd -r nginx
COPY index.html /apps/nginx/html
VOLUME ["/apps/nginx/html"]
EXPOSE 80 443
CMD ["-g","daemon off;"]
ENTRYPOINT ["/apps/nginx/sbin/nginx"]
#上面两条指令相当于ENTRYPOINT ["/apps/nginx/sbin/nginx","-g","daemon off;"]
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://127.0.0.1/

利用脚本实现指定环境变量动态生成配置文件内容

bash 复制代码
[root@ubuntu2404 ~]# echo 'Nginx Website in Dockerfile' > index.html
[root@ubuntu2404 ~]# cat Dockerfile
FROM nginx:1.16-alpine
LABEL maintainer="caoge"
ENV DOC_ROOT='/data/website/'
ADD index.html ${DOC_ROOT}
ADD entrypoint.sh /bin/
EXPOSE 80/tcp 8080
#HEALTHCHECK --start-period=3s CMD wget -0 - -q http://${IP:-0.0.0.0}:{PORT:-80}/
ENTRYPOINT [ "/bin/entrypoint.sh"]

#CMD指令的内容都成为了ENTRYPOINT的参数
CMD ["/usr/sbin/nginx","-g", "daemon off;"]

[root@ubuntu1804 ~]#cat entrypoint.sh
#!/bin/sh
#注意:alpine镜像没有bash,此处使用sh
cat > /etc/nginx/conf.d/www.conf <<EOF
server {
   server_name ${HOSTNAME:-"www.caoge.com"};
   listen ${IP:-0.0.0.0}:${PORT:-80};
   root   ${DOC_ROOT:-/usr/share/nginx/html};
}
EOF
exec "$@"
[root@ubuntu2404 ~]#chmod +x entrypoint.sh
[root@ubuntu2404 ~]#docker bulid -t nginx-alpin:v1.3 .

#方式2
[root@ubuntu2404 ~]# cat Dockerfile
FROM nginx-alpine:1.24.0-v4.0
LABEL maintainer="caoge"
RUN mkdir -p /data/website
#COPY index.html /data/website
COPY entrypoint.sh /
CMD ["nginx","-g","daemon off;"]
ENTRYPOINT ["/entrypoint.sh"]

[root@ubuntu2404 ~]# cat entrypoin.sh
#!/bin/sh
#注意:alpine镜像没有bash,此处使用sh
cat > /apps/nginx/conf/conf.d/www.conf <<EOF
server {
   server_name ${HOST:-"www.caoge.com"};
   listen ${IP:-0.0.0.0}:${PORT:-80};
   root   ${DOC_ROOT:-/apps/nginx/html};
}
EOF
echo ${HOST:-"www.caoge.com"} > ${DOC_ROOT:-/apps/nginx/html}/index.html
#nginx -g "daemon off;"
exec "$@"

ARG:构建参数

ARG指令在build 阶段指定变量,和ENV不同的是,容器运行时并不存在ARG定义的环境变量。

bash 复制代码
ARG <name>[=<default value>]

如果和ENV同名,ENV覆盖ARG变量。

可以用 docker build --build-arg <参数名>=<值> 来覆盖。

示例

bash 复制代码
[root@ubuntu2404 1.26]#cat Dockerfile 
FROM centos:7
ARG maintainer="caoge.com<ywxr@aliyun.com>"
RUN sed -i 's|mirrorlist=http://mirrorlist.centos.org|#mirrorlist=http://mirrorlist.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://mirrors.aliyun.com|g' /etc/yum.repos.d/CentOS-Base.repo && yum install -y gcc gcc-c++  pcre pcre-devel zlib zlib-devel openssl openssl-devel && useradd -r -s /sbin/nologin nginx && yum clean all
ADD nginx-1.26.3.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.26.3 && ./configure --prefix=/apps/nginx && make && make install && rm -rf /usr/local/src/nginx*
COPY index.html /apps/nginx/html/
RUN ln -s /apps/nginx/sbin/nginx /usr/sbin/nginx
EXPOSE 80 443
CMD ["nginx","-g","daemon off;"]

说明:arg和from

bash 复制代码
#FROM指令支持ARG指令放在第一个FROM之前声明变量

#示例:
ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

#在FROM之前声明的ARG在构建阶段之外,所以它不能在FROM之后的任何指令中使用。
#要使用在第一个FROM之前声明的ARG的默认值,请在构建阶段内使用没有值的ARG指令

#示例:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

VOLUME:匿名卷

在容器中创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等,默认会将宿主机上的目录挂载至VOLUME 指令指定的容器目录。即使容器后期被删除,此宿主机的目录仍会保留,从而实现容器数据的持久保存。

宿主机目录为

bash 复制代码
/var/lib/docker/volumes/<volume_id>/_data

语法

bash 复制代码
VOLUME <容器内路径>
VOLUME ["<容器内路径1>", "<容器内路径2>"...]

注意: 
<容器内路径>如果在容器内不存在,在创建容器时会自动创建
<容器内路径>如果是存在的,同时目录内有内容,将会把此目录的内容复制到宿主机的实际目录

注意

Dockerfile中的VOLUME实现的是匿名数据卷,无法指定宿主机路径和容器目录的挂载关系,通过docker rm -fv <容器ID> 可以删除容器的同时删除VOLUME指定的卷。

在容器中创建两个/data1,/data2挂载点

bash 复制代码
VOLUME [ "/data1","/data2" ] 
bash 复制代码
[root@ubuntu2404 1.26]#vim Dockerfile
FROM centos:7
LABEL maintainer="caoge.com<ywxr@aliyun.com>"
RUN sed -i 's|mirrorlist=http://mirrorlist.centos.org|#mirrorlist=http://mirrorlist.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://mirrors.aliyun.com|g' /etc/yum.repos.d/CentOS-Base.repo && yum install -y gcc gcc-c++  pcre pcre-devel zlib zlib-devel openssl openssl-devel && useradd -r -s /sbin/nologin nginx && yum clean all
ADD nginx-1.26.3.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.26.3 && ./configure --prefix=/apps/nginx && make && make install && rm -rf /usr/local/src/nginx*
COPY index.html /apps/nginx/html/
VOLUME ["/data","/data"]
RUN ln -s /apps/nginx/sbin/nginx /usr/sbin/nginx
EXPOSE 80 443
CMD ["nginx","-g","daemon off;"]
[root@ubuntu2404 1.26]# docker build -t nginx-centos7:v1.3 .
[root@ubuntu2404 1.26]# docker run -d --name nginx7 nginx-centos7:v1.3
[root@ubuntu2404 1.26]# docker exec -it nginx7 bash
[root@4597c9f6498d /]# cp /etc/issue /data/f1
[root@4597c9f6498d /]# exit

[root@ubuntu2404 ~]#docker inspect nginx7
"Mounts": [
            {
                "Type": "volume",
                "Name": "56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c",
                "Source": "/var/lib/docker/volumes/56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
[root@ubuntu2404 ~]#tree /var/lib/docker/volumes/56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c/_data
/var/lib/docker/volumes/56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c/_data
└── f1
[root@ubuntu2404 ~]#cat /var/lib/docker/volumes/56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c/_data/f1 
\S
Kernel \r on an \m

EXPOSE:暴露端口

指定服务端的容器需要对外暴露(监听)的端口号,以实现容器与外部通信。

EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会真正暴露端口,即不会自动在宿主进行端口映射。因此,在启动容器时需要通过 -P 或 -p ,Docker 主机才会真正分配一个端口转发到指定暴露的端口才可使用。

**注意:即使 Dockerfile 没有 EXPOSE 端口指令,也可以通过docker run -p 临时暴露容器内程序真正监听的端口,**所以EXPOSE 相当于指定默认的暴露端口,可以通过docker run -P 进行真正暴露。

bash 复制代码
EXPOSE <port>[/ <protocol>] [<port>[/ <protocol>] ..]

#说明
<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议

示例

bash 复制代码
EXPOSE 80 443
EXPOSE 11211/udp 11211/tcp
bash 复制代码
[root@ubuntu2404 1.26]#cat Dockerfile 
FROM centos:7
LABEL maintainer="caoge.com<ywxr@aliyun.com>"
RUN sed -i 's|mirrorlist=http://mirrorlist.centos.org|#mirrorlist=http://mirrorlist.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://mirrors.aliyun.com|g' /etc/yum.repos.d/CentOS-Base.repo && yum install -y gcc gcc-c++  pcre pcre-devel zlib zlib-devel openssl openssl-devel && useradd -r -s /sbin/nologin nginx && yum clean all
ADD nginx-1.26.3.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.26.3 && ./configure --prefix=/apps/nginx && make && make install && rm -rf /usr/local/src/nginx*
COPY index.html /apps/nginx/html/
VOLUME ["/data","/data"]
RUN ln -s /apps/nginx/sbin/nginx /usr/sbin/nginx
EXPOSE 80 443
CMD ["nginx","-g","daemon off;"]
[root@ubuntu2404 1.26]#docker build -t nginx-centos7:v1.2 .
[root@ubuntu2404 1.26]#docker run -d --name nginx7 nginx-centos7:v1.2
[root@ubuntu2404 1.26]#docker ps -a
CONTAINER ID   IMAGE                COMMAND                  CREATED          STATUS          PORTS             NAMES
4597c9f6498d   nginx-centos7:v1.3   "nginx -g 'daemon of..."   15 minutes ago   Up 15 minutes   80/tcp, 443/tcp   nginx7
[root@ubuntu2404 1.26]#curl 172.17.0.3
hello nginx

WORKDIR:指定工作目录

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,当容器运行后,进入容器内WORKDIR指定的默认目录。

WORKDIR 指定工作目录(或称当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会自行创建。

注意:在RUN可以生效,在CMD、ENTRYPOINT可能会有问题。

bash 复制代码
WORKDIR /path/to/workdir

示例

bash 复制代码
#两次RUN独立运行,不在同一个目录
RUN cd /app 
RUN echo "hello" > world.txt

#如果想实现相同目录
RUN cd /app && echo "hello" > world.txt

#可以使用WORKDIR
WORKDIR /app
RUN echo "hello" > world.txt

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。

示例

bash 复制代码
WORKDIR /a 
WORKDIR b 
WORKDIR c
RUN pwd

#则最终路径为 /a/b/c

ONBUILD:子镜像引用父镜像的指令

可以用来配置当构建当前镜像的子镜像时,会自动触发执行的指令,但在当前镜像构建时,并不会执行,即延迟到子镜像构建时才执行。

bash 复制代码
ONBUILD [INSTRUCTION]

示例

bash 复制代码
ONBUILD ADD http://www.caoge.com/wp-content/uploads/2024/09/logo.png /data/
ONBUILD RUN rm -rf /*
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

如果基于 image-A 创建新的镜像image-B时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行ONBUILD 指令内容,等价于在后面添加了两条指令。

bash 复制代码
FROM image-A

#Automatically run the following
ADD http://www.caoge.com/wp-content/uploads/2024/09/logo.png /data
RUN /usr/local/bin/python-build --dir /app/src

说明

尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我能套,且不会触发FROM和 MAINTAINER指令。

使用ONBUILD指令的镜像,推荐在标签中注明,例如 ubuntu2404-onbuild。

USER:指定当前用户

指定运行容器的用户名或 UID,在后续dockerfile中的 RUN ,CMD和ENTRYPOINT指令时使用此用户。

当服务不需要管理员权限时,可以通过该命令指定运行用户,这个用户必须是事先建立好的,否则无法切换。如果没有指定 USER,默认是 root 身份执行。

bash 复制代码
USER <user>[:<group>] 
USER <UID>[:<GID>]

示例

bash 复制代码
RUN groupadd -r mysql && useradd -r -g mysql mysql
USER mysql

HEALTHCHECK:健康检查

bash 复制代码
https://docs.docker.com/engine/reference/builder/#healthcheck

检查容器的健康性

bash 复制代码
HEALTHCHECK [选项] CMD <命令> #设置检查容器健康状况的命令,如果命令执行失败,则返回1,即unhealthy
HEALTHCHECK NONE #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK 支持下列选项:  
--interval=<间隔>   #两次健康检查的间隔,默认为 30 秒
--timeout=<时长>    #健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认30 秒
--retries=<次数>    #当连续失败指定次数后,则将容器状态视为 unhealthy,默认3次
--start-period=<FDURATION> #default: 0s

 #检查结果返回值:
 0  #success    the container is healthy and ready for use
 1  #unhealthy   the container is not working correctly
 2  #reserved   do not use this exit code

示例

bash 复制代码
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/
bash 复制代码
#如果健康性检查成功,STATUS会显示 (healthy) 
[root@ubuntu2404 1.26]#docker ps -a
CONTAINER ID   IMAGE                COMMAND                  CREATED          STATUS                    PORTS             NAMES
0a281a5c2cfe   nginx-centos7:v1.4   "nginx -g 'daemon of..."   21 seconds ago   Up 20 seconds (healthy)   80/tcp, 443/tcp   centos
#如果健康性检查成功,STATUS会显示 (unhealthy) 
[root@ubuntu2404 1.26]#docker ps -a
CONTAINER ID   IMAGE                COMMAND                  CREATED          STATUS                    PORTS             NAMES
0a281a5c2cfe   nginx-centos7:v1.4   "nginx -g 'daemon of..."   21 seconds ago   Up 20 seconds (unhealthy)   80/tcp, 443/tcp   centos
#查看健康状态
[root@ubuntu2404 1.26]#docker inspect -f '{{.State.Health.Status}}' centos
healthy
#以json格式显示
[root@ubuntu2404 1.26]#docker inspect -f '{{json .State.Health}}' centos
{"Status":"healthy","FailingStreak":0,"Log":[{"Start":"2025-03-27T14:37:22.674293332Z","End":"2025-03-27T14:37:22.727509618Z","ExitCode":0,"Output":"hello nginx\n"},{"Start":"2025-03-27T14:37:27.728348411Z","End":"2025-03-27T14:37:27.77337279Z","ExitCode":0,"Output":"hello nginx\n"},{"Start":"2025-03-27T14:37:32.775876225Z","End":"2025-03-27T14:37:32.842151981Z","ExitCode":0,"Output":"hello nginx\n"},{"Start":"2025-03-27T14:37:37.843702282Z","End":"2025-03-27T14:37:37.916123913Z","ExitCode":0,"Output":"hello nginx\n"},{"Start":"2025-03-27T14:37:42.917641054Z","End":"2025-03-27T14:37:42.97324198Z","ExitCode":0,"Output":"hello nginx\n"}]}
[root@ubuntu2404 1.26]#docker inspect -f '{{json .State.Health}}' centos|python3 -m json.tool
{
    "Status": "healthy",
    "FailingStreak": 0,
    "Log": [
        {
            "Start": "2025-03-27T14:38:18.384392361Z",
            "End": "2025-03-27T14:38:18.436155727Z",
            "ExitCode": 0,
            "Output": "hello nginx\n"
        },
        {
            "Start": "2025-03-27T14:38:23.437092106Z",
            "End": "2025-03-27T14:38:23.504983451Z",
            "ExitCode": 0,
            "Output": "hello nginx\n"
        },
        {
            "Start": "2025-03-27T14:38:28.505569904Z",
            "End": "2025-03-27T14:38:28.568333597Z",
            "ExitCode": 0,
            "Output": "hello nginx\n"
        },
        {
            "Start": "2025-03-27T14:38:33.569342355Z",
            "End": "2025-03-27T14:38:33.616883295Z",
            "ExitCode": 0,
            "Output": "hello nginx\n"
        },
        {
            "Start": "2025-03-27T14:38:38.617701799Z",
            "End": "2025-03-27T14:38:38.686184081Z",
            "ExitCode": 0,
            "Output": "hello nginx\n"
        }
    ]
}

.dockerignore文件

官方文档:https://docs.docker.com/engine/reference/builder/#dockerignore-file 与.gitignore文件类似,生成构建上下文时Docker客户端应忽略的文件和文件夹指定模式 .dockerignore 使用 Go 的文件路径规则 filepath.Match。

参考链接:https://golang.org/pkg/path/filepath/#Match

完整语法

bash 复制代码
#     #以#开头的行为注释
*     #匹配任何非分隔符字符序列
?     #匹配任何单个非分隔符
\\     #表示  \

**    #匹配任意数量的目录(包括零)例如,**/*.go将排除在所有目录中以.go结尾的所有文件,包括构建上下文的根。
!     #表示取反,可用于排除例外情况

示例

bash 复制代码
#排除 test 目录下的所有文件
test/*

#排除 md 目录下的 xttblog.md 文件
md/xttblog.md

#排除 xttblog 目录下的所有 .md 的文件
xttblog/*.md

#排除以 xttblog 为前缀的文件和文件夹
xttblog?

#排除所有目录下的 .sql 文件夹
**/*.sql

#除了README的md不排外,排除所有md文件,但不排除README-secret.md
*.md
!README*.md
README-secret.md

#除了所有README的md文件以外的md都排除
*.md
README-secret.md
!README*.md

Dockerfile 构建过程和指令总结

Dockerfile 构建过程

从基础镜像运行一个容器,执行一条指令,对容器做出修改,执行类似docker commit的操作,提交一个新的中间镜像层(可以利用中间层镜像创建容器进行调试和排错)

再基于刚提交的镜像运行一个新容器,执行Dockerfile中的下一条指令,直至所有指令执行完毕。
Dockerfile 指令总结

bash 复制代码
BUILD				RUN					BOTH
FROM				CMD					WORKDIR
TABEL				VOLUME				USER
COPY				EXPOSE				ENV
ADD					ENTRYPOINT	
RUN		
ONBUILD		
.dockerignore		

构建镜像docker build 命令

docker build命令使用Dockerfile文件创建镜像。

格式

bash 复制代码
docker buildx build [OPTIONS] PATH | URL | -

说明:  
PATH | URL | -     #可以使是本地路径,也可以是URL路径。若设置为 - ,则从标准输入获取Dockerfile的内容
-f, --file string  #Dockerfile文件名,默认为 PATH/Dockerfile                   
--force-rm			 	 #总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache   			 #不使用之前构建中创建的缓存
-q  --quiet=false  #不显示Dockerfile的RUN运行的输出结果          
--rm=true  			   #创建镜像成功时,删除临时容器
-t --tag list      #设置注册名称、镜像名称、标签。格式为 <注册名称>/<镜像名称>:<标签>(标签默认为latest)

示例

bash 复制代码
docker build .
docker build /usr/local/src/nginx
docker build -f /path/to/a/Dockerfile .
docker build -t shykes/myapp .
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
docker build -t test/myapp .
docker build -t nginx:v1 /usr/local/src/nginx

查看镜像的构建历史

bash 复制代码
docker history 镜像ID

示例

bash 复制代码
[root@ubuntu2404 1.26]#docker history nginx-centos7:v1.3
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
d779d406ee5b   7 hours ago   CMD ["nginx" "-g" "daemon off;"]                0B        buildkit.dockerfile.v0
<missing>      7 hours ago   EXPOSE map[443/tcp:{} 80/tcp:{}]                0B        buildkit.dockerfile.v0
<missing>      7 hours ago   RUN /bin/sh -c ln -s /apps/nginx/sbin/nginx ...   0B        buildkit.dockerfile.v0
<missing>      7 hours ago   VOLUME [/data /data]                            0B        buildkit.dockerfile.v0
<missing>      7 hours ago   COPY index.html /apps/nginx/html/ # buildkit    12B       buildkit.dockerfile.v0
<missing>      7 hours ago   RUN /bin/sh -c cd /usr/local/src/nginx-1.26....   3.96MB    buildkit.dockerfile.v0
<missing>      7 hours ago   ADD nginx-1.26.3.tar.gz /usr/local/src # bui...   7.39MB    buildkit.dockerfile.v0
<missing>      7 hours ago   RUN /bin/sh -c sed -i 's|mirrorlist=http://m...   131MB     buildkit.dockerfile.v0
<missing>      7 hours ago   LABEL maintainer=caoge.com<ywxr@aliyun.com>     0B        buildkit.dockerfile.v0
<missing>      3 years ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      3 years ago   /bin/sh -c #(nop)  LABEL org.label-schema.sc...   0B        
<missing>      3 years ago   /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4...   204MB     

从镜像导出对应的 Dockerfile

想查看一个其它组织的Docker镜像是怎么构建的,可以通过whaler实现。

bash 复制代码
https://github.com/P3GLEG/Whaler

docker安装whaler

bash 复制代码
docker pull pegleg/whaler
docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock:ro pegleg/whaler -sV=1.36 nginx:latest

docker build --rm -t pegleg/whaler .
alias whaler="docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock:ro pegleg/whaler"
whaler -sV=1.36 nginx:latest

查看tomcat的dockerfile内容

bash 复制代码
[root@ubuntu2404 ~]#whaler tomcat-web:app1
Analyzing tomcat-web:app1
Docker Version: 28.3.3
GraphDriver: overlay2
Environment Variables
|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/jdk/bin
|JAVA_HOME=/usr/local/jdk
|JRE_HOME=/usr/local/jdk/jre
|CLASSPATH=/usr/local/jdk/lib/:/usr/local/jdk/jre/lib/
|TZ=Asia/Shanghai
|LANG=en_US.UTF-8
|TERM=xterm
|TOMCAT_MAJOR_VERSION=9
|TOMCAT_MINOR_VERSION=9.0.108
|CATALINA_HOME=/apps/tomcat
|APP_DIR=/apps/tomcat/webapps

Open Ports
|8009
|8080

Image user
|User is root

Potential secrets:
unable to parse history from json file 

这些就是 Dockerfile 中一些基本的指令介绍,利用这些指令可以构建出功能丰富的 Docker 镜像。理解并正确使用它们对于高效地进行 Docker 容器化应用开发至关重要。希望这篇指南能够帮助你更好地理解和编写 Dockerfile。

相关推荐
风清再凯3 小时前
05-企业级私有仓库Harbor
docker
foundbug9993 小时前
查看nginx日志文件
linux·nginx·github
靠近彗星3 小时前
1.5操作系统引导
java·linux·服务器·操作系统
北'辰3 小时前
VMware安装Kali-Linux
linux·网络安全
失因3 小时前
Docker 镜像结构与 Dockerfile 案例
运维·docker·云原生·容器·tomcat
代码会说话4 小时前
i2c通讯
android·linux·嵌入式硬件·嵌入式
九皇叔叔4 小时前
Docker 镜像维护指南:从配置优化到 MySQL 实战运行
mysql·adb·docker
yong99904 小时前
Linux安装JDK1.8 & tomcat & MariaDB(MySQL删减版)
linux·tomcat·mariadb
DrugOne4 小时前
GAMESS 在 Ubuntu 24.04 平台上的编译与配置
linux·python·ubuntu·drugone