本文来自于生产级别应用的实践,同时需要大家掌握前置技能Docker
环境准备
安装Docker环境
- Linux配置(必须)
- Docker安装(必须)
- Docker镜像测试
配置Docker仓库
- 安装使用harbor
- 使用阿里云仓库(必须)
开启Docker服务远程端口
如果要借助Jenkins、Gradle、Maven
等工具将构建好的Docker image
推到到某个服务器的Docker上,就必须开启该服务中Docker的远程连接端口,注意该端口尽量在内网操作,外网开放可以加入证书认证功能。
例:我现在想通过Gradle直接将镜像推送到192.168.2.195的docker
服务里面,就需要配置195上的Docker,注意这里不是指私服,而是某个机器上的Docker服务。
修改Docker服务的配置文件
$ vim /usr/lib/systemd/system/docker.service
在[Service]部分的
ExecStart
处加入如下内容ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
- 加入后的
Service
完整配置如下 重载配置,重启服务
$ systemctl daemon-reload && systemctl restart docker
如果开启了防火墙,则需要通过该端口
$ firewall-cmd --zone=public --add-port=2375/tcp --permanent $ firewall-cmd --reload
- 如果是
ECS
等云服务器,则需要在安全组中放开2375端口 - 能访问到
http://192.168.2.195:2375/
表示配置成功,注意这里换成你服务的ip
编写build.gradle
引入docker插件(必须)
buildscript{ repositories { maven { url 'https://maven.aliyun.com/repository/central' } } } plugins { ... id 'com.bmuschko.docker-remote-api' version '6.7.0' id 'com.bmuschko.docker-spring-boot-application' version '6.7.0' }
编写构建docker镜像的代码
docker { springBootApplication { //待上传到的docker服务 url = "tcp://192.168.2.195:2375" // 基础镜像 baseImage = 'openjdk:8-alpine' //容器暴露的端口,我们的application.yml中配置的服务端口server.port=7000 ports = [7000] //镜像名称 images = ["it235-order:${version}"] //容器启动的参数 jvmArgs = ['-Dspring.profiles.active=prod', '-Xmx256m'] } }
- 执行
./gradlew dockerBuildImage
任务,我们可以查看到控制台的输出 查看Docker上的镜像是否创建成功
$ docker images | grep it235
通过镜像启动服务
$ docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT # 查看是否启动成功 $ docker ps # 如果启动失败,需要使用日志查看命令进行查看 $ docker logs -f -t --tail=200 容器名称或ID
通过观察
build/docker
目录发现,插件帮我们生成了DockerfileFROM openjdk:8-alpine LABEL maintainer=Ron WORKDIR /app COPY libs libs/ COPY resources resources/ COPY classes classes/ ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Xmx256m", "-cp", "/app/resources:/app/classes:/app/libs/*", "com.it235.it235order.It235OrderApplication"] EXPOSE 7000
Dockerfile整合
编写
Dockerfile
脚本在项目根目录下新增Docker文件夹,并在其中创建
Dockerfile
文件,内容如下:FROM openjdk:8-alpine LABEL maintainer=junge WORKDIR /app COPY it235-order.jar ./ ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Xmx256m" , "-jar" , "/app/it235-order.jar"] EXPOSE 7000
编写
gradle
脚本,一定要让jar
和Dockerfile
在一个目录下// 每次构建都清理一次build/docker目录 task cleanDocker(type: Delete) { delete fileTree("${buildDir}/docker") { include '**/*' } } //将bootJar拷贝到build/docker目录下,并重命名 task copyDockerJar(type: Copy, dependsOn: bootJar) { group = "docker" //拷贝前清理docker目录下所有文件 dependsOn cleanDocker //将jar拷贝到build/docker目录下 from "${project.rootDir}/Dockerfile/Dockerfile" , bootJar.archiveFile into "${project.buildDir}/docker" //改名为it235-order.jar,Dockerfile中需要使用 rename { String fileName -> fileName.replace("-${version}", "") } } //指定构建镜像时的位置和服务URL import com.bmuschko.gradle.docker.tasks.image.* task buildImage(type: DockerBuildImage , dependsOn:copyDockerJar) { group "docker" url = "tcp://192.168.2.194:2375" //指定docker目录和Dockerfile文件 inputDir = file("${project.buildDir}/docker") //指定Dockerfile dockerFile = project.file("${project.buildDir}/docker/Dockerfile") //镜像名称 images.add("it235-order2:${version}") }
- 执行任务
buildImage
,观察Docker镜像列表是否已成功上传 启动容器
docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
- 浏览器访问接口
- 该方式有哪些不足之处呢?是不是多个环境切换时没有办法做到很好的兼容,需要编写多个环境的Dockfile,同时如果我想使用start.sh脚本也不能做到
Dockerfile升级版
使用start.sh启动
在docker目录下添加sh脚本
#!/bin/bash PROFILE_ENV=$1 java -Dspring.profiles.active=$PROFILE_ENV -Xmx256m -Djava.security.egd=file:/dev/./urandom -jar /app/it235-order.jar
注意:切不可采用nohup或&号的形式设置为后台启动,程序一定要在前台,否则容器会直接退出
改造
Dockerfile
# 注意此时需要使用/bin/bash,所以需要更换基础镜像,采用对sh支持更好的镜像 FROM openjdk:8 # sh脚本权限 COPY it235-order.jar ./ RUN sh -c 'chmod 755 start.sh' # 更改启动方式 ENTRYPOINT ["/bin/sh","-c","/app/start.sh ${env}"]
解决多环境切换的问题
# 增加全局扩展属性 project.ext{ //gradle buildImage -Penv=prod if (!project.hasProperty("env")) { env = "dev" } println "run env: ${env}" } # 在buildImage中使用buildArgs属性 buildArgs = ["env" : "${env}"] # copyJar中,需要把start.sh脚本添加进去
改造Dockerfile
# 在workdir下面增加 # 接收gradle传递的参数 ARG env # 将参数设置到env中去,启动时容器内部能读到镜像中的env数据 ENV env ${env}
推送到harbor
docker {
url = 'tcp://192.168.2.194:2375'
certPath = null
registryCredentials {
username = 'admin'
password = 'Harbor12345'
}
}
task copyDockerJar(type: Copy){
group "docker"
from "${project.rootDir}/docker/Dockerfile" ,"${project.rootDir}/docker/start.sh" , bootJar.archiveFile
into "${project.buildDir}/docker"
rename {
fileName -> fileName.replace("-${version}" , "")
}
}
import com.bmuschko.gradle.docker.tasks.image.*
task buildImage(type: DockerBuildImage , dependsOn: copyDockerJar){
group "docker"
url = "tcp://192.168.2.194:2375"
images.add("192.168.2.195:8098/it235-${env}/it235-order:${version}")
buildArgs = ["env" : "${env}"]
}
task tagImage(type: DockerTagImage,dependsOn: buildImage){
group 'docker'
tag = "${version}"
repository = "192.168.2.195:8098/it235-${env}/${project.name}"
targetImageId buildImage.getImageId()
}
// 移除镜像
task removeImage(type: DockerRemoveImage) {
group 'docker'
force = true
targetImageId buildImage.imageId
}
//推送镜像
task pushImage(type: DockerPushImage , dependsOn: tagImage){
group 'docker'
finalizedBy removeImage
images = buildImage.images
}
注意:如果出现HTTPS
问题,请配置daemon.json
文件,参照Docker
中的视频配置,我这边192.168.2.194的配置如下:
$ cat /etc/docker/daemon.json
{
"registry-mirrors" : ["http://hub-mirror.c.163.com"],
"insecure-registries" : ["192.168.2.195:8098"],
"live-restore": true
}
推送到云容器镜像服务
错误解决
# 问题1 connection time out
1. 镜像体积先缩小,可以FROM openjdk:8-alpine,这个打出来只有150M
2. 分步上传,先push到Docker服务,然后在Docker服务上执行push命令,2步打通之后再合并一起操作
docker push registry.cn-hangzhou.aliyuncs.com/it235-dev/it235-order:1.0.0-SNAPSHOT
# 问题2 Could not push image: denied: requested access to the resource is denied
1. registryCredentials中的用户名密码需要填写阿里云镜像服务的用户名密码
2. images的名称要能够与阿里云提供的对上
配置:images.add("registry.cn-hangzhou.aliyuncs.com/it235-${env}/it235-order:${version}")
阿里云:registry.cn-hangzhou.aliyuncs.com/it235-dev/it235-order:[镜像版本号]
3. docker服务机器上需要登录阿里云镜像仓库
docker login --username=用户名 registry.cn-hangzhou.aliyuncs.com
最终配置
project.ext{
//gradle buildImage -Penv=prod
if (!project.hasProperty("env")) {
env = "dev"
}
println "run env: ${env}"
}
docker {
url = "tcp://192.168.2.194:2375"
certPath = null
registryCredentials {
username = 'xxxx'
password = '阿里云镜像服务密码'
}
}
task copyDockerJar(type: Copy){
group "docker"
from "${project.rootDir}/docker/Dockerfile" ,"${project.rootDir}/docker/start.sh" , bootJar.archiveFile
into "${project.buildDir}/docker"
rename {
fileName -> fileName.replace("-${version}" , "")
}
}
import com.bmuschko.gradle.docker.tasks.image.*
task buildImage(type: DockerBuildImage , dependsOn: copyDockerJar){
group "docker"
url = "tcp://192.168.2.194:2375"
images.add("registry.cn-hangzhou.aliyuncs.com/it235-${env}/it235-order:${version}")
buildArgs = ["env" : "${env}"]
}
task tagImage(type: DockerTagImage) {
dependsOn buildImage
repository = "registry.cn-hangzhou.aliyuncs.com/it235-${env}/it235-order"
tag = "${version}"
targetImageId buildImage.getImageId()
}
task removeImage(type: DockerRemoveImage) {
targetImageId buildImage.imageId
force = true
}
task pushImage(type: DockerPushImage) {
group "docker"
//构建
dependsOn tagImage
//推完之后,进行删除
finalizedBy removeImage
images = buildImage.images
}
总结
学习本篇内容需要先掌握Docker使用,在build.gradle调试的过程中你可能会遇到莫名其妙的坑,需要你有清晰的思路去排查,在后续文章中,我将给大家带来Docker系列的内容。我是君哥,关注我,这里有你意向不到的惊喜