基于devops利用微服务引入三方服务---wrapper包装器发布并使用prometheus监控spring boot项目

1、创建provider项目

本次部署使用wrapper包装器对项目进行封装,通过wrapper启动器来实现服务的注册与发现,不再使用nacos插件来实现服务注册
provider结构
08api33
因为本次测试只是为了测试如何去发布如何去监控,这里就不做持续集成了,上图的Jenkinsfile、Dockerfile忽略即可
cat 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.8.RELEASE</version>
                <relativePath/>
        </parent>
        <groupId>com.teng</groupId>
        <artifactId>provider</artifactId>
        <version>0.0.5</version>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>

        <properties>
                <java.version>1.8</java.version>
                <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
        </properties>
 <!-- 设置deploy的地址 -->
    <distributionManagement>
        <repository>
            <id>maven-releases</id>                    <!--库的ID-->
            <name>maven-releases</name>   <!--库的名字-->
            <url>http://192.168.0.152:8081/repository/maven-releases/</url>
        </repository>
 
        <snapshotRepository>
            <id>maven-snapshots</id>
            <name>maven-snapshot</name>
            <url>http://192.168.0.152:8081/repository/maven-snapshots/</url>
        </snapshotRepository>

    </distributionManagement>

        <dependencies>
                <dependency>
                        <groupId>io.micrometer</groupId>
                        <artifactId>micrometer-registry-prometheus</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-actuator</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-thymeleaf</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-devtools</artifactId>
                        <scope>runtime</scope>
                        <optional>true</optional>
                </dependency>

                <dependency>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <optional>true</optional>
                </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>

        <dependencyManagement>
                <dependencies>
                        <dependency>
                                <groupId>com.alibaba.cloud</groupId>
                                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                                <version>${spring-cloud-alibaba.version}</version>
                                <type>pom</type>
                                <scope>import</scope>
                        </dependency>
                </dependencies>
        </dependencyManagement>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                </plugins>
        </build>

</project>

该文件去掉了原provider项目的nacos相关插件,添加了prometheus相关插件
cat src/main/resources/appication.properties

server.port=8080


management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

这里暂时只能使用8080,要配合wrapper包装器,包装器使用58080代理的端口默认是8080,至于可不可以修改还不确定

mvn clean install打包生成jar包

2、生成封装镜像

在进行封装前先来了解一下以前wrapper封装hello镜像的具体原理

  • 镜像创建时对外暴漏代理端口58080
  • 容器启动时,wrapper将应用服务注册至nacos
  • 注册成功后wrapper通过laucher.sh脚本启动应用,并配置check,成功后向nacos发出Heartbeat
  • 外部访问应用通过wrapper代理端口转发到hello应用的8080端口,同时代理端口接受hello响应返回的数据再返回给请求者

明白其原理后,需要根据应用具体情况来修改
查看wrapper代码结构
08api34
cat config/config.yml

# 包装器配置
wrapper:
# 工作模式,默认为 "proxy" 代理模式
# "launcher"  启动器模式,负责为单体应用程序向注册中心注册及注销,并通过脚本控制单体应用执行
# "proxy" 模式为代理模式,包含启动器功能,并启动 HTTP 服务对外提供服务,转发请求至单体应用,可对请求进行日志分析处理
# mode: "proxy"

# HTTP 代理服务主机名, 默认为 "0.0.0.0", 即全部接收
# 该配置项应用于对 wrapper 代理模式下的 HTTP 服务的主机名。对应 http://{hostname}:{port}
# hostname: "0.0.0.0"

# HTTP 代理服务端口,默认 58080
# port: 58080

# HTTP 代理服务请求重试次数,默认值 3
# 转发请求到单体应用时,如果发起请求失败,将进行重试。超过重试次数仍然失败将重新启动单体应用
# request_retry: 3

# 是否根据单体应用 API 进行过滤,默认为否,即透明模式
# 如果为 true 将根据 API MAP 进行过滤,限制对未提供的 API 进行访问,当前版本采用透明模式不过滤,只支持 false
# 过滤 API 需向请求者返回失败信息,如 405 method not allowed,具体返回格式需确认。
# filter_api: false
# 待过滤的 API URL 信息,具体过滤内容需要与单体应用确认
# api:
# data: "/data"

# 单体应用监控 url , 用于确认单体应用运行是否正常,当前版本支持 GET 方法,根据 HTTP 状态 200 进行确认。如果状态不正确,将重新启动单体应用。
# http://{monolith_ip}:{port}/{check_url}
   check_url: "http://127.0.0.1:8080"

# 监控 URL 检查间隔, 默认 5 秒
# check_interval: 5

# 重启间隔,默认 5 秒
# HTTP 转发支持并发访问,可能同时由多个线程触发重启操作,需限制重启间隔,如单体应用已经重启,且不能提供服务,将返回 503 Service Unavailable 错误。
# restart_interval: 5

# 单体应用控制脚本,默认为 "./launcher.sh"
# script_path: "./launcher.sh"

# monolith 配置
# monolith:
# 单体应用接收转发请求的 IP , 默认为 "127.0.0.1"。
# 该配置项应用于接收  wrapper 代理服务器转发的单体应用主机名。对应 http://{IP}:{port}。
# 采用 "127.0.0.1" 避免其它微服务通过内部 overlay 网络直接访问单体应用。
# 如果 wrapper 不是代理模式,可以设置为 0.0.0.0
# ip: "127.0.0.1"

# 单体应用接收转发请求端口,默认 8080
# port: 8080

# center 配置
center:
    # 注册中心主机名, 默认为 "nacos"。
    hostname: "nacos"

    # 注册中心端口,默认 8848
    # port: 8848

# service 配置
service:
    # 命名空间 id, 需要手工在 nacos 中进行创建
    namespace_id: "43a700c7-0316-4133-913e-810b520c47e4"

    # 服务名,需要注册的服务名
    service_name: "wrapper-provider"

    # 元数据
    metadata:
        service_type: "TYPE"
        company_name: "NAME"

    # 查询服务名
    query_name: ["wrapper-provider"]

    # 集群名,默认值 "DEFAULT"
    # cluster_name: "DEFAULT"

    # 组名,默认值 "DEFAULT_GROUP"
    # group_name: "DEFAULT_GROUP"

    # 用户名,默认值 "nacos"
    # username

    # 密码,默认值 "nacos"
    # password

    # 网卡名,默认值 "eth0"
    ethernet_name: "eth2"

# prometheus 配置
# prometheus:
# 是否启用 prometheus, 默认值 true
# enable: true

# jaeger 配置
jaeger:
    # 是否启用 tracing,默认值 true
    # enable: true

    # tracer 名称,默认值 "jaeger-service"
    name: "wrapper-provider-tracer"

    # 采样率,默认值 0.1
    sampler: 1
    # 服务端点,默认值 ":6831"
    endpoint: "jaeger:6831"
# Pool 设置,Job-Worker 线程池
# pool:
# 调度器名称 默认值,"task"
# dispatcher_name: "task"

# 队列长度,默认值 200
# queue_capacity: 200

# 作业容量,默认值 50
# job_capacity: 50

# 工作者容量,默认值 20
# worker_capacity: 20

# breaker 设置
# breaker:
# 阈值,默认值 20,
# 20 次错误后,breaker 为 open 状态
# threshold: 20

# 刷新周期,默认值 10000 毫秒
# tick: 10000

# timing wheel 时间轮设置
# timing_wheel:
# 时间轮单圈容量,默认值 60
# capacity: 60

# 最短间隔周期,默认值 100 毫秒
# tick: 100

cat launcher.sh

#!/bin/sh -e
app="provider"
pid=`ps aux | grep $app | grep -v grep| sed -n '1P' | awk '{print $1}' `

usage(){
    echo "Usage: ./launcher start|stop"
    exit 1
}

cd ./monolith/

start(){
    if [ -z $pid ];then
        java -jar *.jar &
        echo "`date '+%Y/%m/%d %H:%M:%S'` $app has been started."
    else
        echo "`date '+%Y/%m/%d %H:%M:%S'` $app is running."
    fi
}

stop(){
    if [ $pid ];then
        kill $pid
        echo "`date '+%Y/%m/%d %H:%M:%S'` $app has been stopped."
    fi
}

case "$1" in
    "start") start ;;
    "stop") stop ;;
    *) usage;;
esac

将生成的jar包放置在monolith下
构建镜像
docker build -t waka2020/wrapper-provider:latest .

3、启动服务

cat docker-compose.yml

version: "3.4"
services:
  provider:
    image: waka2020/wrapper-provider:latest
    networks:
      - service_overlay
      - monitor_overlay
    ports:
      - 58080:58080
    volumes:
      -  "/etc/localtime:/etc/localtime:ro"
    environment:
      - CENTER_HOSTNAME=nacos
      - SERVICE_ETHERNET_NAME=eth2
    deploy:
      placement:
        constraints: [node.hostname==node150]
      resources:
        limits:
          cpus: '1'
          memory: '1G'
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
networks:
  service_overlay:
    external: true
  monitor_overlay:
    external: true

docker stack deploy -c docker-compose.yml wrapper

4、配置监控

cat prometheus.yml

...
 - job_name: 'wrapper-provider'
    scrape_interval: 5s
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['provider:58080']
...

增加上面配置后重启prometheus,访问http://node150:58080/actuator/prometheus就可以看到暴露的metrics
登录grafana可以在http request指标下找到与wrapper-provider相关的指标信息,编写查询语句就可以出图了

5、监控出图

对于应用及环境架构,通常从以下几个方面来进行监控指标的采集

  • 应用的api,比如nginx负载均衡器或者微服务入口,对http request指标数据进行采集,通常有请求地址、状态码、响应时长,从而可以通过这些历史指标数据分析业务的压力,服务状态
  • 应用本身的资源使用情况,对于容器可以去监控container的资源使用情况
  • 基础设施,一般指虚拟机或物理服务器资源
  • 其他,比如集群中用到的数据库、缓存、消息队列等组件的状态

监控wrapper-provider容器内的内存使用情况

  • 当前使用内存及分配的内存上限
    08api36
    查询语句
    container_memory_usage_bytes{name=~"wrapper_provider.1.+"}
    container_spec_memory_limit_bytes{name=~"wrapper_provider.1.+"}
  • 内存使用率
    08api37
    查询语句
    sum(container_memory_usage_bytes{name=~"wrapper_provider.1.+"}) / sum(container_spec_memory_limit_bytes{name=~"wrapper_provider.1.+"}) * 100 != +inf

监控wrapper-provider容器内cpu使用情况

  • 当前使用cpu及分配的cpu上限
    08api38
    查询语句
    sum(rate(container_cpu_usage_seconds_total{name=~"wrapper_provider.1.+"}[5m])) by (name)
    (sum(container_spec_cpu_quota{name=~"wrapper_provider.1.+"}/100000) by (name))
  • cpu使用率
    08api39
    查询语句
    sum(container_memory_usage_bytes{name=~"wrapper_provider.1.+"}) / sum(container_spec_memory_limit_bytes{name=~"wrapper_provider.1.+"}) * 100 != +inf

监控http请求
本文的provider应用只返回了一个404的错误页面,所以下面只用语句来说明http指标采集

  • 请求数
    http_server_requests_seconds_count{job="wrapper-provider",uri!~".*actuator.*"}
  • 请求时间
    http_server_requests_seconds_sum{job="wrapper-provider",uri!~".*actuator.*"}
  • 请求响应时间
    rate(http_server_requests_seconds_sum{job="wrapper-provider",uri!~".*actuator.*"}[5m]) / rate(http_server_requests_seconds_count{job="wrapper-provider",uri!~".*actuator.*"}[5m])

监控网络流量

  • 发送字节
    08api40
    查询语句
    sum(rate(container_network_transmit_bytes_total{name=~"wrapper_provider.+"}[$interval])) by (name)

  • 接受字节
    08api41
    查询语句
    sum(rate(container_network_receive_bytes_total{name=~"wrapper_hello.1.+"}[$interval])) by (name)

6、代码链接

wrapper-provider

7、metrics指标参考链接

cAdvisor指标参考1
cAdvisor指标参考2
cAdvisor指标参考3
actuator-http-request指标参考

#