CUEBiC TEC BLOG

キュービックTECチームの技術ネタを投稿しております。

CloudWatch LogsからOpenSearchへログ集約を移行した

概要

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
今回は弊社のログ集約基盤についてお話します。

背景

皆さんはログの運用についてどのように取り扱っていますでしょうか。弊社では最近まで各種メディアのアクセスログをサーバー内に保存して、参照したい場合は直接サーバーにログインして見る運用でした。
当然ながら毎回サーバーにログインして見るのは手間がかかりますし、どのサーバーなのか確認する時間ももったいなかったです。そのため、ログを集約させようという動きが出てきました。
当初は簡単に設定できるCloudWatch Logsに集約させようと思い、対象サーバーにインストール済みのCloudWatchエージェントの設定ファイルをSystems Mangaerを使って複数サーバーに一括反映して、CloudWatch Logsに集約させました。

これで各種メディアのアクセスログをCloudWatch Logsに集約させ、ログ分析したい場合はLogs Insightsでクエリ検索できることからログの運用が楽になると考えました。

運用後の課題

ところが、しばらく運用してみて利用者からいくつか課題があると意見をもらいました。

課題

  • ログの検索結果が遅い
  • Logs Insightsのクエリがわかりにくい
  • ログ結果をグラフで視覚化できるようにしたい

その他の問題点としてCloudWatch Logsはログの保存料金に加えて、EC2からログを収集するときにも料金が発生するのですがアクセスログを取り込み始めると一気にCloudWatchのコストが上がりました。

CloudWatch 月別コスト推移

この課題を解決するためにCloudWatch Logsによるログ集約をAWS OpenSearchへ変更して解決してみました。

AWS OpenSearch

aws.amazon.com

AWS OpenSearchは、OSSとして一定の地位を築いているElasticSearchからフォークした検索エンジンサービスです。ログ分析以外の用法としてはエンドユーザーに提供する検索エンジンとしても利用されています。
最初にログ集約基盤を構築する時にOpenSearchにログを集約することも候補に入れていましたが、社内で扱ったことがある人が少なくコストが高いというイメージも持っていたので、CloudWatch Logsでログ集約基盤を構築していました。が、これを気にOpenSearch(ElasticSearch)の使い方を学ぶことも兼ねてログ集約基盤をOpenSearchで再構築してみました。

アーキテクチャ構成図

今回構築するログ集約基盤については以下のとおりです。

OpenSearchアーキテクチャ

本来OpenSearchVPC内に作成することをAWSは推奨しています。ただ、弊社都合になるのですがメディアサーバーが複数のVPC内に点在してそれらを一つのOpenSearchに集約したいという要望があります。解決策としてALBの配下にOpenSearchを置くことで外部通信できるようにすることを検討しました。
ですがALBにアタッチさせる場合、OpenSearchIPアドレスタイプのターゲットグループを選択してOpenSearchのプライベートIPアドレスを関連付けるのですが、OpenSearchのプライベートIPアドレスは可変なため定期的にアタッチさせるIPアドレスを見直す必要が出てきます。 定期的に取得するシェルを作ると、今度はそのシェルの面倒を見る必要が出てきて運用負荷が高まると思いました。なので私の考えた結論としてOpenSearchをパブリックに置いて、セキュリティグループで社内からのアクセスと各種メディアサーバーを集約させる集約サーバーからの通信のみに許可する設定にすることで最低限のセキュリティを確保しました。
OpenSearchダッシュボードにログインする場合は、Amazon Cognitoを使って会社のgoogleアカウントでアクセスできるようにすることでユーザー側で新しくパスワード情報を保持せずにすむようにしました。

Fluentd

www.fluentd.org

ログを転送するための手段ですが、OSSのFluentdを採用しています。Fluentdは前職でも使っていましたし、OpenSearchにログを転送するプラグインもありましたので導入が簡単でした。FluentdでOpenSearchにログを転送する時にログフィールドのタイプを数字型に変更できることも大きかったです。
というもの最初移行するにあたり、CloudWatchLogsから直接OpenSearchに転送したほうが楽と考え、そうした機能も実現できることがわかりましたので最初はCloudWatch Logsに集約されているロググループをサブスクライブしてOpenSearchに転送しました。ただCloudWatch Logsから転送されてきたログのフィールドがすべて文字列型で登録されていて、レスポンスタイムの比較検索などができないという事態に遭遇しました。

ログサイズやレスポンスタイムが文字列型

裏側はLambdaがCloudWatch LogsからOpenSearchに転送しているのでLambdaを修正すればできるかもしれませんが、残念ながら私のスキルではどういうふうにLambdaがログをOpenSearchに転送しているのか理解できず、修正を断念しました。そこで冒頭のFluentdで改めてログを転送しようという結論になりました。

Fluentd実装

# fluentd設定ファイル(メディアサーバー側)
<source>
  @type tail
  path /var/log/nginx/access.log
  format ltsv
  time_format %d/%b/%Y:%H:%M:%S %z
  types size:integer,status:integer,upstream_response_time:float,time:time,response_time:float
  tag nginx
</source>
<match nginx>
  @type forward
  <server>
    host XXX.XXX.XXX.XXX
    port XXXXXXX
  </server>
</match>

メディアサーバーにFluentd導入し、設定ファイルには転送対象のログと転送先の集約サーバーを指定しています。アクセスログは元々LTSV形式でログフォーマットを整形していましたので、typesのパラメーターで数字型で転送したいキーを指定すればOpenSearchに集約時に数字型に変換されます。

# fluentd設定ファイル(集約サーバー側)
<source>
  type forward
  port XXXXXXX
  bind XXX.XXX.XXX.XXX
</source>

<match nginx>
  @type copy
  <store ignore_error>
    type_name nginx
    @type opensearch
    include_tag_key true
    tag_key @log_name
    host endpoint.com
    port 443
    scheme https
    logstash_format true
    flush_interval 10s
    retry_limit 5
    buffer_type file
    reload_connections false
  </store>

  <store>
    @type s3
    s3_bucket XXXXXXX
    s3_region ap-northeast-1
    path XXXXXXX/YYYYYY
    time_slice_format %Y/%m/%d/%H/%M
    time_slice_wait 60
    <buffer>
      timekey 60
      timekey_wait 60
    </buffer>
  </store>
</match>

こちらは集約サーバーのFluentdの設定ファイルです。メディアサーバーから転送されたログをOpenSearchとS3に転送しています。S3にもログを転送する理由ですが、OpenSearchで長期保存せず、保持期間を一ヶ月と定めていますので古いログをアーカイブできるようにS3にも同時に転送しています。
ログをFluentd経由で転送することで、レスポンスタイムやログサイズも数字型でOpenSearchに転送され、比較検索もできるようになりました。検索のリアルタイム性も向上し、CloudWatch Logsはログが取り込まれて、表示されるまで1分くらいラグがありましたがOpenSearchではほぼ誤差なしで表示されるようになりました。

ログサイズが800キロバイト以下のみを抽出

グラフ作成

OpenSearchは取得したログを基に様々なグラフを作成できます。ログファイル内に記載しているドメイン情報を基にどのメディアに多くアクセスが集まっているかわかる円グラフや、時間帯別のレスポンスタイム推移チャート図を作成して、ビジネス分析に役立てるような機能も持っています。

メディア別のアクセス割合(秘匿情報もありますのでドメインラベルを非表示にしています)
時間帯別のレスポンスタイムにかかった時間比較

課題

OpenSearchに移行したことで新しい課題ができました。

  • ストレージがすぐいっぱいになる
  • シャード数が多い

ストレージについて

OpenSearchのEBSストレージサイズは1ノードあたりデフォルトで10GiBです。当然ながらこれで作成したらすぐにいっぱいになりましのでとりあえず100GiBくらいに増やしました。その後も一杯になりましたので、その都度ストレージサイズを上げてきましたがコストが右肩上がりで上昇しましたので定期的にOpenSearchのログファイル(インデックス)を削除する必要がありました。
OpenSearchは削除APIがありまして、インデックスの削除は以下のコマンドで削除できます。

curl  -XDELETE https://endpoint.com/<index>

インデックス名はログの種類別に1日単位の日付で作成していますので、1ヶ月以上前のログは削除するようにしています。

# mailログ
mail.20220629
mail.20220630
mail.20220701

# apacheアクセスログ
apache-access.20220629
apache-access.20220630
apache-access.20220701

今の所まだ自動化できていませんが、LambdaかSystems Managerを使って定期的に自動削除する仕組みづくりも実装する予定です。

シャードについて

基本概念 | Elasticsearchリファレンス [5.4] | Elastic

シャードとはインデックスを分散するときに用いられる単位で、演算処理の並列化などでパフォーマンス向上に役立てます。OpenSearchではデフォルトで5 つのプライマリシャードと 1 つのレプリカに分割されています。AWSのドキュメントによるとシャードの1サイズは10GiB~50GiBの間とされていて、大きすぎると障害発生時のリカバリが困難、小さくしすぎるとパフォーマンス劣化の問題点があります。
デフォルトのシャードでサイズを確認すると数GiBから15GiBくらいの間で収まっていたのでもう少しだけシャード数を変更したほうがよさそうでした。シャードの変更もAPIでできますので最適なサイズについてAWSのドキュメントを参考に修正していこうと思います。

所感

弊社のメディアのログをCloudWatch LogsからOpenSearchに移行してみました。
OpenSearchもといElasticSearch自体初めて触ったので、概念の理解が難しくシャードやインデックスなどなんのこっちゃだと思いました。まだ完全に理解できていませんので、最適化にはほど遠い状態と認識しているので利用者に使い方をレクチャーしつつ継続的に改善できるようにしていきます。

参考文献

dev.classmethod.jp

docs.aws.amazon.com

docs.aws.amazon.com