はじめに
この記事は、RDS for PostgresでDisk使用量が急激に増えたときの原因の特定の仕方について、実際に行ったアプローチをまとめる備忘録的な内容になっている。
実際に行ったこと以外書いていないため、「もっと見るべきところあるだろ」とか「こうしたほうが良い」という部分は沢山あると思うが、マサカリを期待している訳ではないというところだけ一応 (フリじゃないよ | д・)チラ)
背景
1週間くらいでRDSのDisk使用量が急激に増え、残Disk容量が5%くらいになっていたことがアラートにより発覚 (もっと早くアラート上がるようにしておけよ...俺)。
グラフにするとこんな感じ (色々情報潰しているのには目を瞑ってほしい)。
6日くらいから、急激に残Disk容量が減り始め、気づいた頃には残り5%になっていたという始末。
このままでは本番DBが落ちて、サービスが停止してしまうという恐ろしい事態に...。
とにかく急いで対応する必要があり色々調べたので、その内容を次にまとめる。
調べた事
RDSのストレージを増やす際にダウンタイムは発生するのか
とにかく急いで対応する必要があったので、ストレージを増やして心の余裕を増やそうと思った。
しかし、ストレージを上げる際にダウンタイムが発生するのであれば、メンテが必要ということもあり、RDSでストレージを増やす際にダウンタイムが発生するのかを調べた。
ほとんどの場合、ストレージのスケーリングには停止が必要ではなく、サーバーのパフォーマンスを低下しません。DB インスタンスのストレージサイズを変更すると、DB インスタンスのステータスは storage-optimization になります。ストレージ変更後、DB インスタンスは完全に動作します。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_PIOPS.StorageTypes.html#USER_PIOPS.ModifyingExisting
ほう...、ほとんどの場合とは...。
もう少し調べてみると、
RDSのストレージのスケーリングは基本的にはダウンタイムが発生しないということが判明した。
ただ、RDSストレージ容量を増やした際には、RDSインスタンスを作り直さない限り、減らすことはできないので可能であればストレージ容量を増やしたくはない。
ストレージを増やすのは最終手段としてキープしておいて、可能な限り他の方法で対応出来ないか調べる。
なぜ急激にDisk使用量が増えたのか
取り急ぎの対応の目処がついたので、RDSのDisk使用量が激増した原因を調査することにした。
まず、Disk使用量が急増し始めた6日付近のRDSインスタンスの変更ログをCloudTrailで見ることにした。
ふむ、RDSインスタンスに対する変更はスナップショットを取るくらいしかしていないようだ...。
次にグラフから、午前4時付近でDiskが急増していることが判明した。ここで怪しくなってくるのが、バッチの存在だ。
バッチの暴走によって急激にテーブルにInsertされるデータ数が増えたのではないかと考え、バッチの変更履歴を確認してみた。
ふむ、バッチはここ最近アップデートされていないようだ。
では、最近レコード数が急増したテーブルがないか過去のスナップショットから比較して調べてみることにした。
ここで、ある重大な事実に気がつく。データベースの容量は殆ど変わっていなかったのだ。
つまり、データベース以外の部分でDiskを喰い潰しているということだ。
となると、ログか...?
RDS内でどのログが容量を喰い潰しているか確認する方法について
結論から書くと方法は調べた限りでは見つけることができなかった。
というのも、RDSの場合 rds_superuser
ロールがPostgresのsuperuserの権限を持っており。その権限でないとログディレクトリを確認することができないことが判明したからだ(実際に打ったコマンドは忘れました)
ログの詳細は、確認できないとはいえ、調べていくうちにwalが怪しいそうということがわかってきた。
walとは?
WALとは「Write Ahead Logging」の略で、日本語訳をすると「ログ先行書き込み」という意味らしい。
WALが何者かというのはこれくらいにしておいて、WALはトランザクションログを残すために使われる一般的な手法らしい。
このWALという単語を見たときに頭を過ったのは、DMSだった。
というのも、つい先日RDSをデータソースとしてCDCタスクをこの手で設定したばかりだったからだ。
DMSはデータベース移行サービスで、稼働中のデータベースを極力ダウンタイムが少ないように移行できるようになっている。
そんなDMSのタスクの1つに、CDC(Change Data Capture)がある。これは稼働中のデータソースの変更(Insert, Update, Deleteなど)を検知して、移行先のターゲットに反映させる機能だ。
このDMSのCDCタスクをRDS for Postgresで実行可能にするためには、RDSのパラメータグループの設定で、 rds.logical_replication
を 1
にして、 wal_sender_timeout
を 0
に設定してやる必要がある。
https://docs.aws.amazon.com/ja_jp/dms/latest/userguide/CHAP_Source.PostgreSQL.html#CHAP_Source.PostgreSQL.RDSPostgreSQL
ここまで来ると、DMSがとても怪しくなってくる。
…案の定CDCタスクが6日から失敗している事が判明した。
犯人はこいつか...。
とりあえず、失敗しているタスクを再開してみる。
すると、RDSのDiskが少しだけ回復した!
このままDMSタスクが稼働し続ければRDSのDiskも解放されて行きそうだ。
...ちょっと待て、またDMSのタスクが失敗している。
なぜDMSのCDCタスクが失敗したのか
DMSのタスクが失敗した原因について調べようとしたが、DMSのタスクのデバッグの取っ掛かりが見つけられなかった。
AWSサポートに助けを求め、ログの設定をここを参考に変更して、再度タスクを再開。
うーん、あるテーブルのところで毎回失敗しているけど、全然原因がわからない...。
これ以上の調査はAWSサポートでも時間がかかりそうとのことだった。
ということで、緊急の対応としてDMSのタスクで使用していたレプリケーションスロットを解放することで、RDSのDisk圧迫の件は対応することにした。
RDS内のレプリケーションスロットの消し方
レプリケーションスロットについて簡単に説明しておくと、レプリケーションをするための状態の管理や、トランザクションログの管理を行っている機構のことらしい(あんまりわかってない)
とにかく、このレプリケーションスロットがレプリケーション先が復活した時に継続してレプリケーションが行えるように今までの変更ログを全部取ってくれていたことが、今回のDisk使用量急増の原因だったようだ。
ということで、DMSタスクは新しく作り直すとして一旦レプリケーションスロットを削除するとこにした。
レプリケーションスロットの削除方法については、調べたら簡単に出てきた。
SELECT * FROM pg_replication_slots;
SELECT pg_drop_replication_slot('<レプリケーションスロット名>')
1つ目の SELECT
文でレプリケーションスロット名を取得して、2つ目のクエリで解放したいレプリケーションスロットを指定すると、レプリケーションスロットが解放される。
レプリケーションスロットを解放した途端、Diskが劇的な回復を見せた。
対応
ここからは、今回の件で実際にとった対応を記載しておく
Disk使用量のアラートを80%の段階で上げるようにする
今回RDSのアラートは5%の段階で発生してとても焦った(本当に焦った)ので、もっと余裕を持って対応ができるように早めにアラートが上がるように設定を変更した。
本当は、80%とかじゃなくて普段の減り方と明らかに違う減り方をしていたら検知するようにしたいが、生憎CloudWatchしか使っていなかったため、今後の課題としておく。
DMSのタスクが失敗したときにアラートが上がるようにしておく
今回の事でDMSのCDCタスクはRDSのDiskに影響を与えることが判明したので、DMSのタスクが失敗したときに検知できるようにしておいた。
やり方は、以下を参考にDMSのイベントサブスクリプションで Source Type
を Replication Tasks
に設定して、 Event Categories
から failure
を選択し、通知したいSNSのトピックを選択するだけ。とても簡単。
https://docs.aws.amazon.com/ja_jp/dms/latest/userguide/CHAP_Events.html
まとめ
今回の件でアラートの大切さと、AWSサポートのありがたさを痛感した。
今回のような思いを2度としないように、アラートの検知と余裕を持った対応ができる体制を整えることを心に誓った。
後日談だが、DMSタスクが失敗していたのはレプリケーションインスタンスのインスタンスタイプが小さかったとが原因だった。
モニタリングログを見てみると確かにSwapが結構使われている。どうやらレプリケーションタスクは結構メモリを喰うようだ。