先日、弊社内で利用する Redash のインフラ構成を docker-compose on EC2 な環境から ECS, RDS, ElastiCache に移行し、さらに Redash を v7 から v8 にバージョンアップしました。今回はどのように移行を進めたのか、どのようにバージョンアップ作業を行ったのかを紹介します。
これまでのRedash
Redash サーバは開発合宿の成果物として社内に導入されました。当時は動作することを優先させるために、公式が提供するEC2 AMI から docker-compose を使ってコンテナ群を立ち上げるようにしていました。DBやインメモリストアといったミドルウェアも docker-compose でコンテナとして起動している状態でした。
その後、社内の KGI/KPI や Misoca の SLO の可視化など、利用シーンが広がり社内の重要度が高まってきていました。
そうなると、不安となるのがシステムのレガシー化です。1台の EC2 インスタンス上で全部動かしていたのでコンピューティングリソースの不足が気になりますし、Redash のバージョンアップの際も懸念材料となっていました。
というわけで、レガシーになってしまう前に Redash サーバのインフラを見直し、強く柔軟な構成に移行することにしました。
Redash の新インフラ構成
最初に触れたように、新しいインフラ構成は ECS, RDS, ElastiCache を使ったマネージドサービス中心の構成にしています。
Server サービスには Redash の画面を提供する Docker コンテナを起動するタスク定義を指定いており、このサービスにのみ ALB を経由した外部アクセスができるようにしています。Workers サービスにはクエリを実行等を行う複数のDocker コンテナを集約させたタスク定義を指定しています。
Workers サービスが起動する Docker コンテナの種類は公式のdocker-compose.ymlと同じにしています。こういう構成にしようというこだわりが無かったので公式に合わせました。後々変えていくことはありうると思います。
インフラ構成のコード化
インフラ構成のコード化 (IaC) は、今では当たり前のようになってきましたが、ここで今一度 Redash のインフラ構成のコード化によって得られる恩恵は何であるかを列挙します。
- コードがドキュメント代わりになる
- コードレビュー、CI/CDといったソフトウェア開発で用いられる手法がインフラでも使える
その他にも様々な恩恵・利点は確かにありますが、今回は上記2点に絞りコード化の作業を進めました。
インフラ管理には Terraform を利用しました。社内でも既に AWS リソース管理に使われており、ほぼこれ一択という感じで決定しました。もっと様々な観点から他のツールも検討するべきとは思いますが、社内向けのアプリケーションであることと、自分以外の作業者でも問題無くコードが読めること (ドキュメント代わりになる) を重視した結果、社内で広く使われているツールを採用することにしました。
Terraform のファイル構成は以下の通りです。AWS のリソース毎にファイルを切り、 provider や Terraform 自身の設定は main.tf に集約させています。
. ├── README.md ├── ecs.tf ├── elasticache.tf ├── lb.tf ├── main.tf ├── rds.tf ├── task-definitions │ ├── redash-server.yaml.tpl │ └── redash-workers.yaml.tpl └── vpc.tf
ちなみに、 task-definitions の yaml ファイル群はこちらの Issue コメントを参考にして、一度 yamldecode したオブジェクトを jsonencode して読み込むといった一手間を加えています。
GitHub Actions を使ったテスト、apply の自動化
プルリクエスト時の terraform plan や master ブランチ更新時の terraform apply といった事を自動で行うために GitHub Actions を使ってこれらを実現します。HashiCorp 社の公式テンプレートがあるので、今回はこちらを利用しました。
プルリクエスト単位では fmt
-> init
-> validate
-> plan
の順番で実行し、 plan
を除いた全て成功すればグリーンとしています。また、 plan
の結果についてはプルリクエストのコメントに残すようにもしています。この辺の設定も tf_actions_comment: true
とするだけで良いのでとっても簡単です。 master ブランチ更新時 (= PR マージ時) には上記サイクルに加えて最後に apply
を実行して、実際に Terraform の定義と AWS リソースの状態を合わせるようにしています。こうすることで、 master ブランチ == AWS リソースの状態となり、常に最新の状態を確認できるドキュメントとしても十分に機能してくれます。
……と、ここまで hashicorp/terraform-github-actions の使い方等を紹介しましたが、どうやら記事執筆時点ではこのリポジトリはアーカイブ状態になっていて、今後は積極的な開発やメンテナンスが行われないようです。今後は hashicorp/setup-terraform の使用が推奨されますので、皆様もこちらを使う方が良いかと思われます。私自身使ったことは無いので紹介だけ。
作業時点では hashicorp/terraform-github-actions リポジトリもアーカイブになっていなかったので、コミット履歴等を見るに最近決まったみたいですね。設定したばかりなのにまた書き直さないといけないのか (とほほ)。
Redash v7 → v8 へのバージョンアップ
Terraform による IaC と、GitHub Actions による CI/CD のセットアップが完了し、残すは Redash アプリケーションのバージョンアップ作業のみとなりました。ここまでくれば、バージョンアップ作業は Terraform のコードをいじって PR をマージするだけで作業は完了してしまいます。
resource aws_ecs_task_definition
が持つ container_definitions
で設定する image
の値を変更します。 Redash の公式 Docker イメージは Docker Hub で管理されているので、そこから v8 系のタグを探して Terraform 側で設定してあげましょう。
バージョンアップのPRをマージすることで GitHub Actions から terraform apply
が実行され、 AWS ECS のタスク定義とサービスを更新します。サービスは自らのタスクを入れ替えてデプロイが完了します。
NOTE: Terraform を使った AWS ECS リソースの更新について
今回、AWS ECS 周りのコード化〜自動デプロイまで全て Terraform で行いましたが、実際にやってみるといくつか気になる点が出てきました。
まずは、タスク定義のリビジョンが更新されず、最新のリビジョン番号の内容を上書きしてしまう点です。例えば前回のデプロイ内容をリビジョン番号から辿れなくなってしまう問題があります。リビジョン番号が更新されないのは resouce aws_ecs_task_definition
の仕様であり、私も事前に知ってはいましたが、コード自体は Git でバージョン管理できているしロールバックするのも git revert
等やり方は色々思い付くので大丈夫だろうと思っていました。しかし、やはり AWS 上でもリビジョンは更新されてほしいという気持ちもあります。
次に、 ECS へのデプロイ戦略についてです。社内向けアプリケーションということもあり、デプロイは ECS タスク総入れ替えという結構豪快なやり方を取っていますが、ダウンタイムやロールバック方法についてよりシビアに考えるのであれば、ローリングアップデートや blue-green デプロイといった方法でデプロイができるのが好ましいでしょう。 terraform apply
ではこの辺りの融通が効かず、難しい印象を受けました。 Terraform で工夫するよりも、コード管理は Terraform, デプロイは他のツールを使うといった併用の形を取っても良いと思います。
今回はデプロイについても Terraform で行うようにしましたが、こちらについては他のデプロイツールの使用も検討して良いかもしれないという個人的な学びがありました。
まとめ
弊社内で利用している Redash のインフラを ECS やその他マネージドサービスへの移行と IaC 化、そして自動デプロイを使った v8 へのバージョンアップについて紹介しました。
まだまだ詰めきれていない部分も多いですが、こちらについては少しずつ磨いていければと考えています。ひとまずは移行が完了してホッとしています。
採用
今回の作業は SRE 業務の一環として行われたものです。 SRE 職種では IaC でいい感じにやっていくことが好きな方々を募集しています。
開発メンバー id:mizukmb でした。