Workshop RHEL 8 Beta: Bauen vun Aarbechtswebapplikatiounen

RHEL 8 Beta bitt Entwéckler vill nei Fonctiounen, d'Lëscht vun deenen Säiten daueren kënnen, awer nei Saachen léieren ass ëmmer besser an der Praxis, also hei ënnen proposéiere mir en Workshop fir tatsächlech eng Applikatiounsinfrastruktur ze kreéieren baséiert op Red Hat Enterprise Linux 8 Beta.

Workshop RHEL 8 Beta: Bauen vun Aarbechtswebapplikatiounen

Loosst eis Python huelen, eng populär Programméierungssprooch ënner Entwéckler, als Basis, eng Kombinatioun vun Django a PostgreSQL, eng zimlech allgemeng Kombinatioun fir Uwendungen ze kreéieren, a konfiguréieren RHEL 8 Beta fir mat hinnen ze schaffen. Da wäerte mir e puer méi (onklassifizéiert) Zutaten derbäi.

D'Testëmfeld wäert änneren, well et interessant ass d'Méiglechkeeten vun der Automatisatioun ze entdecken, mat Container ze schaffen an Ëmfeld mat multiple Serveren ze probéieren. Fir mat engem neie Projet unzefänken, kënnt Dir ufänken andeems Dir e klengen, einfache Prototyp mat der Hand erstellt, fir datt Dir genau kuckt wat muss geschéien a wéi et interagéiert, an dann weider fir automatiséieren a méi komplex Konfiguratiounen ze kreéieren. Haut schwätze mir iwwer d'Schafung vun esou engem Prototyp.

Loosst eis ufänken mam RHEL 8 Beta VM Bild z'installéieren. Dir kënnt eng virtuell Maschinn vun Null installéieren, oder benotzt de KVM Gaaschtbild verfügbar mat Ärem Beta Abonnement. Wann Dir e Gaaschtbild benotzt, musst Dir eng virtuell CD konfiguréieren déi Metadaten a Benotzerdaten fir d'Cloudinitialiséierung enthält (Cloud-Init). Dir braucht näischt Besonnesches mat der Diskstruktur oder verfügbare Packagen ze maachen; all Konfiguratioun wäert maachen.

Loosst eis de ganze Prozess méi genau kucken.

Django installéieren

Mat der neier Versioun vum Django braucht Dir e virtuellt Ëmfeld (virtualenv) mat Python 3.5 oder méi spéit. An de Beta Notizen kënnt Dir gesinn datt Python 3.6 verfügbar ass, loosst eis kucken ob dat wierklech de Fall ass:

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

Red Hat benotzt aktiv Python als System Toolkit an RHEL, also firwat ass dëst Resultat?

D'Tatsaach ass datt vill Python Entwéckler nach ëmmer den Iwwergank vu Python 2 op Python 2 iwwerdenken, während Python 3 selwer ënner aktiv Entwécklung ass, a méi a méi nei Versioune kommen stänneg op. Dofir, fir de Bedierfnes fir stabil System Tools z'erreechen, wärend de Benotzer Zougang zu verschiddenen neie Versioune vu Python ubitt, gouf de System Python an en neie Package geplënnert an huet d'Fäegkeet fir Python 2.7 an 3.6 z'installéieren. Méi Informatioun iwwer d'Ännerungen a firwat se gemaach gi sinn an der Publikatioun an Dem Langdon White säi Blog (Langdon White).

Also, fir de Python ze schaffen, musst Dir nëmmen zwee Packagen installéieren, mat Python3-Pip abegraff als Ofhängegkeet.

sudo yum install python36 python3-virtualenv

Firwat benotzen net direkt Modul rifft wéi Langdon proposéiert an installéiert pip3? Wann Dir déi kommend Automatisatioun am Kapp behalen, ass et bekannt datt Ansible Pip installéiert brauch fir ze lafen, well de Pip Modul net virtualenvs mat engem personaliséierte Pip ausféierbar ënnerstëtzt.

Mat engem funktionnéierende Python3 Dolmetscher zur Verfügung, kënnt Dir mam Django Installatiounsprozess weidergoen an e funktionnéierende System zesumme mat eisen anere Komponenten hunn. Et gi vill Ëmsetzungsoptiounen um Internet verfügbar. Et gëtt eng Versioun hei presentéiert, awer d'Benotzer kënnen hir eege Prozesser benotzen.

Mir installéieren d'PostgreSQL an Nginx Versioune verfügbar am RHEL 8 Standard mat Yum.

sudo yum install nginx postgresql-server

PostgreSQL wäert psycopg2 erfuerderen, awer et muss nëmmen an engem virtualenv Ëmfeld verfügbar sinn, also wäerte mir et mat pip3 zesumme mam Django a Gunicorn installéieren. Awer als éischt musse mir virtualenv opsetzen.

Et gëtt ëmmer vill Debatt iwwer d'Thema fir déi richteg Plaz ze wielen fir Django Projeten z'installéieren, awer wann Dir Zweifel hutt, kënnt Dir ëmmer op de Linux Filesystem Hierarchy Standard wenden. Spezifesch seet de FHS datt / srv benotzt gëtt fir: "Hostspezifesch Donnéeën ze späicheren - Daten déi de System produzéiert, sou wéi Webserverdaten a Skripte, Daten op FTP-Server gespäichert, a Kontrollsystemrepositories." Versiounen (erschéngen am FHS -2.3 am Joer 2004).

Dëst ass genau eise Fall, also setzen mir alles wat mir brauchen an / srv, déi vun eisem Applikatioun Benotzer (Cloud-Benotzer) gehéiert.

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

PostgreSQL an Django opzemaachen ass einfach: eng Datebank erstellen, e Benotzer erstellen, Permissiounen konfiguréieren. Eng Saach fir am Kapp ze halen wann Dir ursprénglech PostgreSQL installéiert ass de postgresql-Setup Skript dee mam postgresql-Server Package installéiert ass. Dëse Skript hëlleft Iech Basis Aufgaben ze maachen verbonne mat der Datebank Cluster Administratioun, wéi Cluster Initialiséierung oder den Upgrade Prozess. Fir eng nei PostgreSQL Instanz op engem RHEL System ze konfiguréieren, musse mir de Kommando ausféieren:

sudo /usr/bin/postgresql-setup -initdb

Dir kënnt dann PostgreSQL mat Systemd starten, eng Datebank erstellen an e Projet am Django opstellen. Denkt drun PostgreSQL nei ze starten nodeems Dir Ännerunge vun der Clientauthentifikatiounskonfiguratiounsdatei gemaach hutt (normalerweis pg_hba.conf) fir Passwuertspäicherung fir den ApplikatiounsBenotzer ze konfiguréieren. Wann Dir aner Schwieregkeeten begéint, gitt sécher d'IPv4- an IPv6-Astellungen an der pg_hba.conf Datei z'änneren.

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

An der Datei /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

An der Datei /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 }}',
   }
}

Nodeems Dir de Settings.py-Datei am Projet konfiguréiert hutt an d'Datebankkonfiguratioun ageriicht hutt, kënnt Dir den Entwécklungsserver starten fir sécher ze stellen datt alles funktionnéiert. Nodeems Dir den Entwécklungsserver gestart huet, ass et eng gutt Iddi en Admin Benotzer ze kreéieren fir d'Verbindung mat der Datebank ze testen.

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

WSGI? Wai?

Den Entwécklungsserver ass nëtzlech fir ze testen, awer fir d'Applikatioun auszeféieren musst Dir de passenden Server a Proxy fir de Web Server Gateway Interface (WSGI) konfiguréieren. Et gi verschidde gemeinsam Kombinatioune, zum Beispill Apache HTTPD mat uWSGI oder Nginx mat Gunicorn.

D'Aarbecht vum Web Server Gateway Interface ass fir Ufroe vum Webserver an de Python Web Kader weiderzebréngen. WSGI ass e Reliquie vun der schrecklecher Vergaangenheet wann CGI Motore ronderëm waren, an haut ass WSGI den de facto Standard, onofhängeg vum Webserver oder Python Kader benotzt. Awer trotz senger verbreeter Benotzung sinn et nach ëmmer vill Nuancen wann Dir mat dëse Kaderen schafft, a vill Choixen. An dësem Fall wäerte mir probéieren d'Interaktioun tëscht Gunicorn an Nginx iwwer e Socket ze etabléieren.

Well béid vun dëse Komponenten um selwechten Server installéiert sinn, loosst eis probéieren en UNIX Socket ze benotzen amplaz vun engem Netzwierk Socket. Well d'Kommunikatioun op jidde Fall e Socket erfuerdert, loosst eis probéieren nach ee Schrëtt ze huelen an d'Socketaktivéierung fir Gunicorn iwwer Systemd ze konfiguréieren.

De Prozess fir Socket aktivéiert Servicer ze kreéieren ass ganz einfach. Als éischt gëtt eng Eenheetsdatei erstallt déi eng ListenStream-Direktiv enthält, déi op de Punkt weist op deem den UNIX-Socket erstallt gëtt, dann eng Eenheetsdatei fir de Service an deem d'Requires-Direktiv op d'Socket-Eenheetsdatei weist. Dann, an der Service Eenheetsdatei, alles wat bleift ass Gunicorn aus dem virtuellen Ëmfeld ze ruffen an eng WSGI Bindung fir den UNIX Socket an d'Django Applikatioun ze kreéieren.

Hei sinn e puer Beispiller vun Eenheetsdateien déi Dir als Basis benotze kënnt. Als éischt setzen mir de Socket op.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Elo musst Dir den Gunicorn Daemon konfiguréieren.

[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

Fir Nginx ass et eng einfach Saach fir Proxy Konfiguratiounsdateien ze kreéieren an e Verzeechnes opzestellen fir statesch Inhalt ze späicheren wann Dir een benotzt. An RHEL sinn Nginx Konfiguratiounsdateien an /etc/nginx/conf.d. Dir kënnt dat folgend Beispill an d'Datei /etc/nginx/conf.d/default.conf kopéieren an de Service starten. Vergewëssert Iech datt de Server_name mat Ärem Hostnumm passt.

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 an Nginx mat Systemd an Dir sidd prett fir ze testen.

Schlecht Gateway Feeler?

Wann Dir d'Adress an Äre Browser gitt, kritt Dir héchstwahrscheinlech e 502 Bad Gateway Feeler. Et kann duerch falsch konfiguréiert UNIX Socket Permissiounen verursaacht ginn, oder et kann duerch méi komplex Themen am Zesummenhang mat der Zougangskontroll an SELinux sinn.

Am nginx Feeler Log kënnt Dir eng Zeil wéi dës gesinn:

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"

Wa mir Gunicorn direkt testen, kréie mir eng eidel Äntwert.

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

Loosst eis erausfannen firwat dat geschitt. Wann Dir de Log opmaacht, gesitt Dir wahrscheinlech datt de Problem mat SELinux verbonnen ass. Well mir en Daemon lafen fir deen keng Politik erstallt gouf, gëtt et als init_t markéiert. Loosst eis dës Theorie an der Praxis testen.

sudo setenforce 0

All dëst kann Kritik a Blutt Tréinen verursaachen, awer dëst ass just de Prototyp Debugging. Loosst eis de Scheck deaktivéieren just fir sécher ze sinn datt dëst de Problem ass, duerno wäerte mir alles zréck op seng Plaz zréckginn.

Andeems Dir d'Säit am Browser erfrëscht oder eise Curl Kommando nei leeft, kënnt Dir d'Django Test Säit gesinn.

Also, nodeems mir séchergestallt hunn datt alles funktionnéiert an et keng Erlaabnisprobleemer méi sinn, aktivéiere mir SELinux erëm.

sudo setenforce 1

Ech wäert net iwwer Audit2allow schwätzen oder Alarm-baséiert Politiken mat Sepolgen hei erstellen, well et am Moment keng aktuell Django Applikatioun gëtt, also gëtt et keng komplett Kaart vu wat de Gunicorn wëllt zougräifen a wat et den Zougang zu sollt refuséieren. Dofir ass et néideg SELinux ze halen fir de System ze schützen, a gläichzäiteg erlaabt d'Applikatioun ze lafen an d'Botschaften am Auditprotokoll ze verloossen, sou datt déi aktuell Politik dann aus hinnen erstallt ka ginn.

Permissive Domains spezifizéieren

Net jiddereen huet vun erlaabten Domainen an SELinux héieren, awer si sinn näischt Neies. Vill hu souguer mat hinnen geschafft ouni et iwwerhaapt ze realiséieren. Wann eng Politik op Basis vun Auditmeldungen erstallt gëtt, representéiert déi erstallt Politik dat geléist Domain. Loosst eis probéieren eng einfach Erlaabnespolitik ze kreéieren.

Fir eng spezifesch erlaabt Domain fir Gunicorn schafen, Dir braucht eng Zort Politik, an Dir musst och déi entspriechend Fichieren markéieren. Zousätzlech si Tools gebraucht fir nei Politiken ze sammelen.

sudo yum install selinux-policy-devel

Den erlaabten Domainmechanismus ass e super Tool fir Probleemer z'identifizéieren, besonnesch wann et ëm eng personaliséiert Applikatioun kënnt oder Uwendungen déi verschéckt ginn ouni scho geschafe Politik. An dësem Fall wäert déi erlaabt Domain Politik fir Gunicorn sou einfach wéi méiglech sinn - en Haaptart deklaréieren (gunicorn_t), deklaréieren en Typ dee mir benotze fir méi ausführbar ze markéieren (gunicorn_exec_t), an dann en Iwwergang fir de System opzestellen fir richteg ze markéieren Lafen Prozesser. Déi lescht Zeil setzt d'Politik als Standard aktivéiert zur Zäit wou se gelueden ass.

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;

Dir kënnt dës Politikdatei kompiléieren an op Äre System derbäisetzen.

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

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

Loosst eis kucken ob SELinux eppes anescht blockéiert wéi dat wat eisen onbekannte Daemon zougitt.

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 verhënnert datt Nginx Daten op d'UNIX Socket schreift, déi vum Gunicorn benotzt gëtt. Typesch, an esou Fäll fänken d'Politik un ze änneren, awer et ginn aner Erausfuerderunge vir. Dir kënnt och d'Domain-Astellunge vun engem Restriktiounsdomän op eng Erlaabnes-Domain änneren. Loosst eis elo httpd_t op d'Permissiounen Domain plënneren. Dëst gëtt Nginx den néidegen Zougang a mir kënne weider mat weiderer Debugging Aarbecht weiderféieren.

sudo semanage permissive -a httpd_t

Also, wann Dir et fäerdeg bruecht hutt SELinux geschützt ze halen (Dir sollt wierklech net e SELinux Projet am limitéierten Modus verloossen) an d'Erlaabnes Domaine gelueden sinn, musst Dir erausfannen wat genau muss markéiert ginn als gunicorn_exec_t fir alles richteg ze funktionéieren erëm. Loosst eis probéieren d'Websäit ze besichen fir nei Messagen iwwer Zougangsbeschränkungen ze gesinn.

sudo ausearch -m AVC -c gunicorn

Dir gesitt vill Messagen mat 'comm="gunicorn"', déi verschidde Saachen op Dateien an /srv/djangoapp maachen, also ass dëst offensichtlech ee vun de Kommandoen, déi et wäert ze markéieren.

Awer zousätzlech erschéngt e Message wéi dëst:

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

Wann Dir de Status vum Gunicorn Service kuckt oder de ps Kommando ausféiert, gesitt Dir keng lafend Prozesser. Et gesäit aus wéi wann de Gunicorn probéiert de Python Dolmetscher an eisem virtualenv Ëmfeld ze kréien, méiglecherweis fir Aarbechter Scripten auszeféieren. Also loosst eis elo dës zwee ausführbar Dateien markéieren a kucken ob mir eis Django Test Säit kënnen opmaachen.

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

De Gunicorn Service muss nei gestart ginn ier den neien Tag ausgewielt ka ginn. Dir kënnt et direkt nei starten oder de Service stoppen an de Socket starten wann Dir de Site am Browser opmaacht. Vergewëssert Iech datt d'Prozesser déi richteg Etiketten mat ps kritt hunn.

ps -efZ | grep gunicorn

Vergiesst net méi spéit eng normal SELinux Politik ze kreéieren!

Wann Dir elo d'AVC Messagen kuckt, enthält de leschte Message permissive=1 fir alles wat mat der Applikatioun verbonnen ass, an permissive=0 fir de Rescht vum System. Wann Dir verstitt wéi eng Zort Zougang eng real Applikatioun brauch, kënnt Dir séier de beschte Wee fannen fir esou Probleemer ze léisen. Awer bis dohinner ass et am beschten de System sécher ze halen an e kloren, benotzbare Audit vum Django Projet ze kréien.

sudo ausearch -m AVC

Ass geschitt!

E schaffende Django-Projet ass erschéngt mat engem Frontend baséiert op Nginx a Gunicorn WSGI. Mir konfiguréiert Python 3 a PostgreSQL 10 aus de RHEL 8 Beta Repositories. Elo kënnt Dir weidergoen an Django Uwendungen erstellen (oder einfach ofsetzen) oder aner verfügbar Tools an der RHEL 8 Beta entdecken fir de Konfiguratiounsprozess ze automatiséieren, d'Performance ze verbesseren oder souguer dës Konfiguratioun ze containeren.

Source: will.com

Setzt e Commentaire