IBM Cloud Private(ICP)でコンテナログをsyslogに転送する - logstash差替編

# 目的

Kubernetes(k8s)は大量のコンテナが複数のノードで稼働するので、問題判別、監査、あるいは分析のために、分散したログをうまく集約する仕組みが必要です。

IBM Cloud Private(ICP)はそのために標準でELKスタックとFilebeatがインストールされ稼働しています。コンテナ内のアプリが標準出力・標準エラー出力にログデータを出力すると、それはホストOSの/var/log/containers/や/var/log/pods/以下に保管されます。それをDaemonSetとして常駐しているFilebeatが監視し、Logstashに転送し集約します。LogstashはそれをElasticsearchに転送し、ICPのダッシュボードからKibanaを使ってログの検索や可視化を行うことができます。

問題判別や分析の用途はこれで良いですが、特に商用システムでは、ログをファイルとして一定期間保管したい、ログに特定の文字列が出現したらアラート通知をしたいといった要求があります。最近はそのための便利なクラウドサービスが多くありますが、ICPが想定するオンプレミスの環境では、外部のサービスを利用することが困難なケースが多いため、ログを生ファイルとして保管し、バックアップやログ監視のミドルウェアと組み合わせて使いたいというニーズがあります。

そこで、今回はICP上でコンテナログをsyslogサーバに転送し、ファイルとして保管する方法を検証してみます。

ちなみにICPのPublic版？である[IBM Cloud Container Service](https://www.ibm.com/cloud/container-service)ではCLIを使って簡単にsyslog転送の設定を行うことができます。ご参考まで。

> [Kubernetes Log Forwarding with Syslog](https://www.ibm.com/blogs/bluemix/2017/11/kubernetes-log-forwarding-syslog/)

検証環境
- IBM Cloud Private 2.1.0.2 (Kubernetes 1.9.1)
- CentOS 7 (rsyslogd) 

# 方法

前述のとおり、ICPではコンテナログ→Filebeat→Logstash→Elasticseatchのパスがすでにできています。Logstashにsyslog転送にプラグインを追加し、Logstashから集約済みのログを外部のSyslogサーバに転送する経路を新たに作ります（赤線箇所）。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/241424/54fd0da7-35b2-e569-bb5c-7bc85eff4f9f.png)

# 手順

## Syslogサーバの設定

最寄りのLinuxサーバ等のsyslogdで外部からログを受け付けるようにします。CentOS 7の場合はrsyslogを使っていますので、以下のように通信ポートを開きます。

```
$ sudo vi /etc/rsyslog.conf 
$ModLoad imudp
$UDPServerRun 514
$ModLoad imtcp
$InputTCPServerRun 514

$ systemctl restart rsyslog
```

firewalldを使っている場合は忘れずにポートを開けます。

```
$ firewall-cmd --add-port=514/tcp --permanent
$ firewall-cmd --add-port=514/udp --permanent
$ firewall-cmd --reload
```

念のため疎通確認しておきましょう。

```
[k8sノード] $ logger -n admin -p user.info hogeeeeee

[syslogサーバ] $ tail -f /var/log/message
Apr  8 02:46:54 192.168.11.21 root: hogeeeeee
```

## syslogプラグインに対応したLogstashのビルド

ICP付属のLogstashは残念ながらsyslogプラグインがインストールされていません。そのため、syslogプラグインをインストールした状態のイメージを作成します。以下のRUNを実行するとプラグインをダウンロードしてインストールしてくれます。

```txt:Dockerfile
FROM ibmcom/logstash:5.5.1
RUN /usr/share/logstash/bin/logstash-plugin install logstash-output-syslog
```

ビルドしてレジストリにpushします。今回は諸般の事情でDockerHubを使いましたが、本来はプライベートレジストリを使うのが良いでしょう。

```
$ docker build -t logstash:5.5.1-syslog .
$ docker tag logstash:5.5.1-syslog teruz/logstash:5.5.1-syslog
$ docker login
$ docker push
```

## Logstashコンテナのイメージ差し替え

すでに稼働しているLogstashコンテナのイメージを差し替えます。

```
$ kubectl get deployment logstash -n kube-system -o yaml >deployment.yaml
$ vi deployment.yaml

         # image: ibmcom/logstash:5.5.1
         image: teruz/logstash:5.5.1-syslog

$ kubectl apply -f deployment.yaml
```

## LogstashのConfigMapの変更

この段階ではLogstashの動作は何も変わっていません。ConfigMapにsyslogプラグインの設定を追加する必要があります。以下のように既存のoutputの中にsyslogの設定を追加します。

```
$ kubectl edit configmap logstash-pipeline -n kube-system

    output {
      elasticsearch {
        index => "logstash-%{+YYYY.MM.dd}"
        hosts => "elasticsearch:9200"
      }
      # ここから追加
      syslog {
        host => "Syslogサーバのアドレス"
        port => 514
        sourcehost => "%{kubernetes.namespace}/%{kubernetes.pod}"
        message => "%{log}"
      }
    }
```

## 動作確認

ConfigMapを保存してしばらく待つと（1分くらい？）設定が自動反映され、syslogへの転送が始まります。

```
$ tail -f /var/log/messages
Apr  7 18:14:06 192.168.11.23 kube-system/k8s-master-192.168.11.21 LOGSTASH[-]: I0407 18:14:06.647399       1 wrap.go:42] GET /api/v1/namespaces/kube-system/endpoints/kube-scheduler: (2.707909ms) 200 [[hyperkube/v1.9.1+icp (linux/amd64) kubernetes/d97ba3f/leader-election] 127.0.0.1:58472]
Apr  7 18:14:07 192.168.11.23 kube-system/k8s-master-192.168.11.21 LOGSTASH[-]: 2018-04-07T18:14:07.365731538Z AUDIT: id="98bb4004-bc84-4c72-a9ee-e7f531ffa329" stage="ResponseComplete" ip="127.0.0.1" method="list" user="system:apiserver" groups="\"system:masters\"" as="<self>" asgroups="<lookup>" namespace="<none>" uri="/apis/admissionregistration.k8s.io/v1alpha1/initializerconfigurations" response="200"
Apr  7 18:13:59 192.168.11.23 kube-system/k8s-etcd-192.168.11.21 LOGSTASH[-]: 2018-04-07 18:13:59.430418 W | etcdserver/api/v3rpc: failed to receive watch request from gRPC stream ("rpc error: code = Unavailable desc = stream error: stream ID 11813; CANCEL")
Apr  7 18:14:05 192.168.11.23 kube-system/k8s-mariadb-192.168.11.21 LOGSTASH[-]: lock
Apr  7 18:14:07 192.168.11.23 kube-system/k8s-master-192.168.11.21 LOGSTASH[-]: I0407 18:14:07.390144       1 wrap.go:42] GET /apis/admissionregistration.k8s.io/v1alpha1/initializerconfigurations: (24.433583ms) 200 [[hyperkube/v1.9.1+icp (linux/amd64) kubernetes/d97ba3f] 127.0.0.1:58082]
```

# 注意点

今回はとりあえず出力できるところまでを検証しました。実運用では、ログのフォーマットやフィルタリング等、設計が必要になります。

# 参考

https://www.elastic.co/guide/en/logstash/5.2/plugins-outputs-syslog.html