:>/dev/null

ラガードエンジニアの不撓不屈の精神/unlearning/go beyond

apacheのaccess_logをjson化しtd-agentで集約サーバへ収集する

apacheaccess_logを構築した集約サーバへ収集する必要があり、そのついでに解析しやすいようjson化設定したのでメモ。
apacheログをjson化する要件が発生した場合汎用性がありそうなので、調査・対応を下記へ記載しておく。

  • 以下構築要件
    • apacheaccess_logを集約サーバへ収集する
    • 集約サーバで解析しやすいようjson形式で送信
    • td-agentを使用し、送信時でのformatで正規表現指定しない

apacheのconfファイルにjsonフォーマット指定追加

# vi http.conf
// フォーマット指定
LogFormat "{\"timestamp\":\"%{%Y-%m-%dT%H:%M:%S}t.%{msec_frac}t%{%z}t\", \"remote_ip\":\"%a\", \"port\":%{local}p, \"local_ip\":\"%A\", \"httpHost\":\"%v\", \"reqTime\":\"%T\", \"reqStatus\":%>s, \"protocol\":\"%H\", \"bytes_sent\":%O, \"bytes_received\":%I, \"reqTime\":%D, \"file_path\":\"%f\", \"query\":\"%q\", \"method\":\"%m\", \"reqURL\":\"%U\", \"request\":\"%r\", \"ua\":\"%{User-Agent}i\", \"referrer\":\"%{Referer}i\"}" json

//カスタムログにてログ出力
CustomLog /usr/local/apache2/logs/test.com_ssl_access.443.json.log json

以下td-agent2環境fluentd v0.12以降のバージョン向けの設定例

# vi apache.access.conf
<source>
  @id in_apache_http_com_json
  @type   config_expander
   <config>
    @type tail
    path /usr/local/apache2/logs/test.com_ssl_access.443.json.log
    pos_file /var/log/td-agent/in.apache.com.access.443.json.pos

    read_from_head true
    keep_time_key true
    refresh_interval 1s
    format json
    time_key  time
    time_format %Y-%m-%dT%H:%M:%S
    keep_time_key true

    tag test.apache.access
    @label @testserver
  </config>
</source>

後は、上記ファイルをtd-agent.confで@includeし、送信設定(forward)すれば送信される。
※必要に応じてローテート設定行う

# vi /etc/logrotate.d/apache
/usr/local/apache2/logs/*.log {
    daily
    rotate 30
    compress
    missingok
    notifempty
    sharedscripts
    postrotate
        /usr/local/apache2/bin/apachectl graceful > /dev/null 2>/dev/null || true
    endscript
}

ref)

System 5 Admins 0: Apache Access Logs to JSON

カスタムログ形式 - ログのストリーム配信 | Fastly Help Guides

shで日時バックアップscript

shellで日時バックアップ処理を行う必要があった為、設定時メモ。

  • 以下構築条件
    • デイリーバックアップ
    • 圧縮必須
    • suffixが日時のみ処理
    • 開始・終了時刻が記載

本条件でshell scriptを書いた

#!/bin/sh

set -e

readonly SCRIPT_NAME=${0##*/}
readonly HISTORY=/root/logrotate/history.log

LOG_DATE_FORMAT(){
 echo `date '+%Y/%m/%d %H:%M:%S %a'`
}

echo  `LOG_DATE_FORMAT` `hostname -s` ${SCRIPT_NAME}[$$]: START >> ${HISTORY}


#: "テスト" && {
#    echo 今日の日付 … $(date +'%Y-%m-%d %H:%M')
#    echo 更新日が1日以上過去のファイルを探す: &&
#        find /data/td-agent/ -type f -mtime +0  -regextype posix-basic -regex ".*[0-9]\{4\}[0-9]\{1,2\}[0-9]\{1,2\}"
#        find /data/td-agent/ -not -name "*.gz" -type f -mtime +0  -regextype posix-basic -regex ".*[0-9]\{4\}[0-9]\{1,2\}[0-9]\{1,2\}.*"
#    echo 更新日が2日以上過去のファイルを探す: &&
#        find /data/td-agent/ -not -name "*.gz" -type f -mtime +1
#    echo 更新日が3日以上過去のファイルを探す: &&
#        find /data/td-agent/ -not -name "*.gz" -type f -mtime +2
#}


/bin/find /var/td-agent/store/ -not -name "*.gz" -type f -mtime +0 -regextype posix-basic -regex '.*[0-9]\{4\}[0-9]\{1,2\}[0-9]\{1,2\}' -exec gzip {} \;
/bin/find /var/td-agent/store/ -name "*.gz" -type f -mtime +1 -exec rm {} \;

echo  `LOG_DATE_FORMAT` `hostname -s` ${SCRIPT_NAME}[$$]: STOP >> ${HISTORY}

exit 0

後は、cronなりで起動設定すればバックアップされる。

td-agentのbuffer_path重複障害

td-agentを起動した場合、buffer_path指定でエラー発生し、嵌ったので設定時メモ。
sourceディレクティブが複数あり、各tagを指定してサーバへ送信している環境。

  • 以下構築条件
    • 各sourceディレクティブでtag指定
    • sourceディレクティブでtag名重複は無い
    • サーバ側で他へforwardする
    • buffer_pathの出力先は他と共用は無い
    • buffer_path出力先に{tag}.*を指定

■現象
本条件でtd-agent.logに以下エラーが発生する

2018-07-12 10:06:32 +0900 [error]: Cannot output messages with tag 'www.nginx.access.80-json'
2018-07-12 10:06:33 +0900 [error]: failed to configure sub output copy: Other 'secure_forward' plugin already use same buffer_path: type = secure_forward, buffer_path = /var/td-agent/buffer/secure-forward_www.nginx.access.80-json.*

tag名に重複無く「already use same buffer_path」となる原因不明。

■原因
td-agentの起動owner(td-agent)に起因する事象

# ps aux|grep ruby
td-agent 10382  0.0  0.6 217136 24136 ?        Sl   18:36   0:00 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config --group td-agent --daemon /var/run/td-agent/td-agent.pid
td-agent 10385  0.0  1.5 257184 59076 ?        Sl   18:36   0:01 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config --group td-agent --daemon /var/run/td-agent/td-agent.pid

■対応
td-agentの起動owner(root)を変更

#vi /etc/sysconfig/td-agent
TD_AGENT_USER=root
TD_AGENT_GROUP=root

td-agentの起動ownerを確認

# ps aux|grep ruby
root     13127  0.0  0.6 217136 24156 ?        Sl   15:07   0:00 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config --group root --daemon /var/run/td-agent/td-agent.pid
root     13130  0.2  2.4 306384 97564 ?        Sl   15:07   0:12 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config --group root --daemon /var/run/td-agent/td-agent.pid

正常にownerが変更され、td-agent.logにエラー発生してない事を確認。
logに記載されている内容から原因調査→対応まで嵌ったのでここに記載しておく。

fluentdの複数sourceディレクティブで各label毎に特定record追加

fluent(td-agent)を使用したlabel切り分けで各label毎にrecord追加する対応が発生したので設定時でのメモ。
sourceディレクティブが複数あり、送信時に特定recordを追加して送りたい場合に有効。
汎用性がありそうな為、調査・対応を下記へ記載しておく。

  • 以下構築要件
    • sourceディレクティブを各label名にて割り当
    • 各label毎に特定recordを追加

以下td-agent2環境fluentd v0.12以降のバージョン向けの設定例

<source>
  @id in_redis_server
  @type tail
  @label @redis-server
  read_from_head true
  path /var/log/redis/redis.log
  pos_file /var/log/td-agent/redis.pos
  format none
  message_key log_line
  tag test.redis.log
</source>

<source>
  @id in_redis_sentinel
  @type tail
  @label @redis-sentinel
  read_from_head true
  path /var/log/redis/sentinel.log
  pos_file /var/log/td-agent/redis.sentinel.pos
  format none
  message_key log_line
  tag test.redis.log
</source>

<label @redis-server>
  <match>
    @type relabel
    @label @testlabel
  </match>
</label>

<label @redis-sentinel>
  <filter **>
    @type record_transformer
    <record>
      loginfo.subgroup testgroup
      input_tag ${tag}
      last_tag ${tag_parts[-1]}
      redis_type sentinel
    </record>
  </filter>
  <match>
    @type relabel
    @label @testlabel
  </match>
</label>  

■出力イメージ

2018-06-29T18:17:41+09:00    test.redis.log  {"log_line":"testmessage","loginfo.subgroup":"testgroup","input_tag":"test.redis.log","last_tag":"log","redis_type":"sentinel","loginfo.hostname":"testhost","loginfo.group":"groupname"}

正常にrecord追加出来た。
条件分岐して、個別に情報追記を行う場合に便利に使えそうではある。

Zabbix で fluentd のbuffer・キュー等の項目を監視する

fluent(td-agent)サーバ構築時、buffer等の監視設定時のメモ。
Buffer溢れ、転送先サーバ接続不可などの検知に使用出来ればと考えた。
設定方法を下記へ記載しておく。

  • 以下監視項目
    • buffer_queue_length -> (バッファーに蓄積されているキューの数)buffer_queue_limitで設定した値を超えるとログデータがロストするので注意
    • buffer_total_queued_size -> (バッファーに蓄積されている合計サイズ)buffer_queue_limit×buffer_chunk_limitの値を超えるとログデータがロストするので注意
    • retry_count -> ( 再送を試みた回数)retry_limitで設定した回数を超えるとバッファーがリセットされるので注意
    • dirused -> ( フォルダ容量)Bufferに指定されているフォルダ総容量
    • bufnum -> ( フォルダファイル数)Bufferに指定されているフォルダファイル数
    • filetime -> ( フォルダ日時)Bufferに指定されているフォルダに保存されているもっとも古いファイルスタンプ

■以下、zabbixのuserparameter設定

# cat /etc/zabbix/zabbix_agentd.d/userparameter_fluentd.conf
##
# $1: 24220 or 24221 or 24222
# $2: buffer_queue_length or buffer_total_queued_size or retry_count
#
UserParameter=fluentd.queue.get[*],curl -s "localhost:$1/api/plugins.json" | /usr/bin/jq ".plugins[] | .$2" | sort -nr |grep -v null | head -n1
#UserParameter=fluentd.queue.get[*],/root/fluentd.queue.get.sh $1 $2

# cat /etc/zabbix/zabbix_agentd.d/userparameter_td-agent.conf
UserParameter=td-agent.dirused[*],du -bs $1 | cut -f1 2>/dev/null
UserParameter=td-agent.bufnum[*],ls -lt $1 2>/dev/null | wc -l
UserParameter=td-agent.filetime[*],ls -trl --time-style='+%s' $1 | head -n2 | sed -e '1d' | sed -E 's/\s+/,/g' | cut -d, -f6 2>/dev/null
UserParameter=td-agent.chunknum[*],ls /data/buffer/secure-forward_tag.* |wc -l

また、過去に別途shファイルを起動した形式でしたが上記にまとめた。

# cat /root/fluentd.queue.get.sh
#!/bin/bash

##
# $1: 24220 or 24221 or 24222
#
PORT=$1

##
# $2: buffer_queue_length or buffer_total_queued_size or retry_count
#
VALUE=$2

curl -s "localhost:${PORT}/api/plugins.json" | /usr/bin/jq ".plugins[] | .${VALUE}"

後は、zabbixの管理画面でよしなに設定すれば監視される。

fluentdの一時的labelの書き換え

fluent(td-agent)を使用したlabel切り分けで一時的設定→送信時変更する対応が発生したので設定時でのメモ。
sourceディレクティブが複数あり、送信時にsubgroup化して送りたい場合に有効。
sourceディレクティブにsubgroup機能が無い為、送信時のlabelディレクティブ時に追加。
汎用性がありそうな為、調査・対応を下記へ記載しておく。

  • 以下構築要件
    • sourceディレクティブで一時的に送信時のグループ名をlabel割り当
    • 一時的割り当てしたグループ名にtd-agent送信時のsubgroupを追加
    • labelを本来設定する名称へ変更

以下td-agent2環境fluentd v0.12以降のバージョン向けの設定例

<source>
  @id test_id
  @type   config_expander
  <config>
    @type tail
    @label @testgroup
    read_from_head true
    path {log_path}.log
    pos_file {posfilepath}.pos
    format multiline
    format_firstline /^\[\d{4}.\d{2}.\d{2}/
    format1 /^\[(?<time>\d{4}.\d{2}.\d{2}\s\d{2}:\d{2}:\d{2})\]\s(?<log_line>.*)/
    message_key log_line
    tag testtag
  </config>
</source>

<label @testgroup>
  <filter **>
    @type record_transformer
    <record>
      loginfo.subgroup testgroup
    </record>
  </filter>
  <match>
    @type relabel
    @label @testlabel
  </match>
</label>

後は、送信時のmatchディレクティブでよしなに設定すれば送信される。

ホスト側からリモートコマンド実行

ansibleのplaybookを使用し、ホスト側から配下内に複数クライアントへ特定コマンドを実行する
* 下記サンプルはmysqlのerror,slowに一括でコマンド実行しログ出力させる。
* td-agent等使用し、ログ収集行う場合のテスト出力で便利

# cat hosts
[hostlist]
hosta ansible_host=172.27.1.1
hostb ansible_host=172.27.1.2
hostc ansible_host=172.27.1.3

# cat test.yml

---
- hosts: hostlist
  become: yes
  become_method: su
  become_user: root
  roles:
     - td-agent
  tasks:

    - name: check mysql errlog
      shell: echo "2018-04-05T01:10141.161639+09:00 0 [Warning] "tables_priv" entry "user mysql.session@localhost" ignored in --skip-name-resolve mode." >> /data/mysql/{{ inventory_hostname }}.errlog

    - name: check mysql slow
      shell: mysql -u[username] -p[passwd] -e "select sleep(2);"

■ 実行
* -C:テスト実行
* -l:hosts内の特定ホストのみ実行

# ansible-playbook -i hosts -u [username] -l hosta,hostb --ask-pass --ask-become-pass test.yml --diff -C