Masinaõppeinsenerina ja arvutinägemise ekspert , Avastan end üllatavalt sageli Flaskiga API-sid ja isegi veebirakendusi. Selles postituses tahan jagada mõningaid näpunäiteid ja kasulikke retsepte täieliku tootmiseks valmis Kolbi rakenduse loomiseks.
Käsitleme järgmisi teemasid:
Alustame rakenduse koodi ja varade loomisega. Pange tähele, et ma ei käsitle selles postituses õiget Flaski rakenduse struktuuri. Demorakendus koosneb lühiduse ja selguse huvides minimaalsest arvust moodulitest ja pakettidest.
Kõigepealt looge kataloogistruktuur ja lähtestage tühi Giti hoidla.
mkdir flask-deploy cd flask-deploy # init GIT repo git init # create folder structure mkdir static tasks models config # install required packages with pipenv, this will create a Pipfile pipenv install flask flask-restful flask-sqlalchemy flask-migrate celery # create test static asset echo 'Hello World!' > static/hello-world.txt
Järgmisena lisame koodi.
Konfiguratsioonimoodulis määratleme oma pisikese konfiguratsioonihalduse raamistiku. Idee on panna rakendus käituma vastavalt seadistusele, mille on valinud APP_ENV
keskkonnamuutuja, pluss lisage vajadusel ükskõik milline konfiguratsiooniseade konkreetse keskkonnamuutujaga alistama.
import os import sys import config.settings # create settings object corresponding to specified env APP_ENV = os.environ.get('APP_ENV', 'Dev') _current = getattr(sys.modules['config.settings'], '{0}Config'.format(APP_ENV))() # copy attributes to the module for convenience for atr in [f for f in dir(_current) if not '__' in f]: # environment can override anything val = os.environ.get(atr, getattr(_current, atr)) setattr(sys.modules[__name__], atr, val) def as_dict(): res = {} for atr in [f for f in dir(config) if not '__' in f]: val = getattr(config, atr) res[atr] = val return res
See on komplekt konfiguratsiooniklassidest, millest ühe valib APP_ENV
muutuv. Kui rakendus töötab, kood __init__.py
instantsib ühe nendest klassidest, välistades välja väärtused konkreetsete keskkonnamuutujatega, kui need on olemas. Kolvi ja selleri konfiguratsiooni hiljem lähtestamisel kasutame lõplikku konfiguratsiooni objekti.
class BaseConfig(): API_PREFIX = '/api' TESTING = False DEBUG = False class DevConfig(BaseConfig): FLASK_ENV = 'development' DEBUG = True SQLALCHEMY_DATABASE_URI = 'postgresql://db_user: [email protected] :5432/flask-deploy' CELERY_BROKER = 'pyamqp://rabbit_user: [email protected] //' CELERY_RESULT_BACKEND = 'rpc://rabbit_user: [email protected] //' class ProductionConfig(BaseConfig): FLASK_ENV = 'production' SQLALCHEMY_DATABASE_URI = 'postgresql://db_user: [email protected] :5432/flask-deploy' CELERY_BROKER = 'pyamqp://rabbit_user: [email protected] //' CELERY_RESULT_BACKEND = 'rpc://rabbit_user: [email protected] //' class TestConfig(BaseConfig): FLASK_ENV = 'development' TESTING = True DEBUG = True # make celery execute tasks synchronously in the same process CELERY_ALWAYS_EAGER = True
Ülesannete pakett sisaldab selleri initsialiseerimiskoodi. Konfiguratsioonipaketti, millel on kõik seadistused initsialiseerimise ajal juba mooduli tasemel kopeeritud, kasutatakse selleri seadistamisobjekti värskendamiseks juhul, kui meil on tulevikus mõni selleri spetsiifiline säte - näiteks ajastatud toimingud ja töötajate ajalõpp.
from celery import Celery import config def make_celery(): celery = Celery(__name__, broker=config.CELERY_BROKER) celery.conf.update(config.as_dict()) return celery celery = make_celery()
See moodul on vajalik selleri töötaja käivitamiseks ja initsialiseerimiseks, mis töötab eraldi Dockeri konteineris. See lähtestab rakenduse Flask konteksti, et tal oleks juurdepääs rakendusega samale keskkonnale. Kui see pole vajalik, saab need jooned ohutult eemaldada.
from app import create_app app = create_app() app.app_context().push() from tasks import celery
Edasi läheb API pakett, mis määratleb REST API, kasutades paketti Flask-Restful. Meie rakendus on lihtsalt demo ja sellel on ainult kaks lõpp-punkti:
/process_data
- Alustab selleritöötajale pika operatsiooni ja tagastab uue ülesande ID./tasks/
- tagastab ülesande oleku ülesande ID järgi.import time from flask import jsonify from flask_restful import Api, Resource from tasks import celery import config api = Api(prefix=config.API_PREFIX) class TaskStatusAPI(Resource): def get(self, task_id): task = celery.AsyncResult(task_id) return jsonify(task.result) class DataProcessingAPI(Resource): def post(self): task = process_data.delay() return {'task_id': task.id}, 200 @celery.task() def process_data(): time.sleep(60) # data processing endpoint api.add_resource(DataProcessingAPI, '/process_data') # task status endpoint api.add_resource(TaskStatusAPI, '/tasks/')
Nüüd lisame SQLAlchemy mudeli User
jaoks objekt ja andmebaasimootori lähtestuskood. User
Meie demorakendus ei kasuta objekti mingil tähenduslikul viisil, kuid me vajame seda, et veenduda andmebaasi migreerimiste toimimises ja SQLAlchemy-Flaski integreerimise õiges seadistamises.
import uuid from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class User(db.Model): id = db.Column(db.String(), primary_key=True, default=lambda: str(uuid.uuid4())) username = db.Column(db.String()) email = db.Column(db.String(), unique=True)
Pange tähele, kuidas UUID luuakse vaikeväljendina automaatselt objekti ID-na.
Lõpuks loome peamise Flaski rakendusfaili.
from flask import Flask logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s]: {} %(levelname)s %(message)s'.format(os.getpid()), datefmt='%Y-%m-%d %H:%M:%S', handlers=[logging.StreamHandler()]) logger = logging.getLogger() def create_app(): logger.info(f'Starting app in {config.APP_ENV} environment') app = Flask(__name__) app.config.from_object('config') api.init_app(app) # initialize SQLAlchemy db.init_app(app) # define hello world page @app.route('/') def hello_world(): return 'Hello, World!' return app if __name__ == '__main__': app = create_app() app.run(host='0.0.0.0', debug=True) Siin me oleme:
- Põhilogimise seadistamine õiges vormingus koos aja, taseme ja protsessi ID-ga
- Rakenduse Flask loomise funktsiooni määratlemine API initsialiseerimise ja „Tere, maailm!” Abil lehele
- Rakenduse arendusajal töötamiseks sisestuspunkti määratlemine
wsgi.py
Samuti vajame Gunicorniga Flaski rakenduse käitamiseks eraldi moodulit. Sellel on ainult kaks rida:
from app import create_app app = create_app()
Rakenduse kood on valmis. Meie järgmine samm on Dockeri konfiguratsiooni loomine.
Dokkeri konteinerite ehitamine
Meie rakendus nõuab töötamiseks mitut Dockeri konteinerit:
- Rakenduskonteiner malllehtede serveerimiseks ja API lõpp-punktide kuvamiseks. Need kaks funktsiooni on mõistlik lavastuses jagada, kuid meil pole demorakenduses ühtegi malllehte. Konteiner töötab Gunicorni veebiserveriga, mis suhtleb kolbiga WSGI-protokolli kaudu.
- Selleri töötaja konteiner pikkade ülesannete täitmiseks. See on sama rakenduse konteiner, kuid Gunicorni asemel Celery käivitamiseks kohandatud käsu abil.
- Seller võitis konteineri - sarnane ülaltooduga, kuid korrapärase ajakava järgi kutsutud ülesannete jaoks, näiteks nende kasutajate kontode eemaldamiseks, kes pole kunagi oma e-posti aadressi kinnitanud.
- JänesMQ konteiner. Seller nõuab töötajate ja rakenduse vahel suhtlemiseks ning ülesannete tulemuste salvestamiseks sõnumivahendajat. RabbitMQ on tavaline valik, kuid võite kasutada ka Redist või Kafkat.
- Andmebaasikonteiner koos PostgreSQL-iga.
Loomulik viis mitme konteineri hõlpsaks haldamiseks on Docker Compose'i kasutamine. Kuid kõigepealt peame looma Dockerfile'i, et ehitada meie rakenduse jaoks konteineripilt. Pange see projekti kataloogi.
FROM python:3.7.2 RUN pip install pipenv ADD . /flask-deploy WORKDIR /flask-deploy RUN pipenv install --system --skip-lock RUN pip install gunicorn[gevent] EXPOSE 5000 CMD gunicorn --worker-class gevent --workers 8 --bind 0.0.0.0:5000 wsgi:app --max-requests 10000 --timeout 5 --keep-alive 5 --log-level info
See fail juhendab Dockerit:
- Installige kõik sõltuvused Pipenv abil
- Lisage konteinerisse rakenduste kaust
- Paljastage hostile TCP-port 5000
- Määrake konteineri vaikekäivituskäskuks Gunicorn kõne
Arutleme rohkem, mis juhtub viimasel real. See käitab Gunicorni, täpsustades töölisklassi järgmiselt tuulutati . Gevent on kerge samaaegne lib, mis on mõeldud ühistööga multitegumtöötlemiseks. See annab sisend- / väljundühendustega koormustele märkimisväärse jõudluse kasvu, pakkudes paremat protsessori kasutamist võrreldes OS-i ennetava niitide mitmeülesandega. --workers
parameeter on töötajate protsesside arv. See on hea mõte seadistada võrdseks tuumade arvuga serveris.
Kui meil on rakenduse konteiner Dockerfile, saame luua docker-compose.yml
fail, mis määrab kõik konteinerid, mida rakendus vajab.
version: '3' services: broker-rabbitmq: image: 'rabbitmq:3.7.14-management' environment: - RABBITMQ_DEFAULT_USER=rabbit_user - RABBITMQ_DEFAULT_PASS=rabbit_password db-postgres: image: 'postgres:11.2' environment: - POSTGRES_USER=db_user - POSTGRES_PASSWORD=db_password migration: build: . environment: - APP_ENV=${APP_ENV} command: flask db upgrade depends_on: - db-postgres api: build: . ports: - '5000:5000' environment: - APP_ENV=${APP_ENV} depends_on: - broker-rabbitmq - db-postgres - migration api-worker: build: . command: celery worker --workdir=. -A tasks.celery --loglevel=info environment: - APP_ENV=${APP_ENV} depends_on: - broker-rabbitmq - db-postgres - migration api-beat: build: . command: celery beat -A tasks.celery --loglevel=info environment: - APP_ENV=${APP_ENV} depends_on: - broker-rabbitmq - db-postgres - migration
Määrasime järgmised teenused:
broker-rabbitmq
- RabbitMQ sõnumivahendaja konteiner. Ühenduse mandaadid on määratletud keskkonnamuutujate abil db-postgres
- PostgreSQL-i konteiner ja selle volikirjad migration
- Rakenduse konteiner, mis teostab andmebaasi üleviimise rakendusega Flask-Migrate ja väljub. API konteinerid sõltuvad sellest ja töötavad pärast seda. api
- peamine rakenduse konteiner api-worker
ja api-beat
- konteinerid, mis töötavad selleritöötajatega API-lt saadud ülesannete ja kavandatud ülesannete jaoks
Iga rakenduse konteiner saab ka APP_ENV
muutuja väärtusest docker-compose up
käsk.
Kui kõik rakenduse varad on meil valmis, pange need GitHubi, mis aitab meil koodi serverisse juurutada.
git add * git commit -a -m 'Initial commit' git remote add origin [email protected] :your-name/flask-deploy.git git push -u origin master
Serveri seadistamine
Meie kood on nüüd GitHubis ja üle jääb vaid serveri esmane seadistamine ja rakenduse juurutamine. Minu puhul on server AWS-i eksemplar, milles töötab AMI Linux. Muude Linuxi versioonide puhul võivad juhised veidi erineda. Samuti eeldan, et serveril on juba väline IP-aadress, DNS on konfigureeritud sellele IP-le osutava kirjega A ja domeenile väljastatakse SSL-sertifikaadid.
Turvalisuse näpunäide: Ärge unustage oma hostikonsoolis lubada HTTP (S) liikluse jaoks porte 80 ja 44, SSH porti 22 (või | | + + | |) kasutades ja sulgeda välimine juurdepääs kõigile teistele portidele! Tehke kindlasti sama ka IPv6 protokoll!
Sõltuvuste installimine
Esiteks vajame koodi tõmbamiseks Nginxit ja Dockerit ning Gitit. Logime sisse SSH kaudu ja kasutame nende installimiseks paketihaldurit.
iptables
Nginxi seadistamine
Järgmine samm on Nginxi konfigureerimine. Peamine sudo yum install -y docker docker-compose nginx git
konfiguratsioonifail on tihtipeale hea. Siiski kontrollige kindlasti, kas see sobib teie vajadustega. Loome oma rakenduse jaoks uue konfiguratsioonifaili kaustas nginx.conf
kausta. Tipptasemel konfiguratsioonil on direktiiv, mis hõlmab kõiki conf.d
faile sellest.
.conf
Siin on Nginxi kolbi saidi konfiguratsioonifail, kaasas patareid. Sellel on järgmised omadused:
- SSL on konfigureeritud. Teil peaks olema oma domeeni jaoks kehtivad sertifikaadid, nt tasuta Krüpteerime tunnistus.
cd /etc/nginx/conf.d sudo vim flask-deploy.conf
taotlused suunatakse aadressile www.your-site.com
- HTTP-päringud suunatakse HTTPS-pordi turvaliseks muutmiseks.
- Tagurpidi puhverserver on konfigureeritud edastama päringuid kohalikule porti 5000.
- Staatilisi faile teenindab Nginx kohalikust kaustast.
your-site.com
Pärast faili redigeerimist käivitage server { listen 80; listen 443; server_name www.your-site.com; # check your certificate path! ssl_certificate /etc/nginx/ssl/your-site.com/fullchain.crt; ssl_certificate_key /etc/nginx/ssl/your-site.com/server.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; # redirect to non-www domain return 301 https://your-site.com$request_uri; } # HTTP to HTTPS redirection server { listen 80; server_name your-site.com; return 301 https://your-site.com$request_uri; } server { listen 443 ssl; # check your certificate path! ssl_certificate /etc/nginx/ssl/your-site.com/fullchain.crt; ssl_certificate_key /etc/nginx/ssl/your-site.com/server.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; # affects the size of files user can upload with HTTP POST client_max_body_size 10M; server_name your-site.com; location / { include /etc/nginx/mime.types; root /home/ec2-user/flask-deploy/static; # if static file not found - pass request to Flask try_files $uri @flask; } location @flask { add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; proxy_read_timeout 10; proxy_send_timeout 10; send_timeout 60; resolver_timeout 120; client_body_timeout 120; # set headers to pass request info to Flask proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; proxy_pass http://127.0.0.1:5000$uri; } }
ja vaadake, kas seal on vigu.
GitHubi mandaatide seadistamine
Hea tava on projekti ja CI / CD-süsteemi juurutamiseks eraldi VCS-konto „juurutamine“. Nii ei riski te oma konto mandaatide avaldamisega. Projektihoidla täiendavaks kaitsmiseks võite selle konto õigusi piirata ainult kirjutuskaitstud juurdepääsuga. GitHubi hoidla jaoks vajate selleks organisatsiooni kontot. Demorakenduse juurutamiseks loome lihtsalt avaliku võtme serverisse ja registreerime selle GitHubis, et pääseda juurde meie projektile iga kord mandaate sisestamata.
Uue SSH-võtme loomiseks käivitage:
sudo nginx -s reload
Seejärel logige sisse GitHubi ja lisage oma avalik võti alates cd ~/.ssh ssh-keygen -b 2048 -t rsa -f id_rsa.pub -q -N '' -C 'deploy'
konto seadetes.
Rakenduse juurutamine
Viimased sammud on üsna lihtsad - peame hankima GitHubilt rakenduskoodi ja käivitama kõik konteinerid rakendusega Docker Compose.
~/.ssh/id_rsa.pub
Võib olla hea mõte cd ~ git clone https://github.com/your-name/flask-deploy.git git checkout master APP_ENV=Production docker-compose up -d
välja jätta (mis käivitab konteineri eraldatud režiimis) esimeseks jooksuks, et näha iga konteineri väljundit otse terminalis ja kontrollida võimalikke probleeme. Teine võimalus on kontrollida iga üksikut konteinerit -d
-ga pärast. Vaatame, kas kõik meie konteinerid töötavad docker logs
-ga
Suurepärane. Kõik viis konteinerit on töökorras. Docker Koostage määratud konteinerite nimed automaatselt vastavalt teenuses docker-compose.yml määratud teenusele. Nüüd on aeg lõpuks testida, kuidas kogu konfiguratsioon töötab! Parim on testid käivitada välisest arvutist, veendumaks, et serveris on õiged võrguseaded.
docker ps.
See on kõik. Meil on AWS-i eksemplaris töötava rakenduse minimalistlik, kuid täielikult tootmisvalmis konfiguratsioon. Loodetavasti aitab see teil tõsielulist rakendust kiiresti üles ehitama hakata ja vältida levinumaid vigu! Kogu kood on saadaval GitHubi hoidlas .
Järeldus
Selles artiklis käsitlesime mõnda parimat praktikat kolvi rakenduse struktureerimisel, konfigureerimisel, pakendamisel ja tootmisse juurutamisel. See on väga suur teema, mida pole võimalik ühes ajaveebipostituses täielikult kajastada. Siin on loetelu olulistest küsimustest, mida me ei käsitlenud:
See artikkel ei hõlma järgmist:
- Pidev integreerimine ja pidev juurutamine
- Automaatne testimine
- Logide saatmine
- API jälgimine
- Rakenduse laiendamine mitmesse serverisse
- Volituste kaitse lähtekoodis
Kuid saate teada, kuidas seda teha, kasutades mõnda muud selle ajaveebi suurepärast ressurssi. Näiteks metsaraie uurimiseks vt Pythoni logimine: põhjalik õpetus või üldise ülevaate CI / CD ja automatiseeritud testimise kohta vt Kuidas luua tõhusat esialgset juurutamistorustikku . Jätan nende rakendamise harjutuseks teile, lugeja.
Täname lugemast!
Põhitõdede mõistmine
Mis on kolbi rakendus?
Flaski rakendus on Pythoni rakendus, mis on loodud veebi jaoks koos Flaski teegiga.
Kumb on parem, kolb või Django?
Mõlemad raamistikud sobivad mitmesuguste veebiga seotud ülesannete täitmiseks. Kolbi kasutatakse sageli veebiteenuste ehitamiseks, mis ei ole täieõiguslikud veebisaidid, ja see on tuntud oma paindlikkuse poolest.
Mis on Gunicorn?
Gunicorn on Pythoni WSGI HTTP-server. Selle tavaliseks kasutamiseks on Flaski või Django Pythoni rakenduste serveerimine WSGI-liidese kaudu tootmises.
Mis on seller ja miks seda koos kolbiga kasutada?
Veebiserverid, nagu Flask, ei sobi pikaajaliste ülesannete täitmiseks, näiteks videotöötluseks. Seller on ülesannete järjekord selliste ülesannete mugavaks ja asünkroonseks käsitsemiseks. Ülesande andmed salvestatakse toetatud tagumises salvestusmootoris, näiteks RabbitMQ või Redis.
Miks kasutada Docker Compose'i?
Docker Compose on Dockeri jaoks mugav tööriist, mis võimaldab teenuseid määratledes töötada konteineritega kõrgemal abstraktsioonitasemel. Samuti käsitletakse konteinerite käivitamise ja peatamise tavalisi stsenaariume.
Miks on meil vaja kolbi jaoks Nginxi veebiserverit?
Kuigi Gunicorn sobib hästi rakendusserveriks, ei ole turvalisuse kaalutlustel ja veebipäringute käsitlemise piirangute tõttu mõistlik lasta Internetil silmitsi seista. Levinud muster on Nginxi kasutamine pöördproxyna Gunicorni ees teenusevaheliste taotluste suunamiseks.
Millal on PostgreSQL hea valik?
PostgreSQL on küps suure jõudlusega relatsiooniline andmebaasimootor, millel on palju pistikprogramme ja teeke kõigi suuremate programmeerimiskeelte jaoks. See on ideaalne valik, kui teie projekt, hoolimata sellest, kas see on suur või väike, vajab lihtsalt andmebaasi. Lisaks on see avatud lähtekoodiga ja tasuta.