こんにちは。技術部プラットフォームグループの染矢(@kesompochy)です。最近は、自分のアイコンに集中線を入れる仕事をしています。
今回は、Google Cloudプロジェクトのログバケットに保存されたログエントリを、gcloud
コマンドによってCloud Storageのバケットに保存する方法を紹介します。
なぜ記事にするのか
公式ドキュメントのリリースノートには、この機能は2024年2月13日にGA(一般提供)になった機能だと書かれています。この記事を書いているのが2024年3月なので、比較的新しい機能ですね。現代においてはまだ、新しい機能についてはAIに聞いても正確に答えてくれないので、人間による記事の需要があります。
やってみる中で、ドキュメントを読むだけではわからなかった点がいくつかありました。そのため、これからこの機能を使ってみようという方向けに、公式ドキュメント以外の補足情報として使っていただければと思いこの記事を執筆しています。
また、前提を理解してもらうために、この機能によって解決される課題も説明します。
この機能を使用してみた記録だけ読みたい方は実施記録の項までジャンプしてください。
この機能が解決する課題
ログの保存要件を満たしたい
Webサービスにおいては、セキュリティ要件などによって、サービスへのアクセスログやデータベースへのクエリログを一定期間は保存しておくことがしばしば求められます。その要件を達成するためには、発生するログをどこかに保存しておく必要があります。AWSやGoogle Cloudなどのホスティングサービスを使う場合は、ログを同じサービス内のストレージに保存することが多いでしょう。
Google CloudのCloud Loggingによってログの集約と管理ができる
Google CloudにはCloud Loggingというサービスがあります。Cloud Loggingを使うことによって、Google Cloudプロジェクト内のさまざまなリソースから発生するログを集約・管理・検索できます。
Cloud Loggingでは"ログバケット"にログデータが保存されます。ほとんどのログバケットは保持期間を変更できます。 これによってログの保存要件を達成できるでしょう。しかし、頻繁にアクセスする必要がないのであれば、ログバケットの保持期間を延ばすのはtoo muchです。長期間保存しておくことが目的であれば、例えばGCS(Google Cloud Storage)の適切なクラスのバケットに保持しておく方がコストが低く済みます。 また、ログの種類によって保持期間を変えたい場合もあるでしょう。その場合は、ログをフィルタリングして、長期保存が必要なログだけを別のバケットに移せると嬉しいです。
参考: https://cloud.google.com/logging/docs
Log routerによってログを転送できる
Google Cloudには、"Log router"という機能があります。
Log routerではSink
という単位で、ログデータを特定の宛先に転送できます。例えばCloud Storage, BigQuery, またCloud Pub/Subなどに送信できます。転送するログデータのフィルタリングもできます。
参考: https://cloud.google.com/logging/docs/export/configure_export_v2
Log routerはSinkが作られてからのログしか送ることができない
Sinkによってログの転送設定が作られると、それ以降のログは設定に応じて転送されるようになります。しかし、それ以前のログは転送されません。 つまり、ログバケットにすでに存在するログを、手軽にストレージバケットに保存する手段はありませんでした。
そこで、今回紹介する機能の登場によって、コマンドを実行するだけで過去のログをストレージバケットに保存できるようになりました。
実施記録
コマンド履歴
最新のgcloud
コマンドがインストールされている状態とします。また、gcloudプロジェクトにアクセスするための適切な権限がある状態とします。
まず、現在、どのログバケットがプロジェクト内に存在するかを確認します。
$ gcloud logging buckets list --project=my-project
LOCATION BUCKET_ID RETENTION_DAYS CMEK RESTRICTED_FIELDS INDEX_CONFIGS LIFECYCLE_STATE LOCKED CREATE_TIME UPDATE_TIME
global _Default 30 ACTIVE
global _Required 400 ACTIVE True
_Default
バケットと_Required
バケットが存在します。今回は_Default
バケットを対象とします。
転送対象は、MySQLのgeneral logとします。次のクエリで転送対象のログをフィルタリングできることを、コンソールのLogs Explorer画面から確認できます。
resource.type="cloudsql_database"
log_name="projects/my-project/logs/cloudsql.googleapis.com%2Fmysql-general.log"
次に、ログ保存用のバケットlog-archive-mysql-general-log
が存在することを確認します。
$ gcloud storage buckets list --format="value(name)" --project=my-project
...
log-archive-mysql-general-log
...
ドキュメントに倣ってコマンドを実行すると、実行結果が返ってきます。
$ gcloud logging copy _Default storage.googleapis.com/log-archive-mysql-general-log \
--location=global --log-filter='resource.type="cloudsql_database"
logName: "projects/my-project/logs/cloudsql.googleapis.com%2Fmysql-general.log"' \
--project=my-project
metadata:
'@type': type.googleapis.com/google.logging.v2.CopyLogEntriesMetadata
destination: storage.googleapis.com/log-archive-mysql-general-log
source: projects/my-projecet/locations/global/buckets/_Default
startTime: '2024-03-25T02:26:51.312953Z'
state: OPERATION_STATE_SCHEDULED
verb: copy
name: projects/my-project/locations/global/operations/転送バッチIDの文字列
その流れでドキュメントに書かれているままに、実行されているオペレーションの一覧取得と詳細表示をしてみました。
list
の場合は、--location
と--operation-filter
、describe
の場合は--location
が必須項目なようです。項目が不足しているとERROR: (gcloud.logging.operations.describe) argument --location: Must be specified.
のようにエラーが返ってきます。
$ gcloud logging operations list --location=global --operation-filter=request_type=CopyLogEntries --project=my-project
---
metadata:
'@type': type.googleapis.com/google.logging.v2.CopyLogEntriesMetadata
destination: storage.googleapis.com/log-archive-mysql-general-log
progress: 1
source: projects/my-project/locations/global/buckets/_Default
startTime: '2024-03-25T02:26:51.312953Z'
state: OPERATION_STATE_RUNNING
verb: copy
name: projects/my-project/locations/global/operations/転送バッチIDの文字列
$ gcloud logging operations describe 転送バッチIDの文字列 --location=global --project=my-project
metadata:
'@type': type.googleapis.com/google.logging.v2.CopyLogEntriesMetadata
destination: storage.googleapis.com/log-archive-mysql-general-log
progress: 1
source: projects/my-project/locations/global/buckets/_Default
startTime: '2024-03-25T02:26:51.312953Z'
state: OPERATION_STATE_RUNNING
verb: copy
name: projects/my-project/locations/global/operations/転送バッチIDの文字列
30分くらい放置してから再度取得すると、progress
の値が増えていました。進んでそうですね。
$ gcloud logging operations list --location global --operation-filter=request_type=CopyLogEntries --project=my-project
---
metadata:
'@type': type.googleapis.com/google.logging.v2.CopyLogEntriesMetadata
destination: storage.googleapis.com/log-archive-mysql-general-log
progress: 44
source: projects/my-project/locations/global/buckets/_Default
startTime: '2024-03-25T02:26:51.312953Z'
state: OPERATION_STATE_RUNNING
verb: copy
name: projects/my-project/locations/global/operations/転送バッチIDの文字列
実行時間について、ドキュメントには次のように書かれていました。
All copy operations take at least an hour to complete, no matter the amount of data that is being copied.
約10分おきにオペレーションを取得して確認していたところ、実行してから約2.5時間後にオペレーションが完了していました。 今回は30日分のログを保存しました。
保存されるパス
今回のオペレーションで保存したログは、cloudsql.googleapis.com/mysql-general.log/YYYY/MM/DD/hogehoge.json
のパスに保存されていました。
これは、Log routerの機能で転送されるときと同じパスです。つまり、Log routerの設定で転送先に指定したバケットに、gcloud logging copy
コマンドによって過去のログを保存しても、2種類の保存方法間のパスに齟齬が生まれないということです。Log routerによる転送とgcloud logging copy
コマンドによる保存でバケットを分ける必要がないので嬉しいですね。
個人的にはこのことを事前に知っておきたかったためこの記事の執筆に至りました。
オペレーション実施中の挙動
オペレーションがstate: OPERATION_STATE_RUNNING
の状態のときに、保存先に指定したバケットの様子を観察していると、過去の日付から順番にオブジェクトが作られていました。
きっと、ログバケットにある最古のものから日付の古い順にコピー処理をしているのだろうと推測できます。
おわり
以上、gcloud logging copy
で過去ログの手動転送をやってみた、でした。
ここまで読んでくださったあなたにとって何かの参考になれば私はとてもハッピィです。