摘要
ELK在微服务的使用中成了刚需,安装和使用ELK也成了一个程序员必备的技能之一。本文从0开始介绍ELK的安装、部署及使用。里面涉及到笔者踩过的一些坑,希望各位及早规避。
ELK的安装分4个部分,分别是jdk安装、elasticsearch,logstash,kibana。
为了避开一些环境冲突,所以选择了rpm方式安装。
一,环境介绍
1,操作系统
我选用CentOS-7-x86_64-Minimal-2207-02。缺点是预装的少,各工具基本从0安装;优点是避免不必要的一些环境冲突(比如jdk)。
虚拟机安装centos的过程略,建议内存不低于1G,其他没啥建议的。root账号登录就行。
2,JDK
ELK的环境需要JDK支持,我们选择JDK1.8版本。1.8还是大多数java-er钟爱的,不建议太高版本。这里的版本对应查下官方elk对应的jdk版本。简单点说,ELK7及以下的,都建议用jdk1.8版本。ELK8开始就需要升级了。
3,ELK版本
刚才说了,我们打算用ELK7及以下的,目前ELK7的最高版本是7.17,然后就是8.x了。本文我们选择66大顺版本,打算elasticsearch,logstash,kibana这3个版本号都选用6.6.0(建议3个版本保持一致,尤其是大版本,就是不建议es是6,logstash是5)
4,OS安装后的前序准备
在开始安装ELK之前,先检查虚拟机的网络
ip addr
再检查时间,
timedatectl
并设置时间同步,必要时修改时区为上海。
yum install ntp
systemctl enable ntpd
systemctl start ntpd
也可以选择性的安装一些工具,比如vim
yum install vim
不多说了,反正缺啥补啥,yum直接装。
二,JDK安装
先看看本机安装jdk没有
java -version
有的话先卸载,
rpm -qa|grep jdk
rpm -e --nodeps jdk1.8.xxxxxx.x86_64
没有的话我们就开始装了。建议用rpm的方式安装,会帮我们省下好多可能发生的环境问题。我们安装到/usr/java路径下,注意这个路径,将来要配置它的,所以要记得它。
sudo rpm -ivh --prefix=/usr/java jdk-8u381-linux-x64.rpm
安装好后,还是要添加一些环境变量的
vi /etc/profile
在末尾换行添加JAVA的环境变量,路径要是真实的jdk安装路径,先去核实一下在不在,别乌龙。
JAVA_HOME=/usr/java/jdk-1.8-oracle-x64
CLASSPATH=.:%JAVA_HOME%/lib:%JAVA_HOME%/jre/lib
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
export PATH CLASSPATH JAVA_HOME
然后重载profile,让新的java变量生效。
source /etc/profile
然后再java -vsersion就能看到jdk生效了
[root@localhost ~]# java -version
java version "1.8.0_371"
Java(TM) SE Runtime Environment (build 1.8.0_371-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.371-b11, mixed mode)
三,Elasticsearch安装和部署
wget装起来
yum install wget
下载,安装elasticsearch
wget https://mirrors.huaweicloud.com/elasticsearch/elasticsearch-6.6.0.rpm
rpm -ivh elasticsearch-6.6.0.rpm
安装好其实就可以启动了,但是我们还要关注下es的配置文件
vim /etc/elasticsearch/elasticsearch.yml
path.data: /data/lib/elasticsearch
path.logs: /data/log/elasticsearch
里面大部分我觉得是不需要改的,比如数据文件路径,日志文件路径,端口9200等,若不需要的话可以不改。主要是一个访问对象的ip地址是不是需要拦截需要关注下,也就是logstash等。我的logstash是和es安装在同一台机器上,不存在拦截,所以也不需要改,全程默认参数即可。
#network.host: 192.168.0.1
#http.port: 9200
#不需要改的话,这个#号都不需要删除,默认的就是192.168.0.1:9200
启动es
# 启动
systemctl start elasticsearch
或者后台启动(不能使用root用户,需要建立elk用户启动,并赋予组权限)
useradd elk
groups elk
usermod -aG elk elk
chown -R elk:elk /etc/elasticsearch
chown -R elk:elk /etc/logstash
./elasticsearch >/dev/null 2>&1 &
# 查看状态
systemctl status elasticsearch
# 设置开机启动
systemctl enable elasticsearch
验证es安装成功
curl http://127.0.0.1:9200
显示如下即成功啦
curl http://127.0.0.1:9200
反显
{
"name" : "sJGRgq0",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "hPfC9i-zTOuIaJMXlnHKgQ",
"version" : {
"number" : "6.6.0",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "a9861f4",
"build_date" : "2019-01-24T11:27:09.439740Z",
"build_snapshot" : false,
"lucene_version" : "7.6.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
如果安装过程发生一些意外,需要卸载重装的话,那么卸载方式是:
# 检查elasticsearch服务状态
systemctl status elasticsearch
# 停止elasticsearch服务状态
systemctl stop elasticsearch
# 剔除elasticsearch服务
systemctl disable elasticsearch
# 重载系统服务
systemctl daemon-reload
# 查询rpm安装的elasticsearch,若有会显示文件名,该文件名下一步会用
rpm -qa | grep elasticsearch
# rpm卸载elasticsearch
rpm -e --nodeps elasticsearch-7.17.7-1.x86_64
# 删除残留文件
rm -rf /etc/elasticsearch
# 删除elasticsearch自定义文件
rm -rf /opt/software/elasticsearch
如果换了ip不在本机甚至不在本局域网环境的,需要修改es的yml文件中network.host参数,比如面向所有ip开放就是network.host:0.0.0.0,并开放防火墙的9200端口。如果ELK装在一台机器上,可以不对外开放。
#防火墙打开和重载
firewall-cmd --add-port=9200/tcp --permanent
firewall-cmd --reload
# 安装net-tools
yum install -y net-tools
# 检查9200是否有监听
netstat -antp |grep 9200
[root@localhost ~]# netstat -antp |grep 9200
tcp6 0 0 127.0.0.1:9200 :::* LISTEN 35601/java
tcp6 0 0 ::1:9200 :::* LISTEN 35601/java
四,Logstash安装和部署
和ES的安装类似,下载,安装Logstash:
wget https://mirrors.huaweicloud.com/logstash/6.6.0/logstash-6.6.0.rpm
rpm -ivh logstash-6.6.0.rpm
注意了,如果你安装的是7以上版本的logstash,安装好后,关注下提示语的第一行,这里的using JAVA_HOME defined java的路径是不是你之前jdk的路径,如果不是,还需要强制去修改logstash的JDK路径的。需要在配置时,添加一个LS_JAVA_HOME的参数,指定到JAVA_HOME即可。不过logstash8开始明确不支持jdk1.8了,需要升级jdk或降低logstash的版本。下图所示的就是说7.17.7的logstash他不愿意使用jdk1.8,而采用了自带的openjdk。
[root@localhost ~]# rpm -ivh logstash-7.17.7-x86_64.rpm
警告:logstash-7.17.7-x86_64.rpm: 头V4 RSA/SHA512 Signature, 密钥 ID d88e42b4: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:logstash-1:7.17.7-1 ################################# [100%]
Using JAVA_HOME defined java: /usr/local/java/jdk1.8.0_371
WARNING: Using JAVA_HOME while Logstash distribution comes with a bundled JDK.
DEPRECATION: The use of JAVA_HOME is now deprecated and will be removed starting from 8.0. Please configure LS_JAVA_HOME instead.
Using provided startup.options file: /etc/logstash/startup.options
/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/pleaserun-0.0.32/lib/pleaserun/platform/base.rb:112: warning: constant ::Fixnum is deprecated
Successfully created system startup script for Logstash
如果出现
WARNING: Could not find logstash.yml which is typically located in $LS_HOME/config or /etc/logstash. You can specify the path using --path.settings. Continuing using the defaults
Could not find log4j2 configuration at path /usr/share/logstash/config/log4j2.properties. Using default config which logs errors to the console
ERROR: Failed to read pipelines yaml file. Location: /usr/share/logstash/config/pipelines.yml
usage:
bin/logstash -f CONFIG_PATH [-t] [-r] [] [-w COUNT] [-l LOG]
bin/logstash --modules MODULE_NAME [-M "MODULE_NAME.var.PLUGIN_TYPE.PLUGIN_NAME.VARIABLE_NAME=VALUE"] [-t] [-w COUNT] [-l LOG]
bin/logstash -e CONFIG_STR [-t] [--log.level fatal|error|warn|info|debug|trace] [-w COUNT] [-l LOG]
bin/logstash -i SHELL [--log.level fatal|error|warn|info|debug|trace]
bin/logstash -V [--log.level fatal|error|warn|info|debug|trace]
bin/logstash --help
[ERROR] 2023-12-19 17:45:56.944 [LogStash::Runner] Logstash - java.lang.IllegalStateException: Logstash stopped processing because of an error: (SystemExit) exit
则需要建立软连接
ln -s /etc/logstash /usr/share/logstash/config
那我们回到主题,来启动logstash
# 启动
systemctl start logstash
或者后台启动
./logstash >/dev/null 2>&1 &
# 查看状态
systemctl status logstash
# 设置开机启动
systemctl enable logstash
启动后,通过查看状态,我们发现logstash启动是能启动,但是pid一直在更换。为什么?因为他一直在重启。为什么重启可以看下错误日志,一般是jdk环境和配置文件正确。一般logstash在7以下只要配置了JAVA_HOME等参数,就不怎么会出现jdk的环境问题。
检查配置文件
logstash.yml文件基本不用动,数据和日志的存放路径就默认的好了。高版本的可能要改下yml文件里的
vim /etc/logstash/logstash.yml
path.data: /data/lib/logstash
path.logs: /data/log/logstash
pipeline.ecs_compatibility: <desired_mode>
#明确设置 pipeline.ecs_compatibility 的值。你可以选择以下ECS兼容模式:
#- disabled :这个模式禁用ECS兼容性,使用Logstash的默认行为。
#- v1 :这个模式启用ECS兼容性,并使用ECS规范的版本1。
低版本的可以修改下logstash.yml文件中的path.config,本文的6.6及以上7,8的版本,该参数在pipelines.yml文件下。
path.config: "/etc/logstash/conf.d/logstash.conf"
可以关注在/etc/logstash/路径下的这些文件。
[root@localhost ~]# ls /etc/logstash
conf.d log4j2.properties logstash.yml startup.options
jvm.options logstash-sample.conf pipelines.yml
还有一些更敏感的配置文件在logstash的安装目录下,也就是 /usr/share/logstash/bin/
[root@localhost ~]# ll /usr/share/logstash/
[root@localhost ~]# ll /usr/share/logstash/bin
在配置文件中,最重要的是conf文件,在有些版本中,把/etc/logstash/目录下的logstash-sample.conf,拷贝进conf.d文件夹内即可。也可以在conf文件夹下单独新建个:
vim /etc/logstash/conf.d/logstash.conf
input {
tcp {
mode => "server"
host => "0.0.0.0"
port => 9600
codec => json_lines
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "%{projectName}-%{+YYYY.MM.dd}"
#user => "elastic"
#password => "changeme"
}
}
projectName取springboot系统中的参数
然后我们开放下9600的端口,
firewall-cmd --add-port=9600/tcp --permanent
firewall-cmd --reload
关于conf文件有2个坑,1个坑是他里面配置的host和port不能与yml文件里的host和port同时配置,否则会造成冲突,表象是logstash反复重启。我是yml文件里不配置,只在conf文件的input里来配置。
另外一个坑是这个conf的内容写入,不能vim创建文件后,借助ssh的鼠标拷贝进去。这会有可能丢失“input”里面的“i”,而变成“nput”,实在要偷懒拷贝的话,可以在ssh里面的资源管理器里,用记事本打开编辑,拷贝,保存。注意格式得是utf-8的。
好了,既然conf文件也配置好了,我们重启logstash,让新conf生效。
systemctl restart logstash
我们发现logstash的pid稳定了,不重启了。
再输入以下测试命令,看看logstash是否正常工作了没?
/usr/share/logstash/bin/logstash -e 'input { stdin { } } output { elasticsearch { hosts => ["127.0.0.1:9200"] } stdout { codec => rubydebug }}'
输入命令后,紧跟着随便输入一段日志内容:test log
如果输出以下json代表正常
test log
{
"@version" => "1",
"message" => "test log",
"@timestamp" => 2023-08-22T06:24:00.602Z,
"host" => "localhost.localdomain"
}
当然,在输入这段json之前,可能还会报一些WARN和INFO,关于警告部分,可以忽略,因为我们这条测试命令没有指定具体的配置文件。不要紧,只要不是ERRO都不影响的。
五,Kibana的安装和部署
下载,安装Kibana
wget https://mirrors.huaweicloud.com/kibana/kibana-6.6.0-x86_64.rpm
rpm -ivh kibana-6.6.0-x86_64.rpm
修改配置文件
vim /etc/kibana/kibana.yml
前面的ES和LS的端口和IP可以不设置是因为基本上是本机访问,而kibana需要对C端开放访问了,所以安全策略上还是要提高下。
# Kibana is served by a back end server. This setting specifies the port to use.
server.port: 5601
#这里的端口要在防火墙上策略关照下,不建议公网所有人访问该端口
# Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values.
# The default is 'localhost', which usually means remote machines will not be able to connect.
# To allow connections from remote users, set this parameter to a non-loopback address.
server.host: "0.0.0.0"
#这里的允许访问kibana的对象host,建议指定具体的IP或IP段访问
然后我们启动
# 启动
systemctl start kibana
或者后台启动
nohup ./kibana >/dev/null 2>&1 &
# 查看状态
systemctl status kibana
# 设置开机启动
systemctl enable kibana
打开防火墙
[root@localhost ~]# firewall-cmd --add-port=5601/tcp --permanent
success
[root@localhost ~]# firewall-cmd --reload
success
此时,我们访问kibana的地址http://192.168.226.150:5601/,终于看到效果了。
停止只能使用
netstat -tunlp|grep 5601
kill -9 ‘pid’
先在Management里面创建好索引,就可以看回到Discover看到之前测试进去的日志啦。前面忙活了这么久,到这里是不是有些欣慰呢?
不急,因为kibana是2C的,除了在防火墙和路由层面进行ip和端口的限制之外,还建议给kibana设置一道登录密码。但是这里不做介绍了,有兴趣的可以去研究下。其实ES提供了强大的账号安全机制。
六,Spring boot应用ELK的demo
首先我们创建一个spring boot的web应用程序,然后执行以下步骤
1,脚手架部分的pom如下:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.1</version>
</dependency>
这里注意版本号,有时候连接成功但是无日志有可能是这里的版本不对原因,可以换个5.3或其他版本试试。
2,新建src/main/resources/logback-spring.xml文件,
内容简单点可以如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 取项目名称 -->
<springProperty scop="context" name="logstash.projectName" source="logstash.projectName" defaultValue="test"/>
<!-- 取配置文件中的logstash的serverurl -->
<springProperty name="logstash.serverurl" source="logstash.serverurl"/>
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<springProperty scope="context" name="LOG_HOME" source="logging.file"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] --- %logger{50}.%M\(%L\) - %X{tid} - %X{uid} - %msg%n</pattern>
<!-- 此处设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/linkcle_portal.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/log_portal.%d{yyyyMMdd}.%i.log.zip</fileNamePattern>
<!--只保留最近7天的日志-->
<maxHistory>7</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 最大100MB 超过最大值,会重新建一个文件-->
<maxFileSize>40 MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] --- %logger{50}.%M\(%L\) - %X{tid} - %X{uid} - %msg%n</pattern>
<!-- 此处设置字符集 -->
<charset>UTF-8</charset>
</encoder>
<!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
<prudent>false</prudent>
</appender>
<!--输出到logstash的appender-->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--可以访问的logstash日志收集端口-->
<destination>${logstash.serverurl}</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<!--配置索引字段-->
<customFields>{"projectName":"${logstash.projectName}"}</customFields>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<logger name="root" level="INFO">
<appender-ref ref="FILE" />
<appender-ref ref="LOGSTASH" />
</logger>
</configuration>
3,application.properties文件中增加:
# Logstash appender 配置
logging.config:classpath:logback-spring.xml
logstash:
serverurl: 127.0.0.1:9600
projectName: test
4,Controller文件里写日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Controller
public class BasicController {
private static final Logger logger = LoggerFactory.getLogger(BasicController.class);
// http://127.0.0.1:8080/hello?name=elk
@RequestMapping("/hello")
@ResponseBody
public String hello(@RequestParam(name = "name", defaultValue = "unknown user") String name) {
logger.info("这是一条关于hello elk的日志消息");
return "Hello " + name;
}
运行后,可以看到日志写入的效果啦