文章目录
一、Dockerfile(制作镜像脚本化)1.1 什么是Dockerfile1.2 为什么要使用Dockerfile 二、Docker Build 工作原理三、Dockerfile 常用指令3.1 FROM3.2 MAINTAINER(新版即将废弃)3.3 RUN3.4 ADD3.5 COPY3.6 CMD3.7 ENTRYPOINT3.8 LABEL3.9 ENV3.10 EXPOSE3.11 VOLUME3.12 WORKDIR3.13 USER3.14 ARG3.15 ONBUILD 四、制作镜像
一、Dockerfile(制作镜像脚本化)
1.1 什么是Dockerfile
Dockerfile 是一个文本文件,包含一系列用于构建镜像的指令(Instructions)。每条指令都会构建一层镜像,指令的内容描述了该层镜像应如何构建。
Dockerfile 用于指导 docker image build 命令自动构建镜像它是一个纯文本文件示例:
docker build -f /path/Dockerfile
1.2 为什么要使用Dockerfile
问题:Docker Hub 官方提供了许多满足我们服务需求的镜像,为什么还需要自定义镜像?
核心作用:用户可以将自己的应用打包成镜像,从而让应用在容器中运行。还可以对官方镜像进行扩展,打包成适合生产环境的应用镜像。
完整镜像的结构图:
Dockerfile的格式
包含两种类型的行:
以 # 开头的注释行以专用"指令(Instruction)"开头的指令行Image Builder 按顺序执行各指令,从而完成镜像构建
二、Docker Build 工作原理
docker build [选项] <上下文路径/URL/->
docker build 命令后的 .
表示当前目录,同时也指定了上下文路径。
上下文:
Docker 运行时分为 Docker 引擎(服务端守护进程)和客户端工具。Docker 引擎提供了一组 REST API,称为 Docker Remote API。客户端工具(如 docker 命令)通过这组 API 与 Docker 引擎交互,完成各种功能。
虽然表面上我们似乎在本机执行各种 Docker 功能,但实际上,一切都是通过远程调用在服务端(Docker 引擎)完成的。这种客户端/服务器设计使得操作远程服务器上的 Docker 引擎变得简单易行。
镜像构建过程中,不是所有定制都通过 RUN 指令完成。我们经常需要将本地文件复制进镜像,比如使用 COPY 或 ADD 指令。docker build 命令构建镜像时,实际上是在服务端(Docker 引擎)中进行的。那么,在这种客户端/服务端架构中,如何让服务端获得本地文件呢?
这就引入了上下文的概念。构建时,用户指定构建镜像的上下文路径,docker build 命令会将该路径下的所有内容打包并上传给 Docker 引擎。Docker 引擎收到并展开这个上下文包后,就能获得构建镜像所需的全部文件。
为什么有人误以为 .
是指定 Dockerfile 所在目录呢?这是因为默认情况下,如果不额外指定 Dockerfile,系统会将上下文目录下名为 Dockerfile 的文件作为 Dockerfile。
实际上,Dockerfile 的文件名不必是 “Dockerfile”,也不必位于上下文目录中。例如,可以用 -f …/Dockerfile 参数指定其他文件作为 Dockerfile。
不过,人们通常会沿用默认的文件名 Dockerfile,并将其放在镜像构建的上下文目录中。
三、Dockerfile 常用指令
官方 build 参考
3.1 FROM
指定基础镜像,必须为第一个命令
格式: FROM <image> FROM <image>:<tag> FROM <image>@<digest>示例: FROM mysql:5.6注: tag 或 digest 是可选的,如果不指定,将使用 latest 版本的基础镜像
3.2 MAINTAINER(新版即将废弃)
维护者信息
格式: MAINTAINER <name>示例: MAINTAINER bertwu MAINTAINER xxx@163.com MAINTAINER bertwu <xxx@163.com>
3.3 RUN
构建镜像时执行的命令
RUN 用于在构建镜像时执行命令,有以下两种执行方式:shell 执行格式: RUN <command>exec 执行格式: RUN ["executable", "param1", "param2"]示例: RUN ["executable", "param1", "param2"] RUN apk update RUN ["/etc/execfile", "arg1", "arg2"]注:RUN 指令创建的中间镜像会被缓存,并在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定 --no-cache 参数,如:docker build --no-cache
3.4 ADD
将本地文件添加到容器中,tar 类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似 wget
格式: ADD <src>... <dest> ADD ["<src>",... "<dest>"] 用于支持包含空格的路径示例: ADD hom* /mydir/ # 添加所有以"hom"开头的文件 ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt" ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/ ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
3.5 COPY
功能类似 ADD,但不会自动解压文件,也不能访问网络资源
3.6 CMD
构建镜像后调用,也就是在容器启动时才进行调用。
格式: CMD ["executable","param1","param2"] (执行可执行文件,优先) CMD ["param1","param2"] (设置了 ENTRYPOINT,则直接调用 ENTRYPOINT 添加参数) CMD command param1 param2 (执行 shell 内部命令)示例: CMD echo "This is a test." | wc -l CMD ["/usr/bin/wc","--help"]注:CMD 不同于 RUN,CMD 用于指定容器启动时要执行的命令,而 RUN 用于指定镜像构建时要执行的命令。
3.7 ENTRYPOINT
配置容器,使其可执行化。配合 CMD 可省去"application",只使用参数。
格式: ENTRYPOINT ["executable", "param1", "param2"] (可执行文件,优先) ENTRYPOINT command param1 param2 (shell 内部命令)示例: FROM ubuntu ENTRYPOINT ["ls", "/usr/local"] CMD ["/usr/local/tomcat"]之后,docker run 传递的参数,都会先覆盖 CMD,然后由 CMD 传递给 ENTRYPOINT,实现灵活应用注:ENTRYPOINT 与 CMD 非常类似,不同的是通过 docker run 执行的命令不会覆盖 ENTRYPOINT,而 docker run 命令中指定的任何参数,都会被当做参数再次传递给 CMD。Dockerfile 中只允许有一个 ENTRYPOINT 命令,多次指定时会覆盖前面的设置,而只执行最后的 ENTRYPOINT 指令。通常情况下,ENTRYPOINT 与 CMD 一起使用,ENTRYPOINT 写默认命令,当需要参数时使用 CMD 传参
3.8 LABEL
用于为镜像添加元数据
格式: LABEL <key>=<value> <key>=<value> <key>=<value> ...示例: LABEL version="1.0" description="这是一个 Web 服务器" by="IT 笔录"注: 使用 LABEL 指定元数据时,一条 LABEL 指定可以指定一或多条元数据,指定多条元数据时不同元数据 之间通过空格分隔。推荐将所有的元数据通过一条 LABEL 指令指定,以免生成过多的中间镜像。
3.9 ENV
设置环境变量
格式: ENV <key> <value> # <key> 之后的所有内容均会被视为其 <value> 的组成部分,因此,一次只能设置一个变量 ENV <key>=<value> ... # 可以设置多个变量,每个变量为一个 "<key>=<value>" 的键值对,如果 <key> 中包含空格,可以使用 \ 来进行转义,也可以通过 "" 来进行标示;另外,反斜线也可以用于续行示例: ENV myName John Doe ENV myDog Rex The Dog ENV myCat=fluffy
3.10 EXPOSE
指定与外界交互的端口
格式: EXPOSE <port> [<port>...]示例: EXPOSE 80 443 EXPOSE 8080 EXPOSE 11211/tcp 11211/udp注:EXPOSE 并不会让容器的端口访问到主机。要使其可访问,需要在 docker run 运行容器时通过 -p 来发布这些端口,或通过 -P 参数来发布 EXPOSE 导出的所有端口如果没有暴露端口,后期也可以通过 -p 8080:80 方式映射端口,但不能通过 -P 形式映射
3.11 VOLUME
用于指定持久化目录(指定此目录可以被挂载出去)
格式: VOLUME ["/path/to/dir"]示例: VOLUME ["/data"] VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]注:一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:1. 卷可以在容器间共享和重用2. 容器并不一定要和其它容器共享卷3. 修改卷后会立即生效4. 对卷的修改不会对镜像产生影响5. 卷会一直存在,直到没有任何容器在使用它
3.12 WORKDIR
工作目录,类似于 cd 命令
格式: WORKDIR /path/to/workdir示例: WORKDIR /a (这时工作目录为 /a) WORKDIR b (这时工作目录为 /a/b) WORKDIR c (这时工作目录为 /a/b/c)注: 通过 WORKDIR 设置工作目录后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。在使用 docker run 运行容器时,可以通过 -w 参数覆盖构建时所设置的工作目录。
3.13 USER
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用 USER 指定用户时,可以使用用户名、UID 或 GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户
格式:USER userUSER user:groupUSER uidUSER uid:gidUSER user:gidUSER uid:group示例: USER www注: 使用 USER 指定用户后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT 都将使用该用户。 镜像构建完成后,通过 docker run 运行容器时,可以通过 -u 参数来覆盖所指定的用户。
3.14 ARG
用于指定传递给构建运行时的变量(给 Dockerfile 传参),相当于构建镜像时可以在外部为里面传参
格式: ARG <name>[=<default value>]示例: ARG site ARG build_user=www
From centos:7ARG parameterVOLUME /usr/share/nginxRUN yum -y install $parameterEXPOSE 80 443CMD nginx -g "daemon off;"# 可以这样灵活传参docker build --build-arg=parameter=net-tools -t nginx:01 .
3.15 ONBUILD
用于设置镜像触发器
格式: ONBUILD [INSTRUCTION]示例: ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src注: ONBUILD 后面跟指令,当当前的镜像被用做其它镜像的基础镜像时,该镜像中的触发器将会被触发
四、制作镜像
如果有多个 RUN 指令,它们会自上而下依次执行,每次执行都会形成新的层。建议使用 && 将多个命令合并到一行中执行。如果有多个 CMD 指令,只有最后一个会生效。如果有多个 ENTRYPOINT 指令,只有最后一个会生效。如果 CMD 和 ENTRYPOINT 共存,只有 ENTRYPOINT 会执行,且最后的 CMD 会作为 ENTRYPOINT 的参数。镜像制作分为两个阶段:
docker build 阶段:基于 Dockerfile 制作镜像(RUN 指令用于此阶段的命令执行)docker run 阶段:基于镜像运行容器(CMD 指令用于容器启动时需要运行的命令)docker build 阶段(基于现有镜像):当他人以你的镜像为基础制作新镜像时(ENTRYPOINT 或 ONBUILD 指令在此阶段执行)以下示例中的注释已被移除
FROM openjdk:8-jreWORKDIR /appADD demo-0.0.1-SNAPSHOT.jar app.jarEXPOSE 8081ENTRYPOINT ["java", "-jar"]CMD ["app.jar"]
创建 Dockerfile 文件,粘贴如下内容: FROM openjdk:8WORKDIR /rootADD springbootdemo3-0.0.1-SNAPSHOT.jar springboot.jarEXPOSE 9999ENTRYPOINT ["java", "-jar"]CMD ["springboot.jar"]
将你的 jar 包复制到与 Dockerfile 文件同级目录下进入 Dockerfile 所在目录,执行命令:docker build . -t 自定义镜像名称使用命令启动容器:docker run --name yourboot -d -p 端口映射 自定义镜像名称测试是否可以访问,记得在阿里云上开放相应端口 使用 uname -a 命令查看当前操作系统发行版本
使用 apt search name 命令搜索软件包
自制一个 OpenJDK 8 镜像:
FROM ubuntu:18.04RUN apt update -y && \ apt install -y openjdk-8-jre
根据 Dockerfile 构建镜像的命令:
docker build . -t 镜像名称
FROM openjdk:17-jdk-slimCOPY chatgpt-bootstrap/target/*.jar /app/app.jarWORKDIR /appENV TZ=Asia/ShanghaiRUN echo > /etc/apt/sources.list && \ sed -i "1ideb https://mirrors.aliyun.com/debian/ bullseye main non-free contrib" /etc/apt/sources.list && \ sed -i "2ideb-src https://mirrors.aliyun.com/debian/ bullseye main non-free contrib" /etc/apt/sources.list && \ sed -i "3ideb https://mirrors.aliyun.com/debian-security/ bullseye-security main" /etc/apt/sources.list && \ sed -i "4ideb-src https://mirrors.aliyun.com/debian-security/ bullseye-security main" /etc/apt/sources.list && \ sed -i "5ideb https://mirrors.aliyun.com/debian/ bullseye-updates main non-free contrib" /etc/apt/sources.list && \ sed -i "6ideb-src https://mirrors.aliyun.com/debian/ bullseye-updates main non-free contrib" /etc/apt/sources.list && \ sed -i "7ideb https://mirrors.aliyun.com/debian/ bullseye-backports main non-free contrib" /etc/apt/sources.list && \ sed -i "8ideb-src https://mirrors.aliyun.com/debian/ bullseye-backports main non-free contrib" /etc/apt/sources.list && \ apt-get update -y && \ apt-get install -y fontconfig && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezoneEXPOSE 3002ENTRYPOINT ["sh","-c","java -jar -Xms150m -Xmx150m app.jar"]