springboot官网推荐:使用docker构建springboot镜像(2)最佳实践

springboot官网推荐:使用docker构建springboot镜像(2)最佳实践,第1张

文章目录
  • 一、准备
    • 设置Spring启动应用程序
    • 构建jar包
    • 准备dockerfile
  • 二、dockerfile中的`ENTRYPOINT `高级使用
      • 示例1、缩短`ENTRYPOINT `中的命令长度。
      • 示例2、传入环境变量
      • 示例3、传递springboot参数
  • 三、提高构建镜像效率
    • 1、方法1:缩小基础镜像
    • 2、方法2:优化dockerfile,对jar进行分层传输。
    • 3、方法3:Spring引导层索引方式
    • *本文结束*
    • 限于篇幅问题,我们剩余的内容在《springboot官网推荐:使用docker构建springboot镜像(3)最佳实践》中进行说明
  • 补充
    • 1、如何进入docker镜像?
    • 2、如何进入正在运行的docker容器?
  • 引用:

我在上一节 springboot官网推荐:使用docker构建springboot镜像中大概介绍了一些docker构建springboot镜像的基本方式。今天我们来聊一下比较深入的内容。

一、准备

使用idea初始化项目,本例子使用gradle进行构建,如果方便的话,同时创建一个maven项目,我们对比学习。
此处仅仅选择一个web依赖作为演示

设置Spring启动应用程序

现在,您可以创建一个简单的应用程序:

package com.example.springbootdocker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class SpringbootdockerApplication {

    @RequestMapping("/")
    public String home() {
        return "Hello Docker World";
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringbootdockerApplication.class, args);
    }

}


现在在没有docker容器的况下,可以启动我们的项目并访问了。

构建jar包

如果使用Maven,可以运行 ./mvnw install
如果使用Gradle,可以运行./gradlew build。从而构建出jar包。

准备dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

然后我们在构建docker镜像的命令中传入参数JAR_FILE。比如:

#maven
docker build --build-arg JAR_FILE=target/*.jar -t myorg/myapp .
#gradle
docker build --build-arg JAR_FILE=build/libs/*.jar -t myorg/myapp .

镜像构建完成后,就可以启动运行程序了。

docker run -p 8080:8080 myorg/myapp
二、dockerfile中的ENTRYPOINT 高级使用 示例1、缩短ENTRYPOINT 中的命令长度。

在有的情况下,我们启动java可以会配置jvm等一些参数,会导致ENTRYPOINT ["java","-jar","/app.jar"] 命令变得很长。针对这样的情况,我们可以将其提取到shell脚本中。

  • 创建run.sh 脚本
#!/bin/sh
exec java -jar /app.jar
  • 重新编写dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY run.sh .
COPY target/*.jar app.jar
ENTRYPOINT ["run.sh"]
示例2、传入环境变量
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","${JAVA_OPTS}","-jar","/app.jar"]

构建镜像并启动容器:

docker build -t myorg/myapp .
docker run -p 9000:9000 -e JAVA_OPTS=-Dserver.port=9000 myorg/myapp

但是,创建镜像会失败

因为${}替换需要一个shell。exec表单没有shell启动进程,因此使得${JAVA_OPTS}选项失败。我们可以通过示例1的方式,将入口点移动到脚本(如前面所示的run.sh示例)或在入口点中显式创建shell来解决这个问题,代码如下:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]

这样执行如下命令便可以创建成功了。

docker build -t myorg/myapp .
docker run -p 9000:9000 -e "JAVA_OPTS=-Dserver.port=9000 -Ddebug -Xmx128m" myorg/myapp

JAVA_OPTS=后面跟着多个参数的时候,需要用双引号括起来。

示例3、传递springboot参数

你是否想过这样实现,改变springboot的启动端口。

docker run -p 9000:9000 myorg/myapp --server.port=9000

但是启动后,通过看日志,发现端口还是8080。这是为啥呢?

因为docker命令(–server.port=9000部分)被传递到ENTRYPOINT (sh),而没有用它启动的Java进程。要解决这个问题,需要将命令行从CMD添加到ENTRYPOINT

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar ${@} ]"docker

然后在构建镜像,如下命令行运行就可以了

$ 9000 run -p =:9000 myorg/myapp --server.port9000# 或者
docker
$ 9000 run -p "JAVA_OPTS=-Ddebug -Xmx128m":9000 -e = myorg/myapp --server.port9000docker run -p 9000:9000 myorg/myapp

注意${0}用于代表“命令”(--server.port=9000),
${@}用于“命令参数”(ENTRYPOINT )。
如果使用脚本作为#!/bin/sh,则不需要${0}(在前面的示例中是/run.sh)。以下列表显示了脚本文件中的正确命令:

exec
${JAVA_OPTS} java ${@} -jar /app.jar alpine

到此镜像的构建和传参的高级使用结束了。下面我来说一下如何提高构建镜像效率。

三、提高构建镜像效率

到目前为止,dockerfile配置非常简单,生成的镜像效率不高。原因如下:
1、基础镜像较大,第一次可能会下载基础镜像耗时。我们可以缩小基础镜像。这样也方便我们后期推送应用镜像到仓库中。
2、每次都需要将最新的jar拷贝到镜像中,重新生产镜像的更新这一层的文件。然后我们jar在开发过程中会越来越大,不利于如此 *** 作。我们可以通过将jar拆分为多个层来改进这一点。

1、方法1:缩小基础镜像

可以前面示例中的带有mkdir基本图像是openjdk:8-jdk-alpine。alpine镜像比Dockerhub的标准镜像小。

2、方法2:优化dockerfile,对jar进行分层传输。

因为jar本身就是一个分层的压缩包,如果我们先将其解包,它已经被分为外部依赖项和内部依赖项。要在docker构建的一个步骤中完成这一点,我们需要首先解压缩JAR。以下命令(使用Maven命令,Gradle版本相似)将Spring Boot fat JAR解包:

( target/dependency
;cd target/dependency.. jar -xf )/*.jarDEPENDENCY

编写dockerfile

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG =${DEPENDENCY}target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY [/BOOT-INF/classes /app
ENTRYPOINT "java""-cp","app:app/lib/*","com.example.springbootdocker.SpringbootdockerApplication",]docker

构建镜像

. build -t myorg/myapp com.example.springbootdocker.SpringbootdockerApplication

现在docker中有三层,所有的应用程序资源都在后两层。如果应用程序依赖关系没有改变,那么第一层(来自BOOT-INF/lib)就不需要改变,因此构建速度更快。如果已经缓存了基础层,那么在运行时启动容器的速度也更快。

其中com.example.springbootdocker.SpringbootdockerApplication为启动类的全路径,如果需要,可以将JarLauncher 抽象为参数,以按照示例2中的参数的方式设置。

3、方法3:Spring引导层索引方式

在方法2中,需要在dockerfile中硬编码程序的启动类,接下来介绍的方法3,可以避免这样的硬编码。而是使用mkdir。但是有一定的条件,需要springboot.version>=2.3.x

从Spring Boot 2.3.0开始,使用Spring Boot Maven或Gradle插件构建的JAR文件在JAR文件中包含层信息。该层信息根据应用程序构建之间更改的可能性来分隔应用程序的各个部分。这可以让构建Docker 镜像层更加高效。

引导层信息可用于将JAR内容提取到每个层的目录中:

= target/extracted
java -DjarmodeEXTRACTEDlayertools -jar target/*.jar extract --destination target/extracted

dockerfile:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG =${EXTRACTED}target/extracted
COPY ${EXTRACTED}/dependencies/ ./
COPY ${EXTRACTED}/spring-boot-loader/ ./
COPY ${EXTRACTED}/snapshot-dependencies/ ./
COPY [/application/ ./
ENTRYPOINT "java""org.springframework.boot.loader.JarLauncher",]docker

构建镜像

. build -t myorg/myapp docker

启动镜像:

9000 run -p 本文结束:9000 myorg/myapp

docker 限于篇幅问题,我们剩余的内容在《springboot官网推荐:使用docker构建springboot镜像(3)最佳实践》中进行说明 补充 1、如何进入docker镜像?
docker run -ti --entrypoint /bin/sh myorg/myapp
2、如何进入正在运行的docker容器?
exec # 或者 -ti myapp /bin/sh
docker
exec  -ti myapp /bin/bash
引用:

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#container-images.dockerfiles

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/739088.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-04-28
下一篇2022-04-28

发表评论

登录后才能评论

评论列表(0条)

    保存