跳到主要内容

Prometheus 监控注册 Nacos 注册的服务

· 阅读需 6 分钟

Prometheus 监控注册 Nacos 注册的服务

Prometheus官方提供Consul为注册中心的配置方式且只支持Consul的接口,配置后可自动获取Consul中所有实例的信息并进行监控,这里使用 Nacos Consul Adapter 来让 Nacos 伪装成 Consul

在 Spring Cloud Gateway 引入jar包

可以是服务中任意的节点,在Spring Cloud Gateway中引入jar包的原因是它基于Reactor,如果不是使用Spring WebFlux则还需要引入额外的包。

<dependency>
<groupId>io.github.chen-gliu</groupId>
<artifactId>nacos-consul-adapter</artifactId>
<version>version</version>
</dependency>

如果拉取不到包,可以在setting文件中添加如下配置:

<mirror>
<id>mvnrepository</id>
<mirrorOf>*</mirrorOf>
<name>仓库</name>
<url>https://repo1.maven.org/maven2</url>
</mirror>

修改应用的依赖及配置

pom.xml 文件中添加相关的 Maven 依赖项,视情况调整相应的版本

<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.5.1</version>
</dependency>

修改代码,暴露指标接口

在项目启动时,添加相应的监控配置,同时 micrometer 也提供了部分常用的监控数据采集,具体在 io.micrometer.core.instrument.binder 包下,可以按实际情况添加。

姿势一:

public class Application {
// 作为全局变量,可以在自定义监控中使用
public static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
static {
// 添加 Prometheus 全局 Label,建议加一上对应的应用名
registry.config().commonTags("application", "java-demo");
}

public static void main(String[] args) throws Exception {
// 添加 JVM 监控
new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
new FileDescriptorMetrics().bindTo(registry);
System.gc(); // Test GC
try {
// 暴露 Prometheus HTTP 服务,如果已经有,可以使用已有的 HTTP Server
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/metrics", httpExchange -> {
String response = registry.scrape();
httpExchange.sendResponseHeaders(200, response.getBytes().length);
try (OutputStream os = httpExchange.getResponseBody()) {
os.write(response.getBytes());
}
});

new Thread(server::start).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

由于 JVM GC Pause 监控是通过 GarbageCollector Notification 机制实现,因此只有发生 GC 之后才有监控数据。上述示例为了测试更直观,主动调用了 System.gc()

通过 http://localhost:8080/metrics 访问到 Prometheus 协议的指标数据。

姿势二

@Bean
MeterRegistryCustomizer<MeterRegistry> configurer(
@Value("${spring.application.name}") String applicationName) {
return (registry) -> registry.config().commonTags("application", applicationName);
}

在项目启动类加上这个bean配置之后,别忘记在配置文件加上management.endpoints.web.exposure.include=*这些配置来开启 Actuator 服务,因为Spring Boot Actuator 会自动配置一个 URL 为 /actuator/Prometheus 的 HTTP 服务来供 Prometheus 抓取数据,不过默认该服务是关闭的,该配置将打开所有的 Actuator 服务

完成之后,我们再启动服务,然后在浏览器访问http://192.168.0.4:6601/actuator/prometheus,就可以看到服务的一系列不同类型 metrics 信息

例如 http_server_requests_seconds_count、jvm_threads_states_threads、jvm_classes_loaded_classes、jvm_memory_max_bytes、jvm_gc_pause_seconds summary、jvm_gc_memory_promoted_bytes_total counter 等等。

Prometheus 配置

K8s 容器版

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: java-demo
namespace: cm-prometheus
spec:
namespaceSelector:
matchNames:
- java-demo
podMetricsEndpoints:
- interval: 30s
path: /metrics
port: metric-port
selector:
matchLabels:
k8s-app: java-demo

二进制版

- job_name: nacos-prometheus
scrape_interval: 2s
metrics_path: '/actuator/prometheus'
static_configs:
consul_sd_configs:
- server: '192.168.0.4:18016'
services: []

# 注意:-targets: 后面是引入了nacos-consul-adapter.jar包的实例IP+端口,记得换成自己的实例IP和端口。如图:
# 这个配置表示:prometheus每隔2秒钟从http://192.168.0.4:18016/actuator/prometheus这个url拉取指标数据

在 Prometheus 验证监控服务

# 重启 prometheus 容器
访问http://192.168.0.4:9090/targets

# 通过在 graph 关键字搜索一些统计数据
jvm_threads_states_threads

配置 Grafana 并展示监控界面

此处使用模版ID:10280

还有很多不错的模版,比如说:4701,14370,8878...

可以参考:https://grafana.com/grafana/dashboards/

应用概况

![1.png](/img/Prometheus 监控 Nacos 注册的服务/1.png)

JVM 相关的监控数据

![2.png](/img/Prometheus 监控 Nacos 注册的服务/2.png)

GC 情况

![3.png](/img/Prometheus 监控 Nacos 注册的服务/3.png)

日志统计分析数据

![4.png](/img/Prometheus 监控 Nacos 注册的服务/4.png)

自定义监控指标并展示在 Grafana

虽然 Spring-boot-actuator 集成了 Micrometer 来提供的默认监控项,覆盖 JVM 各个层间的监控,配合 Grafana Dashboard 模板基本可以满足我们日常对 Java 应用的监控,当然,它也支持自定义监控指标,实现各个方面的监控,例如统计访问某一个 API 接口的请求数,统计实时在线人数、统计实时接口响应时间等功能,而这些都可以通过使用 Micrometer 来实现

不对针对于接口的请求数和一些接口延迟,也可以使用 APM 来针对去做监控

举个例子:

监控所有API请求次数

监控请求次数可以使用 Counter 计数器来处理,为了测试,我们就直接在 Controller 类中进行累计,项目中,可以使用一个 AOP 切面,通过切面注入可以做到统计所有请求记录,代码:

@RestController
@RequestMapping("/gateway/metrics")
public class GrafanaTestController {
@Autowired
private MeterRegistry meterRegistry;
private Counter counter;

@PostConstruct
public void init() {
Tags tags = Tags.of("common", "test");
// 公共标签
meterRegistry.config().commonTags(tags);
counter = Counter.builder("metrics.request.common").register(meterRegistry);
}

/**
* 订单请求测试
*/
@GetMapping("/order/{appId}")
public RestResponse<String> orderTest(@PathVariable("appId") String appId) {
counter.increment();
return RestResponse.ok(appId);
}

/**
* 商品请求测试
*/
@GetMapping("/product/{appId}")
public RestResponse<String> productTest(@PathVariable("appId") String appId) {
counter.increment();
return RestResponse.ok(appId);
}
}

然后分别访问接口 /order/{appId} 6次,访问接口 /product/{appId} 6次,然后我们在 Grafana Dashboard 上添加一个新的 Panel 并添加 Query 查询,最后图形化展示出来

避免重复代码的更改操作

1.增加 MeterRegistryCustomizer 配置类,而不是在每个实例的启动类挨个配置;

@Configuration
public class MeterRegistryConfig {

@Value("${spring.application.name}")
private String applicationName;

@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("application", applicationName);
}
}

2.使用 nacos 的 shared-configs 启用配置共享,新建配置文件:springcloud-actuator-common.yml,开启全部 actuator 端点:

management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always

基于避免重复造轮子的原则,也可以不需要增加 MeterRegistryCustomizer 配置类了,1.1.0+ 以后也支持在配置文件配置了,所以我们只需要增加一个 nacos 的 shared-configs 共享文件即可。如下:

management:
endpoints:
web:
exposure:
include: 'prometheus' # 暴露/actuator/prometheus
endpoint:
health:
show-details: always
metrics:
tags:
application: ${spring.application.name} # 暴露的数据中添加application label

Istio 基础环境搭建

· 阅读需 4 分钟

K8s 部署环境操作省略

K8s version: 1.21.1

环境的准备:

https://metallb.universe.tf/ #官网

https://metallb.universe.tf/installation/#installation-by-manifest #install

kubectl create ns metallb-system

wget https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml

创建 metallb

# 查看镜像
grep image metallb.yaml

# 拉取镜像
docker pull quay.io/metallb/speaker:v0.10.2
docker pull quay.io/metallb/controller:v0.10.2

# 修改metallb.yaml 镜像拉取策略
image: quay.io/metallb/speaker:v0.10.2
imagePullPolicy: IfNotPresent #新增

image: quay.io/metallb/controller:v0.10.2
imagePullPolicy: IfNotPresent #新增

# 安装
kubectl apply -f metallb.yaml

# 查看是否创建
kubectl get pods -n metallb-system

创建configmap

https://metallb.universe.tf/configuration/#layer-2-configuration

# 创建地址池
vim pool.yaml

apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.15.230-192.168.15.240

# 创建
kubectl apply -f pool.yaml

# 所有的 work 节点都需拉取镜像
docker pull yauritux/busybox-curl
docker pull nginx

istio 安装

# 下载二进制包
wget https://github.com/istio/istio/releases/download/1.10.3/istio-1.10.3-linux-amd64.tar.gz
#wget https://github.com/istio/istio/releases/download/1.10.3/istioctl-1.10.3-linux-amd64.tar.gz

tar xvf istio-1.10.3-linux-amd64.tar.gz

ls istio-1.10.3/bin/
cp istio-1.10.3/bin/istioctl /bin/

#查看 istio 安装的组件
istioctl profile list

# 查看 istio 组件的内容
istioctl profile dump demo #查看demo组件信息,一般demo用于学习环境

# 安装 demo 环境
istioctl install --set profile=demo
y

# 查看
kubectl get pods -n istio-system #查看 有对应的ns和pod被创建出来
kubectl get svc -n istio-system #创建出了LoadBalancer类型的 istio-ingressgateway,ip地址为192.168.15.230
# 如果没有配置metallb的话,可以把 istio-ingressgateway 的类型改为 NodePort

# 如果需要卸载
istioctl x uninstall --purge
y
# 删除命名空间
kubectl delete ns istio-system

istio 注入

好用的命名空间切换工具 kubens 下载

curl -L https://github.com/ahmetb/kubectx/releases/download/v0.9.1/kubens -o /bin/kubens

chmod +x /bin/kubens

# 使用kubens命令即可列出当前所有的namespace

测试一下看之前创建的pod流程

mkdir chap1
cd chap1
kubectl create ns ns1

# 切换namespace
kubens ns1
#Context "kubernetes-admin@kubernetes" modifued.
#Active namespace is "ns1".

# 创建一个pod的yaml
kubectl run pod1 --image=nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > pod1.yaml

# 查询参数值
kubectl explain pods.spec

vim /del.txt
terminationGracePeriodSeconds: 0

# 编辑pod1
vim pod1.yaml

spec:
terminationGracePeriodSeconds: 0
containers:

# 创建pod
kubectl apply -f pod1.yaml
kubectl get pods #此时这个pod里只有一个容器,即没有sidecar

# 删除测试的pod
kubectl delete pod pod1

使用 istio 单独的注入一个pod

# 创建
istioctl kube-inject -f pod1.yaml | kubectl apply -f -

# 查看pod数量
kubectl get pods #此时READY数为2

#这个pod新增的docker,就是 pilot,envoy

## 在创建一个 pod 测试
sed 's/pod1/pod2/' pod1.yaml | kubectl apply -f -
kubectl get pods # 发现这个 pod2 是没有被注入的

设置自动 istio 注入,可以通过 namespace 去做

#给ns1的命名空间添加istio标签,那么在这个标签里,所有创建的 pod 都会被自动注入 istio
kubectl label ns ns1 istio-injection=enabled

# 查看labels
kubectl get ns --show-labels

安装kiali :图形化工具,可以直观的查看流量

kubectl get pods -n istio-system

# 安装
kubectl apply -f istio-1.10.3/samples/addons/kiali.yaml
# 或者可以选择直接全部都安装上
kubectl apply -f istio-1.10.3/samples/addons/

kubectl get svc -n istio-system
# kiali 的PORT:20001,TYPE为 ClusterIP

# 修改 kiali 的 svc
kubectl edit svc kiali -n istio-system

sessionAffinity: None
type: LoadBalancer #把ClusterIP修改为NodePort或者LoadBalancer

# 查看并验证
kubectl get svc -n istio-system
# 查看 kiali 的 ip 和端口,使用浏览器访问
# 进入浏览器后可以测试一下,进入 Graph 页面,选择 istio-system 和 ns1 的 Namespace,可以看到我们现在环境中的拓扑图

Ansible 批量安装 node_exporter

· 阅读需 3 分钟

Ansible 批量安装 node_exporter

安装配置 Ansible

配置 hosts ,并测试连通性

# 节点主机名写入hosts
echo "192.168.0.112 prome-master01" >> /etc/hosts
echo "192.168.0.127 prome-node01" >> /etc/hosts

# master上生成ssh key 并拷贝到node上
ssh-keygen
ssh-copy-id prome_node_01

# 测试ssh联通
ssh prome_node_01

Master 上安装 ansible

yum install -y ansible

# 关闭hostcheck
vim /etc/ansible/ansible.cfg

ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no

编写 playbook

playbook执行时需要设置机器文件

cat <<EOF > /opt/tgzs/host_file
prome-master01
prome-node01
EOF

设置syslog 和logrotate服务

# 编写 yaml
vim init_syslog_logrotate.yaml

- name: init syslog logrotate
hosts: all
user: root
gather_facts: false
vars:
app_log_path: /opt/logs/
sc_path: /opt/tgzs/
syslog_conf: syslog_server.conf
logrotate_conf: logrotate.conf

tasks:

- name: mkdir
file: path={{ app_log_path }} state=directory


- name: copy files
copy:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
owner: root
group: root
mode: 0644
force: true

with_items:
- { src: '{{ sc_path }}/{{ syslog_conf }}', dest: '/etc/rsyslog.d/{{ syslog_conf }}' }
- { src: '{{ sc_path }}/{{ logrotate_conf }}', dest: '/etc/logrotate.d/{{ logrotate_conf }}' }
register: result

- name: Show debug info
debug: var=result verbosity=0

- name: restart service

systemd:
name: "{{ item }}"
state: restarted
daemon_reload: yes
with_items:
- 'rsyslog'
register: result

- name: Show debug info
debug: var=result verbosity=0

# 设置服务
ansible-playbook -i host_file init_syslog_logrotate.yaml

编写ansible 发布服务脚本

# 编写 deploy 脚本
vim service_deploy.yaml

- name: install
hosts: all
user: root
gather_facts: false
vars:
local_path: /opt/tgzs
app_dir: /opt/app

tasks:
- name: mkdir
file: path={{ app_dir }}/{{ app }} state=directory
- name: mkdir
file: path={{ local_path }} state=directory

- name: copy config and service
copy:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
owner: root
group: root
mode: 0644
force: true

with_items:
- { src: '{{ local_path }}/{{ tgz }}', dest: '{{ local_path }}/{{ tgz }}' }
- { src: '{{ local_path }}/{{ app }}.service', dest: '/etc/systemd/system/{{ app }}.service' }

register: result
- name: Show debug info
debug: var=result verbosity=0

- name: tar gz
shell: rm -rf /root/{{ app }}* ; \
tar xf {{ local_path }}/{{ tgz }} -C /root/ ; \
/bin/cp -far /root/{{ app }}*/* {{ app_dir }}/{{ app }}/ \

register: result
- name: Show debug info
debug: var=result verbosity=0

- name: restart service
systemd:
name: "{{ item }}"
state: restarted
daemon_reload: yes
enabled: yes
with_items:
- '{{ app }}'
register: result

- name: Show debug info
debug: var=result verbosity=0

# 部署服务
ansible-playbook -i host_file service_deploy.yaml -e "tgz=node_exporter-1.1.2.linux-amd64.tar.gz" -e "app=node_exporter"

查看结果并验证

检查 node_exporter 服务状态

ansible -i host_file all -m shell -a " ps -ef |grep node_exporter|grep -v grep "

浏览器访问 9100/metrics

node-IP:9100/metrics
master-IP:9100/metrics