Google Cloud

Google Cloudのログバケットにある過去のログをCloud Storageバケットに保存する

Google Cloud

こんにちは。技術部プラットフォームグループの染矢(@kesompochy)です。最近は、自分のアイコンに集中線を入れる仕事をしています。

今回は、Google Cloudプロジェクトのログバケットに保存されたログエントリを、gcloudコマンドによってCloud Storageのバケットに保存する方法を紹介します。

  1. なぜ記事にするのか
  2. この機能が解決する課題
    1. ログの保存要件を満たしたい
  3. 実施記録
    1. コマンド履歴
    2. 保存されるパス
    3. オペレーション実施中の挙動
  4. おわり

なぜ記事にするのか

公式ドキュメントのリリースノートには、この機能は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-filterdescribeの場合は--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による転送とgloud logging copyコマンドによる保存でバケットを分ける必要がないので嬉しいですね。 個人的にはこのことを事前に知っておきたかったためこの記事の執筆に至りました。

オペレーション実施中の挙動

オペレーションがstate: OPERATION_STATE_RUNNINGの状態のときに、保存先に指定したバケットの様子を観察していると、過去の日付から順番にオブジェクトが作られていました。 きっと、ログバケットにある最古のものから日付の古い順にコピー処理をしているのだろうと推測できます。

おわり

以上、gcloud logging copyで過去ログの手動転送をやってみた、でした。 ここまで読んでくださったあなたにとって何かの参考になれば私はとてもハッピィです。