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
    AI 代码解读

    image.png

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

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

    image.png

  4. 重载配置,重启服务

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

    $ firewall-cmd --zone=public --add-port=2375/tcp --permanent
    $ firewall-cmd --reload
    AI 代码解读
  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'
    }
    AI 代码解读

    插件文档地址

  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']
        }
    }
    AI 代码解读
  3. 执行./gradlew dockerBuildImage任务,我们可以查看到控制台的输出

    image.png

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

    $ docker images | grep it235
    AI 代码解读
  5. 通过镜像启动服务

    $ docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
    
    # 查看是否启动成功
    $ docker ps
    
    # 如果启动失败,需要使用日志查看命令进行查看
    $ docker logs -f -t --tail=200 容器名称或ID
    AI 代码解读
  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
    AI 代码解读

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
    AI 代码解读
  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}")
    }
    AI 代码解读
  3. 执行任务buildImage,观察Docker镜像列表是否已成功上传
  4. 启动容器

    docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
    AI 代码解读
  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
      AI 代码解读

      注意:切不可采用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}"]
      AI 代码解读
  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脚本添加进去
    AI 代码解读

    改造Dockerfile

    # 在workdir下面增加
    # 接收gradle传递的参数
    ARG env
    # 将参数设置到env中去,启动时容器内部能读到镜像中的env数据
    ENV env ${env}
    AI 代码解读

推送到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
}
AI 代码解读

注意:如果出现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
}
AI 代码解读

推送到云容器镜像服务

错误解决

# 问题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
AI 代码解读

最终配置

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
}
AI 代码解读

总结

学习本篇内容需要先掌握Docker使用,在build.gradle调试的过程中你可能会遇到莫名其妙的坑,需要你有清晰的思路去排查,在后续文章中,我将给大家带来Docker系列的内容。我是君哥,关注我,这里有你意向不到的惊喜

目录
相关文章
docker load 后镜像名称为空的问题解决
Docker在容器化应用程序时提供了强大的镜像管理功能,但也可能在某些操作中遇到如镜像名称为空的问题。通过理解问题的成因并采取适当的解决方案,如正确保存和加载镜像、手动修复标签等,可以有效避免和解决这一问题。通过本文提供的指导,您可以确保在使用Docker进行镜像操作时更为顺利,并提高容器管理的效率。
120 82
Kubernetes与Docker参数对照:理解Pod中的command、args与Dockerfile中的CMD、ENTRYPOINT。
需要明确的是,理解这些都需要对Docker和Kubernetes有一定深度的理解,才能把握二者的区别和联系。虽然它们都是容器技术的二个重要组成部分,但各有其特性和适用场景,理解它们的本质和工作方式,才能更好的使用这些工具,将各自的优点整合到生产环境中,实现软件的快速开发和部署。
45 25
大模型文件Docker镜像化部署技术详解
大模型文件Docker镜像化部署技术详解
42 2
YashanDB Docker镜像制作
本文介绍了使用Docker部署YashanDB数据库的方法及其优势。相比传统部署方式,Docker简化了环境配置,实现一键部署,确保软件在不同环境中一致运行。文章详细讲解了数据库镜像与容器的概念、Dockerfile的构建流程,以及如何通过Dockerfile定制YashanDB镜像。此外,还演示了镜像的发布过程,包括推送至阿里云容器镜像服务(ACR)。最后,探讨了容器启动时的初始化脚本设置和数据文件复用方法,满足客户对数据库自动化初始化和数据持久化的需求。
Docker Image即Docker镜像
Docker 镜像是 Docker 容器的基础,包含了运行应用程序所需的一切。通过 Dockerfile 可以方便地创建自定义镜像,并且利用 Docker 提供的命令可以轻松管理和使用这些镜像。掌握 Docker 镜像的创建、管理和使用,是进行容器化应用开发和部署的基础技能。希望本文能帮助读者更好地理解 Docker 镜像的概念和操作,提高开发和运维效率。
100 13
多种脚本批量下载 Docker 镜像:Shell、PowerShell、Node.js 和 C#
本项目提供多种脚本(Shell、PowerShell、Node.js 和 C#)用于批量下载 Docker 镜像。配置文件 `docker-images.txt` 列出需要下载的镜像及其标签。各脚本首先检查 Docker 是否安装,接着读取配置文件并逐行处理,跳过空行和注释行,提取镜像名称和标签,调用 `docker pull` 命令下载镜像,并输出下载结果。使用时需创建配置文件并运行相应脚本。C# 版本需安装 .NET 8 runtime。
134 2
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
您是否已集成 Spring Boot 与 ActiveMQ?
您是否已集成 Spring Boot 与 ActiveMQ?
161 0
|
9月前
|
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
368 6
如何实现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. 启动应用,测试流程启动,如通过服务和控制器开始流程实例。 示例代码包括服务类启动流程实例及控制器接口。实际集成需按业务需求调整。
567 4

热门文章

最新文章