kubernetes logging fluent-bit deamonset

여기서는 fluent-bit deamonset 설정하면서 만난 문제점을 간단히 공유하고자 합니다.

fluent-bit deamonset 설정

GitHub – fluent/fluent-bit-kubernetes-logging: Fluent Bit Kubernetes Daemonset

위 리포지토리를 참고해서 fluentbit 을 설치합니다.

rbac 설정

먼저 RBAC for fluentbit 을 설정합니다.0

kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-service-account.yaml
$ kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role.yaml
$ kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role-binding.yaml

원하는 logging system 선택(elastic search)

로그를 보내고 싶은 저장소를 선택합니다. 여기서는 elasticsearch.

  • Fluent bit configmap 생성
 kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/output/elasticsearch/fluent-bit-configmap.yaml
  • Fluent bit elastic search Daemonset 생성

0FLUENT_ELASTICSEARCH_HOSTFLUENT_ELASTICSEARCH_HOST 값을 각자의 시스템에 맞게 변경하고 배포합니다.

kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/output/elasticsearch/fluent-bit-ds.yaml

그리고 각 데몬셋의 로그를 확인합니다.!!

그러면 elasticsearch에 로그를 보낼 때마다 에러 발생….

에러

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
  labels:
    k8s-app: fluent-bit
data:
  # Configuration files: server, input, filters and output
  # ======================================================
  fluent-bit.conf: |
    [SERVICE]
        Flush         1
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf
        HTTP_Server   On
        HTTP_Listen   0.0.0.0
        HTTP_Port     2020

    @INCLUDE input-kubernetes.conf
    @INCLUDE filter-kubernetes.conf
    @INCLUDE output-elasticsearch.conf

  input-kubernetes.conf: |
    [INPUT]
        Name              tail
        Tag               kube.*
        Path              /var/log/containers/*.log
        Parser            docker
        DB                /var/log/flb_kube.db
        Mem_Buf_Limit     5MB
        Skip_Long_Lines   On
        Refresh_Interval  10

  filter-kubernetes.conf: |
    [FILTER]
        Name                kubernetes
        Match               kube.*
        Kube_URL            https://kubernetes.default.svc.cluster.local:443
        Merge_Log           On
        K8S-Logging.Parser  On

  output-elasticsearch.conf: |
    [OUTPUT]
        Name            es
        Match           *
        Host            ${FLUENT_ELASTICSEARCH_HOST}
        Port            ${FLUENT_ELASTICSEARCH_PORT}
        Logstash_Format On
        Retry_Limit     False

  parsers.conf: |
    [PARSER]
        Name   apache
        Format regex
        Regex  ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
        Time_Key time
        Time_Format %d/%b/%Y:%H:%M:%S %z

    [PARSER]
        Name   apache2
        Format regex
        Regex  ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
        Time_Key time
        Time_Format %d/%b/%Y:%H:%M:%S %z

    [PARSER]
        Name   apache_error
        Format regex
        Regex  ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$

    [PARSER]
        Name   nginx
        Format regex
        Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
        Time_Key time
        Time_Format %d/%b/%Y:%H:%M:%S %z

    [PARSER]
        Name   json
        Format json
        Time_Key time
        Time_Format %d/%b/%Y:%H:%M:%S %z

    [PARSER]
        Name        docker
        Format      json
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L
        Time_Keep   On
        # Command      |  Decoder | Field | Optional Action
        # =============|==================|=================
        Decode_Field_As   escaped    log

    [PARSER]
        Name        syslog
        Format      regex
        Regex       ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
        Time_Key    time
        Time_Format %b %d %H:%M:%S

위에가 fluent bit의 기본 config 내용입니다.

기본적으로 kubernetes 에 pod 를 run 한다는 의미는 docker container 를 실행한다는 의미입니다.

그 말인 즉, docker log 는 항상 발생한다는 이야기…

제가 본 에러는 elasticsearch 의 timestamp
PARSER 부분의 docker 설정에서 parsing 되는 timestamp와의 충돌에서 생기는 문제였습니다.

[PARSER]
        Name        docker
...
        Time_Keep   Off
...

위 부분의 Time_KeepOn 에서 Off 로 변경해주고 다시 Daemonset을 실행합니다.

kubectl apply -f fluent-bit-configmap.yaml
kubectl delete -f fluent-bit-ds.yaml
kubectl apply -f fluent-bit-ds.yaml

이제 정상적으로 ES로 로그가 보내지는 것을 확인합니다.

실행 확인

pod의 로그를 봐서 fluent-bit 의 정상 동작 여부를 확인해도 되지만,

참고로 prometheus 가 kubernetes cluster 에 이미 설정되어 있다면,

annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "2020"
        prometheus.io/path: /api/v1/metrics/prometheus

데몬셋의 위의 annotations 설정으로 prometheus가 자동으로 fluent-bit 타켓을 찾아서 fluent-bit 의 메트릭을 수집합니다.

따라서, prometheus dashboard 에서 fluentbit_output_errors_total 이 메트릭을 확인하면 error 가 나고 있는지 아닌지 간단히 확인 가능합니다.

Kong api gateway 설정 – 2

Kong api gateway 설정 – 2

여기서는 플러그인 활성화 하고 적용 및 테스트 하는 과정을 기록합니다.

첫번째 내용은 여기에..
Kong api gateway 설정 – 1 | 9to5의 개발하면서 겪은 경험

인증 auth


key auth

이제 인증 플러그인을 활성화 시켜보겠습니다.

Plugins – Key Authentication | Kong – Open-Source API Management and Microservice Management

Enable auth plugin on the service

curl -X POST http://localhost:8001/services/mocha/plugins \
  --data "name=key-auth"

Enable auth plugin on the routes

Routes ID 가 필요합니다.
ID 확인이 필요하면,

curl http://localhost:8001/routes

플러그인 활성화

curl -X POST http://localhost:8001/routes/cbaf40b1-196f-4e2b-9b47-b39d84c681a4/plugins \
    --data "name=key-auth" 

Create a Consumer

curl -X POST http://localhost:8001/consumers/ \
  --data "username=admin"

Create a Key

curl -X POST http://localhost:8001/consumers/admin/key-auth

Output:

{"id":"7026e178-9ab7-4bcc-a76a-2ab6b023b84d","created_at":1531989664000,"key":"fqS0sTQ9jM9qjlHNapnzFTJWfI5KGo8P","consumer_id":"69f5420a-6712-4ca2-8bc9-dcc964ebb4e4"}

이제 키 생성이 완료됐습니다.
위에서 등록한 api server 를 다시 호출 해봅시다.

curl -i -X GET \
  --url http://localhost:8000/ \
  --header 'Host: mocha1'

Output:

HTTP/1.1 401 Unauthorized
Date: Thu, 19 Jul 2018 08:43:33 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Server: kong/0.13.1

{"message":"No API key found in request"}

이제 plugin 이 enable 되어서 키 없이는 api 가 호출 되지 않습니다.

키를 넣고 다시 호출 합니다.

curl -i -X GET \
  --url http://localhost:8000/ \
  --header 'apikey: fqS0sTQ9jM9qjlHNapnzFTJWfI5KGo8P' \
  --header 'Host: mocha1'

Response code 200 확인 했습니다.

ldap

Plugins – LDAP Authentication | Kong – Open-Source API Management and Microservice Management

curl -X POST http://localhost:8001/routes/cbaf40b1-196f-4e2b-9b47-b39d84c681a4/plugins \
    --data "name=ldap-auth"  \
    --data "config.hide_credentials=true" \
    --data "config.ldap_host=ldap.example.com" \
    --data "config.ldap_port=389" \
    --data "config.start_tls=false" \
    --data "config.base_dn=dc=example,dc=com" \
    --data "config.verify_ldap_host=false" \
    --data "config.attribute=cn" \
    --data "config.cache_ttl=60" \
    --data "config.header_type=ldap

Allow multiple authentication methods per API and/or Consumer · Issue #590 · Kong/kong · GitHub

Kong의 ldap plugin 은 현재까지는 bind_dn을 입력할 수 가 없습니다.

아래 처럼 PR이 들어가 있는거 같긴 한데… 진행되고 있진 않는 듯합니다.
ldap_auth add bind+search feature by daurnimator · Pull Request #3093 · Kong/kong · GitHub

회사 ldap은 거의 bind_dn 을 사용할 것으로 보기 때문에 과감히 버려줍시다…

로그 logging

syslog 나 tcp-log 는 docker 상태에서는 동작하지 않는 것으로 보입니다.

해서, file-log plugin 을 enable해서 사용하는게 가장 간단한 것으로 보입니다.

file-log

file-log를 사용하기 위해서는 nginx 실행시 user 를 root 로 하는 것이 가장 간단 합니다. 아니면 아래의 에러를 보기 쉽상…..

[error] 76#0: [lua] handler.lua:56: [file-log] failed to open the file: Permission denied, context: ngx.timer, client: xxx.xxx.xxx.xxx, server: 0.0.0.0:8000

첫 번째 글에서 kong 을 docker 로 실행할 때 몇 개의 environment variables 이 추가 되어야합니다.

docker run -d --name kong \
    --link kong-database:kong-database \
    -e "KONG_DATABASE=postgres" \
    -e "KONG_PG_HOST=kong-database" \
    -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
    -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
    -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
    -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
    -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
    -e "KONG_ADMIN_LISTEN_SSL=0.0.0.0:8444" \
    -e "KONG_LOG_LEVEL=info" \
    -e "KONG_NGINX_USER=root" \
    -p 8000:8000 \
    -p 8443:8443 \
    -p 8001:8001 \
    -p 8444:8444 \
    kong:0.14.0-alpine

위 명령대로 다시 kong 을 실행하면 모든 로그가 docker logs 로 리다이렉트 되고, file-log를 활성화 시킬 수 있는 요건을 갖추게 됩니다.

plugin 활성화

  • services
curl -X POST http://localhost:8001/services/c5414169-73f8-4220-881a-0fb09a12973a/plugins \
    --data "name=file-log"  \
    --data "config.path=/dev/stdout"
  • plugins
curl -X POST http://localhost:8001/routes/a57bf14c-4c77-4990-8709-fb94ce006d2b/plugins \
    --data "name=file-log"  \
    --data "config.path=/dev/stdout"

로그 확인

docker logs -f --tail 100 kong

위 명령어로 도커 로그를 확인하면 이제 정상적으로 api 호출시에 로그가 찍히는 것을 확인 할 수 있습니다.