Code Monkey home page Code Monkey logo

docker-prometheus-demo's Introduction

[TOC]

项目简介

开发一个 Spring Boot 应用,并使用云原生功能

过程截图

1. 功能要求

1.1 实现接口(5分)

实现一个 REST 接口(简单接口即可,比如 json 串 {"msg":"hello"})

  • 目前提供/ping接口与限流功能
  • 放到github上方便进行持续集成/持续部署

image-20230729200322667

1.2 实现限流功能 (10分)

接口提供限流功能,当请求达到每秒 100 次的时候,返回 429(Too many requests)

  • 代码:

    • private final RateLimiter rateLimiter = RateLimiter.create(100.0);
      
          @GetMapping("/ping")
          public ResponseEntity<String> hello() {
              if (rateLimiter.tryAcquire()) {
                  return ResponseEntity.ok("{\"msg\":\"Hello, this is NJU13\"}");
              } else {
                  return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many requests");
              }
          }
  • 使用Jmeter进行并发测试,结果显示限流成功

image-20230729200404047

image-20230729200343091

image-20230729200307883

1.3 统一限流(bonus 5分)

加分项:当后端服务有多个实例的时候(一个 Service 包含若干个 Pod),如何实现统一限流(bonus 5分)

  • 配置consumer

    package demo.feign;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import com.netflix.loadbalancer.RoundRobinRule;
    
    @FeignClient(name = "hello-service", configuration = RoundRobinRule.class)
    public interface FeignService
    {
        @GetMapping("/ping")
        String msg();
    }
    package demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    
    @SpringBootApplication
    @EnableFeignClients
    public class DemoApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(DemoApplication.class, args);
    	}
    
    }
    package demo.controller;
    
    
    import com.google.common.util.concurrent.RateLimiter;
    import demo.feign.FeignService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class DemoController {
    
        private final RateLimiter rateLimiter = RateLimiter.create(50.0);
    
        @Autowired
        private FeignService feignService;
    
        @GetMapping("/request")
        public ResponseEntity<String> hello() {
            if (rateLimiter.tryAcquire()) {
                return ResponseEntity.ok(feignService.msg());
            } else {
                return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many requests");
            }
        }
    }
  • 启动eureka服务,注册成功

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 10.40.49 AM.png)

  • 访问admin-service/request,确认两个hello-service在轮流提供服务

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 10.42.17 AM.png)

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 10.42.51 AM.png)

  • 使用Jmeter进行限流测试

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 11.55.41 AM.png)

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 11.55.49 AM.png)

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 11.55.58 AM.png)

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 11.56.10 AM.png)

![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 11.56.19 AM.png)

  • 结论

可以发现在限流允许的范围内,admin-service使用round ribbin来交替请求hello01,与hello02,当请求数超过50次/s时,返回Too many requests,实现限流

2. DevOps 要求

2.1 Dockerfile用于构建镜像(5分)

为该项目准备 Dockerfile,用于构建镜像

  • Dockerfile见项目根目录Dockerfile
 # Dockerfile
 FROM openjdk:17
 
 RUN ln -sf /usr/share/zoneinfo/Asia/shanghai /etc/localtime
 RUN echo 'Asia/shanghai' >/etc/timezone
 
 WORKDIR /app
 ADD target/demo-0.0.1-SNAPSHOT.jar .
 
 ENTRYPOINT ["java", "-jar", "demo-0.0.1-SNAPSHOT.jar"]
 

2.2 Kubernetes编排文件(5分)

为该项目准备 Kubernetes 编排文件,用于在 Kubernetes 集群上创建 Deployment 和 Service

  • k8s部署内容见项目中 ./jenkins/scripts/demo.yaml 文件
 # demo.yaml
 apiVersion: apps/v1
 kind: Deployment #对象类型
 metadata:
   labels:
     app: lar-demo
   name: lar-demo
   namespace: nju13
 spec:
   replicas: 1 #运行容器的副本数
   selector:
     matchLabels:
       app: lar-demo
   template:
     metadata:
       annotations:
         prometheus.io/path: /actuator/prometheus
         prometheus.io/port: "8080"
         prometheus.io/scheme: http
         prometheus.io/scrape: "true"
       labels:
         app: lar-demo
     spec:
       containers: #docker容器的配置
       - image: harbor.edu.cn/nju13/lar-demo:{VERSION} #pull镜像的地址,本地测试时注销
 #      - image: demo:latest  # win下本地测试用
         name: lar-demo
 #      imagePullPolicy: Always # 本地测试用
       imagePullSecrets: # 本地测试时注销
        - name: docker-harbor-nju13 # 本地测试时注销
 ---
 apiVersion: v1
 kind: Service
 metadata:
   name: lar-demo
   namespace: nju13
   labels:
     app: lar-demo
 spec:
   type: NodePort
   selector:
     app: lar-demo
   ports:
   - name: tcp
     nodePort: 30332
     protocol: TCP
     port: 8080
     targetPort: 8080
 

2.3 持续集成流水线(5分)

2.4 持续部署流水线(5分)

2.3 编写 Jenkins 持续集成流水线,实现代码构建/单元测试/镜像构建功能(需要写至少一个单元测试)

2.4 编写 Jenkins 持续部署流水线,实现部署到 Kubernetes 集群的功能,该流水线的触发条件为持续集成流水线执行成功

注意:持续集成流水线和持续部署流水线也可以合二为一。

以下为2.3和2.4的需求实现

  • Jenkinsfile见项目中 ./jenkins/Jenkinsfile文件
 pipeline {
     agent none
 
     environment {
         REGISTRY = "harbor.edu.cn/nju13"
     }
 
     stages {
         stage('Clone Code') {//第一步:克隆代码,为了build image
             agent {
                 label 'master'
             }
             steps {
                 echo "1.Git Clone Code"
                 sh 'curl "http://p2.nju.edu.cn/portal_io/login?username=211250127&password=******"'
                 git branch: 'main', url:
 //                 'https://github.com/lar0129/prometheus-test-demo.git'
                 "https://gitee.com/lar0129/prometheus-test-demo.git"
             }
         }
         stage('Maven Build') {//第二步:maven打包
             agent {
                 docker {
                     image 'maven:latest'
                     args '-v /root/.m2:/root/.m2'
                 }
             }
             steps {
                 echo "2.Maven Build Stage"
                 sh 'mvn -B clean package -Dmaven.test.skip=true'
             }
         }
         stage('Image Build') {//第三步:构建镜像
             agent {
                 label 'master'
             }
             steps {
             echo "3.Image Build Stage"
             sh 'docker build -f Dockerfile --build-arg jar_name=target/demo-0.0.1-SNAPSHOT.jar -t lar-demo:${BUILD_ID} . '
             // library使用宏定义,可以在全局设置中设置
             sh 'docker tag  lar-demo:${BUILD_ID}  ${REGISTRY}/lar-demo:${BUILD_ID}'
             }
         }
         stage('Push') {
             agent {
                 label 'master'
             }
             steps {
             echo "4.Push Docker Image Stage"
             sh "docker login --username=nju13 harbor.edu.cn -p nju132023"
             sh "docker push ${REGISTRY}/lar-demo:${BUILD_ID}"
             }
         }
     }
 }
 
 
 node('slave') {
     container('jnlp-kubectl') {
 
         stage('Clone YAML') {
         echo "5. Git Clone YAML To Slave"
         sh 'curl "http://p2.nju.edu.cn/portal_io/login?username=211250127&password=*****"'
         git branch: 'main', url:
 //    'https://github.com/lar0129/prometheus-test-demo.git'
         "https://gitee.com/lar0129/prometheus-test-demo.git"
         }
         
         stage('YAML') {
         echo "6. Change YAML File Stage"
         // ./jenkins/scripts/是自己创建的?
         sh 'sed -i "s#{VERSION}#${BUILD_ID}#g" ./jenkins/scripts/demo.yaml'
         }
     
         stage('Deploy') {
         echo "7. Deploy To K8s Stage"
         sh 'kubectl apply -f ./jenkins/scripts/demo.yaml -n nju13'
         // sh 'kubectl apply -f ./jenkins/scripts/demo-monitor.yaml'
         }
 
         stage('Monitor') {
             echo "8. Deploy Prometheus Monitor"
              sh 'kubectl apply -f ./jenkins/scripts/demo-monitor.yaml'
         }
 
         stage('RTF Test'){//集成测试
         echo "9. RTF Test Stage"
         // sh 'kubectl apply -f ./jenkins/scripts/rtf.yaml -n nju13'
         }
     }
 }
 
 
  • 具体过程:

    1.将jenkins连接到k8s-nju13-namespace中,完成鉴权

    2.部署流水线

    • Jenkins流程:
    Stage step
    持续集成 1.Git Clone Code 拉取spring boot代码
    2.Maven Build maven构建jar包
    3.Image Build 构建镜像
    4.Push Image To Harbor push镜像至docker仓库
    持续部署 5.Git Clone Yaml 拉取部署所需yaml文件
    6.Change YAML File 改变Yaml环境变量
    7.Deploy the Web App 部署spring boot应用
    8.Deploy the Monitor 部署Prometheus监控
    集成测试 9.Test 测试
    • 部署成功截图

    image-20230730224910852

    3.访问端口验证k8s部署是否成功

    • 集群网址172.29.4.18,Nodeport配置为30333,故访问172.29.4.18:30333/ping,验证成功

    • image-20230731185135102

    1. 流水线单元测试
    • 添加针对demoController的单元测试
     package nju13.demo;
     
     import jakarta.annotation.Resource;
     import nju13.demo.controller.DemoController;
     import org.junit.Assert;
     import org.junit.Test;
     import org.junit.runner.RunWith;
     import org.springframework.boot.test.context.SpringBootTest;
     import org.springframework.test.context.junit4.SpringRunner;
     
     @SpringBootTest
     @RunWith(SpringRunner.class)
     public class PingServiceTest
     {
         @Resource
         private DemoController demoController;
     
         @Test
         public void tryPing(){
             Assert.assertEquals("{\"msg\":\"Hello, this is NJU13\"}",demoController.hello().getBody());
         }
     }
     
    • 在jenkins file中添加junit测试步骤
     stage('Unit Tests') { //第三步:单元测试
                 steps {
                     sh 'mvn test'
                     junit '**/target/surefire-reports/*.xml'
                 }
             }

2.5 代码提交到仓库自动触发流水线(bonus 5分)

加分项:代码提交到仓库自动触发流水线

  • 尝试使用gitee webhook

    经过测试,学校的jenkins服务没有公网ip或域名,因此当代码更新时无法通过webhook发送请求到jenkins,因此难以实现触发式更新

  • 经过权衡,最后使用轮询式更新,在configuration中输入H/5 * * * *,让代码仓库每五分钟轮询gitee是否有更新

    ![](https://files.lsmcloud.top/blogScreen Shot 2023-08-14 at 12.04.26 PM.png)

    轮询到代码仓库更新后,jenkins开始重新构建项目

3. 扩容场景

3.1 Prometheus采集监控指标(5分)

为该 Java 项目提供 Prometheus metrics 接口,可以供 Prometheus 采集监控指标

  • 在springboot项目中配置Prometheus metrics 接口

    • image-20230801112225930
  • 部署k8s-monitor对象

    • yaml代码位于./jenkins/script/demo-monitor.yaml
     apiVersion: monitoring.coreos.com/v1
     kind: ServiceMonitor
     metadata:
       labels:
         k8s-app: lar-demo
       name: lar-demo
       namespace: monitoring
     spec:
       endpoints:
       - interval: 30s
         port: tcp
         path: /actuator/prometheus
         scheme: 'http'
       selector:
         matchLabels:
           app: lar-demo
       namespaceSelector:
         matchNames:
         - nju13
    • 放入流水线中部署

    image-20230801112409002

  • 在Prometheus的UI界面验证部署是否成功

    image-20230801112050506

3.2 Grafana定制应用监控大屏(5分)

在 Grafana 中的定制应用的监控大屏(CPU/内存/JVM)

  • 网站系统的安全隐患:Grafana中默认的管理员账户admin密码admin没改,随随便便就能进去操作
  • 在Grafana 中的定制监控应用lar-demo的各种性能
    • 容器CPU使用情况
    • 容器内存使用情况
    • 网络请求情况 image-20230801150417996

3.3 压测并观察监控数据(5分)

使用压测工具(例如 Jmeter)对接口进压测,在 Grafana 中观察监控数据

  • 使用Jmeter,向172.29.4.18:30332/ping 用100个进程同时发送 100个GET请求

    配置和结果如下 image-20230801150552940 image-20230801150604028 image-20230801150640926

    • 部分请求错误,证明限流成功
  • Grafana 监控应用lar-demo结果如下,明显可看出变化

    • CPU使用情况 image-20230801150824336

      • 从25升至50
    • Memory使用情况 image-20230801150928604

      • 从2.7G升至3G
    • 网络使用情况 image-20230801151009171

  • 通过 Kubernetes 命令进行手工扩容,并再次观察 Grafana 中的监控数据

    命令行扩容发现权限不足,故只能更改yaml文件重新构建流水线

    image-20230801152235388

    image-20230801152328292

  • 扩容后观察Grafana可见,Cpu和Memory的监控面板新增两个Container的曲线

    image-20230801152753480

3.5 Auto Scale(bonus 10分)

加分项:使用 Kubernetes HPA 模块根据 CPU 负载做服务的 Auto Scale

  • 创建HPA并查看

  • 创建demo-hpa.yaml如下配置hpa自动扩容的参数

     apiVersion: apps/v1
     kind: Deployment
     metadata:
       labels:
         app: lar-demo-hpa
       name: lar-demo-hpa
       namespace: nju13
     spec:
       replicas: 1
       selector:
         matchLabels:
           app: lar-demo-hpa
       template:
         metadata:
           labels:
             app: lar-demo-hpa
         spec:
           containers:
             - name: lar-demo-hpa
               image: harbor.edu.cn/nju13/lar-demo:70
               ports:
                 - containerPort: 8080
               resources: #基于内存自动扩容
                 limits:
                   cpu: 200m
                 requests:
                   cpu: 80m  
           imagePullSecrets: # 本地测试时注销
             - name: docker-harbor-nju13 # 本地测试时注销
     
     ---
     apiVersion: v1
     kind: Service
     metadata:
       name: lar-demo-hpa
       namespace: nju13
     spec:
       ports:
         - port: 8080
           protocol: TCP
       selector:
         app: lar-demo-hpa
       type: ClusterIP
     
  • 流水线中执行yaml文件创建deploy

    image-20230815224934507

  • 设置HPA:最小1个pod,最多10个pods,cpu负载超过50%扩容

    image-20230815224952591

  • 查看HPA

    image-20230815225026390

    demo-hpa成功监测。且目前replicas数量为1

  • 压测验证HPA

    • image-20230815230319182
    • 146个请求后到达极限
  • 压力测试后,观测到HPA自动扩容成功

    • image-20230815230426622
    • image-20230815230456357

分数完成度

完成所有需求及加分项

1.功能要求(20分)

1.1 实现接口 (5 分)√

1.2 实现限流功能(10 分)√

1.3 实现接口访问指标(QPS),并暴露给 Prometheus(5分)√

1.3 **统一限流(bonus 5 分)**√

2. DevOps 要求(20分)

2.1 Dockerfile 用于构建镜像(5 分)√

2.2 Kubernetes 编排文件(5 分)√

2.3 持续集成流水线(5 分)√

2.4 持续部署流水线(5 分)√

**2.5 代码提交到仓库自动触发流水线(bonus 5分)**√

3. 扩容场景(15分)

3.1 Prometheus 采集监控指标(5 分)√

3.2 Grafana 定制应用监控大屏(5 分)√

3.3 压测并观察监控数据(5 分) √

**3.5 Auto Scale(bonus 10 分)**√

docker-prometheus-demo's People

Contributors

lar0129 avatar jayzzzzj avatar

Stargazers

 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.