![見出し画像](https://assets.st-note.com/production/uploads/images/85445021/rectangle_large_type_2_7bbf3894a7f2c7dedcd8426a0e74089f.png?width=1200)
サーバレス開発におけるハイブリッド開発というアプローチ
はじめに
こんにちは!バックエンドチームの上原(@_uhzz_)です。
バックエンドチームでは、サーバレス開発を推進していて、開発ツールの1つにServerless Frameworkを使っています。
Do more with less. Serverless.
より少ない人数でより多くのことを行う。サーバレスで。
上記のキャッチコピーの通り、Serverless Frameworkを使用することで、インフラリソースの管理が少人数でも行いやすくなります。
以前の記事でも詳細に説明されているのでそちらもご覧ください!
そんな中、Serverless Frameworkのローカル開発について調べていたときに、おもしろい記事を見つけました。
サーバレス開発を進める上で、検証環境を用意するときの悩みはつきものだと思いますが、上記の記事で新しいアプローチが紹介されていたので、記事を引用しながらコメントしていきます。
結論
ローカル環境とリモート環境のいいとこどりしたハイブリッド開発がおすすめ
実際のLambdaイベントのコピーをユニットテストやインテグレーションテストの入力に使用する
ユニットテストでは、クラウドプロバイダーSDKをモックする
インテグレーションテストでは、実際のサービスをモックしない
ローカル環境はなにを解決するか
But if your team is composed of several developers you will need to share the development server or duplicate it. The first scenario can create conflicts on the server and the second is expensive. Neither of them is scalable.
複数メンバーで開発する際、開発サーバーを共有するか複製する必要があり、前者はメンバー同士でリソースを共有していることによる競合が発生し、後者はコストがかかります。どちらもスケーラブルではありません。
ローカル環境を構築する以前に、検証環境をメンバー間で共有したり、複製したりすると思いますが、それはスケーラブルではないとのことです。
そこで、可能な限り本番環境と同じ環境をローカルに用意するという流れになります。
So you will try to replicate the production server on each developer's computer and probably add some developers tools to ease the developer experience […]
そこで、各コンピュータに本番サーバを複製し、開発者体験を容易にするためにツールを追加します。
Each local environment is a completely autonomous environment that should behave as close as possible like the production server.
各コンピュータに複製された環境は完全に自律的で、可能な限り同じように動作させる必要があります。
ローカル環境の開発フロー
記事内では、以下の図で説明されています。
![](https://assets.st-note.com/img/1661155115075-Bpx8OvCgY5.png?width=1200)
ローカルにて検証環境を作成し、開発・検証をオフラインで行うことができるので、デプロイによる待ち時間が発生しません。
現状、バックエンドチームの開発フローもこれに近い方法で運用しています。
しかし、記事内ではこれを古い習慣として言及しています。
クラウドプロバイダーを完全にエミュレートするのは不可能
The server you try to duplicate locally is not an empty Linux virtual machine with ten programs, it's a whole cloud provider.[…]
ローカルに複製しようとしているサーバーは、10個のプログラムが乗った空のLinux仮想マシンではなく、クラウドプロバイダー全体です。
Those mocks are by definition not real services so there are some behavior differences between the local environment and the cloud provider. For example, AWS API Gateway emulated by serverless-offline doesn't handle VTL locally the same as AWS does.
(ローカル環境でエミュレートする)モックは定義上、実際のサービスの挙動とは異なるので、ローカル環境とプロバイダーの環境に差分があります。
例えば、serverless-offlineでエミュレートしたAWS API GatewayはAWSと同じようにVTLを扱えない。
これはバックエンドチームでもたびたび話に上がる問題で、記事内で言及されている、serverless-offlineを使用していますが、同じようにエミュレートできない機能に関しては、実際にクラウド上で検証するという方法で運用しています。
Each time a difference creates a bug, you will have to fix the mock or blindly fix the code assuming the real service behavior. As multiple services are used, the risk is much higher than with a monolith architecture.
(ローカル環境とクラウドプロバイダー間の)差異がバグを生むたびに、モックを修正するか、実際のサービスの挙動を想定したコードをやみくもに修正する必要がある。
これは、複数のサービスを利用するため、モノリスアーキテクチャよりもリスクは高くなる。
私自身、エミュレートするコードの修正プルリクエストを過去に送ったことがあり、このコメントは耳が痛いと思いつつ、たしかにすべてをエミュレートしようとするのは途方もないよなと感じています。
リモート環境というアプローチ
Remote development environment - A slower development feedback loop but an exact replication of production
リモート開発環境 - 開発のフィードバックサイクルは遅くなるかもしれないけど、本番環境を正確に再現できる方法
なぜリモート環境を使うべきか
Because most serverless services have on-demand pricing and cloud providers support several IaC (Infrastructure as Code) tools, creating a full cloud environment for each developer is now cheap and easy.
ほとんどのサーバレスサービスは使用しただけの料金(オンデマンド価格)だし、クラウドプロバイダーはいくつかのIaCツールをサポートしているので、開発者ごとに完全なクラウド環境を作ることが安価で簡単になったから。
It allows the developers to validate their features on an exact replicate of the production infrastructure and to never encounter any behavior differences between their development environment and the production.
リモート環境を使うことで、開発者は正確な本番環境のコピーで機能検証をすることができるし、本番環境と開発環境との差分の違いに遭遇することがなくなる。
Serverless Frameworkを使ってインフラリソースをコードで管理しているなら、リモート環境にメンバーごとの環境を用意しようよ、の精神ですね。
ローカル環境とリモート環境を合わせたハイブリッド開発というアプローチ
リモート環境によって、ローカル環境で課題だった本番環境との差分を解消できるものの、検証環境を完全にリモート環境へ切り替えるのではなく、両者のいいとこ取りをした、ハイブリッド開発というアプローチが提案されています。
![](https://assets.st-note.com/img/1661158693205-WKYiTMp8Hq.png?width=1200)
1. すべてのイベントをログに記録する空の関数をデプロイ
The first step is to configure its trigger regardless of what the function is supposed to do. This could be achieved with an empty function. Making it log its input really helps to understand the events.
最初のステップはLambda関数がなにをするかに関係なく、トリガーを設定することです。
これは空のLambda関数でも実現できるかもしれません。
入力をログに記録するとイベントを理解するのに非常に役立ちます。
You deployed once.
これが1度目のデプロイです。
ここで言及しているのは、実際の機能を開発する前に、トリガーされたイベントをログに入力するLambdaをデプロイすることです。
2. 可能な限りのイベントをローカルにコピー
You can use e2e means to trigger it because your function is deployed in a production-like environment.
Lambda関数は本番環境に近い環境にデプロイされているので、そのLambdaをトリガーするためにE2Eテストが使える。
Get the logs and copy all different events locally to be able to replay them later.
あとで再生するために、ログを取得しすべての異なるイベントをローカルにコピーしておく。
ログに入力されたトリガーイベント、つまりLambdaのトリガーとなるJSONがログから確認できるので、それをコピーしておきます。
3. 実際のサービスと統合した、テスト駆動開発を行う
You got real events to replay. You can put them as input of your function in unit or integration tests. You will be able to develop your function knowing its behavior for each event you got without any effort.
実際のイベントをリプレイすることができます。
ユニットテストやインテグレーションテストでそれらをLambda関数の入力とすることができます。
そうすることで、イベントごとのふるまいを把握した上で開発できます。
コピーしておいたJSONを入力データとすることで、テスト駆動開発を進めることができるようです。
たしかに、Lambda関数を開発する際、機能の実装を進めながら都度デプロイを行い、実際のサービスのトリガーが正常に動作するかを確認していましたが、機能を実装する前にトリガーの設定を済ませておくことで、検証におけるデプロイ頻度は少なくなりそうですね。
テスト方法についても言及されています。
In unit tests: Mock all cloud providers' SDKs or client interface to check your code behave as expected, assuming all external services it uses.
ユニットテスト:クラウドプロバイダーのSDKやクライアントインターフェースをモックして、利用する外部サービスを想定してコードが期待通りに動作することを確認する。
In integration tests: Let the application interact with the real services. Those tests are really powerful because it test the production behavior with a short feedback loop
インテグレーションテスト:実際のサービスとアプリケーションを組み合わせてテストします。
これらは短い開発フィードバックサイクルで、本番環境の挙動をテストするので本当に強力です。
4. 実装した機能をデプロイ
You can safely deploy it and expect your e2e validation not to fail.
(Lambda関数はユニットテストとインテグレーションテストの両方を満たしているので)安全にデプロイできます。また、E2Eもおそらく失敗しません。
You deployed only twice.
デプロイしたのはたったの2回です。
If the e2e validation fails you can copy the event of the failure case and add it to your list of events.
You will be able to replay it locally and the debug will be easier.
もしE2Eが失敗する場合、失敗したときのイベントをコピーして、イベントリストに追加できます。
これはローカルで再現できるし、デバッグが簡単です。
まとめ
ローカル環境ではクラウドプロバイダーを完全にエミュレートできないので、リモート環境と組み合わせて検証環境を構築するアプローチは新鮮でした!
また、リモート環境だけで構成する場合のウィークポイントを、テスト駆動開発を必須にすることで解決したいというアプローチは、リファクタリングのしやすさにもつながりますね。
さいごに
バックエンドチームでは、サーバレスやGoを使った開発に興味のある方を絶賛募集しています!
カジュアル面談もやっているのでお気軽にご連絡ください!
この記事が気に入ったらサポートをしてみませんか?