В этом руководстве вы узнаете, как легко развернуть AWX в кластере Kubernetes с помощью оператора AWX.
AWX — это проект Ansible с открытым исходным кодом, который предоставляет веб-интерфейс пользователя, REST API и движок задач для Ansible. Он служит в качестве исходного проекта, на основе которого в конечном итоге создается компонент контроллера автоматизации (ранее Ansible Tower) в Red Hat Ansible Automation Platform. Оператор AWX — это оператор Kubernetes, который обеспечивает эффективный способ установки, настройки и управления жизненным циклом AWX в кластере Kubernetes или среде OpenShift.
Развертывание AWX в кластере Kubernetes с помощью оператора AWX
Использование AWX Operator является рекомендуемым в настоящее время способом развертывания AWX в кластере Kubernetes или в кластере OpenShift.
Чтобы развернуть AWX в кластере Kubernetes с помощью оператора AWX, выполните следующие действия.
Настройка кластера Kubernetes
Прежде чем продолжить, необходимо установить и запустить кластер Kubernetes (или OpenShift, если вы его используете).
В наших предыдущих руководствах мы подробно рассмотрели, как развернуть кластер Kubernetes с одним или несколькими мастерами.
Перейдите по ссылкам ниже для получения более подробной информации;
Установка и настройка кластера Kubernetes в RHEL 9
В нашем текущем кластере мы запускаем два мастера и 3 рабочих узла kubeadm кластер Kubernetes.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8rhelsmas1.kalyuzhnyy.ru Ready control-plane 14h v1.32.1
k8rhelsmas2.kalyuzhnyy.ru Ready control-plane 14h v1.32.1
k8rhelswor1.kalyuzhnyy.ru Ready <none> 13h v1.32.1
k8rhelswor2.kalyuzhnyy.ru Ready <none> 13h v1.32.1
k8rhelswor3.kalyuzhnyy.ru Ready <none> 13h v1.32.1
Развертывание оператора AWX в кластере Kubernetes
После того как кластер Kubernetes будет запущен и заработает, перейдите к развертыванию оператора AWX.
Создание пространства имен оператора AWX
По умолчанию пространство имен для оператора AWX обычно awx. Поскольку мы используем другой подход к установке - настройку SC, PV и PVC перед развертыванием оператора AWX, давайте начнем с создания пространства имен.
kubectl create namespace awx
Обеспечение сохранности данных базы данных с помощью постоянных томов
Как уже известно, AWX использует PostgreSQL в качестве серверной части базы данных для хранения данных, включая информацию о выполненных заданиях, инвентаризации, пользователях, учетных данных и других конфигурациях, связанных с AWX. Развертывание AWX включает в себя встроенную базу данных PostgreSQL, которая развертывается вместе с AWX.
Таким образом, если вы хотите убедиться, что данные базы данных сохраняются после перезапуска модулей pod, вы можете использовать постоянные тома (PV) для хранения данных базы данных.
Для целей этой демонстрации мы будем использовать локальное хранилище файловой системы для подготовки PV базы данных AWX. По сути, чтобы создать фотоэлектрическую линию:
- Определение серверной части системы хранения данных с помощью StorageClass
- Создайте виртуальную версию, определяющую фактическое дисковое пространство, которое Kubernetes может использовать для сохранения данных для приложения.
- Создайте PVC для запроса определенного размера хранилища от PV (если доступен PV, соответствующий потребностям запроса) или непосредственно из StorageClass, если нет подходящего PV.
1. Создание StorageClass
Существуют различные средства выделения ресурсов хранилища, которые можно настроить для использования оператором AWX. Однако, поскольку мы являемся локальным локальным кластером Kubernetes, мы будем использовать локальный инициализатор StorageClass. Однако Local StorageClass provisioner обычно не рекомендуется для производственных сред!
Таким образом, создайте манифест для определения локального инициализатора класса хранения.
vim ~/local-storageclass.yaml
Вставьте содержимое ниже. Вы можете изменить имя метаданных по своему усмотрению.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
Создайте класс StorageClass;
kubectl apply -f ~/local-storageclass.yaml
Вы можете проверить состояние класса StorageClass;
kubectl get sc -n awx
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-storage kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 7s
2. Создание постоянного тома хранилища оператора AWX
Теперь, когда вы определили тип используемого класса StorageClass, перейдите к определению фактического ресурса хранилища для AWX в Kubernetes (Persistent Volume, PV).
В нашем кластере есть три рабочих узла:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8rhelsmas1.kalyuzhnyy.ru Ready control-plane 14h v1.32.1
k8rhelsmas2.kalyuzhnyy.ru Ready control-plane 14h v1.32.1
k8rhelswor1.kalyuzhnyy.ru Ready <none> 13h v1.32.1
k8rhelswor2.kalyuzhnyy.ru Ready <none> 13h v1.32.1
k8rhelswor3.kalyuzhnyy.ru Ready <none> 13h v1.32.1
и таким образом, мы создадим PV, привязанный к каждому из них, так что любой под, запланированный на каждом узле, будет использовать PV, определенный на том же узле. Это необходимо для того, чтобы гарантировать отсутствие повреждения данных в результате записи нескольких модулей pod на разных узлах в одно и то же хранилище.
vim ~/awx-pv.yaml
Содержимое файла;
apiVersion: v1
kind: PersistentVolume
metadata:
name: awx-pg-wk-01-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/k8s/awx/pg/
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-rhel-node-wk-01
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: awx-pg-wk-02-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/k8s/awx/pg/
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-rhel-node-wk-02
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: awx-pg-wk-03-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/k8s/awx/pg/
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-rhel-node-wk-03
Подводя итог, можно сказать, что этот файл YAML определяет три тома PersistentVolume (PV) для хранения данных AWX PostgreSQL в среде Kubernetes. Каждый том настраивается с учетом следующих ключевых моментов:
- Класс хранения: использует созданный ранее класс local-storage для подготовки локального диска.
- Емкость: 10 Gi на объем.
- Политика возврата: Retain (PV не будет удален после удаления PVC).
- Сходство узлов: Каждый PV ограничен определенным узлом (k8rhelswor1.kalyuzhnyy.ru, k8rhelswor2.kalyuzhnyy.ru и k8rhelswor3.kalyuzhnyy.ru). Путь к локальному хранилищу, /mnt/k8s/awx/pg/, должен уже существовать на узле до того, как будет использован этот PV.
Итак, давайте создадим путь к хранилищу на каждом рабочем узле.
sudo mkdir -p /mnt/k8s/awx/pg
Заметка
Пожалуйста, обратите внимание, что я запускаю свой кластер Kubernetes на серверах RHEL 9 и поэтому я установил SELinux в разрешительный режим, поскольку это всего лишь демонстрационная среда. Если вы используете его в продакшене, найдите способ справиться с контролем безопасности SELinux в отношении доступа подов к локальному хранилищу. $ getenforce Permissive
Примените файл YAML для создания PV.
kubectl apply -f ~/awx-pv.yaml
Подтвердите создание фотоэлектрической системы.
kubectl get pv -n awx
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
awx-pg-wk-01-pv 10Gi RWO Retain Available local-storage <unset> 10s
awx-pg-wk-02-pv 10Gi RWO Retain Available local-storage <unset> 10s
awx-pg-wk-03-pv 10Gi RWO Retain Available local-storage <unset> 10s
3. Создание утверждения постоянного тома для базы данных AWX PostgreSQL
Далее создайте PVC для базы данных AWX PostgreSQL. По умолчанию оператор AWX использует PVC, настроенный следующим образом:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: "2025-01-19T11:19:17Z"
finalizers:
- kubernetes.io/pvc-protection
labels:
app.kubernetes.io/component: database
app.kubernetes.io/instance: postgres-15-awx-demo
app.kubernetes.io/managed-by: awx-operator
app.kubernetes.io/name: postgres-15
name: postgres-15-awx-demo-postgres-15-0
namespace: awx
resourceVersion: "37082"
uid: c07fa7f4-ed6b-4d83-bb6d-b820fd1143d4
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
volumeMode: Filesystem
status:
phase: Pending
Мы просто модифицируем этот PVC, чтобы использовать наши StorageClass и PV. Вот доработанный вариант.
vim ~/awx-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
finalizers:
- kubernetes.io/pvc-protection
labels:
app.kubernetes.io/component: database
app.kubernetes.io/instance: postgres-15-awx-demo
app.kubernetes.io/managed-by: awx-operator
app.kubernetes.io/name: postgres-15
name: postgres-15-awx-demo-postgres-15-0
namespace: awx
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-storage
volumeMode: Filesystem
Создание AWX PostgreSQL PVC;
kubectl apply -f ~/awx-pvc.yaml
Подтверждать;
kubectl get pvc -n awx
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
postgres-15-awx-demo-postgres-15-0 Pending local-storage <unset> 61s
Как видите, он еще не привязан, так как проблем с запросом на хранение еще не было.
Доступ к проектам AWX с уровня хоста
Чтобы иметь возможность получить доступ к каталогу проектов (где хранятся сборники сценариев Ansible и связанные файлы), /var/lib/awx/projects/, с уровня хоста, вам необходимо убедиться, что этот каталог также поддерживается постоянным томом (PV), доступ к которому можно получить из файловой системы хоста. Для этого мы будем использовать общий ресурс NFS.
Настройка постоянного тома для каталога проектов через общий ресурс NFS:
У нас уже есть общий ресурс NFS, /mnt/awx/projects, который мы можем использовать для наших PV-проектов AWX.
Давайте создадим манифест PV и PVC.
vim ~/awx-projects-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: awx-projects-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 2Gi
storageClassName: ""
persistentVolumeReclaimPolicy: Retain
nfs:
path: /mnt/awx/projects
server: 192.168.233.181
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: awx-projects-pvc
namespace: awx
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
Примените манифест для создания проектов PV и PVC для AWX.
kubectl apply -f ~/awx-projects-pv.yaml
Итак, теперь у нас есть PV для баз данных и проектов;
kubectl get pv,pvc -n awx
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/awx-pg-wk-01-pv 10Gi RWO Retain Available local-storage <unset> 7m22s
persistentvolume/awx-pg-wk-02-pv 10Gi RWO Retain Available local-storage <unset> 7m22s
persistentvolume/awx-pg-wk-03-pv 10Gi RWO Retain Available local-storage <unset> 7m22s
persistentvolume/awx-projects-pv 2Gi RWX Retain Bound awx/awx-projects-pvc <unset> 4m19s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/awx-projects-pvc Bound awx-projects-pv 2Gi RWX <unset> 2m49s
persistentvolumeclaim/postgres-15-awx-demo-postgres-15-0 Pending local-storage <unset> 5m9s
Развертывание оператора AWX
Хотя вы можете клонировать репозиторий оператора AWX и развернуть его в каталоге репозитория в вашей системе, вы также можете использовать файл Kustomization. Поскольку нам нужно кустомизировать наше развертывание с помощью пользовательского класса StorageClass, а также PV/PVC, созданного выше, мы будем использовать путь кустомизации.
Вот пример содержимого файла Kustomization;
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
# Find the latest tag here: https://github.com/ansible/awx-operator/releases
- github.com/ansible/awx-operator/config/default?ref=<tag>
# Set the image tags to match the git version from above
images:
- name: quay.io/ansible/awx-operator
newTag: <tag>
# Specify a custom namespace in which to install AWX
namespace: awx
Таким образом, давайте создадим директорию, из которой будет создаваться наш файл kustomization оператора AWX.
mkdir awx
Перейдите в каталог и создайте файл Kustomization.
cd awx
vim kustomization.yaml
Вставьте содержимое ниже;
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: awx
resources:
# Find the latest tag here: https://github.com/ansible/awx-operator/releases
- github.com/ansible/awx-operator/config/default?ref=2.19.1
# Set the image tags to match the git version from above
images:
- name: quay.io/ansible/awx-operator
newTag: 2.19.1
Поэтому сначала разверните оператор AWX, чтобы создать необходимые CRD и другие конфигурации, прежде чем вы сможете его настроить.
kubectl apply -k .
В высокоуровневом резюме происходит следующее:
- Настройка пространства имен AWX для организации ресурсов.
- Определение пользовательских ресурсов (CRD) для компонентов, связанных с AWX, таких как резервное копирование, восстановление и вход сетки.
- Настройка сервисных учетных записей и ролей RBAC для управления разрешениями для оператора AWX.
- Настройка служб и развертываний для запуска оператором AWX и предоставления необходимых метрик.
Далее мы можем настроить развертывание AWX, чтобы определить постоянное хранилище для базы данных и проектов. Ниже приведен наш манифест для внесения этих обновлений.
cat awx-configs.yaml
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx-demo
namespace: awx
spec:
service_type: nodeport
nodeport_port: 31500 # You can remove to assign random port or change to you preference
ingress_type: none
projects_persistence: true
projects_existing_claim: awx-projects-pvc
postgres_init_container_resource_requirements: {}
postgres_data_volume_init: true
postgres_storage_class: local-storage
postgres_init_container_commands: |
chown -R 26:26 /var/lib/pgsql/data
Обновите файл соответствующим образом.
Обратитесь к документации для получения более подробной информации об используемых параметрах конфигурации.
Чтобы применить эти изменения, отредактируйте файл кустомизации и включите этот манифест в раздел ресурсов.
cat kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: awx
resources:
# Find the latest tag here: https://github.com/ansible/awx-operator/releases
- github.com/ansible/awx-operator/config/default?ref=2.19.1
- awx-configs.yaml
# Set the image tags to match the git version from above
images:
- name: quay.io/ansible/awx-operator
newTag: 2.19.1
Примените Kustomization для развертывания оператора AWX в кластере Kubernetes.
kubectl apply -k .
Потребуется некоторое время, прежде чем оператор AWX и другие службы будут запущены.
Вы можете перечислить все ресурсы, созданные в пространстве имен awx, следующим образом:
kubectl get all -n awx
Вы также можете наблюдать за капсулами;
kubectl get pods -n awx -w
NAME READY STATUS RESTARTS AGE
awx-demo-postgres-15-0 1/1 Running 0 117s
awx-demo-task-5d5b6b856b-lhdlx 0/4 Init:0/3 0 91s
awx-demo-web-7cb57bb787-h846q 3/3 Running 0 92s
awx-operator-controller-manager-58b7c97f4b-9h8fm 2/2 Running 0 3m22s
Или наблюдать за событиями;
kubectl get events -n awx -w
Пройдет некоторое время, прежде чем AWX будет готов.
Примеры событий:
0s Normal Scheduled pod/awx-demo-migration-24.6.1-kdtmp Successfully assigned awx/awx-demo-migration-24.6.1-kdtmp to k8s-rhel-node-wk-02
0s Normal Pulled pod/awx-demo-migration-24.6.1-kdtmp Container image "quay.io/ansible/awx:24.6.1" already present on machine
0s Normal Created pod/awx-demo-migration-24.6.1-kdtmp Created container: migration-job
0s Normal Started pod/awx-demo-migration-24.6.1-kdtmp Started container migration-job
0s Normal Completed job/awx-demo-migration-24.6.1 Job completed
0s Normal Pulling pod/awx-demo-task-77ddf68cf8-snzkh Pulling image "quay.io/ansible/awx-ee:24.6.1"
0s Normal Pulled pod/awx-demo-task-77ddf68cf8-snzkh Successfully pulled image "quay.io/ansible/awx-ee:24.6.1" in 26.016s (26.016s including waiting). Image size: 468650745 bytes.
0s Normal Created pod/awx-demo-task-77ddf68cf8-snzkh Created container: init-receptor
0s Normal Started pod/awx-demo-task-77ddf68cf8-snzkh Started container init-receptor
0s Normal Pulling pod/awx-demo-task-77ddf68cf8-snzkh Pulling image "quay.io/centos/centos:stream9"
0s Normal Pulled pod/awx-demo-task-77ddf68cf8-snzkh Successfully pulled image "quay.io/centos/centos:stream9" in 1.897s (1.897s including waiting). Image size: 60726367 bytes.
0s Normal Created pod/awx-demo-task-77ddf68cf8-snzkh Created container: init-projects
0s Normal Started pod/awx-demo-task-77ddf68cf8-snzkh Started container init-projects
0s Normal Pulling pod/awx-demo-task-77ddf68cf8-snzkh Pulling image "docker.io/redis:7"
0s Normal Pulled pod/awx-demo-task-77ddf68cf8-snzkh Successfully pulled image "docker.io/redis:7" in 4.069s (4.069s including waiting). Image size: 45006722 bytes.
0s Normal Created pod/awx-demo-task-77ddf68cf8-snzkh Created container: redis
0s Normal Started pod/awx-demo-task-77ddf68cf8-snzkh Started container redis
0s Normal Pulled pod/awx-demo-task-77ddf68cf8-snzkh Container image "quay.io/ansible/awx:24.6.1" already present on machine
0s Normal Created pod/awx-demo-task-77ddf68cf8-snzkh Created container: awx-demo-task
0s Normal Started pod/awx-demo-task-77ddf68cf8-snzkh Started container awx-demo-task
0s Normal Pulled pod/awx-demo-task-77ddf68cf8-snzkh Container image "quay.io/ansible/awx-ee:24.6.1" already present on machine
0s Normal Created pod/awx-demo-task-77ddf68cf8-snzkh Created container: awx-demo-ee
0s Normal Started pod/awx-demo-task-77ddf68cf8-snzkh Started container awx-demo-ee
0s Normal Pulled pod/awx-demo-task-77ddf68cf8-snzkh Container image "quay.io/ansible/awx:24.6.1" already present on machine
0s Normal Created pod/awx-demo-task-77ddf68cf8-snzkh Created container: awx-demo-rsyslog
0s Normal Started pod/awx-demo-task-77ddf68cf8-snzkh Started container awx-demo-rsyslog
Поды в конце настройки;
kubectl get pod -n awx
NAME READY STATUS RESTARTS AGE
awx-demo-migration-24.6.1-kdtmp 0/1 Completed 0 7m25s
awx-demo-postgres-15-0 1/1 Running 0 27m
awx-demo-task-77ddf68cf8-snzkh 4/4 Running 0 24m
awx-demo-web-6f644fc5-vkg7v 3/3 Running 5 (9m18s ago) 24m
awx-operator-controller-manager-58b7c97f4b-ggfjq 2/2 Running 2 (25m ago) 30m
Доступ к веб-интерфейсу AWX
К настоящему времени AWX должен быть установлен и запущен в кластере Kubernetes.
Если вы проверите доступные услуги;
kubectl get svc -n awx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
awx-demo-postgres-15 ClusterIP None <none> 5432/TCP 47m
awx-demo-service NodePort 10.111.226.182 <none> 80:31500/TCP 44m
awx-operator-controller-manager-metrics-service ClusterIP 10.110.9.218 <none> 8443/TCP 51m
Если вы видите, веб-сервис доступен через NodePort через порт 31500/tcp. Это порт, который был определен в файле конфигурации awx-configs.yaml выше. У вас должна быть возможность доступа к сервису AWX через IP-адрес любого узла в кластере.
Помните, что мы запускаем наш кластер Kubernetes на узлах RHEL 9. И так как мы используем Calico CNI, мы отключили брандмауэр. Следовательно, услуга должна быть легко доступна.
Доступ к веб-интерфейсу AWX осуществляется через http://<any-cluster-node-IP>:NodePort. Например, http://192.168.233.181:31500.
По умолчанию для веб-сайта AWX используется имя пользователя: admin. Вы можете извлечь пароль для входа из секрета пароля администратора AWX.
kubectl get secrets -n awx
NAME TYPE DATA AGE
awx-demo-admin-password Opaque 1 17h
awx-demo-app-credentials Opaque 3 17h
awx-demo-broadcast-websocket Opaque 1 17h
awx-demo-postgres-configuration Opaque 6 17h
awx-demo-receptor-ca kubernetes.io/tls 2 17h
awx-demo-receptor-work-signing Opaque 2 17h
awx-demo-secret-key Opaque 1 17h
redhat-operators-pull-secret Opaque 1 17h
Проверьте поля с паролем;
kubectl get secret awx-demo-admin-password -n awx -o json
{
"apiVersion": "v1",
"data": {
"password": "U29lV1h5cndNNEtuTWcyWHZuS1RjWm5hSXZtZHhpZUk="
},
"kind": "Secret",
"metadata": {
"annotations": {
...
Секретные значения обычно закодированы в Base64. Извлечение и декодирование.
kubectl get secret awx-demo-admin-password -n awx -o jsonpath='{.data.password}' | base64 --decode; echo
Полученная строка должна быть вашим паролем. Используйте его для входа в веб-сайт AWX в качестве администратора пользователя.
Вот и все!
Убедитесь, что данные БД доступны на рабочих узлах Filesystem. Вы можете сделать это, проверив, где в данный момент запущен Pod, и проверив наличие данных в пути к файловой системе соответствующего узла
kubectl get pod awx-demo-postgres-15-0 -o wide -n awx
Для проектов AWX вы можете проверить, перейдя на веб-панель управления AWX и перейдя к проектам. Вы увидите демо-проект. Нажмите кнопку «Синхронизировать», чтобы синхронизировать данные проекта с базовым хранилищем. В данном случае наш путь к хранилищу — это общий ресурс NFS.
После этого вы сможете увидеть файлы проекта;
ll /mnt/awx/projects/
Таким образом, вы также можете начать создавать свои проекты здесь и синхронизировать их с AWX.
На этом мы заканчиваем это руководство по развертыванию AWX в кластере Kubernetes с помощью оператора AWX.
Если у вас есть какие-либо мысли или вопросы по этой теме, пожалуйста, не стесняйтесь оставить комментарий или отправить мне сообщение. Я хотел бы продолжить дискуссию и услышать вашу точку зрения.
А также вы всегда можете поддержать меня зайдя на сайт и подписаться https://dzen.ru/kalyuzhnyy.ru и найти больше статей на моих ресурсах https://kalyuzhnyy.ru и https://dev.kalyuzhnyy.ru