for Startups Tech blog

このブログのデザインを刷新しました。(2023/12/26)

【フォースタ テックブログ】Scrum Fest Mikawa 2020で登壇してきました

 

どうも、むらばやし(@bayashimura)です。

先日Scrum Fest Mikawa2020@onlineに参加して「SIerからアジャイル片手にスタートアップに飛び込んだ話」というタイトルで登壇してきました。

どんなイベント?

現在全国各地で勃興しているScrum Festの三河リージョンです。
スクラムアジャイルを実践しているみんなが集まって発表したりネットワーキングしたりします。
他にはScrum Fest Osaka やScrum Fest Sapporoなどがあります。

まぁ大体オンラインになっちゃったので、その地に行くことなくイベントに参加することが出来ますが、本来は各地方でアジャイル盛り上げようみたいな感じのイベントです。
今回のScrum Fest Mikawa2020に関してはオンラインセッションと三河現地でやる三河セッションがあり、ハイブリッドなイベントでした。

発表内容
https://speakerdeck.com/murabayashi/sierkaraaziyairupian-shou-nisutatoatupunifei-biip-ndahua

何を伝えたかったの?

社員数5000人を越える大きなSIerだろうが、社員数100人にも満たない小さなスタートアップだろうが
そこでは同じような人間が働いていて、同じような苦悩を抱えているんだよっていうことです。
SIerってソフトウェア業界の中では技術の横展開ができないケースが多い特殊な立ち位置だと思っていて、私も前の会社に所属しているときは自分がここでやっていることが他の会社で通用するのかと、勝手にエンジニア、アジャイルコーチとして劣等感を持っていました。
ただそこで得た能力や経験は絶対に無駄にならないし、役に立つということを伝えたかったです。

何を得たかったの?

アジャイル系のコミュニティにはずっと参加者として関わっていました。
ただ絶対に登壇してみんなからフィードバックもらって交流したほうが楽しいな、学び多いなと思い今回Scrum Fest Mikawa2020で発表しました。
実際発表したあと、色んな方から反応をもらえたり、色んな方と交流出来たりと想定通りの価値も得られたのですが、それ以上によかったことがありました。

自分自身としても発表という形で自分のしたことをまとめることで、まとめる前は「俺、結構よくやってる方なんじゃないか」と思い上がっていたのですがスライドにしてみると「あれ、俺、実は全然大したことしてない、、、?」となります。膨れ上がった自分の自尊心を一瞬で打ち砕けるのでオススメです。
勢いでproposalを出して、採択された後に話のストーリーを本気で考え始めどうすれば自分の経験が聞いた人の学びになるかを一ヶ月くらい唸りながら書いた甲斐がありました。

今後どうしていくの?

自身のレベル、やってきたことの価値を測るため、定期的に発表していきたいです。
またフォースタートアップスのValueの一つにBe a Talentというものがあります。
スタートアップスの良きパートナーになるため、社員が外に出て自らの生き様を社会に発信すること、タレントになることを称賛するものです。
幸い会社のバリューとも合致するのでちょこちょこ出ていこうかなと思っています。
そのためにも様々なチャレンジをして、聞く人の学びになるようなことをやっていければなと思っています。

フォースタートアップスではエンジニアを募集しております!
Be a Talentしたい人、アジャイルな開発したい人、スタートアップに興味ある人、是非ご応募ください!

【フォースタ テックブログ】モノリシックなシステムをマイクロサービス化した実例とその効果について

 

フォースタートアップスで「STARTUP DB」(スタートアップデータベース)の開発を担当している竹内 強(@manbo34)です。

前回はSTARTUP DBの技術を紹介しました。今回は小規模ですがマイクロサービス化に挑戦したときの話をさせていただきます。

tech.forstartups.com

まず、元々、弊社のシステムは次のような構成になっていました。弊社では大きくSTARTUP DB(以下、SDB)と弊社内で利用しているタレントエージェンシー支援システム(以下、SFA/CRM)があるのですが、SDBの一部はSFA/CRMと同一のシステムとして動いていました。これは開発リソースが少なく、開発速度優先で開発していたためです。

これを混在していたSDBの一部とSFA/CRMをDBごと完全に分離し、別システムとして切り離しました。

マイクロサービス化に挑戦した理由は、次の課題があったためです。

  • 関連性のない情報が混在している
  • 関連のない修正で関連のないサービスが停止する可能性がある
  • 開発効率が落ちてきていた

関連性のない情報が混在している

弊社内で利用しているSFA/CRMとSTARTUP DB管理システム(以下、SDB管理)の情報を同一のデータベースで管理していました。
当時、初期開発をしたエンジニアと運用しているエンジニアが同じであった為、権限の問題で大きな問題となることはありませんでしたが、まだ仕様の詳細を把握していない新しく入社するエンジニアが間違えて情報に手を加えてしまう可能性がありました。

関連のない修正で関連のないサービスが停止する可能性がある

SDBはSFA/CRMの資産を利用していません。しかし、SFA/CRMとSDBが同一のシステムで運用しているので、SFA/CRMのトラブルでシステムが停止するとSDBも利用できなくなってしまっていました。

開発効率が落ちてきていた

SFA/CRMの開発チームとSDBの開発チームが同じリポジトリで開発していました。結果、コンフリクトや検証環境の取り合いが発生するようになってきていました。チームを倍増していく計画もあり、対処しておきたい問題でした。

これらに対処するため、マイクロサービス化をスタートしました。
今回はそのプロジェクトについてお話します。

計画 〜当初の構想とフェーズ分け〜

マイクロサービス化プロジェクトは全メンバーで行うわけでなく、SFA/CRMの開発と並列して行いました。そのため、SFA/CRMの開発に大きな影響が出ないことを重要視しました。これを達成するため、マイクロサービス化プロジェクトを4つのフェーズに分けて計画しました。

現行システム

当時のシステム構成図を掲載します。
計画した最終的な構想は次のとおりです。

データベースを分割し、それを扱うアプリケーションを作成します。
元々あるシステムは分割したデータベースのModelだけAPI経由で取得するようにし、画面などは変更せず、最低限の修正でデータベースの分割を実現します。
それでは、この構成に至るまでの道筋を説明します。

フェーズ1:SDB 管理 API の作成

  • SDB管理のデータを取得するAPIを実装
  • SDB管理のデータはAPIを経由して利用するように変更

SDBのデータベースに直接繋げられなくなるため、事前にSDB管理のデータはAPI経由で参照・編集するように変更します。
この対応を先に実装することでインフラ層を変更してもAPI Modelでインフラの変更を吸収できるため、以降のフェーズでControllerやViewを修正せずにプロジェクトを進められるようになります。

フェーズ2:SDB API と SDB管理 API の統合

  • 元々存在していたSDB APIをフェーズ1で作成したSDB管理 APIを統合

フェーズ1で作成したControllerに元々存在していたSDB APIを統合し、システムを分割する準備を行います。
APIを利用するためのURLは変わらないためSDB側の修正はありません。

フェーズ3:データベース分割

  • SFA/CRMとSTARTUP DBのテーブルをそれぞれのデータベースに分割します

データベースを分割します。対象のテーブルの接続先を変更するだけでシステムの変更は必要ありません。

フェーズ4:システム分割

  • SDB管理 APIを別システムに移行する

システムを分割します。リポジトリは別になりますが実装自体は修正せずに移行します。このフェーズで行う主な作業はインフラ構築です。

上記手順であれば、SFA/CRM開発への影響を少なくしつつ、リリースできると判断しました。
次に実際どのようにプロジェクトが進んだかをお話します。

実績 〜計画の変更理由と対応内容について〜

実際に作業をすすめてみたところ、計画通りに進めることができませんでした。ここから計画と実績で乖離していた部分について説明します。

計画通りに進められなかった主な原因は修正しない予定だったSDB管理のView部分に大きく修正が必要になったためです。

マイクロサービス化プロジェクトのAPI ModelにはRESTful APIのORMであるHerライブラリを選択しました。Herライブラリを利用したことでAPI経由のモデルもActiveRecordのモデルと同等に扱えるため、Controller層の修正を大きく抑えることができます。ただ、KaminariといったGemやRailsの機能を使えなくなる箇所が発生し、それらに対応するため大量のラッパークラスの開発が必要になりました。また、独自のラッパークラスなどを利用することで保守の難易度も上がってしまいました。

Gemへの対応が必要になりフェーズ1の終わりが見えなくなったこと、本来解決したかった開発効率の改善が達成できなくなることから、1ヶ月半ほどたってから計画通り進めることを断念しました。

計画を変更し、最終的に次の構成を目指します。

計画との差分は画面も移行対象としたことです。
移行対象が増えますが、利用するモデルがHerからActiveRecordになるので全体の修正量は減ります。

実際の各フェーズについて説明していきます。

フェーズ1:SDB 管理 API の作成および画面分割

  • View, Controller, Modelの分割
  • SDB管理 API Controllerの作成
  • SFA/CRMはSDB管理のデータはAPIを経由して利用するように変更

Rails エンジンを利用して同じリポジトリ内でシステムを分割しました。
それ以外については、計画通りです。

フェーズ2・4

フェーズ2も大きく計画を変更したフェーズのひとつです。
元々フェーズ2はフェーズ3でSFA/CRMの開発と並行して開発するための準備フェーズでした。フェーズ4で捨てるコードも多く、計画遅れもありスケジュールの短縮を狙いフェーズ2・フェーズ4を同時に行いました。

問題点はフェーズ4を同時に行うことでリリースできなくなったことです。このフェーズからSFA/CRMのリリース内容を常に把握しフェーズ2・4用のブランチへ取り込む作業が発生してしまいました。

フェーズ3

フェーズ3に関しては元々計画していた内容と差異がなく、ソースコードの修正もほぼ無いフェーズなこともあり予定通り進めました。
これにてリリース完了です!

振り返り

モノリシックな構成のシステムをマイクロサービスとして実際に移行した取り組みについてご紹介しました。

振り返ってみて、最初に取り組みの全体像を把握して計画を行うことが重要だと感じました。今回は前提として、複数あるサービスの稼働は停止せずにマイクロサービス化を進めるという要件がありましたので、フェーズを分けて段階的に移行することでダウンタイムを発生させない計画を立て、結果として一度も稼働を停止すること無く完遂できました。

また反省すべき点として、今回特に気を使って計画し、フェーズ分けを行いましたが、作業を一定進めてから問題が発現し、計画の見直しが必要になりました。
一度立ち止まって俯瞰して現状を把握したうえで判断し、計画を見直すこと自体は行われるべきですが、判断のタイミングを逃すと全体のスケジュールへの影響が大きく出てしまいます。

計画の振り返りと見直しが最適なタイミングで行われることが大切だと感じられたプロジェクトでした。

マイクロサービス化の効果

メインデータベースの利用がしやすくなり、また個別のサービスの修正に他のシステムへの影響を気にしなくて済むようになり、各自が担当するサービスの開発に集中できるようになったのは最大の効果であり、今後の開発効率へ大きく影響します。

さらに、各サービス間の依存が無くなったことで、一方でトラブルが発生しても他方に影響を与えることが少なくなりました。そしてシステムや環境が分離されたことで、お互いのシステムを意識しなくともリリース・デプロイできるようなりました。

今後の開発について

弊社ではエンジニアの組織が拡大する中で、各エンジニアの責任の範囲を明確化しています。マイクロサービス化によってバックエンドをAPIにして、フロントエンドと分離する取り組みを引き続き行っていきます。
バックエンドとフロントエンドそれぞれの役割と担当範囲が明確になっていくので、お互いがそれぞれの範囲の開発に集中できる環境が整いつつあると言えます。

日本No.1の成長産業支援プラットフォームをテクノロジーで実現するために、バックエンド・フロントエンドそれぞれエンジニアがさらに必要で、仲間を募集しています。

興味がありましたら是非話を聞きに来てください!
お待ちしております!!

【フォースタ テックブログ】フォースタートアップスのプロダクト開発におけるデザインの役割

フォースタートアップスでデザイナーをしている甲斐(@MeguruKai)です。
フォースタートアップスのクリエイティブチームである「テックラボ」に所属しています。私が担当している社内向けプロダクトの開発チームでは、PO(プロダクトオーナー)、エンジニア数名、デザイナー(私)といった体勢で開発を行っています。私の主な仕事はUXデザインと画面のUIデザインですが、機能のアイディアを創出するためにデザインシンキングのフレームワークを利用したワークショップのファシリテートなども行います。経営陣やユーザーとの意識あわせの時間なども設けられています。

使用ツール紹介

現在開発中のプロダクトにおける画面デザインには、主にAdobeXDを使用しています。

既存の画面の修正に加え、新機能の開発に必要なワイヤーフレームやプロトタイプの作成などが私の主なタスクです。AdobeXDの他にも作業工程において必要な場合は、FigmaやSketchといったプロトタイピングツールを状況に合わせて使い分けています。

エンジニアとの連携方法

エンジニアとの連携は元のデザインファイルを共有する場合もありますし、XDの開発モードを利用することもあります。アセットの共有はXDの開発リンクからDL出来るように設定していますが、必要に応じてデザイナーが書き出して渡す事もあります。
フォースタートアップスのテックラボでは特定のツールやルールに縛られすぎる事無く、状況に応じてツールやデータの受け渡し方法を変えて柔軟に対応しています。

こうした連携の仕方が出来ているのは、ツールの使い方や開発そのものが私たちの目的ではないからです。
多くのデザイナーにとって、制作のタスクをアサインされてそれをこなすことはそれほど大きなチャレンジではないでしょう。デザイナーにとって本当の仕事は、制作物の先にある課題を見据え、その解決をデザインを通して行う事です。
それはたった1つの小さなバナー制作でも、大企業のサービスサイトのデザインでも同じはずです。
こうした課題解決へのアプローチは大抵デザイナー1人では対処出来ません。

デザイン業務の進め方

幸いにも社内向けプロダクトはユーザーである社員が目の前にいますので、日々ユーザーヒアリングを重ね改善を行っています。
フォースタートアップスでのデザイン業務は単純なデザイン作業ではなく、UXやUIの改善に必要なユーザーテストやユーザーヒアリングを行ったり、情報設計やチームビルディングにデザインの知見を生かせるといった特徴があります。

こうした作業もデザイナー1人ではなく、チームで会話を重ねて対応策を練っていきます。そのためのミーティングも行き当たりばったりに何時間もやるのではなく、デザインシンキングやアジャイルフレームワークを使った手法を導入しています。1人で悶々と課題解決の為のデザインに向き合うと言うよりはチームでコミュニケーションを取りながらアイディアを発散させて進めていきます。
今ではアジャイル開発といえば聞き慣れた言葉ではあると思いますが、とてもデザインシンキングと親和性の高いものだと感じています。
どのようなアプローチであっても、良い組織で良いプロダクトを生み出したいという思いは同じかもしれません。

アジャイルについてデザイナーの私が詳しく知れたのも、社内の「勉強会」で個々人の持つ知識の共有があったからこそですし、お互いの知見を共有することはチームビルディングに大きく貢献していると思います。

エンジニアとデザイナーが同じチームで取り組める理由

私たちフォースタートアップスは、『進化の中心』にいることを選択する挑戦者達のために「日本No.1の成長産業支援プラットフォームをテクノロジーで実現する」というミッションを掲げています。

このミッションを達成するために行っている社内向けプロダクト開発も、社で展開しているWebサービス「STARTUP DB」も、それぞれのチームがプロダクトのゴールを掲げ全員で共有しています。
私たちはビジョンドリブンの組織です。
全員が同じ思いを持って最後まで突き進む為には、チームビルディングが何より重要です。
私たちはコミュニケーションを重視して視線合わせの時間と努力を惜しみません。

これはデザイナーにとって非常に重要な文化だと私は思っています。ビジョンやゴールがはっきりしないプロダクトの場合、そのデザインタスクは非常にやっかいなものだからです。ゴールがはっきりしていないと、課題設定が出来ないので制作するべきクリエイティブの正解が見えません。

デザインや開発の手を止めてチーム全員で課題を探ることは一見遠回りに思えるときもありますが、より良いプロダクト作りに重要な要素の1つなのです。デザイナーチームが独立している組織も多く存在しますが、フォースタートアップスではエンジニアとデザイナーが同じチームに所属し、デザインスプリントなどを行う事で全員の目線を合わせています。

おわりに

テックラボではチームの目線を合わせる時間を設けながら、全員がスタートアップのために力を注いでいます。ミッション達成の為にはまだまだ解決すべき課題が山積みです。日々メンバーが仮説と検証を繰り返しながら力を合わせ協働しています。
そして、さらなる組織の進化の為にも新しい刺激をもたらしてくれる仲間が必要です。全ての試みはまだまだデザインの力で良くしていけると感じているからです。
「日本No.1の成長産業支援プラットフォームをテクノロジーで実現する」というミッションに共感し、デザインの力を活かして一緒に課題に立ち向かいたいと思っているデザイナーさんの参画をお待ちしております。

【フォースタ テックブログ】Serverless Frameworkを用いたAWS Lambdaのバージョン管理とIaC化

 

こんにちは。テックラボの佐々木です。主にバックエンドを担当しています。

フォースタートアップス(以下フォースタ)では、サーバーレスなアプリケーションの構築にAWS Lambdaを利用しています。
以前はLambdaの開発や運用が属人化していても問題なかったのですが、プロダクトやエンジニアが増えるに従いLambdaの数も増え、管理できなくなってきました。
そのため、現在はServerless Frameworkというオープンソースのツールを利用してLambdaの管理を行なっています。

この記事ではServerless Frameworkについてあまり知らない人向けに、Serverless Frameworkの概要とフォースタでの利用例を紹介します。

Serverless Frameworkとは

言葉が混同しないように、以後サーバーレスという概念のことを「サーバーレス」、Serverless Frameworkというツールのことを「Serverless」と書きます。

Serverlessはサーバーレスなアプリケーションを簡単に開発、デプロイするためのNode.js製のツールです。
AWS、Azure、GCP等のクラウドサービスによらず利用することができ、ランタイムの言語もクラウドサービス側で許されているものであれば利用することができます。(フォースタはAWSを利用しているため、この記事ではAWSを例に説明します。)
デプロイコマンドを実行すると、各クラウドサービスのリソース構築処理が実行されます。AWSを例に挙げると、デプロイコマンド実行時にCloudFormationのテンプレートが作成され、Lambdaおよびその他必要なリソースが作成されます。

Serverlessを利用して感じたメリットは以下の3点です。

デプロイが簡単

手動でLambdaをデプロイするのは非常に手間だし、ヒューマンエラーを起こす可能性もあるので危険です。
AWS CLIを利用すればデプロイを自動化できますが、自分でデプロイ用のスクリプトを書く必要があり、管理するリソースが多いとスクリプトも複雑になってしまいます。
Serverlessであれば設定ファイルという形でLambdaを定義することができ、様々なサーバーレスアーキテクチャのパターンが想定されているため、柔軟で記述量も少なくて済みます。また、`serverless deploy`というコマンド一つでデプロイできます。

インフラのコード化が可能

Lambdaの定義や必要なリソースを設定ファイルに記述できるため、Lambda周りのインフラの品質保証ができ、再利用性が高まります。
フォースタではInfrastructure as Codeの手段としてTerraformも利用していますが、Lambdaに関してはサーバーレスアーキテクチャに特化したServerlessを利用しています。

プラグインが豊富

サーバーレスアプリケーション構築の幅を広げてくれるプラグインが1000以上あります。
例を挙げると以下のようなものがあります。

  • 指定したドメインAPIアクセスができるようになるServerless Domain Manager
  • Webpackによるビルドを提供するServerless Webpack
  • Lambdaのコードストレージから不要なコードを削除するServerless Prune Plugin

これらは多くが個人によって開発されているオープンソースで、Github上に公開されています。

Serverlessの仕組み

続いて、Serverlessの仕組みを知るために簡単なチュートリアルを行い、裏側で何が起こっているのかを確認していきます。
このチュートリアルで作成されるのは、実行すると文字列を返すLambdaです。

www.serverless.com

事前準備

Serverlessを初めて利用する場合は、ServerlessのインストールやAWSリソースにアクセスするための認証情報の設定が必要です。

Serverlessのインストール

$ npm install -g serverless

なお、ServerlessはNode.jsのバージョン6以上が必要です。

IAMユーザーの作成

続いてAWSのマネジメントコンソール上で、管理ポリシーが「AdministratorAccess」で「プログラムによるアクセス」を許可するユーザーを作成します。

作成後に表示されるアクセスキーIDとシークレットアクセスキーは次の手順で利用します。

認証情報の設定

AWSの認証情報を作成します。

$ serverless config credentials --provider aws --key アクセスキーID --secret シークレットアクセスキー

Lambdaの作成

事前準備が完了したので、ここからLambdaを作成していきます。

Lambdaの定義ファイルを作成

以下コマンドで作業ディレクトリとLambdaの定義ファイル作成します。

$ serverless create --template aws-python --path myService

「myService」という作業ディレクトリを作成し、Pythonランタイムで動くLambda用のテンプレート「aws-python」が以下のように作業ディレクトリに展開されます。
serverless.ymlがLambdaの設定ファイル、handler.pyが実行時に動くコードです。

myService
├─.gitignore
├─handler.py
└─serverless.yml

デプロイ

$ serverless deploy

コンソールで確認すると以下のようなLambdaが作成されていることがわかります。非常に簡単にデプロイすることができたのではないでしょうか。

裏側で起こっていること

serverless deployコマンド実行後、以下のようにCloudFormationでリソースが作成されています。

Lambdaの実行ロールとして作成されたIAMロールには、CloudWatchの書き込みポリシーが付与されています。
このチュートリアルではこのポリシーのみですが、serverless.yml上で実行ロールを定義することで、S3やRDS等のリソースへのアクセスポリシーを付与することができます。

S3バケットにはCloudFormationのテンプレートとLambdaのデプロイパッケージが作成されています。

今回はこれだけでしたが、他にもserverless.ymlを変更したり、プラグインを追加することで、API GatewayやRoute53等のリソースも作成されます。

フォースタでの利用例

フォースタでは、Googleドライブ上のPDFをテキストに変換する処理を、AWSのHTTP APIとLambdaを利用して構築しています。
VPC等のネットワーク周り、Elasticsearch Service、中間ファイルを置くためのS3バケットは事前に作成する必要がありますが、それ以外のリソースについてはServerlessを利用して作成しています。

対応概要

この仕組みの構築のために、大きく以下の3つの作業がありました。
後ほどコードを見ながら詳細に説明していきます。

Lambda Layer構築

以下のパッケージは容量が重くデプロイに時間がかかるため、Lambda Layerとして事前にデプロイします。Lambda LayerもServerlessを利用してデプロイができます。

  • pdfminer.six(PDFのテキスト変換)
  • pydrive(Googleドライブアクセス)
  • elasticsearch-py(Elasticsearchアクセス)
  • rollbar(エラー通知)

Lambda関数作成

以下2つのLambda関数の構築とHTTP APIドメイン設定をServerlessで行います。

  • pdfParse:APIアクセスをトリガにGoogleドライブからPDFを取得し、テキストに変換してS3に配置
  • updateDocument:S3へファイルが置かれたことをトリガにElasticsearchを更新

HTTP APIのカスタムドメインを作成しないと、デプロイの度にAPIのエンドポイントが変わってしまいます。
serverless-domain-managerプラグインを利用することでカスタムドメインの作成、HTTP APIとの紐付けを行います。

HTTP APIのオーソライザーの設定(事前準備)

HTTP APIのアクセス制御のためにオーソライザーの設定をする必要があります。
今回はAuth0を利用して、以下のようなオーソライザーを設定しました。
ここで設定した値は、後ほどLambda関数の定義時に利用します。

この手順の詳細については以下の通りですが、ServerlessにてHTTP APIとJWTオーソライザーの紐付け等全て実施してくれるので、Configure the JWT Authorizerのセクションに記載のAuth0のコンソール上での設定のみでよいです。

auth0.com

Auth0については以下記事もご参照ください。

www.wantedly.com

構成図

Lambda Layer構築

パッケージインストール

まずLayerにするパッケージをインストールします。

$ pip install -t pdfminer.six/python/lib/python3.7/site-packages pdfminer.six
$ pip install -t pydrive/python/lib/python3.7/site-packages pydrive
$ pip install -t elasticsearch/python/lib/python3.7/site-packages elasticsearch
$ pip install -t rollbar/python/lib/python3.7/site-packages rollbar

フォルダ構成が以下のようになるようにファイルを配置します。
実はここが一番大切で、ディレクトリ構成をしっかりと守らないとデプロイ時にエラーが発生します。

layer
├── elasticsearch
│ └── python
│ └── lib
│ └── python3.7
│ └── site-packages
│ └── …
├── pdfminer.six
│ └── python
│ └── lib
│ └── python3.7
│ └── site-packages
│ └── ...
├── pydrive
│ └── python
│ └── lib
│ └── python3.7
│ └── site-packages
│ └── ...
├── rollbar
│ └── python
│ └── lib
│ └── python3.7
│ └── site-packages
│ └── ...
└── serverless.yml

Lambda Layerの定義

Lambda Layerの定義もserverless.ymlを利用しますが、Lambda関数定義用のserverless.ymlとは別ファイルで管理します。
Lambda関数の定義時に、利用するLambda Layerのリソースを指定する必要があるため、resourcesのOutputsパラメータを指定して後々参照できるようにしておく必要があります。

layer/serverless.yml

service: test-layer
provider:
name: aws
runtime: python3.7
stage: production
region: ap-northeast-1
layers:
pdfminer:
path: pdfminer.six
description: pdfminer layer
CompatibleRuntimes:
- python3.7
pydrive:
path: pydrive
description: pydrive layer
CompatibleRuntimes:
- python3.7
elasticsearch:
path: elasticsearch
description: elasticsearch layer
CompatibleRuntimes:
- python3.7
rollbar:
path: rollbar
description: rollbar layer
CompatibleRuntimes:
- python3.7
resources:
Outputs:
PdfminerLayerExport:
Value:
Ref: PdfminerLambdaLayer
Export:
Name: PdfminerLambdaLayer
PydriveLayerExport:
Value:
Ref: PydriveLambdaLayer
Export:
Name: PydriveLambdaLayer
ElasticsearchLayerExport:
Value:
Ref: ElasticsearchLambdaLayer
Export:
Name: ElasticsearchLambdaLayer
RollbarLayerExport:
Value:
Ref: RollbarLambdaLayer
Export:
Name: RollbarLambdaLayer

デプロイ

layer/serverless.ymlがあるディレクトリにてLambda Layerをデプロイします。
Lambda LayerもLambda関数も同じコマンドでデプロイできるのは楽ですね。

$ serverless deploy

Lambda関数作成

Serverlessプラグインのインストール

今回は以下3つのServerlessプラグインを利用しました。
serverless pluginコマンドでプラグインをインストールできます。

  • serverless-python-requirements

依存するPythonライブラリをインストールせずとも、デプロイ時にrequirements.txtやPipfileを参照してデプロイパッケージを作ってくれます。

$ serverless plugin install -n serverless-python-requirements
  • serverless-prune-plugin

Lambdaのコードストレージから過去のコードを削除してくれます。

$ serverless plugin install -n serverless-prune-plugin
  • serverless-domain-manager

このプラグインによってHTTP APIのカスタムドメインの設定を行うことができます。serverless.ymlを記述することでカスタムドメイン作成だけでなく、Route53へのレコード追加も行ってくれます。
このプラグインだけ、npmでインストールすることを奨励されていました。

$ npm install serverless-domain-manager --save-dev

これらのプラグインをインストールすると、package.jsonプラグイン情報が記述されるだけでなく、serverless.ymlにも以下が追記されます。

function/serverless.yml

plugins:
- serverless-python-requirements
- serverless-prune-plugin
- serverless-domain-manager

Lambda関数の定義

Lambda関数の構築とHTTP APIドメイン設定をServerlessで行います。
なお、PDFをテキスト変換する処理(parse.py)、Elasticsearchを更新する処理(update.py)についてはServerlessの話から逸脱するため省略します。

以下のようなフォルダ構成でファイルを配置します。

function
├── package-lock.json
├── package.json
├── parse.py
├── requirements.txt
├── serverless.yml
├── service-account.json # Googleドライブアクセス用
└── update.py

serverless.ymlを記述していきます。
ポイントは以下の設定部分です。

function/serverless.yml

service: test
provider:
name: aws
runtime: python3.7
stage: production
region: ap-northeast-1
environment:
LAYER_SERVICE: test-layer
TMP_BUCKET: ${self:provider.s3.tmpBucket.name}
OUTPUT_PATH: example/
ROLLBAR_KEY: rollbarのアクセストークン
logRetentionInDays: 30
iamRoleStatements: # 適切に設定
- Effect: Allow
Action: '*'
Resource: '*'
httpApi:
authorizers:
auth0:
identitySource: $request.header.Authorization
issuerUrl: Auth0のテナントURL
audience:
- https://auth0-jwt-authorizer
s3:
tmpBucket:
name: testBucket
package:
exclude:
- .git/**
- layer/**
- package.*
functions:
parse:
handler: parse.post
name: parsePdf
package: {}
events:
- httpApi:
method: POST
path: /example
authorizer:
name: auth0
layers:
- ${cf:${self:provider.environment.LAYER_SERVICE}-${opt:stage, self:provider.stage}.PdfminerLayerExport}
- ${cf:${self:provider.environment.LAYER_SERVICE}-${opt:stage, self:provider.stage}.PydriveLayerExport}
- ${cf:${self:provider.environment.LAYER_SERVICE}-${opt:stage, self:provider.stage}.RollbarLayerExport}
update:
handler: update.put
name: updateDocument
package: {}
events:
- s3:
bucket: tmpBucket
event: s3:ObjectCreated:Put
rules:
- prefix: ${self:provider.environment.OUTPUT_PATH}
vpc:
securityGroupIds:
- 作成したセキュリティグループ
subnetIds:
- 作成したプライベートサブネット
layers:
- ${cf:${self:provider.environment.LAYER_SERVICE}-${opt:stage, self:provider.stage}.ElasticsearchLayerExport}
- ${cf:${self:provider.environment.LAYER_SERVICE}-${opt:stage, self:provider.stage}.RollbarLayerExport}
plugins:
- serverless-python-requirements
- serverless-prune-plugin
- serverless-domain-manager
custom:
prune:
automatic: true
includeLayers: true
number: 5
customDomain:
domainName: 設定したいカスタムドメイン名
certificateName: 証明書名
endpointType: regional
securityPolicy: tls_1_2
apiType: http

HTTP APIのオーソライザー

オーソライザーの設定はAWSのコンソールからももちろん設定できますが、出来る限りServerlessで管理したいので、serverless.ymlの記述をすることでオーソライザーの設定を行います。
「HTTP APIのオーソライザーの設定」のセクションで設定したIdentifierをHTTP APIのパラメータとして登録します。

 httpApi:
authorizers:
auth0:
identitySource: $request.header.Authorization
issuerUrl: Auth0のテナントURL
audience:
- https://auth0-jwt-authorizer

HTTP APIのカスタムドメイン

Lambda関数のトリガとなるHTTP APIドメインを固定するためにServerless Domain Managerプラグインを利用しています。
このプラグインを利用すると、serverless.ymlに以下を記述することでカスタムドメインの設定をすることができます。

custom:
customDomain:
domainName: 設定したいカスタムドメイン名
certificateName: 証明書名
endpointType: regional
securityPolicy: tls_1_2
apiType: http

デプロイ

Lambda関数のデプロイの前にカスタムドメインを作成します。

$ serverless create_domain

10分程度で初期化され、AWS上でカスタムドメインが作成されたことを確認できます。

カスタムドメイン作成、Lambda関数のデプロイを行います。

$ serverless deploy

Lambda関数がデプロイされ、HTTP APIのオーソライザーも設定されたことが確認できました。

おわりに

Serverlessの概要とフォースタにおける利用例を紹介しました。

Serverlessを利用することで、サーバーレスアプリケーションのデプロイが簡単になるだけでなく、インフラがコード化できるため品質保証でき、再利用性が高まります。
フォースタでも実際に導入して、サーバーレスアプリケーション開発の質も効率も格段に上がりました。

サーバーレスは今後も大きなトレンドだと思うので、Serverlessのような便利なツールを適宜使い、効果的に開発していければと思います。