Probando Ansible AWX con MicroK8s
Actualizado: 2020-01-18
AVISO: Post largo (intro a Ansible, AWX, MicroK8s)
Actualización 2020-01-14: Actualizado a Ansible AWX 9.1.1
Ansible (/ánsibol/) es el gestor de configuración de moda, y por méritos propios. Aunque no es perfecto (en determinadas ocasiones se puede preferir un modelo cliente/servidor en lugar de una conexión SSH ad-hoc), ofrece una buena combinación entre funcionalidad y simplicidad. Siempre y cuando tengamos conectividad SSH con la máquina a gestionar (o no, a través de bastiones), en el caso de equipos Linux, o conectividad WinRM o o PSRP para equipos Windows, podremos realizar infinidad de acciones o tareas sobre las máquinas a gestionar.
Uno de los problemas de Ansible (hablando correctamente, Ansible Engine) es que no tiene una forma de ejecutar de forma automatizada playbooks, para mantener la configuración sincronizada de forma periódica, ejecutar tareas planificadas o incluso auto-provisionar equipos. Aquí es donde entra Ansible Tower, que es la versión con soporte de Ansible AWX, al estilo de lo que Red Hat hace con Wildfly y JBoss. Ansible Tower/AWX en básicamente una API REST con una interfaz web que se comunica con ella. Utilizando esta API, se pueden definir inventarios, credenciales, equipos, plantillas de trabajo, flujos de trabajo, etc. así como asignar permisos por usarios/grupos mediante su sistema RBAC.
He de reconocer que al principio cuesta un poco, pero cuando se le pilla el truco, uno se da cuenta de lo potente que es. Pero lo que no me explico es la complejidad de instalación del software. Creo que Red Hat se está empeñando en poner las cosas difíciles a quienes usan sus productos sin suscripción (que al final son los que en gran medida depuran el software, contribuyen de forma gratuita, etc.); en este caso, se nos obliga a hacer una instalación mediante Docker, que aunque está de moda, que lo veo muy bien, creo que deberían dar alternativas (que sí que las dan con la versión con soporte, por lo que impedimentos técnicos no los hay, simplemente es intencionalidad). Para la versión libre (AWX) se soportan los siguientes métodos de instalación:
- Openshift (claramente enfocado a utilizar un stack completo de Red Hat)
- Docker Compose
- Kubernetes
En cambio, para Tower, la versión con soporte, es básicamente un script de instalación, que la verdad no he probado, aunque me imagino que lo que hace es provisionar los nodos con el software necesario, a la antigua usanza (no sé si por debajo creará un cluster de K8s u Openshift, o directamente lo hace sobre el sistema operativo).
Básicamente, la forma de instalación de AWX es crear una serie de contenedores en el orquestador en cuestión (AWX task, AWX web, RabbitMQ, Postgres, Memcached).
Con ideas de probar AWX para un proyecto interno, empecé a analizar las 3 opciones de instalación; la primera de ellas, Openshift, la descarté desde el principio por ser una tecnología no muy extendida, en favor, en todo caso, de Kubernetes. Ya que esto era una PoC, no quería complicarme mucho con Kubernetes, por lo que empecé a probar con Docker Compose, pero a la hora de escalar, lanzando la instalación en varios nodos, me di cuenta de que el modo de funcionar de AWX requiere un cluster de RabbitMQ, que es difícil de configurar dinámicamente con Docker Compose. Con Kubernetes y su “magia” hace que el cluster de RabbitMQ se configure y escale automáticamente.
Siendo mi única opción Kubernetes, no quería montarme un cluster por mi cuenta, ni tener que montar un cluster de EKS, que de base ya son 144$ al mes más los nodos de computación, al final tuve que buscar alternativas más simples.
Mi primera intención fue probar Minikube en mi máquina local, pero cada pod de AWX consume 6 GB de RAM, por lo que mínimo a la MV de Minikube le tenía que dar 7 GB, y teniendo mi portátil 8 GB, murió varias veces en el intento… Me planteé montar Minikube en una instancia EC2, pero esto sería montar Virtualbox, sobre un entorno ya virtualizado (EC2), y sobre el que correría Kubernetes… Aunque factible, me parecía un poco engorroso. Así que gracias a mi compañero Roque, que me recordó la existencia de MicroK8s, me decidí a probar.
Partiendo de una EC2 con Ubuntu 18.04 limpia, estos son los comandos a ejecutar para montar un entorno de Ansible AWX con MicroK8s (forzamos la versión 1.15, ya que AWX no es compatible con una versión mayor, por ahora):
sudo snap refresh microk8s --channel 1.15/stable
sudo snap install helm --channel=2.16/stable --classic
(grep "^--allow-privileged$" /var/snap/microk8s/current/args/kube-apiserver > /dev/null) || (echo "--allow-privileged" | sudo tee -a /var/snap/microk8s/current/args/kube-apiserver)
(grep "^--allow-privileged$" /var/snap/microk8s/current/args/kubelet > /dev/null) || (echo "--allow-privileged" | sudo tee -a /var/snap/microk8s/current/args/kubelet)
sudo microk8s.stop
sudo microk8s.start
microk8s.enable ingress
microk8s.enable dns
microk8s.enable storage
sudo snap alias microk8s.kubectl kubectl
microk8s.config > $HOME/.kube/config
helm init
Helm es necesario para instalación de Postgres; si vamos a utilizar una BBDD externa, no es necesario.
Tras esto, ya tenemos el entorno de MicroK8s disponible; ahora nos bajamos AWX y lo configuramos:
# Actualizar e instalar dependencias mínimas
sudo apt update
sudo apt upgrade -y
sudo apt install -y python3-pip vim
pip3 install docker docker-compose --user
# Instalar Ansible, necesario para instalar AWX
sudo add-apt-repository ppa:ansible/ansible -y
sudo apt update
sudo apt install -y ansible
# Nos bajamos la release 9.1.1 de AWX
curl -sLO https://github.com/ansible/awx/archive/9.1.1.tar.gz
tar zxf 9.1.1.tar.gz
cd awx-9.1.1/installer
cp -a inventory{,.original}
# Configuramos el fichero de inventario para que use Kubernetes
sed -i -e 's/#* *kubernetes_context.*/kubernetes_context=microk8s/' inventory
sed -i -e 's/#* *kubernetes_namespace.*/kubernetes_namespace=awx/' inventory
# Y lanzamos la instalación
ansible-playbook -i inventory install.yml
Variables interesantes en el fichero inventory
:
kubernetes_context
: el contexto del cliente de Kubernetes que usaremos para conectarnos al cluster; para MicroK8s, en general serámicrok8s
.kubernetes_namespace
: el namespace del cluster de Kubernetes que se usará para crear todos los elementos de AWX; por defecto esawx
.kubernetes_deployment_name
: es un prefijo que se usa para generar los nombres de los recursos dentro del namespace; por defecto esawx
.pg_hostname
: es el host remoto donde debemos tener instalado un servidor de Postgres; si esta variable está comentada, el instalador creará un servidor de Postgres en Kubernetes usando Helm; si está definida, se usará el servidor indicado (habrá que configurar adecuadamente el resto de parámetros de conexión a la BBDD:pg_username
,pg_password
,pg_database
, etc.).
También se pueden especificar las siguientes variables para indicar imágenes y versiones alternativas a las oficiales (por ejemplo, si hemos creado unas propias para añadir software, etc.):
kubernetes_task_version
(por defecto:9.1.1
)kubernetes_task_image
(por defecto:ansible/awx_task
)kubernetes_web_version
(por defecto:9.1.1
)kubernetes_web_image
(por defecto:ansible/awx_web
)
Esto nos creará un namespace llamado awx
en MicroK8s, y desplegará en él 2 statefulsets
: 1 para Postgres y uno para AWX. Cada statefulset/pod
de AWX contiene los siguientes contenedores:
- memcached
- rabbitmq
- awx-celery (awx_task)
- awx-web
Cada vez que se escala el statefulset
se crean estos 4 contenedores. Gracias al plugin rabbitmq_peer_discovery_k8s
(aquí se puede ver fichero YAML de configuración) los contenedores de rabbitmq forman un cluster de forma automática.
Para volver a empezar desde 0, tenemos 2 posibilidades; o bien borrar el namespace:
kubectl delete namespaces awx
O bien con el comando microk8s.reset
, aunque con este comando, frecuentemente se queda “colgado”:
microk8s.reset
Después del reset, es necesario volver a configurar los complementos (dns, storage, ingress), y configurar el cliente:
microk8s.enable ingress
microk8s.enable dns
microk8s.enable storage
sudo snap alias microk8s.kubectl kubectl
microk8s.config > $HOME/.kube/config
helm init
Y podemos lanzar de nuevo la instalación:
ansible-playbook -i inventory install.yml
Si por delante del servidor vamos a poner un balanceador (por ejemplo, un ELB o ALB), debemos configurar el ingress
de Nginx para que reenvíe las cabeceras X-Forwarded-*
:
kubectl -n default edit configmaps nginx-load-balancer-microk8s-conf
Añadiendo/modificando la sección data con este valor:
data:
use-forwarded-headers: "true"
Esto es necesario, por ejemplo, si se usa un balanceador con HTTPS, y configuramos la autentificación de Azure, ya que sin el reenvío de estas cabeceras, AWX se cree que vamos por HTTP en lugar de HTTPS, y la URL de callback para el login de Azure se genera incorrectamente.
Una vez realizados los pasos de la instalación, lo único que nos queda es acceder a la aplicación, usando el puerto 80 de la máquina, por ejemplo, http://localhost, que es donde publica los servicios el ingress controller de nginx.