当前位置: 首页 > article >正文

Kubernetes 深入浅出系列 | 容器剖析之Dockerfile

目录

    • 1.构建过程
    • 2.指令
      • FROM
      • RUN
      • ARG
      • ENV
      • ADD
      • COPY
      • WORKDIR
      • VOLUME
      • EXPOSE
      • CMD 和 ENTRYPOINT
    • 3.多阶段构建
    • 4.镜像瘦身

Dockerfile

  • Dockerfile 是用来构建 Docker 镜像的文本文件,是有一条条构建镜像所需要的指令和参数所组成的脚本文件,类似于 Linux 中的 Shell 脚本文件。
    在这里插入图片描述
    使用 Dockerfile 文件构建镜像的步骤
  • ① 编写 Dockerfile 文件。
  • ② 使用 docker build 命令构建镜像。
  • ③ 使用 docker run 命令根据生成的镜像运行容器。

1.构建过程

Dockerfile 基础知识

  • ① 每条保留字指令都 必须为大写字母 且后面要跟随至少一个参数。
  • ② 指令按照从上到下的顺序依次执行。
  • ③ # 表示注释。
  • ④ 每条指令都会创建一个新的镜像层并对镜像进行提交。

Docker 执行 Dockerfile 的大致流程

  • ① Docker 从基础镜像上运行一个容器。
  • ② 执行一条指令并对容器进行修改。
  • ③ 执行类似 docker commit 的操作提交一个新的镜像层。
  • ④ Docker 再基于刚才提交的镜像运行一个新的容器。
  • ⑤ 依次类推,直到 Dockerfile 文件中的所有指令都执行完成。

总结

  • 从应用软件的角度来看,Dockerfile、Docker 镜像和 Docker 容器分别代表软件的三个不同的阶段:
    • Dockerfile 是软件的原材料。
    • Docker 镜像是软件的交付品。
    • Docker 容器则可以认为是软件镜像的运行态,即根据镜像运行的容器实例。
  • Dockerfile 面向开发,Docker 镜像成为交付标准,Docker 容器则涉及部署和运维,三者缺一不可,合力充当了 Docker 体系的基石。

在这里插入图片描述

  • Dockerfile 定义了进程需要的一切东西。Dockerfile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务以及内核进程打交道的时候,还需要考虑如何设计 namespace 的权限控制)等等。
  • Docker 镜像就是在编写了一个 Dockerfile 文件之后,使用 docker build 命令来产生一个镜像,当运行 Docker 镜像的时候会真正的提供服务。
  • Docker 容器是直接提供服务的。

2.指令

一般而言,Dockerfile 可以分为四个部分:基础镜像信息、维护者信息、镜像操作指令、启动时执行指令。

FROM	指定基础镜像。
RUN	镜像构建过程中运行的命令。
CMD	指定启动容器时默认的命令。
ENTRYPOINT	指定镜像的默认入口以及运行命令 。
EXPOSE	声明镜像内服务监听的端口,一般而言,此指令只有指导意义,如:SpringBoot 项目的端口是 8080 ,而指定的 EXPOSE 是 8090 ,当然依据 8080 了。
ENV	指定环境变量,可以在 docker run 的时候使用 -e 改变。
ADD	复制指定的 src 路径下的内容到容器中的 dest 路径下,src 可以为 url 会自动下载,也可以为 tar 文件,会自动解压。
COPY	复制本地主机的 src 路径下的内容到镜像中的 dest 路径下,但是不会自动解压等等。
LABEL	指定生成镜像的元数据标签信息。
VOLUME	创建数据卷挂载点。
USER	指定运行容器时的用户名或 UID 。
WORKDIR	配置工作目录,为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
ARG	指定镜像内使用的参数(如版本号信息等),可以在 docker build 的时候,使用 --build-args 改变。
OBBUILD	配置当创建的镜像作为其他镜像的基础镜像是,所指定的创建操作指令。
STOPSIGNAL	容器退出的信号值。
HEALTHCHECK	健康检查。
SHELL	指定使用 shell 时的默认 shell 类型。

自定义镜像

  • 要求:CentOS 7 镜像具备 vim 、ifconfig 和 JDK8 的功能。
    温馨提示:编写 Dockerfile ,可以使用 VsCode 编辑器,装上 Docker 插件,这样可以校验 Dockerfile 的语法。
  • ① 编写 Dockerfile :
# 模板镜像
FROM centos:7.9.2009
# 工作目录
WORKDIR /usr/local/java
# 安装依赖
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \
    && yum clean all \
    && yum makecache \
    && yum -y update \
    && yum -y install wget \
    && yum -y install glibc.i686 \
    && yum -y install vim \
    && yum -y install net-tools \
    && yum -y install curl
# 下载 JDK 并解压   
RUN curl https://repo.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz | tar -xzC /usr/local/java/ --strip-components 1 \
    && ls -lah /usr/local/java/
# 设置环境变量
ENV JAVA_HOME=/usr/local/java
ENV JRE_HOME=$JAVA_HOME/jre
ENV CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH=$JAVA_HOME/bin:$PATH
# 启动命令
CMD ["java","-version"]
  • ② 构建:
docker build --no-cache --force-rm -t 镜像名称:TAG .

说明:

  • –no-cache : 表示构建的时候不使用之前的缓存。
  • –force-rm:删除构建过程中的中间容器层。
  • .:表示构建环境的上下文,通常而言是 . ,表示以 Dockerfile 所在的目录作为构建的起点。

FROM

  • FROM 指定基础镜像,推荐使用 alpine 或 slim 之类的基础小镜像。
  • scratch 镜像是一个空镜像,常常用于多阶段构建。
  • 『问』:如何确定我们需要的基础镜像?
  • 『答』:
  • ① 如果是 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"

构建期和运行期

  • 构建期是指使用 Dockerfile 构建镜像的整个过程时期,如:docker build 等。
  • 运行期是指使用之前构建的镜像启动容器的过程,如:docker start 、docker run 等。

RUN

  • RUN 指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。
  • 生成的提交镜像将用于 Dockerfile 中的下一步,分层运行 RUN 指令并生成提交符合 Docker 的核心概念,就像 Git 管理源代码一样。
  • 注意:多个 RUN 指令并没有上下文的关系,换言之,多个 RUN 指令都是在同一个目录操作的。
  • RUN 有两种格式:
#shell 形式,/bin/bash -c 的方式运行,可以破坏 shell 字符串
RUN <command>
# exec 的形式
RUN ["executable", "param1", "param2"]
  • 在 RUN 中可以使用 \ 将一条 RUN 指令继续到下一行。
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

# 等同于
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
 # 测试案例
FROM alpine
ENV msg='hello Docker'
RUN echo $msg
RUN ["echo","$msg"]
RUN /bin/sh -c 'echo $msg'
RUN ["/bin/sh","-c","echo $msg"]
CMD sleep 1000
docker build -t test --force-rm --no-cache .
  • 总结:
    • 由于 [] 不是 shell 形式,所以不能输出变量信息,而是输出 $msg 。其他任何 /bin/sh -c 的形式都可以输出变量信息。
    • shell 是 RUN /bin/sh -c 的方式,RUN [“/bin/sh”,“-c”,command] 的 exec 方式等同于 shell 方式,而 RUN [“/bin/sh”,command] 的 exec 默认不会进行变量替换。

ARG

ARG name=defaultValue
  • ARG 指令定义了一个变量,用户可以在构建的时候使用 --build-arg name=value 传递,docker build 命令会将其传递给构建器。

  • –build-arg 指定参数会覆盖 Dockerfile 中指定的同名参数。

  • 如果用户指定了 未在 Dockerfile 中定义的构建参数 ,则构建会输出 警告 。

  • ARG 只在构建时期有效,运行时期无效。

  • 不建议使用构建时变量来传递注入 github 密码、用户凭据等机密,因为构建时变量的值可以通过 docker history 来观察到。

  • ARG 变量定义从 Dockerfile 定义的行开始生效。

  • 使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令。

  • 示例:

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

# 在构建时期会运行的指令(根据 Dockerfile 创建一个镜像的整个过程时期)
RUN echo 1111
RUN echo ${param}

# 在运行时候会运行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
# docker start 或 docker run
CMD ["/bin/sh","-c","echo 2222;echo $param"]
docker build -t test02 --force-rm --no-cache --build-arg param=test .

ENV

  • 语法:
ENV name=value
  • ENV 和 ARG 很类似,但是 ENV 在构建期和运行期都有效,并且使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令。

  • 可以使用 docker run -e name=value 修改 ENV 定义的环境变量。

  • 示例:

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

# ENV 在构建期和运行期都有效,但是只能在运行期进行修改,修改通过 docker run -e name=value 命令。
ENV app=taobao

# 在构建时期会运行的指令(根据 Dockerfile 创建一个镜像的整个过程时期)
RUN echo 1111
RUN echo ${param}
RUN echo ${app}

# 在运行时候会运行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
# docker start 或 docker run
CMD ["/bin/sh","-c","echo 2222;echo $param;echo app_$app"]
  • 坑:ENV 在构建期就会被解析并持久化,可以通过 docker inspect image 查看。

  • 示例:

# ENV 的坑
FROM alpine
ENV msg="hello"
ENV msg2=${msg}
RUN echo ${msg}
RUN echo ${msg2}
# 如果运行期修改了 msg=666,那么 msg 和 msg2 的值是 666 和 hello ,因为 ENV 在构建期就会被解析并持久化。
CMD ["/bin/sh","-c","echo $msg;echo $msg2;"]

ADD

  • ADD 可以将上下文指定的内容添加(复制)到镜像中,如果是压缩包,ADD 会自动解压;如果是远程 URL ,ADD 会自动下载;但是,ADD 并没有自动下载远程压缩文件并解压的功能。
  • 语法:
ADD src dest
  • 注意:

  • src 路径必须在构建的上下文,不能使用 …/…/xxx 这种方式,因为 Docker 构建的第一步是将上下文目录(包括子目录)发送给 Docker 的守护进程。

  • 如果 src 是 URL ,并且 dest 不以 / 结尾,那么就会从 URL 下载文件并将其复制为 dest(名称)。

  • 如果 src 是 URL ,并且 dest 以 / 结尾,会自动推断出文件的名称(URL 的最后一部分)并保存到 dest(目录)中。

  • 如果 src 是目录,则将复制目录的整个内容,包括文件系统元数据。

  • 示例:

FROM alpine
# 如果是远程文件,自动下载
# 如果是压缩文件,自动解压
# 注意:ADD 并没有自动下载远程压缩文件并解压的功能
# 将当前内容复制到 alpine 中
ADD https://download.redis.io/releases/redis-6.2.6.tar.gz /dest
# 注意,RUN 指令上下并没有上下文的关系。
RUN ls -l
FROM alpine
# 如果是远程文件,自动下载
# 如果是压缩文件,自动解压
# 注意:ADD 并没有自动下载远程压缩文件并解压的功能
ADD https://download.redis.io/releases/redis-6.2.6.tar.gz /dest/
# 注意,RUN 指令上下并没有上下文的关系。
RUN ls -l
RUN cd /dest && ls -l

COPY

  • 语法:
    COPY [–chown=:] …

  • COPY 和 ADD 类似,都有将上下文指定的内容添加(复制)到镜像中的功能,只不过 ADD 的自动下载或解压压缩文件的功能。

  • –chown 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,而在 Windows 容器上不起作用。

  • 示例:略。

USER

  • 语法:
USER <user>[:<group>]
USER <UID>[:<GID>]
  • USER 指令和 WORKDIR 指令类似,都是改变环境状态并影响以后的层,WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN 、CMD 、以及 ENTRYPOINT 这类命令的身份。

  • 注意:USER 只是帮助我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。

  • 示例:USER 身份问题

FROM alpine
# 创建用户和组
RUN addgroup -S test && adduser -S test -G test -h /home/test
# USER 只是帮准我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。USER 则是改变之后层的执行 RUN、CMD、以及 ENTRYPOINT 这类命令的身份。
USER test:test
COPY *.txt /test/
# 注意:一旦声明了 USER 之后,USER 后面的 RUN、CMD、ENTRYPOINT 的身份就是 test ,而 a.txt 是主机生成的,身份是 root ,必然会报错,权限不对。
RUN cd /test && ls -l && echo 1111 > a.txt
FROM alpine
# 创建用户和组
RUN addgroup -S test && adduser -S test -G test -h /home/test
# USER 只是帮准我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。USER 则是改变之后层的执行 RUN、CMD、以及 ENTRYPOINT 这类命令的身份。
USER test:test
# 通过 COPY 指定的 chown 功能改变复制文件的权限
COPY --chown=test:test *.txt /test/
# 注意:一旦声明了 USER 之后,USER 后面的 RUN、CMD、ENTRYPOINT 的身份就是 test ,而 a.txt 是主机生成的,身份是 root ,但是,因为使用了 COPY --chown=test:test ,所以文件的权限是 test
RUN cd /test && ls -l && echo 1111 > a.txt

WORKDIR

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

# 效果:/a/b/c
  • WORKDIR 指令也可以用在环境变量上,如:
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

# 效果:/path/$DIRNAME

VOLUME

#可以是JSON数组
VOLUME ["/var/log/"]
#可以直接写
VOLUME /var/log
#可以空格分割多个
VOLUME /var/log /var/db
  • 注意:用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定要在 volume 声明之前修改内容 。
FROM alpine
# 挂载 容器指定的文件夹,如果不存在,会自动创建。
# 指定了 VOLUME 指令后,即使启动容器的时候没有指定 -v 参数,也会自动进行匿名卷挂载。
VOLUME [ "/demo","/app" ]
CMD ping www.baidu.com
  • 示例:用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定要在 volume 声明之前修改内容
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

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

EXPOSE [80,443]

EXPOSE 80/tcp

EXPOSE 80/udp
  • EXPOSE 指令通知 Docker 容器在运行的时候在指定的网络端口上进行侦听,可以指定端口是侦听 TCP 还是 UDP ,如果没有指定,默认就是 TCP 。

  • EXPOSE 指令实际上不会发布端口,它充当了构建镜像人员和运行容器人员之间的一种文档,即打算发布那些端口的信息,要在运行容器时映射端口,需要使用 docker run -p xxx:xxx 或 docker run -P 的命令。

  • 示例:略。

CMD 和 ENTRYPOINT

  • CMD 的语法:
# exec 方式, 首选方式
CMD ["executable","param1","param2"]

# 为 ENTRYPOINT 提供默认参数
CMD ["param1","param2"]

# shell 形式
CMD command param1 param2
  • ENTRYPOINT 的语法:
# exec 方式, 首选方式
ENTRYPOINT ["executable", "param1", "param2"]

# shell 形式
ENTRYPOINT command param1 param2

注意:

  • ① 如果 Dockerfile 文件中,使用多个 CMD 或 ENTRYPOINT 作为唯一的入口,即写多个 CMD 或 ENTRYPOINT ,则会产生覆盖现象,只有最后一个生效。
  • ② shell 方式是可以读取环境变量的值的(如:${xxx}),默认情况下,exec 的方式是读取不了环境变量值的,但是 exec 方式的 [“/bin/sh”,”-c”,”xxx”] 等同于 shell 方式,也是可以读取环境变量值。
  • ③ 官方推荐使用 RUN 、CMD 以及 ENTRYPOINT 使用 exec 的方式。
  • ④ 如果既有 CMD 的 exec 方式,又有 ENTRYPOINT 的 exec 方式,那么 CMD 是作为 ENTRYPOINT 的参数的(最佳实践)。
  • ⑤ 使用 docker run -d xxx CMD 命令是可以覆盖 Dockerfile 中的 CMD 指令的,不是覆盖 exec 方式数组中的一个,而是全部。
FROM alpine
#  CMD 和 ENTRYPOINT 作为唯一入口,写多个,只有最后一个生效
CMD ping baidu.com
CMD ping bing.com
FROM alpine
#  CMD 和 ENTRYPOINT 作为唯一入口,写多个,只有最后一个生效
ENTRYPOINT ping baidu.com
ENTRYPOINT ping bing.com
FROM alpine
# java -jar xxx.jar --spring.profile.active=dev --server.port=8888
# CMD [ "-jar","xxx.jar","--spring.profile.active=dev","--server.port=8888"]
# ENTRYPOINT [ "java" ]
CMD ["baidu.com"]
ENTRYPOINT ["ping"]
FROM alpine
# java -jar xxx.jar --spring.profile.active=dev --server.port=8888
# CMD [ "-jar","xxx.jar","--spring.profile.active=dev","--server.port=8888"]
# ENTRYPOINT [ "java" ]
CMD ["baidu.com"]
ENTRYPOINT ["ping"]

3.多阶段构建

官网:docs.docker.com

  • 多阶段构建中,只有最后一个阶段生成的内容会被包含在最终的镜像中。
  • 多阶段构建出现的目的就是为了解决如何让一个镜像变得更小。
  • 示例:常规打包
### 我们如何打包一个 Java 镜像
FROM maven
WORKDIR /app
COPY . .
RUN mvn clean package
COPY /app/target/*.jar /app/app.jar
ENTRYPOINT java -jar app.jar

## 这样的镜像有多大?

## 我们最小做到多大??

生产示例

  • 需求:将 SpringBoot 项目使用多阶段构建打包成 Docker 镜像,并进行启动。
  • ① 环境要求:
  • JDK :8。
  • Maven :3.5+。
  • IDEA:2.22+。
  • SpringBoot :2.5.10。
  • ② 新建 SpringBoot 工程:

在这里插入图片描述
在这里插入图片描述

  • ② pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.10</version>
    </parent>

    <packaging>jar</packaging>

    <groupId>com.github.demo</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1</version>

    <name>demo</name>
    <description>demo</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.10</version>
                <configuration>
                    <layout>ZIP</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- 为了加速下载需要在 pom 文件中复制如下 -->
    <repositories>
        <repository>
            <id>aliyun</id>
            <name>Nexus Snapshot Repository</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
            </releases>
            <!-- snapshots默认是关闭的,需要开启  -->
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>aliyun</id>
            <name>Nexus Snapshot Repository</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

  • ③ 启动类:
package com.github.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • ④ Dockerfile :
# 以下所有前提 保证 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" ]
docker build -t test --force-rm --no-cache .
docker run -d -P -e JAVA_OPTS="-Xmx512m -Xms64m" -e PARAMS="--spring.profiles.active=dev --server.port=8080" test

4.镜像瘦身

  • ① 选择最小的基础镜像。
  • ② 合并 RUN 环节的所有指令,少生成一些镜像层。
  • ③ RUN 期间可能安装其它程序会生成临时缓存,要自行删除,如:
# 开发期间,逐层验证正确的
RUN xxx
RUN xxx
RUN aaa \
aaa \
vvv \
# 生产环境
RUN apt-get update && apt-get install -y \
        bzr \
        cvs \
        git \
        mercurial \
        subversion \
        && rm -rf /var/lib/apt/lists/*
  • ④ 使用 .dockerignore 文件,排除上下文中无需参与构建的资源。
  • ⑤ 合理使用多阶段构建。
  • ⑥ 合理使用构建缓存加速构建,但是有时也会有坑,开发的时候建议还是 docker build -t xxx --no-cache --force-rm . 来构建镜像。

http://www.kler.cn/a/349806.html

相关文章:

  • 零基础Vue入门4——Vue3基础核心
  • 神经网络和深度学习
  • 中间件安全
  • 【微服务与分布式实践】探索 Sentinel
  • mamba论文学习
  • matlab提取滚动轴承故障特征
  • Apache Doris 2.0.15 版本发布
  • 【网络基础知识】网络通信概述与TCPIP、UDP协议
  • 1.C++经典实例-计算两个数的最大公约数
  • 前沿论文解读:CARAT
  • 【LangChain】(三)如何利用LangChain和TruLens提升大规模语言模型的质量?全面教程与实战案例!
  • -bash: dig: command not found
  • C++中的vector介绍(常用函数)
  • 龙信科技:引领电子物证技术,助力司法公正
  • 【WebGis开发 - Cesium】三维可视化项目教程---图层管理基础
  • STM32CUBE导入DSP库时出现大量multiple definition,first defined here
  • AutoML:自动化机器学习的技术与应用
  • Leetcode 第 359 场周赛题解
  • Vue 3 数组变更详解:哪些操作会修改原数组?| 笔记
  • 信息与计算科学:“数学 + 计算机”,奏响未来科技新乐章
  • 微信小程序手机号授权获取(aes加密手机号)
  • 【WKWebview】WKWebView Cookie 同步
  • 【漏洞复现】SpringBlade menu/list SQL注入漏洞
  • 将图片转换为视频
  • 【Linux】Linux进程概念
  • 域2:资产安全 第5章-保护资产安全