はじめに
前回の記事では、ROSAからAmazon CloudWatch Logsへログを転送する際に、ClusterLogForwarderのInputを利用して転送対象のログを絞り込む方法を紹介しました。
ROSAからCloudWatchへのログ転送コストを抑える① - Inputで転送対象を絞る #AWS - Qiita
Inputを利用すると、ApplicationログをNamespaceやLabelで絞り込んだり、InfrastructureログやAuditログをsource単位で絞り込むことができます。
一方、次のようなケースはInputだけでは対応できません。
- Inputで取得したログから、さらに特定条件のログを除外したい
-
/healthなど特定の内容を含むログを除外したい - 特定のログレベルを除外したい
- ログ自体は残しつつ、CloudWatchで利用しないフィールドを削除したい
このような場合に利用できるのが、ClusterLogForwarderのFilterです。
今回はOpenShift Logging 6.6を前提に、ログ量の削減に利用できるdropとpruneについて紹介します。
また、実運用ではクラスターで発生するすべてのログを事前に把握することは難しいため、設定方法だけでなく、どのようにログ削減を進めていくかについても最後に考察します。
検証環境
本記事では以下の環境を前提とします。
- Red Hat OpenShift Service on AWS(ROSA)
- Red Hat OpenShift Logging 6.6
-
ClusterLogForwarderからAmazon CloudWatch Logsへのログ転送を設定済み
CloudWatch Logsへの転送設定自体については本記事では割愛します。
ClusterLogForwarderのFilterについて
Filterは、Inputで収集したログをOutputへ転送する前に、ログレコードを除外・加工するための機能です。
今回利用するFilterは次の2つです。
| Filter | 用途 |
|---|---|
drop |
条件に一致したログレコード全体を除外する |
prune |
ログレコード内の不要なフィールドを削除する |
例えば、/healthへのアクセスログ自体が不要であればdrop、ログ自体は必要でもKubernetesのAnnotationなどをCloudWatchで利用しない場合はpruneを利用します。
dropで不要なログを除外する
dropは、指定した条件に一致するログレコードを除外するFilterです。
基本的な設定は次のようになります。
spec:
filters:
- name: drop-marketplace
type: drop
drop:
- test:
- field: .kubernetes.namespace_name
matches: openshift-marketplace
各項目の内容は以下の通りです。
| 項目 | 内容 |
|---|---|
field |
条件判定に利用するログレコードのフィールド |
matches |
指定した値または正規表現に一致する場合にTrue |
notMatches |
指定した値または正規表現に一致しない場合にTrue |
1つのフィールドに対してmatchesとnotMatchesを同時に指定することはできません。
また、指定したfieldがログレコードに存在しない場合、その条件はFalseとして評価されます。
複数の条件を指定する
1つのtest内に複数条件を指定した場合はAND条件になります。
drop:
- test:
- field: .log_type
matches: application
- field: .kubernetes.namespace_name
matches: sample
この場合は、
.log_type = application
AND
.kubernetes.namespace_name = sample
の両方を満たすログが除外されます。
一方、複数のtestを指定した場合はOR条件です。
drop:
- test:
- field: .kubernetes.namespace_name
matches: namespace-a
- test:
- field: .kubernetes.namespace_name
matches: namespace-b
この場合はnamespace-aまたはnamespace-bのログが除外されます。
dropで利用する正規表現
matchesとnotMatchesには正規表現を指定できます。
ここでは、本記事で利用する基本的な表現だけ紹介します。
| 表現 | 意味 |
|---|---|
^ |
文字列の先頭 |
$ |
文字列の末尾 |
.* |
任意の0文字以上 |
.+ |
任意の1文字以上 |
| `A | B` |
例えば、
^openshift-.*
と指定すると、openshift-から始まる文字列に一致します。
openshift-marketplace
openshift-monitoring
openshift-ingress
などが対象になります。
正規表現を利用すると複数のログをまとめて除外できますが、dropではログレコード自体が削除されます。
条件を広くしすぎて必要なログまで除外しないよう、実際のログを確認した上で設定する必要があります。
dropの利用例
Part①では、Inputで転送対象を絞り込む方法を紹介しました。
dropでは、Inputで取得したログに対して、ログレコードに含まれるフィールドを条件にさらにログを除外できます。
そのため、InfrastructureログやAuditログなどでも、対象となるフィールドがログレコードに存在すれば、その値を条件に除外できます。
特定Namespaceのログを除外する
例えば、Infrastructureログのうちopenshift-marketplace Namespaceのログを除外する場合は、.kubernetes.namespace_nameを条件に指定します。
spec:
filters:
- name: drop-marketplace
type: drop
drop:
- test:
- field: .kubernetes.namespace_name
matches: "^openshift-marketplace$"
Applicationログについては、Namespaceが事前に分かっている場合はPart①で紹介したInput側で絞り込む方がシンプルです。
/healthなど特定のログを除外する
dropではKubernetesのメタデータだけでなく、.messageを条件にログの内容を判定できます。
例えばApacheから次のようなアクセスログが継続的に出力されているとします。
10.0.0.1 - - [08/Sep/2026:10:00:00 +0900] "GET /health HTTP/1.1" 200 2
この/healthへのアクセスログをCloudWatchで使用しない場合は、次のように設定できます。
spec:
filters:
- name: drop-healthcheck
type: drop
drop:
- test:
- field: .message
matches: 'GET /health '
/healthと/readyの両方を対象にする場合は、例えば次のように指定できます。
matches: 'GET /(health|ready) '
実際に指定する正規表現は、Applicationが出力するログフォーマットに合わせて調整します。
ログレベルで除外する
dropでは.levelを条件にログを除外することもできます。
例えば、実際のログレコードで.levelにdebugが設定されており、DEBUGログをCloudWatchへ転送する必要がない場合は、次のように設定できます。
spec:
filters:
- name: drop-debug
type: drop
drop:
- test:
- field: .level
matches: debug
どのログレベルまでCloudWatchへ転送するかは、システムの監視や障害調査の方針に合わせて決めます。
Applicationログでは、ログの出力形式によって.levelに期待した値が設定されているとは限らないため、実際のログを確認した上で設定します。
pruneで不要なフィールドを削除する
ここまで紹介したdropは、ログレコード自体を削除する方法です。
一方、ログ自体は必要でも、ログレコードに付与されているすべての情報をCloudWatchで利用するとは限りません。
このような場合に利用できるのがpruneです。
pruneでは不要なフィールドを削除することで、1レコードあたりのサイズを小さくできます。
OpenShift Logging 6.6では、削除対象を指定するinと、保持対象を指定するnotInが利用できます。
inで不要なフィールドを指定する
inには削除するフィールドを指定します。
例えば、KubernetesのAnnotationやID情報をCloudWatchで利用しない場合は、次のように設定できます。
spec:
filters:
- name: prune-fields
type: prune
prune:
in:
- .kubernetes.annotations
- .kubernetes.namespace_id
- .kubernetes.pod_id
inに指定したフィールドが削除され、それ以外のフィールドは残ります。
notInで必要なフィールドを指定する
notInでは、ログレコードに残すフィールドを指定します。
指定されていないフィールドは削除されます。
例えば、次のような設定です。
spec:
filters:
- name: prune-fields
type: prune
prune:
notIn:
- ."@timestamp"
- .log_type
- .log_source
- .message
- .level
- .kubernetes.namespace_name
- .kubernetes.pod_name
- .kubernetes.container_name
inが不要なフィールドを指定する方法なのに対して、notInは残したいフィールドを指定する方法です。
pruneで削除できないフィールド
pruneでは、すべてのフィールドを自由に削除できるわけではありません。
OpenShift Logging 6.6では、次のフィールドは必須フィールドとなっており、pruneで削除できません。
.log_type
.log_source
.message
.kubernetes.namespace_name
.kubernetes.pod_name
.kubernetes.container_name
.level
例えば、次のように.messageを削除対象として指定することはできません。
prune:
in:
- .message
削除できない必須フィールドを指定した場合でも、ClusterLogForwarder CR自体は受け付けられます。
ただし、Operatorによる検証でvalidation conditionがFalseになります。
そのため、CRを作成できたからといって設定が有効とは限らない点には注意が必要です。
notInを利用する場合も、必須フィールドを削除対象にしないよう注意します。
なお、inとnotInを両方指定した場合は、notInが先に処理され、保持対象を決めた後にinに指定したフィールドが削除されます。
pruneで残すフィールドをどう考えるか
pruneを設定する際に、ログに含まれるすべてのフィールドを確認し、「不要なもの」を1つずつ探して削除する方法もあります。
しかし、実運用で発生するログの種類やフィールドをすべて事前に把握することは難しいです。
ログを削減するために、発生するすべてのログを把握する必要はありません。
障害発生時に、
- いつ発生したか
- どこで発生したか
- 何が発生したか
を確認できる情報を基準に、残すフィールドを決める方が現実的です。
例えば、次のような情報です。
| 確認したいこと | 主なフィールド |
|---|---|
| いつ発生したか | @timestamp |
| どこで発生したか | Namespace、Pod、Container |
| 何が発生したか | message |
つまり、
「どのフィールドが不要か」をすべて調査する
のではなく、
「障害調査で必要になる情報は何か」を先に決める
という考え方です。
Namespace、Pod、Container、messageなどは必須フィールドのため削除できませんが、それ以外にCloudWatchで利用しないメタデータを削除することで、ログレコードを軽量化できます。
CloudWatch Logsでは取り込まれるデータ量が料金に影響するため、不要なフィールドを削除して1レコードあたりのサイズを小さくすることは、ログ取り込み量の削減につながります。
【考察】実運用ではどのようにログを削減するか
ここからはOpenShift Loggingの仕様ではなく、実際にログ転送量を削減する場合の進め方についての考察です。
ログ転送を開始する前に、クラスターで発生するすべてのログを把握し、不要なログをあらかじめ洗い出しておくことは現実的ではありません。
そのため、実運用では次の流れでログ削減を検討していく方法が考えられます。
1. Inputで明確に不要な範囲を除外する
まず、転送前から不要と判断できるログをInputで除外します。
例えば、CloudWatchへ転送するApplicationのNamespaceが決まっている場合は、Inputで対象Namespaceを絞り込みます。
Part①で紹介した通り、Inputで除外できるログについては、まずInput側で対象を整理します。
2. pruneで運用に必要なフィールドを整理する
次に、障害発生時に「いつ、どこで、何が起きたか」を確認できるよう、CloudWatchへ残す情報を整理します。
ここでは、個々のログメッセージをすべて確認して削除対象を探すのではなく、障害調査で必要な情報を基準に保持するフィールドを決めます。
特にnotInを利用する場合は、不要なフィールドを探すのではなく、必要なフィールドを基準に保持対象を決めることができます。
この方法であれば、個々のログ内容に依存せず、広い範囲のログに同じ削減ルールを適用できます。
3. 実際にCloudWatchへ転送されたログを確認する
運用を開始した後、CloudWatchへ転送されたログを確認します。
例えば、次のような傾向が見つかるかもしれません。
-
/healthへのアクセスログが大量に出力されている - 特定のInfrastructure Namespaceから大量のログが出力されている
- DEBUGやINFOレベルのログが想定以上に多い
この段階で、ログレコード自体を除外できるかを検討します。
4. 明確に不要なログをdropする
実際のログを確認した上で、CloudWatchでの運用に不要と判断できるログをdropします。
例えば、
-
/healthなどの定型的なアクセスログ - 転送する必要のないInfrastructure Namespace
- 不要なログレベル
などです。
最初からすべての不要ログを洗い出すのではなく、
Inputで明確な範囲を絞る
↓
pruneで必要なフィールドに絞る
↓
実際のログを確認する
↓
dropで不要なログを追加で除外する
という形で段階的にチューニングすることで、事前調査の負担を抑えながらログ取り込み量を削減できます。
まとめ
今回はClusterLogForwarderのdropとpruneを利用して、CloudWatch Logsへのログ取り込み量を削減する方法を紹介しました。
それぞれの役割は次のように整理できます。
| 方法 | 役割 |
|---|---|
| Input | 収集するログの範囲を絞る |
drop |
不要なログレコード自体を除外する |
prune |
必要なログから不要なフィールドを削除する |
dropは不要なログレコードそのものを削除できる一方、どのログを削除してよいか判断するには、実際に発生するログを確認する必要があります。
そのため、まずInputで明確に不要な範囲を絞り、pruneで障害調査に必要な情報を残しながらログレコードを軽量化します。
その後、実際にCloudWatchへ転送されたログを確認し、明確に不要なログをdropしていくことで、運用実態に合わせてログ取り込み量を削減できます。
参考文献
-
Red Hat OpenShift Logging 6.6 - Configuring log forwarding