Gunakan aplikasi FastAPI dengan SQLite pada Fly.io

Mary-Kate Olsen
Lepaskan: 2024-10-22 11:15:29
asal
1063 orang telah melayarinya

Deploy FastAPI application with SQLite on Fly.io

Penyelesaian awan bagus untuk projek sederhana dan besar, tetapi terlalu berat untuk projek peribadi kecil. Jika anda ingin melancarkan sesuatu yang kecil (beberapa titik akhir api dan repositori kecil), terdapat tiga pilihan:

  • Gunakan pendekatan yang sama seperti untuk projek "besar" (AWS ECS/EKS, RDS), tetapi ia berlebihan dan kod infrastruktur boleh lebih besar daripada kod projek sebenar. Ia juga mahal (~$100).
  • Gunakan penyelesaian tanpa pelayan (Lambda, Vercel). Kebanyakan penyedia awan mempunyai penyelesaian sedemikian, tetapi perkhidmatan ini menghadapi kesukaran dengan pangkalan data mudah – mereka menyediakan penyelesaian vendor murah (AWS) atau memerlukan pangkalan data terurus, yang sekali lagi mahal (kebanyakannya tiada untuk tanpa pelayan, ~$20 untuk DB)
  • Gunakan VPS dengan Docker. Ia murah (~$5 untuk mesin kecil) dan hampir tidak perlu mengurus prasarana, tetapi pengerahan (memerlukan pendaftaran semula peribadi atau dihoskan sendiri, akses SSH daripada CI).

Saya biasanya menulis aplikasi kecil saya menggunakan SQLite, ia adalah pangkalan data fail tunggal kecil yang berguna yang berfungsi dalam mana-mana bahasa pengaturcaraan dan boleh disalin ke mesin tempatan untuk menganalisis data contohnya. Jadi saya sedang mencari beberapa penyelesaian middleware yang menggabungkan pendekatan tanpa khidmat, kemudahan penggunaan dan keupayaan untuk menggunakan SQLite dan menemui Fly.io.

Persediaan

Jika anda tidak mempunyai akaun dalam Fly.io – anda perlu menciptanya. Juga alat CLI yang dipanggil flyctl diperlukan untuk menguruskan projek. Fly.io boleh digunakan secara tempatan dan dari CI.

flyctl membuat penggunaan daripada folder akar projek daripada Dockerfile, yang bagus, kerana Dockerfile yang sama boleh digunakan dalam sistem lain. Untuk bermain dengan Fly.io, saya menyediakan projek FastAPI ringkas yang menyimpan keadaan dalam pangkalan data – pemendek url generik dengan pengiraan klik.

Fail Docker:

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}
Salin selepas log masuk
Salin selepas log masuk

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)
Salin selepas log masuk
Salin selepas log masuk

keperluan.txt:

aiosqlite
fastapi
uvicorn
Salin selepas log masuk
Salin selepas log masuk

Sebarkan

Untuk menggunakan kod kami, mula-mula kami perlu mencipta projek Fly.io. Ini boleh dilakukan sama ada dalam antara muka web atau dengan flyctl. Untuk membuat proejct dengan alat CLU dalam folder akar (di mana kod terletak) pelancaran flyctl harus dijalankan. Perintah ini akan menawarkan untuk memilih perkakasan yang diingini dan akan mencipta fail fly.toml:

fly launch --build-only
Salin selepas log masuk
Salin selepas log masuk

Anda boleh mengubah suai projek pada masa hadapan dengan menukar parameter dalam fail ini atau melalui ui web. Fly.toml asas kelihatan baik, tetapi SQLite memerlukan Storan, yang boleh dibuat dengan:

fly volumes create sqlite_data -s 1 -r ams
Salin selepas log masuk
Salin selepas log masuk

di mana -s 1 menetapkan saiz volum kepada 1 GB (lalai ialah 3 GB), dan -r ialah rantau di mana volum akan dibuat (gunakan kawasan yang sama di mana projek Fly.io dibuat). Anda sentiasa boleh menukar saiz storan kemudian.

Perkara terakhir yang perlu dilakukan ialah menambah bahagian pelekap pada fly.toml, yang melampirkan kelantangan pada aplikasi:

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}
Salin selepas log masuk
Salin selepas log masuk

sqlite_data ialah nama storan, /data ialah laluan di mana volum akan disambungkan. Ini pada asasnya sama seperti docker run --mount source=sqlite_data,target=/data atau bahagian Docker Compose yang sepadan.

SQLite tidak boleh ditulis daripada lebih daripada satu apl dan Fly.io secara lalai mencipta 2 tika untuk apl, jadi kami boleh menentukan bilangan replika sebagai satu untuk berjaga-jaga:

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)
Salin selepas log masuk
Salin selepas log masuk

Semua konfigurasi telah dilakukan sekarang dan kami boleh menggunakan apl kami dengan arahan:

aiosqlite
fastapi
uvicorn
Salin selepas log masuk
Salin selepas log masuk

Apl harus berjaya but dan nama DNS awam akan dicetak ke konsol. Kini kita boleh menyemaknya dengan menyiarkan beberapa url kepada pemendek:

fly launch --build-only
Salin selepas log masuk
Salin selepas log masuk

Kemudian kita boleh melawati pautan ini, ia harus mengubah hala ke https://example.com. Akhir sekali, kami boleh menyemak bahawa klik telah dikemas kini:

fly volumes create sqlite_data -s 1 -r ams
Salin selepas log masuk
Salin selepas log masuk

Untuk menyemak keadaan pangkalan data yang disimpan antara kerahan, kami boleh melakukan kerahan baharu dengan penggunaan terbang dan menyemak senarai pautan kekal sama seperti di atas (1 pautan, 1 klik).

Penghijrahan

Jika anda menggunakan penyelesaian luaran untuk migrasi, dan bukannya menjalankannya daripada kod apabila apl dimulakan, maka satu-satunya cara untuk menjalankan migrasi ialah meletakkannya dalam Dockerfile sebagai sebahagian daripada arahan RUN.

Sandaran

Kami boleh menyambung ke mesin dengan konsol fly ssh dan kemudian dalam folder /data berinteraksi dengan fail pangkalan data. Juga kita boleh menyalin fail pangkalan data ke mesin tempatan dengan:

[mounts]
source = "sqlite_data"
destination = "/data"
Salin selepas log masuk

Kesimpulan

Fly.io ialah perkhidmatan yang ringkas dan mudah untuk menggunakan aplikasi. Sebarkan kerja dari Docker Containers, perkhidmatan tambahan termasuk PSQL, Redis, storan seperti S3 (tidak seperti Vercel). Ia murah, perkhidmatan termurah berharga 3 dolar (1 CPU kongsi / 256 MB) – mungkin lebih rendah, jika anda mempunyai sedikit trafik – kontena ditutup selepas beberapa minit tanpa aktiviti dan dihidupkan secara automatik apabila trafik muncul.

Pada sisi negatifnya, tiada penyelesaian terbina dalam untuk tugas yang dijadualkan – sebaliknya, penyelesaian rasmi adalah untuk menyediakan pelayan berasingan dengan crontab dan menjalankan tugas daripadanya – ia agak menyeramkan.

Atas ialah kandungan terperinci Gunakan aplikasi FastAPI dengan SQLite pada Fly.io. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan