Fly.io 上の SQLite を使用した FastAPI アプリケーションをデプロイする

Mary-Kate Olsen
リリース: 2024-10-22 11:15:29
オリジナル
983 人が閲覧しました

Deploy FastAPI application with SQLite on Fly.io

クラウド ソリューションは中規模および大規模なプロジェクトには適していますが、小規模な個人プロジェクトには重すぎます。小規模なもの (いくつかの API エンドポイントと小規模なリポジトリ) を起動したい場合は、3 つのオプションがあります:

  • 「大規模な」プロジェクト (AWS ECS/EKS、RDS) と同じアプローチを使用しますが、冗長であり、インフラストラクチャ コードは実際のプロジェクトのコードよりも大きくなる可能性があります。また、高価です (~$100)。
  • サーバーレス ソリューション (Lambda、Vercel) を使用します。ほとんどのクラウドプロバイダーはそのようなソリューションを持っていますが、これらのサービスは単純なデータベースには問題があります。安価なベンダーソリューション (AWS) を提供するか、マネージドデータベースが必要ですが、これもまた高価です (サーバーレスの場合はほとんど何もなく、DB の場合は約 20 ドル)
  • Docker で VPS を使用します。安価で (小型マシンで約 5 ドル)、インフラストラクチャを管理する必要はほとんどありませんが、デプロイメントは面倒です (プライベートまたはセルフホストのレジストリ、CI からの SSH アクセスが必要です)。

私は通常、SQLite を使用して小規模なアプリケーションを作成しています。SQLite は、あらゆるプログラミング言語で動作し、データ分析などのためにローカル マシンにコピーできる便利な小さな単一ファイル データベースです。そこで、サーバーレス アプローチ、導入の容易さ、SQLite の使用機能を組み合わせたミドルウェア ソリューションを探していたところ、Fly.io を見つけました。

設定

Fly.io にアカウントをお持ちでない場合は、作成する必要があります。プロジェクトを管理するには、flyctl という CLI ツールも必要です。 Fly.io はローカルと CI の両方でデプロイできます。

flyctl は、Dockerfile からプロジェクトのルート フォルダーからデプロイします。これは、同じ Dockerfile を他のシステムでも使用できるため、優れています。 Fly.io で遊ぶために、状態をデータベースに保存するシンプルな FastAPI プロジェクト、つまりクリック数をカウントする汎用の URL 短縮機能を準備しました。

Dockerfile:

FROM python:3.13-alpine
WORKDIR /app

COPY ./requirements.txt .
RUN pip install --no-cache-dir --upgrade -r requirements.txt

COPY . /app

ENV HOST=0.0.0.0 PORT=8080
EXPOSE ${PORT}
CMD uvicorn main:app --host ${HOST} --port ${PORT}
ログイン後にコピー
ログイン後にコピー

main.py:

import asyncio
import random
import string
from urllib.parse import urlparse

import aiosqlite
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import RedirectResponse

DB_PATH = "/data/app.db"

app = FastAPI()

async def get_db() -> aiosqlite.Connection:
    if db := getattr(get_db, "_db", None):
        if db.is_alive:
            return db

    db = await aiosqlite.connect(DB_PATH, loop=asyncio.get_event_loop())
    db.row_factory = aiosqlite.Row

    qs = """
    CREATE TABLE IF NOT EXISTS links (
        created_at INTEGER DEFAULT (strftime('%s', 'now')),
        short_code TEXT PRIMARY KEY,
        full_url TEXT NOT NULL,
        clicks INTEGER DEFAULT 0
    )
    """

    await db.execute(qs)
    await db.commit()

    setattr(get_db, "_db", db)
    return db

def random_code(length=8) -> str:
    alphabet = string.ascii_letters + string.digits
    return "".join(random.choice(alphabet) for x in range(length))

def is_valid_url(url: str) -> bool:
    try:
        parts = urlparse(url)
        return all([parts.scheme, parts.netloc])
    except ValueError:
        return False

@app.post("/")
async def shorten(url: str, req: Request):
    if not is_valid_url(url):
        raise HTTPException(status_code=400, detail="Invalid URL")

    host = req.headers.get("host")
    if host is None:
        raise HTTPException(status_code=500, detail="Missing host header")

    short_code = random_code()
    db = await get_db()
    qs = "INSERT INTO links (short_code, full_url) VALUES (?, ?)"
    await db.execute(qs, (short_code, url))
    await db.commit()

    return f"https://{host}/{short_code}"

@app.get("/")
async def list_links():
    db = await get_db()
    qs = "SELECT short_code, full_url, clicks FROM links ORDER BY created_at DESC"
    async with db.execute(qs) as cursor:
        return await cursor.fetchall()

@app.get("/{short_code}")
async def redirect(short_code: str):
    db = await get_db()
    qs = """
    UPDATE links SET clicks = clicks + 1 WHERE short_code = ?
    RETURNING full_url
    """

    async with db.execute(qs, (short_code,)) as cursor:
        if row := await cursor.fetchone():
            return RedirectResponse(row["full_url"])

    raise HTTPException(status_code=404)
ログイン後にコピー
ログイン後にコピー

requirements.txt:

aiosqlite
fastapi
uvicorn
ログイン後にコピー
ログイン後にコピー

展開する

コードをデプロイするには、まず Fly.io プロジェクトを作成する必要があります。これは、Web インターフェイスまたは flyctl のいずれかで実行できます。 CLU ツールを使用してルート フォルダー (コードが存在する場所) にプロジェクトを作成するには、flyctl launch を実行する必要があります。このコマンドは、目的のハードウェアを選択するよう提案し、fly.toml ファイルを作成します:

fly launch --build-only
ログイン後にコピー
ログイン後にコピー

このファイル内のパラメータを変更するか、Web UI を介して、将来プロジェクトを変更できます。基本的な fly.toml は問題ないように見えますが、SQLite には次のコマンドで作成できるストレージが必要です。

fly volumes create sqlite_data -s 1 -r ams
ログイン後にコピー
ログイン後にコピー

ここで、-s 1 はボリューム サイズを 1 GB (デフォルトは 3 GB) に設定し、-r はボリュームが作成されるリージョンです (Fly.io プロジェクトが作成されるリージョンと同じリージョンを使用します)。ストレージ サイズは後からいつでも変更できます。

最後に行うことは、ボリュームをアプリケーションに接続する mounts セクションを fly.toml に追加することです。

FROM python:3.13-alpine
WORKDIR /app

COPY ./requirements.txt .
RUN pip install --no-cache-dir --upgrade -r requirements.txt

COPY . /app

ENV HOST=0.0.0.0 PORT=8080
EXPOSE ${PORT}
CMD uvicorn main:app --host ${HOST} --port ${PORT}
ログイン後にコピー
ログイン後にコピー

sqlite_data はストレージの名前、/data はボリュームが接続されるパスです。これは本質的に docker run --mount source=sqlite_data,target=/data または対応する Docker Compose セクションと同じです。

SQLite は複数のアプリから書き込み可能ではなく、Fly.io はデフォルトでアプリに対して 2 つのインスタンスを作成するため、念のためレプリカの数を 1 つとして指定できます。

import asyncio
import random
import string
from urllib.parse import urlparse

import aiosqlite
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import RedirectResponse

DB_PATH = "/data/app.db"

app = FastAPI()

async def get_db() -> aiosqlite.Connection:
    if db := getattr(get_db, "_db", None):
        if db.is_alive:
            return db

    db = await aiosqlite.connect(DB_PATH, loop=asyncio.get_event_loop())
    db.row_factory = aiosqlite.Row

    qs = """
    CREATE TABLE IF NOT EXISTS links (
        created_at INTEGER DEFAULT (strftime('%s', 'now')),
        short_code TEXT PRIMARY KEY,
        full_url TEXT NOT NULL,
        clicks INTEGER DEFAULT 0
    )
    """

    await db.execute(qs)
    await db.commit()

    setattr(get_db, "_db", db)
    return db

def random_code(length=8) -> str:
    alphabet = string.ascii_letters + string.digits
    return "".join(random.choice(alphabet) for x in range(length))

def is_valid_url(url: str) -> bool:
    try:
        parts = urlparse(url)
        return all([parts.scheme, parts.netloc])
    except ValueError:
        return False

@app.post("/")
async def shorten(url: str, req: Request):
    if not is_valid_url(url):
        raise HTTPException(status_code=400, detail="Invalid URL")

    host = req.headers.get("host")
    if host is None:
        raise HTTPException(status_code=500, detail="Missing host header")

    short_code = random_code()
    db = await get_db()
    qs = "INSERT INTO links (short_code, full_url) VALUES (?, ?)"
    await db.execute(qs, (short_code, url))
    await db.commit()

    return f"https://{host}/{short_code}"

@app.get("/")
async def list_links():
    db = await get_db()
    qs = "SELECT short_code, full_url, clicks FROM links ORDER BY created_at DESC"
    async with db.execute(qs) as cursor:
        return await cursor.fetchall()

@app.get("/{short_code}")
async def redirect(short_code: str):
    db = await get_db()
    qs = """
    UPDATE links SET clicks = clicks + 1 WHERE short_code = ?
    RETURNING full_url
    """

    async with db.execute(qs, (short_code,)) as cursor:
        if row := await cursor.fetchone():
            return RedirectResponse(row["full_url"])

    raise HTTPException(status_code=404)
ログイン後にコピー
ログイン後にコピー

すべての設定が完了したので、次のコマンドを使用してアプリをデプロイできます。

aiosqlite
fastapi
uvicorn
ログイン後にコピー
ログイン後にコピー

アプリは正常に起動し、パブリック DNS 名がコンソールに出力されます。これで、URL を短縮ツールに投稿して確認できるようになりました:

fly launch --build-only
ログイン後にコピー
ログイン後にコピー

その後、このリンクにアクセスすると、https://example.com にリダイレクトされるはずです。最後に、クリック数が更新されていることを確認できます:

fly volumes create sqlite_data -s 1 -r ams
ログイン後にコピー
ログイン後にコピー

デプロイメント間でデータベースの状態が保存されていることを確認するには、flydeploy を使用して新しいデプロイメントを実行し、リンク リストが上記と同じであることを確認します (リンク 1 つ、クリック 1 つ)。

移行

アプリの起動時にコードから移行を実行するのではなく、移行に外部ソリューションを使用している場合、移行を実行する唯一の方法は、RUN コマンドの一部として Dockerfile にそれを配置することです。

バックアップ

fly ssh コンソールを使用してマシンに接続し、/data フォルダーでデータベース ファイルと対話できます。また、次のようにしてデータベース ファイルをローカル マシンにコピーすることもできます:

[mounts]
source = "sqlite_data"
destination = "/data"
ログイン後にコピー

結論

Fly.io は、アプリケーションをデプロイするためのシンプルで便利なサービスです。デプロイは Docker コンテナから機能し、追加サービスには PSQL、Redis、S3 などのストレージ (Vercel とは異なります) が含まれます。安価です。最も安価なサービスの料金は 3 ドル (1 共有 CPU / 256 MB) です。トラフィックが少ない場合は、さらに安くなる可能性があります。コンテナはアクティビティがなくなると数分後にシャットダウンし、トラフィックが発生すると自動的にオンになります。

欠点としては、スケジュールされたタスク用の組み込みソリューションがありません。代わりに、公式のソリューションは、crontab を使用して別のサーバーをセットアップし、そこからタスクを実行することです。ちょっと不気味です。

以上がFly.io 上の SQLite を使用した FastAPI アプリケーションをデプロイするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!