Սեմինար RHEL 8 Beta. Աշխատանքային վեբ հավելվածների կառուցում

RHEL 8 Beta-ն ծրագրավորողներին առաջարկում է բազմաթիվ նոր հնարավորություններ, որոնց ցուցակագրումը կարող է տևել էջեր, սակայն նոր բաներ սովորելը գործնականում միշտ ավելի լավ է, ուստի ստորև մենք առաջարկում ենք սեմինար Red Hat Enterprise Linux 8 Beta-ի հիման վրա իրականում հավելվածի ենթակառուցվածք ստեղծելու վերաբերյալ:

Սեմինար RHEL 8 Beta. Աշխատանքային վեբ հավելվածների կառուցում

Եկեք որպես հիմք վերցնենք Python-ը՝ ծրագրավորողների շրջանում հայտնի ծրագրավորման լեզու, Django-ի և PostgreSQL-ի համադրությունը, որը բավականին տարածված համակցություն է հավելվածներ ստեղծելու համար, և կարգավորենք RHEL 8 Beta-ն, որպեսզի աշխատի դրանց հետ: Այնուհետև մենք կավելացնենք ևս մի քանի (չդասակարգված) բաղադրիչներ:

Փորձարկման միջավայրը կփոխվի, քանի որ հետաքրքիր է ուսումնասիրել ավտոմատացման, կոնտեյներների հետ աշխատելու և բազմաթիվ սերվերներով միջավայրեր փորձելու հնարավորությունները: Նոր նախագիծ սկսելու համար կարող եք սկսել ձեռքով ստեղծելով փոքր, պարզ նախատիպ, որպեսզի կարողանաք ճշգրիտ տեսնել, թե ինչ պետք է տեղի ունենա և ինչպես է այն փոխազդում, այնուհետև անցնել ավտոմատացմանը և ստեղծել ավելի բարդ կոնֆիգուրացիաներ: Այսօր մենք խոսում ենք նման նախատիպի ստեղծման մասին։

Սկսենք RHEL 8 Beta VM պատկերի տեղակայմամբ: Դուք կարող եք զրոյից տեղադրել վիրտուալ մեքենա կամ օգտագործել KVM հյուրի պատկերը, որը հասանելի է ձեր Beta բաժանորդագրությամբ: Հյուրի պատկեր օգտագործելիս ձեզ հարկավոր է կարգավորել վիրտուալ CD, որը կպարունակի մետատվյալներ և օգտագործողի տվյալներ ամպի սկզբնավորման համար (cloud-init): Ձեզ հարկավոր չէ որևէ հատուկ բան անել սկավառակի կառուցվածքի կամ հասանելի փաթեթների հետ, ցանկացած կոնֆիգուրացիա կկատարվի:

Եկեք ավելի սերտ նայենք ամբողջ գործընթացին:

Ջանգոյի տեղադրում

Django-ի նորագույն տարբերակով ձեզ անհրաժեշտ կլինի վիրտուալ միջավայր (virtualenv) Python 3.5 կամ ավելի նոր տարբերակով: Բետա նշումներում դուք կարող եք տեսնել, որ Python 3.6-ը հասանելի է, եկեք ստուգենք, արդյոք դա իսկապես այդպես է.

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

Red Hat-ը ակտիվորեն օգտագործում է Python-ը որպես համակարգային գործիքակազմ RHEL-ում, ուստի ինչու է դա արդյունք տալիս:

Փաստն այն է, որ Python-ի շատ ծրագրավորողներ դեռ մտածում են Python 2-ից Python 2-ի անցման մասին, մինչդեռ Python 3-ն ինքը գտնվում է ակտիվ մշակման փուլում, և անընդհատ հայտնվում են ավելի ու ավելի նոր տարբերակներ: Հետևաբար, կայուն համակարգի գործիքների կարիքը բավարարելու համար՝ միաժամանակ օգտատերերին Python-ի տարբեր նոր տարբերակների հասանելիություն առաջարկելու համար, Python համակարգը տեղափոխվեց նոր փաթեթ և ապահովեց Python 2.7 և 3.6-ը տեղադրելու հնարավորություն: Փոփոխությունների և դրանց կատարման մասին լրացուցիչ տեղեկություններ կարելի է գտնել հրապարակման մեջ Լենգդոն Ուայթի բլոգը (Լենգդոն Ուայթ):

Այսպիսով, աշխատող Python-ը ստանալու համար անհրաժեշտ է տեղադրել միայն երկու փաթեթ՝ որպես կախվածություն ներառելով python3-pip-ը:

sudo yum install python36 python3-virtualenv

Ինչու՞ չօգտագործել ուղղակի մոդուլի զանգերը, ինչպես առաջարկում է Լենգդոնը և տեղադրել pip3: Նկատի ունենալով գալիք ավտոմատացումը, հայտնի է, որ Ansible-ին կպահանջվի տեղադրել pip-ը գործարկելու համար, քանի որ pip մոդուլը չի ​​աջակցում virtualenv-ներին հատուկ pip գործարկմամբ:

Ձեր տրամադրության տակ գտնվող աշխատանքային python3 թարգմանիչով դուք կարող եք շարունակել Django-ի տեղադրման գործընթացը և ունենալ աշխատանքային համակարգ մեր մյուս բաղադրիչների հետ միասին: Ինտերնետում առկա են բազմաթիվ իրականացման տարբերակներ: Այստեղ ներկայացված է մեկ տարբերակ, բայց օգտվողները կարող են օգտագործել իրենց սեփական գործընթացները:

Մենք կտեղադրենք PostgreSQL և Nginx տարբերակները, որոնք հասանելի են RHEL 8-ում լռելյայն՝ օգտագործելով Yum-ը:

sudo yum install nginx postgresql-server

PostgreSQL-ը կպահանջի psycopg2, բայց այն պետք է հասանելի լինի միայն virtualenv միջավայրում, այնպես որ մենք այն կտեղադրենք pip3-ի միջոցով՝ Django-ի և Gunicorn-ի հետ միասին: Բայց նախ մենք պետք է կարգավորենք virtualenv-ը:

Միշտ շատ բանավեճեր կան Django նախագծերի տեղադրման համար ճիշտ վայր ընտրելու թեմայի շուրջ, բայց կասկածի դեպքում միշտ կարող եք դիմել Linux Filesystem Hierarchy Standard-ին: Մասնավորապես, FHS-ն ասում է, որ /srv-ն օգտագործվում է «հոսթին հատուկ տվյալներ պահելու համար. տվյալներ, որոնք համակարգը արտադրում է, ինչպիսիք են վեբ սերվերի տվյալները և սկրիպտները, FTP սերվերներում պահվող տվյալները և համակարգի պահեստները վերահսկելու համար»: տարբերակները (հայտնվում են FHS-ում: -2.3 2004 թ.)»:

Սա հենց մեր դեպքն է, ուստի մենք դնում ենք այն ամենը, ինչ մեզ անհրաժեշտ է /srv-ում, որը պատկանում է մեր հավելվածի օգտագործողին (cloud-user):

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-ի և Django-ի կարգավորումը հեշտ է՝ ստեղծել տվյալների բազա, ստեղծել օգտվող, կարգավորել թույլտվությունները: PostgreSQL-ն ի սկզբանե տեղադրելիս պետք է հիշել մի բան՝ postgresql-setup script-ը, որը տեղադրված է postgresql-server փաթեթի հետ: Այս սկրիպտը օգնում է ձեզ կատարել տվյալների բազայի կլաստերի կառավարման հետ կապված հիմնական առաջադրանքները, ինչպիսիք են կլաստերի սկզբնավորումը կամ արդիականացման գործընթացը: RHEL համակարգում նոր PostgreSQL օրինակը կարգավորելու համար մենք պետք է գործարկենք հրամանը.

sudo /usr/bin/postgresql-setup -initdb

Այնուհետև կարող եք սկսել PostgreSQL-ը՝ օգտագործելով systemd-ը, ստեղծել տվյալների բազա և ստեղծել նախագիծ Django-ում: Հիշեք, որ վերագործարկեք PostgreSQL-ը հաճախորդի նույնականացման կազմաձևման ֆայլում (սովորաբար pg_hba.conf) փոփոխություններ կատարելուց հետո՝ հավելվածի օգտագործողի համար գաղտնաբառի պահպանումը կարգավորելու համար: Եթե ​​այլ դժվարությունների հանդիպեք, համոզվեք, որ փոխեք IPv4 և IPv6 կարգավորումները pg_hba.conf ֆայլում:

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

Ֆայլում /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

Ֆայլում /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 }}',
   }
}

Նախագծում settings.py ֆայլը կարգավորելուց և տվյալների բազայի կազմաձևումը կարգավորելուց հետո կարող եք գործարկել զարգացման սերվերը՝ համոզվելու համար, որ ամեն ինչ աշխատում է: Զարգացման սերվերը գործարկելուց հետո լավ գաղափար է ստեղծել ադմինիստրատորի օգտատեր՝ տվյալների բազայի հետ կապը փորձարկելու համար:

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

WSGI? Վայ

Մշակման սերվերը օգտակար է թեստավորման համար, սակայն հավելվածը գործարկելու համար դուք պետք է կարգավորեք համապատասխան սերվերը և վստահված անձը Web Server Gateway Interface-ի (WSGI) համար: Կան մի քանի ընդհանուր համակցություններ, օրինակ՝ Apache HTTPD uWSGI-ով կամ Nginx՝ Gunicorn-ի հետ։

Web Server Gateway Interface-ի գործն է վեբ սերվերից հարցումները Python վեբ շրջանակին փոխանցել: WSGI-ն սարսափելի անցյալի մասունք է, երբ CGI շարժիչները կային, և այսօր WSGI-ն դե ֆակտո ստանդարտն է՝ անկախ օգտագործվող վեբ սերվերից կամ Python շրջանակից: Բայց չնայած դրա լայն կիրառմանը, այս շրջանակների հետ աշխատելիս դեռ կան բազմաթիվ նրբերանգներ և շատ ընտրություններ: Այս դեպքում մենք կփորձենք փոխազդեցություն հաստատել Gunicorn-ի և Nginx-ի միջև վարդակից:

Քանի որ այս երկու բաղադրիչներն էլ տեղադրված են միևնույն սերվերում, եկեք փորձենք օգտագործել UNIX վարդակից ցանցային վարդակից: Քանի որ հաղորդակցությունը ցանկացած դեպքում պահանջում է վարդակ, եկեք փորձենք ևս մեկ քայլ անել և կարգավորել վարդակների ակտիվացումը Gunicorn-ի համար systemd-ի միջոցով:

Socket-ի ակտիվացված ծառայությունների ստեղծման գործընթացը բավականին պարզ է: Նախ, ստեղծվում է միավոր ֆայլ, որը պարունակում է ListenStream դիրեկտիվ, որը ցույց է տալիս այն կետը, որտեղ ստեղծվելու է UNIX վարդակը, այնուհետև մի միավոր ֆայլ ծառայության համար, որտեղ Requires դիրեկտիվը ցույց կտա վարդակից միավորի ֆայլը: Այնուհետև սպասարկման միավորի ֆայլում մնում է միայն զանգահարել Gunicorn-ին վիրտուալ միջավայրից և ստեղծել WSGI կապող UNIX վարդակից և Django հավելվածի համար:

Ահա միավոր ֆայլերի օրինակներ, որոնք կարող եք օգտագործել որպես հիմք: Սկզբում մենք տեղադրեցինք վարդակը:

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Այժմ դուք պետք է կարգավորեք Gunicorn deemon-ը:

[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

Nginx-ի համար պրոքսի կոնֆիգուրացիայի ֆայլեր ստեղծելը և ստատիկ բովանդակությունը պահելու համար գրացուցակ ստեղծելը պարզ խնդիր է, եթե դուք օգտագործում եք այդպիսին: RHEL-ում Nginx կազմաձևման ֆայլերը գտնվում են /etc/nginx/conf.d-ում: Կարող եք պատճենել հետևյալ օրինակը /etc/nginx/conf.d/default.conf ֆայլում և սկսել ծառայությունը: Համոզվեք, որ սահմանել եք server_name-ը, որպեսզի համապատասխանի ձեր հյուրընկալողի անվանը:

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;
   }
}

Գործարկեք Gunicorn վարդակից և Nginx-ը՝ օգտագործելով systemd-ը, և դուք պատրաստ եք սկսել փորձարկումը:

Վատ Gateway-ի սխալ:

Եթե ​​հասցեն մուտքագրեք ձեր բրաուզերում, ամենայն հավանականությամբ կստանաք 502 Bad Gateway սխալ: Դա կարող է առաջանալ UNIX վարդակների սխալ կազմաձևված թույլտվությունների պատճառով կամ կարող է պայմանավորված լինել SELinux-ում մուտքի վերահսկման հետ կապված ավելի բարդ խնդիրների պատճառով:

Nginx-ի սխալների մատյանում կարող եք տեսնել այսպիսի տող.

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"

Եթե ​​ուղղակիորեն փորձարկենք Gunicorn-ը, ապա դատարկ պատասխան կստանանք։

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

Եկեք պարզենք, թե ինչու է դա տեղի ունենում: Եթե ​​բացեք գրանցամատյանը, ամենայն հավանականությամբ կտեսնեք, որ խնդիրը կապված է SELinux-ի հետ։ Քանի որ մենք գործարկում ենք deemon, որի համար քաղաքականություն չի ստեղծվել, այն նշվում է որպես init_t: Եկեք փորձարկենք այս տեսությունը գործնականում:

sudo setenforce 0

Այս ամենը կարող է քննադատության և արյան արցունքների պատճառ դառնալ, բայց սա պարզապես նախատիպի վրիպազերծում է: Եկեք անջատենք չեկը միայն համոզվելու համար, որ դա է խնդիրը, որից հետո ամեն ինչ կվերադարձնենք իր տեղը։

Թարմացնելով էջը բրաուզերում կամ նորից գործարկելով մեր curl հրամանը, կարող եք տեսնել Django-ի թեստային էջը:

Այսպիսով, համոզվելով, որ ամեն ինչ աշխատում է և այլևս թույլտվության հետ կապված խնդիրներ չկան, մենք նորից միացնում ենք SELinux-ը:

sudo setenforce 1

Այստեղ ես չեմ խոսի audit2allow-ի կամ sepolgen-ի հետ նախազգուշացման վրա հիմնված քաղաքականությունների ստեղծման մասին, քանի որ այս պահին իրական Django հավելված չկա, հետևաբար չկա ամբողջական քարտեզ, թե ինչ կարող է Gunicorn-ը ցանկանալ մուտք գործել և ինչին նա պետք է մերժի մուտքը: Հետևաբար, անհրաժեշտ է պահպանել SELinux-ը` համակարգը պաշտպանելու համար, միևնույն ժամանակ թույլ տալով հավելվածին գործարկել և հաղորդագրություններ թողնել աուդիտի մատյանում, որպեսզի այնուհետև դրանցից ստեղծվի իրական քաղաքականությունը:

Թույլատրելի տիրույթների նշում

Ոչ բոլորն են լսել SELinux-ում թույլատրված տիրույթների մասին, բայց դրանք նորություն չեն: Շատերը նույնիսկ աշխատել են նրանց հետ՝ նույնիսկ չգիտակցելով դա։ Երբ քաղաքականությունը ստեղծվում է աուդիտի հաղորդագրությունների հիման վրա, ստեղծված քաղաքականությունը ներկայացնում է լուծված տիրույթը: Փորձենք ստեղծել թույլտվության պարզ քաղաքականություն։

Gunicorn-ի համար կոնկրետ թույլատրված տիրույթ ստեղծելու համար ձեզ անհրաժեշտ է որոշակի քաղաքականություն, ինչպես նաև պետք է նշել համապատասխան ֆայլերը: Բացի այդ, գործիքներ են անհրաժեշտ նոր քաղաքականություն մշակելու համար:

sudo yum install selinux-policy-devel

Թույլատրված տիրույթների մեխանիզմը հիանալի գործիք է խնդիրների բացահայտման համար, հատկապես, երբ խոսքը վերաբերում է հատուկ հավելվածին կամ հավելվածներին, որոնք առաքվում են առանց արդեն ստեղծված քաղաքականության: Այս դեպքում, Gunicorn-ի համար տիրույթի թույլատրված քաղաքականությունը հնարավորինս պարզ կլինի. հայտարարեք հիմնական տիպ (gunicorn_t), հայտարարեք մի տեսակ, որը մենք կօգտագործենք մի քանի գործարկվողներ նշելու համար (gunicorn_exec_t), այնուհետև ստեղծեք անցում, որպեսզի համակարգը ճիշտ նշի: գործարկման գործընթացները: Վերջին տողը սահմանում է քաղաքականությունը որպես լռելյայն միացված այն բեռնման պահին:

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;

Դուք կարող եք կազմել այս քաղաքականության ֆայլը և ավելացնել այն ձեր համակարգում:

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

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

Եկեք ստուգենք, թե արդյոք SELinux-ը արգելափակում է այլ բան, քան այն, ինչին հասանելի է մեր անհայտ դեյմոնը:

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-ը թույլ չի տալիս Nginx-ին տվյալներ գրել Gunicorn-ի կողմից օգտագործվող UNIX վարդակից: Որպես կանոն, նման դեպքերում քաղաքականությունը սկսում է փոխվել, բայց առջևում կան այլ մարտահրավերներ: Դուք կարող եք նաև փոխել տիրույթի կարգավորումները սահմանափակող տիրույթից դեպի թույլտվության տիրույթ: Այժմ եկեք տեղափոխենք httpd_t դեպի թույլտվությունների տիրույթ: Սա Nginx-ին անհրաժեշտ հասանելիություն կտա, և մենք կարող ենք շարունակել վրիպազերծման հետագա աշխատանքը:

sudo semanage permissive -a httpd_t

Այսպիսով, երբ դուք կարողացաք SELinux-ը պաշտպանված պահել (դուք իսկապես չպետք է թողնեք SELinux նախագիծը սահմանափակ ռեժիմում) և թույլտվության տիրույթները բեռնվեն, դուք պետք է պարզեք, թե կոնկրետ ինչ պետք է նշվի որպես gunicorn_exec_t, որպեսզի ամեն ինչ ճիշտ աշխատի: կրկին. Փորձենք այցելել կայք՝ մուտքի սահմանափակումների մասին նոր հաղորդագրություններ տեսնելու համար:

sudo ausearch -m AVC -c gunicorn

Դուք կտեսնեք «comm="gunicorn» պարունակող բազմաթիվ հաղորդագրություններ, որոնք տարբեր գործողություններ են կատարում /srv/djangoapp-ի ֆայլերի վրա, ուստի սա ակնհայտորեն այն հրամաններից մեկն է, որն արժե նշել:

Բայց բացի այդ, հայտնվում է այսպիսի հաղորդագրություն.

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

Եթե ​​նայեք Gunicorn ծառայության կարգավիճակին կամ գործարկեք ps հրամանը, ապա չեք տեսնի որևէ գործող գործընթաց: Կարծես թե Gunicorn-ը փորձում է մուտք գործել Python թարգմանիչ մեր virtualenv միջավայրում, հնարավոր է աշխատակցական սկրիպտներ գործարկելու համար: Այսպիսով, եկեք նշենք այս երկու գործարկվող ֆայլերը և ստուգենք, թե արդյոք կարող ենք բացել մեր Django թեստային էջը:

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

Gunicorn ծառայությունը պետք է վերագործարկվի՝ նախքան նոր պիտակը ընտրելը: Դուք կարող եք անմիջապես վերագործարկել այն կամ դադարեցնել ծառայությունը և թույլ տալ, որ վարդակը գործարկի այն, երբ բացեք կայքը բրաուզերում: Ստուգեք, որ գործընթացները ստացել են ճիշտ պիտակներ՝ օգտագործելով ps:

ps -efZ | grep gunicorn

Մի մոռացեք ստեղծել նորմալ SELinux քաղաքականություն ավելի ուշ:

Եթե ​​հիմա նայեք ՀՎՀ հաղորդագրություններին, ապա վերջին հաղորդագրությունը պարունակում է permissive=1 հավելվածի հետ կապված ամեն ինչի համար, և permissive=0 համակարգի մնացած մասի համար: Եթե ​​հասկանում եք, թե իրական հավելվածին ինչպիսի մուտք է պետք, կարող եք արագ գտնել նման խնդիրների լուծման լավագույն միջոցը: Բայց մինչ այդ ավելի լավ է համակարգը անվտանգ պահել և ստանալ Django նախագծի հստակ, օգտագործելի աուդիտ:

sudo ausearch -m AVC

Տեղի է ունեցել!

Աշխատանքային Django նախագիծ է հայտնվել Nginx-ի և Gunicorn WSGI-ի վրա հիմնված ճակատով: Մենք կազմաձևեցինք Python 3-ը և PostgreSQL 10-ը RHEL 8 բետա պահոցներից: Այժմ դուք կարող եք առաջ շարժվել և ստեղծել (կամ պարզապես տեղակայել) Django հավելվածներ կամ ուսումնասիրել այլ հասանելի գործիքներ RHEL 8 բետա-ում՝ ավտոմատացնելու կազմաձևման գործընթացը, բարելավելու կատարողականը կամ նույնիսկ կոնֆիգուրացիան:

Source: www.habr.com

Добавить комментарий