springboot项目中使用docker

docker容器化部署是现在后端集群服务部署的主流方式,我们项目也是基于这种方式,通过swarm管理平台进行集群管理,当需要发布项目时从docker镜像仓库拉取需要发布的镜像,然后让镜像在集群的某一台机器生成容器完成发布。这就需要我们开发将自己的项目在开发完成后把项目打包成docker镜像上传到docker镜像仓库中。我们这里分别介绍一下java springboot项目在maven结构和gradle结构下,如何docker插件完成docker镜像的生成和往docker仓库推送镜像的工作。

maven场景

使用插件是"io.fabric8 docker-maven-plugin",maven插件配置如下:

            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.40.2</version>
                <configuration>

                    <authConfig>
<!--                        <username>test</username>-->
                        <!-- docker.server.username 这些是设置的maven全局变量 -->
                        <username>${docker.server.username}</username>
                        <password>${docker.server.password}</password>
                    </authConfig>

                    <!-- Docker 远程管理地址-->
                    <dockerHost>http://xx.xx.xx.xx:14974</dockerHost>
                    <!-- Docker 推送镜像仓库地址-->
                    <pushRegistry>https://xxxxx.xxxx.com</pushRegistry>
                    <verbose>true</verbose>

                    <images>
                        <image>
                            <!--由于推送到私有镜像仓库,镜像名需要添加仓库地址-->
                            <name>xxxx.xxx.com/${docker.server.image.group}/${project.name}:latest</name>
                            <!--定义镜像构建行为-->
                            <build>
                                <dockerFile>${project.basedir}/src/main/docker/DockerFile</dockerFile>
                                <!--定义哪些文件拷贝到容器中-->
                                <assembly>
                                    <!--定义拷贝到容器的目录-->
                                    <targetDir>/</targetDir>
                                    <!--只拷贝生成的jar包-->
                                    <descriptorRef>artifact</descriptorRef>
                                </assembly>
                            </build>
                        </image>
                        <image>
                            <!--由于推送到私有镜像仓库,镜像名需要添加仓库地址-->
                            <name>registry.memeyule.com/${docker.server.image.group}/${project.name}:${project.version}</name>
                            <!--定义镜像构建行为-->
                            <build>
                                <dockerFile>${project.basedir}/src/main/docker/DockerFile</dockerFile>
                                <!--定义哪些文件拷贝到容器中-->
                                <assembly>
                                    <!--定义拷贝到容器的目录-->
                                    <targetDir>/</targetDir>
                                    <!--只拷贝生成的jar包-->
                                    <descriptorRef>artifact</descriptorRef>
                                </assembly>
                            </build>
                        </image>
                    </images>
                </configuration>
            </plugin>

io.fabric8插件有官方文档,不过是全英文的。里面有各种通过xml的方式来表示dockerFile的内容,具体文档地址:https://dmp.fabric8.io/ 由于查阅起来比较麻烦,所以这使用了该插件直接引用dockerFile的方式,需要注意的是这里配置的docker路径是${project.basedir}/src/main/docker/DockerFile,如果是多模块项目,指的是子模块下的路径,不是根项目路径。dockerFile配置文件如下。

dockerFile配置:

FROM java:8
MAINTAINER xiaosong
ENV JAVA_OPTS="-Duser.timezone=GMT+8 -Xms3g -Xmx3g -XX:MetaspaceSize=256m"
#ENV TINI_VERSION v0.19.0

EXPOSE 8036 50051
COPY maven /
#COPY start.sh /start.sh
#COPY tini /tini

# 加tini,暂时不需要了
#ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
#RUN chmod +x /tini
# 通过脚本启动
# RUN chmod +x /start.sh


#ENTRYPOINT ["/tini", "--"]
#CMD ["/start.sh"]
#ENTRYPOINT exec ./start.sh

ENTRYPOINT exec java $JAVA_OPTS -jar /game-core.jar

启动脚本配置 start.sh,该脚本放在dockerFile同目录下,这要可以避免额外的配置(放在其他地方需要额外配置,否则找不到该文件),启动脚本内容如下:

exec java $JAVA_OPTS -jar /game-core.jar

这里说一下entrypoint(docker程序入口命令),有两种执行模式:exec和shell模式,exec模式如下:

ENTRYPOINT ["java","-jar","/game-core.jar"]

shell模式是这样:

ENTRYPOINT java -jar /game-core.jar

区别在于shell模式会在用docker执行该入口命令之前以shell的方式执行一遍该命令,之后再带入docker启动命令执行。而exec模式则不会,直接带入docker启动命令。这两者的区别在于,如果用shell模式,因为先执行了一次shell命令,所以docker容器中进程ID为1的进程是shell进程,而如果使用exec模式的话,进程ID为1的进程则是我们启动命令启动的进程。而当我们docker容器关闭的时候,docker会给容器id为1的进程发送一个关闭信号,我们可以用该关闭信号实现服务的优雅发布,如果我们服务是基于docker这种自动的优雅发布机制就会发现,如果不实用exec模式,这时候就会导致服务进程id不为1,从而导致优雅发布失效。那exec模式有什么缺点呢,就是入口点命令无法接收到环境变量。比如我们上面那样定义了一个JAVA_OPS的环境变量来指定java服务的jvm参数。如果我们使用exec模式,$JAVA_OPS这个参数是无不能解析成对应的环境变量的,会被docker当作一个入口命令的字符串,这往往会直接导致入口命令执行失败。那能不能既让入口命令读到环境变量也能保证我们的服务的进程ID仍然为1呢,就是上面我们这个命令:

ENTRYPOINT exec java $JAVA_OPTS -jar /game-core.jar

以shell模式执行,但是shell中有exec关键字。该关键字的作用是当执行到exec那一行命令时如果启动的是一个子进程,则该子进程会替代当前进程,所以当shell命令执行到java $JAVA_OPTS -jar /game-core.jar后,我们的java进程会替代原本的shell进程成为进程ID为1的进程。
除了这种通过后来的进程替代原本的shell进程实现,既可以接收信号,又可以在启动的时候解析环境变量,还有一些相对复杂的方式,就是使用主控系统,比如/tini,这是一个类似于微型操作系统的进程服务,我们可以使用该进程作为进程ID为1的进程,再用该进程去开启其他服务,当tini接收到信号的时候,会把接受的信号转发该给他的子服务,从而保证子服务的优雅关闭。前面也尝试过,但是子服务一直没有收到信号,导致这条路没走通,最终使用了shell的exec的方式才让环境变量的解析和优雅发布的两全其美。

gradle场景

除了maven结构,gradle也是java的常见项目结构,我们项目中也有部分项目使用的gradle,gradle的docker插件是这个:com.bmuschko,同样他也有他的官方文档:https://bmuschko.github.io/gradle-docker-plugin/#examples_3 这个插件中有一个简易插件叫:com.bmuschko.docker-spring-boot-application,是专门针对springboot项目的,可以很简单的配置就可以实现springboot项目的docker镜像生成:

plugins {
    id("com.bmuschko.docker-spring-boot-application") version "4.10.0"
}

apply {
    plugin("com.bmuschko.docker-spring-boot-application")
}

docker {
    url.set("tcp://1.1.1.1:port")
    registryCredentials {
        url.set(dockerRegistryUrl)
        username.set(dockerRegistryUserName)
        password.set(dockerRegistryPassword)
    }
    springBootApplication {
        baseImage.set("openjdk:8u212-b04-jdk")
        ports.set(listOf(8016))
        maintainer.set("erpang")
        tag.set("$dockerRegistryHost/$dockerImageGroup/roar:$dockerImageVersion")

    }

    tasks {
        dockerCreateDockerfile {
            environmentVariable("JAVA_OPTS", "-Duser.timezone=GMT+8 -Xms5g -Xmx5g -XX:MetaspaceSize=256m")
            entryPoint("sh","-c","exec java \$JAVA_OPTS -cp /app/resources:/app/classes:/app/libs/* com.xxx.xxx.xxx.Application")
        }
    }


}

这里可以看到我们在配置入口点的时候是[“sh”,“-c”,“exec java $JAVA_OPTS …”]
这种在exec模式下配上 sh -c,他就等价于我们shell模式了,这里这么配置的原因是因为该插件没法以shell模式进行配置,所以这样曲线救国了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值