DockerFile

Docker高阶

一、Dockerfile 概念

Dockerfile 是一个用于定义 Docker 容器镜像的文本文件。它包含了一组指令,告诉 Docker 如何构建镜像,这些指令包括从基础镜像开始,添加应用程序代码、安装依赖项、设置环境变量,以及配置启动命令等。

执行Dockerfile build能够按照预先设定的指令构建好docker镜像进行使用。


构建步骤

● ① 编写 Dockerfile 文件。

● ② 使用 docker build 命令构建镜像。

● ③ 使用 docker run 命令根据生成的镜像运行容器。


基础知识

● ① 每条保留字指令 都必须为大写字母 且后面要跟随至少一个参数。

● ② 指令按照从上到下的顺序依次执行。

● ③ # 表示注释。

● ④ 每条指令都会创建一个新的镜像层并对镜像进行提交。


执行流程

● ① Docker 从基础镜像上运行一个容器。

● ② 执行一条指令并对容器进行修改。

● ③ 执行类似 docker commit 的操作提交一个新的镜像层。

● ④ Docker 再基于刚才提交的镜像运行一个新的容器。

● ⑤ 依次类推,直到 Dockerfile 文件中的所有指令都执行完成。

Docker 使用 联合文件系统来构建和管理镜像。联合文件系统允许多个文件系统层叠加在一起,从而创建一个单一的、统一的文件系统视图。Docker 中的每个镜像都由多个层组成,而容器则是镜像的可写副本。
Docker采用联合文件系统,在Dockerfile中体现为每执行一个对应的指令就构建出了一个新的镜像,所有指令执行完成后就构建出了目标镜像。


从应用软件的角度来看,Dockerfile、Docker 镜像和 Docker 容器分别代表软件的三个不同的阶段:

○ Dockerfile 是软件的原材料。

○ Docker 镜像是软件的交付品。

○ Docker 容器则可以认为是软件镜像的运行态,即根据镜像运行的容器实例。

Dockerfile 面向开发,Docker 镜像成为交付标准,Docker 容器则涉及部署和运维,三者缺一不可,合力充当了 Docker 体系的基石。

● Dockerfile 定义了进程需要的一切东西。Dockerfile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务以及内核进程打交道的时候,还需要考虑如何设计 namespace 的权限控制)等等。

● Docker 镜像就是在编写了一个 Dockerfile 文件之后,使用 docker build 命令来产生一个镜像,当运行 Docker 镜像的时候会真正的提供服务。

● Docker 容器是直接提供服务的。

二、Dockerfile的保留字指令

指令 说明
FROM 指定基础镜像,推荐使用小镜像,scratch是一个空镜像,常用于多阶段构建。
MAINTAINER 指定维护着信息,已过期,可以使用 LABEL xxx=yyy 来代替。
RUN RUN 指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。
CMD 指定启动容器时默认的命令。例如 CMD ["sh","c","127.0.0.1"]
ENTRYPOINT 指定镜像的默认入口以及运行命令 。例如 ENTRYPOINT["sh","c","127.0.0.1"]
EXPOSE 声明镜像内服务监听的端口,一般而言,此指令只有指导意义,如:SpringBoot 项目的端口是 8080 ,而指定的 EXPOSE 是 8090 ,当然依据 8080 了。
ENV 指定环境变量,可以在 docker run 的时候使用 -e 改变。
ADD 复制指定的 src 路径下的内容到容器中的 dest 路径下,src 可以为 url 会自动下载,也可以为 tar 文件,会自动解压。
COPY 复制本地主机的 src 路径下的内容到镜像中的 dest 路径下,但是不会自动解压等等。
LABEL 标注镜像的一些说明信息,常常用来指定维护者的信息。
VOLUME 创建数据卷挂载点,此处仅是指定挂载点不会绑定挂载目录,具体绑定要到run时指定。
USER 指定运行容器时的用户名或 UID 。
WORKDIR 配置工作目录,为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
ARG 指定镜像内使用的参数(如版本号信息等),可以在 docker build 的时候,使用 --build-args 改变。
OBBUILD 配置当创建的镜像作为其他镜像的基础镜像是,所指定的创建操作指令。
STOPSIGNAL 容器退出的信号值。
HEALTHCHECK 健康检查。
SHELL 指定使用 shell 时的默认 shell 类型。
  • FROM

    • 如果是 Java 应用,可以选择 Java 基础镜像或 Tomcat 基础镜像。
    • 如果是 JS 模块化应用,可以选择 nodejs 基础镜像。
    • 每种语言都有自己的服务器或基础环境镜像,如:Python、Golang、Java、PHP 等。
  • LABEL

    • 一下两种方式都可以指定label
    • LABEL multi.label1="value1" multi.label2="value2" other="value3"
    • LABEL multi.label1="value1"
      multi.label2="value2"
      other="value3"
  • RUN

    • 生成的提交镜像将用于 Dockerfile 中的下一步,分层运行 RUN 指令并生成提交符合 Docker 的核心概念,就像 Git 管理源代码一样。
    • 多个 RUN 指令并没有上下文的关系,换言之,多个 RUN 指令都是在同一个目录操作的。
    • 格式1: RUN < command > 格式2: RUN ["executable", "param1", "param2"]
  • CMD和ENTRYPOINT:

    • CMD和ENTRYPOINT都只会执行一次,如果存在多个,只有最后一个生效。
    • 如果同时设置了这两个命令,且CMD仅仅是选项而不是参数,CMD的内容会作为ENTRYPOINT的参数。
    • 如果两个都是完整的命令,那么会执行最后一条。
  • ARG

    • ARG 指令定义了一个变量,用户可以在构建的时候使用 --build-arg name=value 传递,docker build 命令会将其传递给构建器。
    • --build-arg 指定参数会覆盖 Dockerfile 中指定的同名参数。
    • 如果用户指定了未在 Dockerfile 中定义的构建参数 ,则构建会输出警告 。
    • ARG 只在构建时期有效,运行时期无效
    • 不建议使用构建时变量来传递注入 github 密码、用户凭据等机密,因为构建时变量的值可以通过 docker history 来观察到。
    • ARG 变量定义从 Dockerfile 定义的行开始生效。
    • 使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令。
  • ENV

    • ENV 和 ARG 很类似,但是 ENV 在构建期和运行期都有效,并且使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令。
    • 可以使用 docker run -e name=value 修改 ENV 定义的环境变量。
    • ENV 在构建期就会被解析并持久化,可以通过 docker inspect image 查看。
  • ADD

    • ADD 可以将上下文指定的内容添加(复制)到镜像中,如果是压缩包,ADD 会自动解压;如果是远程 URL ,ADD 会自动下载;但是,ADD 并没有自动下载远程压缩文件并解压的功能
    • src 路径必须在构建的上下文,不能使用 .../.../xxx 这种方式,因为 Docker 构建的第一步是将上下文目录(包括子目录)发送给 Docker 的守护进程。
    • 如果 src 是 URL ,并且 dest 不以 / 结尾,那么就会从 URL 下载文件并将其复制为 dest(名称)。
    • 如果 src 是 URL ,并且 dest 以 / 结尾,会自动推断出文件的名称(URL 的最后一部分)并保存到 dest(目录)中。
    • 如果 src 是目录,则将复制目录的整个内容,包括文件系统元数据。
  • USER

    • USER 指令和 WORKDIR 指令类似,都是改变环境状态并影响以后的层,WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN 、CMD 、以及 ENTRYPOINT 这类命令的身份。
    • USER 只是帮助我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。
  • WORKDIR

    • WORKDIR 指令为 Dockerfile 中跟随它后面的 RUN 、CMD 、ENTRYPOINT、 COPY、ADD 指令设置工作目录。
    • WORKDIR 指令可在 Dockerfile 中多次使用。 如果提供了相对路径,则它将相对于上一个 WORKDIR 指令的路径
  • VOLUME

    • 挂载容器指定的文件夹,如果不存在,会自动创建,即使启动容器的时候没有指定 -v 参数,也会自动进行匿名卷挂载。
    • -v 和 VOLUME 挂载出去的目录,主机变,容器里面也会发生变化,但是
      • ① docker commit 提交当前容器的所有变化为镜像,就会丢弃。
      • ② VOLUME [ "/demo","/app" ] 容器会自动挂载,在之后对这些目录所操作的变化,也会丢弃
      • ③ 挂载仅仅是为了将外边的数据同步到容器里面
    • 用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定要在 volume 声明之前修改内容 。
shell 复制代码
FROM alpine
RUN mkdir /demo && mkdir /app
RUN echo 111 > /demo/a.txt
RUN echo 222 > /app/b.txt
# 挂载 容器指定的文件夹,如果不存在,会自动创建。
# 指定了 VOLUME 指令后,即使启动容器的时候没有指定 -v 参数,也会自动进行匿名卷挂载。容器内的 /demo 和 /app ,需要在启动容器的时候,需要使用 -v 参数进行挂载。
# VOLUME 挂载出去的东西,容器改变也不会最终在 docker commit 的时候生效。
# -v 和 VOLUME 挂载出去的目录,主机变,容器里面也会发生变化,但是 
# ① docker commit 提交当前容器的所有变化为镜像,就会丢弃。
# ② VOLUME [ "/demo","/app" ] 容器会自动挂载,在之后对这些目录所操作的变化,也会丢弃
# ③ 挂载仅仅是为了将外边的数据同步到容器里面
# VOLUME 的最佳实践是写在 CMD 或 ENTRYPOINT 前面
VOLUME [ "/demo","/app" ]
# 下面的 2 个 RUN 指令没有生效,因为 VOLUME 指定的挂载目录是固化配置,当执行到 VOLUME 的时候就已经写入到容器中了,即使后面容器怎么变,也不会改变。
RUN echo 333 > /demo/a.txt
RUN echo 444 > /app/b.txt
CMD ping www.baidu.com

三、虚悬镜像和多阶段构建

3 .1 虚悬镜像

虚悬镜像就是仓库名和标签名都是 < none > 的镜像,俗称 dangling image。

使用 Dockerfile 写一个虚悬镜像:

shell 复制代码
FROM ubuntu
CMD echo 'action is success'

Dockerfile构建后的镜像结果:

3.2 多阶段构建

多阶段构建的目的是让一个镜像变得更小,构建时耗费的资源更少。

示例: 常规package

shell 复制代码
### 我们如何打包一个 Java 镜像
FROM maven
WORKDIR /app
COPY . .
RUN mvn clean package
COPY /app/target/*.jar /app/app.jar
ENTRYPOINT java -jar app.jar
## 这样的镜像有多大?
## 我们最小做到多大??

Docker底层采用的联合文件系统的多层构建提供了很大的操作空间,在构建项目的每一阶段只使用需要用到的资源,删除多余的资源,每一层都做到最小,达到镜像瘦身的目的。

示例: 多阶段package

shell 复制代码
# 以下所有前提 保证 Dockerfile 和项目在同一个文件夹
# 第一阶段:环境构建
FROM maven:3.8.4-openjdk-8-slim AS builder
WORKDIR /app
# 此时有坑,想想 Maven 的标准目录结构
COPY src ./src/
COPY pom.xml .
RUN mvn clean package -Dmaven.test.skip=true
# 第二阶段,最小运行时环境,只需要 jre;第二阶段并不会有第一阶段哪些没用的层
# jdk springboot-actutor(jdk)
FROM openjdk:8u282-slim
LABEL maintainer="xxxx@qq.com"
# 从上一个阶段复制内容
COPY --from=builder /app/target/*.jar /app.jar
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone && touch /app.jar
# 环境变量
# docker run -e JAVA_OPTS="-Xmx512m -Xms64m" -e PARAMS="--spring.profiles.active=dev --server.port=8080" xxx
ENV JAVA_OPTS=""
ENV PARAMS=""
# 运行 jar 包
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]
  • 可以使用 .dockerignore 文件,排除上下文中无需参与构建的资源。
  • 合理使用多阶段构建。
  • 合理使用构建缓存加速构建,但是有时也会有坑,开发的时候建议还是 docker build -t xxx --no-cache --force-rm . 来构建镜像。

咔咔,Dockerfile具体构建可以问gpt,上边的保留字指令和示例可供参考。

相关推荐
蜜桃小阿雯3 分钟前
JAVA开源项目 校园美食分享平台 计算机毕业设计
java·jvm·spring boot·spring cloud·intellij-idea·美食
黄昏_5 分钟前
苍穹外卖Day01-2
java·spring
努力的八爪鱼17 分钟前
记录工作中遇到的问题(持续更新~)
java
求学小火龙19 分钟前
ElasticSearch介绍+使用
java·大数据·elasticsearch
mikey棒棒棒21 分钟前
算法练习题25——合并多项式
java·算法·hashmap·哈希·多项式
学步_技术21 分钟前
Python编码系列—Python工厂方法模式:构建灵活对象的秘诀
开发语言·python·工厂方法模式
kimloner23 分钟前
工厂模式(二):工厂方法模式
java·设计模式·工厂方法模式
月临水32 分钟前
JavaEE:网络编程(UDP)
java·网络·udp·java-ee
Deryck_德瑞克39 分钟前
Java集合笔记
java·开发语言·笔记
MengYiKeNan44 分钟前
C++二分函数lower_bound和upper_bound的用法
开发语言·c++·算法