Palvelukehityksen perusteet
CODE02, Mayk
Lyhytlinkki: http://dy.fi/qbm
Palvelukehitys
Palvelu: aina käynnissä oleva, nettiä kuunteleva, pyyntöihin vastaava ohjelma
Palvelukehitys: miten saadaan aikaan netissä olevia palveluita
Palvelukehityksessä on monta puolta, seuraavassa ihan muutama
Ensimmäisellä tunnilla keskitytään siihen, että saadaan palvelu omalle koneelle
Toisella tunnilla keskitytään siihen, miten koodia julkaistaan ja muokataan yhdessä
Kolmannen tunnin aihe on palvelun saaminen nettiin
Palvelukehitys 1:�palvelu pyörimään koneella
(Windows-versio)
tämä on tärkeää palvelun kokeilemisen vuoksi
Asennukset
Tarvitaan: ennakoitavasti toimiva komentotila. Sellainen tulee Gitin mukana.
Asetukset
Tarvitaan: python, jonka voi käynnistää komentotilasta
$ cd OneDrive/
$ mkdir CODE02
$ cd CODE02/
$ export PATH="/c/ProgramData/anaconda3/:$PATH"
$ python --version
Python 3.11.7
Panu.Kalliokoski@MAYKS1312 MINGW64 ~/OneDrive/CODE02
$
Ensimmäinen versio
Tämä on webbisovellus, joka vastaa aina samalla tavalla. Tämän voi kirjoittaa esim. spyderissa. palvelu.py:
from wsgiref.simple_server import make_server
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
yield "Hello w€rld😞!".encode('utf-8')
if __name__ == '__main__':
with make_server("localhost", 8000, app) as server:
server.serve_forever()
Sovelluksen ajaminen omalla koneella
Tämä käynnistää verkkoyhteyksiä kuuntelevan sovelluspalvelinprosessin.
seuraava rivi tulee vasta, kun palvelua kokeillaan selaimella (seuraava slide)
Prosessin voi myös sammuttaa Ctrl-C:llä (ainakin jos on järkevä komentotulkki)
$ python kokeilu.py
127.0.0.1 - - [28/Feb/2025 11:09:32] "GET / HTTP/1.1" 200 11
Palvelun kokeileminen
Mene selaimella osoitteeseen localhost:8000
Mitä selaimen ja palvelun välillä kulkee?
Web developer tools löytyy valikoista tai Ctrl-Shift-E.
Tee reload (Ctrl-R tai F5).
Itse vastaus löytyy "Response"-kohdasta
Tässä on varsinainen kysely
Tässä vastauksen perustiedot
Tässä vastauksen lisätiedot
Tässä kyselyn lisätiedot
Iteraatio 1: Selaimen lähettämien tietojen käyttö
Nämä ovat app-funktiolle annetussa environ-parametrissa. Muuta app-funktio:
Tulostamme tiedot takaisin selaimelle, jotta näkyisi, mitä tietoja selain lähettää.
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
yield "Hello €nviron😞!\n\n".encode('utf-8')
for key in environ:
yield ("%s: %s\n" % (key, environ[key])).encode('utf-8')
Kokeilu
python (komentotilassa) pitää mahdollisesti käynnistää uudelleen.
Sitten reload.
Täällä on paljon hyödyllisiä tietoja! Erityisesti PATH_INFO ja QUERY_STRING näyttävät käyttökelpoisilta.
Myös wsgi.input mutta se ei oo merkkijono…
Iteraatio 2: salanimet
Muuta app-funktion koodi seuraavaan muotoon:
Tämän jälkeen pitää (totuttuun tapaan) uudelleenkäynnistää python ja ladata selaimessa sivu uudestaan
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
nimi = environ['PATH_INFO'].strip('/')
salanimi = nimi.replace('a', 'apa').replace('i', 'ipi') \
.replace('n', 'non').replace('na', 'nana')
yield ("salainen nimesi on: %s\n" % salanimi).encode('utf-8')
Kokeilu
Jipii, olemme onnistuneet tekemään dynaamisen (ja äärimmäisen hyödyllisen) palvelun! Sieltä saa salanimiä.
Iteraatio 3: ulkonäön viilausta
Se, miten selain näyttää sille lähetetyn datan, riippuu sisältötyypistä. Vaihdetaan se text/plain:sta text/html:ksi ja laitetaan muotoiluja sekaan.
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/html; charset=utf-8')])
nimi = environ['PATH_INFO'].strip('/')
salanimi = nimi.replace('a', 'apa').replace('i', 'ipi') \
.replace('n', '<b>non</b>').replace('na', 'nana')
yield "<h1>huomio!</h1>".encode('utf-8')
yield ("<p>salainen <em>nimesi</em> on: %s</p>" % salanimi).encode('utf-8')
Oi miten paljon kauniimpaa
Palvelukehitys 1:�palvelu pyörimään koneella
(Linux-versio)
tämä on tärkeää palvelun kokeilemisen vuoksi
Asennukset
Tarvitaan: paikka, johon asentaa ohjelmistopaketteja
atehwa@kladakong:~/proj/code02$ mkdir tunti1
atehwa@kladakong:~/proj/code02$ cd tunti1/
atehwa@kladakong:~/proj/code02/tunti1$ python3 -m venv myenv
atehwa@kladakong:~/proj/code02/tunti1$ source myenv/bin/activate
(myenv) atehwa@kladakong:~/proj/code02/tunti1$
Asennukset
Tarvitaan: sovelluspalvelinohjelma
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ pip install gunicorn
Collecting gunicorn
Downloading gunicorn-23.0.0-py3-none-any.whl (85 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 85.0/85.0 KB 4.4 MB/s eta 0:00:00
Collecting packaging
Using cached packaging-24.2-py3-none-any.whl (65 kB)
Installing collected packages: packaging, gunicorn
Successfully installed gunicorn-23.0.0 packaging-24.2
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ gunicorn --version
gunicorn (version 23.0.0)
Ensimmäinen versio
Tämä on webbisovellus, joka vastaa aina samalla tavalla.
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ cat palvelu.py
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
yield "Hello w€rld😞!".encode('utf-8')
Sovelluksen ajaminen omalla koneella
Tämä käynnistää verkkoyhteyksiä kuuntelevan sovelluspalvelinprosessin.
gunicornin voi myös sammuttaa Ctrl-C:llä (ainakin jos on järkevä komentotulkki)
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ gunicorn palvelu:app
[2025-02-25 15:20:16 +0200] [4079377] [INFO] Starting gunicorn 23.0.0
[2025-02-25 15:20:16 +0200] [4079377] [INFO] Listening at: http://127.0.0.1:8000 (4079377)
[2025-02-25 15:20:16 +0200] [4079377] [INFO] Using worker: sync
[2025-02-25 15:20:16 +0200] [4079378] [INFO] Booting worker with pid: 4079378
Palvelun kokeileminen
Mene selaimella osoitteeseen localhost:8000
Mitä selaimen ja palvelun välillä kulkee?
Web developer tools löytyy valikoista tai Ctrl-Shift-E.
Tee reload (Ctrl-R tai F5).
Itse vastaus löytyy "Response"-kohdasta
Tässä on varsinainen kysely
Tässä vastauksen perustiedot
Tässä vastauksen lisätiedot
Tässä kyselyn lisätiedot
Iteraatio 1: Selaimen lähettämien tietojen käyttö
Nämä ovat app-funktiolle annetussa environ-parametrissa.
Tulostamme ne takaisin selaimelle, jotta näkyisi, mitä tietoja selain lähettää.
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ cat palvelu.py
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
yield "Hello €nviron😞!\n\n".encode('utf-8')
for key in environ:
yield ("%s: %s\n" % (key, environ[key])).encode('utf-8')
Kokeilu
gunicorn pitää mahdollisesti käynnistää uudelleen.
Sitten reload.
Täällä on paljon hyödyllisiä tietoja! Erityisesti PATH_INFO ja QUERY_STRING näyttävät käyttökelpoisilta.
Myös wsgi.input mutta se ei oo merkkijono…
Iteraatio 2: salanimet
Muuta palvelumme koodi seuraavaan muotoon:
Tämän jälkeen pitää (totuttuun tapaan) uudelleenkäynnistää gunicorn ja ladata selaimessa sivu uudestaan
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ cat palvelu.py
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
nimi = environ['PATH_INFO'].strip('/')
salanimi = nimi.replace('a', 'apa').replace('i', 'ipi') \
.replace('n', 'non').replace('na', 'nana')
yield ("salainen nimesi on: %s\n" % salanimi).encode('utf-8')
Kokeilu
Jipii, olemme onnistuneet tekemään dynaamisen (ja äärimmäisen hyödyllisen) palvelun! Sieltä saa salanimiä.
Iteraatio 3: ulkonäön viilausta
Se, miten selain näyttää sille lähetetyn datan, riippuu sisältötyypistä. Vaihdetaan se text/plain:sta text/html:ksi ja laitetaan muotoiluja sekaan.
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ cat palvelu.py
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/html; charset=utf-8')])
nimi = environ['PATH_INFO'].strip('/')
salanimi = nimi.replace('a', 'apa').replace('i', 'ipi') \
.replace('n', '<b>non</b>').replace('na', 'nana')
yield "<h1>huomio!</h1>".encode('utf-8')
yield ("<p>salainen <em>nimesi</em> on: %s</p>" % salanimi).encode('utf-8')
Oi miten paljon kauniimpaa
Flask
environ:n PATH_INFOn ja varsinkin QUERY_STRINGin jäsentäminen on vaivalloista. "Web framework" (verkkosovelluskehys?) on kirjasto, joka helpottaa gunicornin (tai oikeastaan WSGI-rajapinnan) käyttöä. Otamme minimalistisen sovelluskehyksen, joka on nimeltään Flask.
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ pip install flask
Collecting flask
[...]
Flask-sovelluksen rakenne
… näyttää aika erilaiselta kuin puhdas WSGI.
Ajaminen käy sen sijaan hyvinkin samalla tavalla:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
$ gunicorn muntiedosto:app
Jos vielä jää aikaa…
… niin jatketaan Flask-tutoriaalilla
https://flask.palletsprojects.com/en/stable/quickstart/
Palvelukehitys 2: koodin hallinta ja yhteistyöstö
Tätä tarvitaan myös, jotta voi julkaista palveluita
Muistutuksena iso kuva: miten koodia julkaistaan ja muokataan yhdessä
Versiohallinnan pointti
https://bitbucket.org/product/version-control-software
https://git-scm.com/book/ms/v2/Getting-Started-About-Version-Control
https://www.geeksforgeeks.org/version-control-systems/
Versiohallinta ei itse asiassa liity mitenkään erityisesti koodiin.
… paitsi, että se toimii huonosti muulle kuin raakatekstille.
Ihan alkuun pääsy
https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git init
hint: [...]
Initialized empty Git repository in /home/atehwa/proj/code02/tunti1/.git/
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git checkout -b main
Switched to a new branch 'main'
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git config user.email atehwa@sange.fi
sisällön lisääminen gitiin
https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git add palvelu.py
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git commit -m "salanimipalvelu"
[main (root-commit) ae0aa0d] salanimipalvelu
1 file changed, 9 insertions(+)
create mode 100644 palvelu.py
Mitä muutoksia on tehty?
Git log kertoo commitilla talteen viedyt muutokset, git diff ne joita ei ole vielä viety
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git log
commit 1d1fb92877a95bdce5cfbc96a9736b8554487730 (HEAD -> main)
Author: Panu Kalliokoski <atehwa@sange.fi>
Date: Tue Feb 25 17:13:44 2025 +0200
dependencies
commit ae0aa0d716fff4e3cc773b0a0f46e2b0837873d0
Author: Panu Kalliokoski <atehwa@sange.fi>
Date: Tue Feb 25 17:11:48 2025 +0200
salanimipalvelu
Mitä muutoksia on tehty?
Jos muokkaan tiedostoa, git diff huomaa sen
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git diff
diff --git a/palvelu.py b/palvelu.py
index 7244ee1..1472026 100644
--- a/palvelu.py
+++ b/palvelu.py
@@ -2,8 +2,6 @@
def app(environ, respond):
respond('200 OK', [('Content-type', 'text/html; charset=utf-8')])
nimi = environ['PATH_INFO'].strip('/')
- salanimi = nimi.replace('a', 'apa').replace('i', 'ipi') \
- .replace('n', '<b>non</b>').replace('na', 'nana')
yield "<h1>huomio!</h1>".encode('utf-8')
- yield ("<p>salainen <em>nimesi</em> on: %s</p>" % salanimi).encode('utf-8')
+ yield ("<p><em>nimesi</em> on: %s</p>" % nimi).encode('utf-8')
Voin tallettaa osan tai kaikki muutokset
git add -p + git commit vie talteen haluamasi muutokset.�git commit -a vie kaikki.
(Tämän jälkeen usein viedään muutokset Githubiin komennolla git push.)
git log -p -komennolla voit tarkastella, mitä on tapahtunut missäkin muutoksessa.
https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History
Github-tunnuksen perustaminen
https://github.com/signup
Tee itsellesi säilö (repository)
Tämä on koodisi �julkaisua varten.
tähän tarvitsee säätöä (seuraava slide)
Githubiin joutuu nykyään tekemään kaikenlaista säätöä
Muutokset, joita�git remote add�-komentoon pitää tehdä
tähän tuli yksi vaihe lisää, klikkaa "personal access tokens"
$ git remote add origin https://atehwajotain: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@github.com/atehwajotain/code02-tunti1.git
$ git push -u origin main
Vihdoin, git-muutosten vienti luotuun repositoryyn
Alla oleva git remote add -komento pitää muodostaa githubin ohjeista, käyttäjätunnuksestasi ja äsken luodusta salasanasta.
atehwa@kladakong:~/proj/code02/tunti1$ ls
myenv palvelu.py __pycache__ requirements.txt
atehwa@kladakong:~/proj/code02/tunti1$ git remote add origin https://jokutosihieno435:ghp_grt7Ws7FNSGl1oILlF90Ad82A@github.com/jokutosihieno435/web-palvelu.git
atehwa@kladakong:~/proj/code02/tunti1$ git push -u origin main
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
[...]
Perustyöskentelytapa gitin kanssa
git pull --ff-only — bring your local working directory up-to-date
Extra: älä tee tätä automaattisesti! Harjoitus 1
Extra: älä tee tätä automaattisesti! Harjoitus 2
Palvelukehitys 3: oma palvelu nettiin
Sitten sitä voi esitellä sukulaisille.
Muistutus, missä mennään isossa kuvassa
Taas lisää webbipalveluita!
https://dashboard.render.com/register
render.com tekee lähdekoodistamme virtuaalikoneen
Odota, että se sanoo:
sitten:
Palvelun päivittäminen
Muokataan palvelua (testaus!), viedään muutokset versiohallintaan ja julkaistaan:
atehwa@kladakong:~/proj/code02/tunti1$ git add -p
[...]
yield "<h1>huomio!</h1>".encode('utf-8')
+ yield "<hr size=5>".encode('utf-8')
yield ("<p><em>nimesi</em> on: %s</p>" % nimi).encode('utf-8')
(1/1) Stage this hunk [y,n,q,a,d,e,?]? y
atehwa@kladakong:~/proj/code02/tunti1$ git commit -m "väliviiva"
[main 38cae79] väliviiva
1 file changed, 1 insertion(+)
atehwa@kladakong:~/proj/code02/tunti1$ git push -u origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
[...]
muutos näkyy githubissa
render.com pitää erikseen kehottaa päivittämään palvelu
Samat odottelut taas
Nykyään näemmä render tekee tän myös automaattisesti kun tulee uusi commit sen seuraamaan haaraan (main)
Palvelukehitys 4: monimutkaisempi palvelu
Kehitetään jotain mikä reagoi
Olisko nyt oikeasti jonkin kunnon web-frameworkin aika :0
Aletaan tässä vaiheessa tehdä Flaskilla juttuja :)
Usein palvelun pitäisi tehdä ihan eri asioita riippuen siitä, mikä esimerkiksi on selaimeen kirjoitettu osoite (joka tulee environ['PATH_INFO']:ssa meille) ja monista muista asioista riippuen.
Web framework (esim Flask) auttaa app:n saamien syötteiden (argumenttien) jäsentämisessä ja vastauksen muodostamisessa selaimelle.
Valmis esimerkkisovellus
Tätä varten Githubissa on valmis sovellus, joka on tehty flaskilla.
Sovellus on täällä: https://github.com/jokutosihieno435/hieno-lomake
Tee siitä itsellesi fork, jotta voit helposti julkaista muunneltuja versioita siitä (eli paina "Fork" oikealla ylhäällä)
Sen jälkeen voit tehdä omasta forkistasi (Githubissa) itsellesi kloonin (omalla koneellasi). Ota "code"-kohdasta repon osoite, mutta siihen pitää taas editoida käyttäjätunnus ja salasana kuten aiemmin.
Esimerkkisovelluksen osat
https://github.com/jokutosihieno435/hieno-lomake/blob/main/main.py:
https://github.com/jokutosihieno435/hieno-lomake/blob/main/templates/lomake.html:
https://github.com/jokutosihieno435/hieno-lomake/blob/main/templates/vastaus.html:
Esimerkkisovelluksen osat (jatkoa)
https://github.com/jokutosihieno435/hieno-lomake/blob/main/main.py#L11:
https://github.com/jokutosihieno435/hieno-lomake/blob/main/static/tyylit.css:
Ehdotuksia jatkoprojekteiksi
Esimerkki Panun jatkokehityksestä
Tein pysäkkiaikatauluja näyttävän palvelun, sen lähdekoodi on täällä:
https://github.com/pkalliok/hieno-lomake
Seuraavat slidet kertovat, miten Flask-sovellusta lähdetään tekemään tyhjästä.
Flask-sovelluksen kehitys alusta
Asennukset
Tarvitaan: paikka, johon asentaa ohjelmistopaketteja
atehwa@kladakong:~/proj/code02$ mkdir tunti1
atehwa@kladakong:~/proj/code02$ cd tunti1/
atehwa@kladakong:~/proj/code02/tunti1$ python3 -m venv myenv
atehwa@kladakong:~/proj/code02/tunti1$ source myenv/bin/activate
(myenv) atehwa@kladakong:~/proj/code02/tunti1$
Flask
environ:n PATH_INFOn ja varsinkin QUERY_STRINGin jäsentäminen on vaivalloista. "Web framework" (verkkosovelluskehys?) on kirjasto, joka helpottaa gunicornin (tai oikeastaan WSGI-rajapinnan) käyttöä. Otamme minimalistisen sovelluskehyksen, joka on nimeltään Flask.
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ pip install flask
Collecting flask
[...]
Otetaan asennetut kirjastot muistiin
Saadaan samalla esimerkki tokasta muutoksesta
https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ pip freeze > requirements.txt
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git add requirements.txt
(myenv) atehwa@kladakong:~/proj/code02/tunti1$ git commit -m "dependencies"
[main 1d1fb92] dependencies
1 file changed, 9 insertions(+)
create mode 100644 requirements.txt
Flask-sovelluksen rakenne
… näyttää aika erilaiselta kuin puhdas WSGI.
Ajaminen käy Flaskin omalla komennolla, jos haluaa kaikenlaisia kätevyyksiä:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
$ flask --app muntiedosto run -h localhost -p 8000
Teoreettiset ekstrat
käytettäväksi sopivassa välissä
Teoreettista lisäkamaa: www-sovelluksen tilakäsittely
www-sovellus toimii näin:
Teoreettista lisäkamaa: www-sovelluksen tilakäsittely
Esim. hieno-lomake-palvelussamme tämä toimii näin:
Teoreettista lisäkamaa: tekstiohjelma www-ohjelmaksi
Jokainen tilanne, jossa ohjelma näyttää joltain ja odottaa käyttäjän syötettä, on yksi ohjelman tila. Esimerkiksi tässä ohjelmassa on melko selvästi kolme tilaa.
Teoreettista lisäkamaa: tekstiohjelma www-ohjelmaksi
Jokaisesta alkuperäisen ohjelman tilasta pitää tehdä www-ohjelman tila. Ja koska www:ssä tila tarkoittaa tiettyä osoitetta, jokaisella tilalla pitää olla oma osoite.
Teoreettista lisäkamaa: tekstiohjelma www-ohjelmaksi
Jokaiseen sivuun pitää antaa ohjeet selaimelle, mihin tiloihin siitä pääsee. Esimerkiksi tässä /menu -tilan sisältämät linkit ovat selaimelle siirtymäohjeita, mihin tiloihin menusta voi siirtyä.
Panu Kalliokoski, 2025. Käytetty Helsingin matematiikkalukion CODE02-kurssilla.
Kysymyksiä voi lähettää:
panu.kalliokoski@sange.fi