こちらの記事は カケハシ Advent Calendar 2023 の 15 日目の記事になります。 adventar.org
はじめに
こんにちは、おくすり連絡帳 Pocket Musubiというサービスを開発している石井です。
私たちのチームはフルスタックなエンジニアで構成されております。しかし個人個人で得意な分野はあって、私はその中でもインフラが好きなエンジニアになります。さてさてそんなインフラですが、最近 Wing
というツールを知りまして結構面白かったので紹介します。
Wing とは?
Wing は IfC のツールです。
IaC だったら耳に馴染みがあるのですが、a でなく f です。IfC です。これはInfrastructure from Code
の略だそうです。
IaC と同様にインフラをつくるアプローチなのですが、違いは IaC はインフラをコードから作るのに対して、IfC はコードからインフラを推測して作るようです。
つまり IfC はインフラを意識しないで、ただただ要件を満たすロジックを記載すれば、そこに必要なインフラが勝手にデプロイされるのです。素晴らしいですね。
さて話がもどりますが、 Wing は IfC を独自の言語を使って実現しています。今現在(2023/12/07)対応しているクラウドプラットフォームは AWS, GCP, Azure だそうです。また現状のステータスはプレリリース状態です。なので今は Wing を使って製品を実装できるフェーズにはありません。ですがチュートリアルとかをやってみて「あー将来こんな感じの世界になるのね」とかワクワクすることはできます。
作ってみる
では Wing を使って API を作ってみたいと思います。補足で利用した Wing のバージョンは0.51.12
です。
API の内容ですが、エンドポイントが POST と GET の2つで、POST で DB にレコードを保存して、GET でレコードを取得するというものにしようと思います。
コードはこんな感じになりました。
bring cloud; bring ex; let api = new cloud.Api(); let db = new ex.Table( name: "config", primaryKey: "name", columns: { "value" => ex.ColumnType.STRING } ); api.get("/config/:name", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { let id = request.vars.get("name"); let item = db.get(id); return cloud.ApiResponse { status: 200, body: Json.stringify(item) }; }); api.post("/config", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { if let body = request.body { let j = Json.parse(body); let pk = j.get("name").asStr(); let item = Json {"value": j.get("value").asStr()}; db.upsert(pk, item); return cloud.ApiResponse { status: 201, body: "success" }; } });
これはconfig
を保存する謎の API です。
下記のようなデータを POST で/config
へ送ると DynamoDB に pk をhoge
としてレコードを保存されます。
{"name":"hoge", "value":"abc"}
そして GET で/config/hoge
へアクセスすると保存したレコードを取得できます。
ちなみにコードは 33 行なのです。33 行でインフラとアプリが完成するのはすごいですね。が、本当にすごいのはここからです。
ローカルで動かしてみる
ローカルのシミュレーターがとてもすごいです。
Wing はローカルでインフラを含めた全体の動作確認ができます。これがどういうことなのか文章で説明するのが難しいので、紙芝居的に説明してみようと思います。
まずさきほどのコードをapi.main.w
とかの名前で保存します。
そして下記のコマンドを叩きます。
$ wing it api.main.w
するとブラウザが立ち上がって、現状のインフラ(?)が表示されます。
現状は cloud.Api というインスタンスの中にエンドポイントが 2 つあって、それぞれ ex.Table に紐づいている感じです。コードに対して違和感のない構成ですね。
次にこれを使って「POSTでデータを入れて、GETする」というシナリオをシミュレーションしていみようと思います。
この図ですが、色々クリックできます。
cloud.Api をクリックすると、下記のような画面が出てきます。
右側のメニューが cloud.Api 用のものへ変化し、URL から利用したいエンドポイントを選択できます。ここではPOST /config
を選択します。
次にヘッダーや URL パラメーターやリクエストボディを設定する箇所があるので、そこへリクエストボディを記入してSend
を押します。
ちなみに今回はこんな感じのリクエストボディを送りました。
{"name":"hoge", "value":"a"}
すぐに201 Created
と表示されるので、ex.Table をクリックしてテーブルの中身を確認してみます。
テーブルの中に{"name":"hoge", "value":"a"}
がちゃんと保存されているのが確認できました。
次に同じ要領であと 2 回 POST してみました。
テーブルの中身を確認してみると、ちゃんと 3 件保存されていました。
次にこのレコードを取得するために GET してみます。
cloud.Api にもどり、今度はGET /config/{name}
を選択します。そして URL パラメーターにhoge2
と記入します。
Send
を押すと右下に取得したデータが表示されました。
無事hoge2
のレコードが取得できました。
シナリオは終わりです。ローカルでここまでできるのはすごいですね!!
AWS にデプロイしてみる
(ここが苦戦しました。。。可能なら CDK で出力したかったのですがエラーで出力できず Terraform で出力しました。)
下記のコマンドで出力します。
$ wing compile -t tf-aws api.main.w
するとtarget/api.main.tfaws
というディレクトリーが生成されて、そのなかに Terraform のコードが出力されます。
あとはそのまま Terraform を使ってデプロイするだけです。
$ cd target/api.main.tfaws
$ terraform init
$ terraform apply
こんな感じのものがデプロイされました!
想像の通りですね。
ここに POST を投げてみます
$ curl -X POST -H "Content-Type: application/json" -d '{"name":"hoge","value":"a"}' https://xxxxxxxxxx.amazonaws.com/prod/config > success
DynamoDB の中身を確認するときちんと入っていました。
続いて GET してみます。
$ curl https://xxxxxxxxxx.amazonaws.com/prod/config/hoge >{"name":"hoge","value":"a"}
無事取得できました!!
ローカルでシミュレーションしたものがデプロイされていることが確認できました。
動作確認したら忘れずにお掃除しましょう。
$ terraform destroy
最後に
IfC のツールである Wing を使ってみました。
Wing は独自の言語でロジックを記載し、そのロジックからインフラを推測してインフラとアプリをクラウドプラットフォームにデプロイしてくれるツールです。
また Wing は強力なシミュレーターを搭載しており、インフラを含めた動作確認をローカルで完結して行うことができて開発体験が高そうです。
まだプレリリース版なのですが、今後の発展が楽しみですね。