
Docker y Kubernetes son dos herramientas fundamentales para desplegar aplicaciones modernas en servidores Linux. Docker permite construir y ejecutar contenedores de forma reproducible, mientras Kubernetes permite orquestarlos, escalarlos, reiniciarlos, exponerlos en red y actualizarlos de manera controlada.
El flujo general es sencillo: primero se crea una imagen de contenedor con Docker, luego se publica en un registro y finalmente Kubernetes la ejecuta como Pods dentro de un clúster. Kubernetes define los Deployments como objetos que administran Pods y ReplicaSets para mantener el estado deseado de una aplicación.
Objetivo de la guía: instalar Docker, crear una imagen, preparar Kubernetes con kubeadm, desplegar una aplicación, exponerla mediante Service e Ingress, y aplicar buenas prácticas básicas de seguridad.
Entorno recomendado: Ubuntu Server o Debian actualizado, acceso sudo, IP fija, DNS configurado y al menos dos servidores si se desea probar un clúster más realista.
1. Docker, containerd y Kubernetes: qué hace cada uno
| Componente | Función | Uso práctico |
|---|---|---|
| Docker | Construye, ejecuta y publica imágenes de contenedor. | Empaquetar una aplicación con sus dependencias. |
| containerd | Runtime de contenedores compatible con Kubernetes. | Ejecutar contenedores dentro del clúster. |
| Kubernetes | Orquesta contenedores en uno o varios nodos. | Escalar, reiniciar, exponer y actualizar aplicaciones. |
| kubectl | Cliente de administración de Kubernetes. | Aplicar YAML, revisar Pods, ver logs y diagnosticar errores. |
Docker sigue siendo muy útil para construir y probar imágenes, pero Kubernetes trabaja con runtimes compatibles con CRI, como containerd o CRI-O. La documentación de Kubernetes recomienda alinear el controlador de cgroups entre kubelet y el runtime; si kubelet usa systemd, el runtime también debe usarlo.
2. Preparar el servidor Linux
Antes de instalar Docker o Kubernetes, conviene actualizar el sistema y revisar red, hostname, memoria, almacenamiento y puertos activos.
sudo apt update sudo apt full-upgrade -y hostnamectl ip -br address free -h df -h lsblk ss -lntup
En servidores de producción, cada nodo debe tener una IP estable, sincronización horaria, resolución DNS confiable y conectividad entre nodos.
3. Instalar Docker Engine en Linux
Docker recomienda instalar Docker Engine desde su repositorio oficial APT en Ubuntu, ya que permite recibir actualizaciones mediante el sistema normal de paquetes. La documentación oficial también advierte que algunos paquetes no oficiales pueden entrar en conflicto con los paquetes oficiales de Docker.
sudo apt remove -y docker.io docker-compose docker-doc podman-docker containerd runc || true sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker docker --version
Nota para Debian: si usas Debian, cambia el repositorio de Docker de /linux/ubuntu a /linux/debian siguiendo la documentación oficial de Docker.
4. Crear una aplicación de prueba con Docker
Para probar el flujo completo, se puede crear una aplicación web mínima con Nginx. En un caso real, la imagen podría contener una aplicación Node.js, Python, Java, Go, PHP o cualquier framework compatible con contenedores.
mkdir -p ~/demo-docker-k8s cd ~/demo-docker-k8s cat > index.html <<'EOF' <!doctype html> <html lang="es"> <head> <meta charset="utf-8"> <title>App Docker y Kubernetes</title> </head> <body> <h1>Aplicación desplegada con Docker y Kubernetes</h1> <p>Servidor Linux funcionando correctamente.</p> </body> </html> EOF cat > Dockerfile <<'EOF' FROM nginx:alpine COPY index.html /usr/share/nginx/html/index.html EXPOSE 80 EOF
Luego se construye y prueba la imagen localmente:
docker build -t demo-web:1.0 . docker run --rm -d \ --name demo-web \ -p 8080:80 \ demo-web:1.0 curl http://localhost:8080 docker stop demo-web
5. Publicar la imagen en un registro
Kubernetes necesita descargar la imagen desde un registro accesible por los nodos. Puede usarse Docker Hub, GitHub Container Registry, GitLab Container Registry, Harbor o un registro privado.
docker login docker tag demo-web:1.0 USUARIO/demo-web:1.0 docker push USUARIO/demo-web:1.0
No incluyas secretos dentro de la imagen. Contraseñas, tokens, llaves privadas y archivos .env deben gestionarse fuera de la imagen, usando Secrets, variables protegidas o un gestor de secretos.
6. Preparar Kubernetes con kubeadm
kubeadm es una herramienta oficial de Kubernetes para crear clústeres mediante comandos como kubeadm init y kubeadm join. Kubernetes aclara que kubeadm se enfoca en arrancar el clúster, pero no instala automáticamente todos los complementos adicionales, como monitoreo, dashboard o componentes específicos de nube.
Preparar kernel, red y swap
Estos pasos deben aplicarse en todos los nodos del clúster.
sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter cat <<'EOF' | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system
Configurar containerd
Cuando kubelet usa el controlador de cgroups systemd, Kubernetes recomienda configurar el runtime con el mismo controlador.
sudo mkdir -p /etc/containerd containerd config default | \ sudo tee /etc/containerd/config.toml > /dev/null sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' \ /etc/containerd/config.toml sudo systemctl restart containerd sudo systemctl enable containerd sudo systemctl status containerd --no-pager
Instalar kubeadm, kubelet y kubectl
La guía oficial de instalación de kubeadm indica cómo instalar kubeadm, kubelet y kubectl desde los repositorios de Kubernetes. La documentación consultada corresponde a Kubernetes v1.36, por lo que en producción siempre conviene confirmar la versión estable que se desea usar antes de instalar.
sudo apt update sudo apt install -y apt-transport-https ca-certificates curl gpg sudo mkdir -p /etc/apt/keyrings curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | \ sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /' | \ sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt update sudo apt install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl kubeadm version kubectl version --client
7. Crear el clúster Kubernetes
En el nodo principal se inicializa el plano de control. El parámetro --pod-network-cidr debe coincidir con el complemento de red que se instalará después.
sudo kubeadm init \ --pod-network-cidr=192.168.0.0/16
Después de ejecutar el comando, kubeadm mostrará una instrucción kubeadm join para unir nodos worker. Ese comando debe guardarse de forma segura.
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config kubectl get nodes
Instalar la red de Pods
Kubernetes necesita un complemento de red para que los Pods puedan comunicarse. kubeadm no instala automáticamente todos los complementos, por lo que se debe elegir uno compatible con el entorno: Calico, Cilium, Flannel u otro CNI.
kubectl get nodes kubectl get pods -A
Importante: instala solo un complemento de red y revisa su documentación oficial. Mezclar varios CNI puede provocar problemas de conectividad difíciles de diagnosticar.
8. Desplegar la aplicación en Kubernetes
Una aplicación en Kubernetes se despliega normalmente mediante un Deployment. Este objeto define la imagen, el número de réplicas, los recursos solicitados y la forma en que Kubernetes debe mantener la aplicación disponible.
cat > deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-web
labels:
app: demo-web
spec:
replicas: 3
selector:
matchLabels:
app: demo-web
template:
metadata:
labels:
app: demo-web
spec:
containers:
- name: demo-web
image: USUARIO/demo-web:1.0
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "256Mi"
EOF
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods -o wide
En producción, no conviene desplegar aplicaciones sin límites de CPU y memoria, porque una aplicación defectuosa puede consumir recursos de todo el nodo.
9. Exponer la aplicación con un Service
Un Service crea un punto de acceso estable para llegar a uno o varios Pods. Kubernetes define Service como una forma de exponer una aplicación de red que se ejecuta en un conjunto de Pods.
cat > service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: demo-web
spec:
type: NodePort
selector:
app: demo-web
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080
EOF
kubectl apply -f service.yaml
kubectl get svc demo-web
Con NodePort, la aplicación puede probarse desde la IP de un nodo usando el puerto configurado. Para producción, suele ser mejor usar un balanceador, un Ingress Controller o una solución como MetalLB en entornos propios.
kubectl get pods -o wide kubectl get endpoints demo-web curl http://IP_DEL_NODO:30080
10. Publicar por dominio con Ingress
Ingress permite publicar servicios HTTP y HTTPS mediante reglas de host, rutas y backends. La documentación de Kubernetes aclara que Ingress requiere un Ingress Controller para funcionar.
cat > ingress.yaml <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-web
spec:
ingressClassName: nginx
rules:
- host: demo.ejemplo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-web
port:
number: 80
EOF
kubectl apply -f ingress.yaml
kubectl get ingress
Para producción: configura DNS, certificados TLS, un Ingress Controller estable y reglas de firewall. En servidores propios también puede usarse MetalLB o un balanceador externo.
11. Configuración y secretos
Kubernetes recomienda usar ConfigMaps para separar configuración no confidencial de las imágenes de contenedor. Para información sensible se usan Secrets, aunque la propia documentación advierte que deben protegerse correctamente con RBAC, cifrado en reposo y buenas prácticas de acceso.
kubectl create configmap demo-config \ --from-literal=APP_ENV=production kubectl create secret generic demo-secret \ --from-literal=DB_PASSWORD='cambia-esto-en-produccion'
Seguridad: no uses Secrets como si fueran una bóveda completa. En entornos críticos, habilita cifrado en reposo, RBAC estricto y una solución especializada para gestión de secretos.
12. Actualizar la aplicación sin detener el servicio
Kubernetes permite cambiar la imagen de un Deployment y hacer un despliegue progresivo. Si algo falla, también se puede volver a una versión anterior mediante rollback.
kubectl set image deployment/demo-web \ demo-web=USUARIO/demo-web:1.1 kubectl rollout status deployment/demo-web kubectl get pods
kubectl rollout history deployment/demo-web kubectl rollout undo deployment/demo-web kubectl rollout status deployment/demo-web
13. Comandos esenciales de diagnóstico
| Comando | Uso |
|---|---|
| kubectl get pods -A | Ver Pods de todos los namespaces. |
| kubectl describe pod NOMBRE | Revisar eventos, errores y detalles del Pod. |
| kubectl logs POD | Consultar logs de la aplicación. |
| kubectl get svc | Ver servicios expuestos. |
| kubectl get ingress | Ver reglas de entrada HTTP/HTTPS. |
| kubectl top pods | Ver consumo de CPU y memoria si Metrics Server está instalado. |
14. Buenas prácticas de seguridad
- Usa imágenes oficiales, verificadas y actualizadas.
- No ejecutes contenedores como root si no es necesario.
- No guardes secretos dentro del Dockerfile ni dentro del repositorio.
- Define límites de CPU y memoria en cada Deployment.
- Usa namespaces para separar ambientes.
- Aplica RBAC con privilegios mínimos.
- Publica tráfico externo mediante Ingress con TLS.
- Escanea imágenes antes de pasarlas a producción.
- Centraliza logs y métricas.
- Documenta procedimientos de rollback y recuperación.
15. Errores frecuentes al desplegar con Docker y Kubernetes
- Creer que Kubernetes necesita Docker Engine como runtime obligatorio.
- Instalar Docker y Kubernetes sin configurar containerd correctamente.
- No desactivar swap en nodos creados con kubeadm.
- Desplegar aplicaciones sin límites de recursos.
- Usar la etiqueta latest en producción.
- Subir contraseñas o tokens dentro de imágenes Docker.
- No instalar un Ingress Controller y esperar que Ingress funcione solo.
- Exponer NodePort públicamente sin firewall ni balanceador.
- No tener monitoreo, logs ni estrategia de respaldo.
- No probar rollback antes de producción.
Preguntas clave
¿Necesito Docker para usar Kubernetes?
No necesariamente como runtime del clúster. Kubernetes usa runtimes compatibles con CRI, como containerd o CRI-O. Docker sigue siendo muy útil para construir, probar y publicar imágenes.
¿Qué es kubeadm?
Es una herramienta oficial para crear clústeres Kubernetes mediante comandos como kubeadm init y kubeadm join.
¿Qué es un Deployment?
Es un objeto de Kubernetes que administra la ejecución deseada de una aplicación, incluyendo réplicas y actualizaciones de Pods.
¿Qué diferencia hay entre Service e Ingress?
Service crea un endpoint estable para acceder a Pods. Ingress define reglas HTTP/HTTPS para publicar servicios hacia el exterior, pero requiere un Ingress Controller.
¿Puedo usar Kubernetes en un solo servidor?
Sí, sirve para laboratorio, aprendizaje o proyectos pequeños. Para producción real conviene usar varios nodos, alta disponibilidad, monitoreo, backups y una estrategia clara de red y almacenamiento.
¿Docker Compose o Kubernetes?
Docker Compose es más simple para desarrollo local o un solo servidor. Kubernetes es más adecuado cuando necesitas escalado, alta disponibilidad, despliegues controlados, auto-recuperación y operación en varios nodos.
Recomendamos
- Cómo crear un entorno profesional para programar en Linux con Visual Studio Code, Docker y Git.
- Guía completa de observabilidad: cómo monitorear servidores Linux, aplicaciones y contenedores.
- Cómo automatizar un centro de datos utilizando Linux, Ansible y herramientas de software libre.
- Guía definitiva para construir un entorno DevSecOps con herramientas open source.
En resumen
El despliegue moderno en Linux suele seguir una ruta clara: crear una imagen Docker, publicarla en un registro, preparar Kubernetes con containerd y kubeadm, desplegar con un Deployment, exponer con un Service y publicar con Ingress.
Docker facilita el empaquetado de aplicaciones. Kubernetes aporta orquestación, escalado, actualización controlada y recuperación automática. Pero para producción no basta con que funcione: también se necesitan límites de recursos, seguridad, logs, métricas, certificados, backups y rollback probado.
Conclusión editorial
Docker ayuda a empaquetar la aplicación; Kubernetes ayuda a operarla. La verdadera madurez DevOps aparece cuando el despliegue es repetible, seguro, observable y capaz de volver atrás si algo falla.

