写点什么

Java 应用在 docker 环境配置容器健康检查

作者:程序员欣宸
  • 2022-11-04
    四川
  • 本文字数:6748 字

    阅读完需:约 22 分钟

Java应用在docker环境配置容器健康检查

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos


  • 《极速体验docker容器健康》一文已体验了 docker 容器健康检查功能,今天就来给 java 应用的容器加入健康检查,使应用的状态随时都可以被监控和查看。

实战环境信息

  1. 操作系统:macOS Catalina 10.15

  2. Docker:19.03.2

java 应用简介

  • 今天实战的 java 应用,是用来模拟生产环境应用的,特点如下:


  1. 普通 springboot 应用,对外提供 http 服务,路径: /hello

  2. springboot 应用运行在 docker 容器,在容器的 /app/depend/ 目录下有名为 abc.txt 的文件;

  3. 上述 abc.txt 文件存在时,springboot 应用的 hello 接口正常,若 abc.txt 不存在,springboot 应用就不对外提供服务,相当于不健康状态(以此来模拟应用出现异常);

源码下载

  • 如果您不想写代码,上述 springboot 应用的源码可在 GitHub 下载到,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章的应用在 springboot-app-docker-health-check 文件夹下,如下图红框所示:

步骤简介

  • 应用接入容器健康检查的步骤如下:


  1. 将 java 应用制作成 docker 镜像时需要基础镜像,因此先准备好基础镜像,将容器健康检查的参数都配置在基础镜像中,包括提供容器健康信息的接口路径,这里定为 /getstate

  2. 改造 java 应用,提供 /getstate 接口服务,根据业务的实际情况决定当前应用是否健康,健康时返回码为 200,不健康时返回码为 403;

  3. 编译构建应用并且生成 docker 镜像;

  4. 验证;

制作基础镜像

  • 创建名为 Dockerfile 的文件,内容如下:


# Docker file from bolingcavalry # VERSION 0.0.1# Author: bolingcavalry
#基础镜像FROM openjdk:8-jdk-stretch
#作者MAINTAINER BolingCavalry <zq2599@gmail.com>
#健康检查参数设置,每5秒检查一次,接口超时时间2秒,连续10次返回1就判定该容器不健康HEALTHCHECK --interval=5s --timeout=2s --retries=10 \ CMD curl --silent --fail localhost:8080/getstate || exit 1
复制代码


  • 由上述可见 Dockerfile 的内容非常简单,选定自身的基础镜像为 openjdk:8-jdk-stretch ,再配置好健康检查参数:



  • 在 Dockerfile 文件所在目录执行命令 docker build -t bolingcavalry/jdk8-healthcheck:0.0.1 . (最后那个点号不要漏掉),控制台输出如下,提示镜像构建成功:


(base) zhaoqindeMacBook-Pro:springboot-app-docker-health-check zhaoqin$ docker build -t bolingcavalry/jdk8-healthcheck:0.0.1 .Sending build context to Docker daemon  217.6kBStep 1/3 : FROM openjdk:8-jdk-stretch8-jdk-stretch: Pulling from library/openjdk9a0b0ce99936: Already existsdb3b6004c61a: Already existsf8f075920295: Already exists6ef14aff1139: Already exists962785d3b7f9: Already exists631589572f9b: Already existsc55a0c6f4c7b: Already existsDigest: sha256:8bce852e5ccd41b17bf9704c0047f962f891bdde3e401678a52d14a628defa49Status: Downloaded newer image for openjdk:8-jdk-stretch ---> 57c2c2d2643dStep 2/3 : MAINTAINER BolingCavalry <zq2599@gmail.com> ---> Running in 270f78efa617Removing intermediate container 270f78efa617 ---> 01b5df83611dStep 3/3 : HEALTHCHECK --interval=5s --timeout=2s --retries=10   CMD curl --silent --fail localhost:8080/getstate || exit 1 ---> Running in 7cdd08b9ca22Removing intermediate container 7cdd08b9ca22 ---> 9dd7ffb22df4Successfully built 9dd7ffb22df4Successfully tagged bolingcavalry/jdk8-healthcheck:0.0.1
复制代码


  • 此时宿主机上已经有了名为 bolingcavalry/jdk8-healthcheck:0.0.1 的镜像,该镜像带有容器健康检查的参数配置,以此作为基础镜像来构建的其他镜像都集成了健康检查的特性;

  • 如果您已经在 hub.docker.com 上注册过,就可以用 docker login 命令登录,然后执行以下命令将本地镜像推送到 hub.docker.com 给更多人使用:


docker push bolingcavalry/jdk8-healthcheck:0.0.1
复制代码

改造 Java 应用

  • 本次实战的目标是让 Java 应用支持 docker 的容器健康检查功能,接下来一起创建这个 Java 应用:

  • 这是个基于 maven 构建的 springboot 工程,pom.xml 内容如下:


<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.2.0.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.bolingcavalry</groupId>    <artifactId>springboot-app-docker-health-check</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>springboot-app-docker-health-check</name>    <description>Demo project for Spring Boot</description>
<properties> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>

<!--使用jib插件--> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>1.7.0</version> <configuration> <!--from节点用来设置镜像的基础镜像,相当于Docerkfile中的FROM关键字--> <from> <!--基础镜像是bolingcavalry/jdk8-healthcheck:0.0.1,该镜像已配置了健康检查参数--> <image>bolingcavalry/jdk8-healthcheck:0.0.1</image> </from> <to> <!--镜像名称和tag,使用了mvn内置变量${project.version},表示当前工程的version--> <image>bolingcavalry/${project.artifactId}:${project.version}</image> </to> <!--容器相关的属性--> <container> <!--jvm内存参数--> <jvmFlags> <jvmFlag>-Xms1g</jvmFlag> <jvmFlag>-Xmx1g</jvmFlag> </jvmFlags> <!--要暴露的端口--> <ports> <port>8080</port> </ports> <!--使用该参数将镜像的创建时间与系统时间对其--> <useCurrentTimestamp>true</useCurrentTimestamp> </container> </configuration> </plugin> </plugins> </build></project>
复制代码


  • 上述 pom.xml 有以下几处需要注意:a. 使用 jib 插件来将当前工程构建成 docker 镜像;b. 基础镜像是前面构建的 bolingcavalry/jdk8-healthcheck:0.0.1 ,以此为基础镜像的镜像都带有健康检查功能;

  • 主要功能类是 SpringbootAppDockerHealthCheckApplication.java


package com.bolingcavalry.springbootappdockerhealthcheck;
import lombok.extern.slf4j.Slf4j;import org.apache.commons.io.FileUtils;import org.apache.commons.io.IOUtils;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.http.ResponseEntity;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;
import java.io.*;import java.util.List;
@SpringBootApplication@RestController@Slf4jpublic class SpringbootAppDockerHealthCheckApplication {
public static void main(String[] args) { SpringApplication.run(SpringbootAppDockerHealthCheckApplication.class, args); }
/** * 读取本地文本文件的内容并返回 * @return */ private String getLocalFileContent() { String content = null;
try{ InputStream is = new FileInputStream("/app/depend/abc.txt"); List<String> lines = IOUtils.readLines(is, "UTF-8");
if(null!=lines && lines.size()>0){ content = lines.get(0); } } catch (FileNotFoundException e) { log.error("local file not found", e); } catch (IOException e) { log.error("io exception", e); }
return content; }
/** * 对外提供的http服务,读取本地的txt文件将内容返回, * 如果读取不到内容返回码为403 * @return */ @RequestMapping(value = "/hello", method = RequestMethod.GET) public ResponseEntity<String> hello(){ String localFileContent = getLocalFileContent();
if(StringUtils.isEmpty(localFileContent)) { log.error("hello service error"); return ResponseEntity.status(403).build(); } else { log.info("hello service success"); return ResponseEntity.status(200).body(localFileContent); } }
/** * 该http服务返回当前应用是否正常, * 如果能从本地txt文件成功读取内容,当前应用就算正常,返回码为200, * 如果无法从本地txt文件成功读取内容,当前应用就算异常,返回码为403 * @return */ @RequestMapping(value = "/getstate", method = RequestMethod.GET) public ResponseEntity<String> getstate(){ String localFileContent = getLocalFileContent();
if(StringUtils.isEmpty(localFileContent)) { log.error("service is unhealthy"); return ResponseEntity.status(403).build(); } else { log.info("service is healthy"); return ResponseEntity.status(200).build(); } }}
复制代码


  • 上述代码有以下几处需要注意:a. hello 方法是此应用对外提供的服务,如果本地文件 abc.txt 存在且内容不为空,hello 方法的返回码就是 200,否则返回码为 403,表示当前服务出现异常;b. getstate 方法是新增的服务,该接口会被 docke-daemon 调用,如果返回码是 200,就表示容器健康,如果返回码是 403,表示容器不健康;

  • 在 pom.xml 文件所在目录执行 mvn clean compile -U -DskipTests jib:dockerBuild ,即可将当前工程构建为镜像,名为 bolingcavalry/springboot-app-docker-health-check:0.0.1-SNAPSHOT

  • 至此,支持容器健康检查的 Java 应用镜像构建成功,接下来验证容器的健康检查功能是否正常;

验证步骤

  • 验证的步骤如下:a. 让应用容器正常工作,确保文件 /app/depend/abc.txt 是正常的,此时容器状态应该是 healthyb. 将文件 /app/depend/abc.txt 删除,此时应用 hello 接口返回码为 403,并且容器状态变为 unhealthy

验证操作

  • 创建文件 abc.txt ,完整路径是 /Users/zhaoqin/temp/201910/20/abc.txt ,文件内容是个字符串,例如:123456

  • 执行以下命令,用新建的 java 应用镜像创建容器,该容器会将 test 文件夹映射到容器的 /app/depend 文件夹:


docker run --rm \--name=java-health-check \-p 8080:8080 \-v /Users/zhaoqin/temp/201910/20:/app/depend \bolingcavalry/springboot-app-docker-health-check:0.0.1-SNAPSHOT
复制代码


  • 控制台可见以下输出,表明健康检查接口已经被调用:


2019-10-20 14:16:34.875  INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'2019-10-20 14:16:34.876  INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'2019-10-20 14:16:34.892  INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 16 ms2019-10-20 14:16:34.959  INFO 1 --- [nio-8080-exec-1] pringbootAppDockerHealthCheckApplication : service is healthy2019-10-20 14:16:40.159  INFO 1 --- [nio-8080-exec-2] pringbootAppDockerHealthCheckApplication : service is healthy2019-10-20 14:16:45.356  INFO 1 --- [nio-8080-exec-4] pringbootAppDockerHealthCheckApplication : service is healthy2019-10-20 14:16:50.580  INFO 1 --- [nio-8080-exec-6] pringbootAppDockerHealthCheckApplication : service is healthy
复制代码


  • 执行命令 docker ps 查看容器状态,可见已经是 healthy


(base) zhaoqindeMBP:20 zhaoqin$ docker psCONTAINER ID        IMAGE                                                             COMMAND                  CREATED              STATUS                        PORTS                    NAMES51572d2488fb        bolingcavalry/springboot-app-docker-health-check:0.0.1-SNAPSHOT   "java -Xms1g -Xmx1g …"   About a minute ago   Up About a minute (healthy)   0.0.0.0:8080->8080/tcp   java-health-check
复制代码


  • 删除宿主机上的 /Users/zhaoqin/temp/201910/20/abc.txt ,相当于容器内的 abc.txt 文件被删除,此时控制台可见健康检查接口在被调用时发现文件不存在,已返回了 403 错误码:


019-10-20 14:22:37.490 ERROR 1 --- [nio-8080-exec-7] pringbootAppDockerHealthCheckApplication : service is unhealthy
复制代码


  • 健康检查接口被连续 10 次调用后,再执行命令 docker ps 查看容器状态,可见已经是 unhealthy


(base) zhaoqindeMBP:20 zhaoqin$ docker psCONTAINER ID        IMAGE                                                             COMMAND                  CREATED             STATUS                     PORTS                    NAMES51572d2488fb        bolingcavalry/springboot-app-docker-health-check:0.0.1-SNAPSHOT   "java -Xms1g -Xmx1g …"   7 minutes ago       Up 7 minutes (unhealthy)   0.0.0.0:8080->8080/tcp   java-health-check
复制代码


  • 至此,Java 应用在 docker 环境配置容器健康检查的实战就完成了,希望您在给自己的应用添加健康检查时,此文能给您一些参考。

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 刚刚阅读数: 3
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018-04-19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Java应用在docker环境配置容器健康检查_Java_程序员欣宸_InfoQ写作社区