Setting up FastAPI with Uvicorn and Openrc to serve Jupyter Notebook generated html files.¶
Prerequisites:¶
- system with openrc
- fastapi
- uvicorn
- jupyter notebook (optional)
- nginx
Creating FastAPI application¶
Create a directory for project and enter it:
$ mkdir -p ~/Fastapi_blog/static && cd ~/Fastapi_blog
Create a file containing code and html template folder and template for the main page:
$ touch main.py
$ mkdir templates
$ touch templates/main_page.html
main_page.html can be really minimal:
<html>
<body>
<h1>List of articles</h1>
<h2>Number of posts: {{ files|length }}</h2>
{% if files %}
{% for item in files %}
<a href="static/html/{{ item }}">{{ item }}</a><br>
{% endfor %}
{% endif %}
</body>
</html>
Create basic FastAPI application serving static files at '/'. I want that as I'll be serving if from behind Nginx reverse proxy. Prefix will be set with Nginx conf file
#!/usr/bin/env python
"""
FastAPI site serving static html files.
"""
from os import listdir
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
ARTICLES = 'static/html'
templates = Jinja2Templates(directory="templates")
LIST_OF_ARTICLES = [i for i in listdir(ARTICLES) if i.endswith('.html')]
app = FastAPI()
app.mount("/", StaticFiles(directory="static", html=True), name="static")
@app.get("/")
async def main_page(request: Request):
"""Generate main page"""
return templates.TemplateResponse(
"main_page.html", {
"request": request,
"files": LIST_OF_ARTICLES,
"static": ARTICLES,
}
)
if __name__ == "__main__":
uvicorn.run(
app,
port=8000,
host='127.0.0.1',
log_level="info",
# uds="/run/tech_blog.sock", <- uncomment if you want Unix socket
)
Test run:
$ uvicorn main:app --reload
If we try to run it as regular user with 'uds="/run/tech_blog.sock"' line uncommented we should get permission denied error as we don't have rights to create files in /run.
In browser, take a peek at http://localhost:8000 and you should already see something.
Create a file with a test html code:
$ touch static/test.html
Put this in it:
<html>
<body>
<h1>Fun stuff comming up</h1>
<p>Bob's your uncle!</p>
</body>
</html>
Visit http://localhost:8000/ You should see the main page with test article and you should be able to navigate to the article clicking the link.
Jupyter side note¶
To write stuff with Jupyter Notebook start it up:
$ jupyter-notebook
In browser a new tab with jupyter notebook should have started. Write your fun stuff. Save it. If you don't want to execute cell content convert to html with:
$ jupyter nbconvert --to html <path to your file>
If you want to execute cell content convert with:
$ jupyter nbconvert --execute --to html <path to your file>
Want to change theme to dark?
$ jupyter nbconvert --execute --to html <path to your file> --HTMLExporter.theme=dark
Now put it in your static
folder. It will be served by your FastAPI application.
Bash script¶
In order to run our python script with Openrc first we need to create a Bash script:
#!/usr/bin/env bash
cd /home/discordia/Fastapi_blog || exit # If your login is different, change home directory name.
exec python main.py
Save it (here as fastapi_blog.sh) as a root user to /usr/local/bin and make executable:
# chmod ugo+x /usr/local/bin/fastapi_blog.sh
Openrc service file¶
Now create a minimal service file
#!/sbin/openrc-run
supervisor=supervise-daemon
command="/usr/local/bin/fastapi_blog.sh"
pidfile="/run/${RC_SVCNAME}.pid"
command_args="-p ${pidfile}"
command_background=True
Save it as a root user in /etc/init.d and make executable:
Save it as a root user in /etc/init.d and make executable:
# chmod ugo+x /etc/init.d/fastapi_blog
You should be able to start/restart/stop it normally:
# rc-service fastapi_blog start
# rc-service fastapi_blog restart
# rc-service fastapi_blog stop
If you're using Unix socket, check for existence of /run/fastapi_blog.sock. Else navigate to http://127.0.0.1:8000/test.html to see page is indeed there.
$ ps aux | grep "python main.py"
You want to see only one 'python main.py'. At first I did not use exec which resulted in lingering processess. You don't want that.
Now you can plug it in to your preffered proxy server using either a unix socket or a TCP socket.
If you're using unix socket, check for existence of /run/fastapi_blog.sock, else either use lsof to see what's using port 8000 or navigate to http://127.0.0.1:8000/test.html