Ansible y Contenedores 🐳
Hasta ahora hemos usado Ansible para gestionar servidores tradicionales. En este capítulo damos el salto al mundo de los contenedores: aprenderás a orquestar Docker y Kubernetes desde Ansible, sustituyendo scripts bash frágiles por playbooks idempotentes y reutilizables.
Suscríbete al canal de YouTube para recibir la notificación.
🎯 ¿Por qué usar Ansible con contenedores?
La analogía: el contenedor y el estibador
Si Docker es la caja de mercancías estandarizada que viaja por todo el mundo, Ansible es el estibador que decide qué cajas se cargan en qué barco, en qué orden, y con qué etiquetas. Docker resuelve el "cómo se empaqueta una aplicación"; Ansible responde al "cómo desplegamos cientos de esas cajas en producción de forma repetible".
Cuándo Ansible aporta valor frente a Compose o kubectl
| Escenario | Herramienta nativa | Ansible aporta |
|---|---|---|
| Levantar un stack en local | docker compose up | — (úsalo nativo) |
| Desplegar Docker en 50 hosts | scripts bash | Inventario + idempotencia |
| Aplicar manifiestos K8s en CI | kubectl apply | Plantillas Jinja2 + Vault |
| Bootstrap de un clúster K3s | bash + scp | Roles reutilizables |
| Mezclar VMs y K8s en un mismo despliegue | varias | Un solo playbook |
Regla de oro: Ansible brilla cuando hay que coordinar la capa de infraestructura (instalar Docker, configurar el daemon, gestionar redes, certificados) junto con la capa de aplicación contenerizada. Para flujos puramente declarativos dentro del clúster, Helm o Kustomize son mejores.
🐳 Parte 1: Ansible + Docker
Instalación de la colección community.docker
Las funcionalidades de Docker en Ansible viven en una colección externa que hay que instalar primero:
ansible-galaxy collection install community.docker
pip install docker # SDK de Python requerido en el host destino
Módulos clave
| Módulo | Para qué sirve |
|---|---|
community.docker.docker_image | Construir, descargar o eliminar imágenes |
community.docker.docker_container | Crear, arrancar, parar contenedores |
community.docker.docker_network | Gestionar redes Docker |
community.docker.docker_volume | Volúmenes persistentes |
community.docker.docker_compose_v2 | Orquestar stacks con Compose |
community.docker.docker_login | Autenticarse en registries |
Ejemplo: instalar Docker y desplegar Nginx
---
- name: Bootstrap Docker host y desplegar Nginx
hosts: webservers
become: true
tasks:
- name: Instalar dependencias
ansible.builtin.apt:
name:
- ca-certificates
- curl
- gnupg
state: present
update_cache: true
- name: Añadir repositorio oficial de Docker
ansible.builtin.deb822_repository:
name: docker
types: deb
uris: https://download.docker.com/linux/ubuntu
suites: "{{ ansible_distribution_release }}"
components: stable
signed_by: https://download.docker.com/linux/ubuntu/gpg
- name: Instalar Docker Engine
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: true
- name: Asegurar que el servicio docker está activo
ansible.builtin.service:
name: docker
state: started
enabled: true
- name: Desplegar contenedor Nginx
community.docker.docker_container:
name: web
image: nginx:1.27-alpine
state: started
restart_policy: unless-stopped
ports:
- "80:80"
volumes:
- /srv/web/html:/usr/share/nginx/html:ro
Orquestar un stack Compose desde Ansible
- name: Levantar stack con Compose
community.docker.docker_compose_v2:
project_src: /opt/app
state: present
pull: always
Este patrón es ideal para bootstraping: Ansible prepara el host (firewall, certificados, variables de entorno renderizadas con Jinja2) y luego delega el ciclo de vida de los contenedores a Compose.
Construir imágenes en pipeline
- name: Construir y publicar imagen
community.docker.docker_image:
name: registry.local/notastack-api
tag: "{{ git_commit_sha }}"
source: build
build:
path: ./api
pull: true
push: true
⚓ Parte 2: Ansible + Kubernetes
Instalación de la colección kubernetes.core
ansible-galaxy collection install kubernetes.core
pip install kubernetes openshift pyyaml jsonpatch
Necesitas un kubeconfig válido en el host donde corre Ansible (normalmente el control node).
Módulos clave
| Módulo | Para qué sirve |
|---|---|
kubernetes.core.k8s | Aplicar/borrar cualquier recurso (equivale a kubectl apply) |
kubernetes.core.k8s_info | Consultar recursos existentes (equivale a kubectl get) |
kubernetes.core.helm | Gestionar releases de Helm |
kubernetes.core.helm_repository | Añadir/quitar repositorios Helm |
kubernetes.core.k8s_exec | Ejecutar comandos dentro de pods |
kubernetes.core.k8s_scale | Escalar deployments y statefulsets |
Ejemplo: desplegar una aplicación en Kubernetes
---
- name: Desplegar NotaStack en K8s
hosts: localhost
gather_facts: false
vars:
app_namespace: notastack
app_image: registry.local/notastack-api:1.4.2
tasks:
- name: Crear namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ app_namespace }}"
- name: Aplicar manifiestos renderizados con Jinja2
kubernetes.core.k8s:
state: present
namespace: "{{ app_namespace }}"
template: "manifests/{{ item }}.yaml.j2"
loop:
- configmap
- deployment
- service
- ingress
- name: Esperar a que el deployment esté disponible
kubernetes.core.k8s_info:
kind: Deployment
namespace: "{{ app_namespace }}"
name: notastack-api
register: dep
until: >
dep.resources[0].status.availableReplicas | default(0) ==
dep.resources[0].spec.replicas
retries: 30
delay: 10
Plantilla Jinja2 de un Deployment
# manifests/deployment.yaml.j2
apiVersion: apps/v1
kind: Deployment
metadata:
name: notastack-api
spec:
replicas: {{ replicas | default(2) }}
selector:
matchLabels:
app: notastack-api
template:
metadata:
labels:
app: notastack-api
spec:
containers:
- name: api
image: {{ app_image }}
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: "{{ db_host }}"
Este patrón —manifiestos K8s renderizados con Jinja2 desde Ansible— es muy potente: te da variables, condicionales y bucles que Helm sólo ofrece de forma limitada, y se integra con tu inventario de Ansible para distinguir entornos (dev/staging/prod).
Instalar charts con Helm desde Ansible
- name: Añadir repo de Bitnami
kubernetes.core.helm_repository:
name: bitnami
repo_url: https://charts.bitnami.com/bitnami
- name: Instalar PostgreSQL
kubernetes.core.helm:
name: db
chart_ref: bitnami/postgresql
release_namespace: notastack
create_namespace: true
values:
auth:
postgresPassword: "{{ pg_password }}"
primary:
persistence:
size: 20Gi
Bootstrap completo de un clúster K3s
Un caso de uso muy común: usar Ansible para levantar el clúster (no sólo desplegar dentro de él):
- name: Instalar K3s en el master
hosts: k3s_master
become: true
tasks:
- name: Descargar e instalar K3s
ansible.builtin.shell: |
curl -sfL https://get.k3s.io | sh -
args:
creates: /usr/local/bin/k3s
- name: Recoger token del nodo
ansible.builtin.slurp:
src: /var/lib/rancher/k3s/server/node-token
register: k3s_token
- name: Unir workers al clúster
hosts: k3s_workers
become: true
tasks:
- name: Instalar K3s agent
ansible.builtin.shell: |
curl -sfL https://get.k3s.io | \
K3S_URL=https://{{ hostvars[groups['k3s_master'][0]].ansible_host }}:6443 \
K3S_TOKEN={{ hostvars[groups['k3s_master'][0]]['k3s_token']['content'] | b64decode | trim }} \
sh -
args:
creates: /usr/local/bin/k3s
✅ Buenas prácticas
- Usa Vault para tokens de registry, kubeconfigs y credenciales (lo vimos en el capítulo 7).
- No reinventes Helm: si ya existe un chart oficial, instálalo desde Ansible en lugar de traducirlo a manifiestos sueltos.
- Separa bootstrap (infra) de despliegue (app): Ansible es excelente para el primero; para el segundo, considera usar GitOps (ArgoCD/Flux) y limitar Ansible al pipeline.
- Idempotencia: usa siempre
state: present/absenten lugar decommand: kubectl apply .... - Tags por entorno: estructura tu playbook con tags
dev,staging,prody combínalo con inventarios separados.
🧪 Reto del capítulo
- Crea un playbook que instale Docker en un host Linux limpio.
- Despliega un stack con Nginx + un backend Node + Redis usando
docker_compose_v2. - Bonus: levanta un clúster K3s de 1 master + 2 workers con Ansible y despliega el mismo stack como Deployments + Services.
📚 Recursos
- Documentación oficial
community.docker - Documentación oficial
kubernetes.core - Curso de Docker y Kubernetes en pabpereza.dev
Próximo capítulo: Ansible en DevOps y CI/CD — integramos todo lo aprendido en pipelines de Jenkins y GitHub Actions.