Workshop RHEL 8 Beta: Bouwe wurkjende webapplikaasjes

RHEL 8 Beta biedt ûntwikkelders in protte nije funksjes, wêrfan de list siden kin nimme, lykwols, nije dingen leare is altyd better yn 'e praktyk, dus hjirûnder biede wy in workshop oan oer it feitlik meitsjen fan in applikaasje-ynfrastruktuer basearre op Red Hat Enterprise Linux 8 Beta.

Workshop RHEL 8 Beta: Bouwe wurkjende webapplikaasjes

Litte wy Python, in populêre programmeartaal ûnder ûntwikkelders, as basis nimme, in kombinaasje fan Django en PostgreSQL, in frij gewoane kombinaasje foar it meitsjen fan applikaasjes, en konfigurearje RHEL 8 Beta om mei har te wurkjen. Dan sille wy noch in pear (net-klassifisearre) yngrediïnten tafoegje.

De testomjouwing sil feroarje, om't it ynteressant is om de mooglikheden fan automatisearring te ferkennen, te wurkjen mei konteners en omjouwings te besykjen mei meardere servers. Om te begjinnen mei in nij projekt, kinne jo begjinne mei it meitsjen fan in lyts, ienfâldich prototype mei de hân, sadat jo sjen kinne presys wat moat barre en hoe't it ynteraksje, en dan fierder te automatisearjen en meitsje mear komplekse konfiguraasjes. Hjoed prate wy oer it meitsjen fan sa'n prototype.

Litte wy begjinne mei it ynsetten fan de RHEL 8 Beta VM-ôfbylding. Jo kinne in firtuele masine fanôf it begjin ynstallearje, of brûk de KVM-gastôfbylding beskikber mei jo Beta-abonnemint. As jo ​​​​in gastôfbylding brûke, moatte jo in firtuele CD konfigurearje dy't metadata en brûkersgegevens sil befetsje foar inisjalisaasje fan 'e wolk (cloud-init). Jo hoege neat spesjaal te dwaan mei de skiifstruktuer of beskikbere pakketten; elke konfiguraasje sil dwaan.

Litte wy it hiele proses in tichterby besjen.

Django ynstallearje

Mei de nijste ferzje fan Django hawwe jo in firtuele omjouwing (virtualenv) nedich mei Python 3.5 of letter. Yn 'e Beta-notysjes kinne jo sjen dat Python 3.6 beskikber is, litte wy kontrolearje as dit yndie it gefal is:

[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found

Red Hat brûkt Python aktyf as in systeemtoolkit yn RHEL, dus wêrom docht dit resultaat?

It feit is dat in protte Python-ûntwikkelders noch tinke oer de oergong fan Python 2 nei Python 2, wylst Python 3 sels ûnder aktive ûntwikkeling is, en hieltyd mear nije ferzjes ferskine. Dêrom, om te foldwaan oan de needsaak foar stabile systeem ark wylst it oanbieden fan brûkers tagong ta ferskate nije ferzjes fan Python, systeem Python waard ferpleatst yn in nij pakket en levere de mooglikheid om te ynstallearjen sawol Python 2.7 as 3.6. Mear ynformaasje oer de feroarings en wêrom't se makke binne is te finen yn 'e publikaasje yn Langdon White's blog (Langdon de Wit).

Dus, om Python te wurkjen, hoege jo allinich twa pakketten te ynstallearjen, mei python3-pip opnommen as ôfhinklikens.

sudo yum install python36 python3-virtualenv

Wêrom net brûke direkte module calls as Langdon suggerearret en ynstallearje pip3? Mei it each op de kommende automatisearring, is it bekend dat Ansible pip ynstalleare sil fereaskje om te rinnen, om't de pip-module gjin virtualenvs stipet mei in oanpaste pip-útfierbere.

Mei in wurkjende python3-tolk ta jo beskikking kinne jo trochgean mei it Django-ynstallaasjeproses en in wurkjend systeem hawwe tegearre mei ús oare komponinten. D'r binne in protte útfieringsopsjes beskikber op it ynternet. D'r is hjir ien ferzje presintearre, mar brûkers kinne har eigen prosessen brûke.

Wy sille de PostgreSQL- en Nginx-ferzjes ynstalleare yn RHEL 8 standert mei Yum.

sudo yum install nginx postgresql-server

PostgreSQL sil psycopg2 fereaskje, mar it moat allinich beskikber wêze yn in virtualenv-omjouwing, dus wy sille it ynstallearje mei pip3 tegearre mei Django en Gunicorn. Mar earst moatte wy virtualenv ynstelle.

D'r is altyd in protte debat oer it ûnderwerp fan it kiezen fan it juste plak om Django-projekten te ynstallearjen, mar as jo yn twifel binne, kinne jo altyd nei de Linux Filesystem Hierarchy Standard wende. Spesifyk seit de FHS dat / srv wurdt brûkt om: "host-spesifike gegevens op te slaan - gegevens dy't it systeem produseart, lykas webservergegevens en skripts, gegevens opslein op FTP-tsjinners, en kontrôlesysteemrepositories." ferzjes (ferskynd yn FHS -2.3 yn 2004)."

Dit is krekt ús gefal, dus wy sette alles wat wy nedich binne yn /srv, dy't eigendom is fan ús applikaasje-brûker (wolk-brûker).

sudo mkdir /srv/djangoapp
sudo chown cloud-user:cloud-user /srv/djangoapp
cd /srv/djangoapp
virtualenv django
source django/bin/activate
pip3 install django gunicorn psycopg2
./django-admin startproject djangoapp /srv/djangoapp

It ynstellen fan PostgreSQL en Django is maklik: meitsje in database, meitsje in brûker, konfigurearje tagongsrjochten. Ien ding om yn gedachten te hâlden by it earstoan ynstallearjen fan PostgreSQL is it postgresql-setup-skript dat ynstalleare is mei it postgresql-serverpakket. Dit skript helpt jo út te fieren basistaken ferbûn mei database kluster administraasje, lykas kluster inisjalisaasje of it upgrade proses. Om in nije PostgreSQL-eksimplaar op in RHEL-systeem te konfigurearjen, moatte wy it kommando útfiere:

sudo /usr/bin/postgresql-setup -initdb

Jo kinne dan PostgreSQL begjinne mei systemd, in database oanmeitsje en in projekt yn Django ynstelle. Unthâld om PostgreSQL opnij te begjinnen nei't jo wizigingen makke hawwe oan it konfiguraasjetriem foar kliïntautentikaasje (meastentiids pg_hba.conf) om wachtwurdopslach foar de applikaasjebrûker te konfigurearjen. As jo ​​oare swierrichheden tsjinkomme, soargje derfoar dat jo de IPv4- en IPv6-ynstellingen wizigje yn it pg_hba.conf-bestân.

systemctl enable -now postgresql

sudo -u postgres psql
postgres=# create database djangoapp;
postgres=# create user djangouser with password 'qwer4321';
postgres=# alter role djangouser set client_encoding to 'utf8';
postgres=# alter role djangouser set default_transaction_isolation to 'read committed';
postgres=# alter role djangouser set timezone to 'utc';
postgres=# grant all on DATABASE djangoapp to djangouser;
postgres=# q

Yn it bestân /var/lib/pgsql/data/pg_hba.conf:

# IPv4 local connections:
host    all        all 0.0.0.0/0                md5
# IPv6 local connections:
host    all        all ::1/128                 md5

Yn it bestân /srv/djangoapp/settings.py:

# Database
DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql_psycopg2',
       'NAME': '{{ db_name }}',
       'USER': '{{ db_user }}',
       'PASSWORD': '{{ db_password }}',
       'HOST': '{{ db_host }}',
   }
}

Nei it konfigurearjen fan it settings.py-bestân yn it projekt en it ynstellen fan de databankkonfiguraasje, kinne jo de ûntwikkelingstsjinner begjinne om te soargjen dat alles wurket. Nei it starten fan de ûntwikkelingsserver is it in goed idee om in admin-brûker te meitsjen om de ferbining mei de database te testen.

./manage.py runserver 0.0.0.0:8000
./manage.py createsuperuser

WSGI? Wai?

De ûntwikkelingstsjinner is nuttich foar testen, mar om de applikaasje út te fieren moatte jo de passende tsjinner en proxy konfigurearje foar de Web Server Gateway Interface (WSGI). D'r binne ferskate mienskiplike kombinaasjes, bygelyks Apache HTTPD mei uWSGI of Nginx mei Gunicorn.

De taak fan 'e Web Server Gateway Interface is om fersiken fan' e webtsjinner troch te stjoeren nei it Python-webkader. WSGI is in oerbliuwsel fan it skriklike ferline doe't CGI-motoren wiene, en hjoed is WSGI de de facto standert, nettsjinsteande de brûkte webserver of Python-ramt. Mar nettsjinsteande it wiidferspraat gebrûk, binne d'r noch in protte nuânses by it wurkjen mei dizze kaders, en in protte karren. Yn dit gefal sille wy besykje ynteraksje te fêstigjen tusken Gunicorn en Nginx fia in socket.

Sûnt beide fan dizze komponinten binne ynstallearre op deselde tsjinner, lit ús besykje te brûken in UNIX socket ynstee fan in netwurk socket. Sûnt kommunikaasje fereasket in socket yn alle gefallen, lit ús besykje te nimmen noch ien stap en konfigurearje socket aktivearring foar Gunicorn fia systemd.

It proses fan it meitsjen fan socket-aktivearre tsjinsten is frij simpel. Earst wurdt in ienheidsbestân makke dy't in ListenStream-rjochtline befettet dy't wiist nei it punt wêrop de UNIX-socket sil wurde oanmakke, dan in ienheidsbestân foar de tsjinst wêryn de Requires-rjochtline sil wize op it socket-ienheidbestân. Dan, yn 'e tsjinst-ienheidbestân, is alles wat oerbliuwt om Gunicorn te skiljen fan' e firtuele omjouwing en in WSGI-ferbining te meitsjen foar de UNIX-socket en de Django-applikaasje.

Hjir binne wat foarbylden fan ienheidsbestannen dy't jo as basis kinne brûke. Earst sette wy de socket op.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

No moatte jo de Gunicorn-daemon konfigurearje.

[Unit]
Description=Gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=cloud-user
Group=cloud-user
WorkingDirectory=/srv/djangoapp

ExecStart=/srv/djangoapp/django/bin/gunicorn 
         —access-logfile - 
         —workers 3 
         —bind unix:gunicorn.sock djangoapp.wsgi

[Install]
WantedBy=multi-user.target

Foar Nginx is it in ienfâldige saak fan it meitsjen fan proxy-konfiguraasjebestannen en it ynstellen fan in map om statyske ynhâld op te slaan as jo ien brûke. Yn RHEL binne Nginx-konfiguraasjebestannen yn /etc/nginx/conf.d. Jo kinne it folgjende foarbyld kopiearje yn it bestân /etc/nginx/conf.d/default.conf en de tsjinst begjinne. Soargje derfoar dat jo de server_name ynstelle om oerien te kommen mei jo hostnamme.

server {
   listen 80;
   server_name 8beta1.example.com;

   location = /favicon.ico { access_log off; log_not_found off; }
   location /static/ {
       root /srv/djangoapp;
   }

   location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_pass http://unix:/run/gunicorn.sock;
   }
}

Start de Gunicorn-socket en Nginx mei systemd en jo binne ree om te begjinnen mei testen.

Bad Gateway flater?

As jo ​​​​it adres yn jo blêder ynfiere, krije jo nei alle gedachten in 502 Bad Gateway-flater. It kin wurde feroarsake troch ferkeard ynstelde UNIX-socket-tagongsrjochten, of it kin wêze fanwege mear komplekse problemen yn ferbân mei tagongskontrôle yn SELinux.

Yn it nginx-flaterlog kinne jo in rigel lykas dit sjen:

2018/12/18 15:38:03 [crit] 12734#0: *3 connect() to unix:/run/gunicorn.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.122.1, server: 8beta1.example.com, request: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", host: "8beta1.example.com"

As wy Gunicorn direkt testen, krije wy in leech antwurd.

curl —unix-socket /run/gunicorn.sock 8beta1.example.com

Litte wy útfine wêrom dit bart. As jo ​​​​it log iepenje, sille jo wierskynlik sjen dat it probleem relatearre is oan SELinux. Om't wy in daemon útfiere dêr't gjin belied foar makke is, wurdt it markearre as init_t. Litte wy dizze teory yn 'e praktyk testen.

sudo setenforce 0

Dit alles kin krityk en bloedtriennen feroarsaakje, mar dit is gewoan it debuggen fan it prototype. Litte wy de kontrôle útskeakelje gewoan om der wis fan te wêzen dat dit it probleem is, wêrnei't wy alles werombringe op syn plak.

Troch de side yn 'e browser te ferfarskjen of ús curl-kommando opnij út te fieren, kinne jo de Django-testside sjen.

Dat, nei't der wis fan is dat alles wurket en d'r gjin tastimmingsproblemen mear binne, skeakelje wy SELinux wer yn.

sudo setenforce 1

Ik sil hjir net prate oer audit2allow of it meitsjen fan warskôgingsbasearre belied mei sepolgen, om't d'r op it stuit gjin echte Django-applikaasje is, dus d'r is gjin folsleine kaart fan wêr't Gunicorn tagong ta kin en wêr't it tagong ta moat wegerje. Dêrom is it nedich om SELinux rinnend te hâlden om it systeem te beskermjen, wylst tagelyk de applikaasje kin útfiere en berjochten yn 'e kontrôlelog litte litte, sadat it eigentlike belied dan fan har oanmakke wurde kin.

It opjaan fan permissive domeinen

Net elkenien hat heard fan tastiene domeinen yn SELinux, mar se binne neat nij. In protte wurken sels mei har sûnder it sels te realisearjen. As in belied wurdt makke op basis fan kontrôleberjochten, fertsjintwurdiget it oanmakke belied it oploste domein. Litte wy besykje in ienfâldich fergunningsbelied te meitsjen.

Om in spesifyk tastien domein foar Gunicorn te meitsjen, hawwe jo in soarte fan belied nedich, en jo moatte ek de passende bestannen markearje. Derneist binne ark nedich om nij belied te sammeljen.

sudo yum install selinux-policy-devel

It tastiene domeinmeganisme is in geweldich ark foar it identifisearjen fan problemen, benammen as it giet om in oanpaste applikaasje of applikaasjes dy't ferstjoere sûnder al makke belied. Yn dit gefal sil it tastiene domeinbelied foar Gunicorn sa ienfâldich mooglik wêze - ferklearje in haadtype (gunicorn_t), ferklearje in type dat wy sille brûke om meardere útfierbere te markearjen (gunicorn_exec_t), en set dan in oergong yn foar systeem om korrekt te markearjen rinnende prosessen. De lêste rigel stelt it belied yn as standert ynskeakele op it momint dat it wurdt laden.

gunicorn.te:

policy_module(gunicorn, 1.0)

type gunicorn_t;
type gunicorn_exec_t;
init_daemon_domain(gunicorn_t, gunicorn_exec_t)
permissive gunicorn_t;

Jo kinne dit beliedsbestân kompilearje en tafoegje oan jo systeem.

make -f /usr/share/selinux/devel/Makefile
sudo semodule -i gunicorn.pp

sudo semanage permissive -a gunicorn_t
sudo semodule -l | grep permissive

Litte wy kontrolearje om te sjen oft SELinux wat oars blokkearret as wat ús ûnbekende daemon tagong hat.

sudo ausearch -m AVC

type=AVC msg=audit(1545315977.237:1273): avc:  denied { write } for pid=19400 comm="nginx" name="gunicorn.sock" dev="tmpfs" ino=52977 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file permissive=0

SELinux foarkomt dat Nginx gegevens skriuwt nei de UNIX-socket brûkt troch Gunicorn. Typysk begjint yn sokke gefallen belied te feroarjen, mar d'r binne oare útdagings foarút. Jo kinne ek de domeinynstellingen feroarje fan in beheiningdomein nei in tastimmingsdomein. Litte wy no httpd_t ferpleatse nei it tagongsdomein. Dit sil Nginx de nedige tagong jaan en wy kinne trochgean mei fierdere debuggen.

sudo semanage permissive -a httpd_t

Dat, as jo ienris slagge binne om SELinux beskerme te hâlden (jo moatte wirklik gjin SELinux-projekt yn beheinde modus litte) en de tastimmingsdomeinen binne laden, moatte jo útfine wat krekt as gunicorn_exec_t markearre wurde moat om alles goed te wurkjen wer. Litte wy besykje de webside te besykjen om nije berjochten te sjen oer tagongsbeperkingen.

sudo ausearch -m AVC -c gunicorn

Jo sille in protte berjochten sjen dy't 'comm="gunicorn"' befetsje dy't ferskate dingen dogge op bestannen yn /srv/djangoapp, dus dit is fansels ien fan 'e kommando's dy't it wurdich is om te flaggen.

Mar boppedat ferskynt in berjocht as dit:

type=AVC msg=audit(1545320700.070:1542): avc:  denied { execute } for pid=20704 comm="(gunicorn)" name="python3.6" dev="vda3" ino=8515706 scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0

As jo ​​​​nei de status fan 'e gunicorn-tsjinst sjogge of it ps-kommando útfiere, sille jo gjin rinnende prosessen sjen. It liket derop dat gunicorn besiket tagong te krijen ta de Python-tolk yn ús virtualenv-omjouwing, mooglik om arbeidersskripts út te fieren. Litte wy no dizze twa útfierbere bestannen markearje en kontrolearje as wy ús Django-testside kinne iepenje.

chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6

De gunicorn-tsjinst sil opnij starte moatte foardat de nije tag kin wurde selektearre. Jo kinne it fuortendaliks opnij starte of de tsjinst stopje en de socket it begjinne as jo de side yn 'e browser iepenje. Ferifiearje dat prosessen de juste labels hawwe ûntfongen mei ps.

ps -efZ | grep gunicorn

Ferjit net letter in normaal SELinux-belied te meitsjen!

As jo ​​no nei de AVC-berjochten sjogge, befettet it lêste berjocht permissive=1 foar alles relatearre oan de applikaasje, en permissive=0 foar de rest fan it systeem. As jo ​​begripe hokker soarte tagong in echte applikaasje nedich is, kinne jo fluch de bêste manier fine om sokke problemen op te lossen. Mar oant dan is it it bêste om it systeem feilich te hâlden en in dúdlike, brûkbere kontrôle fan it Django-projekt te krijen.

sudo ausearch -m AVC

Happened!

In wurkjend Django-projekt is ferskynd mei in frontend basearre op Nginx en Gunicorn WSGI. Wy konfigureare Python 3 en PostgreSQL 10 fanút de RHEL 8 Beta-repositories. No kinne jo foarút gean en Django-applikaasjes oanmeitsje (of gewoan ynsette) of oare beskikbere ark yn RHEL 8 Beta ferkenne om it konfiguraasjeproses te automatisearjen, prestaasjes te ferbetterjen, of sels dizze konfiguraasje te kontenerearjen.

Boarne: www.habr.com

Add a comment