Observability PipelineのVectorを触ってみた

この記事は [3-shake Advent Calendar 2023](https://qiita.com/advent-calendar/2023/3-shake) 22 日目の記事です！

# はじめに
いきなりですがみなさんログコレクターツールは何を使っていますか？Fluentd, Logstash色々あると思います。私は最近Vectorというツールを知りました。本記事ではVectorについて他のログコレクターツールと比べた時の特色とコンセプト。実際に動かしてみたという内容でお送りしようと思います。

# Vectorとは

https://vector.dev/

- Datadogによって開発が進められているOSSの[Observability Pipelines](https://vector.dev/docs/about/what-is-observability-pipelines/)
- 効率的なメモリ/CPU消費と高いデータスループット
- ログ、メトリクス（ベータ）、トレース（coming soon）を1つのツールで管理できる
- エージェント、アグリゲーターとして動作
- オンザフライでのデータ変換
- Rust製


## Vector Components

https://vector.dev/components/

VectorにはSource, Transform, Sinksの3つのコンポーネントがあります。それぞれ
- **Source**：Vectorがどこからデータをプルするか、またプッシュされたデータをどの様に受信するかを定義します。例：syslog, stdin, docker_logs, s3など
- **Transform**：Vectorによって転送されるデータの変換を定義します。変換はfilterやreduceなどの基本的な操作が用意されていたり、あるいはVRL（Vector独自のDSL）やLuaなどのプログラミング言語を使うことで柔軟に対応することもできます。
- **Sinks**：データの送信先を定義します。データ転送の方法はダウンストリームによって異なります。


## ログコレクタ実践

試しに以下の様なシンプルな構成を考えます
![vector (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778243/d016a5e0-8e29-3875-dcbf-987ebda29e0f.png)


<details><summary>Manifests</summary><div>

vector-agent.yaml

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
        - name: shared-data
          mountPath: /var/log/nginx
      - name: vector-agent
        image: timberio/vector:0.34.0-debian
        volumeMounts:
        - name: vector-agent-config
          mountPath: /etc/vector/
        - name: shared-data
          mountPath: /var/log/nginx
      volumes:
        - name: vector-agent-config
          configMap:
            name: vector-agent-config
        - name: nginx-config
          configMap:
            name: nginx-config
        - name: shared-data
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
```

vector-aggregator.yaml

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vector-aggregator
  labels:
    app: vector-aggregator
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vector-aggregator
  template:
    metadata:
      labels:
        app: vector-aggregator
    spec:
      containers:
      - name: vector-aggregator
        image: timberio/vector:0.34.0-debian
        volumeMounts:
        - name: vector-aggregator-config
          mountPath: /etc/vector/
      volumes:
        - name: vector-aggregator-config
          configMap:
            name: vector-aggregator-config
---
apiVersion: v1
kind: Service
metadata:
  name: aggregator-service
spec:
  selector:
    app: vector-aggregator
  ports:
  - protocol: TCP
    port: 6000
    targetPort: 6000
```

</div></details>

nginx.confにはどのPodのログかわかりやすいように$hostnameを追加しておきます

```plaintext
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$hostname" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
```

vector-agent-config.yaml

```
sources:
  nginx_logs:
    type: file
    include:
      - "/var/log/nginx/*.log"
sinks:
  vector:
    type: vector
    inputs:
      - nginx_logs
    address: "aggregator-service:6000"
```

vector-aggregator-config.yaml

```
sources:
  vector:
    type: vector
    address: "0.0.0.0:6000"
sinks:
  stdout:
    type: console
    inputs:
      - vector
    target: stdout
    encoding:
      codec: "text"
```

vectorのconfはシンプルなので、読めばなんとなく理解ができるレベルのものだと思います。
コンポーネントとそのタイプを指定、タイプ毎に必要なパラメーターを設定すればOKです。各コンポーネント間のデータの受け渡しはinputsに識別子を記述することでできます。
また、ここではyamlで書いていますが、その他にJSONやToml形式で記述することができます。


```
k create configmap vector-agent-config --from-file=vector.yaml=vector-agent-config.yaml
k create configmap vector-aggregator-config --from-file=vector.yaml=vector-aggregator-config.yaml
k create configmap nginx-config --from-file=nginx.conf
k apply -f vector-agent.yaml
k apply -f vector-aggregator.yaml
```

アグリゲーターのログを確認すると各Podのログが集約されて標準出力されていますね

```
k logs vector-aggregator-6489756b9c-wpbr2

10.244.0.5 - - [21/Dec/2023:16:59:48 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.85.0" "nginx-584856b57f-z4s4x" "172.18.0.1"
10.244.0.5 - - [21/Dec/2023:16:59:49 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.85.0" "nginx-584856b57f-kv4vq" "172.18.0.1"
10.244.0.5 - - [21/Dec/2023:16:59:50 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.85.0" "nginx-584856b57f-z4s4x" "172.18.0.1"
10.244.0.5 - - [21/Dec/2023:16:59:51 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.85.0" "nginx-584856b57f-8lnj5" "172.18.0.1"
10.244.0.5 - - [21/Dec/2023:16:59:52 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.85.0" "nginx-584856b57f-kv4vq" "172.18.0.1"
10.244.0.5 - - [21/Dec/2023:16:59:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.85.0" "nginx-584856b57f-kv4vq" "172.18.0.1"
```

次はTransformを追加してnginxのlogをParse、VRLで理解できる形にした後、HTTPステータスコードを200でフィルターしJSONで出力する例です

```
sources:
  vector:
    type: vector
    address: "0.0.0.0:6000"
transforms:
  parse-nginx:
    type: remap
    inputs:
      - vector
    source: |
      . |= parse_nginx_log!(.message,"combined")
  filtered:
    type: filter
    inputs:
      - parse-nginx
    condition:
      type: "vrl"
      source: ".status == 200"
sinks:
  stdout:
    type: console
    inputs:
      - filtered
    target: stdout
    encoding:
      codec: "json"
```

```
{"agent":"curl/7.85.0","client":"10.244.0.5","compression":"172.18.0.1","file":"/var/log/nginx/access.log","host":"nginx-584856b57f-sbt4c","message":"10.244.0.5 - - [21/Dec/2023:18:10:42 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"curl/7.85.0\" \"172.18.0.1\"","referer":"-","request":"GET / HTTP/1.1","size":615,"source_type":"file","status":200,"timestamp":"2023-12-21T18:10:42Z"}
{"agent":"curl/7.85.0","client":"10.244.0.5","compression":"172.18.0.1","file":"/var/log/nginx/access.log","host":"nginx-584856b57f-v8dl6","message":"10.244.0.5 - - [21/Dec/2023:18:10:43 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"curl/7.85.0\" \"172.18.0.1\"","referer":"-","request":"GET / HTTP/1.1","size":615,"source_type":"file","status":200,"timestamp":"2023-12-21T18:10:43Z"}
{"agent":"curl/7.85.0","client":"10.244.0.5","compression":"172.18.0.1","file":"/var/log/nginx/access.log","host":"nginx-584856b57f-v8dl6","message":"10.244.0.5 - - [21/Dec/2023:18:10:45 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"curl/7.85.0\" \"172.18.0.1\"","referer":"-","request":"GET / HTTP/1.1","size":615,"source_type":"file","status":200,"timestamp":"2023-12-21T18:10:45Z"}
```

とまぁこんな感じで比較的簡単に、ログの収集、変換、出力が出来ました。VRLなどは弱化の学習コストがかかるかもしれないですが、LogQLなどのクエリ言語を触ったことがある方にはそれほど抵抗がないのではないでしょうか？また今回はローカルで手軽に触るためクラウドサービスは利用していませんが、いくつかサポートされているので、ぜひお試ししてみてください。


# まとめ

Observability PiplineのVectorを紹介しました。今回はログコレクタについて記載させてもらいましたが、メトリクスやトレースも利用できる（予定）ので、ツール選定で迷っている方は候補として入れてみてはいかがでしょうか？

最後までご覧いただきありがとうございました！