Spring Boot应用容器化

创建项目

https://start.spring.io/

创建spring boot项目,添加Spring Web依赖。

编写业务代码

一个简单的REST API。

package cn.har01d.demo.springbootdocker;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {
    @Value("${server.name:Spring}")
    private String serverName;

    @RequestMapping("/")
    public String hello() {
        return "Hello world!";
    }

    @GetMapping("/server")
    public String server() {
        return serverName;
    }
}

简单的Dockerfile

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

使用Maven构建项目

./mvnw package

使用Docker构建image

docker build -t springio/gs-spring-boot-docker .

运行Docker container

docker run --name spring-boot-docker --rm -p 8080:8080 -t springio/gs-spring-boot-docker

最小权限运行

容器默认运行在root用户,为了减少安全风险,创建一个新用户和组。容器运行在非root权限下。

修改Dockerfile

FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

构建性能优化

一般情况下项目的依赖很少变化而项目本身代码经常变化。

Docker在构建过程中对不同的层有缓存机制,可以加快构建过程的速度。

修改Dockerfile

FROM openjdk:8-jre-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
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/*","cn.har01d.demo.springbootdocker.SpringBootDockerApplication"]

3个COPY创建了不同的层。

如果项目依赖没有变化,那么就可以重用。

构建Docker image

将Spring Boot生成的fat jar解压,取出依赖的jar文件。

mvn clean package
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
docker build -t springio/gs-spring-boot-docker .

使用Maven插件

spring-boot-maven-plugin插件支持直接构建Docker image,不需要使用Dockerfile。

修改pom.xml文件

            <build>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>build-image</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <image>
                                <name>springio/gs-spring-boot-docker</name>
                            </image>
                        </configuration>
                    </plugin>
                </plugins>
            </build>

运行时环境变量

指定Spring profiles

docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker

修改server.name

docker run -e "server.name=Test" -p 8080:8080 -t springio/gs-spring-boot-docker
http://localhost:8080/server

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 4
Date: Sun, 12 Jun 2022 03:02:48 GMT
Keep-Alive: timeout=60
Connection: keep-alive

Test

Response code: 200; Time: 65ms; Content length: 4 bytes

Java Debug

docker run -e "JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n" -p 8080:8080 -p 5005:5005 -t springio/gs-spring-boot-docker

示例项目

https://github.com/power721/spring-boot-docker

参考资料