SpringBoot与Docker集成
SpringBoot与Docker集成
许多人正在使用容器包装其Spring Boot应用程序,而构建容器并不是一件容易的事。这是Spring Boot应用程序开发人员的指南,容器对于开发人员而言并非总是很好的抽象-它们会迫使您学习和思考非常低级的问题-但是有时您会被要求创建或使用容器,因此有必要了解这些基本要素。在这里,我们旨在向您展示一些您需要创建自己的容器时可以做出的选择。
我们将假定您知道如何创建和构建基本的Spring Boot应用程序。如果不这样做,请转到入门指南之一,例如有关构建REST服务的指南。从此处复制代码,并使用以下一些想法进行练习。
在Docker上也有一个入门指南,这也是一个很好的起点,但是它没有涵盖我们在此所做的选择范围,也没有详细介绍。 |
基本的Dockerfile
Spring Boot应用程序很容易转换为可执行的JAR文件。所有入门指南都这样做,从Spring Initializr下载的每个应用程序都将具有一个创建可执行JAR的构建步骤。与Maven ./mvnw install
和Gradle一起./gradlew build
。然后,在项目的顶层,运行该JAR的基本Dockerfile如下所示:
Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
将JAR_FILE
可在作为的一部分被传递docker
命令(这将是Maven和摇篮不同)。例如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 .
当然,一旦选择了构建系统,就不需要ARG
-只需对jar位置进行硬编码即可。例如Maven:
Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
然后我们可以简单地用
$ docker build -t myorg/myapp .
并像这样运行它:
$ docker run -p 8080:8080 myorg/myapp. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.0.2.RELEASE)Nov 06, 2018 2:45:16 PM org.springframework.boot.StartupInfoLogger logStarting
INFO: Starting Application v0.1.0 on b8469cdc9b87 with PID 1 (/app.jar started by root in /)
Nov 06, 2018 2:45:16 PM org.springframework.boot.SpringApplication logStartupProfileInfo
...
如果您想在图像内部四处浏览,可以像这样打开其中的外壳(基本图像没有bash
):
$ docker run -ti --entrypoint /bin/sh myorg/myapp
/ # ls
app.jar dev home media proc run srv tmp var
bin etc lib mnt root sbin sys usr
/ #
我们在示例中使用的高山基础容器没有bash ,因此这是一个ash 外壳。它具有某些功能,bash 但不是全部。
|
如果您有一个正在运行的容器,并且想窥视它,请使用docker exec
以下方法:
$ docker run --name myapp -ti --entrypoint /bin/sh myorg/myapp
$ docker exec -ti myapp /bin/sh
/ #
其中myapp
在--name
传递给docker run
命令。如果您不使用,--name
则docker分配一个助记符名称,您可以从的输出中抓取该名称docker ps
。您也可以使用容器的sha标识符代替名称,该名称也可以从中看到docker ps
。
入口
使用了Dockerfile 的exec形式,ENTRYPOINT
因此没有包装Java进程的外壳。优点是java进程将响应KILL
发送到容器的信号。实际上,这意味着,例如,如果您docker run
在本地使用图像,则可以使用停止它CTRL-C
。如果命令行有点长,您可以COPY
在运行它之前将其提取到shell脚本中,然后提取到映像中。例:
Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY run.sh .
COPY target/*.jar app.jar
ENTRYPOINT ["run.sh"]
请记住使用exec java …
启动Java进程(以便它可以处理KILL
信号):
run.sh
#!/bin/sh
exec java -jar /app.jar
入口点另一个有趣的方面是您是否可以在运行时将环境变量注入到Java进程中。例如,假设您希望具有在运行时添加java命令lline选项的选项。您可以尝试执行以下操作:
Dockerfile
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
这将失败,因为${}
替换需要外壳程序;exec表单不使用外壳程序来启动进程,因此不会应用选项。您可以通过将入口点移动到脚本(如run.sh
上面的示例),或在入口点中显式创建外壳来解决这个问题。例如:
Dockerfile
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 run -p 8080:8080 -e "JAVA_OPTS=-Ddebug -Xmx128m" myorg/myapp
.... ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.2.0.RELEASE)
...
2019-10-29 09:12:12.169 DEBUG 1 --- [ main] ConditionEvaluationReportLoggingListener :============================
CONDITIONS EVALUATION REPORT
============================
...
(显示Spring Boot DEBUG
生成的全部输出的一部分-Ddebug
。)
ENTRYPOINT
与上面的显式shell一起使用,意味着您可以将环境变量传递到java命令中,但是到目前为止,您还不能为Spring Boot应用程序提供命令行参数。此技巧无法在端口9000上运行应用程序:
$ docker run -p 9000:9000 myorg/myapp --server.port=9000. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.2.0.RELEASE)
...
2019-10-29 09:20:19.718 INFO 1 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
它不起作用的原因是因为docker命令(该--server.port=9000
部分)传递给了入口点(sh
),而不是传递给了它启动的Java进程。要解决此问题,您需要将命令行从添加CMD
到ENTRYPOINT
:
Dockerfile
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 ${0} ${@}"]
$ docker run -p 9000:9000 myorg/myapp --server.port=9000. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.2.0.RELEASE)
...
2019-10-29 09:30:19.751 INFO 1 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9000
注意${0}
“命令”(在此情况下为第一个程序参数)和${@}
“命令参数”(其余程序参数)的用法。如果您使用脚本作为入口点,则不需要${0}
(/app/run.sh
在上面的示例中)。例:
run.sh
#!/bin/sh
exec java ${JAVA_OPTS} -jar /app.jar ${@}
到目前为止,docker配置非常简单,并且生成的映像不是很有效。docker映像只有一个文件系统层,其中包含胖子罐,我们对应用程序代码进行的每一次更改都会更改该层,该层可能为10MB或更大(某些应用程序甚至为50MB)。我们可以通过将JAR分为多个层来改善这一点。
较小的图像
请注意,以上示例中的基本图像为openjdk:8-jdk-alpine
。这些alpine
图像比Dockerhub的标准openjdk
库图像小。尚无Java 11的正式高山图像(AdoptOpenJDK已有一段时间,但不再出现在其Dockerhub页面上)。您还可以使用“ jre”标签而不是“ jdk”在基本映像中节省大约20MB。并非所有的应用程序都可以与JRE一起使用(而不是JDK),但是大多数的应用程序都可以,并且确实有些组织会强制执行每个应用程序必须遵循的规则,因为存在滥用某些JDK功能(例如编译)的风险。
可以使您缩小图像的另一个技巧是使用JLink,它与OpenJDK 11捆绑在一起。JLink允许您从完整JDK中的模块子集构建自定义JRE分发,因此您不需要JRE或JDK在基本图像中。原则上,这将使您的总映像大小小于使用openjdk
官方docker映像。实际上,您还无法使用alpine
带有JDK 11的基本映像,因此您对基本映像的选择将受到限制,并且可能会导致最终映像尺寸更大。另外,您自己的基本映像中的自定义JRE无法在其他应用程序之间共享,因为它们将需要不同的自定义。因此,对于所有应用程序来说,它们可能都有较小的映像,但是启动它们仍需要更长的时间,因为它们无法从缓存JRE层中受益。
最后一点突出了图像构建者的一个真正重要的关注点:目标不一定总是要构建尽可能小的图像。通常,较小的图像是一个好主意,因为它们只需要较少的时间就可以上传和下载,但是前提是它们中的所有层都没有被缓存。如今,图像注册表非常复杂,通过尝试巧妙地构建图像,您很容易失去这些功能的优势。如果使用公共基础层,则图像的总大小将变得无关紧要,并且随着注册管理机构和平台的发展,图像的总大小可能会变得更小。话虽如此,尝试并优化我们的应用程序映像中的各层仍然很重要且有用,但是目标始终应该是将变化最快的东西放在最高层中,
更好的Dockerfile
由于罐子本身的包装方式,Spring Boot胖子罐子自然具有“层”。如果我们先拆包,它将已经分为内部和外部依赖关系。为了在Docker构建中一步一步做到这一点,我们需要首先解压jar。例如(坚持使用Maven,但Gradle版本非常相似):
$ mkdir target/dependency
$ (cd target/dependency; jar -xf ../*.jar)
$ docker build -t myorg/myapp .
有了这个 Dockerfile
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 ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
现在有3层,所有应用程序资源都位于后面的2层中。如果应用程序依存关系不变,那么第一层(from BOOT-INF/lib
)将不会改变,因此构建会更快,并且只要基本层已经被缓存,容器在运行时的启动也会更快。
我们使用了一个硬编码的主应用程序类hello.Application 。对于您的应用程序,这可能会有所不同。如果需要,可以与另一个参数化ARG 。您也可以将Spring Boot fat复制JarLauncher 到映像中,然后使用它来运行应用程序-它可以工作,并且不需要指定主类,但是启动时会慢一些。
|
调整
如果您想尽快启动您的应用程序(大多数人都这样做),则可以考虑一些调整。这里有一些想法:
使用
spring-context-indexer
(指向docs的链接)。对于小型应用程序而言,它不会增加太多,但对您有所帮助。如果负担不起,请不要使用执行器。
使用Spring Boot 2.1和Spring 5.1。
使用(命令行参数或系统属性等)修复Spring Boot配置文件的位置
spring.config.location
。关闭JMX-您可能不需要在容器中使用-
spring.jmx.enabled=false
使用运行JVM
-noverify
。还请考虑-XX:TieredStopAtLevel=1
(这将在以后降低JIT速度,但会节省启动时间)。使用Java 8的容器内存提示:
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
。对于Java 11,默认情况下是自动的。
您的应用程序在运行时可能不需要完整的CPU,但需要多个CPU才能尽快启动(至少2、4个更好)。如果您不介意启动速度较慢,则可以将CPU的速度降低到4以下。如果您被迫以少于4个CPU的速度启动,则可能会有所帮助,-Dspring.backgroundpreinitializer.ignore=true
因为这会阻止Spring Boot创建可能不会创建的新线程能够使用(适用于Spring Boot 2.1.0及更高版本)。
多阶段构建
在Dockerfile
上述假设脂肪JAR已经建在命令行上。您也可以使用多阶段构建在Docker中执行此步骤,将结果从一个映像复制到另一个映像。使用Maven的示例:
Dockerfile
FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/appCOPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src srcRUN ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
第一个图像标记为“ build”,它用于运行Maven并构建胖罐,然后解压缩它。拆包也可以由Maven或Gradle完成(这是《入门指南》中采用的方法)-确实没有太大区别,只是必须编辑构建配置并添加插件。
请注意,源代码已分为4层。后面的层包含构建配置和应用程序的源代码,而前面的层包含构建系统本身(Maven包装器)。这是一个很小的优化,这也意味着我们不必将target
目录复制到docker映像,即使是用于构建的临时目录也是如此。
源代码更改的每个构建都会很慢,因为必须在第一RUN
部分中重新创建Maven缓存。但是您拥有一个完全独立的构建,只要拥有docker,任何人都可以运行它来使您的应用程序运行。在某些环境中,例如在需要与不懂Java的人共享代码的环境中,这很有用。
实验特征
Docker 18.06带有一些“实验性”功能,其中包括一种缓存构建依赖项的方法。要打开它们,您需要在守护程序(dockerd
)中有一个标志,并且在运行客户端时还需要一个环境变量,然后可以在其中添加魔术第一行Dockerfile
:
Dockerfile
# syntax=docker/dockerfile:experimental
然后该RUN
指令接受一个新标志--mount
。这是一个完整的示例:
Dockerfile
# syntax=docker/dockerfile:experimental
FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/appCOPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src srcRUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
然后运行它:
$ DOCKER_BUILDKIT=1 docker build -t myorg/myapp .
...=> /bin/sh -c ./mvnw install -DskipTests 5.7s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:3defa...=> => naming to docker.io/myorg/myapp
使用实验性功能,您可以在控制台上获得不同的输出,但是您可以看到,一旦缓存变热,Maven构建现在只需要几秒钟而不是几分钟。
此Dockerfile
配置的Gradle版本非常相似:
Dockerfile
# syntax=docker/dockerfile:experimental
FROM openjdk:8-jdk-alpine AS build
WORKDIR /workspace/appCOPY . /workspace/app
RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build
RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/build/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
这些功能处于试验阶段时,用于打开和关闭buildkit的选项取决于docker 您所使用的版本。检查文档以了解您拥有的版本(上面的示例对于docker 18.0.6 是正确的)。
|
安全方面
就像在传统的VM部署中一样,不应使用root权限运行进程。相反,映像应包含运行该应用程序的非root用户。
在中Dockerfile
,可以通过添加另一层来添加一个(系统)用户和组,然后将其设置为当前用户(而不是默认用户root)来实现:
Dockerfile
FROM openjdk:8-jdk-alpineRUN addgroup -S demo && adduser -S demo -G demo
USER demo...
如果有人设法突破您的应用程序并在容器内运行系统命令,这将限制他们的功能(特权最小的原则)。
一些其他Dockerfile 命令仅以root用户身份运行,因此也许您必须将USER命令进一步向下移动(例如,如果您打算将更多软件包安装到仅以root用户身份使用的容器中)。
|
不使用的其他方法Dockerfile 可能更合适。例如,在稍后描述的buildpack方法中,大多数实现默认情况下将使用非root用户。
|
构建插件
如果您不想docker
直接在构建中调用,那么Maven和Gradle就有很多丰富的插件可以为您工作。这里仅仅是少数。
Spotify Maven插件
在Spotify的Maven插件是一个受欢迎的选择。它要求应用程序开发人员编写a Dockerfile
,然后docker
为您运行,就像您在命令行上一样。docker image标签和其他内容有一些配置选项,但它使您的应用程序中的docker知识集中在Dockerfile
,很多人都喜欢。
对于真正的基本用法,它无需额外配置即可直接使用:
$ mvn com.spotify:dockerfile-maven-plugin:build
...
[INFO] Building Docker context /home/dsyer/dev/demo/workspace/myapp
[INFO]
[INFO] Image will be built without a name
[INFO]
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.630 s
[INFO] Finished at: 2018-11-06T16:03:16+00:00
[INFO] Final Memory: 26M/595M
[INFO] ------------------------------------------------------------------------
这将构建一个匿名docker镜像。我们现在可以docker
在命令行上用标记,或者使用Maven配置将其设置为repository
。示例(不更改pom.xml
):
$ mvn com.spotify:dockerfile-maven-plugin:build -Ddockerfile.repository=myorg/myapp
或在pom.xml
:
pom.xml
<build><plugins><plugin><groupId>com.spotify</groupId><artifactId>dockerfile-maven-plugin</artifactId><version>1.4.8</version><configuration><repository>myorg/${project.artifactId}</repository></configuration></plugin></plugins>
</build>
Palantir Gradle插件
该真知晶球摇篮插件工作有Dockerfile
,它也能产生一个Dockerfile
适合你,然后它运行docker
,如果你是在命令行中运行它。
首先,您需要将插件导入您的build.gradle
:
build.gradle
buildscript {...dependencies {...classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0')}
}
然后最后应用插件并调用其任务:
build.gradle
apply plugin: 'com.palantir.docker'group = 'myorg'bootJar {baseName = 'myapp'version = '0.1.0'
}task unpack(type: Copy) {dependsOn bootJarfrom(zipTree(tasks.bootJar.outputs.files.singleFile))into("build/dependency")
}
docker {name "${project.group}/${bootJar.baseName}"copySpec.from(tasks.unpack.outputs).into("dependency")buildArgs(['DEPENDENCY': "dependency"])
}
在此示例中,我们选择将Spring Boot胖子罐解压缩到build
目录中的特定位置,该目录是docker构建的根目录。然后,上面的多层(不是多阶段)Dockerfile
将起作用。
Jib Maven和Gradle插件
Google有一个名为Jib的开源工具,它相对较新,但出于多种原因却很有趣。可能最有趣的事情是您不需要docker来运行它-它使用与您获得的输出相同的标准输出来构建映像,docker build
但docker
除非您要求使用否则不使用-因此它可以在没有docker的环境中工作已安装(在构建服务器中并不罕见)。您也不需要Dockerfile
(无论如何都会被忽略),也不需要任何东西pom.xml
来获得在Maven中构建的映像(Gradle要求您至少在中安装插件build.gradle
)。
Jib的另一个有趣的功能是,它对层有看法,并且以与Dockerfile
上面创建的多层略有不同的方式优化了它们。就像在胖子罐中一样,Jib将本地应用程序资源与依赖项分离开来,但它走得更远,而且还将快照依赖项放入一个单独的层中,因为它们更容易发生变化。有一些配置选项可用于进一步自定义布局。
Maven的示例(不更改pom.xml
):
$ mvn com.google.cloud.tools:jib-maven-plugin:build -Dimage=myorg/myapp
要运行上述命令,您将需要具有在myorg
存储库前缀下推送到Dockerhub的权限。如果您docker
在命令行上通过进行了身份验证,则可以在本地~/.docker
配置中使用。您还可以在您的仓库中设置Maven“服务器”身份验证~/.m2/settings.xml
(id
存储库的身份很重要):
settings.xml
<server><id>registry.hub.docker.com</id><username>myorg</username><password>...</password></server>
还有其他选项,例如,您可以docker
使用dockerBuild
目标而不是来针对docker守护程序在本地构建(例如在命令行上运行)build
。还支持其他容器注册表,对于每个容器注册表,您将需要通过docker或Maven设置来设置本地身份验证。
一旦将gradle插件放入,它就具有类似的功能build.gradle
,例如
build.gradle
plugins {...id 'com.google.cloud.tools.jib' version '1.8.0'
}
或使用入门指南中使用的较旧样式:
build.gradle
buildscript {repositories {maven {url "https://plugins.gradle.org/m2/"}mavenCentral()}dependencies {classpath('org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE')classpath('com.google.cloud.tools.jib:com.google.cloud.tools.jib.gradle.plugin:1.8.0')}
}
然后您可以用
$ ./gradlew jib --image=myorg/myapp
与Maven构建一样,如果您docker
在命令行上使用进行了身份验证,则映像推送将从您的本地~/.docker
配置进行身份验证。
持续集成
如今(或应该如此),自动化已成为每个应用程序生命周期的一部分。人们用来实现自动化的工具往往非常擅长从源代码调用构建系统。因此,如果您得到一个docker映像,并且构建代理中的环境与开发人员自己的环境充分匹配,这可能就足够了。向Docker注册表进行身份验证可能是最大的挑战,但是所有自动化工具中都有一些功能可以帮助实现这一点。
但是,有时最好将容器创建完全留给自动化层,在这种情况下,可能不需要污染用户的代码。容器创建非常棘手,开发人员有时并不真正在意它。如果用户代码更简洁,则其他工具更有可能“做正确的事”,应用安全修复程序,优化缓存等。自动化有多种选择,并且这些天它们都将具有与容器相关的某些功能。我们只看几个。
Concourse
Concourse是基于管道的自动化平台,可用于CI和CD。它在Pivotal内部大量使用,该项目的主要作者在那里工作。除CLI外,Concourse中的所有内容都是无状态的,并且所有内容都在容器中运行。由于运行容器是自动化管道的主要业务顺序,因此很好地支持创建容器。该泊坞窗图像资源负责保持你构建的输出状态更新,如果它是一个容器图像。
这是一个示例管道,为上面的示例构建一个docker映像,假设它位于github上,myorg/myapp
并且Dockerfile
在根目录下有一个,并且在以下位置有一个构建任务声明src/main/ci/build.yml
:
resources:
- name: myapptype: gitsource:uri: https://github.com/myorg/myapp.git
- name: myapp-imagetype: docker-imagesource:email: {{docker-hub-email}}username: {{docker-hub-username}}password: {{docker-hub-password}}repository: myorg/myappjobs:
- name: mainplan:- task: buildfile: myapp/src/main/ci/build.yml- put: myapp-imageparams:build: myapp
管道的结构非常具有声明性:您可以定义“资源”(输入或输出或两者兼有)和“作业”(使用动作并将其应用于资源)。如果任何输入资源发生更改,则会触发新的构建。如果作业期间任何输出资源发生更改,则将对其进行更新。
可以在与应用程序源代码不同的位置定义管道。对于通用构建设置,任务声明也可以集中或外部化。如果这是滚动的方式,则可以将开发和自动化之间的关注点分离开。
Jenkins
Jenkins是另一种流行的自动化服务器。它具有广泛的功能,但在此处与其他自动化示例最接近的功能是管道功能。这是一个Jenkinsfile
使用Maven构建Spring Boot项目,然后使用Dockerfile
构建图像并将其推送到存储库的:
Jenkinsfile
node {checkout scmsh './mvnw -B -DskipTests clean package'docker.build("myorg/myapp").push()
}
对于需要在构建服务器中进行身份验证的(现实的)泊坞库,您可以使用将凭证添加到上述docker
对象docker.withCredentials(…)
。
构建包
Cloud Foundry多年来一直在内部使用容器,用于将用户代码转换为容器的技术的一部分是Build Packs,该思想最初是从Heroku借来的。当前的buildpacks(v2)生成通用二进制输出,该输出由平台组装到容器中。在新一代buildpacks的(v3)是Heroku与其他公司(包括Pivotal)之间的合作,它直接且显式地构建了容器映像。这对于开发人员和操作员而言非常有趣。开发人员不需要太在乎如何构建容器的细节,但是如果需要,他们可以轻松地创建一个容器。Buildpacks还具有许多用于缓存生成结果和依赖项的功能,因此,Buildpack的运行速度要比本地Docker构建快得多。操作员可以扫描容器以审核其内容,并对其进行转换以修补它们以进行安全更新。您可以在本地(例如,在开发人员机器上或在CI服务中)或在Cloud Foundry之类的平台上运行构建包。
buildpack生命周期的输出是一个容器映像,但是您不需要docker或a Dockerfile
,因此它对CI和自动化友好。输出映像中的文件系统层由buildpack控制,通常,将进行许多优化而无需开发人员知道或关心它们。在较低层(如包含操作系统的基础映像)与较高层(如包含中间件和特定于语言的依赖关系)之间还有一个应用程序二进制接口。如果存在安全更新,则Cloud Foundry这样的平台可以修补较低的层,而不会影响应用程序的完整性和功能。
为了让您了解buildpack的功能,这里是一个从命令行使用Pack CLI的示例(它可以与我们在本指南中使用的示例应用程序一起使用,不需要Dockerfile
任何特殊的构建配置):
$ pack build myorg/myapp --builder=cloudfoundry/cnb:bionic --path=.
2018/11/07 09:54:48 Pulling builder image 'cloudfoundry/cnb:bionic' (use --no-pull flag to skip this step)
2018/11/07 09:54:49 Selected run image 'packs/run' from stack 'io.buildpacks.stacks.bionic'
2018/11/07 09:54:49 Pulling run image 'packs/run' (use --no-pull flag to skip this step)
*** DETECTING:
2018/11/07 09:54:52 Group: Cloud Foundry OpenJDK Buildpack: pass | Cloud Foundry Build System Buildpack: pass | Cloud Foundry JVM Application Buildpack: pass
*** ANALYZING: Reading information from previous image for possible re-use
*** BUILDING:
-----> Cloud Foundry OpenJDK Buildpack 1.0.0-BUILD-SNAPSHOT
-----> OpenJDK JDK 1.8.192: Reusing cached dependency
-----> OpenJDK JRE 1.8.192: Reusing cached launch layer-----> Cloud Foundry Build System Buildpack 1.0.0-BUILD-SNAPSHOT
-----> Using Maven wrapperLinking Maven Cache to /home/pack/.m2
-----> Building applicationRunning /workspace/app/mvnw -Dmaven.test.skip=true package
...
---> Running in e6c4a94240c2
---> 4f3a96a4f38c
---> 4f3a96a4f38c
Successfully built 4f3a96a4f38c
Successfully tagged myorg/myapp:latest
$ docker run -p 8080:8080 myorg/myapp. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.0.5.RELEASE)2018-11-07 09:41:06.390 INFO 1 --- [main] hello.Application: Starting Application on 1989fb9a00a4 with PID 1 (/workspace/app/BOOT-INF/classes started by pack in /workspace/app)
...
这--builder
是一个运行buildpack生命周期的docker映像-通常,它将是所有开发人员或单个平台上所有开发人员的共享资源。您可以在命令行上设置默认构建器(在中创建文件~/.pack
),然后在后续构建中忽略该标志。
该cloudfoundry/cnb:bionic 构建器还知道如何从可执行的jar文件构建映像,因此您可以mvnw 先使用构建,然后再将其指向--path jar文件以获得相同的结果。
|
Knative
容器和平台领域的另一个新项目是Knative。Knative有很多东西,但是如果您不熟悉Knative,则可以将其视为构建无服务器平台的基础。它基于Kubernetes构建,因此最终它会使用容器映像,并将它们转换为平台上的应用程序或“服务”。但是,它的主要功能之一是能够使用源代码并为您构建容器,从而使其对开发人员和操作员更友好。基建是执行此操作的组件,它本身就是一个将用户代码转换为容器的灵活平台-您几乎可以按照自己喜欢的任何方式进行操作。一些模板提供了通用模式,例如Maven和Gradle构建,以及使用Kaniko的多阶段docker构建。还有一个使用Buildpacks的模板,这对我们来说很有趣,因为buildpacks一直对Spring Boot具有良好的支持。用户还可以使用Riff和Pivotal Function Service在Knative上进行构建包选择,以将用户功能转换为正在运行的无服务器应用程序。
结语
本指南提供了许多用于为Spring Boot应用程序构建容器映像的选项。所有这些都是完全有效的选择,现在由您决定需要哪一个。您的第一个问题应该是“我真的需要构建容器映像吗?” 如果答案是“是”,那么您的选择可能会受到效率和可缓存性以及关注点分离的影响。您是否希望使开发人员不必过多地了解如何创建容器映像?当需要修补操作系统和中间件漏洞时,是否要让开发人员负责更新映像?也许开发人员需要对整个过程进行完全控制,并且他们拥有所需的所有工具和知识。
SpringBoot与Docker集成相关推荐
- SpringBoot 2.x 集成 Redis
SpringBoot 2.x 集成 Redis windows上搭建redis环境 添加依赖 此处redis客户端使用jedis. <!-- redis --> <dependenc ...
- springboot security 权限校验_十二、SpringBoot 优雅的集成Spring Security
前言 至于什么是Spring security ,主要两个作用,用户认证和授权.即我们常说的,用户只有登录了才能进行其他操作,没有登录的话就重定向到登录界面.有的用户有权限执行某一操作,而有的用户不能 ...
- SpringBoot项目Docker化并上传DockerHub的使用过程
. springboot项目 docker化 添加依赖 代码片段:<plugin><groupId>com.spotify</groupId><artifac ...
- SpringBoot与SpringCloud集成
SpringBoot与SpringCloud集成 : 简介 Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册 ...
- SpringBoot和Elasticsearch集成
SpringBoot和Elasticsearch的集成: 步骤 依赖 在Maven的pom文件中 123456789 <!--SpringBoot默认使用SpringData ElasticSe ...
- springboot与docker整合
一.springboot与docker整合 a.创建Dockerfile FROM java MAINTAINER "Wing"<1561815137@qq.com> ...
- SpringBoot使用JWT集成Ng-Alain之Token失效处理
在 SpringBoot使用JWT集成Ng-Alain中,我们简单介绍了SpringBoot与Ng-Alain的集成,在这种前后端分离框架实践中,我们使用了JWT来作为交互的安全标识,考虑一个问题,从 ...
- 一个springboot 项目a集成另一个springboot 项目b
一个springboot 项目a集成另一个springboot 项目b 并且可以运行访问b的controller层 操作1: 项目b打包依赖修改,把上面的springboot默认打包依赖注释,改为下面 ...
- springboot打包docker镜像部署
springboot打包docker镜像部署 环境准备 机器 vultr一台,centos7 资源下载 jdk8 maven git yum install git docker yum instal ...
最新文章
- Hugo快速搭建Blog
- linux通配符和正则表达式的区别总结
- Git :LF will be replaced by CRLF in readme.txt的原因与解决方案
- Java基础day15
- 请举例说明如何在Spring 中注入一个Java 集合?
- SAP Spartacus 404 Not found页面的显示机制 - canActivateNotFoundPage
- jsp空白页面传html代码,echarts在HTML里测试一般,在jsp页面不显示,而且还把整个页面变成空白...
- 直播丨国产最强音:HTAP融合型分布式数据库EsgynDB架构详解
- 变了,iPhone 12变身iPhone 4模样;下一代只支持单种5G频段?
- win10 + python3.6.1 + tensorflow1.10 + cuda9.0 + cudnn7.2
- S5PV210体系结构与接口09:SD卡启动详解
- 蓝牙MESH学习笔记
- VMware 11.0 简体中文版|附永久密钥
- 码栈使用手册(二)---界面介绍
- 用LNMP+wordpress搭了一个网站
- 用pageOffice插件实现 word文档在线填充指定数据
- Viz-artist常用脚本操作
- 【houdini vop】Block
- 对学计算机学生礼仪,计算机系学生分会社团部主持人和礼仪队选拔大赛计划1.doc...
- 【认证】数字电视高清认证情况及关键技术指标