Gradle从0入门到实战系列【十】集成Dockerfile构建Docker镜像

简介: 在工作中,我们会将SpringBoot程序打包成Docker镜像,这就需要在Gradle中配置Dockerfile构建程序,并且能将打包后的镜像部署并启动为一个容器,Gradle的插件能够帮我们完成这件事
本文来自于生产级别应用的实践,同时需要大家掌握前置技能Docker

环境准备

安装Docker环境

  1. Linux配置(必须)
  2. Docker安装(必须)
  3. Docker镜像测试

配置Docker仓库

  1. 安装使用harbor
  2. 使用阿里云仓库(必须)

开启Docker服务远程端口

如果要借助Jenkins、Gradle、Maven等工具将构建好的Docker image推到到某个服务器的Docker上,就必须开启该服务中Docker的远程连接端口,注意该端口尽量在内网操作,外网开放可以加入证书认证功能。

例:我现在想通过Gradle直接将镜像推送到192.168.2.195的docker服务里面,就需要配置195上的Docker,注意这里不是指私服,而是某个机器上的Docker服务。

  1. 修改Docker服务的配置文件

    $ vim /usr/lib/systemd/system/docker.service

    image.png

  2. 在[Service]部分的ExecStart处加入如下内容

    ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
  3. 加入后的Service完整配置如下

    image.png

  4. 重载配置,重启服务

    $ systemctl daemon-reload && systemctl restart docker
  5. 如果开启了防火墙,则需要通过该端口

    $ firewall-cmd --zone=public --add-port=2375/tcp --permanent
    $ firewall-cmd --reload
  6. 如果是ECS等云服务器,则需要在安全组中放开2375端口
  7. 能访问到http://192.168.2.195:2375/表示配置成功,注意这里换成你服务的ip

    image.png

编写build.gradle

  1. 引入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'
    }

    插件文档地址

  2. 编写构建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']
        }
    }
  3. 执行./gradlew dockerBuildImage任务,我们可以查看到控制台的输出

    image.png

  4. 查看Docker上的镜像是否创建成功

    $ docker images | grep it235
  5. 通过镜像启动服务

    $ docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
    
    # 查看是否启动成功
    $ docker ps
    
    # 如果启动失败,需要使用日志查看命令进行查看
    $ docker logs -f -t --tail=200 容器名称或ID
  6. 通过观察build/docker目录发现,插件帮我们生成了Dockerfile

    FROM 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整合

  1. 编写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
  2. 编写gradle脚本,一定要让jarDockerfile在一个目录下

    // 每次构建都清理一次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}")
    }
  3. 执行任务buildImage,观察Docker镜像列表是否已成功上传
  4. 启动容器

    docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
  5. 浏览器访问接口
  6. 该方式有哪些不足之处呢?是不是多个环境切换时没有办法做到很好的兼容,需要编写多个环境的Dockfile,同时如果我想使用start.sh脚本也不能做到

Dockerfile升级版

  1. 使用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}"]
  2. 解决多环境切换的问题

    # 增加全局扩展属性
    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系列的内容。我是君哥,关注我,这里有你意向不到的惊喜

目录
相关文章
|
7天前
|
运维 Kubernetes Docker
利用Docker和Kubernetes构建微服务架构
利用Docker和Kubernetes构建微服务架构
|
11天前
|
缓存 Linux 网络安全
docker的镜像无法下载如何解决?
【10月更文挑战第31天】docker的镜像无法下载如何解决?
333 28
|
7天前
|
存储 关系型数据库 Linux
【赵渝强老师】什么是Docker的镜像
Docker镜像是一个只读模板,包含应用程序及其运行所需的依赖环境。镜像采用分层文件系统,每次修改都会以读写层形式添加到原只读模板上。内核bootfs用于加载Linux内核,根镜像相当于操作系统,上方为应用层。镜像在物理存储上是一系列文件的集合,默认存储路径为“/var/lib/docker”。
|
7天前
|
API Docker 容器
【赵渝强老师】构建Docker Swarm集群
本文介绍了如何使用三台虚拟主机构建Docker Swarm集群。首先在master节点上初始化集群,然后通过特定命令将node1和node2作为worker节点加入集群。最后,在master节点上查看集群的节点信息,确认集群构建成功。文中还提供了相关图片和视频教程,帮助读者更好地理解和操作。
|
7天前
|
缓存 JavaScript 安全
深入理解Docker镜像构建过程
深入理解Docker镜像构建过程
23 0
|
4月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
287 6
|
4月前
|
Java 关系型数据库 MySQL
如何实现Springboot+camunda+mysql的集成
【7月更文挑战第2天】集成Spring Boot、Camunda和MySQL的简要步骤: 1. 初始化Spring Boot项目,添加Camunda和MySQL驱动依赖。 2. 配置`application.properties`,包括数据库URL、用户名和密码。 3. 设置Camunda引擎属性,指定数据源。 4. 引入流程定义文件(如`.bpmn`)。 5. 创建服务处理流程操作,创建控制器接收请求。 6. Camunda自动在数据库创建表结构。 7. 启动应用,测试流程启动,如通过服务和控制器开始流程实例。 示例代码包括服务类启动流程实例及控制器接口。实际集成需按业务需求调整。
364 4
|
4月前
|
消息中间件 Java 测试技术
【RocketMQ系列八】SpringBoot集成RocketMQ-实现普通消息和事务消息
【RocketMQ系列八】SpringBoot集成RocketMQ-实现普通消息和事务消息
317 1
|
5月前
|
消息中间件 Java Kafka
springboot集成kafka
springboot集成kafka
168 2
|
4月前
|
消息中间件 Java Kafka
Spring Boot与Apache Kafka Streams的集成
Spring Boot与Apache Kafka Streams的集成