格安でwebアプリをホスティングしたい
個人の趣味で適当なサービスを運用する際とか、デモ用のプロダクトを公開するときとか
一日1アクセスあるかないか程度のサービスのためにインスタンスを立てたくない。
従量課金が嬉しい
サーバレスアーキテクチャでLambdaとかで構築すれば解決
しかし、デバッグがやりづらい。
普通のwebアプリとして動かして、手元の環境で動くような状態で開発したい。
Djangoやrails、goなどで書きたい。
そうすればいざとなったらサーバーに移行も可能。
普通のwebサーバをlambdaとかで動かす。
今回はawsの範囲内でやる。
Cloudflare workerとかあるようだけど無料枠を求めてサービスを渡り歩くのはしたくない
Google cloud runはゼロスケールするので結構いいっぽい。
TLS終端・CDN
Cloudfrontを使う
サーバー
ファイルストレージ
S3
データベース
RDBを使いたい。性能は問わない。
DynamoDBとかは置いておく。
EFSにsqliteを置くのはどうか。
ヘビーユーズじゃなければ月1ドルかからなさそう。
引用
https://scrapbox.io/files/66cc967693ce8500222ceddf.png
性能は悪くない。
ポイントインタイムリカバリはできないものの、aws backupで定時バックアップもできる。
やってみる編
まずLambdaからためす。
とりあえずdjango projectをつくる
code:sh
mkdir app && cd app
rye init
rye add django
rye add djangorestframework
./.venv/bin/django-admin startproject config .
./.venv/bin/python manage.py startapp sample
code:sample/views.py
import random
from rest_framework.decorators import api_view
from rest_framework.response import Response
def index(request):
return Response({'message': f'Hello, World!, {random.randint(1, 100)}'})
code:config/urls.py
from django.contrib import admin
from django.urls import path
from sample.views import index
urlpatterns = [
path('admin/', admin.site.urls),
path('', index),
]
INSTALLED_APP も追加
簡単なアプリが完成
https://scrapbox.io/files/66cca2eb75cf7b001cca79bf.png
Dockerfileを作る
wsgiサーバにはgunicornを使う。
❯ rye add gunicorn
code:Dockerfile
FROM python:3.12
RUN mkdir /app
WORKDIR /app
COPY requirements.lock .
RUN sed '/-e/d' requirements.lock > requirements.txt && pip install -r requirements.txt
COPY . .
RUN useradd app
USER app
CMD gunicorn config.wsgi --bind=0.0.0.0:8080 --workers=1 --threads=1 --max-requests=500
EXPOSE 8080
説明
requirements.lock はそのまま pip install -r できるフォーマットだが、 -e . の行があるので消す。
portは aws-lambda-adapter デフォルトの8080番。
ok
code:sh
docker build --tag app .
docker run -p 8000:8000 app
https://scrapbox.io/files/66cca83463db66001cbb55d3.png
aws-lambda-web-adaptor も入れておく。
code:diff
diff --git a/app/Dockerfile b/app/Dockerfile
index 5b4ca46..8a8c25b 100644
--- a/app/Dockerfile
+++ b/app/Dockerfile
@@ -3,6 +3,8 @@ FROM python:3.12
RUN mkdir /app
WORKDIR /app
+COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.0 /lambda-adapter /opt/extensions/lambda-adapter
+
COPY requirements.lock .
RUN sed '/-e/d' requirements.lock > requirements.txt && pip install -r requirements.txt
Lambdaにデプロイしてみる。
ECRを作って、それからデプロイする。
terraformでやる。
Lambda function URL が便利。API gatewayいらず。
動いた。簡単すぎて感動。
https://scrapbox.io/files/66ccb636e5eb98001da3ca66.png
DB
sqliteを読む。 /mnt/db/db.sqlite3 にDBファイルを置く。
https://scrapbox.io/files/66ccba503e5f08001c158422.png
EFSをマウントする。vpcを作ってprivate subnetに置く。
lambdaもprivate subnetに置く必要があるらしい。
マウントされた /mnt/db/db.sqlite3 にアクセスして、ちゃんとマイグレーションできている。
https://scrapbox.io/files/66ccc6ea1e3ad7001db1285a.png
コールドスタートはやはり遅い。3-5secくらい
provisioned concurrency
料金
https://scrapbox.io/files/66cd93127297c5001cda7ee0.png
https://scrapbox.io/files/66cd931e0eb02f001d090356.png
メモリ128MB x86で一ヶ月provisionedした場合
0.0000041667 * 60 * 60 * 24 / 4 = 0.09 USD
provisonしておいても割と低コストで運用できそう。
provisioned concurrencyを設定してみた
code:diff
diff --git a/terraform/lambda.tf b/terraform/lambda.tf
index a7cc3ba..20faea3 100644
--- a/terraform/lambda.tf
+++ b/terraform/lambda.tf
@@ -25,6 +25,7 @@ resource "aws_lambda_function" "http_lambda" {
role = aws_iam_role.lambda_exec_role.arn
package_type = "Image"
image_uri = "${aws_ecr_repository.this.repository_url}:v6"
+ publish = true
memory_size = 128
timeout = 10
@@ -41,6 +42,12 @@ resource "aws_lambda_function" "http_lambda" {
}
+resource "aws_lambda_provisioned_concurrency_config" "this" {
+ function_name = aws_lambda_function.http_lambda.function_name
+ provisioned_concurrent_executions = 1
+ qualifier = aws_lambda_function.http_lambda.version
+}
+
# Lambda Function URL
resource "aws_lambda_function_url" "lambda_url" {
function_name = aws_lambda_function.http_lambda.function_name
やってみた→コールドスタートが解決しない……
シンプルにcurlで投げてもダメ。
ブラウザでアクセスした場合は同時にいくつかリクエストが飛ぶのでどちらにしろコールドスタートが発生してしまう気がする。
→ NATゲートウェイが課金されていて死亡。
@ebiyu_: NATゲートウェイを作ったのを忘れていてクラウド破産 https://pbs.twimg.com/media/GZ715AdawAAt0QL.png