Czym jest awx-operator
awx-operator to operator Kubernetes stworzony, aby ułatwić wdrażanie, konfigurację i zarządzanie instancjami AWX na klastrach Kubernetes. AWX jest otwartoźródłową wersją Ansible Tower, narzędziem zaprojektowanym do zarządzania i uruchamiania zadań w Ansible w skali korporacyjnej.
Główne funkcje awx-operator
Automatyczne wdrażanie: Pozwala na łatwe wdrożenie AWX w klastrze Kubernetes za pomocą jednego polecenia.
Konfiguracja: Operator umożliwia dostosowywanie różnych aspektów instancji AWX, takich jak ilość zasobów, konfiguracja baz danych czy dostęp do zewnętrznych systemów magazynowania.
Automatyczne aktualizacje: Dzięki operatorowi, aktualizacja AWX może odbywać się płynnie, bez konieczności manualnej interwencji.
Zarządzanie stanem: Operator dba o to, aby instancje AWX działały poprawnie i były w odpowiednim stanie. Monitoruję, czy wszystkie komponenty działają poprawnie i podejmuje odpowiednie działania w przypadku problemów.
Zastosowanie awx-operator
Uproszczenie zarządzania: Dzięki awx-operator, administracja wieloma instancjami AWX w klastrze Kubernetes staje się prostsza.
Integracja z chmurą: Możliwość uruchamiania AWX na platformach typu Kubernetes ułatwia integrację z popularnymi usługami chmurowymi.
Skalowalność: Korzystanie z Kubernetes w połączeniu z awx-operator pozwala na łatwe skalowanie instancji AWX, zarówno w górę, jak i w dół, w zależności od aktualnych potrzeb.
AWX - czym jest
AWX to otwartoźródłowa wersja Ansible Tower. Jest to narzędzie webowe zaprojektowane do zarządzania i uruchamiania zadań w Ansible. Ansible to popularne narzędzie do automatyzacji, służące do konfiguracji systemów, wdrażania aplikacji oraz zarządzania orkiestracją. AWX stanowi "kontrolne centrum dowodzenia" dla Ansible, umożliwiające zarządzanie playbookami, zmiennymi, inwentarzami i dostępnymi hostami w centralny i uporządkowany sposób.
Kluczowe funkcje AWX
Interfejs Użytkownika: AWX oferuje graficzny interfejs użytkownika, który umożliwia łatwe zarządzanie i monitorowanie zadań Ansible.
Rest API: Oprócz interfejsu użytkownika, AWX udostępnia RESTful API, co ułatwia integrację z innymi narzędziami i skryptami.
Planowanie Zadań: AWX pozwala na planowanie uruchamiania playbooków Ansible w określonych terminach lub cyklicznie.
Zarządzanie Inwentarzem: Możesz definiować i grupować hosty, na których będą uruchamiane zadania, oraz synchronizować inwentarze z różnymi źródłami, takimi jak AWS, Google Cloud czy Azure.
Role-Based Access Control (RBAC): AWX umożliwia definiowanie uprawnień na podstawie ról, dzięki czemu można precyzyjnie kontrolować, kto ma dostęp do określonych zasobów i jakie akcje może wykonywać.
Zarządzanie Credentialem: Bezpieczne przechowywanie i zarządzanie danymi uwierzytelniającymi, które są używane podczas uruchamiania playbooków.
Wizualizacje: AWX dostarcza wizualizacje w postaci diagramów i statystyk, umożliwiające szybki wgląd w status operacji oraz historię uruchomień.
Integracja z systemami powiadomień: Możliwość wysyłania powiadomień na różne kanały, takie jak e-mail, Slack czy nawet webhooki, w odpowiedzi na różne zdarzenia w systemie.
Zastosowanie AWX
Centralne zarządzanie konfiguracją: Zarządzaj konfiguracją różnych systemów i aplikacji z jednego centralnego miejsca.
Automatyzacja zadań IT: Automatyzuj rutynowe zadania, takie jak wdrażanie oprogramowania, zarządzanie użytkownikami czy aktualizacje systemowe.
Orkiestracja wielostanowiskowa: Koordynuj działania pomiędzy różnymi środowiskami i platformami.
Integracja z innymi narzędziami DevOps: Dzięki REST API, AWX może być łatwo zintegrowany z narzędziami takimi jak Jenkins, GitLab czy Jira.
Konfiguracja AWX-operatora
Przystąpmy zatem do konfiguracji naszego awx-operatora. Na początek potrzeba nam klastra kubernetes. Możesz wykorzystać dowolny. Jeżeli nie masz klastra kubernetes skonfigurowanego możemy skorzystać z docker-desktop i włączyć funkcje kubernetes na platformie docker.
Docker desktop
Zatem pobieramy naszą platformę docker desktop instalujemy naszą aplikację i po jej uruchomieniu włączmy funkcje kubernetes - jak to zrobić przedstawiają obrazki poniżej.
Przechodzimy do ustawień aplikacji docker desktop, a potem w ustawieniach przechodzimy w sekcje kubernetes i włączamy nas engine k8s
Zaznaczamy funkcje Enable Kubernetes i restartujemy klaster po przez Apply & restart - kubernetes zaczyna się uruchamiać zajmie to kilkadziesiąt sekund.
Po uruchomieniu kubernetesa w docker sprawdzamy działanie naszego polecenia kubectl (jeżeli nie masz tego polecenia to zobacz jak go zainstalować z oficjalnej dokumentacji o instalacji kubectl)
Wydajemy polecenie kubectl version:
$ kubectl version
WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short. Use --output=yaml|json to get the full version.
Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.2", GitCommit:"7f6f68fdabc4df88cfea2dcf9a19b2b830f1e647", GitTreeState:"clean", BuildDate:"2023-05-17T14:20:07Z", GoVersion:"go1.20.4", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v5.0.1
Unable to connect to the server: dial tcp: lookup k3s1.koska.in on 192.168.64.1:53: no such host
I jeżeli tak ja ja masz kilka kontekstów konfiguracyjnych kubernetes to musimy zmienić kontekst pracy z naszym klastrem dla polecenia kubectl - robimy to za pomocą polecania:
kubectl config use-context docker-desktop
Na obrazku powyżej widać aktualna wersję uzywana umnie oraz kontekst na jaki mam ustawione polecenie kubectl - takie wyśietlenie dale shell zsh z nakładka oh my zsh
Oczywiście mozna sprawdzic to poleceniem kubectl config get-contexts
Na koniec sprawdźmy czy działa nasz klaster poleceniem kubectl get nodes -> i powinniśmy otrzymać coś takiego:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 28m v1.27.2
UWAGI: Nie musimy korzystać z docker-desktop i kubernetesa w nim. Możemy skorzystać z platformy minikube, w dokumentacji awx-operator znajdziemy instrukcje jak przygotować klaster minikube.
Plan naszej konfiguracji
Na początku musimy się zastanowić gdzie będziemy trzymać naszą bazę danych postgresql dla naszego AWX - mamy do wyboru w klastrze kubernetes (awx-operator stawia i robi to za nasz przy czystej instalacji) lub na osobnym serwerze - dedykowana instancja (nie zarządzana przez awx-operator).
W przypadku drugiej konfiguracji mamy to opisane w dokumentacji związanej z konfiguracja bazy danych
Ja w tym wpisie pójdę druga drogą, pierwsza po prostu pomija konfiguracja secretu dla bazy danych.
Przygotowanie pliku kustomization.yaml
Na początek musimy przygotować nasz plik kustomization.yaml (możemy bazować na tym dostępnym w instrukcji podstawowej instalacji) plik u mnie przedstawia się następująco:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/ansible/awx-operator/config/default?ref=2.7.0
images:
- name: quay.io/ansible/awx-operator
newTag: 2.7.0
namespace: default
Ten kod przedstawia plik `Kustomization`, który jest częścią narzędzia `kustomize` używanego do dostosowywania zasobów Kubernetes. Pozwala on na tworzenie wielowarstwowych konfiguracji na bazie istniejących zasobów, bez konieczności modyfikowania oryginalnych plików YAML.
Rozbijmy ten konkretny plik `Kustomization` na poszczególne sekcje:
apiVersion i kind:
- `apiVersion`: Określa wersję API, z którą jest zgodny ten plik. W tym przypadku używana jest wersja `v1beta1` związana z `kustomize.config.k8s.io`.
- `kind`: Określa typ obiektu. W tym przypadku jest to `Kustomization`, co wskazuje, że jest to plik konfiguracyjny dla `kustomize`.
resources:
- W tej sekcji określono zasoby, które mają być uwzględnione w procesie dostosowywania.
- W tym przypadku zasób jest pobierany z repozytorium GitHub (`github.com/ansible/awx-operator`). Szczególnie korzysta z konfiguracji znajdującej się w katalogu `config/default` w odniesieniu do tagu `ref=2.7.0`.
images:
- Ta sekcja pozwala na modyfikację obrazów używanych w zasobach. Możliwe jest podmienienie nazwy obrazu, tagu lub repozytorium.
- W tym przypadku obraz `quay.io/ansible/awx-operator` jest modyfikowany tak, aby używać tagu `2.7.0`.
namespace:
- Określa przestrzeń nazw, w której mają zostać wdrożone zasoby. W tym przypadku jest to przestrzeń nazw `default`.
Możemy zatem wydać polecenie kubectl apply -k . które uruchomi nam nasza konfigurację:
$ kubectl apply -k .
Warning: resource namespaces/default is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
namespace/default configured
customresourcedefinition.apiextensions.k8s.io/awxbackups.awx.ansible.com created
customresourcedefinition.apiextensions.k8s.io/awxrestores.awx.ansible.com created
customresourcedefinition.apiextensions.k8s.io/awxs.awx.ansible.com created
serviceaccount/awx-operator-controller-manager created
role.rbac.authorization.k8s.io/awx-operator-awx-manager-role created
role.rbac.authorization.k8s.io/awx-operator-leader-election-role created
clusterrole.rbac.authorization.k8s.io/awx-operator-metrics-reader created
clusterrole.rbac.authorization.k8s.io/awx-operator-proxy-role created
rolebinding.rbac.authorization.k8s.io/awx-operator-awx-manager-rolebinding created
rolebinding.rbac.authorization.k8s.io/awx-operator-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/awx-operator-proxy-rolebinding created
configmap/awx-operator-awx-manager-config created
service/awx-operator-controller-manager-metrics-service created
deployment.apps/awx-operator-controller-manager created
Ostrzeżenie:
W wyniku pojawiło się ostrzeżenie dotyczące zasobu namespaces/default. Mówi ono o braku anotacji kubectl.kubernetes.io/last-applied-configuration, która jest wymagana przez kubectl apply. Ostrzeżenie sugeruje, że powinieneś używać polecenia kubectl apply tylko na zasobach utworzonych deklaratywnie przez
kubectl create --save-config lub kubectl apply. Brakująca anotacja zostanie dodana automatycznie.
Zasoby:
namespace/default configured: Przestrzeń nazw default została skonfigurowana. Słowo kluczowe "configured" wskazuje, że zasób istniał wcześniej i został zmodyfikowany, a nie stworzony od nowa.
customresourcedefinition.apiextensions.k8s.io/... created: Trzy Custom Resource Definitions (CRD) dla awxbackups, awxrestores i awxs zostały utworzone. CRD pozwala na dodanie własnych zasobów do API Kubernetes.
serviceaccount/awx-operator-controller-manager created: Utworzono ServiceAccount o nazwie awx-operator-controller-manager.
Role i ClusterRole (role.rbac.authorization.k8s.io/... i clusterrole.rbac.authorization.k8s.io/... created): Utworzono różne role i clusterrole, które definiują zestaw uprawnień na zasoby w klastrze.
RoleBindings i ClusterRoleBindings (rolebinding.rbac.authorization.k8s.io/... i clusterrolebinding.rbac.authorization.k8s.io/... created): Utworzono rolebindings i clusterrolebindings, które przypisują określone role do użytkowników lub grup.
configmap/awx-operator-awx-manager-config created: Utworzono ConfigMap, który przechowuje konfigurację dla managera AWX.
service/awx-operator-controller-manager-metrics-service created: Utworzono usługę (Service) służącą do zbierania metryk z kontrolera awx-operator-controller-manager.
deployment.apps/awx-operator-controller-manager created: Utworzono Deployment dla kontrolera awx-operator-controller-manager, który definiuje i kontroluje instancje aplikacji.
Między czasie możemy sprawdzić czy konfiguracja ta stworzyła i działają nasze pody, poleceniem kubectl get pods
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
awx-operator-controller-manager-54fd9bc446-zl562 0/2 ContainerCreating 0 21s
Jak nasze pody dla awx będą ze statusem running wtedy możemy sprawdzić dodatkowo logi dla naszego deployment i zobaczyć czy wszystko z naszym deployment jest ok.
$ kubectl logs -f deployments/awx-operator-controller-manager -c awx-manager
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"cmd","msg":"Version","Go Version":"go1.19.11","GOOS":"linux","GOARCH":"amd64","ansible-operator":"v1.31.0","commit":"e67da35ef4fff3e471a208904b2a142b27ae32b1"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"cmd","msg":"Watching single namespace.","Namespace":"default"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"controller-runtime.metrics","msg":"Metrics server is starting to listen","addr":"127.0.0.1:8080"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"watches","msg":"Environment variable not set; using default value","envVar":"ANSIBLE_VERBOSITY_AWX_AWX_ANSIBLE_COM","default":2}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"watches","msg":"Environment variable not set; using default value","envVar":"ANSIBLE_VERBOSITY_AWXBACKUP_AWX_ANSIBLE_COM","default":2}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"watches","msg":"Environment variable not set; using default value","envVar":"ANSIBLE_VERBOSITY_AWXRESTORE_AWX_ANSIBLE_COM","default":2}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"ansible-controller","msg":"Watching resource","Options.Group":"awx.ansible.com","Options.Version":"v1beta1","Options.Kind":"AWX"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"ansible-controller","msg":"Watching resource","Options.Group":"awx.ansible.com","Options.Version":"v1beta1","Options.Kind":"AWXBackup"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"ansible-controller","msg":"Watching resource","Options.Group":"awx.ansible.com","Options.Version":"v1beta1","Options.Kind":"AWXRestore"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"proxy","msg":"Starting to serve","Address":"127.0.0.1:8888"}
{"level":"info","ts":"2023-10-25T08:53:13Z","logger":"apiserver","msg":"Starting to serve metrics listener","Address":"localhost:5050"}
{"level":"info","ts":"2023-10-25T08:53:13Z","msg":"Starting server","path":"/metrics","kind":"metrics","addr":"127.0.0.1:8080"}
awx.yml
W tym samym katalogu gdzie stworzyliśmy nasz plik kustomization.yaml tworzymy nasz plik awx.yml zawierający konfiguracje naszego serwisu awx:
---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx
spec:
service_type: NodePort
service_labels: |
environment: production
hostname: ansible.koska.in
postgres_configuration_secret: awx-postgres-configuration
ingress_type: none
ipv6_disabled: true
db.yml
Tworzymy nasz plik konfiguracyjny z konfiguracja polaczenia do naszej bazy danych
---
apiVersion: v1
kind: Secret
metadata:
name: awx-postgres-configuration
namespace: default
stringData:
host: <IP>
port: "<PORT>"
database: <DBNAME>
username: <USER>
password: "<PASSWORD>"
sslmode: disable
type: unmanaged
type: Opaque
Nasza baza danych
W celu udawania zewnętrznego hosta z bazą danych postawie moja baze danych postgresql w kontenerze z wykorzystaniem docker-compose.yml:
services:
# postgres server
db:
image: postgres:13.9
restart: always
environment:
POSTGRES_PASSWORD: example
ports:
- 5432:5432
Uruchommy naszą bazę danych. W katalogu z plikiem docker-compose.yml wydajemy komende docker compose up -d
Jak możemy zobaczyć wszystko się udało - nasz baza danych została postawiona i uruchomiona. Oczywiście w konfiguracji docker-compose brakuje pewnych parametrów związanych z zachowaniem danych. Jednak w ramach tego testu pozwoliłem sobie je pominąć - nie zależy mi na zachowaniu danych w tym akurat przypadku.
Pozostaje nam dodać naszą bazę danych z której będzie korzystał AWX, logujemy sie do kontenera z terminala lub przez docker desktop. W przypadku terminala będzie to polecenie:
$ docker compose exec -it "db" bash
Znajdziemy się teraz w kontenerze i wydajmy komendę w celu stworzenia bazy danych.
createdb -U postgres -h 127.0.0.1 awxdb
Mamy stworzoną bazę danych. Możemy pobrać pgadmin i sprawdzić działanie naszej bazy oraz czy dodatkowa baza została utworzona.
Mamy potwierdzone że serwer bazy danych działa i mamy stworzona bazę danych teraz powróćmy do naszego pliku db.yml i uzupełnijmy nasze brakujące dane.
Uzupełniamy db.yml
Nasz plik musimy uzupełnic o nastepujace dane: host: <IP> port: "<PORT>" database: <DBNAME> username: <USER> password: "<PASSWORD>"
Port znamy jest to 5432, database to awxdb, username i password wykorzystamy domyślne zdefiniowane w docker compose. Pozostaje nam zatem adres IP. Sprawdźmy zatem jak nasz klaster w przypadku konfiguracji docker desktop widzi sieci.
Posłużymy się podem uruchomionym na naszym klastrze, uruchomimy go za pomoca naszego pliku konfiguracyjnego ubuntu-pod.yml:
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-pod
namespace: default
spec:
containers:
- name: ubuntu-container
image: ubuntu:latest
command:
- sleep
- "infinity"
Wydajemy polecenie:
$ kubectl apply -f ubuntu-pod.yml
A potem łączymy się do naszego poda
$ kubectl exec -n default -it ubuntu-pod -- /bin/bash
I wydajemy zestaw komend:
apt update
ping
apt install iputils-ping telnet
ping <ADDRESS_IP_WSL_HOST> or <IP_NODE_CLUSTER>
Adres IP_NODE_CLUSETR możemy zdobyć z polecania:
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
docker-desktop Ready control-plane 3h24m v1.27.2 172.31.255.3 <none> Docker Desktop 5.15.90.1-microsoft-standard-WSL2 docker://24.0.6
Jak ktoś nie ufa ping można jeszcze uruchomić telnet (<ADDRESS_IP_WSL_HOST> or <IP_NODE_CLUSTER>) <PORT>
Zatem mamy wszystkie informacje. Możemy uzupełnić nasz db.yml
---
apiVersion: v1
kind: Secret
metadata:
name: awx-postgres-configuration
namespace: default
stringData:
host: 192.168.79.146
port: "5432"
database: awxdb
username: postgres
password: "example"
sslmode: disable
type: unmanaged
type: Opaque
Dodajemy sekrety
Do naszej pierwotnej konfiguracji kustomization.yaml dodajemy dodatkowa linijkę z naszym plikiem db.yml
...
resources:
- github.com/ansible/awx-operator/config/default?ref=2.7.0
- db.yml
...
Zmiany zapisujemy i uruchamiamy nasze polecenie kubectl apply -k .
...
secret/awx-postgres-configuration created
...
kubectl poinformuje nas o tym że sekret został dodany / stworzony.
Teraz dodajemy nasz awx.yml do kustomization.yaml
...
resources:
- github.com/ansible/awx-operator/config/default?ref=2.7.0
- db.yml
- awx.yml
...
Zmiany zapisujemy i uruchamiamy nasze polecenie kubectl apply -k .
Po wydaniu polecenia możemy od razu wskoczyć w logi naszego awx-menagera
$ kubectl logs -f deployments/awx-operator-controller-manager -c awx-manager
Warto podczas działania uruchamiania naszych podów i konfiguracji potrzeć czy jakie dane przechodzą do naszego postgres.
Z poziomu kubectl możemy sprawdzić nasze pody czy mają odpowiedni status:
kubectl get pods -l "app.kubernetes.io/managed-by=awx-operator"
Oraz na jakim porcie działa nasza aplikacja:
kubectl get svc -l "app.kubernetes.io/managed-by=awx-operator"
Hasło do naszego panelu możemy pobrać z polecenia:
kubectl get secret awx-admin-password -o jsonpath="{.data.password}" | base64 --decode ; echo
Mamy nasz panel i możemy się zalogować - oczywiscie to tylko testowa konfiguracja ale na jej bazie możemy już planować nasze wdrożenia.
A w tym artykule to już wszystko. Zapraszam do kolejnych wpisów.
Comments