SAIO에 Fluentd 설정하기

Swift를 조금 더 잘 감시하기 위한 방편으로 ElasticSearch를 활용한 로그 모니터링을 사용할 수 있다는 것을 OpenStack Conference 영상으로 배웠다.

이 좋은 내용을 실제로 적용을 해보기로 했다.

1. Logstash -> Fluentd

CNCF 를 좋아하는 나는 Logstash 대신에 Fluentd를 선호한다. 저 동영상의 발표자는 Logstash로 구성을 했기 때문에, Fluentd에 맞게 내가 구성을 바꿔야 했다.

2. Fluentd 설치하기

2.1 File Descriptors 값 변경하기

오류가 안나도록 하는게 좋으니 권장하는 대로 File Descriptor 값을 바꿨다.
일단 ulimit -n 명령어로 현재 값을 확인 했다. 1024라는 값은 작은 값이기에 65536로 변경 하였다.

/etc/security/limits.conf에 아래의 내용을 넣어주었다.

root soft nofile 65536
root hard nofile 65536
* soft nofile 65536
* hard nofile 65536

2.2 커널의 네트워크 값 조정하기

TCP_WAIT의 문제가 발생하여 성능에 영향을 끼친다면 권장한다고 하길래, 나는 그냥 넘겨버렸다.
아래 내용을 /etc/sysctl.conf 에 적용 해달라던데.

net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240    65535

2.3 이제 진짜 설치하기

스크립트 하나로 설치가 가능하단다. Treasure Data 분들은 참으로 친절한것같다.
curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent3.sh | sh

마지막에 나오는 깨알 메시지가 참 귀엽다. “Installation completed. Happy Logging!”

2.4 Fluentd 첫 실행하기

언제나 그렇듯이, process를 시작해 주는것과 자동시작 설정은 세트니까.

sudo systemctl start td-agent.service
sudo systemctl enable td-agent.service

3. Fluentd 설정파일 작성하기

Fluentd 동작을 위해서는 설정파일을 수정해 주어야 한단다. /etc/td-agent/td-agent.conf를 수정했다.

YouTube 동영상에 나온 내용을 Fluentd에 맞게 수정한 것이다.

3.1 변경사항

YouTube 동영상에 나온 LogStash 용의 grok에서 변경한 내용이 하나 있다. “int” 라고 되어있는 부분은 모두 “integer”로 바꿔주었다. Fluentd는 “int”를 인식하지 못해서 “integer” 이라는 단어로 변경해 주어야 했다. 예를들면 %{NUMBER:status_int:int} 이라는 내용이 있는데 이것을 %{NUMBER:status_int:integer} 로 바꿔주었다는 말씀.

한 10군데를 수정하였다.

3.2 Pattern 파일 생성

/etc/td-agent/swift-extras라는 이름으로 custom grok pattern 파일을 생성하였다. 내용은 모두 YouTube 동영상에 나온 내용들을 그대로 가져왔다.

# Swift logs documentation: http://docs.openstack.org/developer/swift/logs.html

# Swift proxy_access.log 'datetime' mapping
SWIFT_PROXY_DATETIME %{MONTHDAY}[/]%{MONTH}[/]%{YEAR}[/]%{HOUR}[/]%{MINUTE}[/]%{SECOND}

# Swift all.log 'datetime' mapping
SWIFT_STORAGE_DATETIME %{MONTHDAY}[/]%{MONTH}[/]%{YEAR}[:]%{TIME}

3.3 /etc/td-agent/td-agent.conf 작성

드디어 설정파일을 작성할 시간이다. 아래와 같이 넣어주면 된다.
이렇게 되면 /var/log/td-agent/td-agent.log에 결과 값을 저장하게 된다.

<source>
  @type tail
  path /var/log/swift/*
  pos_file /var/log/td-agent/swift-logs.pos
  tag swift.*
    <parse>
      @type grok
      custom_pattern_path /etc/td-agent/swift-extras
      <grok>
        pattern %{TIMESTAMP_ISO8601:node_tz} %{HOSTNAME:hostname} %{NOTSPACE:swift_service}\: %{NOTSPACE:client_ip} %{NOTSPACE:remote_addr} %{SWIFT_PROXY_DATETIME:datetime} %{WORD:request_method} %{URIPATHPARAM:request_path} HTTP/%{NUMBER:httpversion} %{NUMBER:status_int:integer} %{NOTSPACE:referer} %{NOTSPACE:user_agent} %{NOTSPACE:auth_token} %{NOTSPACE:bytes_recvd:integer} %{NOTSPACE:bytes_sent:integer} %{NOTSPACE:client_etag} %{NOTSPACE:transaction_id} %{NOTSPACE:headers} %{NUMBER:request_time:float} %{NOTSPACE:source} %{NOTSPACE:log_info} %{NUMBER:request_start_time} %{NUMBER:request_end_time} %{NOTSPACE:policy_index:integer}
      </grok>
      <grok>
        %{MONTH:month} %{MONTHDAY:date} %{TIME:time} %{HOSTNAME:hostname} %{NOTSPACE:swift_service}\: %{IPV4:remote_addr} %{NOTSPACE:dash1} %{NOTSPACE:dash2} \[%{SWIFT_STORAGE_DATETIME:datetime} %{ISO8601_TIMEZONE}\] \"%{WORD:request_method} %{URIPATHPARAM:request_path}\" %{NUMBER:status_int:integer} %{NOTSPACE:content_length:integer} \"%{NOTSPACE:referer}\" \"%{NOTSPACE:transaction_id}\" %{QUOTEDSTRING:user_agent} %{NUMBER:request_time:float} \"%{NOTSPACE:additional_info}\" %{NUMBER:server_pid:integer} %{NOTSPACE:policy_index:integer}
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Pass beginning; %{BASE10NUM:PossibleContainers} possible containers; %{BASE10NUM:PossibleObjects} possible objects
      </grok>
      <grok>
        pattern OBJ_EXP_FINISH \A%{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Pass completed in %{NUMBER:CompletedSeconds}s; %{BASE10NUM:ObjectsExpired} objects expired
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Starting object reconstruction pass.
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Nothing reconstructed for %{NUMBER:Seconds} seconds.
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Object reconstruction complete. \(%{NUMBER:CompletedMinutes} minutes\)
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Begin object audit \"%{WORD:AuditMode}\" mode \(%{WORD:AuditType}\)
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Object audit \(%{WORD:ModeFlag}\) "%{WORD:AuditMode}" mode completed: %{NUMBER:CompletedSeconds}s. Total quarantined: %{BASE10NUM:TotalQuarantined}, Total errors: %{BASE10NUM:TotalErrors}, Total files/sec: %{NUMBER:TotalFilesPerSec}, Total bytes/sec: %{NUMBER:TotalBytesPerSec}, Auditing time: %{NUMBER:TotalAuditingTime}, Rate: %{NUMBER:TotalAuditRate}
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Starting object replication pass.
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: %{BASE10NUM:CurrentRecords}/%{BASE10NUM:TotalRecords} \(%{NUMBER:PercentComplete}\%\) partitions replicated in %{NUMBER:SecondsRunning}s \(%{NUMBER:RecordsPerSecond}/sec, %{NUMBER:SecondsRemaining}s remaining\)
      </grok>
      <grok>
        pattern %{SYSLOGTIMESTAMP} %{HOSTNAME} %{WORD:ServiceType}-%{WORD:JobType}: Object replication complete. \(%{NUMBER:TotalTimeMinutes} minutes\)
      </grok>
    </parse>

</source>
<match swift.**>
  @type stdout
</match>

4. 환경설정

Fluentd가 제대로 동작하기 위해서는 몇 가지 설정이 필요하다.

4.1 grok 플러그인 설치

fluent-plugin-grok-parser를 설치해아 한다.

td-agent-gem install fluent-plugin-grok-parser

4.2 grok 플러그인 버그 해결

Fluentd 1.0.2 버전에는 버그가 존재한다고 한다. 그러므로 내 fluentd 버전과 grok 플러그인의 버전을 확인해 봤다. 아래 두 개의 명령어를 실행했다.

td-agent-gem list | grep fluentd
td-agent-gem list | grep fluent-plugin-grok
[root@saio ~]# td-agent-gem list | grep fluentd
fluentd (1.0.2)
[root@saio ~]# td-agent-gem list | grep fluent-plugin-grok
fluent-plugin-grok-parser (2.1.6)

설정파일을 다 작성하고 systemctl start td-agent 명령어를 입력하면 시작이 되지 않는다. /var/log/td-agent/td-agent.log 파일을 보면 “can’t modify frozen string” 이라는 에러 로그가 남아있는걸 확인할 수 있다.

해결방법은 요 파일을 수정하는 것이다.
/opt/td-agent/embedded/lib/ruby/gems/2.4.0/gems/fluentd-1.0.2/lib/fluent/config/types.rb

수정전:
STRING_TYPE = Proc.new { |val, opts| val.to_s.force_encoding(Encoding::UTF_8) }
수정후:
STRING_TYPE = Proc.new { |val, opts| val.to_s }

4.3 로그파일 퍼미션 설정하기

/var/log/swift의 permission을 잘 확인해야 했다.
디렉토리에는 execute permission을 주었다. chmod 655 /var/log/swift
각각의 파일에는 read permission을 주었다. chmod -R 644 /var/log/swift/

5. FluentD 동작 확인하기

Swift Cluster에 접속을 해서 로그를 발생시켜봤다.
curl -v -H 'X-Auth-Token:AUTH_tk09f6c4ae127745e2a514013717279519' http://127.0.0.1:8080/v1/AUTH_test/saio/

/var/log/td-agent/td-agent.log에 기록이 잘 남는것을 확인 하였다.

2018-03-15 17:34:08.350319231 +0000 swift.var.log.swift.proxy.log: {"node_tz":"2018-03-15T17:34:08.349970+00:00","hostname":"saio","swift_service":"proxy-server","client_ip":"-","remote_addr":"-","datetime":"15/Mar/2018/17/34/08","request_method":"HEAD","request_path":"/v1/AUTH_test/saio%3Fformat%3Djson","httpversion":"1.0","status_int":204,"referer":"-","user_agent":"Swift","auth_token":"-","bytes_recvd":0,"bytes_sent":0,"client_etag":"-","transaction_id":"txc831d584b97c47e0a6136-005aaaae90","headers":"-","request_time":0.0092,"source":"RL","log_info":"-","request_start_time":"1521135248.339988947","request_end_time":"1521135248.349173069","policy_index":0}
2018-03-15 17:34:08.354420621 +0000 swift.var.log.swift.storage3.error: {}
2018-03-15 17:34:08.360284333 +0000 swift.var.log.swift.storage3.log: {}
2018-03-15 17:34:08.360607812 +0000 swift.var.log.swift.storage3.error: {}
2018-03-15 17:34:08.365039360 +0000 swift.var.log.swift.proxy.error: {}
2018-03-15 17:34:08.365264127 +0000 swift.var.log.swift.proxy.log: {"node_tz":"2018-03-15T17:34:08.364121+00:00","hostname":"saio","swift_service":"proxy-server","client_ip":"127.0.0.1","remote_addr":"127.0.0.1","datetime":"15/Mar/2018/17/34/08","request_method":"GET","request_path":"/v1/AUTH_test/saio/%3Fformat%3Djson","httpversion":"1.0","status_int":200,"referer":"-","user_agent":"curl/7.29.0","auth_token":"AUTH_tk09f6c4ae1...","bytes_recvd":0,"bytes_sent":59,"client_etag":"-","transaction_id":"txc831d584b97c47e0a6136-005aaaae90","headers":"-","request_time":0.0357,"source":"-","log_info":"-","request_start_time":"1521135248.327434063","request_end_time":"1521135248.363121033","policy_index":0}

휴우... 참으로 힘든 여정이었다.

6. 참고문헌

Swift의 설치 및 운영에 대한 OpenStack 공식문서다.
OpenStack Docs: Welcome to Swift’s documentation!

“Can’t Modify Frozen String”에 관한 해결방법은 아래 링크에서 찾을수 있었다.
Cannot start td-agent when using grok plugin in config · Issue #1827 · fluent/fluentd · GitHub

Grok 설정에 대한 자료는 아래 링크에서 확보할 수 있었다.
swift-to-elk/elk-server/opt/logstash/extra_patterns at master · swiftstack/swift-to-elk · GitHub

ElasticSearch로 Swift를 모니터링 하는 방법에 대한 OpenStack Conference 영상이다. 이 포스팅을 가능하게한 바로 그 동영상이다.
Monitoring Swift With Elastic Search - YouTube

Photo by Gianluca Zuccarelli on Unsplash