使用Dockerfile构建镜像
由于格式和图片解析问题,为了更好体验可前往 阅读原文
前面我们学习了可以使用docker commit
命令式构建新的镜像,而此方式相对来说比较繁琐且对于旁人来说内部都是黑箱操作,无法了解制作的具体细节。很有可能很长时间后制作者也会对其忘却,且制作多镜像时相同阶段也无法共用已构建的产物,Dockerfile便可以完美解决这些问题。
概念
Dockerfile是用于定义Docker镜像的文件,是一种纯文本文件,其中包含了一系列的指令和配置信息,以描述如何构建和运行一个Docker容器。Dockerfile的内容可以基于现有的镜像进行扩展和定制化,也可以自己编写底层的操作系统配置和应用程序安装脚本。
镜像是分层构建的,每一层都对应了具体的构建指令,而Dockerfile中每一条指令也代表着每层的构建过程
作用
使用Dockerfile为镜像的构建使得更加便利,其优势大致如下:
- 自动化构建:使用Dockerfile可以自动构建Docker镜像,避免了手动操作的繁琐和错误
- 可重复性:通过Dockerfile可以确保每次构建出的镜像都是相同的,避免了因为不同的环境或依赖出现的差异
- 可维护性:使用Dockerfile可以清晰地描述容器的配置和依赖,方便维护和升级
- 灵活性:Dockerfile提供了灵活的自定义选项,可以根据需要对容器进行个性化配置
- 可分享性:Dockerfile可以作为代码一样进行版本控制和分享,方便团队协作和共享
初识Dockerfile
和大部分配置文件不同,Dockerfile其实就是个普通的文本文件,内部由一条条具体的构建指令组合而成,可以说Dockerfile是加强版的命令构建版本。以下是一个简单的Dockerfile:
FROM nginx:alpine
ENV DESC="DOCKERFILE_DEMO"
WORKDIR /var/nginx/html
RUN echo $DESC
COPY . .
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
以上配置指定了构建以nginx:alpine
镜像为基础,定义容器的工作目录为/var/nginx/html
,并将宿主机当前目录下的文件复制到容器的工作目录下,暴露端口80,并规定容器启动后让nginx以前台的形式运行。除此之外还定义了环境变量DESC
的值为DOCKERFILE_DEMO
,并打印其结果
接下来构建生成目标镜像:nginx:dockerfile
➜ docker build -t nginx:dockerfile .
[+] Building 15.3s (9/9) FINISHED=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 182B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/nginx:alpine 15.3s=> [1/4] FROM docker.io/library/nginx:alpine@sha256:eb05700fe7baa6890b74278e39b66b2ed1326831f9ec3ed4bdc6361a4ac2f333 0.0s=> [internal] load build context 0.0s=> => transferring context: 182B 0.0s=> CACHED [2/4] WORKDIR /var/nginx/html 0.0s=> CACHED [3/4] RUN echo DOCKERFILE_DEMO 0.0s=> [4/4] COPY . . 0.0s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:bcd9c0828a32f7f2816cc128ac1f1f7368066caf401c93057825d0f3b5df4864 0.0s=> => naming to docker.io/library/nginx:dockerfile
查看并运行:
# 查看镜像已经存在
➜ docker images | grep dockerfile
nginx dockerfile bcd9c0828a32 56 seconds ago 22.1MB# 运行容器
➜ docker run --rm -p 8088:80 nginx:dockerfile
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
以上我们便使用Dockerfile配置文件定制nginx镜像的完整流程,可以体验出它相比命令式更加便捷,操作简单透明,并且同阶段的构建会被缓存起来复用,可以很好的提高构建效率,对于相同镜像的共享也只需拿到配置文件即可制作完全一样的镜像。
相反不使用Dockerfile,需要我们一步一步使用命令进行构建,那样会相对繁琐,整体构建的操作也会是黑箱操作,无法满足简单、易维护、高效的特性。
了解了Dockerfile的便捷之处后,接下来学习详细语法。
语法
FROM
初始化基础的镜像,在Dockerfile中必须的也是最开始就需要的,后面的命令需要基于最初的镜像进行操作。FROM
一般是最开始执行的,ARG
命令是唯一可以在FROM
前执行的命令。
语法:
FROM [--platform=<platform>] <image>[:tag] [AS <name>]
AS name
:可选,指定构建阶段的名称,适用于多阶段构建--platform
:指定平台,如arm,amd等等tag
:可选,不指定镜像tag默认为latest
ARG
ARG 指令用于设置构建时的参数,这些参数可以在构建时传递给 Docker 以影响镜像的构建,使用ARG可以简化镜像的构建过程、将容器的配置信息与容器的代码分离,提高容器的可维护性
语法:
ARG <name>[=<default value>]
可以在构建时使用 --build-arg <key>=<value>
参数来传递一个新的值给指定的参数
例子:
# Dockerfile-ARG
FROM nginx:alpine
ARG VERSION=0.0.1
ENV VERSION=$VERSION
RUN echo $VERSION
构建镜像重新赋值:从第9行看到已经被赋新值0.1.0
➜ docker build --build-arg VERSION=0.1.0 -t nginx:ARG --file Dockerfile-ARG .
[+] Building 15.9s (6/6) FINISHED=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load build definition from Dockerfile-ARG 0.0s=> => transferring dockerfile: 119B 0.0s=> [internal] load metadata for docker.io/library/nginx:alpine 15.6s=> CACHED [1/2] FROM docker.io/library/nginx:alpine@sha256:eb05700fe7baa6890b74278e39b66b2ed1326831f9ec3ed4bdc6361a4ac2 0.0s=> [2/2] RUN echo 0.1.0 0.3s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:c073ffd0029a4a3e64b68f2a9e450a4389d0b81fc7221bfcd727448be48bea36 0.0s=> => naming to docker.io/library/nginx:ARG
然后可以通过docker inspect
查看镜像的变量:
➜ docker inspect nginx:ARG | grep -A 6 Env"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.21.5","NJS_VERSION=0.7.1","PKG_RELEASE=1","VERSION=0.1.0"],
ENV
该指令用于设置环境变量,可以在容器运行时使用这些环境变量
语法:
ENV <key>=<value> ...
其中,<key>
表示要设置的环境变量的名称,<value>
表示要设置的环境变量的值。如果要设置多个环境变量,可以在ENV指令中指定多个键值对,每个键值对用空格分隔,双引号将会被移除如果没有使用转义。Dockerfile中ENV定义的变量会持久保存在镜像中,可以使用docker inspect image
查看内部的环境变量,可以在运行容器时使用-e <key>=<value>
来覆盖原来的值,或设置新的环境变量
例子:
# Dockerfile
FROM nginx:alpine# 设置时区
ENV TZ=Asia/ShanghaiENV k1=v1
RUN echo $k1
构建镜像:
docker build --file Dockerfile -t nginx:ENV .
[+] Building 15.7s (6/6) FINISHED=> [internal] load build definition from Dockerfile-ENV 0.0s=> => transferring dockerfile: 86B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/nginx:alpine 15.5s=> CACHED [1/2] FROM docker.io/library/nginx:alpine@sha256:eb05700fe7baa6890b74278e39b66b2ed1326831f9ec3ed4bdc6361a4ac2 0.0s=> [2/2] RUN echo v1 0.2s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:c6e1d107c5aa56a9c70fcd377c539e6ae28267e4f1660d9dd8ee5d7a40ac79e6 0.0s=> => naming to docker.io/library/nginx:ENV
查看镜像内部持久化的变量:第8行看到镜像内部设置的k1
变量
docker inspect nginx:ENV | grep -A 5 ENV
# ...
"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.21.5","NJS_VERSION=0.7.1","PKG_RELEASE=1","k1=v1"
],
运行容器并覆盖镜像中默认的k1
变量值:
docker run -d -p 8080:80 -e k1=v2 --name nginx-env nginx:ENV# 查看容器 nginx-env 内部配置
docker inspect nginx-env | grep -A 6 Env"Env": ["k1=v2","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.21.5","NJS_VERSION=0.7.1","PKG_RELEASE=1"],
WORKDIR
WORKDIR 指令用于设置容器中的工作目录。当我们在容器中运行命令时,它们将在 WORKDIR 指定的目录中运行。如果在 Dockerfile 中没有使用 WORKDIR 指令,则默认的工作目录是根目录(/)。
语法:
WORKDIR /path/to/workdir
例子:
FROM ubuntu:latest
# 指定容器工作目录为/app
WORKDIR /app
# 将当前目录下的内容复制到容器中的 /app 目录中
COPY . .
RUN apt-get update && apt-get install -y python3
CMD ["python3", "app.py"]
LABEL
给镜像添加标签,label使用键值对的方式定义,有空格的值要用双引号包括,尽量在一行中定义完所有的label,自己使用意义不大了解下即可
语法:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
例子:
LABEL label1=xxxxx \label2=123143 \label3="sdfsdfs"
例子:
# 使用 arm架构的 nginx:1.15.3 的镜像作为 build 阶段的基础镜像
FROM --platform=arm nginx:1.15.3 AS build
RUN
在当前镜像上层执行相关命令,并commited一个新的镜像层提供给下一层阶段使用。由于一条RUN指令便会产生新的镜像层,使得构建体积变大,在生产构建时尽量采用 &&
把RUN合并成一条指令执行,当然在测试构建时分开写更容易排查构建错误
:::warning 注意
使用RUN执行命令时当前阶段必须包含所执行的命令才可以,否则需要先安装相关命令
:::
语法:
RUN <command>
RUN ["executable", "param1", "param2"...]
RUN支持两种执行形式:shell格式和exec格式。shell格式就是在终端执行命令一样,可以使用管道、重定向、变量的一些特性,而exec格式不会采用shell执行,而是直接在容器中执行,写法就像一个一维数组,将每个参数放在每个位置即可,使用exec可以减少启动新shell进程的开销,且会作为第一进程执行。
例子:
RUN curl -I http://localhost
RUN ["curl", "-I", "http://localhost"]
COPY
复制本地文件或路径到容器的文件系统,支持通配符匹配,不支持远程url文件地址,不支持压缩包自动解压,相比ADD指令一般使用这个也够用了
语法:
COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
复制文件也支持设置指定的用户和权限组,同时支持--from=xx
多阶段产物的复制,这种特性对于减小镜像构建体积作用很大,你可以从多阶段构建了解其作用
例子:
# 绝对路径
COPY *.md /app
COPY hom?.txt /app# 相对路径 work相对于 WORKDIR
COPY *.sh work# 修改文件权限
COPY --chown=1 files* /somedir/
ADD
复制本地文件、路径或远程文件到容器文件系统,相比COPY可以远程文件,并自动下载复制到目标位置。如果是压缩文件,也会自动解压。
语法:
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
例子:
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
ADD *.md /app
VOLUME
定义容器数据挂载点,在运行容器时使用-v
或--mount
参数指定宿主机目录或文件挂载到容器的挂载点,实现容器与主机之间的数据共享,将容器的数据和应用程序分离开来,保持容器的数据独立性,也便于在主机上修改相应的配置文件。
语法:
VOLUME /dir1 /dir2 /dir3 ...
VOLUME ["dir1", ...., "dir9999"]
VOLUME支持多个挂载点,在运行容器时通过-v
进行挂载
例子:
# ...
# 创建文件夹 static
mkdir /static
# 将容器外部的html文件复制到 static中
COPY /html /static
# 创建挂载点
VOLUME /static
在运行时通过-v /somedir:/static
可以覆盖容器中的文件
EXPOSE
EXPOSE 指令用于声明容器运行时要监听的网络端口号。该指令并不会在容器运行时自动将端口号映射到主机上,而是作为一种文档形式的标记,用于告诉用户哪些端口可以被容器访问。
:::warning 注意
EXPOSE指令并不会自动将容器的端口映射到主机上,如果需要将容器的端口映射到主机上,需要在运行容器时使用 -p 或 --publish 参数指定端口映射规则。同时,也需要确保容器运行的应用程序实际上监听了指定的端口号,否则端口号将无法被访问。
:::
语法:
EXPOSE <port> [<port>/<protocol>...]
其中,<port>
表示要监听的端口号,可以是1到65535之间的任意整数。如果要监听多个端口号,可以在 EXPOSE 指令中指定多个端口号,每个端口号用空格分隔。如果要监听的端口号是使用 TCP 或 UDP 协议,则可以在端口号后面添加 /tcp 或 /udp 表示协议类型。例如,EXPOSE 80/tcp
表示要监听 TCP 协议的 80 端口。
例子:
FROM nginx
EXPOSE 80/tcp
指明该容器运行是会监听容器中的80端口,然后在运行时可以通过-p
和主机进行端口之间的映射:
# 将主机的 8088 端口映射到 容器的 80端口,就可以使用 宿主机的 IP:port 方式访问到nginx
docker run -p 8088:80 nginx
CMD
CMD 指令用于指定容器启动时默认要执行的命令。当我们使用docker run
命令启动容器时,如果没有指定要执行的命令,那么将会执行 CMD 中指定的命令。
:::warning 注意
Dockerfile 中只能使用一条 CMD 指令。如果在 Dockerfile 中使用了多个 CMD 指令,只有最后一个 CMD 指令会生效
:::
语法:
# exec执行
CMD ["executable","param1","param2"]# 作为 ENTRYPOINT参数
CMD ["param1","param2"]# shell 执行
CMD command param1 param2
CMD支持exec和shell两种写法,使用exec方式可以和ENTRYPOINT结合并作为其参数使用,强烈建议使用exec方式使用。
例子:
FROM busybox# 使用ping localhost 3次后停止
CMD ["ping", "-c", "3", "localhost"]
构建镜像:
docker build -t nginx:CMD --file Dockerfile-CMD .
运行容器:容器启动会执行Dockerfile中指定的CMD命令,这里执行ping localhost
3次后停止
➜ docker run --rm nginx:CMD
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.064 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.198 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.196 ms--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.064/0.152/0.198 ms
运行容器时提供默认运行的命令来覆盖默认的命令:这里使用date
命令代替了Dockerfile中CMD的命令
➜ docker run --rm nginx:CMD date
Tue Mar 28 07:23:07 UTC 2020
:::warning 注意
容器的运行必须提供默认的运行命令,可以运行时提供(CMD/ENTRYPOINT)或者构建时提供,没有默认运行命令则会自动退出,低版本可能会报错
:::
我们去掉以上的CMD
命令重新构建镜像:
docker build -t nginx:CMD --file Dockerfile-CMD .
然后运行容器:
➜ docker run --rm nginx:CMD
ENTRYPOINT
和CMD一样ENTRYPOINT命令也是用于指定容器启动时默认要执行的命令,也是只能最后一个生效。若Dockerfile中CMD和ENTRYPOINT都存在,则ENTRYPOINT作为容器的运行命令,CMD将作为ENTRYPOINT的参数;若运行容器时再次指定了CMD的参数,将会覆盖默认的CMD参数,但参数也会追加到ENTRYPOINT作为参数使用。
语法:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
尽管ENTRYPOINT支持两种形式的写法,但是也是强烈建议使用exec形式,避免和CMD参数追加的问题。
若想覆盖Dockerfile中的ENTRYPOINT时,可以在运行容器时通过--entrypoint
来指定要运行的程序
例子:
FROM busybox
ENTRYPOINT ["ping"]
CMD ["localhost", "-c", "3"]
以上指定容器运行程序为ping
,CMD作为ENTRYPOINT的参数,整体命令为ping localhost -c 3
,ping完localhost 3次就会停止
运行容器:
➜ docker run --rm nginx:ENTRYPOINT
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.069 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.256 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.248 ms--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.191/0.256 ms
运行时通过传参覆盖掉默认的 CMD,并且会将动态添加的参数追加到 ENTRYPOINT:这里改为ping qq.com 1次就停止
➜ docker run --rm nginx:ENTRYPOINT qq.com -c 1
PING qq.com (183.3.226.35): 56 data bytes
64 bytes from 183.3.226.35: seq=0 ttl=127 time=34.314 ms--- qq.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 34.314/34.314/34.314 ms
通过运行容器时使用--entrypoint
替换默认的ENTRYPOINT命令:
➜ docker run --rm --entrypoint "ls" nginx:ENTRYPOINT -ll
total 16
drwxr-xr-x 2 root root 12288 Dec 29 2021 bin
drwxr-xr-x 5 root root 340 Mar 28 10:07 dev
drwxr-xr-x 1 root root 66 Mar 28 10:07 etc
drwxr-xr-x 2 nobody nobody 6 Dec 29 2021 home
dr-xr-xr-x 294 root root 0 Mar 28 10:07 proc
drwx------ 2 root root 6 Dec 29 2021 root
dr-xr-xr-x 12 root root 0 Mar 28 10:07 sys
drwxrwxrwt 2 root root 6 Dec 29 2021 tmp
drwxr-xr-x 3 root root 18 Dec 29 2021 usr
drwxr-xr-x 4 root root 30 Dec 29 2021 var
ONBUILD
当使用包含 ONBUILD 指令的镜像作为下一个 Dockerfile 的基础镜像时,ONBUILD 指令中的命令将自动执行,并将其结果添加到子镜像中。这样可以在构建镜像时预先处理一些操作,以便在基于该镜像构建更高级别的应用程序时,执行这些操作不必重复编写代码。
语法:
ONBUILD <INSTRUCTION>
例子:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3
WORKDIR /app
ONBUILD COPY . .
ONBUILD RUN pip3 install -r requirements.txt
ONBUILD CMD ["python3", "app.py"]
当使用这个镜像作为基础镜像创建一个新的 Dockerfile 时,Docker 将自动复制当前 Dockerfile 目录中的所有文件到新的镜像中,并安装 requirements.txt 中指定的 Python 库。然后,在新的镜像中,CMD 指令将自动运行 Python 应用程序。
假如以上的镜像名为myimage
,使用其作为基础镜像再次构建新的镜像:
FROM myimage
EXPOSE 5000
CMD ["python3", "run.py"]
在这个 Dockerfile 中,我们使用 myimage 镜像作为基础镜像,并指定了一些额外的操作,例如暴露端口和运行run.py
文件。由于 myimage 镜像中包含 ONBUILD 指令,因此 Docker 将自动执行 COPY 和 RUN 命令,并安装 requirements.txt 中指定的 Python 库。然后,在新的镜像中,CMD 指令将自动运行 Python 应用程序。
HEALTHCHECK
指定容器健康检查的方式和频率,以及容器健康检查失败时的行为,Docker 支持多种检查方式,例如 HTTP 接口、TCP 端口、命令行输出等。可以根据应用程序的特点和需求选择适合的健康检查方式。
语法:
HEALTHCHECK [OPTIONS] CMD command# 禁止健康检查
HEALTHCHECK NONE
例子:
FROM nginx
HEALTHCHECK --interval=10s --timeout=10s \CMD curl -I http://localhost || exit 1
CMD ["nginx", "g", "daemon off;"]
在这个 Dockerfile 中,我们使用HEALTHCHECK指令设置了容器健康检查的方式。具体来说,我们使用 curl 命令检查容器中是否能够访问 http://localhost
,并将检查间隔设置为10s
,检查超时时间设置为10s
。如果健康检查失败,则容器将退出。
在运行容器时,可以使用 --health-cmd、–health-interval、–health-timeout 等参数覆盖 Dockerfile 中设置的健康检查参数
⭐️⭐️⭐️⭐️⭐️
到这里关于Dockerfile的语法知识已经讲的差不多了,如果你还需要更多有关Dockerfile语法请阅读官方文档
注意事项
- 保留字指令必须大写,后面要跟随至少一个参数
- 指令从上到下执行
- 每条指令都会创建一个新的镜像层并对镜像进行提交
- docker会对前面相同的步骤进行构建缓存
构建上下文
Dockerfile 的构建上下文(Build Context)是指在构建 Docker 镜像时,Docker 引擎需要读取的文件和目录的集合。
构建上下文通过docker build
命令的-f
和 .
参数指定。其中,-f
参数用于指定 Dockerfile 文件的路径,.
参数用于指定构建上下文的路径。例如,下面是一个使用 docker build 命令构建 Docker 镜像的示例:
docker build -f Dockerfile -t myimage .
在这个命令中,我们使用 -f
参数指定 Dockerfile 文件的路径为当前目录下的 Dockerfile 文件,使用 .
参数指定构建上下文的路径为当前目录。Docker引擎将读取当前目录下的所有文件和目录,并根据 Dockerfile 文件中的指令构建 Docker 镜像。
需要注意的是,构建上下文中包含的所有文件和目录都会被上传到 Docker 引擎中进行构建。因此,构建上下文的大小会直接影响构建时间和构建过程中网络传输的数据量。建议使用.dockerignore
将构建上下文限制在必要的文件和目录范围内,避免上传无用文件和目录,以提高构建效率。
镜像构建
:::warning 注意
规定在Dockerfile中第一条指令必须是FROM
,作为制作镜像的最基础的镜像层,基础镜像可以是空镜像如:scratch 镜像
:::
或许你已经注意到了,以上镜像构建使用的是docker build
命令,它就是用来构建镜像的。构建是在服务端(docker引擎)进行的,我们知道docker是典型的C/S
架构,通过构建命令会将当前上下文的文件传递给服务端,然后进行构建,这种架构也天然的支持分布式远端构建,这里不用了解太多。
语法:
docker build [OPTIONS] PATH | URL | -
支持本地Dockerfile文件,还支持远程URL,如果文件是个tar压缩包,将会自动解压。常用的参数如下:
构建上下文
:指定上下文目录,默认.
,可以是任何文件系统路径-f
:指定Dockerfile文件路径,其命名可以随意,默认当前路径下的Dockerfile文件-t
:镜像名及标签,如 nginx:1.1--build-arg
:Dockerfile构建arg参数--no-cache
:禁用构建缓存,强制重新构建镜像--pull
:强制每次重新拉取远程镜像
在构建命令前加上 DOCKER_BUILDKIT=1 参数可以查看构建构建日志和进度,便于调试
例子:
docker build -t nginx:1.1 -f ../Dockerfile . --pull --no-cache
更多关于
docker build
的用法可以使用docker build -h
了解
多阶段构建
到这里你基本上已经会简单的构建镜像了,但往往构建的镜像体积比较大,内部往往包含了一些无用的内容文件。如前端静态项目可能需要用node进行打包,最后用nginx提供http服务,实际并不需要node环境及文件,但还是一并放进了镜像中,体积会变大好多,这种情况可以使用多阶段构建解决。
Dockerfile 多阶段构建是一种优化 Docker 镜像构建过程的技术。它可以在一个 Dockerfile 文件中定义多个阶段,每个阶段可以使用不同的基础镜像和构建步骤,并且可以挑取上一阶段的产物,最终生成一个精简的镜像。
语法:
FROM xxx AS stageName
同样的每个阶段都是以FROM
开始,使用AS
可以为当前阶段命名,名字需小写
例子:
# build阶段
FROM node:14.16.2 AS build
WORKDIR /app
COPY package.json .
COPY src .
RUN npm install && npm run build# 最后一个阶段
FROM nginx:alpine
COPY --from=build /app/dist /var/etc/nginx/html
CMD ["nginx", "g", "daemon off;"]
这个Dockerfile定义了两个阶段的构建,第一阶段为build
阶段,使用node:14.16.2
为基础镜像,安装前端依赖并进行打包,产物为dist
目录;第二阶段以nginx:alpine
为基础镜像使用COPY --from=build
将build阶段的/app/dist
产物复制到第二阶段的nginx静态目录,最终设置容器的启动命令。最终打包出来的镜像只包含了最后一阶段的文件,不会包含build阶段的node内容,这样会使镜像的体积减小很多,也能在CI/CD
中减小打包交付时间,提高效率
你可以尝试将以上的Dockerfile分别用单阶段和多阶段进行构建,对比下两者的大小,加深其作用印象
dockerignore
使用多阶段构建是一种优化手段,另一个重要的概念便是dockerignore
,它是用来做什么的?前面也提到了docker是一个C/S
架构,镜像的构建是在Docker Engine构建的,构建时会将构建上下文的文件全部上传到服务端,如果上传了一些不必要的文件,就会影响总体耗时。
docker也支持像.gitignore
类似的配置文件.dockerignore
,在构建上传文件时将会忽略掉.gitignore
中匹配的文件或路径,从而缩短文件上传耗时。
.DS_Store
node_modules
*.md
*/dist
构建前端项目
本次就分别以前端的静态项目和NodeJS项目为例子做个简单的构建演示
静态项目
静态项目用node打包,最终以nginx发布,源码地址👉点击这里
这里使用vite
简单创建一个demo,使用react+ts
模板
npm create vite
编写Dockerfile:
# docker发布前端静态项目简单demo
FROM node:alpine as builder
WORKDIR /app
COPY . .
RUN npm install && npm run build# 大小只有20MB左右
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
CMD [ "nginx", "-g", "daemon off;" ]
构建镜像:
docker build -t blog:v1 .
运行容器:
docker run --rm -p 8088:80 blog:v1
以本人为例以上镜像大小只有22.4MB
,浏览器输入IP:8088
访问页面就可以看到静态页面了
NodeJS项目
NodeJS服务项目,使用pm2
进行发布,源码地址👉点击这里。
初始化项目,并安装相应的依赖:
npm init -y# package.json文件
{"name": "frontend-nodejs","version": "1.0.0","main": "index.js","dependencies": {"dayjs": "^1.11.7","express": "^4.18.2"}
}
创建server.js
文件:
const express = require("express");
const app = express();
const dayjs = require("dayjs");app.use((req, res) => {console.log("【request】:" + req.url);res.json({code: 200,date: dayjs().format("YYYY-MM-DD HH:mm:ss"),path: req.url,query: {...req.query,},});
});app.listen(10001, () => console.log("server is running on port 10001."));
创建pm2.json
配置文件:
{"apps": [{"name": "frontend-nodejs","script": "server.js","watch": false,"instance": 3,"autorestart": true,"max_memory_restart": "1G","env": {"NODE_ENV": "development"},"env_production": {"NODE_ENV": "production"}}]
}
Dockerfile配置文件:
FROM node:alpine
WORKDIR /app
COPY package.json .
RUN npm install && npm install pm2 -g
COPY . .
EXPOSE 10001
ENTRYPOINT ["pm2-runtime", "start", "pm2.json", "--env", "production"]
构建镜像:
docker build -t blog:v2 .
运行容器:
docker run --rm -p 8088:10001 blog:v2
以本人为例以上镜像大小有200MB大小,浏览器输入IP:8088
访问页面就可以页面了,你还可以控制台查看日志输出
构建优化
构建优化在生产环境中是非常必要的,常见的优化手段有:
- 多阶段构建:使用多阶段构建可以减小镜像大小,提高构建速度。多阶段构建可以将构建环境和运行环境分离,只保留必要的文件和组件。
- 使用 .dockerignore 文件:使用 .dockerignore 文件可以避免将不必要的文件和目录复制到镜像中,从而减小镜像大小。
- 构建时使用缓存:使用 Dockerfile 构建缓存可以加速镜像构建过程。例如,在 Dockerfile 中,可以将不经常更改的指令放在前面,从而利用缓存。
- 使用更小的基础镜像:使用更小的基础镜像可以减小镜像大小,提高镜像构建和部署的效率。
参考文档
- https://docs.docker.com/engine/reference/builder
- https://docs.docker.com/engine/reference/commandline/build
- https://docs.docker.com/language/nodejs/build-images
- https://docs.docker.com/engine/reference/run
总结
Dockerfile 是构建和管理 Docker 镜像的重要工具,使用它可以简化应用程序的部署和管理,并提高可重复性、可自动化性、可定制性和可扩展性。
由于格式和图片解析问题,为了更好体验可前往 阅读原文
相关文章:
使用Dockerfile构建镜像
由于格式和图片解析问题,为了更好体验可前往 阅读原文 前面我们学习了可以使用docker commit命令式构建新的镜像,而此方式相对来说比较繁琐且对于旁人来说内部都是黑箱操作,无法了解制作的具体细节。很有可能很长时间后制作者也会对其忘却&am…...
SQL字符串截取函数——Left()、Right()、Substring()用法详解
SQL字符串截取函数——Left()、Right()、Substring()用法详解 1. LEFT() 函数:从字符串的左侧提取指定长度的子字符串。 LEFT(string, length)string:要操作的字符串。length&#x…...
python字符串函数用法大全
目录 1.0 capitalize()函数 2.0 title()函数 3.0 swapcase()函数 4.0 lower()函数 5.0 upper()函数 7.0 center()函数 8.0 ljust()函数 9.0 rjust()函数 10.0 zfill()函数 11.0 count()函数 13.0 decode()函数 14.0 expandtabs()函数 15.0 find()函数 16.0 rfind()…...
纵览!报表控件 Stimulsoft Reports、Dashboards 和 Forms 2025.1 新版本发布!
Stimulsoft 2025.1 新版发布,旨在增强您创建报告、仪表板和 PDF 表单的体验!此最新版本为您带来了许多改进和新功能,使数据处理更加高效和用户友好。亮点包括对 .NET 9 的支持、Microsoft Analysis Services 的新数据适配器、发布向导中适用于…...
【蓝桥杯——物联网设计与开发】Part2:OLED
目录 一、OLED (1)资源介绍 🔅原理图 🔅驱动原理 (2)STM32CubeMX 软件配置 (3)代码编写 (4)实验现象 二、OLED接口函数封装 🟡️OLED_Wri…...
壁纸样机神器,这个工具适合专业设计师用吗?
壁纸样机神器在一定程度上适合专业设计师使用,但是否适合具体取决于设计师的需求和使用场景: 适合专业设计师的方面 快速实现设计想法:专业设计师在创作过程中,有时需要快速将设计想法变为可视化的效果图,以便进行初…...
Linux环境(CentOs7) 安装 Node环境
Linux环境(CentOs7) 安装 Node环境 使用NodeSource安装Node.js 1、清除缓存(可选但推荐) sudo yum clean all2、添加NodeSource仓库,根据你想要安装的Node.js版本,选择相应的NodeSource安装脚本。例如&am…...
【Springboot知识】Springboot集成assembly打包组件
assembly系统打包 概述一、项目结构二、配置Maven Assembly插件三、配置assembly.xml四、打包项目五、部署和使用六、注意事项 assembly配置文件说明一、assembly配置文件的基本结构二、assembly配置文件的关键元素三、assembly配置文件的示例四、assembly配置文件与Spring Boo…...
逻辑推理算法
为说明逻辑推理算法的用法,下面是一个简单的逻辑推理算法的伪代码示例,使用了命题逻辑的推理规则(例如:命题逻辑中的合取、析取、否定等)。代码实现了一个简单的推理引擎,可以根据已知的前提推导出新的结论…...
鸿蒙1.2:第一个应用
1、create Project,选择Empty Activity 2、配置项目 project name 为项目名称,建议使用驼峰型命名 Bundle name 为项目包名 Save location 为保存位置 Module name 为模块名称,即运行时需要选择的模块名称,见下图 查看模块名称&…...
数据结构考前一天
线性表:矩阵,链表(单链表必考) 栈和队列:出入判断,括号匹配,中缀转后缀 字符串数组:模式匹配next,nextval数组,数组寻址,三角矩阵对应一维数组k…...
【记录】Angr|Angr 标准库函数替换怎么看哪些库函数被Angr支持?
文章目录 前言分析什么是库函数替换?为什么需要库函数替换? 如何查找支持的库函数官方支持列表目录结构说明 常用的替换包括哪些?1. 字符串处理函数2. 内存管理函数3. 文件操作函数 高级技巧1. 自定义库函数实现2. 条件替换 常见问题与解决方案详解1. 找不到合适的…...
npx和npm和pnpm的异同
npx、npm 和 pnpm 都是用于管理和运行 JavaScript/Node.js 包的工具,但它们的功能和使用场景有所不同。以下是它们之间的异同点: 一、共同点 Node.js 包管理生态的一部分: 它们都围绕 Node.js 生态系统,提供包管理功能。 支持安装…...
【蓝桥杯比赛-C++组-经典题目汇总】
1. 最短路 题目描述: 如下图所示,G是一个无向图,其中蓝色边的长度是1、橘色边的长度是2、绿色边的长度是3。 则从 A 到 S 的最短距离是多少? #include <iostream> #include <cstring> using namespace std; const i…...
redis7基础篇2 redis的主从模式1
目录 一 主从模式 1.1 主从复制的作用 1.2 配置常用命令 1.3 主从复制常见问题 1.4 主从复制的缺点 1.5 redis主从复制原理 二 redis主从复制的搭建流程 2.1 注意事项 2.2 redis的主从复制架构图 2.3 以6379.conf配置文件配置为例 2.4 以6380.conf配置文件配置为例 …...
QT----------QT Data Visualzation
实现思路: 配置项目:在 .pro 文件中添加 QT datavisualization 以引入 QT Data Visualization 模块。创建主窗口:使用 QMainWindow 作为主窗口,添加 Q3DScatter、Q3DBars 和 Q3DSurface 等三维视图组件。初始化和创建三维图表&a…...
默认ip无法访问,利用dhcp功能获取ip进行访问的方法
应用场景: ac的默认ip如192.168.1.1在pc与ac的eth2以后网口直连无法ping通,而且pc改为dhcp自动获取ip也获取不到ip地址,无法进行web配置和命令行操作。 原因是ac或其他设备被修改了默认ip或者端口vlanid,现在的端口vlan对应子接…...
重学 Android 自定义 View 系列(十):带指针的渐变环形进度条
前言 该篇文章根据前面 重学 Android 自定义 View 系列(六):环形进度条 拓展而来。 最终效果如下: 1. 扩展功能 支持进度顺时针或逆时针显示在进度条末尾添加自定义指针图片使用线性渐变为进度条添加颜色效果 2. 关键技术点解析 2.1 进度方向控制的…...
websocket在各主流浏览器中默认的请求头是如何设置的?
谷歌浏览器(Chrome) 在谷歌浏览器中,当创建一个 WebSocket 连接时,会自动添加一些标准的请求头。其中包括Sec - WebSocket - Key,这个是一个 Base64 - 编码的随机值,长度为 16 字节。它用于在服务器端进行安全验证,确保连接的合法性。例如,Sec - WebSocket - Key: dGhl…...
C++之map和set的模拟实现
目录 引言 红黑树迭代器实现 红黑树元素的插入 map模拟实现 set模拟实现 之前我们已经学习了map和set的基本使用,但是因为map和set的底层都是用红黑树进行封装实现的,上期我们已经学习了红黑树的模拟实现,所以本期我们在红黑树模拟实现…...
大学物理(2)期末复习笔记【1】
图片不知道咋回事居然不能直接复制上来,过段时间修改好再编辑一下 9. 振动 一、振动 def:某一物理量在某一数值附近做周期性变化 周期(T):完成一次往复运动所需要的时间(s) 频率(…...
25.1.3
java数组: dataType[] arrayRefVar //推荐写法 //int[] mylist //或 dataType arrayRefVar[] //int mylist[]创建数组对象: arrayRefVar new dataType[arraySize]; dataType[] arrayRefVar new dataType[arraySize];for-each循环: jav…...
数据库知识汇总2
一. 范式 定义:范式是符合某一种级别的关系模式的集合。 关系数据库中的关系必须满足一定的要求。满足不同程度要求的为不同范式; 一个低一级范式的关系模式,通过模式分解(schema decomposition)可以转换为若干个高一…...
SpringBoot:生成条形码的项目
在软件开发中,条形码和二维码的生成与解析是一项常见需求,特别是在商品管理、物流跟踪、支付验证等场景。Spring Boot 作为一个流行的微服务框架,提供了快速构建应用的能力。本文将介绍如何在 Spring Boot 项目中生成条形码,并提供…...
docker内外如何实现ROS通信
写在前面 在一台电脑上装有docker,docker内外均装有ROS系统,现在想要实现docker内外的ROS通信,怎么办呢? 首先,因为是同一台电脑的docker内外,所以IP本身是互通的,不需要在/etc/hosts中添加IP…...
leetcode 面试经典 150 题:多数元素
链接多数元素题序号169题型数组解法1. 排序法、2. Boyer-Moore投票算法难度简单熟练度✅✅✅✅✅ 题目 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的…...
SpringBoot返回文件让前端下载的几种方式
01 背景 在后端开发中,通常会有文件下载的需求,常用的解决方案有两种: 不通过后端应用,直接使用nginx直接转发文件地址下载(适用于一些公开的文件,因为这里不需要授权)通过后端进行下载&#…...
论述数据、数据库、数据库管理系统、数据库系统的概念。
数据是指描述事物特征的符号记录,可以是数字、文字、图像、音频等形式。数据在现代社会中广泛存在于各个领域,对于组织和管理数据,提供数据的可靠性、一致性和安全性至关重要。 数据库是一个有组织的数据集合,它存储在计算机系统…...
【深度学习基础之多尺度特征提取】多尺度卷积神经网络(MS-CNN)是如何在深度学习网络中提取多尺度特征的?附代码(二)
【深度学习基础之多尺度特征提取】多尺度卷积神经网络(MS-CNN)是如何在深度学习网络中提取多尺度特征的?附代码(二) 【深度学习基础之多尺度特征提取】多尺度卷积神经网络(MS-CNN)是如何在深度…...
企业AAA认证的好处
体系认证#ISO三体系认证 #三体系认证好处 #企业双软认证好处 #ISO体系认证有哪些#体系认证办理流程及费用#招投标#招投标必备资质 企业信用评级AAA认证 办理条件及流程! 一、企业申请3A认证好处有哪些? 1.提高企业信誉: 拥有3A企业信用等级证书意味…...
PyTorch AMP 混合精度中grad_scaler.py的scale函数解析
PyTorch AMP 混合精度中的 scale 函数解析 混合精度训练(AMP, Automatic Mixed Precision)是深度学习中常用的技术,用于提升训练效率并减少显存占用。在 PyTorch 的 AMP 模块中,GradScaler 类负责动态调整和管理损失缩放因子&…...
分数阶傅里叶变换代码 MATLAB实现
function Faf myfrft(f, a) %分数阶傅里叶变换函数 %输入参数: %f:原始信号 %a:阶数 %输出结果: %原始信号的a阶傅里叶变换N length(f);%总采样点数 shft rem((0:N-1)fix(N/2),N)1;%此项等同于fftshift(1:N),起到翻…...
腾讯云OCR在制造业的应用:内存模组产品识别实战指南
腾讯云OCR在制造业的应用 一、 背景二、 腾讯云OCR技术概述三、 内存模组产品识别需求四、基于腾讯云OCR的内存模组产品识别4.1、准备工作4.2、API调用与代码实现 五、 代码示例六、 应用场景七、 总结 一、 背景 制造业在产品识别环节经历着前所未有的挑战。传统的依赖人工进…...
基于STM32F1的基本定时器的原理
一,基本定时器原理 1,进入数据手册,了解基本定时器的主要特征 2,看懂理解基本定时器框图 3,查阅2.3章中的存储器映像以及时钟树就可以知道定时器是挂载在哪个总线下,从而知道对应是时钟频率 4,…...
Adobe Acrobat Pro DC 2023 下载安装教程,附详细图文
简介: Adobe Acrobat Pro DC 2023 是由 Adobe 公司推出的一款全面的 PDF 编辑、查看和管理软件。这款软件无论是个人用户还是企业级用户,都可以凭借其强大的功能满足不同的需求。作为一款业内领先的 PDF 处理工具,Adobe Acrobat Pro DC 不仅…...
活动预告 |【Part1】 Azure 在线技术公开课:迁移和保护 Windows Server 和 SQL Server 工作负载
课程介绍 通过 Microsoft Learn 免费参加 Microsoft Azure 在线技术公开课,掌握创造新机遇所需的技能,加快对 Microsoft 云技术的了解。参加我们举办的“迁移和保护 Windows Server 和 SQL Server 工作负载”活动,了解 Azure 如何为将工作负载…...
根据 el-dialog 的高度动态计算 el-table 的高度
根据 el-dialog 的高度动态计算 el-table 的高度,可以使用 Vue 的 ref 和生命周期钩子来实现。以下是一个实现方案: 首先,给 el-dialog 和 el-table 添加 ref: <el-dialogv-model"testInstrumentDialogVisible"tit…...
算法解析-经典150(双指针、滑动窗口)
文章目录 双指针1.验证回文串1.答案2.思路 2.判断子序列1.动态规划解法2.双指针 3.两数之和 II - 输入有序数组1.答案2.思路 4.盛最多水的容器1.答案2.思路 5.三数之和1.答案2.思路 滑动窗口1.长度最小的子数组1.答案2.思路 2.无重复字符的最长子串1.答案2.思路 3.最小覆盖子串…...
Postman[2] 入门——界面介绍
可参考官方 文档 Postman 导航 | Postman 官方帮助文档中文版Postman 拥有各种工具、视图和控件,帮助你管理 API 项目。本指南是对 Postman 主要界面区域的高级概述:https://postman.xiniushu.com/docs/getting-started/navigating-postman 1. Header&a…...
GAMES101学习笔记(一):Overview 计算机图形学概述
文章目录 关于计算机图形学本课程讨论的话题光栅化曲线和网格光线追踪动画/仿真 课程大纲CG vs CV图形学的依赖学科线性代数回顾 课程资源:GAMES101-现代计算机图形学入门-闫令琪 Lec1 ~ Lec2 学习笔记: Lecture 01 :Overview of Computer G…...
iOS 18手机不越狱玩MC java版---PojavLauncher
环境 手机: iPhone SE 3 iOS: 18.1.1 电脑操作系统:macOS Sequoia 15.1.1 步骤 电脑上安装altstore https://altstore.io/ 直接下载自己操作系统对应的版本即可。 安装altstore到手机 以下是我记忆中的步骤,关键步骤我提一下 手机连接…...
uni-app开发-习惯养成小程序/app介绍
目录 一:功能概述 二:功能部分代码和截图 一:功能概述 1 习惯目标生成 创建习惯:用户可以添加新的习惯目标,每个习惯可以包含名称、描述、图标、目标天数。 关联习惯完成:用户通过设定达成目标以后,生成习惯养成记录。 2 习惯打卡 简单快捷的打卡:提供一个直观的界面…...
服务器迁移中心——“工作组迁移”使用指南
简介 服务器迁移中心(Server Migration Center,简称SMC)是阿里云提供给您的迁移平台。专注于提供能力普惠、体验一致、效率至上的迁移服务,满足您在阿里云的迁移需求。 工作组迁移是SMC的一项功能,提供标准化迁移流程…...
下载离线的瓦片图是做了模糊处理嘛?
问题: 1.下载离线的瓦片图是做了模糊处理嘛? 2.怎么加载自己的离线瓦片图比实际图片模糊了很多?啊 3.同层级的图片都比实际的图片模糊 原因:https://zhuanlan.zhihu.com/p/389945647 可以尝试下略微优化下: 1.降低…...
日志聚类算法 Drain 的实践与改良
在现实场景中,业务程序输出的日志往往规模庞大并且类型纷繁复杂。我们在查询和查看这些日志时,平铺的日志列表会让我们目不暇接,难以快速聚焦找到重要的日志条目。 在观测云中,我们在日志页面提供了聚类分析功能,可以…...
git 问题解决记录
在用git上传文件到仓库中出现了2个问题 第一个问题: 需要修改git的代理端口与电脑自己的代理服务器设置中的端口和VPN的端口保持一致, 比如我的端口是7897,就设置 git config --global http.proxy http://127.0.0.1:7897 git config --glo…...
Node Exporter常用Prometheus指标
Node Exporter 是一个常用的 Prometheus 导出器,用于采集主机操作系统层面的指标。以下是 Node Exporter 中一些常用的指标分类和关键指标: 1. CPU 相关指标 常用指标: CPU 使用率 rate(node_cpu_seconds_total{mode!"idle"}[5m]…...
golang 编程规范 - 项目目录结构
原文:https://makeoptim.com/golang/standards/project-layout 目录结构 Go 目录 cmdinternalpkgvendor 服务端应用程序目录 api Web 应用程序目录 web 通用应用程序目录 buildconfigsdeploymentsinitscriptstest 其他目录 assetsdocsexamplesgithooksthird_par…...
【ArcGISPro/GeoScenePro】裁剪和打包栅格数据
检查并处理扫描地图 数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 使用标准相机或扫描仪创建的数字影像通常存储在 RGB 颜色模型中,这意味着这些影像将由红色、绿色和蓝色三个栅格组成。 此扫描地图在提供给您之前已在坐标系…...
数据库新建用户后(Host:%),报错:localhost无法连接
存在问题 在给数据库(MySQL、MariaDB等)创建了新的用户名(eg:maxscale)后,无法使用新用户名登录,并报如下错误:ERROR 1045 (28000): Access denied for user maxscalelocalhost (us…...