Grafana LokiのログをAzure Blob Storageに保存する

# この記事について
[前回の記事](https://qiita.com/t5693_g/items/96cd30f9b64319352ba1)でAKSにGrafana Lokiをデプロイした。今回はGrafana LokiのストレージをAzure Blob Storageに変更してみたのでその作業記録をメモする。Azure Blob Storageへの認証には、セキュアなworkload identityを使用した。

# 前提条件
- AKSはデプロイ済み
- AKS上にGrafana Lokiをデプロイ済み
- azure cliをインストール済み

# 環境
- Azure Kubernetes Service
    - Kubernetes Version: 1.26.6
    - Agentpoolのノードサイズと数: Standard_B2ms x 1
- Grafana Loki
    - chart version: 5.15.0
    - app versaion: 2.8.4
- Promtail
    - chart version: 6.15.0
    - app versaion: 2.8.3
- Grafana
    - chart version: 6.59.0
    - app versaion: 10.1.0
- azure cli version: 2.52.0

# 手順
## workload identityを有効する
公式ドキュメントの手順に従えばOK。Key Vaultは今回は使わないのでデプロイしない。

https://learn.microsoft.com/ja-jp/azure/aks/workload-identity-deploy-cluster

環境変数を設定する。service account namespaceはgrafanaにしておく。あとはデフォルトのまま。

```bash
export RESOURCE_GROUP="myResourceGroup"
export LOCATION="japaneast"
export SERVICE_ACCOUNT_NAMESPACE="grafana"
export SERVICE_ACCOUNT_NAME="workload-identity-grafana-loki"
export SUBSCRIPTION="$(az account show --query id --output tsv)"
export USER_ASSIGNED_IDENTITY_NAME="myIdentity"
export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity"
export AKS_CLUSTER_NAME="myakscluster"
```


workload identityが使えるようにAKSの設定を更新する。
```bash
az aks update -g "${RESOURCE_GROUP}" -n ${AKS_CLUSTER_NAME} \
  --enable-oidc-issuer --enable-workload-identity
```

OIDC issuerのURLを取得する。
```bash
export AKS_OIDC_ISSUER="$(az aks show -n ${AKS_CLUSTER_NAME} \
  -g "${RESOURCE_GROUP}" --query "oidcIssuerProfile.issuerUrl" -otsv)"
```

Managed IDを作成し、クライアントIDを取得する。
```bash
az identity create --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --subscription "${SUBSCRIPTION}"
export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)"
```

:::note info
公式ドキュメントではここでサービスアカウントを作成するが、今回はGrafana LokiのHELMから作成させたいので、スキップする。
:::



## Azure Blob Storageのデプロイ

設定はここを参考に。

https://grafana.com/docs/loki/latest/setup/install/helm/configure-storage/

ストレージアカウントを作成する。
```bash
export STORAGE_ACCOUNT_NAME="NAME OF YOUR STORAGE ACCOUT"
az storage account create --name ${STORAGE_ACCOUNT_NAME} \
                          --resource-group ${RESOURCE_GROUP} \
                          --location japaneast \
                          --sku Standard_LRS
```

:::note warn
デフォルトではstorage accountに対するpublic accessが有効なので、これを無効にすべきである。が、今回は設定後に動作確認をしやすくするために、あえてデフォルトのままにしている。
:::

コンテナを作成する。Lokiはデフォルトで `chunks`, `ruler`, `admin` という名前の3つのコンテナを使用する。コンテナがない場合は、自動作成などはしてくれないので必ず事前に作成すること。

```bash
az storage container create --name chunks --account-name ${STORAGE_ACCOUNT_NAME} -g ${RESOURCE_GROUP}
az storage container create --name ruler --account-name ${STORAGE_ACCOUNT_NAME} -g ${RESOURCE_GROUP}
az storage container create --name admin --account-name ${STORAGE_ACCOUNT_NAME} -g ${RESOURCE_GROUP}
```

Managed IDがStorage Accoutに書き込みできるように、Storage Blob Data Contributor ロールを付与する。

```
export STORAGE_ACCOUNT_SCOPE=$(az storage account show -n $STORAGE_ACCOUNT_NAME --query id -o tsv)

az role assignment create --role "Storage Blob Data Contributor" \
  --assignee ${USER_ASSIGNED_CLIENT_ID} --scope ${STORAGE_ACCOUNT_SCOPE}
```

## Lokiの更新
lokiのストレージをAzure Blob Storageに切り替えるための設定を行う。設定はloki-customvalues.yamlファイルに記述。ポイントは以下の4つ。
- ストレージタイプをazureに設定し、minioはfalseに。
- ストレージとの認証に`useFederatedToken`を設定（`useManagedIdentity`を使いたくなるが、そうではないので注意。
- Service Accountのannotationにworkload identityのIDを追加する。
- Podに`azure.workload.identity/use: "true"`のラベルをつける。


```bash
cat <<EOF > loki-customvalues.yaml
loki:
  auth_enabled: false
  podLabels:
    azure.workload.identity/use: "true"
  commonConfig:
    replication_factor: 1
  storage:
    type: azure
    azure:
      accountName: ${STORAGE_ACCOUNT_NAME}
      useManagedIdentity: false
      useFederatedToken: true
      userAssignedId: null
      requestTimeout: null
      endpointSuffix: null
serviceAccount:
  create: true
  name: ${SERVICE_ACCOUNT_NAME}
  annotations:
    azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
minio:
  enabled: false
write:
  replicas: 1
read:
  replicas: 1
backend:
  replicas: 1
EOF
```

helm upgradeで新しいvalueを適用する。
```
helm upgrade --install loki -n grafana grafana/loki -f loki-customvalues.yaml
```


マネージドID、サービス アカウント発行者、サブジェクトの間にフェデレーションID資格情報を作成する。
```
az identity federated-credential create \
  --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \
  --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \
  --resource-group "${RESOURCE_GROUP}" \
  --issuer "${AKS_OIDC_ISSUER}" \
  --subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}" \
  --audience api://AzureADTokenExchange
```

## 動作確認

Azure Blob Storageのchunksコンテナにログらしき保存されていることを確認（設定してから反映されるまでに、しばらく時間がかかる）。
adminコンテナとrulerコンテナには何も保存されていなかった…。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290024/0ce6b6e3-a681-18e3-0638-4fa832dfe4ee.png)


# お片付け
前回の記事でストレージとしてminioを利用したため、minioがつくっｔPersistent Volumeがゴミとして残るので、削除する。Persistent Volume Claimを消せばOK。

```bash
kubectl delete pvc -n grafana \
  $(kubectl get pvc -n grafana -o custom-columns=NAME:.metadata.name | grep loki-minio)
```


# まとめ
Lokiの収集したデータをAzure Blob Storageに保存することができた。

workload identityの設定は情報が少なかったが、最終的にはPodに対するService Account設定＋ラベル付けという基本的な設定のみで完了することがわかった。workload identityはシークレットを持つ必要がない、権限付与の範囲もPodに絞れる等、非常にセキュアな印象。Podから見るとKubernetesのService Accountを設定しているだけにみえるので、非常にスマート。


# 参考情報

- https://learn.microsoft.com/ja-jp/cli/azure/storage/account?view=azure-cli-latest#az-storage-account-create
- https://grafana.com/docs/loki/latest/setup/install/helm/configure-storage/