分类 运维 下的文章

jq: shell 脚本中JSON创建、解析工具

0x01 背景

在Linux下开发监控、自动化shell脚本是运维的日常工作之一,JSON是通用且流行的数据交换格式,在Web API中使用很普遍

0x02 问题/诉求

如何在Linux shell脚本中创建与解析JSON呢?

0x03 土办法

1. 创建JSON

变量嵌入

#!/bin/bash
FILENAME="/tmp/xxxx"
curl -i -X POST -H "Content-Type: application/json" -d '{"type": "test", "params":{"item":{"file":"'"$FILENAME"'"}}}' http://192.168.0.1/api
#!/bin/bash
header="Content-Type: application/json"
FILENAME="/tmp/xxxx"
request_body=$(< <(cat <<EOF
{
  "type": "test",
  "params": {
    "item": {
      "file": "$FILENAME"
    }
  }
}
EOF
))
curl -i -X POST -H "$header" -d "$request_body" http://192.168.0.1/api

循环拼接

#!/bin/bash

arr=""
while read var; do
  arr="\"${var}\",${arr}"
done < list

json=`echo ${arr} | sed 's/,$//g'`
echo "[${json}]"

文本内容:
$ cat list
aa
bb
cc
dd
123
r4
t55
5t
gg
1
rgs
g
f
gs

脚本输出:
$ sh t.sh
["gs","f","g","rgs","1","gg","5t","t55","r4","123","dd","cc","bb","aa"]

2. 解析JSON

awk

没有具体尝试过,可参考stackoverflow上的一则回答

0x04 完美解法 —— jq

jq is a lightweight and flexible command-line JSON processor.

jq官网

$ jq -h
jq - commandline JSON processor [version 1.5-1-a5b5cbe]
Usage: jq [options] <jq filter> [file...]

    jq is a tool for processing JSON inputs, applying the
    given filter to its JSON text inputs and producing the
    filter's results as JSON on standard output.
    The simplest filter is ., which is the identity filter,
    copying jq's input to its output unmodified (except for
    formatting).
    For more advanced filters see the jq(1) manpage ("man jq")
    and/or https://stedolan.github.io/jq

    Some of the options include:
     -c        compact instead of pretty-printed output;
     -n        use `null` as the single input value;
     -e        set the exit status code based on the output;
     -s        read (slurp) all inputs into an array; apply filter to it;
     -r        output raw strings, not JSON texts;
     -R        read raw strings, not JSON texts;
     -C        colorize JSON;
     -M        monochrome (don't colorize JSON);
     -S        sort keys of objects on output;
     --tab    use tabs for indentation;
     --arg a v    set variable $a to value <v>;
     --argjson a v    set variable $a to JSON value <v>;
     --slurpfile a f    set variable $a to an array of JSON texts read from <f>;
    See the manpage for more options.

1. 创建JSON

变量嵌入

$ FILENAME="/tmp/xxxx"
$ jq -n -c -M --arg v "$FILENAME" '{"type":"test","params":{"item":{"file": $v}}}'
{"type":"test","params":{"item":{"file":"/tmp/xxxx"}}}

// 去掉 -c 参数可格式化输出json
$ jq -n -M --arg v "$FILENAME" '{"type":"test","params":{"item":{"file": $v}}}'
{
  "type": "test",
  "params": {
    "item": {
      "file": "/tmp/xxxx"
    }
  }
}

循环拼接

$ jq -R -M -c -s 'split("\n")' < list
["aa","bb","cc","dd","123","r4","t55","5t","gg","1","rgs","g","f","gs",""]

数组末尾的空字符串,可以另想办法处理下。

2. 解析JSON

这块可直接学习jq官方教程,由浅入深,讲的很详细。

        jq '.foo'
Input    {"foo": 42, "bar": "less interesting data"}
Output    42

        jq '.foo'
Input    {"notfoo": true, "alsonotfoo": false}
Output    null

        jq '.["foo"]'
Input    {"foo": 42}
Output    42

        jq '.[0]'
Input    [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Output    {"name":"JSON", "good":true}

        jq '.[2]'
Input    [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Output    null

        jq '.[2:4]'
Input    ["a","b","c","d","e"]
Output    ["c", "d"]

        jq '.[2:4]'
Input    "abcdefghi"
Output    "cd"

        jq '.[:3]'
Input    ["a","b","c","d","e"]
Output    ["a", "b", "c"]

        jq '.[-2:]'
Input    ["a","b","c","d","e"]
Output    ["d", "e"]

        jq '.[-2]'
Input    [1,2,3]
Output    2

logstash 修改Gem源为淘宝源后不生效问题

环境:
CentOS 6 x86_64
logstash 1.5.4

问题描述:

/opt/logstash 下的 Gemfile、Gemfile.jruby-1.9.lock 都修改了 source 为 https://ruby.taobao.org,但是执行 /opt/logstash/bin/plugin install logstash-input-beats 还是连接的 https://rubygems.org/ 并且因为地址被墙而无法下载插件,如下图

1457933293336.png

解决办法:

google一番有说用以下命令可以解决,但机器上没有 Ruby 环境,gem 命令不存在,并且 logstash 用的自带的 jruby ,想想应该会配置在某个地方,遂决定探究一下。

gem sources remove https://rubygems.org
gem sources add https://ruby.taobao.org

很自然的想到先全量 grep 下这个地址(字符串),命令如下:

grep -nr 'https://rubygems.org' /opt/logstash/

可以看到找到了很多文件,但 /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash- 开头的都是已经安装的插件目录,可以忽略不看。

剩下的一个个仔细的瞄过去,发现一个比较像的文件/opt/logstash/vendor/jruby/lib/ruby/shared/rubygems/defaults.rb,因为有字符串 DEFAULT_HOST

1457933923101.png

vi 打开文件看一下,开头有以下配置项

1457934120376.png

通过注释 “An Array of the default sources that come with RubyGems” 可以知道,应该就是这个配置文件了。修改两处 https://rubygems.orghttps://ruby.taobao.org :wq保存文件,再次执行 /opt/logstash/bin/plugin install logstash-input-beats 即可成功下载插件并安装。

1457934399274.png

总结

因为 rubygems.org 被墙,logstash 下载安装插件时需要将以下文件中的gems地址https://rubygems.org替换为淘宝源地址https://ruby.taobao.org

/opt/logstash/Gemfile
/opt/logstash/Gemfile.jruby-1.9.lock
/opt/logstash/vendor/jruby/lib/ruby/shared/rubygems/defaults.rb

【翻译】ElasticSearch手册:索引模块-合并(Index Modules » Merge)

官方手册 英文原文:index-modules-merge


合并

在elasticsearch中,每个分片都是一个Lucene索引,每个Lucene索引又被分解成段。段是索引内部的存储元件,索引数据就存在里面,并且存储的位置是永久不变的除非被删除。定期执行分段合并,将分段的数据合并到大段里面,保持索引的大小到极点直到删除。

大量的分段留在Lucene索引里面,意味着较慢的搜索和占用更多的内存。分段合并设计用来减少分段数量,但是合并动作是非常昂贵的,尤其是在低IO环境中,可能会受到存储性能上的限制。

一、合并策略(Policy)

索引合并策略模块用于控制分片中哪个分段要合并。它有几种策略类型,默认的设置为 分层 政策。

1、分层(tiered)

合并后的分段大小大致相等,每层的分段数必须小于所允许的值。这个类似于log_bytes_size的合并策略,但该合并策略能够合并非相邻分段,并且多少分段会被一次性合并取决于该层共允许多少分段,该合并策略也不是从头到尾的合并(即:级联合并)。

这个策略有以下设置:

设置项 简要描述
index.merge.policy.expunge_deletes_allowed 当触发删除操作时,我们只合并这个分段,如果它的删除百分比超过了这个阈值。默认是10.
index.merge.policy.floor_segment 分段的大小四舍五入后比这个值还小,即视为等于(约等于)需要合并的大小。
这是为了防止频繁地刷新微小分段,在一个允许有长尾巴(tail)的索引中。该值默认是:2mb
index.merge.policy.max_merge_at_once 单次合并允许操作的最大分段数。Default is10.
index.merge.policy.max_merge_at_once_explicit 单次合并允许操作的最大分段数,在optimize操作或删除操作( expungeDeletes )的时候。Default is30.
index.merge.policy.max_merged_segment 在正常的合并过程(不显式的优化optimize)中产生的分段的最大大小。此设置是近似值:合并后的段大小的预估值是由被合并分段的大小计算出来的(删除文档补偿百分比) Default is5gb.
index.merge.policy.segments_per_tier 每层所允许的分段数。较小的值意味着更多的合并,但是存在较少的分段。默认:10。请注意,这个值必须 >= max_merge_at_once 不然就会强制执行太多的合并。
index.reclaim_deletes_weight 控制如何积极回收更多的删除合并是好的。值越高,越有利于选择合并回收删除。值为0.0意味着删除不影响合并选择。Defaults to2.0.
index.compound_format 是否让存储使用复合格式. Defaults tofalse.

对于正常的合并,该策略首先计算出(“预算”)在这个索引中允许多少个分段。如果该索引的分段数超出预算,则该策略对分段进行排序通过降低大小(通过排序找到,合并后能删除多的分段),即找到成本最低的合并。合并成本的测量是靠以下三个参数:合并的偏移量,总的合并大小和删除回收的比例(删除后能回收的空间),对合并来说更低的偏移、更小的尺寸和更多的回收删除后的空间是有利的。

如果合并后将产生一个大小大于max_merged_segment的分段,则该策略将合并大小较少的分段,保持合并后的分段大小保持在预算之内。

注意,这可能意味着大量的分片将拥有超过千兆字节的数据,默认的max_merged_segment(5GB)可能会导致在一个索引上有许多分段,并导致搜索变慢。使用索引分段API 可以看到该索引有多少分段,解决方式可以是增大max_merged_segment的值,或者运行索引的(在低峰期)优化请求(optimize)。

2、日志字节大小(log_byte_size)

注意!

该策略已过时
将在 2.0 版本后移除该策略,来支持分层策略(即上节内容)。

一个合并策略,将分段合并成字节大小级别成倍增加的分段,其中每个级别有比合并因素的值更小的分段。每当遇到额外的分段(超出合并因素的上界),所有分段将在等级范围内被合并。

这个策略有以下设置:

Setting Description
index.merge.policy.merge_factor 确定在索引操作的时候,多长时间索引的分段进行合并。数值越小,建立索引时使用较少的RAM,搜索未优化的索引速度较快,但索引速度较慢。较大的值,在索引过程中使用更多的RAM,搜索未优化的索引速度较慢,但索引速度较快。因此,较大的值(大于10),最好的用于批处理创建索引;较小的值(低于10),用于以交互方式保持的索引(即一边索引一边查询)。Defaults to 10.
index.merge.policy.min_merge_size 设置分段的最低级别的下限值。任何低于此大小的分段都被认为是在同一级别上(即使它们的大小差异很大),并且将被合并只要他们有合并的机会。这将有效地截断小分段的“长尾巴”,否则将被创建成一个单一等级。如果你设置过大,它会极大地提高合并成本,在索引创建的过程中(如果你刷新许多小的部分)。Defaults to 1.6mb
index.merge.policy.max_merge_size 设置分段的最大文件大小(衡量段的文件的总字节大小),与其它分段合并后。默认为无限制。
index.merge.policy.maxMergeDocs 确定分段的最大文档数(统计总数),与其它分段合并后。默认为无限制。

3、日志文档(log_doc)

注意!

该策略已过时
将在 2.0 版本后移除该策略,来支持分层策略(即第一节内容)。

一个合并策略,将分段合并成文档数量级别成倍增加的分段,其中每个级别有比合并因素的值更小的分段。每当遇到额外的分段(超出合并因素的上界),所有分段将在等级范围内被合并。

Setting Description
index.merge.policy.merge_factor 确定在索引操作的时候,多长时间索引的分段进行合并。数值越小,建立索引时使用较少的RAM,搜索未优化的索引速度较快,但索引速度较慢。较大的值,在索引过程中使用更多的RAM,搜索未优化的索引速度较慢,但索引速度较快。因此,较大的值(大于10),最好的用于批处理创建索引;较小的值(低于10),用于以交互方式保持的索引(即一边索引一边查询)。Defaults to 10.
index.merge.policy.min_merge_docs 这将有效地截断小分段的“长尾巴”,否则将被创建成一个单一等级。如果你设置过大,它会极大地提高合并成本,在索引创建的过程中(如果你刷新许多小的部分)。Defaults to 1000.
index.merge.policy.max_merge_docs 确定可与其它分段合成的分段的最大文档数量(分段的文档数量统计)。默认为无限制。

二、合并调度(Scheduling)

合并调度(并发合并调度 ConcurrentMergeScheduler)用于控制合并操作的执行,一旦它们需要被执行时(按照合并策略)。

1、并发合并调度(ConcurrentMergeScheduler)

合并操作在独立的线程中执行,当达到最大合并线程数时,后续的合并操作需要等待前面的线程结束才能执行。

该调度程序支持以下设置:

Setting Description
index.merge.scheduler.max_thread_count 合并操作可用的最大线程数。默认是:Math.max(1, Math.min(3, Runtime.getRuntime().availableProcessors() / 2)),这在具有SSD支持时运行良好。如果你的索引运行在旋转盘片驱动器(spinning platter drives),最好把这个设置成 1.

2、串行合并调度(SerialMergeScheduler)

一个合并调度,简单的顺序使用合并线程(阻断触发 合并操作和索引操作)。

配置Apache mod_proxy模块反向代理tomcat,去除tomcat访问的端口号

首先检查apache根目录下的modules目录中是否有mod_proxy.so、mod_proxy_http.so文件,如果没有,说明编译安装apache时没有配置proxy模块。可以参照这篇文章的方法,给已经编译运行的Apache增加mod_proxy模块。

配置完后重启apache生效,此时就已经加载mod_proxy.so、mod_proxy_http.so这两个模块了,可以

grep 'proxy' conf/httpd.conf
检查下。如果有用到AJP或者tomcat负载均衡,则还需编译增加其他模块,这个再自行搜索即可。

然后在apache的conf/vhost/目录中新增一个conf配置文件,可以复制已存的拿来改。如下是我的内容

<VirtualHost *:80>
DocumentRoot /www/domain.com
ServerName www.domain.com
ServerAlias *.domain.com

ProxyRequests Off
ProxyPass /download.html !   #表示改URL不反代
ProxyPass /img !
ProxyPass / http://www.domain.com:8080/MyTomcatApp/
ProxyPassReverse / http://www.domain.com:8080/MyTomcatApp/
</VirtualHost>

参考文章:

http://freeloda.blog.51cto.com/2033581/1301382
https://rvdb.wordpress.com/2012/04/26/reverse-proxying-tomcat-webapps-behind-apache/

Ubuntu sftp配置及上传文件权限问题解决方案

编辑配置文件/etc/ssh/sshd_config

Subsystem sftp /usr/lib/openssh/sftp-server

改成

Subsystem sftp internal-sftp

增加如下内容

Match user ftpuser
ChrootDirectory %h
ForceCommand internal-sftp

多个用户则重复配置上面的三行。

然后修改用户,设定shell为/bin/false,就可以限制用户无法登录SSH只能使用sftp


配置默认上传文件权限

Subsystem sftp internal-sftp -u 0002

增加上面加粗的-u 0002

表示配置sftp中的umask为0002,这样在sftp里新建目录权限就是775(777-002),文件权限664(666-002)

参考文章:

Limiting Access with SFTP Jails on Debian and Ubuntu
How do I set default permissions for SFTP for an Ubuntu Server?
【系统管理】Linux Umask 介绍