株式会社メドレーDeveloper Portal

2023-12-22

稼働中の Amazon Aurora PostgreSQL を暗号化した話

はじめに

こんにちは。 プロダクト開発室第二開発グループ所属の古川です。

私は 22 年新卒としてメドレーに入社し、現在はインフラとアプリケーションを 5 : 5 の割合ぐらいで開発・運用・保守などを行っています。 学生時代からインフラ周りに触る機会も多く、入社前に AWS Certified Solutions Architect - Professional を取得しました。

大のコーヒ好きで自宅で生豆から焙煎をし、毎朝豆を挽き、抽出したコーヒーを飲むことが日々の幸せです。 好きなコーヒー豆の種類は インドネシア・スマトラ島産・アラビカ種のマンデリン( G1 )の中深煎りです。

 dev202312 000

さて、今回の記事では私が携わっているプロダクトで運用している Amazon Aurora(以下 Aurora )を暗号化した時の検証・暗号化の作業について紹介させていただきます。

私が検証を始める前の Aurora 周りに関する私自身の認識は以下の通りでした。

  • AWS のサービス全体は大体知っている
  • 論理レプリケーションというものが存在することは知っており触ったこともあるが、実際に稼働中のものに対して実施したことはない
  • 非暗号化の Aurora DB クラスターを暗号化するのは大変であるという理解

上記のような認識の新卒エンジニアが、検証をしながら大きな問題なく暗号化作業を実施した過程を紹介できればと思います。

暗号化を実施する Aurora とその周辺アプリケーションについて

影響範囲と暗号化をする Aurora DB クラスターについて

今回暗号化を行う Aurora DB クラスターの周辺アプリケーションはざっくり以下のようになっています。

 dev202312 001

実際に暗号化をする Aurora DB クラスターのエンジンは Aurora PostgreSQL で、主に AWS Fargate 上にある Ruby on Rails (以下 Rails )で書かれた API サーバからのアクセスがあります。 インフラ構築は IaC ( Infrastructure as Code ) ツールの Terraform で管理しています。 また、暗号化対象の Aurora DB クラスターのデータ量は 5GiB 以下で比較的容量は小さめです。

Aurora DB クラスターの周辺について

複数プロダクトへ同時にアクセスを行うクライアントから、本クラスターにはアクセスが頻繁にあります。 そのため長時間読み込み処理ができないと他のプロダクトにも影響がでる可能性があります。

また、本 Aurora DB クラスターに対して書き込みが行われるメイン機能が実行されるタイミングでは、アプリケーションへの影響を最小限にしたいと考えました。 API サーバ内では基本的にロールバック処理がされているので、データ不整合は起きませんが処理に失敗はします。 当たり前ですがユーザ体験は悪くなるので、できるだけ失敗しないように暗号化作業を行う必要があります。

実施方法の検討

実施方法の検討をする上で以下の観点を意識しました。

  • 暗号化を実施する時間帯
  • 暗号化するためにかかる時間
  • 移行までの準備と労力・アプリケーションコードへの依存

暗号化を実施する時間帯

前提としてライターインスタンスの停止は 10 分程度にしたい という目標のもと時間帯を検討しました。この時間は暗号化実施時に予期せぬことが起き、元の状態に戻すことが必要になった時も踏まえ、できるだけ短く・かつクライアントの影響範囲が比較的少なくなるように決めたものです。 また、暗号化を実施する Aurora DB クラスターは複数プロダクトからのアクセスがあります。その中でも、今回は書き込み処理が行われる機能の利用が少ない時間帯に実施することにしました。

Amazon CloudWatch の Logs や Datadog の情報をもとに、あるメイン機能は夜間・休日の利用が少ない傾向にあり、特に土曜日の深夜 26:00 が 1 番少ないと判明したのでその時間に実施することにしました。

暗号化するためにかかる時間

暗号化をするために行う作業はいくつか存在し、その内容とかかる時間が異なります。 例えば、 Rails がアクセスする DB ホスト名を変更しただけだとコネクションプールが残ってしまい、変更前の非暗号化クラスターに接続しようとしてしまいます。そのため、ホスト名を変更した後に AWS Fargate サービスの再起動が必要で、その時間も安定稼働までに確保しなければなりません。

以上のような時間が発生することと、ライターインスタンスの停止は 10 分程度にしたいという目標を踏まえ、検証時に時間を計測しました。

移行までの準備と労力・アプリケーションコードへの依存

基本的に 1 人で検証・移行作業を行う、かつ私自身が別のプロジェクトも並行して進めているため、準備に時間がかかりすぎてしまうものは採用しないことにしました。 そのためできるだけ変更箇所は少ないもの、特にできればアプリケーションコードの変更が極力ない方法というのを検証時点で決めておきました。

上記を踏まえ、以下の 3 つの方法で検証をしたのでそれぞれについて説明します。

  • スナップショットを用いて新規暗号化済みクラスターを作成する方法(方法 1 )
  • AWS Database Migration Service (以下 AWS DMS )を用いて継続的論理レプリケーションを使用する方法(方法 2 )
  • pg_dump & pg_restore を使用する方法(方法 3 )

スナップショットを用いて新規暗号化済みクラスターを作成する方法

こちらの方法は Web 上で「 Aurora 暗号化 後から」などと検索すると良くヒットする方法です。 すでに起動している非暗号化クラスターのスナップショットを取得し、そのスナップショットから暗号化済みの新クラスターを復元する方法です。データの不整合が起きないように、スナップショットを取得する段階から書き込みがされないように制限する必要があります。

準備することはほぼなく簡単に実施できるのですが、ライターインスタンスの停止時間が長すぎるため不採用としました。 検証時ではそれぞれ以下の時間だけかかり、ライターインスタンスの停止許容時間を大きく上回り現実的ではないと判断しました。

  • スナップショットの取得時間:3 分
  • 暗号化済み新クラスターの作成:約 30 分
  • アプリケーションから接続する DB ホストの切り替え:数秒程度
  • AWS Fargate の再起動:3 分強

AWS DMS を用いて継続的論理レプリケーションを使用する方法

 dev202312 002

こちらの方法は AWS DMS で論理レプリケーションを行い、旧クラスターの変更を検知して、新クラスターに反映させる方法です。 想定しているライターインスタンスの停止時間は、旧クラスターを読み取り専用に変更 〜 レプリケーションが追いつき DB ホストの変更が終わるまでの時間です。 この停止時間は 5 分弱と想定しており採用するつもりだったのですが、AWS DMS の移行前評価を行った時すぐに実施できないことが明らかになりました。

移行前評価の詳細は省きますが、主な原因は Large Object ( LOB ) の制約で、150 以上のカラムがそのままの状態では移行することができかったためです。 そのほとんどが null 許可の制約だったので解消しようとしましたが、テーブル構造だけでなくアプリケーション側の変更もいくつか必要だったため、不採用としました。

また論理レプリケーション設定を ON にするためには事前にライターインスタンスの再起動が必要で、結局別のタイミングで少しのダウンタイムは発生してしまうこともあり実施しないことにしました。

pg_dump & pg_restore を使用する方法

 dev202312 003

こちらの方法は、 PostgreSQL クライアントに標準で提供されている pg_dumppg_restore を使用する方法です。 実施する時は、 AWS Systems Manager のセッションマネージャーを介してクラスターにアクセス可能な状態を作り、コマンドを実行します。

結論からいうとこちらの方法を実際に採用しました。 理由は主に以下になりますが、暗号化するクラスターのデータサイズが 5GiB 以下だったので、本方法でもそこまで時間がかからなかったのが一番の理由です。

  • 検証時にライターインスタンスの停止時間は 8 分程度だったこと
    • pg_dump & pg_restore : 4 分弱
    • アプリケーションから接続する DB ホストの切り替え:数秒程度
    • AWS Fargate サービスの再起動: 3 分強
  • 移行時に実行するシェルスクリプトを用意すれば良いので準備が大変ではないこと
  • 検証時に開発環境で実行した後も不具合は確認されなかったこと

またこちらの方法は Amazon Aurora PostgreSQL をマイグレーションする時の方法として公式にも掲載されていたので、迷いなく行えると思いました( pg_dump and pg_restore )。

それぞれの検証結果をまとめると以下のようになります。 今回のユースケースでは方法 3 の pg_dump & pg_restore を使用した暗号化が良いと判断し、実施することにしました。

項目方法 1方法 2方法 3
暗号化するためにかかる時間40 分弱2 〜 5 分程度5 〜 8 分程度
移行までの準備と労力
アプリケーションコードへの依存なしありなし

実施内容

行った作業内容は以下の通りで、それぞれ説明していきます。 移行時は、私とは別にもう 1 人の方にアラートなどの状況を Sentry 等で確認していただき、予期せぬ問題が起きていないかを確認します(当たり前ですが、ダブルチェックはすごく大事)。

  1. API アプリケーションから Aurora に接続する時には DNS Alias で名前解決するように設定する
  2. 暗号化された Aurora DB 新クラスターを Terraform で構築する
  3. 旧クラスターを読み取り専用に変更する
  4. 移行スクリプトを実行する(実行時間: 3 分強)
  5. DNS Alias を旧クラスターから新クラスターに切り替える(実行時間:数秒)
  6. AWS Fargate サービスを再起動する(実行時間: 3 分強)

1. API アプリケーションから Aurora に接続する時には DNS Alias で名前解決するように設定する

AWS のマネージドサービスである Route 53 で、レコードに旧クラスターのエンドポイントを指定します。エイリアスの名前は、取得済みのドメインからサブドメインを指定しています。API アプリケーションから、そのエイリアス名で DB ホスト名を名前解決できるように設定を変更しておきます。 本 API アプリケーションは DB のホスト名は Parameter Store で管理していたので、AWS Fargate の再起動を行い起動時に新しい値が読み込まれるようにしました。

2. 暗号化された Aurora DB 新クラスターを Terraform で構築する

移行の前に暗号化済みの新クラスター作成しておきます。暗号化には AWS Key Management Service を使用します( Terraform の公式リファレンスを見ればすぐに構築可能です)。

余談ですが、 Terraform で Aurora クラスターを作成する時、 aws_rds_cluster に記載されているサンプルでは、暗号化設定が記述にはないので作成する時は気をつけた方が良さそうです。

3. 旧クラスターを読み取り専用に変更する

旧クラスターに紐づいているパラメーターグループの default_transaction_read_only を 0 から 1 に変更します。この作業が完了すると旧クラスターへの書き込み処理はされなくなります。

4. 移行スクリプトを実行する(実行時間: 3 分強)

移行するためのスクリプトを実行します。 実際に実行した時には、いくつかのオプションをつけましたが、基本的にやったことは、 pg_dump したものを暗号化された Aurora DB 新クラスターに pg_restore するだけです。 2 で構築した新クラスターは、旧クラスターと同じプライベートサブネットに作成しておけば旧クラスターにアクセスできる状態になるので、移行コマンドは問題なく実行できると思います。

$ pg_dump -h <旧クラスターのホスト名> -p <ポート番号> -U <ユーザー名> -d <データベース名> | \
    pg_restore -h <新クラスターのホスト名> -U <ユーザ名> -d <データベース名>

5. DNS Alias を旧クラスターから新クラスターに切り替える(実行時間:数秒)

移行が完了したら、 1 で作成した DNS Alias を新クラスターに向くように変更します。 移行時は TTL( Time to Live )はできるだけ短く設定( 15 秒)することで、ホストの切り替えに時間がかからないようにします。

6. AWS Fargate サービスを再起動する(実行時間: 3 分強)

ホストが切り替わったら、AWS Fargate サービスを再起動します(以下コマンドを実行するだけ)。 先述しましたが、本作業を行わないと Rails から Aurora DB クラスターへの DB コネクションプールが残ってしまいます。 すでにホスト名は変更されているためコネクションが失敗すると自動で新しく接続をし直し、失敗後は意図したホスト(暗号化済みの新クラスター)に接続されますが、接続先を必ず変更したいので、サービスの再起動を行います。

$ aws ecs update-service --profile <プロファイル> \
    --cluster <クラスター名> \
    --service <サービス名> \
    --force-new-deployment

本作業は検証時に DNS Alias を変更しただけだと接続先が変わらないことに気付き入れた対応でした。

ここまでの作業が完了したら実際に動かしているアプリケーションに不具合がないかを確認し作業完了です ✨

さいごに

まとめ

今回は稼働中の Amazon Aurora PostgreSQL を暗号化した時の検証・実施内容を紹介しました。

今回の方法は、対象のプロダクトで行う上での一例ですが、比較的少ないダウンタイムで作業を完了することができました。

また、 pg_dumppg_restore 実行時に並列度を増やすことでもっと早く移行作業は完了できると思いますが、暗号化するデータの容量が大きくない、Aurora クラスター側に負荷をかける必要がなかったので今回は 1 で実行しました。

私自身本作業を終えて、パラメータグループの細かい設定についての知見も増え、この後にやった他の施策にも活きているので自分自身の成長にも繋がりました。 先輩方に意見を求めたりもしましたが、施策自体は 1 人でやり切れたことはとても良い経験になりました。 普段は、 AWS のマネージドサービス内で行えないかを真っ先に考えるのですが、マネージドサービスに依存しない形で行うこともユースケースによっては必要だなと再認識できた良い経験でした。 少しでも私の経験が誰かの役に立てばうれしいなと思います。

このように、メドレーは新卒・中途に関わらず裁量を持って施策を実行できる環境です。インフラやアプリケーションにとらわれず様々なプロジェクトに携われる開発チームにジョインしてみませんか?

https://www.medley.jp/jobs/

株式会社メドレーDeveloper Portal

© 2016 MEDLEY, INC.