通过结合docker容器,目前我们应用的发布流程大致如下:
我们的工程是:
根据我们发布流程的要求,构建出最终的镜像需要满足几个目标:
我们最终选用了 https://github.com/bmuschko/gradle-docker-plugin 的gradle-docker-plugin来实现构建docker镜像。该插件支持 java-application
和 spring-boot-application
两种方式,很明显我们选取spring-boot-applicaiton插件。
在原工程的build.gradle 最后加上:
apply from: 'docker.gradle'
在build.gradle的同一级目录下新建文件docker.gradle。
添加:
buildscript { repositories { mavenCentral() gradlePluginPortal() } dependencies { classpath 'com.bmuschko:gradle-docker-plugin:4.0.1' } } apply plugin: 'java' apply plugin: 'org.springframework.boot' apply plugin: DockerRemoteApiPlugin apply plugin: com.bmuschko.gradle.docker.DockerSpringBootApplicationPlugin
都是一些基础配置。
def projectname = "${project.getName()}" def dockerVer = getGitVersion() def port = 8990
这里定义一些全局变量使用。port可以根据实际情况定义。其中 getGitVersion()
的定义:
import java.text.SimpleDateFormat def getGitVersion() { def logTime = 'git log'.execute() | 'grep Date:'.execute() | 'head -n 1'.execute() logTime.waitFor() Date date = new Date(logTime.text.replace("Date:", "").trim()) String pushTime = new SimpleDateFormat("yyyyMMddHHmmss").format(date) return pushTime + "." + ('git rev-parse --short HEAD'.execute().text.trim()) }
可以看到拼接了git仓库的最后一个commit 的提交时间和hash值。
gradle-docker-plugin 预定义了一些task,我们只需要简单配置:
docker { url = getDefaultDockerUrl() registryCredentials { url = "registry.cn-hangzhou.aliyuncs.com" username = 'yourRepoUsername' password = 'yourRepopswd' } springBootApplication { baseImage = 'openjdk:8-alpine' ports = [port, port] } }
getDefaultDockerUrl
是本地docker 的url,plugin对windows、linux、mac都已经有了相应实现。
registryCredentials
是docker Hub的认证信息配置。 阿里云的dockerHub配置如上。
springBootApplication
只需要配置 baseImage
和端口映射。可以看到我们基于 openjdk:8-alpine
,alpine linux 镜像只有4.4M,不过 openjdk:8-alpine
镜像体积赫然有103MB那么大了。
配置这些就可以构建镜像,但是有些小问题需要解决。
openjdk:8-alpine` 默认是标准时区,而不是+8时区,需要修改。 接下来我们需要重写其中的 createDockerFile,buildImage,PushImage Task 来解决这两个问题。
其中 createDockerFile:
task createDockerfile(type: Dockerfile) { dependsOn dockerSyncArchive from(docker.springBootApplication.baseImage.get()) copyFile(bootWar.archiveName, "/app/${bootWar.archiveName}".toString()) //https://wiki.alpinelinux.org/wiki/Setting_the_timezone //https://github.com/gliderlabs/docker-alpine/issues/428 runCommand(' echo /'http://mirrors.ustc.edu.cn/alpine/v3.8/main/' > /etc/apk/repositories ' + ' && echo /'http://mirrors.ustc.edu.cn/alpine/v3.8/community/' >>/etc/apk/repositories ' + ' && apk update && apk add tzdata ' + ' && ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ' + ' && echo "Asia/Shanghai" > /etc/timezone ' + ' && date '+ ' && rm -rf /var/cache/apk/* ' + ' && rm -rf /usr/local/share/.cache') entryPoint("java") defaultCommand("-jar", "/app/${bootWar.archiveName}".toString()) exposePort(docker.springBootApplication.ports) }
上面主要是创建Dockerfile,编写指令来配置合适的 timezone
。使用中科大的apk安装源(mirrors.ustc.edu.cn)更快。。。
task buildImage(type: DockerBuildImage) { dependsOn createDockerfile inputDir = createDockerfile.destFile.get().asFile.parentFile tag = "zongwu233/${projectname}:${dockerVer}".toLowerCase() } //https://github.com/bmuschko/gradle-docker-plugin/issues/209 task dockerTag(type: com.bmuschko.gradle.docker.tasks.image.DockerTagImage) { dependsOn buildImage imageId = "zongwu233/${projectname}:${dockerVer}".toLowerCase() tag = "${dockerVer}".toLowerCase() repository = "registry.cn-hangzhou.aliyuncs.com/zongwu233/${projectname}".toLowerCase() }
解决私有DockerHub 的tag问题。更详细的讨论见 https://github.com/bmuschko/gradle-docker-plugin/issues/209
其中 zongwu233
是在阿里云DockerHub中的命名空间, ${projectname}
是仓库名称。
最后是push task:
task pushImage(type: DockerPushImage) { dependsOn dockerTag imageName = "registry.cn-hangzhou.aliyuncs.com/zongwu233/${projectname}".toLowerCase() }
另外要在createDockerFile,buildImage 等task之前 加上import 声明:
import com.bmuschko.gradle.docker.DockerRemoteApiPlugin import com.bmuschko.gradle.docker.tasks.image.* import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage import com.bmuschko.gradle.docker.tasks.image.DockerPushImage
这样,在控制台输入命令:
gradle pushImage
即可完成应用的docker镜像构建并且push到阿里云仓库。