まず、目的
2023 年の現在、ほとんどの javaweb アーキテクチャはスプリングブート マイクロサービスであり、フロントエンド関数リクエストはバックグラウンドで複数の異なるサービスによって完了される可能性があります。たとえば、ユーザーが注文関数を配置すると、js がバックグラウンドに転送されます。ゲートウェイゲートウェイサービス、その後に認証 Spring-Security サービス、その後にビジネスオーダーサービス、その後に決済サービス、フォローアップでは、配送、顧客へのラベル付け、その他のサービスが行われます。
各サービスは負荷分散のために複数のインスタンスを起動します。このようにして、この機能の完了プロセス ログを確認したい場合は、対応するサーバー IP とログ ファイルの場所を見つけて、特定の負荷がどのサーバーにあるのかを判断する必要があります。に転送されます。それが生産上の問題であり、原因をすぐに特定したい場合は、解決策が必要です。
2. 関連するテクノロジースタック
- 基本アーキテクチャ:
spring cloud
(springBoot+サービスディスカバリ+ゲートウェイ+ロードヒューズおよびその他のnetflex)。現在、ミドルウェアredis、quartz、kafka、mysql、elasticsearchを含むビジネス機能でspringboot+eureka+gateway+springSercurity+openfeign+springConfigを使用しています。 - ログの収集と処理の表示: ELK
- elasticsearch: 大規模な json データ ストレージのアドホック クエリ
- logstash: ソース データの収集 (tcp、ファイル、redis、mq)、フォーマット処理、プッシュ エス ストレージ
- kibana: 公式 ES ビジュアル インタラクティブ カード ツール
- 効率的で軽量なデータ収集ツール: filebeat。監視ログファイルはリアルタイムで取得され、kafkaにプッシュ可能
- Kafka: logstash で使用する filebeat データを受信する
- マルチサービス リンクの追跡: sleuth-zipkin。コードの侵入はありません。簡単に言うと、印刷されるログの内容に tranceId と scanId が追加されます。例えば
3. プロセス
-
js はバックグラウンド ゲートウェイ サービスへの Ajax リクエストを開始します
-
ゲートウェイ サービスは Maven の
<artifactId>spring-cloud-starter-zipkin</artifactId>
依存関係を統合し、現在のリクエスト ヘッダーに tranceId フィールドとspanId フィールドを自動的に追加します。これら 2 つのフィールド値はランダムに生成されます。ここで、tranceId は、ヘッダーにこれら 2 つのフィールドがない場合、spanId と等しくなります。たとえば、tranceId=123a、spanId=123a とヘッダーに追加されます。ログを印刷すると、この情報が印刷されます。 -
その後、ゲートウェイはリクエスト パスに従ってそれをビジネス サービス A に転送します。サービス A の zipkin は、ヘッダーに tranceId 情報があることを検出し、spanId (たとえば、tranceId=123a、spanId=231b) のみを生成し、それをビジネス サービス A に追加します。ヘッダー。そして、ログを印刷するときに、この情報も印刷されます。
-
A サービスと RPC は B サービスを呼び出します。サービス B の zipkin は、ヘッダーに tranceId 情報があることを検出し、tranceId=123a、spanId=342h などの spanId のみを生成し、ヘッダーに追加します。そして、ログを印刷するときに、この情報も印刷されます。
-
呼び出しが完了すると、フロントエンド応答が返されます。
-
上記のログは本サーバーのログファイルに追加されます。次に、
filebeat
ツールは各サービスの新しいログをリッスンし、それらを読み取ってプッシュします。kafka
-
新しいデータはメッセージ キューのトピックの下に生成され、
logstash
ツールは事前に構成され、kafka の使用を開始し、データを処理して保存しますelasticsearch
。filebeat
ここで、なぜes を直接プッシュしないのか、または springboot のログ フレームワークがアペンダーを介して es を直接プッシュしないのかが気になります。- filebeat との分離は Springboot のパフォーマンスには影響しません。そして軽量
- Kafka を使用すると、大量の同時データを処理し、logstash への負荷を軽減できます。
- 最後に、logstash を介して es をプッシュする目的は、ソース データを処理してフォーマットし、それを es に保存することです。これは、es がログをクエリするのに便利です。
-
es を永続化した後、ログをクエリすることで、完全なログをクエリできることが
kibana
クエリ条件となります。tranceId=123a
4. 統合設定ファイルbeat、kafka、logstash の例
これを 2 つの部分に分割し、一部はサーバーにデプロイされた jar で、filebeat を通じて収集します。一部はローカル ノートブックにデプロイされたサービスで、アペンダーは filebeat なしで kafka に出力するように logback.xml で直接構成されています。
- Java の pom は依存関係を導入します
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
- logback-spring.xml ファイル構成の出力形式 (内容の一部)
<appender name="fileUserLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 配置我们自己写的包的日志信息,目的是为了方便查看自己的类日志,此日志文件只有我们自己的的log -->
<File>${logdir}/user.${appname}.${serverport}.${KPHOSTNAME}.log</File>
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>${logdir}/history/user.${appname}.${serverport}.%d{yyyy-MM-dd}.${KPHOSTNAME}.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<!--日志输出编码格式化-->
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
{
"dateTime": "%d{yyyy-MM-dd HH:mm:ss.SSS}",
"message": "%message",
"stackTrace": "%exception",
"level": "%level",
"traceId": "%X{X-B3-TraceId:-}",
"spanId": "%X{X-B3-SpanId:-}",
"service": "${appname}",
"thread": "%thread",
"class": "%logger.%method[%line]"
}
</pattern>
</pattern>
<timestamp>
<timeZone>GMT+8</timeZone>
</timestamp>
</providers>
</encoder>
</appender>
ログスタッシュ.conf
input {
kafka{
bootstrap_servers => "node101:30701"
client_id => "logstash_kafka_consumer_id"
group_id => "logstash_kafka_consumer_group"
auto_offset_reset => "latest"
consumer_threads => 1
decorate_events => true
topics => ["logstash"]
}
}
filter {
json {
source => "message"
}
}
output{
elasticsearch{
hosts => ["node101:30600"]
index => "logstash-%{
+YYYY.MM.dd}"
}
}
ログ ファイルの内容、このリクエストの完全なプロセスを確認したい
ファイルビート.yml
filebeat.modules:
filebeat.prospectors:
- type: log
enabled: true
paths:
- /spring-boot-logs/*/user.*.log
#include_lines: ["^ERR", "^WARN"]
# 适用于日志中每一条日志占据多行的情况,比如各种语言的报错信息调用栈
multiline:
pattern: '^[[:space:]]'
negate: false
match: after
processors:
- drop_fields:
fields: ["metadata", "prospector", "offset", "beat", "source","type"]
output.kafka:
hosts: ["node101:30701"]
topic: logstash
codec.format:
string: '%{[message]}' #输出的信息只包含message;例如java日志文件里面是什么样子输出到kafka也就是什么样子
k8s yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: elk
name: ssx-elk-dm
namespace: ssx
spec:
replicas: 1
selector: #标签选择器,与上面的标签共同作用
matchLabels: #选择包含标签app:mysql的资源
app: elk
template: #这是选择或创建的Pod的模板
metadata: #Pod的元数据
labels: #Pod的标签,上面的selector即选择包含标签app:mysql的Pod
app: elk
spec: #期望Pod实现的功能(即在pod中部署)
hostAliases: #给pod添加hosts网络
- ip: "192.168.0.101"
hostnames:
- "node101"
- ip: "192.168.0.102"
hostnames:
- "node102"
- ip: "192.168.0.103"
hostnames:
- "node103"
containers: #生成container,与docker中的container是同一种
- name: ssx-elasticsearch6-c
image: 9d77v/elasticsearch:6.2.4 #配置阿里的镜像,直接pull即可
ports:
- containerPort: 9200 # 开启本容器的80端口可访问
- containerPort: 9300 # 开启本容器的80端口可访问
env: #容器运行前需设置的环境变量列表
- name: discovery.type #环境变量名称
value: "single-node" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
volumeMounts:
- mountPath: /usr/share/elasticsearch/data #这是mysql容器内保存数据的默认路径
name: c-v-path-elasticsearch-data
- mountPath: /usr/share/elasticsearch/logs #这是mysql容器内保存数据的默认路径
name: c-v-path-elasticsearch-logs
- mountPath: /usr/share/elasticsearch/.cache #这是mysql容器内保存数据的默认路径
name: c-v-path-elasticsearch-cache
- mountPath: /etc/localtime #时间同步
name: c-v-path-lt
- name: ssx-kibana-c
image: wangxiaopeng65/kibana:6.2.4 #配置阿里的镜像,直接pull即可
ports:
- containerPort: 5601 # 开启本容器的80端口可访问
env: #容器运行前需设置的环境变量列表
- name: ELASTICSEARCH_URL #环境变量名称
value: "http://localhost:9200" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
volumeMounts:
- mountPath: /usr/share/kibana/data2 #无用,我先看看那些挂载需要
name: c-v-path-kibana
- mountPath: /etc/localtime #时间同步
name: c-v-path-lt
- name: ssx-logstash-c
image: docker.elastic.co/logstash/logstash:6.2.4 #配置阿里的镜像,直接pull即可
env: #容器运行前需设置的环境变量列表
- name: "xpack.monitoring.enabled" #禁用登录验证
value: "false" #环境变量的值 这是mysqlroot的密码 因为是纯数字,需要添加双引号 不然编译报错
args: ["-f","/myconf/logstash.conf"]
volumeMounts:
- mountPath: /myconf #配置
name: c-v-path-logstash-conf
- mountPath: /usr/share/logstash/data #data
name: c-v-path-logstash-data
- mountPath: /etc/localtime #时间同步
name: c-v-path-lt
- name: ssx-filebeat-c
image: elastic/filebeat:6.2.4 #配置阿里的镜像,直接pull即可
env: #容器运行前需设置的环境变量列表
volumeMounts:
- mountPath: /usr/share/filebeat/filebeat.yml #配置
name: c-v-path-filebeat-conf
- mountPath: /usr/share/filebeat/data #配置
name: c-v-path-filebeat-data
- mountPath: /spring-boot-logs #data
name: c-v-path-filebeat-spring-logs
- mountPath: /etc/localtime #时间同步
name: c-v-path-lt
volumes:
- name: c-v-path-elasticsearch-data #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch6/data #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
- name: c-v-path-elasticsearch-logs #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch6/logs #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
- name: c-v-path-elasticsearch-cache #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/elasticsearch6/.cache #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
- name: c-v-path-kibana #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/kibana #此路径需要实现创建 注意要给此路径授权777权限 不然pod访问不到
- name: c-v-path-logstash-conf
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/logstash/myconf
- name: c-v-path-logstash-data
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/logstash/data
- name: c-v-path-lt
hostPath:
path: /etc/localtime #时间同步
- name: c-v-path-filebeat-conf
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/filebeat/myconf/filebeat.yml
- name: c-v-path-filebeat-data
hostPath:
path: /home/app/apps/k8s/for_docker_volume/elk/filebeat/data
- name: c-v-path-filebeat-spring-logs
hostPath:
path: /home/ssx/appdata/ssx-log/docker-log
nodeSelector: #把此pod部署到指定的node标签上
kubernetes.io/hostname: node101
---
apiVersion: v1
kind: Service
metadata:
labels:
app: elk
name: ssx-elk-sv
namespace: ssx
spec:
ports:
- port: 9000 #我暂时不理解,这个设置 明明没用到?
name: ssx-elk-last9200
protocol: TCP
targetPort: 9200 # 容器nginx对外开放的端口 上面的dm已经指定了
nodePort: 30600 #外网访问的端口
- port: 9010 #我暂时不理解,这个设置 明明没用到?
name: ssx-elk-last9300
protocol: TCP
targetPort: 9300 # 容器nginx对外开放的端口 上面的dm已经指定了
nodePort: 30601 #外网访问的端口
- port: 9011 #我暂时不理解,这个设置 明明没用到?
name: ssx-kibana
protocol: TCP
targetPort: 5601 # 容器nginx对外开放的端口 上面的dm已经指定了
nodePort: 30602 #外网访问的端口
selector:
app: elk
type: NodePort