实战虚拟机安装服务

1.docker安装elasticsearch

docker run -d \
        --name elasticsearch \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    -e "discovery.type=single-node" \
    -e "xpack.security.enabled=false" \
    -v /work_docker/elk/elasticsearch/data:/usr/share/elasticsearch/data \
    -v /work_docker/elk/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
    --privileged \
    -p 9200:9200 \
    -p 9300:9300 \
elasticsearch:8.13.0

命令解释:

-e "cluster.name=es-docker-cluster":设置集群名称 -e "http.host=0.0.0.0":监听的地址,可以外网访问 -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":内存大小 -e "ES_JAVA_OPTS="-Xms256m -Xmx256m" :可以设置小内存(太小不行,无法启动,各种功能栈溢出内存不够),但是Xms要小于Xmx -e "discovery.type=single-node":非集群模式 -v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录 -v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录 -v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录 --privileged:授予逻辑卷访问权 --network es-net :加入一个名为es-net

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://192.168.206.128:9200 \
-p 5601:5601  \
kibana:8.13.0
1.在线安装ik插件
docker exec -it es ./bin/elasticsearch-plugin  install https://github.com/infinilabs/analysis-ik/releases/download/v8.12.2/elasticsearch-analysis-ik-8.12.2.zip

重启es容器

2.配置扩展词词典
1.打开IK分词器config目录

Docker安装ELK及实战_docker

2.在IKAnalyzer.cfg.xml配置文件内容添加
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">ext.dic</entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

2.docker安装kibana

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://192.168.206.128:9200 \
-p 5601:5601  \
kibana:8.13.0

3.docker 安装 LogStashd

拉取镜像并拷贝配置
docker run -d --name logstash logstash:7.14.1

# 拷贝数据
docker cp logstash:/usr/share/logstash/config ./config
docker cp logstash:/usr/share/logstash/data ./data
docker cp logstash:/usr/share/logstash/pipeline ./pipeline

#文件夹赋权
chmod -R 777 ./config ./data ./pipeline
修改相应配置文件

修改config 下的 logstash.yml 文件,主要修改 es 的地址

http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]

http.host:当设置为 “0.0.0.0” 时,意味着服务将在所有可用网络接口上监听HTTP请求,允许任何IP地址的客户端连接。 xpack.monitoring.elasticsearch.hosts:指向Elasticsearch服务的URL ->http://elasticsearch:9200,但这里没有使用具体的IP地址,而是用了一个名为 elasticsearch 的服务名或容器名。

修改config下的jvm.options

ElasticsearchLogstash或其他使用Java虚拟机(JVM)的应用程序中,jvm.options 文件是用来配置JVM运行时参数的重要文件。当你需要调整JVM相关的设置,比如堆内存大小、垃圾回收策略、线程数量等时,就需要修改这个文件。

# 增加JVM堆内存大小
-Xms512m
-Xmx512m
修改pipeline 下的 logstash.conf
input {
  tcp {
    mode => "server"
    host => "0.0.0.0"
    port => 5044
    codec => json_lines
  }
}

output {
  elasticsearch {
      hosts  => ["http://124.221.147.235:9200"]
      user => elastic
      password => 123456
      index  => "logs-%{+YYYY.MM}"
      codec  => "json"
  }
  stdout {
    codec => rubydebug
  }
}
input部分:

使用TCP输入插件(tcp)创建一个服务器监听器,等待来自客户端的连接。 mode => "server" 指定为服务器模式,接受来自其他服务或应用的日志数据。 host => "0.0.0.0" 表示在所有网络接口上监听连接请求。 port => 5044 设置了监听端口为5044。 codec => json_lines 指定了编解码器类型,这意味着每个TCP消息应该包含一个或多条JSON格式的数据,每行一条JSON记录。

output部分:

使用Elasticsearch输出插件(elasticsearch)将处理后的日志事件发送到Elasticsearch集群。 hosts => "127.0.0.1:9200" 设置了Elasticsearch集群的地址与端口,这里指本地主机上的默认Elasticsearch实例。 index => "%{[spring.application.name]}-%{+YYYY.MM.dd}" 定义了索引名称模板。该模板会根据事件中的字段动态生成索引名,其中: %{[spring.application.name]} 是从日志事件中提取的Spring Boot应用的名字作为索引前缀。 %{+YYYY.MM.dd} 是基于当前日期时间动态生成的索引后缀,每天都会创建一个新的索引以存储当天的日志数据。 这样配置后,Logstash将作为一个TCP日志收集服务器运行,并且能够接收JSON格式的日志数据,然后将其按照指定的规则写入到Elasticsearch集群中相应的索引里,便于后续进行搜索、分析和可视化展示。

启动容器并挂载
#注意先删除之前的容器
docker rm -f logstash

# 启动容器并挂载
docker run --name logstash \
-p 5044:5044 \
-p 9600:9600 \
--privileged=true \
-e ES_JAVA_OPTS="-Duser.timezone=Asia/Shanghai" \
-v /work_docker/elk/logstash/pipeline/:/usr/share/logstash/pipeline/ \
-v /work_docker/elk/logstash/config/:/usr/share/logstash/config/ \
-v /work_docker/elk/logstash/data/:/usr/share/logstash/data/ \
-d logstash:8.12.0

ELK概念介绍

索引&类型&文档&字段&映射

Docker安装ELK及实战_Elastic_02

1.ElasticSearch 接口

ElasticSearch 对外提供的 API 是以 HTTP 协议的方式,通过 JSON 格式以 REST 约定对外提供。

HTTP 配置文件放在 elasticsearch.yml 中。REST 通常是开发的一种约定,当所有开发者都遵循这种约定时,就会简化开发的沟通成本。

REST 约定用 HTTP 的请求头 POST、GET、PUT、DELETE 正好对应 CRUD(Create、Read、Update、Delete)四种数据操作。如果应用程序符合 REST 原则,可称为 RESTful Web Service。

Docker安装ELK及实战_docker_03

1 集群信息操作命令
1 查询集群状态

http://hlink1:9200/_cat/health?v

返回结果的主要字段意义:

cluster:集群名,是在 ES 的配置文件中配置的 cluster.name 的值。 status:集群状态。集群共有 green、yellow 或 red 中的三种状态。green 代表一切正常(集群功能齐全),yellow 意味着所有的数据都是可用的,但是某些复制没有被分配(集群功能齐全),red 则代表因为某些原因,某些数据不可用。如果是 red 状态,则要引起高度注意,数据很有可能已经丢失。 node.total:集群中的节点数。 node.data:集群中的数据节点数。 shards:集群中总的分片数量。 pri:主分片数量,英文全称为 private。 relo:复制分片总数。 unassign:未指定的分片数量,是应有分片数和现有的分片数的差值(包括主分片和复制分片)。

2 使用 help 参数查询

http://hlink1:9200/_cat/health?help

指定返回的参数

curl -XGET "hlink1:9200/_cat/health?h=cluster,pri,relo&v"

3 查询集群中的节点信息

http://hlink1:9200/_cat/nodes?v

4 查询集群索引信息

http://hlink1:9200/_cat/indices?v

2 index 操作命令
1 创建索引 PUT 请求

http://hlink1:9200/study

{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "study"
}
2 查看索引 GET 请求

http://hlink1:9200/study

{
    "study": {
        "aliases": {},
        "mappings": {},
        "settings": {
            "index": {
                "routing": {
                    "allocation": {
                        "include": {
                            "_tier_preference": "data_content"
                        }
                    }
                },
                "number_of_shards": "1",
                "provided_name": "study",
                "creation_date": "1711964771851",
                "number_of_replicas": "1",
                "uuid": "rZhAVVYjRHSgSSPGRfLPIA",
                "version": {
                    "created": "8503000"
                }
            }
        }
    }
}
3 删除索引 DELETE 请求

http://hlink1:9200/study

{
    "acknowledged": true
}
3 document 操作命令
1 创建文档 POST 请求

索引创建完成后,可以创建 document 文档,该文档对应数据库中 表的行数据 添加的数据格式为 JSON 内容

{ "name": "3分钟秒懂大数据", "introduction": "专注于大数据技术研究" }

http://hlink1:9200/study/_doc

{
    "_index": "study",
    "_id": "0GAamY4BAp9AOplvze8p",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 2,
    "_primary_term": 1
}
2 更新文档 POST 请求

上面的数据创建后,没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机生成一个。更新时可以指定唯一性标识

#请求体内容如下: { "name": "3分钟秒懂大数据", "introduction": "专注于Hadoop、Kafka、Flink等多个组件的技术研究" }

http://hlink1:9200/study/_doc/0GAamY4BAp9AOplvze8p

{
    "_index": "study",
    "_id": "0GAamY4BAp9AOplvze8p",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 3,
    "_primary_term": 1
}
3 查询文档 GET 请求

查看文档时,需要指明文档的唯一性标识

http://hlink1:9200/study/_doc/0GAamY4BAp9AOplvze8p

{
    "_index": "study",
    "_id": "0GAamY4BAp9AOplvze8p",
    "_version": 2,
    "_seq_no": 3,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "name": "分钟秒懂大数据",
        "introduction": "专注于大数据技术研究"
    }
}
4 删除文档 DELETE 请求

删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)

http://hlink1:9200/study/_doc/0GAamY4BAp9AOplvze8p


2.映射mapping

创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)

查看映射

语法: GET /{索引名称}/_mapping

动态映射

在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建 表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不 需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入 Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。

映射规则对应:

数据

对应的类型

null

字段不添加

true\

flase

字符串

text/keyword

数值

long

小数

float

日期

date

特殊类型:字符串

text:用于长文本,对本文内容进行分词(产生倒排索引文档列表),支持多关键字全文查询。例如:对电商项目商品名称,或者文章正文设置为text 缺点:不能进行聚合(分组),不支持排序。 keyword:用于词条精确查询,支持等值查询,不需要进行分词字符串(分词后无意义)。例如:用户昵称、用户手机号、身份证号、图片地址。场景:根据品牌名称等值查询。支持聚合(分组)、排序 不支持多关键词查询

静态映射

静态映射是在Elasticsearch中也可以事先定义好映射,即手动映射,包含文档的各字段类型、分词器等,这称为静态映射

#删除原创建的索引
DELETE /my_index

#创建索引,并同时指定映射关系和分词器等。
GET /study_index/_mapping

PUT /study_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "keyword",
        "index": true,
        "store": true
      },
      "category": { 
        "type": "text",
        "index": true,
        "store": true,
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "images": {
        "type": "text",
        "index": false,
        "store": true
      },
      "price": {
        "type": "integer",
        "index": true,
        "store": true
      }
    }
  }
}

结果:
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "study_index"
}

Springboot应用ELK

spring-boot-starter-data-elasticsearch

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

1.ElasticSearchRepository方式

1.了解注解

@Document: 代表一个文档记录

indexName: 用来指定索引名称

type: 用来指定索引类型

@Id: 用来将对象中id和ES中_id映射

@Field: 用来指定ES中的字段对应Mapping

type: 用来指定ES中存储类型

analyzer: 用来指定使用哪种分词器

2.新建实体类
@Data
@Document(indexName = "study_index")
public class Product {

    @Id
    private String id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String category;

    @Field(type = FieldType.Text)
    private String images;

    @Field(type = FieldType.Long)
    private Long price;

}
public interface ProductRepository extends ElasticsearchRepository<Product, String> {

    /**
     * 描述
     *
     * @param title String
     * @return List<Product>
     */
    List<Product> findByTitle(String title);

    /**
     * 描述
     *
     * @param title String
     * @param price Long
     * @return List<Product>
     */
    List<Product> findByTitleAndPrice(String title, Long price);


}
3自定义查询

Keyword

Sample

Elasticsearch Query String

And

findByNameAndPrice

{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}

Or

findByNameOrPrice

{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}

Is

findByName

{"bool" : {"must" : {"field" : {"name" : "?"}}}}

Not

findByNameNot

{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}

Between

findByPriceBetween

{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

LessThanEqual

findByPriceLessThan

{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

GreaterThanEqual

findByPriceGreaterThan

{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}

Before

findByPriceBefore

{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}

After

findByPriceAfter

{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}

Like

findByNameLike

{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}

StartingWith

findByNameStartingWith

{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}

EndingWith

findByNameEndingWith

{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}

Contains/Containing

findByNameContaining

{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}

In

findByNameIn(Collection<String>names)

{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}

NotIn

findByNameNotIn(Collection<String>names)

{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}

Near

findByStoreNear

Not Supported Yet !

True

findByAvailableTrue

{"bool" : {"must" : {"field" : {"available" : true}}}}

False

findByAvailableFalse

{"bool" : {"must" : {"field" : {"available" : false}}}}

OrderBy

findByAvailableTrueOrderByNameDesc

{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

我们只要按照上面的定义在接口中定义相应的方法,无须写实现就可实现我们想要的功能

Demo

1.保存文档和修改文档

@PostMapping("/save")
    public Product findById(Product product) {
        return repository.save(product);
    }

2.删除文档

@GetMapping("/deleteById")
    public String deleteById(@RequestParam("id") String id) {
        repository.deleteById(id);
        return "ok";
    }

3.检索一条记录

@GetMapping("/findById")
    public Product findById(@RequestParam("id") String id) {
        Optional<Product> result = repository.findById(id);
        return result.orElse(null);
    }

4.查询所有

@GetMapping("/findAll")
    public Page<Product> findAll(@RequestParam("page") int page, @RequestParam("size") int size) {
        PageRequest pageRequest = PageRequest.of(page, size);
        return repository.findAll(pageRequest);
    }

5.排序

@GetMapping("/findAllSort")
    public Iterable<Product> findAll(@RequestParam("key") String key) {
        Sort sort = Sort.by(Sort.Order.desc(key));
        return repository.findAll(sort);
    }

2.ElasticsearchClient方式

1.配置类
@Configuration
public class ElasticSearchConfig {

    @Bean
    public ElasticsearchClient client() {
        // ES服务器URL
        String serverUrl = "http://192.168.206.128:9200";
        // ES用户名和密码
        String name = "xxx";
        String password = "xxx";

//        BasicCredentialsProvider credential = new BasicCredentialsProvider();
//        credential.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(name, password));

        RestClient restClient = RestClient.builder(HttpHost.create(serverUrl)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }

}
2.索引CRUD

1.增加index

@Autowired
    ElasticsearchClient client;

    @PostMapping("/create")
    public String create(@RequestParam("indexName") String indexName) {
        CreateIndexResponse indexResponse = null;
        try {
            indexResponse = client.indices().create(c -> c.index(indexName));
        } catch (ElasticsearchException | IOException e) {
            log.info(e.getMessage());
            return e.getMessage();
        }
        return String.valueOf(indexResponse != null && indexResponse.acknowledged());
    }

2.查询Index

@GetMapping("/query")
    public String query(@RequestParam("indexName") String indexName) throws IOException {
        GetIndexResponse getIndexResponse = null;
        try {
            getIndexResponse = client.indices().get(i -> i.index(indexName));
        } catch (ElasticsearchException e) {
            log.info(e.getMessage());
            return e.getMessage();
        }
        if (getIndexResponse != null) {
            Optional<IndexState> first = getIndexResponse.result().values().stream().findFirst();
            return first.map(IndexState::toString).orElse(null);
        }
        return "unknown";
    }

3.判断index是否存在

@GetMapping("/exists")
    public boolean exists(@RequestParam("indexName") String indexName) throws IOException {
        BooleanResponse booleanResponse = client.indices().exists(e -> e.index(indexName));
        log.info(String.valueOf(booleanResponse.value()));
        return booleanResponse.value();
    }

4.删除index

@GetMapping("/delete")
    public String delete(@RequestParam("indexName") String indexName) throws IOException {
        DeleteIndexResponse deleteIndexResponse = null;
        try {
            deleteIndexResponse = client.indices().delete(d -> d.index(indexName));
        } catch (ElasticsearchException e) {
            log.info(e.getMessage());
            return e.getMessage();
        }
        if (deleteIndexResponse != null) {
            log.info(String.valueOf(deleteIndexResponse.acknowledged()));
            return String.valueOf(deleteIndexResponse.acknowledged());
        }
        return "unknown";
    }
3.Document CRUD

1.插入document

@PostMapping("/addDocument")
    public R<?> addDocument(@RequestParam("indexName") String indexName, Product product) {
        IndexResponse indexResponse = null;
        try {
            indexResponse = client.index(i -> i.index(indexName).document(product));
        } catch (IOException e) {
            return R.fail(e.getMessage());
        }
        return R.data(indexResponse.result());
    }

2.更新Document

@PostMapping("/updateDocument")
    public R<?> updateDocument(@RequestParam("indexName") String indexName, Product product) {
        UpdateResponse<Product> updateResponse = null;
        try {
            updateResponse = client.update(u -> u.index(indexName).id(product.getId()).doc(product), Product.class);
        } catch (ElasticsearchException | IOException e) {
            return R.fail(e.getMessage());
        }
        return R.success(updateResponse.toString());
    }

3.判断Document是否存在

@GetMapping("/existDocument")
    public R<?> existDocument(@RequestParam("indexName") String indexName, @RequestParam("id") String id) {
        BooleanResponse indexResponse = null;
        try {
            indexResponse = client.exists(e -> e.index(indexName).id(id));
        } catch (ElasticsearchException | IOException e) {
            return R.fail(e.getMessage());
        }
        return R.data(indexResponse.value());
    }

4.查询Document

@GetMapping("/getDocument")
    public R<?> getDocument(@RequestParam("indexName") String indexName, @RequestParam("id") String id) {
        GetResponse<Product> getResponse = null;
        try {
            getResponse = client.get(g -> g.index(indexName).id(id), Product.class);
        } catch (ElasticsearchException | IOException e) {
            return R.fail(e.getMessage());
        }
        return R.data(getResponse.source());
    }

5.删除Document

@GetMapping("/deleteDocument")
    public R<?> deleteDocument(@RequestParam("indexName") String indexName, @RequestParam("id") String id) {
        DeleteResponse deleteResponse = null;
        try {
            deleteResponse = client.delete(d -> d.index(indexName).id(id));
        } catch (ElasticsearchException | IOException e) {
            return R.fail(e.getMessage());
        }
        return R.data(deleteResponse.result());
    }

6.查询

@GetMapping("/search")
    public R<?> search(@RequestParam("indexName") String indexName, @RequestParam("key") String key, @RequestParam(
            "value") String value, @RequestParam("page") int page, @RequestParam("size") int size) {
        SearchResponse<Product> search = null;
        try {
            search = client.search(s -> s.index(indexName)
                    .query(q -> q.term(t -> t.field(key).value(v -> v.stringValue(value))))
                    .from(page).size(size)
                    .sort(f -> f.field(o -> o.field("price").order(SortOrder.Desc))), Product.class);
        } catch (ElasticsearchException | IOException e) {
            return R.fail(e.getMessage());
        }
        return R.data(search.hits().hits().stream().map(Hit::source).collect(Collectors.toList()));
    }

3.SpringBoot日志整合 ELK

引入Maven依赖

<!-- logstash -->
<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  version>7.1.1</version>
</dependency>

修改项目内的 logback.xml 文件 增加 logstash 配置

<springProperty scope="context" name="appName" source="spring.application.name"/>

    <!--输出到logstash的appender-->
    <appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <!--可以访问的logstash日志收集端口-->
        <destination>127.0.0.1:5044</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
            <customFields>{"spring.application.name":"${appName}"}</customFields>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="logstash"/>
    </root>