2019你该掌握的开源日志管理平台ELK STACK
在企业级开源日志管理平台ELK VS GRAYLOG一文中,我简单阐述了日志管理平台对技术人员的重要性,并把ELK Stack和Graylog进行了标记。本篇作为“企业级开源日志管理平台”的延伸,基于我在生产环境中的使用经验,向读者介绍ELK Stack的安装与配置。不足之处,还望指正。
架构
Beats工具收集各节的日志,以list数据结构存储在Redis中,Logstash从Redis消费这些数据并在条件匹配及规则过滤后,输出到Elasticsearch,最终通过kibana展示给用户。
环境介绍
Elastic Stack的产品被设计成需要一起使用,并且版本同步发布,以简化安装和升级过程。本次安装,采用最新的6.5通用版。完整堆栈包括:
1. Beats 6.5
2. Elasticsearch 6.5
3. Elasticsearch Hadoop 6.5(不在本次介绍范围)
4. Kibana 6.5
5. Logstash 6.5
操作系统CentOS7.5,JDK需要8及以上版本。
官方介绍的安装途径包括:tar包安装、rpm包安装、docker安装、yum仓库安装,我使用RPM包安装。
系统设置
Elasticsearch默认监听127.0.0.1,这显然无法跨主机交互。当我们对网络相关配置进行修改后,Elasticsearch由开发模式切换为生产模式,会在启动时进行一系列安全检查,以防出现配置不当导致的问题。
这些检查主要包括:
1. max_map_count:Elasticsearch默认使用混合的NioFs( 注:非阻塞文件系统)和MMapFs( 注:内存映射文件系统)存储索引。请确保你配置的最大映射数量,以便有足够的虚拟内存可用于mmapped文件。此值设置不当,启动Elasticsearch时日志会输出以下错误:[1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决方法:
# vim /etc/sysctl.conf
vm.max_map_count=262144
# sysctl -p
- 修改最大文件描述符
# vim /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
- 修改最大线程数
# vim /etc/security/limits.conf
* soft nproc 4096
* hard nproc 4096
注意:通过RPM包或YUM形式安装,Elasticsearch会自动优化这些参数,如果采用tar包的形式安装,除了手动修改这些配置,还需要创建启动Elasticsearch程序的系统用户,Elasticsearch不允许以root身份运行。
安装
安装顺序:
1. Elasticsearch
2. Kibana
3. Logstash
4. Beats
安装Elasticsearch
- 导入Elasticsearch PGP key,使用Elasticsearch签名密钥(PGP key D88E42B4,可从https://pgp.mit.edu获得)和fingerprint对所有包进行签名:
# rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
- 下载安装RPM包
# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.rpm
# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.rpm.sha512
# sha512sum -c elasticsearch-6.5.4.rpm.sha512 #比较RPM包的SHA和应输出的已发布校验和,输出 elasticsearch-6.5.4.rpm: OK
# rpm --install elasticsearch-6.5.4.rpm
- 配置文件
Elasticsearch包含三个配置文件:
elasticsearch.yml:配置Elasticsearch
jvm.options:设置Elasticsearch堆栈大小,对JVM进行优化
log4j2.properties:定义Elasticsearch日志
这些文件的默认位置取决于安装方式。tar包形式的配置文件目录$ES_HOME/config,RPM包默认位置/etc/elasticsearch/。
elasticsearch.yml文件采用yaml格式,以修改路径为例:
path:
data: /var/lib/elasticsearch
logs: /var/log/elasticsearch
or
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
elasticsearch.yml文件也支持环境变量,引用环境变量的方法${…},例如:
node.name: ${HOSTNAME}
network.host: ${ES_NETWORK_HOST}
jvm.options文件包含以下格式:
行分割;
空行被忽略;
以#开头表示注释;
以-开头的行被视为独立于JVM版本,此项配置会影响所有版本的JVM;
数字开头,后面跟一个:和一个-号的行,只会影响匹配此数字的JVM版本,例如:8:-Xmx2g,只会影响JDK8;
数字开头跟一个-号再跟一个数字再跟一个:,定义两个版本之间,且包含这两个版本,例如8-9:-Xmx2g,影响JDK8,JDK9。
注意:在配置中,应保证JVM堆栈min和max的值相同,Xms表示总堆栈空间的初始值,XmX表示总堆栈空间的最大值。
# cat /etc/elasticsearch/elasticsearch.yml
cluster.name: vtlab #集群名称,只有cluster.name相同时,节点才能加入集群。请设置为具有描述性的名字。不建议在不同环境中使用相同的集群名。
node.name: vtlab01 #节点描述名称,默认情况下,Elasticsearch将使用随机生成的UUID的前7个字符作为节点id。设为服务器的主机名 node.name: ${HOSTNAME}
node.attr.rack: r1 #指定节点的部落属性,机架位置,比集群范围更大。
path.data: /var/lib/elasticsearch #Elasticsearch的数据文件存放目录 如果是默认位置,在将Elasticsearch升级到新版本时,很可能会把数据删除。
path.logs: /var/log/elasticsearch #日志目录
bootstrap.memory_lock: true #启动后锁定内存,禁用swap交换,提高ES性能。伴随这个参数还需要调整其他配置,后面讨论。
network.host: 0.0.0.0 #指定监听的地址
http.port: 9200 #监听的WEB端口
discovery.zen.ping.unicast.hosts: #默认网络配置中,Elasticsearch将绑定到回环地址,并扫描9300-9305端口,试图连接同一台服务器上的其他节点,可以自动发现并加入集群。
- 10.0.0.46:9300 #此端口为TCP传输端口,用于集群内节点发现、节点间信息传输、ES Java API也是通过此端口传输数据,transport.tcp.port定义。9200为HTTP端口。
- 10.0.0.28:9300
- 10.0.0.29:9300
- host1.vtlab.io
discovery.zen.minimum_master_nodes: 2 #为防止数据丢失,discovery.zen.minimum_master_nodes设置至关重要,主节点的最小选举数。避免脑裂,应将此值设为(master_eligible_nodes / 2) + 1,换言之,如果有3个节点,(3/2)+1 or 2
#以下选项仅在完全重启集群时生效
#本地网关模块在整个群集重新启动时存储群集状态和分片数据。
#以下静态设置必须在每个主节点上设置,控制新选择的主节点在试图恢复集群状态和集群数据之前应该等待多长时间:
gateway.expected_nodes: 0 #集群中预期的(数据或主)节点数量。一旦加入集群的节点数量达到预期,本地碎片的恢复就会开始。默认值为0
gateway.expected_master_nodes: 0 #集群中预期的主节点数量。一旦加入集群的主节点数量达到预期,本地碎片的恢复就会开始。默认值为0
gateway.expected_data_nodes: 0 #集群中预期的数据节点数量。一旦预期的数据节点数量加入集群,本地碎片的恢复就会开始。默认值为0
gateway.recover_after_time: 5 #如果没有达到预期的节点数量,则恢复过程将等待配置的时间量,然后再尝试恢复。如果配置了一个expected_nodes设置,则默认值为5m。
gateway.recover_after_nodes: 1 #只要有这么多数据或主节点加入集群,就可以恢复。
gateway.recover_after_master_nodes: 1 #只要有这么多主节点加入集群,就可以恢复。
gateway.recover_after_data_nodes: 1 #只要有这么多数据节点加入集群,就可以恢复。
action.destructive_requires_name: true #禁用通过api以通配符删除所有索引。删除索引时需要指定被删除的索引名称。
配置文件中,我将bootstrap.memory_lock的值设为了true,启动时遇到了以下错误:Elasticsearch process memory locking failed
解决此问题,还需要修改三个地方:
1. /etc/sysconfig/elasticsearch
ES_JAVA_OPTS="-Xms4g -Xmx4g"
MAX_LOCKED_MEMORY=unlimited
替换4g为总内存的一半(Elasticsearch官方建议是主机总内存的一半)
2. /etc/security/limits.conf
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
需要将elasticsearch替换为运行Elasticsearch程序的用户
3. /usr/lib/systemd/system/elasticsearch.service
取消服务脚本文件/usr/lib/systemd/system/elasticsearch.service中对LimitMEMLOCK=infinity的注释
LimitMEMLOCK=infinity
然后运行systemctl daemon-reload命令
# systemctl daemon-reload
- 启动节点
# systemctl enable elasticsearch
# systemctl start elasticsearch
Elasticsearch安装完毕,接下来安装Kibana。
安装Kibana
同样使用RPM的形式安装
1. 安装公钥
# rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
- 下载rpm包
# wget https://artifacts.elastic.co/downloads/kibana/kibana-6.5.4-x86_64.rpm
# sha512sum kibana-6.5.4-x86_64.rpm
# rpm --install kibana-6.5.4-x86_64.rpm
- 配置文件
Kibana启动时从/etc/kibana/kibana.yml文件里读取配置信息,配置文件内容如下:
# Default: 5601。定义Kibana后端服务启动时监听的端口。
server.port: 5601
# Default: "localhost"。后端服务监听的地址,需要修改为网卡地址
server.host: "localhost"
# 如果前面有代理,可以指定安装Kibana的路径。
# 使用server.rewriteBasePath设置告诉Kibana是否应从其收到的请求中删除basePath,并防止在启动时发生弃用警告。
# 此设置不能以斜杠(/)结尾。
server.basePath: ""
# Default: false
# 指定Kibana是否应重写以server.basePath为前缀的请求,或者要求它们由反向代理重写。
# 在Kibana 6.3之前,此设置实际上始终为false,在Kibana 7.0中默认为true
server.rewriteBasePath: false
# Default: 1048576
# 请求服务的最大有效负载,单位字节
server.maxPayloadBytes: 1048576
# Default: "your-hostname"。Kibana服务器的名称,用于显示目的。
server.name: "your-hostname"
# 用于查询的Elasticsearch实例的URL
elasticsearch.url: "http://localhost:9200"
# Default: true
# 值为true时,Kibana使用server.host设置中指定的主机名。值为false时,Kibana使用连接到此Kibana实例的主机的主机名
elasticsearch.preserveHost: true
# Default: ".kibana"
# Kibana使用此索引名存储Elasticsearch中已保存的搜索、可视化和仪表板。
# 如果索引尚不存在,Kibana会创建一个新索引。
kibana.index: ".kibana"
# 要加载的默认应用程序。
kibana.defaultAppId: "home"
# 如果Elasticsearch设置了认证,Kibana通过这两个选项进行Elasticsearch的验证。
elasticsearch.username: "user"
elasticsearch.password: "pass"
# Default: "false"
# 从Kibana服务器到浏览器的传出请求启用SSL,当设置为True时,server.ssl.certificate and server.ssl.key必须设置
server.ssl.enabled: false
server.ssl.certificate: /path/to/your/server.crt
server.ssl.key: /path/to/your/server.key
# 可选设置,提供PEM格式SSL证书和密钥文件的路径。
# 这些文件用于验证Kibana到Elasticsearch的身份,并需要在Elasticsearch中的xpack.ssl.verification_mode设置为certificate或full
elasticsearch.ssl.certificate: /path/to/your/client.crt
elasticsearch.ssl.key: /path/to/your/client.key
# 可选设置,使您可以为Elasticsearch实例的证书颁发机构指定PEM文件的路径列表。
elasticsearch.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ]
# 控制Elasticsearch提供的证书验证。有效值为none,certificate和full。完整执行主机名验证,证书不执行
elasticsearch.ssl.verificationMode: full
# Defau:elasticsearch.requestTimeout 设置的值,等待Elasticsearch响应ping的时间,单位ms。
elasticsearch.pingTimeout: 1500
# Default:30000ms
# 等待来自后端或Elasticsearch的响应时间,必须是正整数。单位ms。
elasticsearch.requestTimeout: 30000
# Default: [ 'authorization' ]
# 要发送到Elasticsearch的Kibana客户端标头列表。要不发送客户端标头,请将此值设置为[](空列表)。
elasticsearch.requestHeadersWhitelist: [ authorization ]
# Default: {}
# 要发送到Elasticsearch的标头名称和值。无论elasticsearch.requestHeadersWhitelist配置如何,客户端标头都不能覆盖任何自定义标头。
elasticsearch.customHeaders: {}
# Default: 30000
# 对Elasticsearch等待碎片响应的时间(以毫秒为单位)。设置为0禁用
elasticsearch.shardTimeout: 30000
# Default: 5000
# 在重试之前在Kibana启动时等待Elasticsearch的时间(以毫秒为单位)
elasticsearch.startupTimeout: 5000
# Default false记录发送到Elasticsearch的查询。需要将logging
# 记录发送到Elasticsearch的查询。需要将logging.verbose设置为true。
# 这对于查看当前没有检查器的应用程序生成的查询DSL非常有用,例如Timelion和Monitoring。
elasticsearch.logQueries: false
# PID文件路径
pid.file: /var/run/kibana.pid
# Default: stdout
# 日志输出文件路径
logging.dest: stdout
# Default: false
# 此值设为true时,禁止记录日志
logging.silent: false
# Default: false
# 当设置为true时,仅记录error日志
logging.quiet: false
# Default: false
# 将此值设置为true以记录所有事件,包括系统使用信息和所有请求。
logging.verbose: false
# Default: 5000
# 设置以毫秒为单位的间隔来采样系统和处理性能指标。最小值是100。
ops.interval: 5000
- 启动
# systemctl enable kibana
# systemctl start kibana
安装Logstash
- 下载
# wget https://artifacts.elastic.co/downloads/logstash/logstash-6.5.4.rpm
# rpm --install logstash-6.5.4.rpm
安装Filebeat
Filebeat客户端是一个轻量级,资源消耗较低的工具,它从服务器的文件中收集日志,并将这些日志转发到Logstash进行处理。
1. 下载filebeat
# wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.5.4-x86_64.rpm
# rpm -vi filebeat-6.5.4-x86_64.rpm
- 配置文件
vim /etc/filebeat/filebeat.yml
# 定义获取日志的路径,可以是目录或文件:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/messages
tags: [syslog]
- type: log
enabled: true
paths:
- /data/logs/nginx/vtlab/*.log
tags: [nginx_access]
# 配置输出
output.redis:
enable: true #默认true,是否开启传输
hosts: ["10.0.0.26:6379"] #redis服务的IP、端口
db: 1 #选择redis哪个库
timeout: 5 #连接redis超时时间,默认5s
key: filter_index #数据被传输到redis list的名称
#password: password #连接redis的密码,如果redis配置文件中没有设置,则此处不需要填写
- 启动Filebeat
# systemctl enable filebeat.service
# systemctl start filebeat.service
ELK各组件安装完成,开始日志收集相关的配置。
Logstash pipeline
Logstash管道包含两个必要元素Inputs和Outputs,以及一个可选元素Filter。Inputs接收、消费元数据,Filter根据设定过滤数据,Outputs将数据输出到指定程序,我这里定义的是输出到Elasticsearch。
我配置了Filebeat输出message日志和www.vtlab.io 的访问日志,现在创建Logstash管道接收这些数据。
# vim first-pipeline.conf
# 定义Input
input {
redis {
host => "10.0.0.26"
type => "redis-input"
data_type => "list"
db => 1
key => "filter_index"
port => 6379
#password => "password"
}
}
# 定义Filter
filter{
if "syslog" in [tags] {
grok {
match => {"message" => "%{SYSLOGBASE2}"}
remove_field => "beat.version"
remove_field => "beat.name"
}
}
if "nginx_access" in [tags] {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
remove_field => "beat.version"
remove_field => "beat.name"
}
geoip {
source => "clientip"
# fields => ["city_name", "country_code2", "country_name", "latitude", "longitude", "region_name"]
# remove_field => ["[geoip][latitude]", "[geoip][longitude]"]
}
}
}
# 定义output
output{
if "syslog" in [tags] {
elasticsearch {
hosts => [ "10.0.0.28:9200","10.0.0.29:9200","10.0.0.46:9200" ]
index => "message-%{+YYYY.MM.dd}"
}
}
if "nginx_access" in [tags] {
elasticsearch {
hosts => [ "10.0.0.28:9200","10.0.0.29:9200","10.0.0.46:9200" ]
index => "logstash-nginx-%{+YYYY.MM.dd}"
}
}
}
配置文件中,我定义了两个patterns:”message” => “%{SYSLOGBASE2}”与”message” => “%{COMBINEDAPACHELOG}”,一个匹配系统messages日志,一个匹配Nginx访问日志,其他日志类型,就需要不同的Patterns了。ELK提供了很多默认的Patterns,也可以自定义。
定义Patterns
一个简单的方法,通过以下两个步骤实现:
1. 打开Grokdebug discover页面,输入日志内容到文本框,点击discover按钮,如下图:
2. 打开Grokdebug页面,按图中步骤操作,输出内容就是Patterns。
启动Logstash
- 启动前,检验first-pipeline.conf配置文件的语法是否正确:
# /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/first-pipeline.conf --config.test_and_exit
Configuration OK
[INFO ] 2019-01-07 17:23:32.527 [LogStash::Runner] runner - Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash
输出中包含以上内容,说明配置文件正确。
2. 启动指定pipeline:
–config.reload.automatic:开启自动加载配置,这样在每次修改配置文件时会自动加载配置文件
# /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/first-pipeline.conf --config.reload.automatic
Kibana UI
日志流已经通过Filebeat、Redis、Logstash进入了Elasticsearch,我们通过Kibana对Elasticsearch做一些可视化的管理,方便查看日志。
创建索引
登录Kibana页面,在浏览器中输入 http://10.0.0.21:5601 (10.0.0.21为部署Kibana主机的IP地址,5601是Kibana监听的端口),如图:
点击“Management”–>“Index Patterns”–>“Create index pattern”,在Index pattern框中填写Index name,也就是我们通过Logstash输出到Elasticsearch时的索引名”message-*”和”logstash-nginx-*”,点击Next step,在“Time Filter field name”中选择“@timestamp”,最后点击“Create index pattern”完成索引创建。
经过以上的步骤,就可以在Kibana的discover中依照索引查看日志了。
通过GeoIP展示用户分布
我在Logstash的filter插件部分进行了GeoIP相关的配置,现在我演示下将用户的分布按照地图展示出来。Kibana的图形化可谓丰富多姿,其他的就交给读者自己探索了。
1. 点击左侧导航栏“Dashboard”–>“Create new dashboard”
2. 点击页面中间的“add”标签,如果之前在Visualize中创建过地图,可以通过搜索名字添加,没有的话,需要先在Visualize中创建
3. 点击“Add new Visualization”,选择Maps下的“Coordinate Map”
4. 选择我们需要创建地图的索引“logstash-nginx”
5. 点击“Geo Coordinates”,选择“Geohash”
6. 点击“Options”调整颜色及图例说明位置
7. 点击页面右上角的“Save”按钮,创建完成
安全
细心的读者朋友可能已经发现了,登录kibana的时候,页面并没有验证功能,任何能访问Kibana地址的人,都能查询日志,这对我们来说是不可接受的。
改进方法:
1. X-Pack:Kibana本身不提供认证机制,需要通过X-Pack插件来实现。X-Pack是付费插件,可以申请License获取一年期的免费试用。
2. Nginx:通过htpasswd创建用户及密码,进行Kibana认证。
结尾
ELK Stack包含的功能太多了,这里只介绍了些常用功能,足以应付日常所需,更多功能,还需深入探索。
2019你该掌握的开源日志管理平台ELK STACK的更多相关文章
- 你必须知道的容器日志 (2) 开源日志管理方案 ELK
本篇已加入<.NET Core on K8S学习实践系列文章索引>,可以点击查看更多容器化技术相关系列文章.上一篇<你必须知道的容器日志(1)>中介绍了Docker自带的log ...
- 集中式日志分析平台 - ELK Stack - 安全解决方案 X-Pack
大数据之心 关注 0.6 2017.02.22 15:36* 字数 2158 阅读 16457评论 7喜欢 9 简介 X-Pack 已经作为 Elastic 公司单独的产品线,前身是 Shield, ...
- Go语言学习之13 日志管理平台开发
主要内容: 1. ElasticSearch介绍与使用2. kibana介绍与使用 1. ElasticSearch安装 详见上节内容2. kibana安装 (1) 下载ES,下载地址:https:/ ...
- 基础架构之日志管理平台搭建及java&net使用
在现代化的软件开发流程中,日志显得非常的重要,不可能再零散的游离在各个项目中,等查看日志的时候再登录服务器去到特定的目录去查看,这显然很繁琐且效率低下,所有整合一套日志管理平台,也显得非常重要,这篇文 ...
- 快速搭建ELK集中化日志管理平台
由于我们的项目是分布式,服务分布于多个服务器上,每次查看日志都要登录不同服务器查看,而且查看起来还比较麻烦,老大让搭一个集中化日志管理的东西,然后就在网上找到了这个东西ELK ELK就是elastic ...
- ES 集中式日志分析平台 Elastic Stack(介绍)
一.ELK 介绍 ELK 构建在开源基础之上,让您能够安全可靠地获取任何来源.任何格式的数据,并且能够实时地对数据进行搜索.分析和可视化. 最近查看 ELK 官方网站,发现新一代的日志采集器 File ...
- 集中式日志分析平台 Elastic Stack(介绍)
一.ELK 介绍 二.ELK的几种常见架构 >>ELK 介绍<< ELK 构建在开源基础之上,让您能够安全可靠地获取任何来源.任何格式的数据,并且能够实时地对数据进行搜索.分析 ...
- Logstash 2.0.0 beta2 发布,开源日志管理
Logstash 是一个应用程序日志.事件的传输.处理.管理和搜索的平台.你可以用它来统一对应用程序日志进行收集管理,提供 Web 接口用于查询和统计. Logstash 现在是 ElasticSea ...
- kettle系列-我的开源kettle管理平台[kettle-manager]介绍
kettle管理工具 专门为kettle这款优秀的ETL工具开发的web端管理工具. 项目简介 kettle作为非常优秀的开源ETL工具得到了非常广泛的使用,一般的使用的都是使用客户端操作管理,但问题 ...
随机推荐
- sql行转列实例
select gh ,xm , max(A.bz) as bz , max(A.jcz) as jcz , max(A.dl) as dl , max(A.czzx) as czzx , max(A. ...
- webpack配置之代码优化
前面的话 前面介绍了webpack的基本配置,本文将详细介绍webpack中关于代码优化的配置 打包公共代码 CommonsChunkPlugin 插件,是一个可选的用于建立一个独立文件(又称作 ch ...
- Vasya and a Tree CodeForces - 1076E(线段树+dfs)
I - Vasya and a Tree CodeForces - 1076E 其实参考完别人的思路,写完程序交上去,还是没理解啥意思..昨晚再仔细想了想.终于弄明白了(有可能不对 题意是有一棵树n个 ...
- ES6字符串操作
讨论字符串操作之前,我们先来了解一下Unicode 编码的由来,因为Js中的字符串就是一系列Unicode码的集合. 我们都知道,世界上存在着各种各样的语言,汉语,英语,日语等,相对应的,也就存在各种 ...
- RPM包定制
概述 问题:当领导给你 100 台已经安装好系统的服务器,然后让优化,让你提出一个快速部署方案.解答: 1.tar 打包 先编译安装 打包-->分发-->解包(比如 mysql 打包后直接 ...
- .net Json 反序列化时,属性带点
.net Json 反序列化时,属性带点 使用[JsonProperty("xxx.xxx")] static void Main(string[] args) { string ...
- kibana get 查询失效
kibana版本:5.4 在使用kibana 查询数据时,如果我们根据数据id 来获得一条数据,写法 get 索引名称/类型名称/文档主键 如:get testindex/testtype/01 这样 ...
- Codeforces734 E. Anton and Tree
传送门:>Here< 题意:给出一颗树,节点不是黑色就是白色,每次可以将一个颜色相同的块变颜色,问最少变几次才能让其变为同色 解题思路: 我们考虑由于每一次都是把同样颜色的色块进行变色,因 ...
- Iroha and a Grid AtCoder - 1974(思维水题)
就是一个组合数水题 偷个图 去掉阴影部分 把整个图看成上下两个矩形 对于上面的矩形求出起点到每个绿点的方案 对于下面的矩形 求出每个绿点到终点的方案 上下两个绿点的方案相乘后相加 就是了 想想为什么 ...
- ubuntu 16.04 主题美化及终端美化
如果你使用的是图形界面,你会发现ubuntu默认的界面真是丑的一批,所以简单美化一下: 1.安装unity-tweak-tool: sudo apt-get install unity-tweak-t ...