Loki + NFS で Kubernetes 上のログ取得

# 目的

ログの監視はシステムを安定稼働させるために重要な項目である。
ログを収集することでシステムトラブルや不審な操作・動作の検知に役立ち、その解決の一助とできる。
今回は Kubernetes 上に helm を用いて Loki をインストールし、Grafana と連携したログの可視化や検索が可能な環境を構築する。

Loki は他のロギングシステムと異なり、ログに関するメタデータのみのインデックスを作成するという考えに基づいて構築されている。
その後、ログデータ自体が圧縮され、S3 や GCS などのオブジェクトストアやローカルのファイルシステムにチャンクとして保存される。
小さなインデックスと高度に圧縮されたチャンクにより、操作が簡素化され、Loki のコストが大幅に削減できるという特徴を持つ。

ログの保存には NFS サーバを用いる。
Loki としては保存先に NFS サーバを用いることは非推奨であるが、Kubernetes 含めすべてローカルの環境を利用するケースでも Loki の動作を確認することを目的に NFS サーバを用いる。

- 第 1 章: この章

NFS をボリュームとして利用し、Loki をインストールする。

- 第 2 章: [Kubernetes + Grafana Loki + Promtail で任意の VM のログ収集する](https://qiita.com/yoshi_1010/items/b58f37bd878f7231a5b1)

Promtail をインストールし、今回インストールした Loki に集約する。

# 構成

Linux Machine (Ubuntu 20.04) を 3 台を使用し、下記の構成で Kubernetes Cluster を構築した。
Kubernetes 上にはすでに Prometheus + Grafana が動作している状態である。
また、Linux Machine (Ubuntu 20.04) を 1 台 NFS サーバとして準備する。

- Kubernetes (Master Node)
	- 10.100.2.220
- Kubernetes (Worker Node)
	- 10.100.2.221
	- 10.100.2.222
- NFS サーバ
    - 10.100.2.170

# 手順

## Helm インストール

- 実行場所
  - Kubernetes (Master Node) 

Kubernetes のパッケージマネージャである Helm をインストールする。

```
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
```

インストールされているか確認する。

```
$ helm version
version.BuildInfo{Version:"v3.12.3", GitCommit:"3a31588ad33fe3b89af5a2a54ee1d25bfe6eaa5e", GitTreeState:"clean", GoVersion:"go1.20.7"}
```

## NFS サーバ構築

- 実行場所
  - NFS サーバ

NFS サーバを構築する。
今回は `/export/nfs` 以下のディレクトリを公開する。

```
sudo apt-get install nfs-kernel-server
sudo mkdir -p /export/nfs
sudo chmod 1777 /export/nfs/.
sudo sh -c 'echo "/export/nfs 10.100.2.0/22(rw,async,no_root_squash) 127.0.0.1/32(rw,async,no_root_squash)" | tee -a /etc/exports'
sudo systemctl restart nfs-server rpcbind
sudo systemctl enable nfs-server rpcbind
```

### NFS サーバの利用

- 実行場所
  - Kubernetes (Master Node) 
  - Kubernetes (Worker Node) 

NFS クライアントにあたるすべてのサーバ上で実行する。

```
sudo apt-get install nfs-common
```

## Dynamic Provisioner

- 実行場所
  - Kubernetes (Master Node) 

NFS 上の領域を Dynamic Provisioner の機能を提供するためのデフォルトの Provisioner がない。
そのため、External Provisioner をインストールする必要がある。
NFS 用の External Provisioner には NFS Ganesha を利用する場合と NFS サブディレクトリを利用する場合がある。

- [NFS Ganesha server and external provisioner](https://github.com/kubernetes-sigs/nfs-ganesha-server-and-external-provisioner#nfs-ganesha-server-and-external-provisioner)
- [Kubernetes NFS Subdir External Provisioner](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner#kubernetes-nfs-subdir-external-provisioner)

今回は NFS サブディレクトリを用いる場合を利用する。

### Helm リポジトリの追加

Helm リポジトリを追加する。

```
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update
```

### インストール

Kubernetes NFS Subdir External Provisioner をインストールする。

```
helm install -n monitoring nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=10.100.2.170 \
--set nfs.path=/export/nfs/loki
```

確認する。

```
$ kubectl get all -n monitoring | grep nfs
pod/nfs-subdir-external-provisioner-7697b6f886-gl5pw         1/1     Running   0          176m
deployment.apps/nfs-subdir-external-provisioner       1/1     1            1           176m
replicaset.apps/nfs-subdir-external-provisioner-7697b6f886       1         1         1       176m
```

## Loki 

- 実行場所
  - Kubernetes (Master Node) 

Loki は多くのコンポーネントマイクロサービスから構築されており、水平方向に拡張可能な分散システムとして実行されるように設計されている。
スケーラブルなインストールには、AWS S3 や Google Cloud Storage などのマネージド オブジェクトストア、または Minio などのセルフホストストアが必要である。

Loki は Single Binary Mode というモードでインストールすることもできる。
Single Binary Mode はファイルシステムストレージで動作するように設計されたモノリシックモードですべてのターゲットを実行するように Loki を構成するモードである。
今回は Single Binary Mode でインストールする。

### Helm リポジトリの追加

Helm リポジトリを追加する。

```
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
```

### 設定ファイル作成

Loki 起動時に使用する設定ファイルを作成する。
gateway.serivce で NodePort を設定することで外部からの接続ポートを開けている。
次の記事ではこのポートに接続し、任意の VM からログを取得できるようにする。

```
cat << _EOF_ > loki-values-single.yaml
loki:
  auth_enabled: false
  containerSecurityContext:
    readOnlyRootFilesystem: false
  podSecurityContext:
    fsGroup: 0
    runAsNonRoot: false
    runAsUser: 0
  commonConfig:
    replication_factor: 1
  storage:
    type: filesystem
  commonStorageConfig:
    type: filesystem
  persistence:
    enabled: true
  rulerConfig:
    storage:
      type: local
singleBinary:
  replicas: 1
  persistence:
    enabled: true
    storageClass: nfs-client
    size: 10Gi
gateway:
  service:
    nodePort: 31001
    type: NodePort
table_manager:
  retention_deletes_enabled: true
  retention_period: 24h
_EOF_
```

### インストール

Loki をインストールする。
インストール時には `--namespace` で Prometehus + Grafana が動作しているネームスペースと同じものを指定する。

```
helm install  --namespace monitoring --values loki-values-single.yaml loki grafana/loki
```

確認する。

```
$ kubectl get all -n monitoring | grep loki
pod/loki-0                                                   1/1     Running   0          3h58m
pod/loki-canary-xcq8z                                        1/1     Running   0          3h58m
pod/loki-gateway-67569c4d49-tgrtg                            1/1     Running   0          3h58m
pod/loki-grafana-agent-operator-85c78d5968-mvszg             1/1     Running   0          3h58m
pod/loki-logs-8crbw                                          2/2     Running   0          3h58m
service/loki                                      ClusterIP   10.104.90.215   <none>        3100/TCP,9095/TCP               3h58m
service/loki-canary                               ClusterIP   10.101.17.20    <none>        3500/TCP                        3h58m
service/loki-gateway                              NodePort    10.109.55.79    <none>        80:31001/TCP                    3h58m
service/loki-headless                             ClusterIP   None            <none>        3100/TCP                        3h58m
service/loki-memberlist                           ClusterIP   None            <none>        7946/TCP                        3h58m
daemonset.apps/loki-canary                               1         1         1       1            1           <none>                   3h58m
daemonset.apps/loki-logs                                 1         1         1       1            1           <none>                   3h58m
deployment.apps/loki-gateway                          1/1     1            1           3h58m
deployment.apps/loki-grafana-agent-operator           1/1     1            1           3h58m
replicaset.apps/loki-gateway-67569c4d49                          1         1         1       3h58m
replicaset.apps/loki-grafana-agent-operator-85c78d5968           1         1         1       3h58m
statefulset.apps/loki                                                   1/1     3h58m
```

### Grafana 連携

ブラウザから Grafana に接続し、インストールした Loki と連携する。

- [Home] > [Addministration] > [Data sources]
- [+ Add new data source]
- [Logging & document databases] の [Loki] 
- [HTTP] 
  - URL: http://loki-gateway
- [Save & test]

上記を実行し、エラーが出なければ問題ない。

- [Home] > [Explore]

Explore からログを確認できるようになる。
下記は Pod 名 loki-0 のログである。

![qiita-loki-log1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735085/cc4193c3-75fb-eec0-8b7a-b953cd50f1c2.png)

# まとめ

今回は NFS サーバ、Kubernetes Cluster すべてをローカル環境に構築し、Loki の動作を試した。
また、Dynamic Provisoner を利用することで事前に PV を準備することなく動的に NFS のボリュームを割り当て、ログの確認ができるようにもした。
Loki 自体はログの集約や検索は可能であるが、ログ収集自体は他のツールを導入する必要がある。
今回は Loki のみをインストールしているため、Kubernetes 上で動作している他の Pod 等のログは収集できていない。
次の記事ではそのツールとして Promtail を導入し、任意の VM や他の Pod 等からもログを収集できるようにもする。

また、現状は Single Binary で動作させており、Loki を水平方向に拡張できない状態である。
ローカルでもマイクロサービスの Grafana Loki の動作確認をできることが次の目標である。