K8s 搭建 FileBeat+ELK 分布式日志收集系统 以及 KQL 语法介绍

news2024/11/20 15:36:51

一、K8s FileBeat + ELK 介绍

ELK,即ElasticsearchLogstashKibana三个开源软件的组合,是由Elastic公司提供的一套完整的日志管理解决方案。Elasticsearch是一个高度可扩展的开源全文搜索和分析引擎,它允许你快速地、近乎实时地存储、搜索和分析大量数据。Logstash是一个服务器端数据处理管道,它能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如Elasticsearch等存储库中。Kibana则允许你通过Elasticsearch使用图表和表格直观地展示数据。

ELK主要解决了如何有效地收集、处理、存储和可视化大量的日志数据。优势在于它能够处理和分析大量的日志数据,提供实时的搜索和分析能力,同时它的分布式设计使其具有很高的扩展性。但劣势在于它的配置和维护相对复杂,特别是Logstash,需要编写复杂的配置文件,对系统资源的要求也相对较高。

然而,直接使用ELK栈在数据收集方面可能存在一些问题。例如,Logstash是一个重量级的工具,它在数据收集过程中可能会消耗大量的系统资源,这对于资源有限的环境来说可能是一个问题。此外,Logstash的配置也相对复杂,需要编写大量的配置文件,这对于一些简单的日志收集任务来说可能显得过于繁琐。

为了解决这些问题,Elastic公司推出了FilebeatFilebeat是一个轻量级的日志传输工具,它可以安装在需要收集日志的服务器上,实时读取日志文件,并将这些日志数据发送到指定的输出,如LogstashElasticsearchkafka。它的优势在于轻量级设计,对系统资源的消耗极低,同时它的配置也相对简单,易于管理和维护。通过接入Filebeat,可以有效地解决Logstash在数据收集方面的问题,提高整个ELK栈的性能和稳定性。

在这里插入图片描述

K8s 环境中的 ELK

随着 k8s 的越来越普及化,很多应用都被部署在了 k8s 中,如何在 k8s 中进行 ELK 日志管理呢?首先要搞清楚 k8s 中的日志都存在了哪里,可以主要从下面三个目录入手:

  • /var/lib/docker/containers/ :是Docker守护进程用于存储容器元数据和日志数据的地方。每个容器的日志文件通常以<container_ID>-json.log的形式存储在这个目录下的相应子目录中。这些日志文件包含了容器的stdoutstderr输出,并且是以JSON格式存储的。Filebeat可以直接读取这些文件来收集容器的日志。

  • /var/log/containers/ :是k8s为了便于日志管理而创建的符号链接(symlink)目录。它包含了到/var/lib/docker/containers/目录中日志文件的链接。每个符号链接的名称通常是由Pod名称、容器名称和实例编号组成的。Filebeat可以通过这个目录轻松地访问和收集容器的日志,而不需要直接访问Docker的内部目录。

  • /var/log/pods/ :是 K8s 用于存储Pod日志的地方。每个Pod都会在这个目录下有一个以Pod UID命名的子目录,Pod内的每个容器又有各自以容器名称命名的日志文件。这些日志文件包含了容器的stdoutstderr输出,但是它们是以文本格式而不是JSON格式存储的。这个目录的结构有助于保持Pod和容器日志的隔离性,便于管理和访问。

因此我们可以主要从 /var/log/containers/ 目录入手,FilebeatDaemonSet的方式部署在k8s集群中。DaemonSet可以确保每个节点上都有一个Filebeat实例在运行,通过读取宿主机上的日志文件来收集k8s的日志。

Filebeat 中已经内置了 k8s 的支持,我们可以通过配置 processors.add_kubernetes_metadata 来增强日志数据,将Kubernetes的元数据添加到日志事件中,比如Pod名称、Namespace、容器名称等:

  processors:
    - add_kubernetes_metadata:
        default_indexers.enabled: true
        default_matchers.enabled: true
        matchers:
        - logs_path:
            logs_path: "/var/log/containers/"

当数据发送给到 logstash 时,直接可以通过 [kubernetes][namespace][kubernetes][container][name] 获取到 k8s 中的元数据信息,因此可以利用 logstash 动态创建不同应用的动态日志索引,针对每个命名空间下不同的应用创建单独的索引记录日志,这样更便于后期对日志的排查。例如 logstash.conf 可以这样配置:

output { 
  if [kubernetes][namespace] and [kubernetes][container][name] {
    elasticsearch { 
      hosts => ["http://elasticsearch:9200"] 
      index => "k8s-%{[kubernetes][namespace]}-%{[kubernetes][container][name]}-%{+YYYY.MM.dd}"
    }
  } else {
    elasticsearch { 
      hosts => ["http://elasticsearch:9200"] 
      index => "k8s-%{+YYYY.MM.dd}"
    }
  }
} 

下面开始实践在 k8s 中部署 FileBeat+ELK 分布式日志系统,实验采用版本均为 7.14.0

创建命名空间

这里首先创建一个命名空间,后续所有操作均在该空间下:

kubectl create ns elk

创建测试服务

在开始前这里创建一个 SpringBoot 项目,项目名称为 elk-demo ,用来打印不同级别的日志,用来验证是否能被收集,以及收集的索引是否动态创建。

创建测试接口:

@Slf4j
@RestController
public class TestController {

    @GetMapping("/t1")
    public void t1() {
        log.info("this is test, level: info");
        log.warn("this is test, level: warn");
        log.debug("this is test, level: debug");
        log.error("this is test, level: err", new RuntimeException("Err detail!"));
    }
    
}

编写 Dockerfile 文件:

FROM java:8
MAINTAINER bxc
WORKDIR /app
ENV TZ=Asia/Shanghai
ADD target/elk-demo-0.0.1-SNAPSHOT.jar /app/app.jar
CMD ["java", "-jar", "app.jar"]

构建镜像并上传至私服 harbor 中,如果没有 harbor 仓库也可以将镜像上传至每个 k8s node 节点中,然后使用 docker load 到本地镜像仓库中。

# 打包成 jar 包
mvn clean package
# 构建镜像
cd elk-demo
docker build -t elk-demo:1.0 .
# 上传至 harbor
docker tag elk-demo:1.0 11.0.1.150/image/elk-demo:1.0 .
docker push 11.0.1.150/image/elk-demo:1.0

k8s 部署测试服务

vi elk-demo.yml
apiVersion: v1
kind: Service
metadata:
  name: elk-demo
  namespace: elk
  labels:
    app: elk-demo
spec:
  type: NodePort
  ports:
    - port: 8080
      name: client
      nodePort: 31880
      targetPort: 8080
  selector:
    app: elk-demo

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: elk-demo
  namespace: elk
spec:
  replicas: 3
  selector:
    matchLabels:
      app: elk-demo
  template:
    metadata:
      labels:
        app: elk-demo
    spec:
      containers:
        - name: elk-demo
          image: elk-demo:1.0
          ports:
            - containerPort: 8080
              name: server
          env:
          - name: TZ
            value: Asia/Shanghai

kubectl apply -f elk-demo.yml

二、K8s 部署 FileBeat+ELK

2.1 部署 ES 集群和 Kibana

ES 集群和 kibana 的部署过程可以参考下面这篇文章:

K8s 部署 elasticsearch-7.14.0 集群 及 kibana 客户端

这里我 ESService 名称为 elasticsearch ,所以后面 ES 的访问地址可以写成 :http://elasticsearch:9200

2.2 部署 logstach

vi logstach.yml
apiVersion: v1 
kind: Service
metadata: 
  name: logstash
  namespace: elk
  labels:
    app: logstash
spec: 
  ports: 
  - port: 5044
    name: logstash  
    targetPort: 5044    
  selector: 
    app: logstash 
  type: ClusterIP
  
--- 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: logstash-conf 
  namespace: elk
data: 
  logstash.conf: |- 
    input {
      beats { 
        port => 5044 
      } 
    } 
    filter {
      grok {
        match => { "message" => "(%{TIMESTAMP_ISO8601:logdatetime}  %{LOGLEVEL:level} %{GREEDYDATA:logmessage})|%{GREEDYDATA:logmessage}" }
        remove_field => [ "logmessage" ]
      }
    }
    
    output { 
      if [kubernetes][namespace] and [kubernetes][container][name] {
        elasticsearch { 
          hosts => ["http://elasticsearch:9200"] 
          index => "k8s-%{[kubernetes][namespace]}-%{[kubernetes][container][name]}-%{+YYYY.MM.dd}"
        }
      } else {
        elasticsearch { 
          hosts => ["http://elasticsearch:9200"] 
          index => "k8s-%{+YYYY.MM.dd}"
        }
      }
    } 
    
---
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: logstash-yml 
  namespace: elk 
  labels: 
    type: logstash 
data: 
  logstash.yml: |-
    http.host: "0.0.0.0"
    xpack.monitoring.elasticsearch.hosts: http://elasticsearch:9200
    
---
apiVersion: apps/v1 
kind: Deployment
metadata:
  name: logstash 
  namespace: elk
spec: 
  selector: 
    matchLabels: 
      app: logstash 
  template: 
    metadata: 
      labels: 
        app: logstash 
    spec: 
      containers: 
      - image: elastic/logstash:7.14.0
        name: logstash 
        ports: 
        - containerPort: 5044 
          name: logstash 
        command: 
        - logstash 
        - '-f' 
        - '/usr/share/logstash/config/logstash.conf' 
        volumeMounts: 
        - name: config-volume 
          mountPath: /usr/share/logstash/config/logstash.conf
          subPath: logstash.conf
        - name: config-yml-volume 
          mountPath: /usr/share/logstash/config/logstash.yml
          subPath: logstash.yml
        resources:
          limits: 
            cpu: 1000m 
            memory: 2048Mi 
          requests: 
            cpu: 512m 
            memory: 128Mi 
      volumes: 
      - name: config-volume 
        configMap: 
          name: logstash-conf 
      - name: config-yml-volume 
        configMap: 
          name: logstash-yml 

kubectl apply -f logstach.yml

查看启动 pod

kubectl get pods -n elk

在这里插入图片描述

观察 logstash 日志,是否启动成功:

kubectl logs -f logstash-84d8d7cd55-rcfgs -n elk

在这里插入图片描述

2.3 部署 filebeta

注意,由于 filebeta 需要获取 k8s 中的信息,这里需要创建 ServiceAccount 并授权获取信息的权限。

vi filebeta.yml
apiVersion: v1 
kind: ServiceAccount
metadata: 
  name: filebeat-account
  namespace: elk
  labels: 
    app: filebeat
--- 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole 
metadata: 
  name: filebeat-cr
  labels: 
    app: filebeat 
rules: 
- apiGroups: [""] 
  resources: 
  - namespaces 
  - pods 
  - nodes
  verbs: ["get", "watch", "list"]
--- 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding 
metadata: 
  name: filebeat-crb
subjects: 
- kind: ServiceAccount 
  name: filebeat-account 
  namespace: elk 
roleRef: 
  kind: ClusterRole 
  name: filebeat-cr 
  apiGroup: rbac.authorization.k8s.io 

---
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: filebeat-config 
  namespace: elk
data: 
  filebeat.yml: |- 
    filebeat.inputs: 
    - type: container 
      enable: true 
      paths: 
        - /var/log/containers/*.log 
      multiline: 
        pattern: '^\d{4}-\d{1,2}-\d{1,2}'
        negate: true
        match: after
        max_lines: 100
        timeout: 5s
      processors:
        - add_kubernetes_metadata:
            default_indexers.enabled: true
            default_matchers.enabled: true
            matchers:
            - logs_path:
                logs_path: "/var/log/containers/"
                        
    output.logstash:  
       hosts: ["logstash:5044"] 
       enabled: true 
    #output.kafka:  # 输出到 kafka 示例
    #  hosts: ["kafka-01:9092", "kafka-02:9092", "kafka-03:9092"] 
    #  topic: 'topic-log' 
    #  version: 2.0.0 
--- 
apiVersion: apps/v1 
kind: DaemonSet  
metadata: 
  name: filebeat 
  namespace: elk
  labels: 
    app: filebeat 
spec: 
  selector: 
    matchLabels: 
      app: filebeat 
  template: 
    metadata: 
      labels: 
        app: filebeat 
    spec: 
      serviceAccountName: filebeat-account
      containers: 
      - name: filebeat 
        image: elastic/filebeat:7.14.0
        args: [ 
          "-c", "/etc/filebeat.yml", 
          "-e", "-httpprof","0.0.0.0:6060" 
        ] 
        securityContext: 
          runAsUser: 0 
          # If using Red Hat OpenShift uncomment this: 
          #privileged: true 
        resources: 
          limits: 
            memory: 1000Mi 
            cpu: 1000m 
          requests: 
            memory: 100Mi 
            cpu: 128m 
        volumeMounts: 
        - name: filebeat-config  
          mountPath: /etc/filebeat.yml 
          subPath: filebeat.yml 
        - name: filebeat-data
          mountPath: /usr/share/filebeat/data 
        - name: docker-containers 
          mountPath: /var/lib/docker/containers
          readOnly: true 
        - name: pods-log
          mountPath: /var/log/pods 
          readOnly: true 
        - name: containers-log
          mountPath: /var/log/containers
          readOnly: true 
      volumes: 
      - name: filebeat-config 
        configMap: 
          name: filebeat-config 
      - name: filebeat-data 
        hostPath: 
          path: /data/filebeat-data 
          type: DirectoryOrCreate 
      - name: docker-containers 
        hostPath: 
          path: /var/lib/docker/containers
      - name: pods-log 
        hostPath: 
          path: /var/log/pods
      - name: containers-log 
        hostPath: 
          path: /var/log/containers
kubectl apply -f filebeta.yml

查看 Pod

在这里插入图片描述
由于我测试环境的 k8s node 有三台,所以这里起了三个 filebeat

观察其中某个 filebeta 日志,是否启动成功:
在这里插入图片描述

三、ELK 环境测试

访问测试服务,产生日志:http://{k8s node ip}:31880/t1

然后查询当下所有 k8s 开头的索引:

GET /_cat/indices/k8s*?h=index

在这里插入图片描述

可以看到测试服务的索引已经生成。

3.1 kibana 日志查询

  1. 点击侧边栏 Discover

    在这里插入图片描述

  2. 然后在 Index patternsCreate an index pattern

    在这里插入图片描述

  3. 这里输入测试服务的索引 k8s-elk-elk-demo-2024.05.17 ,注意改成你环境下的名字,然后点击 Next step :

    在这里插入图片描述

  4. 然后时间字段可以选择 @timestamp :

    在这里插入图片描述

  5. 然后再次点击侧边栏 Discover 进到查询页面,使用介绍如下:

    在这里插入图片描述

  6. 例如查询包含 ERROR 的日志:

    在这里插入图片描述

    这里的 KQL 语法,在文章最后给了出常用语法说明。

3.2 kibana 日志监测

  1. 点击侧边栏 logs

    在这里插入图片描述

  2. 点击修改配置:

    在这里插入图片描述

  3. 增加 k8s-* 的监测,然后点击最后的 Apply

在这里插入图片描述

  1. 然后在 Stream 中可以查看日志:

    在这里插入图片描述

3.3 kibana 使用 ES DSL 查询日志

查询错误日志:

GET /k8s-elk-elk-demo-2024.05.17/_search
{
  "query": {
    "match": {
      "message": "java.lang.RuntimeException"
    }
  },
  "sort": [{
		"@timestamp": {
			"order": "desc"
		}
	}]
}

在这里插入图片描述

四、KQL 语法介绍

KQLKibana Query Language)是Kibana中用于搜索和过滤数据的查询语言。它是一种简单的、易于学习的语法,允许用户在不熟悉Elasticsearch的复杂查询DSL的情况下,也能够执行有效的数据搜索和过滤操作。

注意:KQL默认是大小写不敏感的。

4.1 关键字查询

例如:查询 message 字段中包括 hello word 的:

message:"hello word"

如果想要分词,可以不加双引号,这样包含 helloword 的也可以被检索出来:

message:hello word

4.2 逻辑查询

支持 orandnot查询,and的优先级高于or

message:error and kubernetes.pod.name: "elk-demo-6f8b4b7fd7-4xskf"
message:error and (kubernetes.node.hostname:node1 or kubernetes.node.name:node1)

4.3 范围查询

支持对数字和日志类型使用 < <= > >=

log.offset > 881576

4.4 Wildcard 查询

message:*error*

4.5 字段存在查询

message:*

4.6 字段不存在

not _exists_:level

或者

not level:*

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1691710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PCB设计——返回路径

回流基本概念 从电路理论上看&#xff0c;信号是由电流传播的&#xff0c;明确的说是电子的运动&#xff0c;电子流的特性之一就是电子从不在任何地方停留&#xff0c;无论电流流到哪里&#xff0c;必然要回来&#xff0c;因此电流总是在环路中流动&#xff0c;从源到负载然后从…

高效使用 LaTeX 技巧

但对于一般人而言&#xff0c;你不需要通过学习 Vim 来达到高效编辑 LaTeX 的方式。而是通过一些比较容易实现的方式&#xff0c;使得你能够在原来的基础上更加高效得使用 LaTeX&#xff0c;并达到以思考的速度输入 LaTeX 的方式。 在第一部分&#xff0c;我会首先介绍高效编辑…

1301-习题1-1高等数学

1. 求下列函数的自然定义域 自然定义域就是使函数有意义的定义域。 常见自然定义域&#xff1a; 开根号 x \sqrt x x ​&#xff1a; x ≥ 0 x \ge 0 x≥0自变量为分式的分母 1 x \frac{1}{x} x1​&#xff1a; x ≠ 0 x \ne 0 x0三角函数 tan ⁡ x cot ⁡ x \tan x \cot x …

告别登录烦恼,WPS免登录修改器体验!(如何实现不登录使用WPS)

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f3e1; 演示环境 &#x1f3e1;&#x1f4d2; 解决方案 &#x1f4d2;&#x1f388; 获取方式 &#x1f388;⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4d6; 想象一下&#xff0c;如果你能够绕过繁琐的登录流程&#x…

探秘机器学习经典:K-近邻算法(KNN)全解析

在浩瀚的机器学习宇宙中,K-近邻算法(K-Nearest Neighbors,简称KNN)如同一颗璀璨的明星,以其简洁直观的原理和广泛的应用范围,赢得了众多数据科学家的喜爱。今天,让我们一起揭开KNN的神秘面纱,深入探讨它的运作机制、优缺点、应用场景,以及如何在实际项目中灵活运用。 …

从零到一建设数据中台 - 数据治理路径

一、数据治理的内容 数据治理用于规范数据的生成以及使用,改进数据质量,对数据进行加工处理,提升数据价值。提供识别和度量数据质量能力、数据清洗转换能力、数据加工三个核心能力。 数据汇集:数据汇集是数据中台数据接入的入口,所有数据来自于业务系统、日志、文件、网络…

JDBC总结

目录 JDBC(java database connection) JDBC连接数据库步骤: 1. 在项目中添加jar文件,如图所示 2.加载驱动类 向数据库中插入数据代码示例: 第一种: 第二种: 查询操作 : 第一种: 第二种: JDBC(java database connection) java数据库连接.api(应用程序编程接口) ,可…

esp32开发中CMakeLists.txt文件在编译时添加打印信息

在使用CMakeLists.txt文件时&#xff0c;我们时常会对一些宏定义表示的具体路径表示迷茫&#xff0c;不太确定具体表示的路径是哪个&#xff0c;这个时候就希望能在编译的时候打印当前文件中使用的宏定义表示的路径的具体信息。 就像下图中&#xff0c;编译时打印出 CMAKE_CU…

牛客NC391 快乐数【simple 模拟法 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/293b9ddd48444fa493dd17da0feb192d 思路 直接模拟即可Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值…

serverless在点淘的质量保障实践

SERVERLESS能够将应用分为研发域和运维域&#xff0c;使两者独立迭代&#xff0c;降低运维成本&#xff0c;提升研发效率。点淘作为试点项目&#xff0c;经历了包括功能回归、压力测试和监控验证在内的质量保障流程&#xff0c;并在实践中遇到了各种问题&#xff0c;如依赖梳理…

VOS3000被DDOS攻击后该怎么办

VOS3000遭受DDoS攻击的应对措施 当VOS3000遭受DDoS攻击时&#xff0c;可以采取以下几个步骤来应对&#xff1a; 立即启动防火墙&#xff1a;尽管难以完全阻止DDoS攻击&#xff0c;但防火墙可以在一定程度上帮助抵御攻击&#xff0c;减轻其造成的危害。 联系服务器提供商&#…

抖音小店新规重磅来袭!事关店铺流量!商家的福音来了?

大家好&#xff0c;我是喷火龙。 就在前两天&#xff0c;抖店发布了新规&#xff0c;我给大家总结了一下&#xff0c;无非就是两点。 第一点&#xff1a;保证金下调&#xff0c;一证开多店。 第二点&#xff1a;新品上架破10单&#xff0c;有流量扶持。 咱来细细的解读&…

人生苦短,我学python之数据类型(下)

个人主页&#xff1a;星纭-CSDN博客 系列文章专栏&#xff1a;Python 踏上取经路&#xff0c;比抵达灵山更重要&#xff01;一起努力一起进步&#xff01; 目录 一.集合 1.1子集与超集 1.2交集&#xff0c;并集&#xff0c;补集&#xff0c;差集 1.intersection(英文&a…

学习笔记——STM32F103V3版本——HC-05模块控制数码管

一.硬件 1.HC-05模块 2.数码管 3.连接硬件 二.在keil5中的代码 main.c代码&#xff1a; #include "stm32f10x.h" #include "buletooth.h" #include "led.h" #include "sys.h" #include "usart.h" #include "delay.…

python实用系列:按顺序重命名文件

啊&#xff0c;好久没更博客了&#xff0c;今天偶然想换个桌面壁纸&#xff0c;于是上网搜了两个比较满意的桌面壁纸&#xff0c;都是压缩包&#xff1a; 当我想要给他们放到我的桌面壁纸文件里的时候患了难&#xff0c;因为他们的名字有相同的&#xff1a; anime文件夹里边&a…

el-select可选择可搜索可输入新内容

需求&#xff1a;el-form-item添加el-select&#xff0c;并且el-select可选择可搜索可输入新内容&#xff0c;并且和其他的el-input做联动&#xff0c;如果是选择&#xff0c;那么el-input自动回填数据并且不可编辑&#xff0c;如果el-select输入新的内容&#xff0c;那么el-in…

js禁止使用浏览器的前进后退按钮的方法

效果图&#xff1a; // 替换当前页面的历史记录&#xff0c;使用户不能通过浏览器的前进后退按钮导航 history.replaceState(null, null, location.href);// 监听浏览器的历史记录变化事件 window.onpopstate function(event) {// 再次替换当前页面的历史记录&#xff0c;确保…

接口使用实例——数组排序

对于基本数据类型的大小比较&#xff0c;我们可以使用<,>,或者equals方法进行比较&#xff0c;但是对象之间如何进行比较呢&#xff1f;要对对象进行比较&#xff0c;我们必须对同一个成员变量进行比较&#xff0c;如我们可以通过比较name的大小来得出两个对象的大小&…

四万字长文详解——node.js使用移动云,EOS对象存储

目录 前言 安装及安装前的操作 前置条件 如何创建认证信息 使用npm安装SDK开发包 安装开发包命令 初始化操作 存储桶 查看结果命令 查看桶列表 查看结果命令 删除桶 查看结果命令 创建桶 获取桶列表 判断桶是否存在 查询桶所属地域 查询桶的访问权限 管理桶的…

前后端 | 低代码平台之 Erupt

前文提要 最近大家是不是都有那种危机感&#xff0c;项目变多了&#xff0c;工时压紧了&#xff0c;老板说&#xff0c;我不管你加不加班&#xff0c;我只看结果&#xff0c;项目经理说&#xff0c;我不管你用什么技术栈&#xff0c;我只要没BUG&#xff0c;测试说&#xff0c…