Elasticearch集群and备份s3
本文最后更新于 741 天前,其中的信息可能已经有所发展或是发生改变

1、Elasticsearch 作为一个搜索引擎,我们对它的基本要求就是存储海量数据并且可以在非常短的时间内查询到我们想要的信息。所以第一步我们需要保证的就是 Elasticsearch 的高可用性,什么是高可用性呢?它通常是指,通过设计减少系统不能提供服务的时间。假设系统一直能够提供服务,我们说系统的可用性是 100%。如果系统在某个时刻宕掉了,比如某个网站在某个时间挂掉了,那么就可以它临时是不可用的。所以,为了保证 Elasticsearch 的高可用性,我们就应该尽量减少 Elasticsearch 的不可用时间

2、针对一个索引,Elasticsearch 中其实有专门的衡量索引健康状况的标志,分为三个等级:

  • green,绿色。这代表所有的主分片和副本分片都已分配。你的集群是 100% 可用的
  • yellow,黄色。所有的主分片已经分片了,但至少还有一个副本是缺失的。不会有数据丢失,所以搜索结果依然是完整的。不过,你的高可用性在某种程度上被弱化。如果更多的分片消失,你就会丢数据了。所以可把 yellow 想象成一个需要及时调查的警告
  • red,红色。至少一个主分片以及它的全部副本都在缺失中。这意味着你在缺少数据:搜索只能返回部分数据,而分配到这个分片上的写入请求会返回一个异常

3、如果你只有一台主机的话,其实索引的健康状况也是 yellow,因为一台主机,集群没有其他的主机可以防止副本,所以说,这就是一个不健康的状态,因此集群也是十分有必要的

4、另外,既然是群集,那么存储空间肯定也是联合起来的,假如一台主机的存储空间是固定的,那么集群它相对于单个主机也有更多的存储空间,可存储的数据量也更大

详细了解 Elasticsearch 集群

1、首先我们应该清楚多台主机构成了一个集群,每台主机称作一个节点(Node)

  • 如图就是一个三节点的集群:

2、在图中,每个 Node 都有三个分片,其中 P 开头的代表 Primary 分片,即主分片,R 开头的代表 Replica 分片,即副本分片。所以图中主分片 1、2,副本分片 0 储存在 1 号节点,副本分片 0、1、2 储存在 2 号节点,主分片 0 和副本分片 1、2 储存在 3 号节点,一共是 3 个主分片和 6 个副本分片。同时我们还注意到 1 号节点还有个 MASTER 的标识,这代表它是一个主节点,它相比其他的节点更加特殊,它有权限控制整个集群,比如资源的分配、节点的修改等等

3、这里就引出了一个概念就是节点的类型,我们可以将节点分为这么四个类型:

  • 主节点:即 Master 节点。主节点的主要职责是和集群操作相关的内容,如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。稳定的主节点对集群的健康是非常重要的。默认情况下任何一个集群中的节点都有可能被选为主节点。索引数据和搜索查询等操作会占用大量的cpu,内存,io资源,为了确保一个集群的稳定,分离主节点和数据节点是一个比较好的选择。虽然主节点也可以协调节点,路由搜索和从客户端新增数据到数据节点,但最好不要使用这些专用的主节点。一个重要的原则是,尽可能做尽量少的工作
  • 数据节点:即 Data 节点。数据节点主要是存储索引数据的节点,主要对文档进行增删改查操作,聚合操作等。数据节点对 CPU、内存、IO 要求较高,在优化的时候需要监控数据节点的状态,当资源不够的时候,需要在集群中添加新的节点
  • 负载均衡节点:也称作 Client 节点,也称作客户端节点。当一个节点既不配置为主节点,也不配置为数据节点时,该节点只能处理路由请求,处理搜索,分发索引操作等,从本质上来说该客户节点表现为智能负载平衡器。独立的客户端节点在一个比较大的集群中是非常有用的,他协调主节点和数据节点,客户端节点加入集群可以得到集群的状态,根据集群的状态可以直接路由请求
  • 预处理节点:也称作 Ingest 节点,在索引数据之前可以先对数据做预处理操作,所有节点其实默认都是支持 Ingest 操作的,也可以专门将某个节点配置为 Ingest 节点

4、以上就是节点几种类型,一个节点其实可以对应不同的类型,如一个节点可以同时成为主节点和数据节点和预处理节点,但如果一个节点既不是主节点也不是数据节点,那么它就是负载均衡节点。具体的类型可以通过具体的配置文件来设置

集群搭建

1、修改内核参数

  • max_map_count文件包含限制一个进程可以拥有的VMA(虚拟内存区域)的数量
sysctl -a|grep vm.max_map_count
  • 临时修改
sysctl -w vm.max_map_count=262144
  • 永久修改
vim /etc/sysctl.conf
vm.max_map_count=262144
  • 刷新参数
sysctl -p

启动elasticsearch

1、先拷贝es数据目录和配置文件到宿主机映射

docker run -d --name es docker.elastic.co/elasticsearch/elasticsearch:6.8.23
docker cp es:/usr/share/elasticsearch .

2、docker-compose文件

version: '3.3'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.8.23
    deploy:
      resources:
        limits:
          memory: 3G
        reservations:
          memory: 2G
    container_name: elasticsearch
    privileged: true
    environment:
      - node.name=es01  # 节点名称
      - discovery.zen.ping.unicast.hosts=10.10.10.171,10.10.10.179  # 设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点
      - cluster.name=docker-cluster  # 集群名称,相同名称为一个集群, 三个es节点须一致
      - bootstrap.memory_lock=true   # 内存交换的选项,官网建议为true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"  # 设置内存,如内存不足,可以尝试调低点
    ulimits:  # 栈内存的上限
      memlock:
        soft: -1  # 不限制
        hard: -1  # 不限制
    volumes:
      - /data/es/elasticsearch:/usr/share/elasticsearch
    ports:
      - 9200:9200  # http端口,可以直接浏览器访问 
      - 9300:9300  # es集群之间相互访问的端口,jar之间就是通过此端口进行tcp协议通信,遵循tcp协
networks:
  default:
    external:
      name: es_net

3、修改配置文件

[root@ops-es es]# cat elasticsearch/config/elasticsearch.yml 
cluster.name: "docker-cluster"
network.host: 0.0.0.0
node.name: es01 # 节点名称
http.port: 9200 # rest客户端连接端口
transport.tcp.port: 9300 # 集群中节点互相通信端口
action.destructive_requires_name: true
node.master: true # 设置master角色
node.data: true # 设置data角色
node.ingest: true # 设置ingest角色
bootstrap.memory_lock: false  ## 内存交换的选项,官网建议为true
node.max_local_storage_nodes: 2  # 最大集群节点数
#默认的elasticsearch不允许跨域,因此elasticsearch head插件无法连接。需要修改配置文件才行
http.cors.enabled: true  # 是否支持跨域
http.cors.allow-origin: "*"  # 跨域配置,表示支持所有域名
network.publish_host: 10.10.10.171  # 用于集群内各机器间通信,对外使用,其他机器访问本机器的es服务,一般为本机宿主机IP
path.logs: /usr/share/elasticsearch/logs

4、另外两台服务器也照着这个配置进行配置,但 network.publish_host,以及 node.name 需要改一下即可

5、启动集群,在docker-compose文件所在目录下运行命令

docker-compose up -d

验证是否搭建成功

[root@ops-es es]# curl https://10.10.10.171:9200/_cat/nodes?pretty
10.10.10.179 55 61 14 0.19 0.29 0.34 mdi - es02
10.10.10.171 68 98  7 0.44 0.38 0.37 mdi * es01

1、查看集群的节点

[root@ops-es es]# curl https://127.0.0.1:9200/_cat/nodes?v
ip           heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
10.10.10.179           61          61  22    0.18    0.28     0.33 mdi       -      es02
10.10.10.171           75          98   8    0.38    0.37     0.36 mdi       *      es01
  • 通过该连接返回了集群中各节点的情况。这些信息中比较重要的是master列,带*星号表明该节点是主节点。带-表明该节点是从节点

2、查看集群所在磁盘的分配状况

[root@ops-es es]# curl https://127.0.0.1:9200/_cat/allocation?v
shards disk.indices disk.used disk.avail disk.total disk.percent host         ip           node
   101        2.3gb    45.5gb    151.1gb    196.7gb           23 10.10.10.179 10.10.10.179 es02
   101        2.3gb   333.8gb    157.2gb    491.1gb           67 10.10.10.171 10.10.10.171 es01

通过该连接返回了集群中的各节点所在磁盘的磁盘状况

返回的信息包括:

分片数(shards),集群中各节点的分片数相同,都是6,总数为12,所以集群的总分片数为12。

索引所占空间(disk.indices),该节点中所有索引在该磁盘所点的空间。

磁盘使用容量(disk.used),已经使用空间45.5gb

磁盘可用容量(disk.avail),可用空间151.1gb

磁盘总容量(disk.total),总共容量196.7gb

磁盘便用率(disk.percent),磁盘使用率。

es head浏览器插件

索引的查看与删除

查看Elasticsearch 索引状态

[root@ops-es ~]# curl -XGET 'https://127.0.0.1:9200/_cat/indices?v'
health status index                         uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   360-nginx-2023.01.03       3Ajl4HzxRWuAFu6z2lu6TA   5   1     520527            0        1gb        513.7mb
green  open   cloud-nginx-2023.01.05 YU8RjRVoReOBguk-FUurPQ   5   1         96            0    834.1kb          417kb
green  open   yhl-service-2023.01.04        VnkbH-NkTCOZYTom0Du07g   5   1         98            0    568.1kb        299.2kb
green  open   360-service-2023.01.03     N1jJWBC-RRWRd7N75lcGEw   5   1     533710            0    975.3mb        487.4mb
green  open   sss-nginx-2023.01.03     kaiNWxtbRreXu1hOa5Dq4A   5   1          9            0    192.5kb         96.2kb
green  open   ccc-nginx-2023.01.05      AiZkLN-_Q5yFx6BKozonSg   5   1          1            0     22.6kb         11.2kb

2、ES的索引有红,黄,绿三种状态,其中绿色代表正常状态,红色和黄色则说明多少有一些问题。我们在正常的ES运维过程中常常需要处理这些情况

  • 绿色:索引的所有分片都正常分配
  • 黄色:至少有一个副本没有得到正确的分配
  • 红色:至少有一个主分片没有得到正确的分配
  • 集群和节点同样有三种颜色,这个颜色取决于相关索引的最差状况。比如说,整个集群中有3个节点,10个索引,每个索引分成3个分片,并有一份副本。如果其中有一个节点离线,上面有多个索引的主分片。那么集群的状态就是红色(red)

3、可能的情况,事实上,索引状态变成红色或者黄色并不一定是出了问题。这些情况可以通过catAPI查询获知参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cat-shards.html

4、下面这些情况有可能是正常的情况,可能导致索引的状态临时性的变成红色和黄色

  • INDEX_CREATED: 集群正在创建索引。
  • CLUSTER_RECOVERED: 集群处于重启阶段。
  • INDEX_OPENED: 正在重新打开一个已经关闭的索引。
  • NEW_INDEX_RESTORED:还原数据到一个新索引。
  • EXISTING_INDEX_RESTORED: 还原数据到一个已经关闭的索引。
  • REPLICA_ADDED: 索引的设置被修改,副本数增加。
  • REROUTE_CANCELLED: 由于reroute命令被取消导致有一些分片没有被分配。
  • REINITIALIZED:由于一个分片的状态重新退回到初始化导致。
  • REALLOCATED_REPLICA:副本位置变化导致未分配

5、只有下面几个状态是明确地由于分片错误导致的

  • ALLOCATION_FAILED: 由于配置原因或者资源问题导致未分配情况。
  • DANGLING_INDEX_IMPORTED: 副本在节点离线情况下被修改,在这个副本回到集群中时会产生此问题

6、在运维的过程中,如果发现集群的状态变成黄色或者红色,我们不妨等一等,很多问题会在短时间消失。然而,并不是所有错误都能自动修复,如果发现集群状态一直不正常的情况下,我们需要分析具体情况,找出导致问题的根本原因,再将其修复,ES提供了healthAPI供我们查询集群的状态和发现问题的原因,参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cluster-health.html

8、我们可以看到一些用法

  • GET _cluster/health:检测集群的健康状态,可以使用这个API检测集群的节点数量
  • GET _cluster/health?level=indices: 查看全部索引的状态,找出有问题的索引
  • GET _cluster/health/index_name: 检测某个索引的状态,分析问题
  • GET _cluster/health?level=shards: 查看分片分配的情况,寻找未分配的分片
  • GET _cluster/allocation/explain: 查看第一个未分配分配的故障原因
  • 在找到原因后,需要对一些不能自动恢复的问题进行修复

索引的删除

1、在ELK架构中,使用Elasticsearch来存储系统日志时,有如下典型的特点:

  • 数据量非常大
  • 经常访问新增的数据,随着时间的推移,数据的价值也在逐渐降低

2、随着数据量的增大,Elasticsearch创建索引的数量也在不断增长,这个时候就需要对 索引 进行一定策略的维护管理甚至是删除清理,否则随着数据量越来越多除了浪费磁盘与内存空间之外,还会严重影响 Elasticsearch 的性能

3、删除指定索引

curl -XDELETE 127.0.0.1:9200/system-log-2023.01.03
  • 其中system-log-2023.01.03为索引全称

4、删除多个索引

curl -XDELETE 127.0.0.1:9200/system-log-2019.05,system-log-2019.05

4、删除所有索引

curl -XDELETE  127.0.0.1:9200/_all
或者
curl -XDELETE  127.0.0.1:9200/*
  • 通常不建议使用通配符,误删了后果就很严重了,所有的index都被删除了
  • 禁止通配符为了安全起见,可以在elasticsearch.yml配置文件中设置禁用_all和*通配符,action.destructive_requires_name = true,这样就不能使用_all和*了

脚本删除

1、我们都知道,ES索引处于open状态,就会占用内存+磁盘,如果将索引close,只会占用磁盘,当索引比较多的时候影响查询速度

2、es定时删除脚本:

  • 删除指定索引
#/bin/bash
LAST_DATE=`date -d "7 day ago" +"%Y.%m.%d"`
app=$1
echo -n "删除${app}-${LAST_DATE}索引" " " >> /mydata/index.log
curl -s -XDELETE 'https://127.0.0.1:9200/'${app}'-'${LAST_DATE}'' >> /mydata/index.log
echo >> /mydata/index.log

设置一个定时脚本

00 01 * * * /usr/bin/bash /mydata/delete_indices.sh yyy-service
00 02 * * * /usr/bin/bash /mydata/delete_indices.sh yyy-nginx
00 03 * * * /usr/bin/bash /mydata/delete_indices.sh www-nginx
  • 删除全部符合索引
#!/bin/bash

# elasticsearch 的 URL
elasticsearch_url="http://localhost:9200"

# 删除索引的当前时间
current_time="$(date +%Y-%m-%d)"

# 计算要删除的索引名称
one_day_ago=`date --date="5 day ago" '+%Y.%m.%d'`
delete_index_pattern="*${one_day_ago}*"

# 获取要删除的索引列表
indices=$(curl -s "${elasticsearch_url}/_cat/indices" | awk -v pattern="${one_day_ago}" '$3 ~ pattern {print $3}')

# 删除索引
for index in ${indices}; do
  curl -XDELETE "${elasticsearch_url}/${index}"
#  echo "删除${elasticsearch_url}的${index}索引"
# 输出结果
  echo "${current_time}号Deleted indices 5天前: ${index}" >> /mydata/index.log
done

备份

1、Elasticsearch 是一个开源的分布式 RESTful 搜索和分析引擎。它可以在近实时条件下,存储,查询和分析海量的数据。它还支持将快照备份至HDFS/S3上面,而阿里云OSS兼容S3的API

  • 由于之前elasticsearch出现崩溃,导致索引被删除,只能恢复三天内的数据,因此需要创建快照进行备份,目标至少快速恢复一个月内的数据

2、首先,我们需要安装repository-s3,可以参考官方文档:https://www.elastic.co/guide/en/elasticsearch/plugins/7.2/repository-s3.html

插件配置

1、进入容器

docker exec -it elasticsearch bash

2、安装repository-s3

elasticsearch-plugin install repository-s3
  • 安装成功后提示repository-s3 installed,插件安装完成后需要重启elasticsearch,才能加载

3、查看

[root@ops-prometheus elasticsearch]# ls plugins/repository-s3/
aws-java-sdk-core-1.11.406.jar  commons-logging-1.1.3.jar       jackson-databind-2.8.11.6.jar  NOTICE.txt
aws-java-sdk-kms-1.11.406.jar   httpclient-4.5.2.jar            jaxb-api-2.2.2.jar             plugin-descriptor.properties
aws-java-sdk-s3-1.11.406.jar    httpcore-4.4.5.jar              jmespath-java-1.11.406.jar     plugin-security.policy
commons-codec-1.10.jar          jackson-annotations-2.8.11.jar  LICENSE.txt 

4、到阿里云后台生成AccessKey和SecretKey,执行如下命令,将key添加到elasticsearch.keystore

[root@79c1ceaf2bfb elasticsearch]# elasticsearch-keystore add s3.client.default.access_key
Enter value for s3.client.default.access_key: 
[root@79c1ceaf2bfb elasticsearch]# elasticsearch-keystore add s3.client.default.secret_key
Enter value for s3.client.default.secret_key:
  • 若没有安装插件,执行添加key会报命令不存在,key添加完成后需要再次重启elasticsearch,才能生效
[root@ops-prometheus elasticsearch]# docker restart elasticsearch

快照执行步骤

1、创建快照仓库

curl -XPUT 'https://10.10.10.171:9400/_snapshot/es_oss' -H 'Content-Type: application/json' -d '{"type":"s3", "settings":{"bucket":"360-loki","endpoint":"oss-cn-shanghai-internal.aliyuncs.com","compress":true,"disable_chunked_encoding":true,"base_path":"es/"}}'

参数说明:

  • type:指定快照仓库类型为S3,也就是oss
  • bucket:指定oss的bucket名称(阿里云上查看)
  • endpoint:指定oss访问域名(阿里云上查看)
  • compress:是否压缩(默认开启压缩,只压缩元数据文件,索引数据文件不压缩)
  • disable_chunked_encoding:是否禁用HTTP服务器响应的分块编码
  • base_path:指定bucket内的存放目录

查看仓库:

[root@ops-es ~]# curl -XGET 'https://10.10.10.171:9200/_snapshot/es_oss/_all?pretty=true'
{
  "snapshots" : [
    {
      "snapshot" : "360",
      "uuid" : "Lkrro*****x92zbiA",
      "version_id" : 6082399,
      "version" : "6.8.23",
      "indices" : [
        "360-nginx-2023.01.03",
        "360-service-2023.01.03",
      ],
      "include_global_state" : false,
      "state" : "SUCCESS",
      "start_time" : "2023-01-05T09:12:38.506Z",
      "start_time_in_millis" : 1672909958506,
      "end_time" : "2023-01-05T09:13:00.518Z",
      "end_time_in_millis" : 1672909980518,
      "duration_in_millis" : 22012,
      "failures" : [ ],
      "shards" : {
        "total" : 136,
        "failed" : 0,
        "successful" : 136
      }
    }
  ]
}

2、创建快照

[root@ops-es elasticsearch]# curl -XPUT 'https://10.10.10.171:9200/_snapshot/es_oss/360-loki?wait_for_completion=true' -H 'Content-Type: application/json' -d '{"indices":"_all","ignore_unavailable":true,"include_global_state":false}'
# 返回值
{"snapshot":{"snapshot":"360-loki","uuid":"q2AklLchTJKmWFDKMQ***","version_id":6082399,"version":"6.8.23","indices":["360-service-2023.01.05",".kibana","360-nginx-2023.01.05","zeus_20221215"],"include_global_state":false,"state":"SUCCESS","start_time":"2023-01-05T07:41:38.374Z","start_time_in_millis":1672904498374,"end_time":"2023-01-05T07:44:06.596Z","end_time_in_millis":1672904646596,"duration_in_millis":148222,"failures":[],"shards":{"total":141,"failed":0,"successful":141}}}

日志输出:

[2023-01-05T07:41:38,864][INFO ][o.e.s.SnapshotsService   ] [es01] snapshot [es_oss:360-loki/q2AklLchW*****DKMQ] started

参数说明:

  • wait_for_completion:是否等待快照完成后返回
  • indices:指定要创建快照的索引,如有多个索引使用英文逗号隔开(如index1,index2,_all代表所有的索引,它还支持通配符,例如:testtest或tet或test*,以及“排除”(-)的能力,例如:test*,-test3)
  • ignore_unavailable:忽略不存在的索引
  • include_global_state:防止集群全局状态被被存储为快照

查看备份:

curl -XGET 'https://10.10.10.171:9200/_snapshot/es_oss/_all?pretty=true'
  • 阿里云oss

3、快照恢复

[root@ops-es ~]# curl -XPOST 'https://localhost:9200/_snapshot/es_oss/360-loki/_restore?pretty'
{
  "accepted" : true
}
  • 然后再次查看索引状态:
[root@ops-es ~]# curl -XGET 'https://127.0.0.1:9400/_cat/indices?v'

脚本每天备份快照

1、Linux定时任务功能使用cron服务来进行

  • 编写定时任务的cron表达式
00 02 * * * /usr/bin/bash /mydata/es_oss.sh
  • 查看一下es_oss.sh脚本内容
#!/bin/bash
repository_name="es_oss"

snapshot_name="$(date +%Y-%m-%d)"

snapshot_seven_days_ago=`date -d '7 days ago' +%Y-%m-%d`

echo "" > /mydata/es_oss.log

curl -XPUT "https://10.10.10.171:9200/_snapshot/${repository_name}/${snapshot_name}?wait_for_completion=true" -H 'Content-Type: application/json' -d '{"indices":"_all","ignore_unavailable":true,"include_global_state":false}' && echo "备份360-${snapshot_name}快照到oss成功" >> /mydata/es_oss.log || {
        echo "备份elasticsearch-${snapshot_name}快照异常" >> /mydata/es_oss.log
        exit 1
}

snapshots=$(curl -s -XGET "localhost:9200/_snapshot/$repository_name/$snapshot_seven_days_ago" | jq -r '.snapshots[].snapshot')

if [ $snapshots == "$snapshot_seven_days_ago" ] ;then

    curl -XDELETE "localhost:9200/_snapshot/$repository_name/$snapshots" && echo "删除elasticsearch-${snapshots}前快照到oss成功" >> /mydata/es_oss.log || {

        echo "删除elasticsearch-${snapshots}快照异常,请检查" >> /mydata/es_oss.log
        exit 1
}

else

    echo "没有找到elasticsearch-${snapshots}快照信息" >> /mydata/es_oss.log
    exit 1

fi

sleep 20

echo "" > /mydata/es_oss.log

2、注意:ES备份数据需要将要备份的索引数据快照一份,需要指定一个快照名,且不能使用相同的快照,如果备份相同的快照名需要在每次备份之前需要删除旧的快照,再备份

备份通知

博客内容均系原创,未经允许严禁转载!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
首页