Loopia hosting

Kako postaviti Python Flask aplikaciju

16.09.2024.

Davno sam pravio PHP web sajtove, a u skorije vreme sam naučio nešto Pythona i dopao mi se Flask kao minimalistična biblioteka za male projekte. Do skoro sam koristio hosting koji ima CPanel, gde je postavljanje Python aplikacije dosta automatizovano. Na Loopia hostingu toga nema. Baza znanja pokriva postavljanje web sajta kreiranog u PHP-u, Wordpress-u, Joomli ili njihovom SiteBuilderu, ali Python nije pokriven. Trebalo mi je više sati eksperimentisanja da sve proradi. Zato pišem ovaj članak kao uputstvo, kako drugima, tako i sebi za ubuduće.

Prednosti Loopia hosting-a

Na hostingu sa CPanel-om je dosta toga lakše i brže, ali na kraju ne naučite puno kako stvari funkcionišu u pozadini. Meni je bilo potrebno da napravim Google autentifikaciju, tj. da upotrebim OAuth2 protokol. Za to mi je potrebna OpenSSL biblioteka, koja je na sistemu sa CPanel-om vezana za verziju CPanel-a i ne može da se upgrade-uje. Dostupna verzja pomenute biblioteke je bila vrlo stara i moja aplikacija nije mogla da radi. Loopia ima skoriju verziju, pa time i preduslove za postavljanje moje aplikacije. Jedino podržani režim je preko CGI servera, što znači da aplikacija radi samo dok se ne završi request. Ako Vam je potrebno da se nešto izvršava periodično, bez pozivanja, Loopia ima Cron. Ne podržava Cron sintaksu, ali možete da postavite da se nešto izvršava na fiksan period.

Postavljanje Python aplikacije

Opisaću ovde kako je meni bilo logično i pogodno da sve proradi. Potrebni preduslovi:

  1. Upoznajte se sa opcijama koje nudi Loopia, jer neću ići u previše detalja.
  2. Podesite SSH nalog.
  3. Podesite FTP nalog.
  4. Kada kupite domen ili ga podesite da pokazuje na "ns1.loopia.se" i "ns2.loopia.se" servere, potrebno je i do 48 sati da se završi propagacija, pa će Vam trebati malo strpljenja pre nego što možete da nastavite.
  5. Otvorite terminal i konektujte se na Vaš nalog preko SSH. Naći ćete se u $HOME folderu gde je potrebno kreirati folder za Vašu aplikaciju. Moj domen je "ujagaga.in.rs", pa sam kreirao
    mkdir ujagaga.in.rs
    cd ujagaga.in.rs
    
  6. Ako zelite da dobijete minimalnu Python Flask "Hello World" aplikaciju, možete upotrebiti moj skript za automatsko postavljanje osnovne aplikacije https://github.com/ujagaga/cgi_falsk_deploy:
    curl https://raw.githubusercontent.com/ujagaga/cgi_flask_deploy/refs/heads/main/cgi_flask_deploy.sh -o cgi_flask_deploy.sh
    chmod +x cgi_flask_deploy.sh
    ./cgi_flask_deploy.sh
    

    Napomena: aplikacija ce raditi uz pomoc cgi-bin/cgi_serve.py skripte, pa Flask-ov "url_for" to pokazuje prilikom generisanja url-ova. Najlakši "fix" je da koristite stringove, na primer, umesto
    url_for('home')
    
    upotrebite "/".
    Ako ipak želite detaljnije da proučite proces, čitajte dalje.
  7. mkdir public_html
    cd public_html
    mkdir cgi-bin
    
    U pozadini radi Apache server koji će izvršavati skripte postavljene u "cgi-bin" ili nekom njegovom pod-folderu.
  8. Koristeći FTP ili SCP prebacite fajlove Vaše aplikacije. Za početak ću pretpostaviti da je Vaš izvršni skript "index.py" i da je Vaša aplikacija u
    ~/<moj_domen.com>/public_html/cgi-bin
    Biće potrebno još i da Python skript postavite za izvršni, tj.
    cd ~/<moj_domen.com>/public_html/cgi-bin
    chmod +x index.py
    
  9. Napravite u "public_html" folderu fajl sa nazivom ".htaccess" koji će Apache serveru dati dodatne parametre. Sadržaj treba da bude nešto kao:
    SetEnv HOME
    
    # ovo je instrukcija da prvo potraži cgi-bin/index.py, a ako ga nema, index.html
    DirectoryIndex cgi-bin/index.py index.html
    
    # Ovim kažemo servery da izvršava i python skriptove
    Options +ExecCGI
    AddHandler cgi-script .py
    
    Za test možete da postavite fajl index.py sa sadržajem:
    #!/usr/bin/env python3
    
    print ("Content-type:text/html\r\n\r\n")
    print ('<html>')
    print ('<head>')
    print ('<title>Hello</title>')
    print ('</head>')
    print ('<body>')
    print ('<h2>Hello Word from Python</h2>')
    print ('</body>')
    print ('</html>')
    
    Trebalo bi da na Vašem domenu sada vidite "Hello Word from Python". Opciono dodajte i "public_html/index.html" da prikaže da je u toku održavanje kada nema glavnog skripta. Obratite pažnju da u Vašem skriptu na vrhu stoji: "#!/usr/bin/env python3" da server zna čime da interpretira Vaš skript.

Python virtual environment

Jednostavan "Hello world" python skript radi sa ugrađenim bibliotekama, ali ozbiljnija aplikacija zahteva dodatne biblioteke koje Apache server ne može da pronađe dok mu se ne kaže lokacija. Zato sam napravio venv:

cd ~/<domen.in.rs>
python3 -m venv venv
source venv/bin/activate
pip install flask ...
Instalacija nekih biblioteka zahteva i Rust kompajler, pa je dobro i njega instalirati u $HOME folderu. Kada imate instalirane biblioteke u virtuelnom environmentu, potrebno je podesiti ".htaccess" da prosledi putanju. Prvo je potrebna apsolutna putanja:
echo $PWD
Time dobijem nesto kao: "/www/webvol39/cm/ukz6s6j0jrlyx9o/<domen.in.rs>", u daljem tekstu "venv_abs_path" za ".htaccess":
SetEnv HOME

DirectoryIndex cgi-bin/index.py index.html
Options +ExecCGI
AddHandler cgi-script .py

# Ovim postavljamo putanje do Python okruženja
SetEnv PATH /<venv_abs_path>/venv/bin:$PATH
SetEnv PYTHONPATH /<venv_abs_path>/venv/lib/python3.11/site-packages
Sada bi server trebalo da može da pronađe i Vaše biblioteke za Python aplikaciju.

Rešavanje prefiksa

Moja aplikacija se nalazi u pod-folderu:

~/<domen.in.rs>/public_html/cgi-bin/<naziv_projekta>/index.py
Da bi Apache server gađao odgovarajući fajl, mora da se prilagodi ".htaccess" fajl:
SetEnv HOME

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ cgi-bin/<naziv_projekta>/index.py/$1 [QSA,L]

DirectoryIndex cgi-bin/gate_server/index.py index.html
Options +ExecCGI
AddHandler cgi-script .py
SetEnv PATH /<venv_abs_path>/venv/bin:$PATH
SetEnv PYTHONPATH /<venv_abs_path>/venv/lib/python3.11/site-packages
Sledeći problem je što koristim Flask, koji treba da se servira CGI serverom. On nije ugrađen, nego se poziva iz Flask-a. Potrebno je napraviti skript za pokretanje aplikacije "cgi_serve.py" u istom folderu gde je "index.py", sa sadržajem:
#!/usr/bin/env python3

from wsgiref.handlers import CGIHandler
from index import application

CGIHandler().run(application)
Ovde se pretpostavlja da moja Flask aplikacija u "index.py" koristi
application = Flask(__name__)
Zatim je potrebno prepraviti ".htaccess" da gađa "cgi_serve.py" umesto "index.py":
SetEnv HOME

RewriteEngine On
RewriteBase /

# Redirect external requests to remove /cgi-bin/cgi_serve.py from the visible URL
RewriteCond %{THE_REQUEST} \s/cgi-bin/cgi_serve.py/([^\s?]*) [NC]
RewriteRule ^ cgi-bin/cgi_serve.py/%1 [L,R=301]

# Internally rewrite clean URLs to the actual script
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ cgi-bin/cgi_serve.py/$1 [QSA,L]

DirectoryIndex cgi-bin/gate_server/cgi_serve.py index.html
Options +ExecCGI
AddHandler cgi-script .py
SetEnv PATH /<venv_abs_path>/venv/bin:$PATH
SetEnv PYTHONPATH /<venv_abs_path>/venv/lib/python3.11/site-packages