概要
こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
今回は弊社のログ集約基盤についてお話します。
背景
皆さんはログの運用についてどのように取り扱っていますでしょうか。弊社では最近まで各種メディアのアクセスログをサーバー内に保存して、参照したい場合は直接サーバーにログインして見る運用でした。
当然ながら毎回サーバーにログインして見るのは手間がかかりますし、どのサーバーなのか確認する時間ももったいなかったです。そのため、ログを集約させようという動きが出てきました。
当初は簡単に設定できるCloudWatch Logsに集約させようと思い、対象サーバーにインストール済みのCloudWatchエージェントの設定ファイルをSystems Mangaerを使って複数サーバーに一括反映して、CloudWatch Logsに集約させました。
これで各種メディアのアクセスログをCloudWatch Logsに集約させ、ログ分析したい場合はLogs Insightsでクエリ検索できることからログの運用が楽になると考えました。
運用後の課題
ところが、しばらく運用してみて利用者からいくつか課題があると意見をもらいました。
課題
- ログの検索結果が遅い
- Logs Insightsのクエリがわかりにくい
- ログ結果をグラフで視覚化できるようにしたい
その他の問題点としてCloudWatch Logsはログの保存料金に加えて、EC2からログを収集するときにも料金が発生するのですがアクセスログを取り込み始めると一気にCloudWatchのコストが上がりました。
この課題を解決するためにCloudWatch Logsによるログ集約をAWS OpenSearchへ変更して解決してみました。
AWS OpenSearch
AWS OpenSearchは、OSSとして一定の地位を築いているElasticSearchからフォークした検索エンジンサービスです。ログ分析以外の用法としてはエンドユーザーに提供する検索エンジンとしても利用されています。
最初にログ集約基盤を構築する時にOpenSearchにログを集約することも候補に入れていましたが、社内で扱ったことがある人が少なくコストが高いというイメージも持っていたので、CloudWatch Logsでログ集約基盤を構築していました。が、これを気にOpenSearch(ElasticSearch)の使い方を学ぶことも兼ねてログ集約基盤をOpenSearchで再構築してみました。
アーキテクチャ構成図
今回構築するログ集約基盤については以下のとおりです。
本来OpenSearchはVPC内に作成することをAWSは推奨しています。ただ、弊社都合になるのですがメディアサーバーが複数のVPC内に点在してそれらを一つのOpenSearchに集約したいという要望があります。解決策としてALBの配下にOpenSearchを置くことで外部通信できるようにすることを検討しました。
ですがALBにアタッチさせる場合、OpenSearchはIPアドレスタイプのターゲットグループを選択してOpenSearchのプライベートIPアドレスを関連付けるのですが、OpenSearchのプライベートIPアドレスは可変なため定期的にアタッチさせるIPアドレスを見直す必要が出てきます。
定期的に取得するシェルを作ると、今度はそのシェルの面倒を見る必要が出てきて運用負荷が高まると思いました。なので私の考えた結論としてOpenSearchをパブリックに置いて、セキュリティグループで社内からのアクセスと各種メディアサーバーを集約させる集約サーバーからの通信のみに許可する設定にすることで最低限のセキュリティを確保しました。
OpenSearchダッシュボードにログインする場合は、Amazon Cognitoを使って会社のgoogleアカウントでアクセスできるようにすることでユーザー側で新しくパスワード情報を保持せずにすむようにしました。
Fluentd
ログを転送するための手段ですが、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ではほぼ誤差なしで表示されるようになりました。
グラフ作成
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自体初めて触ったので、概念の理解が難しくシャードやインデックスなどなんのこっちゃだと思いました。まだ完全に理解できていませんので、最適化にはほど遠い状態と認識しているので利用者に使い方をレクチャーしつつ継続的に改善できるようにしていきます。