小哥之哥 小哥之哥
首页
    • Prometheus
    • Kubertenes
    • Docker
    • MySQL
  • Go
  • Python
  • Vue
  • Jenkins
  • ELK
  • LDAP
  • 随笔
  • 最佳实践
  • 博客搭建
  • 问题杂谈
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

小哥之哥

运维扫地僧
首页
    • Prometheus
    • Kubertenes
    • Docker
    • MySQL
  • Go
  • Python
  • Vue
  • Jenkins
  • ELK
  • LDAP
  • 随笔
  • 最佳实践
  • 博客搭建
  • 问题杂谈
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 博客搭建

  • 随笔

    • https免费证书
    • k8s部署canal 1.1.6集群
    • Docker部署ElasticSearch 7.10
    • Linux系统流量监控-nethogs
    • Java应用Dockerfile编辑
      • Go应用Dockerfile编辑
      • 聊聊平滑迁移这件事
      • kafka二进制模式集群部署
      • Centos7搭建minio
    • 更多
    • 随笔
    tchua
    2023-06-29
    目录

    Java应用Dockerfile编辑

    # 一、概述


    由于Docker容器的轻量化、便捷性特点,在工作成为中容器化解决方案,对于一些开源的中间件,我们部署的时候,都会直接使用官方镜像,我们pull下来,配置启动参数即可,然而,我们工作中接触最多的还是自己开发的业务应用,常见的java、python、go等语言程序,这些对于我们来说,都需要自己定义Dockerfile生成镜像,本文先探究java程序Dockerfile的编辑开发。

    # 二、Java应用传统启动


    # 2.1 简单启动

    对于非容器化的操作,当我们打包好之后,会生成业务jar包,我们直接按照启动参数执行java -jar ${JAR_NAME} ${JAVA_OPTS}即可,比如:

    java -jar sal-toc-1.0-SNAPSHOT.jar --spring.profiles.active=dev
    
    1
    # 2.2 启动脚本

    使用上面直接启动,虽然能够把应用跑起来,但是弊端也很明显,就是对于后期维护(重启发布)是非常不便的,因此我们大多会借助shell脚本,对应用进行启动管理:

    #!/bin/bash
    
    # 应用名
    APP_NAME=sal-toc
    PROG_NAME=$0
    ACTION=$1
    APP_START_TIMEOUT=20    # 等待应用启动的时间 结合健康检查使用
    APP_PORT=9201          # 应用端口
    HEALTH_CHECK_URL=http://127.0.0.1:${APP_PORT}  # 应用健康检查URL
    APP_HOME=/data/webbacks/${APP_NAME} # 应用部署家目录
    JAR_NAME="sal-toc-1.0-SNAPSHOT.jar" # jar包的名字
    JAVA_OPTS="-Xms512M -Xmx512M --spring.profiles.active=prod"
    
    # 判断目录是否存在
    if [ ! -d "$APP_HOME" ]; then
        mkdir -p ${APP_HOME}
    fi
    
    usage() {
        echo "Usage: $PROG_NAME {start|stop|restart}"
        exit 2
    }
    
    health_check() {
        exptime=0
        echo "checking ${HEALTH_CHECK_URL}"
        while true
            do
                status_code=`/usr/bin/curl -L -o /dev/null --connect-timeout 5 -s -w %{http_code}  ${HEALTH_CHECK_URL}`
                if [ "$?" != "0" ]; then
                   echo -n -e "\rapplication not started"
                else
                    echo "code is $status_code"
                    if [ "$status_code" == "200" ];then
                        break
                    fi
                fi
                sleep 1
                ((exptime++))
    
                echo -e "\rWait app to pass health check: $exptime..."
    
                if [ $exptime -gt ${APP_START_TIMEOUT} ]; then
                    echo 'app start failed'
                   exit 1
                fi
            done
        echo "check ${HEALTH_CHECK_URL} success"
    }
    start_application() {
        echo "starting java process"
        nohup java -jar ${JAR_NAME} ${JAVA_OPTS} >/dev/bull 2>&1 &
        echo "started java process"
    }
    
    stop_application() {
       checkjavapid=`ps -ef | grep java | grep ${APP_NAME} | grep -v grep |grep -v 'run.sh'| awk '{print$2}'`
       
       if [[ ! $checkjavapid ]];then
          echo -e "\rno java process"
          return
       fi
    
       echo "stop java process"
       times=60
       for e in $(seq 60)
       do
            sleep 1
            COSTTIME=$(($times - $e ))
            checkjavapid=`ps -ef | grep java | grep ${APP_NAME} | grep -v grep |grep -v 'run.sh'| awk '{print$2}'`
            if [[ $checkjavapid ]];then
                kill -9 $checkjavapid
                echo -e  "\r-- stopping java lasts `expr $COSTTIME` seconds."
            else
                echo -e "\rjava process has exited"
                break;
            fi
       done
       echo ""
    }
    start() {
        start_application
        health_check
    }
    stop() {
        stop_application
    }
    case "$ACTION" in
        start)
            start
        ;;
        stop)
            stop
        ;;
        restart)
            stop
            start
        ;;
        *)
            usage
        ;;
    esac
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102

    注: 上面脚本是一个比较完善的生产可用脚本,如果感觉比较复杂,则可以自行修改,具体需要自己根据业务jar启动特性调整。

    # 三、Dockerfile开发

    # 3.1 简单示例
    # 3.1.1 编辑Dockerfile
    # 设置基础镜像
    FROM openjdk:17-jdk-alpine3.14
    
    LABEL maintainer="tchuaxiaohua@163.com"
    
    # 设置环境变量
    ARG JAVA_OPTIONS="-Xms512M -Xmx512M --spring.profiles.active=prod"
    ENV appPort 9021
    ENV appName sale-toc-1.0-SNAPSHOT.jar
    
    # 设置工作目录
    WORKDIR /apps
    
    # 复制应用程序的JAR文件到容器中
    ADD ${appName} /apps/
    
    # 设置时区
    RUN apk --update add tzdata && \
        cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
        echo "Asia/Shanghai" > /etc/timezone && \
        apk del tzdata && \
        rm -rf /var/cache/apk/*
        
    # 暴露应用程序的端口
    EXPOSE ${appPort}
    
    # 运行Java应用程序
    CMD java  -jar /apps/${appName} ${JAVA_OPTIONS}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    相关参数说明

    • FROM 指定基础镜像构建版本,这里需要自己根据开发本地jdk版本去选择
    • JAVA_TOOL_OPTIONS 设置Java运行时的默认选项,java -jar 启动时会自动应用该变量参数
    • CMD 运行命令还可以使用ENTRYPOINT
    # 3.1.2 构建镜像

    需要确保Dockerfile引用的jar包路径正确

    docker build -t registry.cn-hangzhou.aliyuncs.com/tfgol-dev/auto-toc:v1 .
    
    1
    # 3.1.3 启动
    docker run -d --name v1 -p 9021:9021 registry.cn-hangzhou.aliyuncs.com/tfgol-dev/auto-toc:v1
    
    1
    # 3.1.4 查看主进程

    image-20230629152959338

    # 3.2 进阶示例

    上面构建示例完全可以应用于生产环境,只是有一个问题,那就是容器中Java应用程序的进程ID(PID)为1,我们知道PID为1的进程在Linux系统中通常被用作init进程或主进程,由于该进程的特性,可能会对容器的信号处理,资源管理产生冲突到出现不可预期错误,因此我们可以借助一个启动脚本作为容器的主进程,我们使用开源第三方的软件时,也可以看到对于很多开源的官方镜像,也是都会借助其他工具运行,避免主进程pid为1。

    # 3.2.1 Dockerfile示例
    FROM openjdk:17-jdk-alpine3.14
    
    LABEL maintainer="tchuaxiaohua@163.com"
    
    ENV appPort 9021
    ENV appName sale-toc-1.0-SNAPSHOT.jar
    
    RUN apk --update add tzdata && \
        cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
        echo "Asia/Shanghai" > /etc/timezone && \
        apk del tzdata && \
        rm -rf /var/cache/apk/*
    RUN echo '#!/bin/sh' > /usr/bin/entrypoint.sh && \
        echo 'java -jar /apps/${appName}' >> /usr/bin/entrypoint.sh && \
        chmod +x /usr/bin/entrypoint.sh
    
    ADD ${appName} /apps/
    
    
    EXPOSE ${appPort}
    
    ENTRYPOINT ["entrypoint.sh"]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    说明

    我们可以看到,我们这里创建一个entrypoint.sh脚本,并把启动命令防至该脚本中,在最后ENTRYPOINT中我们执行该脚本即可

    # 3.2.2 构建镜像
    docker build -t registry.cn-hangzhou.aliyuncs.com/tfgol-dev/auto-toc:v1 .
    
    1
    # 3.2.3 启动
    docker run -d --name v1 -p 9021:9021 registry.cn-hangzhou.aliyuncs.com/tfgol-dev/auto-toc:v1
    
    1
    # 3.2.4 查看进程

    image-20230629153220530

    # 3.3 生产最佳实践

    这里我们在进阶的基础上只是针对entrypoint.sh 脚本进行升级,根据我们传统启动方式,完善启动参数,启动步骤基本是一样的

    # 3.3.1 完善entrypoint.sh
    #!/bin/sh
    
    # 应用名
    APP_NAME=sale-toc
    PROG_NAME=$0
    ACTION=$1
    APP_HOME=/apps # 应用部署家目录
    JAR_NAME="sale-toc-1.0-SNAPSHOT.jar" # jar包的名字
    JAVA_OPTIONS="--spring.profiles.active=prod"
    JAVA_JVM_OPTIONS="-Xms512M -Xmx512M" # 该变量主要是用来运行其他agent 比如 监控 链路追踪
    # 判断目录是否存在
    if [ ! -d "$APP_HOME" ]; then
        mkdir -p ${APP_HOME}
    fi
    
    usage() {
        echo "Usage: $PROG_NAME {start|stop|restart}"
        exit 2
    }
    
    start_application() {
        echo "starting java process"
        java -jar ${JAVA_JVM_OPTIONS} ${APP_HOME}/${JAR_NAME} ${JAVA_OPTS}
        echo "started java process"
    }
    
    stop_application() {
       checkjavapid=`ps -ef | grep java | grep ${APP_NAME} | grep -v grep |grep -v 'run.sh'| awk '{print$2}'`
       
       if [[ ! $checkjavapid ]];then
          echo -e "\rno java process"
          return
       fi
    
       echo "stop java process"
       times=60
       for e in $(seq 60)
       do
            sleep 1
            COSTTIME=$(($times - $e ))
            checkjavapid=`ps -ef | grep java | grep ${APP_NAME} | grep -v grep |grep -v 'run.sh'| awk '{print$2}'`
            if [[ $checkjavapid ]];then
                kill -9 $checkjavapid
                echo -e  "\r-- stopping java lasts `expr $COSTTIME` seconds."
            else
                echo -e "\rjava process has exited"
                break;
            fi
       done
       echo ""
    }
    start() {
        start_application
    }
    stop() {
        stop_application
    }
    case "$ACTION" in
        start)
            start
        ;;
        stop)
            stop
        ;;
        restart)
            stop
            start
        ;;
        *)
            usage
        ;;
    esac
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    • 脚本移除监控检查,这是因为大多数基础镜像是没有curl命令的
    • 启动命令不能使用nohub,否则会直接退出
    # 3.3.2 改造Dockerfile
    FROM openjdk:17-jdk-alpine3.14
    
    LABEL maintainer="tchuaxiaohua@163.com"
    
    ENV appPort 9021
    ENV appName sale-toc-1.0-SNAPSHOT.jar
    
    ADD ${appName} /apps/
    COPY entrypoint.sh /usr/bin/entrypoint.sh
    
    RUN chmod +x /usr/bin/entrypoint.sh
    
    RUN apk --update add tzdata && \
        cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
        echo "Asia/Shanghai" > /etc/timezone && \
        apk del tzdata && \
        rm -rf /var/cache/apk/*
    
    EXPOSE ${appPort}
    
    ENTRYPOINT ["entrypoint.sh","start"]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 3.3.3 构建启动
    # 镜像构建
    docker build -t registry.cn-hangzhou.aliyuncs.com/tfgol-dev/auto-toc:v1 .
    # 启动
    docker run -d --name v1 -p 9021:9021 registry.cn-hangzhou.aliyuncs.com/tfgol-dev/auto-toc:v1
    
    1
    2
    3
    4
    # 3.3.4 查看主进程

    image-20230629164301911

    # 四、maven构建镜像

    maven构建镜像需要借助jib插件,jib 插件 (opens new window)是一个由 Google 开发的 Maven 和 Gradle 插件,用于简化构建和推送 Docker 镜像的过程。它不需要编写 Dockerfile,而是直接使用项目的构建配置来生成 Docker 镜像。

    # 4.1 插件引用配置

    在项目的 pom.xml 文件中添加 Jib 插件的依赖。

    <build>
        <plugins>
            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>3.1.1</version>
                <configuration>
                    <!-- 插件的配置选项 -->
                </configuration>
            </plugin>
            <!-- 其他 Maven 插件和配置 -->
        </plugins>
    </build>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 4.2 插件参数配置

    插件配置是在插件的configuration字段中

    <configuration>
        <!-- 基础镜像 -->
        <from>
            <image>openjdk:17-jdk-alpine3.14</image>
        </from>
        <!-- 业务镜像配置 -->
        <to>
            <image>registry.cn-hangzhou.aliyuncs.com/tfgol-dev/${project.name}</image>
            <auth>
            	<username>tfgol</username>
                <password>******</password>
            </auth>
        </to>
        <!-- 业务启动类 推荐在这里指定 -->
        <!--
        <container>
           <mainClass>com.losinx.sale.SaleTocStart</mainClass>
        </container>
    	-->
    </configuration>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    详细配置
    ......
    <build>
            <plugins>
     	   <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>3.1.1</version>
                <configuration>
        			<!-- 基础镜像 -->
        		<from>
            	  <image>openjdk:17-jdk-alpine3.14</image>
        		</from>
        			<!-- 业务镜像配置 -->
        		<to>
            	  <image>registry.cn-hangzhou.aliyuncs.com/tfgol-dev/${project.name}</image>
            	<auth>
            	  <username>tfgol</username>
                	  <password>*****</password>
            	</auth>
    
        		</to>
                </configuration>
            </plugin>
    </build>
    ......
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    配置说明

    • <from></from> 用来定义基础镜像,类似于Dockerfile中的FROM如果我们使用的私有仓库需要认证,则可以使用<auth> </auth>配置用户名密码
    <auth>
      <username>tfgol</username>
      <password>******</password>
    </auth>
    
    1
    2
    3
    4
    • <to></to> 用来定义业务镜像相关参数
      • <image></image> 生成的镜像名称,镜像生产之后,会自动push到对应的仓库中
      • <auth></auth> 使用的私有镜像仓库,所以需要认证
    • <container></container> 定义应用业务启动类,因为使用maven构建镜像的话,生成的就不在是jar包。

    更多参数参考官方插件 (opens new window)说明,很多配置不仅可以在配置文件中指定,也可以在执行构建命令的时候通过-Djib.xx指定

    # 4.3 编译

    到pom.xml文件所在目录,执行mvn compile jib:build -Djib.to.tags=v2

    [WARNING] 
    [WARNING] Some problems were encountered while building the effective settings
    [WARNING] Unrecognised tag: 'blocked' (position: START_TAG seen ...tact/maven/nexus/content/repositories/ebi-repo/</url>\r\n\t\t<blocked>... @169:12)  @ /usr/local/apache-maven-3.6.3/conf/settings.xml, line 169, column 12
    [WARNING] 
    [INFO] Scanning for projects...
    [WARNING] 
    [WARNING] Some problems were encountered while building the effective model for com.losinx:sale-toc:jar:1.0-SNAPSHOT
    [WARNING] 'dependencyManagement.dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: com.alibaba:fastjson:jar -> version ${fastjson} vs ${fastJson.version} @ com.losinx:auto-sale:1.0-SNAPSHOT, /root/auto-sales/pom.xml, line 239, column 25
    [WARNING] 
    [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
    [WARNING] 
    [WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
    [WARNING] 
    [INFO] 
    [INFO] ------------------------< com.losinx:sale-toc >-------------------------
    [INFO] Building sale-toc 1.0-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    [INFO] 
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sale-toc ---
    [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
    [INFO] Copying 4 resources
    [INFO] 
    [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ sale-toc ---
    [INFO] Nothing to compile - all classes are up to date
    [INFO] 
    [INFO] --- jib-maven-plugin:3.1.1:build (default-cli) @ sale-toc ---
    [INFO] 
    [INFO] Containerizing application to registry.cn-hangzhou.aliyuncs.com/tfgol-dev/sale-toc, registry.cn-hangzhou.aliyuncs.com/tfgol-dev/sale-toc:v2...
    [WARNING] Base image 'openjdk:17-jdk-alpine3.14' does not use a specific image digest - build may not be reproducible
    [INFO] Using credentials from <to><auth> for registry.cn-hangzhou.aliyuncs.com/tfgol-dev/sale-toc
    [INFO] The base image requires auth. Trying again for openjdk:17-jdk-alpine3.14...
    [INFO] Using credentials from Docker config (/root/.docker/config.json) for openjdk:17-jdk-alpine3.14
    [INFO] Using base image with digest: sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9dbd9784383d81
    [INFO] 
    [INFO] Container entrypoint set to [java, -cp, @/app/jib-classpath-file, com.losinx.sale.SaleTocStart]
    [INFO] 
    [INFO] Built and pushed image as registry.cn-hangzhou.aliyuncs.com/tfgol-dev/sale-toc, registry.cn-hangzhou.aliyuncs.com/tfgol-dev/sale-toc:v2
    [INFO] Executing tasks:
    [INFO] [============================  ] 91.7% complete
    [INFO] > launching layer pushers
    [INFO] 
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  8.675 s
    [INFO] Finished at: 2023-06-29T17:17:39+08:00
    [INFO] ------------------------------------------------------------------------
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47

    image-20230629173918154

    image-20230629174805495

    说明

    • 构建参数说明

      • mvn compile jib:build 镜像构建命令,构建成功后会自动把生成的镜像上传至仓库

      • -Djib.to.tags=v2 镜像tag,也可以在插件配置处配置

      • -Djib.container.jvmFlags 指定启动参数,比如: “-Xms256m,-Xmx512m”

    • 构建过程说明(截图bu'fen)

      • ① 说明使用仓库及镜像名称,这里可以看到会根据配置的auth自动登录该镜像仓库
      • ② 这里是启动命令,maven构建的镜像,启动使用的是应用类,这个一般开发都会知道,这里推荐在插件配置中直接指定。
      • ③ 对于生成的业务镜像,默认会生成一个tag为latest的镜像,v2是我们指定的指定的tag,这里两个都会上传至镜像仓库。
      • ④ 打包后的文件,这里不再试jar包的模式,生成镜像的时候会把这些文件都copy到镜像的app目录下
    # 4.4 仓库镜像查看

    1688032438714

    # 4.5 使用总结

    通过上面配置我们可以看到,如果maven的jib插件使用比较熟练的话,那么我们可以省很多事情,不用再关注应用Dockerfile,也不用关注镜像上传,这里构建后,会自动把经上传,并且有很多参数,可配置化,当然如果想要完全自定义,那么还是推荐使用自定义Dockerfile的方式

    编辑 (opens new window)
    #Docker
    上次更新: 2023/07/11, 10:09:14
    Linux系统流量监控-nethogs
    Go应用Dockerfile编辑

    ← Linux系统流量监控-nethogs Go应用Dockerfile编辑→

    最近更新
    01
    cert-manager自动签发Lets Encrypt
    09-05
    02
    Docker构建多架构镜像
    08-02
    03
    Prometheus数据迁移至VMstorage
    08-01
    更多文章>
    Theme by Vdoing | Copyright © 2023-2024 |豫ICP备2021026650号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式