Índice
- Chapter 1. From Monolith to Microservices
- Chapter 2. Container Orchestration
- Chapter 3. Kubernetes
- Chapter 4. Kubernetes Architecture
- Chapter 5. Installing Kubernetes
- Chapter 6. Minikube - A Local Single-Node Kubernetes Cluster
- Chapter 7. Accessing Minikube
- Chapter 8. Kubernetes Building Blocks
- Chapter 9. Authentication, Authorization, Admission Control
- Chapter 10. Services
- Chapter 11. Deploying a Stand-Alone Application
- Chapter 12. Kubernetes Volume Management
- Chapter 13. ConfigMaps and Secrets
- Chapter 14. Ingress
- Chapter 15. Advanced Topics
- Chapter 16. Kubernetes Community
Una roca, representa la aplicación del monolito
Monolito, capas sedimentadas de características y lógica redundante traducidas en miles de lineas de código, escritas en un lenguaje de programación no tan moderno, basado en patrones y principios de arquitectura anticuados.
Características de un monolito:
- Las nuevas características y mejoras añaden complejidad al código, mayores tiempos de carga, compilación y construcción.
- Es un software grande y único que crece continuamente.
- Funciona en un solo sistema que tiene que satisfacer requisitos de computación, memoria, almacenamiento y redes.
- El hardware a su vez, es complejo y costoso.
- Se ejecuta como un solo proceso, la ampliación de las características individuales del monolito es casi imposible.
- El escalado de toda la aplicación significa desplegar manualmente una nueva instancia del monolito en otro servidor, típicamente detrás de un balanceador de carga, otra solución costosa.
- Durante las actualizaciones, parches o migraciones, se producen tiempos de inactividad y hay que planificar ventanas de mantenimiento ya que afectarán a todos los clientes.
- Aunque existen soluciones para minimizar los tiempos de inactividad como una configuración activa/pasiva en HA, todavía puede ser complejo implementar estas soluciones.
Similitudes:
- Monolito --> Gran roca
- Microservicios --> Guijarros
Los guijarros representan la totalidad de la roca. Además son faciles de seleccionar y agrupar en base al color, tamaño y forma.
Los microservicios pueden ser desplegados individualmente en servidores separados provistos con los recuros necesarios para cada servicio.
La arquitectura basada en microservicios está alineada con la Arquitectura Dirigida por Eventos y Arquitectura Orientada a Servicios, donde las aplicaciones complejas están compuestas por pequeños procesos que se comunican entre si por medio de APIs. Los microservicios se desarrollan en un lenguaje moderno y el más adecuado para cada servicio. Permite elegir el hardware para cada servicio. La naturaleza distribuida de los servicios añade complejidad a la arquitectura, pero añade beneficios en cuanto a la escalabilidad, además de no tener inactividad.
Una aplicación multiproceso (monolito), no puede funcionar como un microservicio. Por lo tanto, se necesita refactorizar (Big-Bang ó refactorización incremental):
- Big-bang:
Bloquea el desarrollo y nuevas características para centrarse en la refactorización. - Refactorización incremental: Permite que se desarrollen nuevas características y se apliquen como microservicios modernos que puedan comunicarse con la API. Mientras tantos las características actuales del monolito se refactorizaran, y este irá desapareciendo.
Para algunas aplicaciones, puede ser más económico reconstruirlas que refactorizarlas. Las aplicaciones muy unidas a BBDD, son candidatas poco válidas para la refactorización.
Una vez que el monolito ha sobrevivido a la refactorización, el siguiente reto es diseñar mecanismos o encontrar herramientas adecuadas para mantener vivos todos los módulos desacoplados.
Si se despliegan muchos módulos en un solo servidor, lo más probable es que las diferentes librerias y el entorno del runtime, puedan entrar en conflicto.
Esto obliga a separar modulos por servidor, y no es una forma económica de gestión de recursos. Entonces aparecieron los contenedores, que proporcionan entornos encapsulados. El amplio soporte de los contenedores aseguró la portabilidad de las aplicaciones del bare-metal a las máquinas virtuales, pero esta vez con múltiples aplicaciones desplegadas en el mismo servidor.
- Contenedores: Son aplicaciones que ofrecen alto rendimiento y escalabilidad. Son los más adecuados para ofrecer microservicios, ya que proporcionan entornos virtuales portatiles y aislados.
-
Microservicios: Son aplicaciones ligeras escritas en varios lenguajes modernos con dependencias, librerias y requisitos especificos. Los contenedores encapsulan los microservicios y sus dependencias pero no los ejecutan directamente. Los contenedores ejecutan imágenes de contenedores.
-
Imagen de contenedor: Agrupa la aplicación junto con su runtime y sus dependencias, ofreciendo un entorno ejecutable aislado.
En los entornos de desarrollo la ejecución de contenedores en un solo host, puede ser una opción. Sin embargo, para entornos productivos ya no es una opción viable, ya que debe cumplir una seria de requisitos:
- Tolerancia a fallos.
- Escalabildiad a petición.
- Uso óptimo de los recursos.
- Auto-discovery para la comunicación entre si.
- Accesibilidad desde el exterior.
- Actualizaciones y rollbacks sin downtime.
Los orquestadores de contenedores es una herramienta que permite automatizar los despliegues y la gestión de contenedores, al mismo tiempo que cumple con los requisitos anteriores.
Algunos orquestadores:
- AWS ECS
- Azure Container Instances
- Nomad
- Kubernetes
- Docker Swarm
Aunque podemos mantener manualmente un par de contenedores, los orquestadores facilitan mucho las tareas, permite:
- Agrupar host mientras se crear un cluster.
- Programar contenedores para que corran en cluster en funcion de la disponibilidad de los recursos.
- Permite que los contenedores de cluster se comuniquen entre si.
- Gestionar y optimizar el uso de recursos.
- Permitir una implementación de políticas para el acceso seguro a las aplicaciones que corren en los contenedores.
Los orquestadores pueden ser desplegados en bare-metal, máquinas virtuales, on-premise o nube pública.
Kubernetes es un sistema de código abierto (OpenSource) para automatizar el despliegue, el escalado y la gestión de aplicaciones en contenedores.
Viene del griego, que significa timonel. También se le conoce como k8s. Se inspira mucho en el sistema de Google Borg, un orquestador de contenedores escrito en Go. Kubernetes fue iniciado por Google, y con el lanzamiento de la v1 en 2015, fue donado a CNCF.
El sistema de Borg de Google, es un administrador de clusteres que ejecuta cientos de miles de trabajos, de muchos miles de aplicaciones diferentes, a través de varios clusteres, cada uno con decenas de miles de máquinas.
Durante más de una década, Borg ha sido el secreto de Google, gestionando servicios como Gmail, Drive, Maps, etc.
Algunas características heredadas de Borg:
- API Servers
- Pods
- IP-per-Pod
- Services
- Labels
Kubernetes ofrece un conjunto de características para la orquestación:
- Automatic bin packing: Programan en base a los recuros necesarios y limitaciones, para maximizar su utilización sin sacrificar la disponibilidad.
- Self-healing: Reemplaza y reprograma automáticamente los contenedores de los Nodes que fallan. Mata y reinicia los contenedores que no responden a los HealthCheck. También, evita que se dirija el tráfico a contenedores que no responden.
- Horizontal scaling: se escalan manual o automáticamente las aplicaciones, en base a métricas.
- Service discovery and Load Balancing: Los contenedores reciben sus propias direcciones IPs, mientras que se asigna un único DNS a un conjunto de contenedores para ayudar al balanceo de carga.
- Automated rollouts and rollbacks: Despliegan y realizan rollback sin problema en base a las actualizaciones de la aplicación y los cambios de configuración, supervisando constantemente la salud de la aplicación, evitando así inactividad.
- Secret and configuration management: Gestiona los secrets y detalles de la configuración de una aplicación por separado de la imagen del contenedor, con el fin de evitar la reconstrucción de la imagen respectiva. Los secrets son información confidencial que se le pasa a la aplicación sin revelar el contenido, como en GitHub.
- Storage orchestration: Montan automáticamente soluciones de Storage definidas por software en contenedores de almacenamiento local, proveedores de nube externos o sistemas de almacenamiento en red.
- Batch execution: Apoya la ejecución por lotes, los trabajos de larga duración y reemplaza los contenedores que fallan.
Muchas otras características están por llegar aunque se encuentran en base beta. Otras, ya están estables y aportan grandes beneficios como el control de acceso basado en roles (RBAC), estable desde la 1.8.
K8s es portatil y extensible. La arquitectura de k8s es modular y enchufable. No solo orquesta aplicaciones de tipo microservicio desacoplado, si no que su arquitectura sigue patrones de microservicios desacoplados. Pueden escribirse recursos personalizados, operadores APIs, reglas de programación o plugins.
Algunos usuarios que utilizan k8s:
- BlaBlaCar
- eBay
- IBM
- Huawei
- ING
Es uno de los proyectos alojados por Linux-Foundation. Tiene como objetivo acelarar la adopción de contenedores, microservicios y aplicaciones nativas de la nube.
Algunos proyectos:
- Kubernetes
- Prometheus
- Envoy
- CoreDNS
- containerd
- Fluentd
Incuvando:
- CRI-O y rkt
- Linkerd
- etcd
- gRPC
- CNI
- Harbor
- Helm
- Rook y Vitess
- Notary
- TUF
- NATS
- Jaeger y OpenTracing
- Open Policy Agent
Para k8s, CNCF:
- Hogar neutral para la marca registrada, hace cumplir el uso adecuado.
- Proporciona una licencia para escanear el código del nucleo y del proveedor.
- Ofrece oritentación jurídica sobre cuestiones de patentes y derechos de autor.
- Crea un plan de estudios de aprendizaje de código abierto, formación y certificación tanto para los administradores como para los desarrolladores de k8s.
- Gestiona un grupo de trabajo de conformidad con el software.
- Comercializa activamente Kubernetes.
A un nivel muy alto, kubernetes cuenta con los siguientes componentes principales:
- Uno o más Master Nodes.
- Uno o más Worder Nodes.
- Base de datos distribuida, key-value, etcd.
El Master Node, proporciona un entorno de ejecución, es el reponsable de gestionar el estado del cluster de k8s, y es el cerebro detrás de todas las operaciones. Para comunicarse con el cluster, los usuarios envian solicitudes al Master Node a través de la CLI, un panel de control de interfaz de usuario o una interfaz gráfica de programación (API).
El plano de control mantiene un registro de todos los objectos de k8s en el sistema, y ejecuta bucles de control continuos para gestionar el estado de esos objetos. Es importante mantener el plano de control. Perder el plano de control puede introducir tiempos de inactividad, causando la interrumpción del servicio a los clientes. Para asegurar tolerancia a fallos, se añaden réplicas del Master Node al cluster, configurandolo en modo HA. Solo uno de los Master Node administra activamente el cluster, el resto de componentes permanencen sincronizados con el resto de Master Replicas.
Para mantener el estado del cluster de k8s, todos los datos de la configuración del cluster se guardan en etcd. Sin embargo, etcd es un almacenamiento key-value distribuido, que solo guarda datos relacionados con el estado del cluster. Está configurado en el Master Node y ubicado en un host dedicado, para evitar las posibilidades de pérdidas.
Un Master Node tiene los siguientes componentes:
- API Server
- Scheduler
- Controller managers
- etcd
- Todas las tareas administrativas, están coordinadas por el.
- Intercepta las llamadas RESTfull de los usuarios, operadores y agentes externos, las valida y las procesa.
- Durante el procesamiento, lee el estado del cluster desde etcd, y despues de la llamada, el estado resultante del cluster de k8s se guarda en etcd.
- API Server, es el único componente que habla con etcd, tanto para leer como para escribir, actuando como una interferfaz de intermediario.
- Es configurable, también admite la adición de API Servers personalizados, cuando el API Server primario se convierte en un proxy de todos los API servers.
El role del Scheduler es asignar nuevos objetos a los Pods de los Nodes. Durante el proceso de programación, las decisiones se toman en base al estado actual del cluster, y a los requisitos de los nuevos objetos. El scheduler, obtiene de etcd, a través de API Server, los datos de uso de recursos de cada nodo del cluster. También recibe de API Server, los requisitos del nuevo objeto. Los requisitos pueden incluir las restricciones que establecen los usuarios y los operadores. El Scheduler también tiene en cuenta los requisitos de calidad (QoS), la localización de los datos, la afinidad, la antiafinidad, tolerancia, etc.
Es altamente configurable, si se añaden más Schedulers, es necesario indicar en el objeto el nombre del Scheduler, sino lo gestionará el de por defecto.
Es bastante importante y complejo este componente.
Son componentes del plano de control en el Master Node que regulan el estado del cluster. Son bucles de vigilancia que se ejecutan continuamente y comparan el estado deseado del cluster (proporcionado por los nuevos objectos) con el estado actual (proporcionado de etcd a traves de (API Server). En caso de desajuste, se toman medidas correctivas para hacer coincidir con el estado deseado.
Los Controller Managers, son los encargados de actuar cuando los Nodes no están disponibles, garantizar que el número de Pods son el esperado, de crear Endpoints, Service Accounts, y API access tokens. Es el encargado de interactuar con la infraestructura subyacente de un proveedor de nubes cuando los Nodes no están disponibles, de gestionar volumenes de almacenamiento, equilibrar el balancedo de carga y el enrutamiento.
Es un almacenamiento de datos clave-valor distribuido que se utiliza para persistir el estado de cluster. Los nuevos datos se escriben en el almacen solo añadiendolos, los datos nunca son reemplazados. Los datos obsoletos se compactan periódicamente para minimizar el tamaño de etcd.
Solo el API Server es capaz de comunicarse con etcd.
La herramienta de gestión (CLI) de etcd proporciona capacidades de copias de seguridad, snapshots, y restauración.
Para los entornos productivos, es importante replicarlos en HA.
Algunas herramientas de arranque de clusters de k8s, por defecto, aprovisionan Master Nodes de etcd aplilados, en los que etcd se ejecuta junto a los demás componentes del Master Node y comparte recursos con ellos. Puede aislarse el etcd en un host separado, reduciendo así posibilidades de fallo. Tanto las configuraciónes en el mismo host o en otro, adminten configuración en HA.
etcd está escrito en Go. Además de almacenar el estado del cluster, también se almacenan detalles de configuración como subredes, ConfigMaps, secrets, etc.
Un Worder Node, proporciona un entorno de ejecución para las aplicaciones clientes. Aunque son microservicios en contenedores, estas aplicaciones están encapsuladas en Pods, controladas por componentes del Master Node. Los Pods se programan en los Worker Node, donde se encuentran los recursos de computación, memoria, almacenamiento y la red para hablar entre ellos y el mundo exterior.
Un Pod, es la unidad mínima de k8s. Es una colección lógica de uno o más contenedores juntos.
Además, para acceder a las aplicaciones del mundo exterior, nos conectamos a los Worker Nodes y no al Master Node.
Un Worker Node, cuenta con los siguientes componentes:
- Container Runtime
- kubelet
- kube-proxy
- Addons para DNS, Dashboards, monitorización y registros de logs.
Aunque k8s es un motor de orquestación de contenedores, no tiene la capacidad de manejar directamente los contenedores. Para ejecutar y gestionar el ciclo de vida de un contenedor, se requiere de un runtime en el Node en el que se va a programar un Pod y sus contendores.
k8s soporta:
- Docker: utiliza como runtime, containerd, es el más utilizado con k8s.
- CRI-O: contenedor ligero para k8s, soporta registros de imagenes Docker.
- containerd: simple y portatil, proporciona robustez.
- rkt: motor nativo para pods, disponible para imagenes docker.
- rktlet:
kubelet es un agente que se ejecuta en cada Node y se comunica con los componentes del plano de control del Master Node. Recibe las definiciones del Pod, principalmente del API Server, e interactúa con el runtime del contenedor en el Node para ejecutar los contenedores asociados al Pod. También supervisa la salud de los contenedores en ejecución del Pod.
kubelet se conecta al runtime del contenedor mediante la Interfaz de Tiempo de Ejecución del Contenedor (CRI).
CRI consiste en buffers de protocolo, gRPC API, y librerías.
kubelet que actúa como cliente grpc se conecta a la CRI, que actúa como servidor grpc para realizar operaciones de contenedor e imagen. CRI implementa dos servicios: ImageService y RuntimeService:
- ImageService es responsable de todas las operaciones relacionadas con la imagen.
- RuntimeService es responsable de todas las operaciones relacionadas con el Pod y el contenedor.
Los runtime de los contenedores solían estar codificados en duro en Kubernetes, pero con el desarrollo de CRI, Kubernetes es más flexible ahora y utiliza diferentes runtimes de los contenedores sin necesidad de recompilar. Cualquier runtime de contenedor que implemente la CRI puede ser usado por Kubernetes para gestionar Pods, contenedores e imágenes de contenedores.
Ejemplos de CRI:
- dockershim: los contenedores se crean usando Docker instalado en los nodos de los trabajadores. Internamente, Docker utiliza containerd para crear y gestionar los contenedores.
- cri-containerd: podemos usar directamente el contenedor de la descendencia más pequeña de Docker para crear y gestionar los contenedores.
- CRI-O: permite utilizar cualquier runtime compatible con la Iniciativa de Contenedor Abierto (OCI) con los Kubernetes.
El kube-proxy es el agente de red que se ejecuta en cada Node responsable de las actualizaciones dinámicas y del mantenimiento de todas las reglas de red del Node. Absorbe los detalles de la red de Pods y reenvía las solicitudes de conexión a Pods.
Los addons son características y funcionalidades de los clústeres que aún no están disponibles en los Kubernetes, por lo que se implementan a través de Pods y servicios de terceros.
- DNS - el clúster DNS es un servidor DNS necesario para asignar los registros DNS a los objetos y recursos de Kubernetes.
- Dashboard - una interfaz de usuario de propósito general basada en la web para la gestión de clústeres.
- Monitoring - recopila las métricas de los contenedores a nivel de clúster y las guarda en un almacén central de datos.
- Logging - recoge los logs de los contenedores a nivel de cluster y los guarda en un almacenamiento central de logs para su análisis.
Las aplicaciones basadas en microservicios desacoplados dependen en gran medida de la conexión en red para imitar el estrecho acoplamiento que una vez estuvo disponible en la era monolítica. La conexión en red, en general, no es la más fácil de entender e implementar. Kubernetes no es una excepción - como un orquestador de microservicios en contenedores es necesario abordar 4 desafíos de red distintos:
- Container-to-Container: Comunicación dentro de los Pods.
- Pod-to-Pod: en el mismo Node y a través de otros Nodes de otros clusters.
- Pod-to-Service: dentro del mismo Namespaces y a través de los Namespaces de los clústeres.
- External-to-Service: para que los clientes accedan a las aplicaciones de un cluster.
Todos estos desafíos de red deben ser abordados antes de desplegar un cluster de Kubernetes.
Haciendo uso de las características del kernel del sistema operativo del host subyacente, el runtime de un contenedor crea un espacio de red aislado para cada contenedor que inicia. En Linux, ese espacio de red aislado se denomina network namespace. Un network namespaces se comparte entre contenedores, o con el sistema operativo del host. Cuando se inicia un Pod, se crea un network namespace dentro del Pod, y todos los contenedores que se ejecutan dentro del Pod compartirán ese network namespace para que puedan hablar entre ellos a través del localhost.
En un grupo de Kubernetes los Pods están programados en Nodes al azar. Independientemente de su Node anfitrión, se espera que los Pods puedan comunicarse con todos los demás Pods del cluster, todo esto sin la implementación de la Traducción de Direcciones de Red (NAT). Este es un requisito fundamental de cualquier implementación de red en los kubernetes.
El modelo de red de los Kubernetes tiene como objetivo reducir la complejidad, y trata a los Pods como máquinas virtuales en una red, en la que cada máquina virtual recibe una dirección IP, por lo que cada Pod recibe una dirección IP. Este modelo se denomina "IP-per-Pod" y garantiza la comunicación entre los Pods.
Los contenedores comparten el network namespace del Pod y deben coordinar la asignación de puertos dentro del Pod como lo harían las aplicaciones en una VM, todo ello mientras pueden comunicarse entre sí en el host local - dentro del Pod. Sin embargo, los contenedores se integran con el modelo general de red de Kubernetes mediante el uso de la Interfaz de Red de Contenedores (CNI) soportada por los plugins CNI. La CNI es un conjunto de especificaciones y librerías que permiten a los plugins configurar la red de contenedores.
Aunque hay unos pocos plugins de kernel, la mayoría de los plugins CNI son soluciones de red definidas por software (SDN) de terceros que implementan el modelo de red de Kubernetes. Además de abordar el requisito fundamental del modelo de red, algunas soluciones de red ofrecen soporte para las políticas de red. Flannel, Weave, Calico son sólo algunas de las soluciones SDN disponibles para los clusters de Kubernetes.
El runtime del contenedor descarga la asignación de IP a CNI, que se conecta al plugin configurado subyacente, como Bridge o MACvlan, para obtener la dirección IP. Una vez que la dirección IP es dada por el plugin respectivo, CNI la reenvía al tiempo de ejecución del contenedor solicitado.
Para que una aplicación en contenedor que se ejecuta en Pods dentro de un clúster de Kubernetes se pueda desplegar con éxito, se requiere accesibilidad desde el mundo exterior. Kubernetes permite la accesibilidad externa a través de services, construcciones complejas que encapsulan definiciones de reglas de red en Nodes de clúster. Al exponer los services al mundo externo con kube-proxy, las aplicaciones se hacen accesibles desde fuera del cluster a través de una IP virtual.
Los kubernetes pueden ser instalados usando diferentes configuraciones:
-
All-in-One Single-Node Installation: En esta configuración, todos los componentes maestros y trabajadores están instalados y funcionando en un solo Node. Aunque es útil para el aprendizaje, desarrollo y pruebas, no debe utilizarse en la producción. Minikube es un ejemplo de ello.
-
Single-Node etcd, Single-Master and Multi-Worker Installation: En esta configuración, tenemos un Master Node único, que también ejecuta una instancia de un solo Node etcd. Múltiples Workers Nodes están conectados al Master Node.
-
Single-Node etcd, Multi-Master and Multi-Worker Installation: En esta configuración, tenemos Multi-Master Nodes configurados en modo HA, pero tenemos una instancia de un solo Node etcd. Multi-Worker Nodes están conectados a los Master Nodes.
-
Multi-Node etcd, Multi-Master and Multi-Worker Installation: En este modo, etcd está configurado en modo HA agrupado, los Master Nodes están todos configurados en modo HA, conectando a Multi-Worker Nodes.
Esta es la configuración de producción más avanzada y recomendada.
Una vez que decidimos el tipo de instalación, también tenemos que tomar algunas decisiones relacionadas con la infraestructura:
- ¿Debemos establecer los k8s en bare-metal, nube pública o nube privada?
- ¿Qué sistema operativo subyacente deberíamos utilizar? ¿Deberíamos elegir RHEL, CoreOS, CentOS, u otra cosa?
- ¿Qué solución de red deberíamos utilizar?
- Y así sucesivamente.
Estas son sólo algunas de las opciones de instalación del host local disponibles para desplegar clusters de Kubernetes de uno o varios Nodes en nuestra estación de trabajo/portátil:
- Minikube - cluster de Kubernetes locales de un Single-Node.
- Docker Desktop - Single-Node de cluster local de Kubernetes para Windows y Mac.
- CDK en LXD - cluster local Multi-Node con contenedores de LXD.
Minikube es la forma preferida y recomendada para crear una configuración de Kubernetes todo en uno localmente.
Los kubernetes pueden instalarse en las máquinas virtuales y en el bare-metal.
-
On-Premise VMs: Los kubernetes pueden instalarse en las máquinas virtuales creadas a través de Vagrant, VMware vSphere, KVM u otra herramienta de gestión de la configuración (CM) junto con un software de hipervisor. Hay diferentes herramientas disponibles para automatizar la instalación, como Ansible o kubeadm.
-
On-Premise Bare Metal: Los kubernetes pueden ser instalados en bare-metal, en RHEL, CoreOS, CentOS, Fedora, Ubuntu, etc.
kubernetes pueden ser instalados y administrados en casi cualquier entorno de nube:
-
Hosted Solutions: Algunos de los proveedores que ofrecen soluciones alojadas para Kubernetes son:
- Google Kubernetes Engine (GKE)
- Azure Kubernetes Service (AKS)
- Amazon Elastic Container Service for Kubernetes (EKS)
- DigitalOcean Kubernetes.
- OpenShift Dedicated
- Platform9
- IBM Cloud Kubernetes Service.
-
Turnkey Cloud Solutions:
- Google Compute Engine (GCE)
- Amazon AWS (AWS EC2)
- Microsoft Azure (AKS).
-
Turnkey On-Premise Solutions: Las Soluciones On-Premise instalan Kubernetes en nubes privadas internas seguras con sólo unos pocos comandos:
- GKE On-Prem de Google Cloud.
- IBM Cloud Private.
- OpenShift Container Platform de Red Hat.
Algunas herramientas/recursos útiles disponibles:
-
kubeadm: Es una forma segura y recomendada de arrancar un cluster de Kubernetes de Single-Node o Multi-Node.
-
kubespray: (antes conocido como kargo), podemos instalar clusters de Kubernetes en HA en AWS, GCE, Azure, OpenStack, o bare-metal. Está basado en Ansible, y está disponible en la mayoría de las distribuciones de Linux.
-
kops: Podemos crear, destruir, actualizar y mantener clusters de Kubernetes de producción y HA desde la línea de mando. También puede aprovisionar las máquinas. Actualmente, AWS está oficialmente respaldada. La compatibilidad con GCE está en fase beta, y VMware vSphere en fase alfa, y otras plataformas están previstas para el futuro.
-
kube-aws: Podemos crear, actualizar y destruir clusters de Kubernetes en AWS desde la línea de comandos.
Minikube está disponible para Linux, MacOS o Windows. Sin embargo, para aprovechar al máximo todas las características que ofrece Minikube, se debe instalar un Hipervisor de Tipo 2 en tu equipo local, para que funcione en conjunto con Minikube.
Minikube construye toda su infraestructura siempre y cuando el Hipervisor de Tipo 2 esté instalado en nuestra estación de trabajo. Minikube invoca el Hipervisor para crear una sola VM que luego alberga un clúster de Kubernetes de un solo nodo. Por lo tanto, necesitamos asegurarnos de que tenemos el hardware y el software necesario requerido por Minikube para construir su entorno. A continuación se describen los requisitos para ejecutar Minikube en nuestra estación de trabajo local:
-
kubectl: kubectl es un binario utilizado para acceder y gestionar cualquier grupo de Kubernetes. Se instala por separado de Minikube.
-
Hipervisor de tipo 2:
- En Linux VirtualBox o KVM
- En macOS VirtualBox, HyperKit o VMware Fusion
- En Windows VirtualBox o Hyper-V
NOTA: Minikube soporta una opción --vm-driver=none que ejecuta los componentes de Kubernetes directamente en el sistema operativo del host y no dentro de una VM. Con esta opción se requiere una instalación Docker y un sistema operativo Linux en la estación de trabajo local, pero no una instalación de hipervisor. Si utiliza --vm-driver=none, asegúrese de especificar una red de puente para Docker. De lo contrario, podría cambiar entre los reinicios de la red, causando la pérdida de conectividad a su clúster.
-
La virtualización de VT-x/AMD-v debe estar habilitada en la estación de trabajo local en el BIOS.
-
Conexión a Internet en la primera ejecución de Minikube - para descargar paquetes, dependencias, actualizaciones y sacar las imágenes necesarias para iniciar el cluster de Minikube Kubernetes. Las ejecuciones subsiguientes requerirán una conexión a Internet sólo cuando las nuevas imágenes Docker necesiten ser extraídas de un repositorio de contenedores o cuando las aplicaciones desplegadas en contenedores lo necesiten. Una vez que una imagen ha sido extraída puede ser reutilizada sin una conexión a Internet.
Se puede acceder a cualquier grupo de Kubernetes que funcione bien a través de cualquiera de los siguientes métodos:
- Herramientas y scripts de la Interfaz de Línea de Comando (CLI).
- Interfaz de usuario basada en la web (Web UI) desde un navegador web.
- APIs de CLI o programáticamente.
kubectl es el cliente de la Interfaz de Línea de Comando (CLI) de Kubernetes para gestionar los recursos y aplicaciones del cluster. Puede utilizarse de forma autónoma o como parte de scripts y herramientas de automatización. Una vez que se han configurado todas las credenciales y puntos de acceso al clúster necesarios para kubectl, se puede utilizar de forma remota desde cualquier lugar para acceder a un clúster.
El Dashboard de Kubernetes proporciona una Interfaz de Usuario basada en la Web (Web UI) para interactuar con un cluster de Kubernetes para gestionar recursos y aplicaciones en contenedores.
Como sabemos, Kubernetes tiene el API Server, y los operadores/usuarios se conectan a él desde el mundo externo para interactuar con el cluster. Usando tanto la CLI como la Web UI, podemos conectarnos al API Server que se ejecuta en el Master Node para realizar diferentes operaciones. Podemos conectarnos directamente al API Server utilizando sus endpoints y enviarle comandos, siempre y cuando podamos acceder al Master Node y tengamos las credenciales adecuadas.
A continuación, podemos ver una parte de la API HTTP de Kubernetes:
La API HTTP de kubernetes se puede dividir en tres grupos independientes:
-
Core Group (/api/v1) Este grupo incluye objetos como Pods, Service, Nodes, Namespace, ConfigMaps, Secrets, etc.
-
Named Group Este grupo incluye objetos en formato /apis/$NAME/$VERSION. Estas diferentes versiones de la API implican diferentes niveles de estabilidad y soporte:
- Alpha level - puede ser eliminado en cualquier momento, sin previo aviso. Por ejemplo, /apis/batch/v2alpha1.
- Beta level - está bien probado, pero la semántica de los objetos puede cambiar de forma incompatible en una posterior versión beta o estable. Por ejemplo, /apis/certificados.k8s.io/v1beta1.
- Stable level - aparece en el software publicado para muchas versiones posteriores. Por ejemplo, /apis/networking.k8s.io/v1.
-
System-wide Este grupo consiste en API Endpoints de todo el sistema, como /healthz, /logs, /metrics, /ui, etc. Podemos conectarnos a un API Server directamente llamando a los respectivos API Endpoint o a través del CLI/Web UI.
kubectl se instala generalmente antes de instalar el Minikube, pero también podemos instalarlo después. Una vez instalado, kubectl recibe su configuración automáticamente para el acceso al cluster de Kubernetes de Minikube. Sin embargo, en otras configuraciones de clúster de Kubernetes, es posible que necesitemos configurar los puntos de acceso al clúster y los certificados requeridos por kubectl para acceder al clúster.
Existen diferentes métodos que pueden utilizarse para instalar kubectl, que se mencionan en la documentación de Kubernetes. Para obtener los mejores resultados, se recomienda mantener kubectl en la misma versión que los Kubernetes dirigidos por Minikube - en el momento en que se escribió el curso la última versión estable era la v1.14.1.
Para instalar kubectl en Linux, siga las siguientes instrucciones:
Descargue el último binario estable de kubectl, hágalo ejecutable y muévalo al PATH:
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
NOTA: Para descargar y configurar una versión específica de kubectl (como la v1.14.1), emita el siguiente comando:
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
Hay dos maneras de instalar kubectl en macOS: manualmente y usando el administrador de paquetes de Homebrew. A continuación, daremos instrucciones para ambos métodos.
Para instalar kubectl manualmente, descargue el último binario estable de kubectl, hágalo ejecutable y muévalo al PATH con el siguiente comando:
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
NOTA: Para descargar y configurar una versión específica de kubectl (como la v1.14.1), emita el siguiente comando:
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
Para instalar kubectl con el administrador de paquetes de Homebrew, emita el siguiente comando:
$ brew install kubernetes-cli
Para acceder al clúster de Kubernetes, el cliente de kubectl necesita el endpoint del Master Node y las credenciales adecuadas para poder interactuar con el API Server que se ejecuta en el Master Node. Al iniciar Minikube, el proceso de inicio crea, por defecto, un archivo de configuración, config, dentro del directorio .kube (a menudo denominado archivo dot-kube-config), que reside en el directorio principal del usuario. El archivo de configuración tiene todos los detalles de conexión requeridos por kubectl. Por defecto, el binario de kubectl analiza este archivo para encontrar el endpoint de conexión del Master Node, junto con las credenciales.
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/student/.minikube/ca.crt
server: https://192.168.99.100:8443
name: minikube
contexts:
- context:
cluster: minikube
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /home/student/.minikube/client.crt
client-key: /home/student/.minikube/client.key
Una vez instalado kubectl, podemos obtener información sobre el clúster de Minikube con el comando kubectl cluster-info:
$ kubectl cluster-info
Kubernetes master is running at https://192.168.99.100:8443
KubeDNS is running at https://192.168.99.100:8443//api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Aunque para el clúster de Kubernetes instalado por Minikube el archivo ~/.kube/config se crea automáticamente, este no es el caso de los clústeres de Kubernetes instalados por otras herramientas. En otros casos, el archivo de configuración tiene que ser creado manualmente y a veces reconfigurado para adaptarse a varias configuraciones de red y de cliente/servidor.
El dashboard de Kubernetes proporciona una interfaz de usuario basada en la web para la gestión del clúster de Kubernetes. Para acceder al Dashboard desde Minikube, podemos utilizar el comando minikube dashboard, que abre una nueva pestaña en nuestro navegador web, mostrando el Dashboard de los Kubernetes:
$ minikube dashboard
http://127.0.0.1:37751/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/
kubectl se autentica con el API Server en el Master Node y hace que el Dashboard esté disponible en una URL ligeramente diferente a la anterior, esta vez a través del puerto proxy 8001.
Primero, ejecutamos el comando kubectl proxy:
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
Bloquea la terminal mientras el proxy esté funcionando. Con el proxy ejecutándose podemos acceder al Dashboard a través de la nueva URL..
http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/kubernetes-dashboard:/proxy/#!/overview?namespace=default
Cuando se ejecuta kubectl proxy, podemos enviar solicitudes a la API a través del localhost en el puerto proxy 8001 (desde otra terminal, ya que el proxy bloquea la primera terminal):
$ curl http://localhost:8001/
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/apps",
......
......
"/logs",
"/metrics",
"/openapi/v2",
"/version"
]
}
Con la anterior petición, obtenemos todos los edpoints del API Server. Al hacer clic en el enlace anterior (en el comando curl), se abrirá la misma salida de listado en una pestaña del navegador.
Podemos explorar todas las combinaciones de rutas con curl o en un navegador, por ejemplo:
http://localhost:8001/api/v1
http://localhost:8001/apis/apps/v1
http://localhost:8001/healthz
http://localhost:8001/metrics
Cuando no se utiliza el kubectl proxy, es necesario autenticarse en el API Server cuando se envían las solicitudes de la API. Podemos autenticarnos proporcionando un token o un conjunto de claves y certificados.
Un token de acceso es generado por el servidor de autenticación (el API Server del Master Node) y devuelto al cliente. Utilizando ese token, el cliente puede conectarse de nuevo al API Server de Kubernetes sin necesidad de proporcionar más detalles de autenticación y, a continuación, acceder a los recursos.
Consigue el token:
$ TOKEN=$(kubectl describe secret -n kube-system $(kubectl get secrets -n kube-system | grep default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d '\t' | tr -d " ")
Get the API server endpoint:
$ APISERVER=$(kubectl config view | grep https | cut -f 2- -d ":" | tr -d " ")
$ echo $APISERVER
https://192.168.99.100:8443
$ kubectl cluster-info
Kubernetes master is running at https://192.168.99.100:8443 ...
Access the API server using the curl command, as shown below:
$ curl $APISERVER --header "Authorization: Bearer $TOKEN" --insecure
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/apps",
......
......
"/logs",
"/metrics",
"/openapi/v2",
"/version"
]
}
En lugar del token de acceso, podemos extraer el certificado de cliente, la clave de cliente y los datos de la autoridad de certificación del archivo .kube/config. Una vez extraídos, se codifican y luego se pasan con un comando curl para la autenticación. El nuevo comando curl es similar a:
$ curl $APISERVER --cert encoded-cert --key encoded-key --cacert encoded-ca
Kubernetes tiene un modelo de objetos abundante, que representa diferentes entidades persistentes en el cluster de Kubernetes::
- Qué aplicaciones contenedoras estamos ejecutando y en qué Node.
- Consumo de recursos de la aplicación
- Diferentes políticas adjuntas a las aplicaciones, como políticas de reinicio/actualización, tolerancia a fallos, etc.
Con cada objeto, declaramos la sección spec. El sistema de Kubernetes gestiona la sección status de los objetos, donde registra el estado real del objeto. En un momento dado, el Plano de Control de Kubernetes intenta hacer coincidir el estado real del objeto con el estado deseado del mismo.
Algunos ejemplos de objetos son Pods, ReplicaSets, Deployments, Namespaces, etc.
Cuando se crea un objeto, la sección de datos de configuración del objeto que se encuentra debajo del campo spec debe enviarse al API Server de Kubernetes. La sección spec describe el estado deseado, junto con alguna información básica, como el nombre del objeto. La solicitud de la API para crear un objeto debe tener la sección spec, así como otros detalles. Aunque el API Server acepta archivos de definición de objetos en formato JSON, la mayoría de las veces proporcionamos dichos archivos en formato YAML, que se convierten por kubectl en una carga útil JSON y se envían al API Server.
Ejemplo de un objecto en formato YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.11
ports:
- containerPort: 80
Analizando el código del objecto los campos son los siguientes:
- apiVersion es el primer campo obligatorio, y especifica el API endpoint en el Api Server al que queremos conectarnos; debe coincidir con una versión existente para el tipo de objeto definido.
- kind, que especifica el tipo de objeto - en nuestro caso es Deployment, pero puede ser Pod, Replicaset, Namespace, Service, etc.
- metadata, contiene la información básica del objeto, como el name, labels, namespace, etc. Nuestro ejemplo muestra dos campos spec (spec y spec.template.spec).
- spec, requerido, marca el comienzo del bloque que define el estado deseado del objeto Deployment. En nuestro ejemplo, queremos asegurarnos de que 3 Pods están funcionando en un momento dado (replicas). Los Pods se crean utilizando la template de Pods definida en spec.template. Un objeto anidado, como el Pod que forma parte de un Deployment, retiene sus metadata y su spec y pierde la apiVersión y el kind - ambos son reemplazados por la template. En spec.template.spec, definimos el estado deseado del Pod. Nuestro Pod crea un único contenedor que ejecuta la imagen nginx:1.15.11 del Docker Hub.
Una vez creado el objeto de despliegue, el sistema de Kubernetes adjunta el campo de status al objeto.
Un Pod es el objeto más pequeño y simple de los Kubernetes. Es la unidad de despliegue en los Kubernetes, que representa una única instancia de la aplicación. Un Pod es una colección lógica de uno o más contenedores, que:
- Están programados juntos en el mismo host con el Pod.
- Compartir el mismo network namespace.
- Tener acceso a montar el mismo external storage (volumes).
Los Pods son efímeros por naturaleza y no tienen capacidad de auto-curarse a sí mismos. Por eso se usan con controllers que se encargan de la replicación de los Pods, la tolerancia a los fallos, la autocuración, etc.
Algunos ejemplos de controllers son:
- Deployments
- ReplicaSet
- ReplicationController
Ejemplo de un objeto Pod en formato YAML:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.11
ports:
- containerPort: 80
Campos:
- apiVersion debe especificar v1 para la definición del objeto Pod.
- kind que especifica el tipo de objeto Pod.
- metadata, contiene el name y el label del objeto.
- spec, marca el comienzo del bloque que define el estado deseado del objeto Pod, también llamado PodSpec.
Nuestro Pod crea un único contenedor que ejecuta la imagen nginx:1.15.11 de Docker Hub.
Las Labels son pares de key-value unidos a objetos kubernetes (por ejemplo, Pods, ReplicaSet).
- Las labels se utilizan para organizar y seleccionar un subconjunto de objetos, según los requisitos establecidos. Muchos objetos pueden tener los mismos labels.
- Las labels no proporcionan unicidad a los objetos.
- Los controllers utilizan las labels para agrupar lógicamente los objetos desacoplados, en lugar de utilizar los nombres o las identificaciones de los objetos.
En la imagen de arriba, hemos usado dos Label keys: app y env. Basándonos en nuestros requisitos, hemos dado diferentes valores a nuestros cuatro Pods. La label env=dev selecciona y agrupa lógicamente los dos primeros Pods, mientras que la label app=frontend selecciona y agrupa lógicamente los dos Pods de la izquierda. Podemos seleccionar uno de los cuatro Pods seleccionando dos Etiquetas: app=frontend y env=qa.
Los controllers utilizan Label Selectors para seleccionar un subconjunto de objetos. Kubernetes soporta dos tipos de Selectors:
- Equality-Based Selectors (basados en igualdad): Permiten filtrar los objetos en función de las key-value de las Labels. El emparejamiento se logra usando los operadores =, == (iguales, usados indistintamente), o != (no iguales). Por ejemplo, con env==dev o env=dev estamos seleccionando los objetos en los que la clave Label key-value, env-dev.
- Set-Based Selectors (basados en conjunto): Permiten filtrar los objetos en base a un conjunto de valores. Podemos utilizar los operadores in, notin para Label values, y exist/does not exist para Label keys. Por ejemplo, con env in (dev,qa) seleccionamos objetos en los que env esté como dev o qa; con !app seleccionamos objetos sin Label key app.
Aunque ya no es un método recomendado, un ReplicationController es un controller que asegura que un número específico de replicas de un Pod esté funcionando en un momento dado. Generalmente, no desplegamos un Pod de forma independiente, ya que no podría reiniciarse por sí mismo si se terminara por error. El método recomendado es utilizar algún tipo de contolador de replicación para crear y gestionar los Pods.
El controlador por defecto es un Deployment que configura un ReplicaSet para gestionar el ciclo de vida de los Pods.
Un ReplicaSet es la próxima generación de ReplicationController.
Los ReplicaSets soportan tanto selectors basados en igualdad como en conjuntos, mientras que los ReplicationControllers sólo soportan selectores basados en igualdad. Actualmente, esta es la única diferencia.
Con la ayuda del ReplicaSet, podemos escalar el número de Pods que ejecutan una imagen específica de la aplicación del contenedor. La escalada puede ser realizada manualmente o a través del uso de un autoscalers.
Ejemplo de ReplicaSet, donde hemos establecido el número de replicas a 3 para un Pod.
Continuando con el mismo ejemplo, supongamos que uno de los Pods se ve obligado a terminar (debido a la insuficiencia de recursos, el tiempo de espera, etc.), y el estado actual ya no se corresponde con el estado deseado.
El ReplicaSet detectará que el estado actual ya no coincide con el estado deseado, y creará un Pod adicional, asegurando así que el estado actual coincide con el estado deseado.
Los ReplicaSets pueden ser usados independientemente como controllers de Pod, pero sólo ofrecen un conjunto limitado de características.
Un conjunto de características complementarias son proporcionadas por Deployments, los controladores recomendados para la orquestación de Pods. Los Deployments gestiona la creación, eliminación y actualización de los Pods, crea automáticamente un ReplicaSet, que luego crea un Pod. No hay necesidad de administrar los ReplicaSets y los Pods por separado, el Deployment los administrará en nuestro nombre.
Los Deployments proporcionan actualizaciones declarativas de los Pods y los ReplicaSets.
El DeploymentController es parte del administrador de controladores del Master Node, y asegura que el estado actual siempre coincida con el estado deseado. Permite actualizaciones y degradaciones de aplicaciones sin problemas a través de despliegues y rollbacks, y gestiona directamente sus ReplicaSets para el escalado de las aplicaciones.
En el siguiente ejemplo, un nuevo Deployment crea un ReplicaSet A que luego crea 3 Pods, con cada Pods Template configurada para ejecutar una imagen de contenedor nginx:1.7.9. En este caso, el ReplicaSet A se asocia con nginx:1.7.9 representando un estado del Deployment. Este estado particular se registra como la Revisión 1.
Ahora, en el Deployment, cambiamos la Pdos Template y actualizamos la imagen del contenedor de nginx:1.7.9 a nginx:1.9.1. El Deployment dispara un nuevo ReplicaSet B para la nueva imagen del contenedor versionada 1.9.1 y esta asociación representa un nuevo estado registrado del Deployment. Transicionará los dos ReplicaSets, desde el ReplicaSet A con 3 Pods versionados 1.7.9 al nuevo ReplicaSet B con 3 nuevos Pods versionados 1.9.1, o desde la Revisión 1 a la Revisión 2, es una actualización continua del Deployment.
Un rolling update se activa cuando actualizamos Pods Template para un despliegue. Operaciones como escalar o etiquetar el despliegue no activan una rolling update, por lo tanto no cambian el número de revisión.
Una vez que se haya completado el rolling update, el Deployment mostrará tanto las réplicas de los conjuntos A y B, donde A se escala a 0 Pods, y B se escala a 3 Pods. Así es como el Deployment registra sus ajustes de configuración de estado previo, como Revisiones.
Una vez que el ReplicaSet B y sus 3 Pods versionados 1.9.1 están listos, el Deployment comienza a gestionarlos activamente. Sin embargo, el Deployment mantiene sus estados de configuración previos guardados como Revisiones que juegan un factor clave en la capacidad de rollback del Deployment - regresando a un estado de configuración previo conocido. En nuestro ejemplo, si el rendimiento del nuevo nginx:1.9.1 no es satisfactorio, el Deployment puede ser retrocedido a una Revisión previa, en este caso de la Revisión 2 a la Revisión 1 ejecutando nginx:1.7.9.
Si varios usuarios y equipos utilizan el mismo clúster de Kubernetes podemos dividir el clúster en subclústeres virtuales utilizando Namespaces. Los nombres de los recursos/objetos creados dentro de un Namespace son únicos, pero no en los Namespaces del cluster.
Para listar todos los Namespaces, podemos ejecutar el siguiente comando:
$ kubectl get namespaces
NAME STATUS AGE
default Active 11h
kube-node-lease Active 11h
kube-public Active 11h
kube-system Active 11h
Generalmente, Kubernetes crea cuatro Namespaces por defecto:
- kube-system: Contiene los objetos creados por el sistema, principalmente los agentes del plano de control.
- kube-public: Es un namespace inseguro y legible por cualquiera, utilizado para la exposición de información pública no sensible sobre el cluster.
- kube-node-lease:
- Es el namespace que tiene un registro de la salud de los nodos, interactua con kubelet.
En versiones anteriores se utilizaba NodeStatus para comprobar la vida de los mismos pero actualmente se realiza esa comprobación por medio de Node Lease, y este controlador es el asociado a ese namespace.
- default: Contiene los objetos y recursos creados por los administradores y desarrolladores.
Con las Resource Quotas, podemos dividir los recursos del clúster dentro de Namespaces.
Cada solicitud de API que llega al API Server tiene que pasar por tres etapas diferentes antes de ser aceptada por el servidor y actuar en consecuencia:
- Authentication.
- Authorization.
- Admission Control stages of Kubernetes API requests.
Para acceder y administrar cualquier recurso u objeto de Kubernetes en el clúster, necesitamos acceder a un punto final de la API específico en el servidor de la API. Cada solicitud de acceso pasa por las tres etapas siguientes:
- Authentication: Inicia la sesión de un usuario.
- Authorization: Autoriza las solicitudes de API añadidas por el usuario conectado.
- Admission Control: Módulos de software que pueden modificar o rechazar las solicitudes en base a algunas comprobaciones adicionales, como una Quota preestablecida.
La siguiente imagen muestra las etapas anteriores:
Kubernetes no tiene un objeto llamado USER, ni almacena nombres de usuario u otros detalles relacionados en su almacén de objetos. Sin embargo, incluso sin eso, los Kubernetes pueden utilizar los nombres de usuario para el control de acceso y el registro de solicitudes, que exploraremos en este capítulo.
Kubernetes tiene dos tipos de usuarios:
- Normal Users Se gestionan fuera del clúster de Kubernetes a través de servicios independientes como certificados de usuario/cliente, un archivo con nombres de usuario/contraseñas, cuentas de Google, etc.
- Service Accounts Con los usuarios de la Service Account, los procesos del cluster se comunican con el API Server para realizar diferentes operaciones. La mayoría de los ServiceAccount se crean automáticamente a través del API Server, pero también pueden crearse manualmente. Los usuarios de Service Account están vinculados a un Namespace determinado y montan las credenciales respectivas para comunicarse con el API Server como Secrets.
Si se configuran correctamente, kubernetes también pueden admitir anonymous request, junto con las solicitudes de los Normal Users y Service Accounts. También se admite la suplantación de identidad de un usuario para que éste pueda actuar como otro usuario, una función útil para los administradores a la hora de solucionar problemas con las políticas de autorización.
Para la autenticación, Kubernetes utiliza diferentes módulos de autenticación:
- Client Certificates Para habilitar la autenticación de certificados de cliente, necesitamos hacer referencia a un archivo que contenga una o más autoridades de certificación pasando la opción --client-ca-file=SOMEFILE al API Server. Las autoridades de certificación mencionadas en el archivo validarían los certificados de cliente presentados al API Server.
- Static Token File Podemos pasar un archivo que contenga tokens portadores predefinidos con la opción --token-auth-file=SOMEFILE al API Server. Actualmente, estos tokens durarían indefinidamente, y no pueden ser cambiados sin reiniciar el API Server.
- Bootstrap Tokens Esta característica se encuentra actualmente en estado beta y se utiliza principalmente para el bootstrapping de un nuevo cluster de Kubernetes.
- Static Password File Es similar al Static Token File. Podemos pasar un archivo que contenga los detalles básicos de autenticación con la opción --basic-auth-file=SOMEFILE. Estas credenciales durarían indefinidamente, y las contraseñas no pueden ser cambiadas sin reiniciar el API Server.
- Service Account Tokens Se trata de un autentificador habilitado automáticamente que utiliza fichas al portador firmadas para verificar las solicitudes. Estos tokens se adjuntan a los Pods mediante el ServiceAccount Admission Controller, que permite a los procesos del grupo hablar con el API Server.
- OpenID Connect Tokens OpenID Connect nos ayuda a conectar con proveedores de OAuth2, como Azure Active Directory, Salesforce, Google, etc., para descargar la autenticación a servicios externos.
- Webhook Token Authentication Con la autenticación basada en Webhook, la verificación de los tokens portadores puede ser descargada a un servicio remoto.
- Authenticating Proxy Si queremos programar una lógica de autenticación adicional, podemos usar un proxy de autenticación.
Podemos habilitar múltiples autenticadores, y el primer módulo para autenticar con éxito la solicitud provoca un cortocircuito en la evaluación. Para tener éxito, debemos habilitar al menos dos métodos: el Service Account Tokens Authenticator y un User Authenticators.
Después de una autenticación exitosa, los usuarios pueden enviar las solicitudes de la API para realizar diferentes operaciones. A continuación, esas solicitudes de API son autorizadas por kubernetes mediante diversos módulos de autorización.
Algunos de los atributos de las solicitudes de la API que son revisados por los Kubernetes incluyen usuario, grupo, extra, recurso o namespaces, etc. A continuación, estos atributos se evalúan en función de las políticas. Si la evaluación es satisfactoria, entonces la solicitud será permitida, de lo contrario será denegada. De manera similar al paso de autenticación, la autorización tiene múltiples módulos/autorizadores. Se puede configurar más de un módulo para un grupo de Kubernetes, y cada módulo se comprueba en secuencia. Si algún autorizador aprueba o rechaza una solicitud, entonces esa decisión se devuelve inmediatamente.
Módulos de autorización (Parte 1):
-
Node Authorizer La autorización de Nodes es un modo de autorización con fines especiales que autoriza específicamente las solicitudes de API realizadas por kubelets. Autoriza las operaciones de lectura de kubelets para Services, Endpoints, Nodes, etc., y escribe operaciones para Nodes, Pods, eventos, etc.
-
Attribute-Based Access Control (ABAC) Authorizer Con el ABAC authorizer, Kubernetes concede acceso a las solicitudes de API, que combinan políticas con atributos. En el siguiente ejemplo, el usuario student sólo puede leer Pods en el Namespace lfs158.
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "student",
"namespace": "lfs158",
"resource": "pods",
"readonly": true
}
}
Para habilitar el ABAC Authorizer, necesitaríamos iniciar el API Server con la opción --authorization-mode=ABAC. También necesitaríamos especificar la política de autorización con --authorization-policy-file=PolicyFile.json.
- Webhook Authorizer Con el Webhook Authorizer, Kubernetes puede ofrecer decisiones de autorización a algunos servicios de terceros, que se devolverían verdaderas para una autorización exitosa, y falsas para un fracaso. Para habilitar el Webhook Authorizer, necesitamos iniciar el API Server con la opción --authorization-webhook-config-file=SOME_FILENAME, donde SOME_FILENAME es la configuración del servicio de autorización remota.
Módulos de autorización (Parte 2):
- Role-Based Access Control (RBAC) Authorizer En general, con el RBAC podemos regular el acceso a los recursos en base a los roles de los usuarios individuales. En los Kubernetes, podemos tener diferentes roles que pueden ser adjuntados a temas como usuarios, services accounts, etc. Al crear los roles, restringimos el acceso a los recursos mediante operaciones específicas, como create, get, update, patch, etc. Estas operaciones se denominan verbos.
En RBAC, podemos crear dos tipos de roles:
* __Role__
Define un tipo de recusros y operaciones que pueden asignarse a un usuario o grupo a nive de namespace
* __ClusterRole__
Define un tipo de recusros y operaciones que pueden asignarse a un usuario o grupo a nive de cluster
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: lfs158
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
El anterior role sólo tiene acceso a leer los Pods de lfs158 Namespace. Una vez creado el rol, podemos vincularlo a usuarios con RoleBinding.
Hay dos tipos de RoleBindings:
* __RoleBinding__
Nos permite vincular a los usuarios al mismo _Namespace_ que un _Role_. También podríamos referirnos a un _Role_ del Cluster en RoleBinding, lo cual otorgaría permisos a los recursos del _Namespace_ definidos en el _Role_ del Cluster dentro del _Namespace_ de RoleBinding.
* __ClusterRoleBinding__
Nos permite conceder acceso a los recursos a nivel de grupo y a todos los _Namespace_.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: pod-read-access
namespace: lfs158
subjects:
- kind: User
name: student
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Damos acceso al usuario student para leer los Pods de lfs158 Namespace.
Para habilitar el autorizador RBAC, necesitaríamos iniciar el API Server con la opción --authorization-mode=RBAC. Con el autorizador RBAC, configuramos dinámicamente las políticas. Para obtener más detalles, consulte la documentación de Kubernetes.
El Admission Control se utiliza para especificar las políticas de control de acceso granular, que incluyen la autorización de contenedores privilegiados, la comprobación de la cuota de recursos, etc. Forzamos estas políticas usando diferentes Admission Control, como ResourceQuota, DefaultStorageClass, AlwaysPullImages, etc. Sólo entran en vigor después de que las solicitudes de la API son autenticadas y autorizadas.
Para utilizar los Admission Control, debemos iniciar el API Server de Kubernetes con los plugins --enable-admission-plugins, que toma una lista ordenada y delimitada por comas de los nombres de los controllers:
--enable-admission-plugins=NamespaceLifecycle,ResourceQuota,PodSecurityPolicy,DefaultStorageClass
Kubernetes tiene algunos controladores de admisión habilitados por defecto.
Esta guía de ejercicios asume el siguiente entorno, que por defecto utiliza el certificado y la clave de /var/lib/minikube/certs/, y el modo RBAC para la autorización:
- Minikube v1.0.1
- Kubernetes v1.14.1
- Docker 18.06.3-ce
Iniciar Minikube:
$ minikube start
Ver el contenido del archivo de configuración del cliente kubectl, observando el único contexto de minikube y el único usuario de minkube, creado por defecto:
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/student/.minikube/ca.crt
server: https://192.168.99.100:8443
name: minikube
contexts:
- context:
cluster: minikube
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /home/student/.minikube/client.crt
client-key: /home/student/.minikube/client.key
Create lfs158 namespace:
$ kubectl create namespace lfs158
namespace/lfs158 created
Create rbac directory and cd into it:
$ mkdir rbac
$ cd rbac/
Create a private key for the student user with openssl tool, then create a certificate signing request for the student user with openssl tool:
~/rbac$ openssl genrsa -out student.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
.................................................+++++
.........................+++++
e is 65537 (0x010001)
~/rbac$ openssl req -new -key student.key -out student.csr -subj "/CN=student/O=learner"
Create a YAML configuration file for a certificate signing request object, and save it with a blank value for the request field:
~/rbac$ vim signing-request.yaml
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: student-csr
spec:
groups:
- system:authenticated
request: <assign encoded value from next cat command>
usages:
- digital signature
- key encipherment
- client auth
View the certificate, encode it in base64, and assign it to the request field in the signing-request.yaml file:
~/rbac$ cat student.csr | base64 | tr -d '\n'
LS0tLS1CRUd...1QtLS0tLQo=
~/rbac$ vim signing-request.yaml
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: student-csr
spec:
groups:
- system:authenticated
request: LS0tLS1CRUd...1QtLS0tLQo=
usages:
- digital signature
- key encipherment
- client auth
Create the certificate signing request object, then list the certificate signing request objects. It shows a pending state:
~/rbac$ kubectl create -f signing-request.yaml
certificatesigningrequest.certificates.k8s.io/student-csr created
~/rbac$ kubectl get csr
NAME AGE REQUESTOR CONDITION
student-csr 27s minikube-user Pending
Approve the certificate signing request object, then list the certificate signing request objects again. It shows both approved and issued states:
~/rbac$ kubectl certificate approve student-csr
certificatesigningrequest.certificates.k8s.io/student-csr approved
~/rbac$ kubectl get csr
NAME AGE REQUESTOR CONDITION
student-csr 77s minikube-user Approved,Issued
Extract the approved certificate from the certificate signing request, decode it with base64 and save it as a certificate file. Then view the certificate in the newly created certificate file:
~/rbac$ kubectl get csr student-csr -o jsonpath='{.status.certificate}' | base64 --decode > student.crt
~/rbac$ cat student.crt
-----BEGIN CERTIFICATE-----
MIIDGzCCA...
...
...NOZRRZBVunTjK7A==
-----END CERTIFICATE-----
Configure the student user's credentials by assigning the key and certificate:
~/rbac$ kubectl config set-credentials student --client-certificate=student.crt --client-key=student.key
User "student" set.
Create a new context entry in the kubectl client's configuration file for the student user, associated with the lfs158 namespace in the minikube cluster:
~/rbac$ kubectl config set-context student-context --cluster=minikube --namespace=lfs158 --user=student
Context "student-context" created.
View the contents of the kubectl client's configuration file again, observing the new context entry student-context, and the new user entry student:
~/rbac$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/student/.minikube/ca.crt
server: https://192.168.99.100:8443
name: minikube
contexts:
- context:
cluster: minikube
user: minikube
name: minikube
- context:
cluster: minikube
namespace: lfs158
user: student
name: student-context
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /home/student/.minikube/client.crt
client-key: /home/student/.minikube/client.key
- name: student
user:
client-certificate: /home/student/rbac/student.crt
client-key: /home/student/rbac/student.key
While in the default minikube context, create a new deployment in the lfs158 namespace:
~/rbac$ kubectl -n lfs158 create deployment nginx --image=nginx:alpine
deployment.apps/nginx created
From the new context student-context try to list pods. The attempt fails because the student user has no permissions configured for the student-context:
~/rbac$ kubectl --context=student-context get pods
Error from server (Forbidden): pods is forbidden: User "student" cannot list resource "pods" in API group "" in the namespace "lfs158"
The following steps will assign a limited set of permissions to the student user in the student-context.
Create a YAML configuration file for a pod-reader role object, which allows only get, watch, list actions in the lfs158 namespace against pod objects. Then create the role object and list it from the default minikube context, but from the lfs158 namespace:
~/rbac$ vim role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: lfs158
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
~/rbac$ kubectl create -f role.yaml
role.rbac.authorization.k8s.io/pod-reader created
~/rbac$ kubectl -n lfs158 get roles
NAME AGE
pod-reader 57s
Create a YAML configuration file for a rolebinding object, which assigns the permissions of the pod-reader role to the student user. Then create the rolebinding object and list it from the default minikube context, but from the lfs158 namespace:
~/rbac$ vim rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-read-access
namespace: lfs158
subjects:
- kind: User
name: student
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
~/rbac$ kubectl create -f rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/pod-read-access created
~/rbac$ kubectl -n lfs158 get rolebindings
NAME AGE
pod-read-access 23s
Now that we have assigned permissions to the student user, we can successfully list pods from the new context student-context.
~/rbac$ kubectl --context=student-context get pods
NAME READY STATUS RESTARTS AGE
nginx-77595c695-f2xmd 1/1 Running 0 7m41s
Aunque la arquitectura impulsada por los microservicios tiene por objeto desacoplar los componentes de una aplicación, los microservicios siguen necesitando agentes para vincularlos o agruparlos lógicamente y balancear la carga a los que forman parte de ese conjunto lógico.
Aprenderemos sobre los Services, utilizados para agrupar Pods para proporcionar puntos de acceso comunes del mundo exterior a las aplicaciones en contenedores. Aprenderemos sobre el demonio kube-proxy, que se ejecuta en cada Worker Node para proporcionar acceso a los servicios. También hablaremos del descubrimiento de servicios y de los tipos de servicios, que deciden el alcance del acceso a un servicio.
Para acceder a la aplicación, un usuario/cliente debe conectarse a los Pods. Como los Pods son de naturaleza efímera, los recursos como las direcciones IP que se le asignan no pueden ser estáticos. Los Pods pueden ser terminados abruptamente o reprogramados en base a los requerimientos existentes.
Tomemos, por ejemplo, un escenario en el que un usuario/cliente se conecta a un Pod utilizando su dirección IP.
Inesperadamente, el Pod al que está conectado el usuario/cliente se termina, y un nuevo Pod es creado por el controlador. El nuevo Pod tendrá una nueva dirección IP, que no será conocida automáticamente por el usuario/cliente del Pod anterior.
Para superar esta situación, Kubernetes proporciona una abstracción de nivel superior llamada Services, que agrupa lógicamente los Pods y define una política para acceder a ellos. Esta agrupación se realiza a través de Labels y Selectors.
En la siguiente representación gráfica, app es la Label key, frontend y db son los Label values para las diferentes Pods.
Usando los selectores app==frontend y app==db, agrupamos los Pods en dos conjuntos lógicos: uno con 3 Pods, y otro con un solo Pod.
Asignamos un nombre a la agrupación lógica, llamada Service. En nuestro ejemplo, creamos dos Servicios, frontend-svc, y db-svc, y tienen los Selectors app==frontend y app==db, respectivamente.
Los servicios pueden exponer Pods individuales, ReplicaSets, Deployments, DaemonSets y StatefulSets.
Un servicio se define como en el siguiente fichero:
kind: Service
apiVersion: v1
metadata:
name: frontend-svc
spec:
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 5000
En este ejemplo, estamos creando un Service de frontend-svc seleccionando todos los Pods que tienen el Label key=app establecida en value=frontend. Por defecto, cada Service recibe una dirección IP enrutada sólo dentro del clúster, conocida como ClusterIP. En nuestro ejemplo, tenemos 172.17.0.4 y 172.17.0.5 como ClusterIPs asignados a nuestros Servicios frontend-svc y db-svc, respectivamente.
El usuario/cliente se conecta ahora a un servicio a través de su ClusterIP, que reenvía el tráfico a uno de los Pods adjuntos a él. Un Service provee balanceo de carga por defecto mientras selecciona los Pods para el reenvío de tráfico.
Mientras que el Service reenvía el tráfico a los Pods, podemos seleccionar el targetPort en el Pod que recibe el tráfico. En nuestro ejemplo, el Service de frontend-svc recibe las solicitudes del usuario/cliente en el puerto 80 y luego reenvía estas solicitudes a uno de los Pods adjuntos en el targetPort 5000. Si el targetPort no está definido explícitamente, entonces el tráfico será reenviado a los Pods en el puerto en el que el Service recibe el tráfico.
Un conjunto lógico de la dirección IP de un Pod, junto con el targetPort se denomina Service endpoint. En nuestro ejemplo, el frontend-svc Service tiene 3 endpoints: 10.0.1.3:5000, 10.0.1.4:5000, y 10.0.1.5:5000. Los endpoints son creados y gestionados automáticamente por el Service, no por el administrador del clúster de Kubernetes.
Todos los Worker Node ejecutan un demonio llamado kube-proxy, que vigila el API Server en el Master Node para la adición y eliminación de Services y endpoints. En el ejemplo siguiente, para cada nuevo Service, en cada Node, el kube-proxy configura las reglas de iptables para capturar el tráfico para su ClusterIP y lo reenvía a uno de los Service's endpoints. Por lo tanto, cualquier Node puede recibir el tráfico externo y luego enrutarlo internamente en el cluster en base a las reglas iptables. Cuando se elimina el Service, el kube-proxy elimina las correspondientes reglas iptables en todos los Nodes también.
Como los Services son el principal modo de comunicación en K8s, necesitamos una forma de descubrirlos en tiempo de ejecución. Kubernetes admite dos métodos para descubrir los Services:
- Environment Variables: Tan pronto como el Pod se inicia en cualquier Worker Node, el demonio kubelet que se ejecuta en ese Node añade un conjunto de variables de entorno en el Pod para todos los Services activos. Por ejemplo, si tenemos un Service activo llamado redis-master, que expone el puerto 6379, y su ClusterIP es 172.17.0.6, entonces, en un Pod recién creado, podemos ver las siguientes variables de entorno:
REDIS_MASTER_SERVICE_HOST=172.17.0.6
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://172.17.0.6:6379
REDIS_MASTER_PORT_6379_TCP=tcp://172.17.0.6:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=172.17.0.6
Con esta solución, tenemos que ser cuidadosos al pedir nuestros Services, ya que los Pods no tendrán las variables de entorno establecidas para los Services que se crean después de que se creen los Pods.
- DNS Kubernetes tiene un complemento para el DNS, que crea un registro DNS para cada Service y su formato es my-svc.mi-nombreespacio.svc.cluster.local. Los Services dentro del mismo Namespace encuentran otros servicios sólo por su nombre. Si añadimos un Service redis-master en mi Namespace, todos los Pods en el mismo Namespace buscan el Service sólo por su nombre, redis-master. Los pods de otros Namespaces buscan el mismo Service añadiendo el respectivo Nampespace como sufijo, como redis-master.my-ns.
Esta es la solución más común y altamente recomendada. Por ejemplo, en la imagen de la sección anterior, hemos visto que se configura un DNS interno, que mapea nuestros Services frontend-svc y db-svc a 172.17.0.4 y 172.17.0.5, respectivamente.
Al definir un Service, también podemos elegir su ámbito de acceso. Podemos decidir si el Service:
- Es sólo accesible dentro del cluster.
- Es accesible desde el interior del cluster y el mundo exterior.
- Se traza un mapa de una entidad que reside dentro o fuera del cluster.
El alcance del acceso se decide por el ServiceType, que puede ser configurado al crear el Service.
ClusterIP es el ServiceType por defecto. Un Service recibe una dirección IP virtual, conocida como su ClusterIP. Esta dirección IP virtual se utiliza para comunicarse con el Service y sólo se puede acceder a ella dentro del clúster.
Con el NodePort ServiceType, además de un ClusterIP, un puerto alto, elegido dinámicamente del rango predeterminado 30000-32767, se asigna al Service respectivo, de todos los Worker Node. Por ejemplo, si el NodePort mapeado es 32233 para el frontend-svc, entonces, si nos conectamos a cualquier Worker Node en el puerto 32233, el Node redirigirá todo el tráfico al ClusterIP asignado - 172.17.0.4. Si preferimos un número de puerto alto específico en su lugar, entonces podemos asignar ese número de puerto alto al NodePort del rango por defecto.
El NodePort ServiceType es útil cuando queremos que nuestros servicios sean accesibles desde el mundo exterior. El usuario final se conecta a cualquier Worker Node en el puerto alto especificado, que envía la solicitud internamente al ClusterIP del Service, y luego la solicitud se envía a las aplicaciones que se ejecutan dentro del clúster. Para acceder a varias aplicaciones del mundo externo, los administradores pueden configurar un proxy inverso, es decir, un Ingress, y definir reglas que apunten a los Services dentro del clúster.
Con el LoadBalancer ServiceType:
- NodePort y ClusterIP se crean automáticamente, y el balanceador de carga externo se dirigirá a ellos.
- El Service está expuesto en un puerto estático en cada Worker Node.
- El Service se expone externamente utilizando la función de balanceo de carga del proveedor de nubes subyacente.
El LoadBalancer ServiceType sólo funcionará si la infraestructura subyacente soporta la creación automática de Load Balancers y tiene el respectivo soporte en los Kubernetes, como es el caso de la plataforma de Google Cloud y AWS. Si no se configura tal característica, el campo de dirección IP del LoadBalancer no se rellena, y el Service funcionará de la misma manera que un Service de tipo NodePort.
Un Service puede ser mapeado a una ExternalIP si puede enrutar a uno o más de los Worker Nodes. El tráfico que entra en el cluster con el ExternalIP (como IP de destino) en el puerto de Service, se enruta a uno de los Service endpoints. Este tipo de servicio requiere un proveedor de nube externo como Google Cloud Platform o AWS.
Tenga en cuenta que las ExternalIP no son gestionadas por Kubernetes. El administrador del clúster tiene que configurar el enrutamiento que asigna la dirección de__ ExternalIP__ a uno de los Nodes.
ExternalName es un ServiceType especial, que no tiene Selectors y no define ningún endpoint. Cuando se accede dentro del cluster, devuelve un registro CNAME de un Service configurado externamente.
El principal caso de uso de este ServiceType es hacer que los Services configurados externamente como my-database.example.com estén disponibles para las aplicaciones dentro del clúster. Si el Service definido externamente reside dentro del mismo espacio de nombres, el uso del nombre my-database lo haría disponible para otras aplicaciones y servicios dentro de ese mismo Namespace.
## Chapter 11. Deploying a Stand-Alone Application
En este capítulo, aprenderemos a desplegar una aplicación utilizando el Dashboard (Kubernetes WebUI) y la Command Line Interface (CLI). También expondremos la aplicación con un Servicio de tipo NodePort, y accederemos a ella desde el mundo exterior.
En las siguientes secciones, aprenderemos cómo desplegar un servidor web nginx usando la imagen nginx:alpine.
Inicie Minikube y verifique que está funcionando Ejecute este comando primero:
$ minikube start
Deje pasar varios minutos para que comience el Minikube, luego verifique el estado del Minikube:
$ minikube status
host: Running
kubelet: Running
apiserver: Running
kubectl: Correctly Configured: pointing to minikube-vm at 192.168.99.100
Para acceder a la Web IU de Kubernetes, necesitamos ejecutar el siguiente comando:
$ minikube dashboard
Al ejecutar este comando se abrirá un navegador con la interfaz web de Kubernetes, que podemos utilizar para gestionar aplicaciones en contenedores. Por defecto, el Dashboard está conectado Namespace Default.
NOTA: Si el navegador no abre una pestaña nueva con el Dashboard, revise la salida del terminal, mostará algo similar a esto:
http://127.0.0.1:37751/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/
Despliegue un servidor web usando la imagen nginx:alpine Desde el tablero de mandos, haga clic en la pestaña +CREATE en la esquina superior derecha del tablero de mandos. Eso abrirá la interfaz de creación como se ve abajo:
A partir de eso, podemos crear una aplicación usando una configuración de datos de archivo YAML/JSON válida, o manualmente desde la sección CREAR UNA APP. Haga clic en la pestaña CREAR UNA APP y proporcione los siguientes detalles de la aplicación:
- El nombre de la aplicación es webserver.
- La imagen del Docker a usar es nginx:alpine, donde alpine es la etiqueta de la imagen.
- La cuenta de la réplica, o el número de Pods, es 3.
- No hay Service, como lo crearemos más tarde.
Si hacemos clic en Mostrar Opciones Avanzadas, podemos especificar opciones como Labels, Namespaces, Environment Variables, etc. Por defecto, la etiqueta de la aplicación está configurada con el nombre de la aplicación. En nuestro ejemplo k8s-app:webserver Label está establecida para todos los objetos creados por este Deployment: Pods y Servicios (cuando se exponen).
Al hacer clic en el botón de Deployment, activamos el despliegue. Como se esperaba, el servidor web de despliegue creará un ReplicaSet (servidor web-74d8bd488f), que eventualmente creará tres Pods (servidor web-74d8bd488f-xxxxx).
NOTA: Agregue la URL completa en el campo Imagen del Contenedor docker.io/library/nginx:alpine si se encuentra algún problema con el simple nombre de la imagen nginx:alpine (o utilice la URL k8s.gcr.io/nginx:alpine si funciona en su lugar).
Una vez creado el Despliegue del servidor web, podemos utilizar el panel de navegación de recursos del lado izquierdo del Tablero para mostrar los detalles de los Deployment, ReplicaSet y Pods en el Namespace. Los recursos mostrados por el Dashboard coinciden con los recursos individuales mostrados desde el CLI a través de kubectl.
- Listar los Despliegues
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
webserver 3/3 3 3 9m
- List the ReplicaSets
$ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
webserver-74d8bd488f 3 3 3 9m
- List the Pods
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
webserver-74d8bd488f-dwbzz 1/1 Running 0 9m
webserver-74d8bd488f-npkzv 1/1 Running 0 9m
webserver-74d8bd488f-wvmpq 1/1 Running 0 9m
Anteriormente, hemos visto que las Labels y los Selectors juegan un papel importante en la agrupación de un subconjunto de objetos sobre los que podemos realizar operaciones. A continuación, vamos a examinarlos más de cerca.
- Mirar los detalles de un pod Podemos ver los detalles de un objeto usando el comando kubectl describe. En el siguiente ejemplo, puedes ver la descripción de un Pod:
$ kubectl describe pod webserver-74d8bd488f-dwbzz
Name: webserver-74d8bd488f-dwbzz
Namespace: default
Priority: 0
Node: minikube/10.0.2.15
Start Time: Wed, 15 May 2019 13:17:33 -0500
Labels: k8s-app=webserver
pod-template-hash=74d8bd488f
Annotations: <none>
Status: Running
IP: 172.17.0.5
Controlled By: ReplicaSet/webserver-74d8bd488f
Containers:
webserver:
Container ID: docker://96302d70903fe3b45d5ff3745a706d67d77411c5378f1f293a4bd721896d6420
Image: nginx:alpine
Image ID: docker-pullable://nginx@sha256:8d5341da24ccbdd195a82f2b57968ef5f95bc27b3c3691ace0c7d0acf5612edd
Port: <none>
State: Running
Started: Wed, 15 May 2019 13:17:33 -0500
Ready: True
Restart Count: 0
...
El comando de kubectl describe muestra muchos más detalles de un Pod. Por ahora, sin embargo, nos centraremos en el campo Labels, donde tenemos una Label configurada como k8s-app=webserver.
- Lista los Pods junto con sus Labels. Con la opción -L del comando kubectl get pods, añadimos columnas extra en la salida para listar los pods con Label keys y Label Values. En el siguiente ejemplo, estamos listando Pods con las claves Label k8s-app y label2:
$ kubectl get pods -L k8s-app,label2
NAME READY STATUS RESTARTS AGE K8S-APP LABEL2
webserver-74d8bd488f-dwbzz 1/1 Running 0 14m webserver
webserver-74d8bd488f-npkzv 1/1 Running 0 14m webserver
webserver-74d8bd488f-wvmpq 1/1 Running 0 14m webserver
Todos los Pods están listados, ya que cada Pod tiene la Label key k8s-app con el value webserver. Podemos ver eso en la columna K8S-APP. Como ninguno de los Pods tiene la Label key label2, no se listan valores en la columna LABEL2.
- Selecciona las Pods con una etiqueta determinada Para usar un selector con el comando kubectl get pods, podemos usar la opción -l. En el siguiente ejemplo, estamos seleccionando todos los Pods que tienen la key k8s-app establecida como value webserver:
$ kubectl get pods -l k8s-app=webserver
NAME READY STATUS RESTARTS AGE
webserver-74d8bd488f-dwbzz 1/1 Running 0 17m
webserver-74d8bd488f-npkzv 1/1 Running 0 17m
webserver-74d8bd488f-wvmpq 1/1 Running 0 17m
En el ejemplo anterior, enumeramos todos los Pods que creamos, ya que todos ellos tienen la clave Label k8s-app establecida como value webserver.
Intenta usar k8s-app=webserver1 como el Selector:
$ kubectl get pods -l k8s-app=webserver1
No resources found.
Para desplegar una aplicación usando el CLI, primero borremos el Deployment que creamos anteriormente.
Borrar el Deployment que creamos anteriormente Podemos eliminar cualquier objeto usando kubectl delete command. A continuación, estamos eliminando el despliegue del webserver que creamos anteriormente con el Dashboard:
$ kubectl delete deployments webserver
deployment.extensions "webserver" deleted
Eliminado el Deployment, también eliminamos el Replicaset y el Pod asociado:
$ kubectl get replicasets
No resources found.
$ kubectl get pods
No resources found.
Crear un archivo de configuración YAML con los detalles del despliegue Vamos a crear el archivo webserver.yaml con el siguiente contenido:
apiVersion: apps/v1
kind: Deployment
metadata:
name: webserver
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
Usando kubectl, crearemos el despliegue desde el archivo de configuración de YAML. Usando la opción -f con el comando kubectl create, podemos pasar un archivo YAML como especificación de un objeto, o una URL a un archivo de configuración de la web. En el siguiente ejemplo, estamos creando un Deployment del webserver:
$ kubectl create -f webserver.yaml
deployment.apps/webserver created
Esto también creará un ReplicaSet y Pods, como se define en el archivo de configuración de YAML.
$ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
webserver-b477df957 3 3 3 45s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
webserver-b477df957-7lnw6 1/1 Running 0 2m
webserver-b477df957-j69q2 1/1 Running 0 2m
webserver-b477df957-xvdkf 1/1 Running 0 2m
En un capítulo anterior, exploramos diferentes ServiceTypes. Con los ServiceTypes podemos definir el método de acceso para un Servicio. Para un NodePort ServiceType, Kubernetes abre un puerto estático en todos los nodos trabajadores. Si nos conectamos a ese puerto desde cualquier nodo, se nos envía al ClusterIP del Servicio. A continuación, usemos el NodePort ServiceType mientras creamos un Servicio.
Creamos un archivo webserver-svc.yaml con el siguiente contenido:
apiVersion: v1
kind: Service
metadata:
name: web-service
labels:
run: web-service
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
selector:
app: nginx
Using kubectl, create the Service:
$ kubectl create -f webserver-svc.yaml
service/web-service created
Un método más directo de crear un Servicio es exponiendo el Deployment creado previamente (este método requiere un Deployment existente).
Expose a Deployment with the kubectl expose command:
$ kubectl expose deployment webserver --name=web-service --type=NodePort
service/web-service exposed
Listar los Services:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
web-service NodePort 10.110.47.84 <none> 80:31074/TCP 22s
Nuestro web-service está ahora creado y su ClusterIP es 10.110.47.84. En la sección PORT(S), vemos un mapeo de 80:31074, lo que significa que hemos reservado un puerto estático 31074 en el nodo. Si nos conectamos al nodo en ese puerto, nuestras peticiones serán enviadas al ClusterIP del puerto 80.
No es necesario crear el despliegue primero, y el servicio después. Pueden ser creados en cualquier orden. Un Servicio encontrará y conectará los Pods basados en el Selector.
Para obtener más detalles sobre el Servicio, podemos usar el comando kubectl describe, como en el siguiente ejemplo:
$ kubectl describe service web-service
Name: web-service
Namespace: default
Labels: run=web-service
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP: 10.110.47.84
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 31074/TCP
Endpoints: 172.17.0.4:80,172.17.0.5:80,172.17.0.6:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
web-service utiliza app=nginx como un Selector para agrupar lógicamente nuestros tres Pods, que están listados como puntos finales. Cuando una solicitud llega a nuestro Service, será atendida por uno de los Pods listados en la sección de endpoints.
Nuestra aplicación se está ejecutando en el nodo VM de Minikube. Para acceder a la aplicación desde nuestra estación de trabajo, primero obtengamos la dirección IP de la VM de Minikube:
$ minikube ip
192.168.99.100
Ahora, abra el navegador y acceda a la aplicación en el 192.168.99.100 en el puerto 31074.
También podríamos ejecutar el siguiente comando de minicubo que muestra la aplicación en nuestro navegador:
$ minikube service web-service
Opening kubernetes service default/web-service in default browser...
Podemos ver la página de bienvenida de Nginx, mostrada por la aplicación del servidor web que se ejecuta dentro de los Pods creados. Nuestras peticiones podrían ser atendidas por cualquiera de los tres puntos finales agrupados lógicamente por el Service, ya que el Service actúa como un Load Balancer delante de sus endpoints.
Aunque las aplicaciones en contenedores están programadas para ejecutarse en Pods en los Nodes de nuestro cluster, a veces las aplicaciones pueden no responder o pueden retrasarse durante el inicio. La implementación Liveness y Readiness Probes permite que kubelet controle la salud de la aplicación que se ejecuta en el interior de un contenedor del Pod y forzar el reinicio de una aplicación que no responde. Cuando se definen tanto Readiness y Liveness Probes, se recomienda dejar el tiempo suficiente para que Readiness Live pueda fallar unas cuantas veces antes de un pase, y sólo entonces comprobar Liveness Probe. Si Readiness y Liveness Probes se solapan, puede existir el riesgo de que el contenedor nunca alcance el estado de listo.
Si un contenedor de un Pod se está ejecutando, pero la aplicación que se está ejecutando dentro de este contenedor no responde a nuestras peticiones, entonces ese contenedor no nos sirve. Este tipo de situación puede ocurrir, por ejemplo, debido a un bloqueo de la aplicación o a la presión de la memoria. En tal caso, se recomienda reiniciar el contenedor para que la aplicación esté disponible.
En lugar de reiniciarlo manualmente, podemos usar Liveness Probe. Liveness Probe comprueba la salud de una aplicación, y si el control de salud falla, kubelet reinicia el contenedor afectado automáticamente.
Liveness Probe se pueden establecer definiendo:
- Liveness command.
- Liveness HTTP request.
- TCP Liveness Probe.
En el siguiente ejemplo, estamos comprobando la existencia de un archivo /tmp/healthy:
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
La existencia del archivo /tmp/healthy está configurado para ser comprobado cada 5 segundos usando el parámetro periodSeconds. El parámetro initialDelaySeconds pide al kubelet que espere 5 segundos antes de la primera sonda. Al ejecutar el argumento de la línea de comandos al contenedor, primero crearemos el archivo /tmp/healthy, y luego lo eliminaremos después de 30 segundos. El borrado del archivo provocaría un fallo de salud, y nuestro Pod se reiniciaría.
En el siguiente ejemplo, el kubelet envía la petición HTTP GET al punto final /healthz de la aplicación, en el puerto 8080. Si eso devuelve un fallo, entonces la kubelet reiniciará el contenedor afectado; de lo contrario, consideraría que la aplicación está viva.
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
Con el TCP Liveness Probe, el kubelet intenta abrir el TCP Socket al contenedor que está ejecutando la aplicación. Si tiene éxito, la aplicación se considera saludable, de lo contrario el kubelet lo marcaría como no saludable y reiniciaría el contenedor afectado.
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
A veces, las aplicaciones tienen que cumplir ciertas condiciones antes de poder servir al tráfico. Estas condiciones incluyen asegurarse de que el servicio que depende está listo, o reconocer que es necesario cargar un gran conjunto de datos, etc. En tales casos, utilizamos Readiness Probes y esperamos a que se produzca una determinada condición. Sólo entonces, la aplicación puede servir al tráfico.
Un Pod con contenedores que no informan del estado de preparación no recibirá tráfico de los Services.
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
En el modelo de negocios actual, los datos son el activo más valioso para muchas empresas y emprendimientos. En un clúster de Kubernetes, los contenedores en Pods pueden ser productores o consumidores de datos. Mientras que se espera que algunos datos de los contenedores sean transitorios y no se espera que sobrevivan a un Pod, otras formas de datos deben sobrevivir al Pod para poder ser agregados y posiblemente cargados en los motores de análisis. Kubernetes debe proporcionar recursos de almacenamiento para proporcionar datos que serán consumidos por los contenedores o para almacenar los datos producidos por los contenedores. Kubenetes utilizan Volumes de varios tipos y algunas otras formas de recursos de almacenamiento para la gestión de los datos de los contenedores. En este capítulo, hablaremos de los objetos PersistentVolume y PersistentVolumeClaim, que nos ayudan a adjuntar volúmenes de almacenamiento persistente a los contenedores.
Como sabemos, los contenedores que corren en los Pods son efímeros por naturaleza. Todos los datos almacenados dentro de un contenedor se borran si el contenedor se corrompe. El kubelet lo reiniciará, lo que significa que no tendrá ninguno de los datos antiguos.
Para superar este problema, Kubernetes utiliza Volumes. Un Volumen es esencialmente un directorio respaldado por un medio de almacenamiento. El medio de almacenamiento, el contenido y el modo de acceso están determinados por el tipo de Volumen.
En los kubernetes, un Volume se adjunta a un Pod y puede ser compartido entre los contenedores de ese Pod. El Volume tiene la misma vida útil que el Pod, y sobrevive a los contenedores del Pod, lo que permite que los datos se conserven a través de los reinicios de los contenedores.
Un directorio que está montado dentro de un Pod está respaldado por el Tipo de Volumen subyacente. Un Tipo de Volumen decide las propiedades del directorio, como el tamaño, el contenido, los modos de acceso por defecto, etc. Algunos ejemplos de Tipos de Volumen son:
- emptyDir Se crea un volumen vacío para el Pod tan pronto como se programe en el Worker Node. La vida del Volume está estrechamente ligada al Pod. Si el Pod se termina, el contenido de emptyDir se borra para siempre.
- hostPath Con el hostPath Volume Type, podemos compartir un directorio desde el host hasta el Pod. Si el Pod se termina, el contenido del Volume sigue estando disponible en el host.
- gcePersistentDisk Con el gcePersistentDisk Volume Type, podemos montar un disco persistente de Google Compute Engine (GCE) en un Pod.
- awsElasticBlockStore Con el tipo de volumen de awsElasticBlockStore, podemos montar un volumen de AWS EBS en un Pod.
- azureDisk Con azureDisk podemos montar un Microsoft Azure Data Disk en un Pod.
- azureFile Con azureFile podemos montar un volumen de Microsoft Azure File en un Pod.
- cephfs Con los Cephfs, un volumen de CephFS existente puede ser montado en un Pod. Cuando un Pod termina, el volumen se desmonta y el contenido del volumen se conserva.
- nfs Con NFS, podemos montar una parte de NFS en un Pod.
- iscsi Con ISCSI, podemos montar una acción iSCSI en una Pod.
- secreto Con el tipo de volumen secreto, podemos pasar información sensible, como contraseñas, a las Pod. Veremos un ejemplo en un capítulo posterior.
- configMap Con los objetos ConfigMap, podemos proporcionar datos de configuración, o comandos de shell y argumentos en un Pod.
- persistentVolumeClaim Podemos adjuntar un volumen persistente a un Pod usando un persistentVolumeClaim.
En un entorno típico de TI, el almacenamiento es manejado por los administradores de almacenamiento/sistemas. El usuario final sólo recibirá instrucciones para utilizar el almacenamiento, pero no participa en la gestión del almacenamiento subyacente.
En el mundo contenedorizado, nos gustaría seguir reglas similares, pero se convierte en un reto, dados los muchos tipos de Volume que hemos visto anteriormente. Kubernetes resuelve este problema con el subsistema PersistentVolume (PV), que proporciona API para que los usuarios y administradores gestionen y consuman el almacenamiento persistente. Para gestionar el Volume, utiliza el tipo de recurso de la API PersistentVolume, y para consumirlo, utiliza el tipo de recurso de la API PersistentVolumeClaim.
Un volumen persistente es un almacenamiento conectado a la red en el clúster, que es provisto por el administrador.
Los PersistentVolumes pueden ser aprovisionados dinámicamente en base al recurso StorageClass. Una StorageClass contiene aprovisionadores y parámetros predefinidos para crear un PersistentVolume. Mediante PersistentVolumeClaims, un usuario envía la solicitud de creación de PV dinámicos, que se cablea al recurso StorageClass.
Algunos de los tipos de volumen que admiten la administración del almacenamiento mediante PersistentVolumes son:
- GCEPersistentDisk
- AWSElasticBlockStore
- AzureFile
- AzureDisk
- CephFS
- NFS
- iSCSI.
Un PersistentVolumeClaim (PVC) es una solicitud de almacenamiento por parte de un usuario. Los usuarios solicitan recursos de PersistentVolume según el tipo, el modo de acceso y el tamaño. Hay tres modos de acceso:
- ReadWriteOnce (lectura-escritura por un solo nodo)
- ReadOnlyMany (lectura-escritura por muchos nodos)
- ReadWriteMany (lectura-escritura por muchos nodos). Una vez que se encuentra un VolumenPersistente adecuado, está vinculado a un PersistentVolumeClaim.
Después de ser encontrado, el recurso PersistentVolumeClaim puede ser usado en un Pod.
Una vez que el usuario termina su trabajo, el PersistentVolumes adjuntos pueden ser liberados. Los PersistentVolumes pueden entonces ser reclamados (para que un administrador verifique y/o agregue datos), eliminados (se eliminan tanto los datos como el volumen), o reciclados para su uso futuro (sólo se eliminan los datos).
Los orquestadores de contenedores como Kubernetes, Mesos, Docker o Cloud Foundry solían tener sus propios métodos de gestión del almacenamiento externo mediante volúmenes. Para los proveedores de almacenamiento, era un desafío administrar diferentes plugins de Volumen para diferentes orquestadores. Los proveedores de almacenamiento y los miembros de la comunidad de diferentes orquestadores comenzaron a trabajar juntos para estandarizar la interfaz de Volume; un plugin de Volume construido utilizando un CSI estandarizado diseñado para funcionar en diferentes orquestadores de contenedores.
Entre las versiones v1.9 y v1.13 de Kubernetes, CSI maduró de alfa a soporte estable, lo que hace que la instalación de nuevos plugins de volumen compatibles con CSI sea muy fácil. Con CSI, los proveedores de almacenamiento de terceros pueden desarrollar soluciones sin necesidad de añadirlas al código base de Kubernetes.
Mientras se despliega una aplicación, es posible que tengamos que pasar parámetros de tiempo de ejecución como detalles de configuración, permisos, contraseñas, fichas, etc. Supongamos que necesitamos desplegar diez aplicaciones diferentes para nuestros clientes, y para cada cliente, necesitamos mostrar el nombre de la compañía en la interfaz de usuario. Entonces, en lugar de crear diez imágenes Docker diferentes para cada cliente, podemos usar la imagen de la plantilla y pasar los nombres de los clientes como parámetros de tiempo de ejecución. En estos casos, podemos utilizar el recurso de la ConfigMap API. Del mismo modo, cuando queramos pasar información confidencial, podemos utilizar el recurso de la Secrets API.
Los ConfigMaps nos permiten desacoplar los detalles de la configuración de la imagen del contenedor. Usando ConfigMaps, pasamos los datos de configuración como pares key-value, que son consumidos por Pods o cualquier otro componente del sistema y controllers, environment variables, conjuntos de comandos y argumentos, o volumes. Podemos crear ConfigMaps a partir de valores literales, de archivos de configuración, de uno o más archivos o directorios.
Se puede crear un ConfigMap con el comando kubectl create, y podemos mostrar sus detalles usando el comando kubectl get.
Create the ConfigMap
$ kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2
configmap/my-config created
Display the ConfigMap Details for my-config
$ kubectl get configmaps my-config -o yaml
apiVersion: v1
data:
key1: value1
key2: value2
kind: ConfigMap
metadata:
creationTimestamp: 2019-05-31T07:21:55Z
name: my-config
namespace: default
resourceVersion: "241345"
selfLink: /api/v1/namespaces/default/configmaps/my-config
uid: d35f0a3d-45d1-11e7-9e62-080027a46057
Con la opción -o yaml, pedimos al comando kubectl que escupa la salida en formato YAML. Como podemos ver, el objeto tiene el tipo ConfigMap, y tiene los pares key-value dentro del campo de datos. El nombre de ConfigMap y otros detalles forman parte del campo de metadata.
Primero, necesitamos crear un archivo de configuración con el siguiente contenido:
apiVersion: v1
kind: ConfigMap
metadata:
name: customer1
data:
TEXT1: Customer1_Company
TEXT2: Welcomes You
COMPANY: Customer1 Company Technology Pct. Ltd.
donde especificamos el kind, metadata y data, apuntando al punto final v1 del API Server.
Si nombramos el archivo con la configuración anterior como customer1-configmap.yaml, podemos crear el ConfigMap con el siguiente comando:
$ kubectl create -f customer1-configmap.yaml
configmap/customer1 created
Necesitamos crear el fichero file permission-reset.properties con la siguiente configuracion:
permission=read-only
allowed="true"
resetCount=3
Podemos crear el ConfigMap con el siguinte comando:
$ kubectl create configmap permission-config --from-file=<path/to/>permission-reset.properties
configmap/permission-config created
- As Environment Variable Dentro de un Contenedor, podemos recuperar los datos de los valores de las claves de un ConfigMap entero o los valores de claves específicas del ConfigMap como Environment Variable.
En el siguiente ejemplo, todas las variables de entorno del Contenedor_ myapp-full-container_ reciben los valores de full-config-map ConfigMap keys:
...
containers:
- name: myapp-full-container
image: myapp
envFrom:
- configMapRef:
name: full-config-map
...
En el siguiente ejemplo, las variables de entorno del contenedor myapp-specific-container reciben sus valores de pares key-value específicos de ConfigMaps separados:
...
containers:
- name: myapp-specific-container
image: myapp
env:
- name: SPECIFIC_ENV_VAR1
valueFrom:
configMapKeyRef:
name: config-map-1
key: SPECIFIC_DATA
- name: SPECIFIC_ENV_VAR2
valueFrom:
configMapKeyRef:
name: config-map-2
key: SPECIFIC_INFO
...
Con lo anterior, obtendremos la variable de entorno SPECIFIC_ENV_VAR1 establecida al valor de la clave SPECIFIC_DATA de config-map-1 ConfigMap, y la variable de entorno SPECIFIC_ENV_VAR2 establecida al valor de la clave SPECIFIC_INFO de config-map-2 ConfigMap.
- As Volume Podemos montar un ConfigMap vol-config-map como un Volume dentro de un Pod. Para cada clave en el ConfigMap, se crea un archivo en la ruta de montaje (donde el archivo se nombra con el nombre de la clave) y el contenido de ese archivo se convierte en el valor de la clave respectiva:
...
containers:
- name: myapp-vol-container
image: myapp
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: vol-config-map
...
Asumamos que tenemos una aplicación de blog de Wordpress, en la cual nuestro frontend de Wordpress se conecta al backend de la base de datos MySQL usando una contraseña. Mientras creamos el Deployment para wordpress, podemos incluir la contraseña de MySQL en el archivo YAML del Deployment, pero la contraseña no estaría protegida. La contraseña estaría disponible para cualquiera que tenga acceso al archivo de configuración.
En este escenario, el objeto Secret puede ayudar permitiéndonos codificar la información sensible antes de compartirla. Con Secrets, podemos compartir información sensible como contraseñas, fichas o claves en forma de pares key-value, similar a ConfigMaps; así, podemos controlar cómo se utiliza la información de un Secret, reduciendo el riesgo de exposiciones accidentales. En Despliegues u otros recursos, se hace referencia al objeto Secreto, sin exponer su contenido.
Es importante tener en cuenta que los datos del Secret se almacenan como texto plano dentro de etcd, por lo que los administradores deben limitar el acceso al API Server y etcd. Una característica más reciente permite que los datos secretos se cifren en reposo mientras se almacenan en etcd; una característica que debe habilitarse a nivel del API Server.
Para crear un Secret, podemos usar el comando kubectl create secret:
$ kubectl create secret generic my-password --from-literal=password=mysqlpassword
El comando anterior crearía un Secret llamado my-password, que tiene el valor de la clave de la contraseña establecida en mysqlpassword.
Después de crear un secreto con éxito podemos analizarlo con los comandos get y describe. No revelan el contenido del Secret. El tipo está listado como Opaque.
$ kubectl get secret my-password
NAME TYPE DATA AGE
my-password Opaque 1 8m
$ kubectl describe secret my-password
Name: my-password
Namespace: default
Labels: <none>
Annotations: <none>
Type Opaque
Data
====
password: 13 bytes
Podemos crear un Secret manualmente desde un archivo de configuración de YAML. El archivo de ejemplo que se muestra a continuación se llama mypass.yaml. Hay dos tipos de maps para información sensible dentro de un Secreto: data y stringData.
Con los data, cada valor de un campo de información sensible debe ser codificado usando base64. Si queremos tener un archivo de configuración para nuestro Secret, primero debemos crear la codificación de base64 para nuestra contraseña:
$ echo mysqlpassword | base64
bXlzcWxwYXNzd29yZAo=
y luego lo usaremos en el archivo de configuración:
apiVersion: v1
kind: Secret
metadata:
name: my-password
type: Opaque
data:
password: bXlzcWxwYXNzd29yZAo=
Tenga en cuenta que la codificación base64 no significa encriptación, y cualquiera puede decodificar fácilmente nuestros datos codificados:
$ echo "bXlzcWxwYXNzd29yZAo=" | base64 --decode
mysqlpassword
Por lo tanto, asegúrate de no confirmar un archivo de configuración de Secret en el código fuente.
Con los mapas stringData, no hay necesidad de codificar el valor de cada campo de información sensible. El valor del campo sensible será codificado cuando se cree el my-password Secret:
apiVersion: v1
kind: Secret
metadata:
name: my-password
type: Opaque
stringData:
password: mysqlpassword
Usando el archivo de configuración mypass.yaml podemos ahora crear un Secret con el comando kubectl create:
$ kubectl create -f mypass.yaml
secret/my-password created
Para crear un Secret a partir de un fichero, utilizamos kubectl create secret:
Primero, encriptamos los datos sensibles en un fichero de texto:
$ echo mysqlpassword | base64
bXlzcWxwYXNzd29yZAo=
$ echo -n 'bXlzcWxwYXNzd29yZAo=' > password.txt
Creamos el Secret a partir del fichero:
$ kubectl create secret generic my-file-password --from-file=password.txt
secret/my-file-password created
Después de crear un Secret con éxito podemos analizarlo con los comandos get y describe. No revelan el contenido del Secret. El tipo está listado como Opaque.
$ kubectl get secret my-file-password
NAME TYPE DATA AGE
my-file-password Opaque 1 8m
$ kubectl describe secret my-file-password
Name: my-file-password
Namespace: default
Labels: <none>
Annotations: <none>
Type Opaque
Data
====
password.txt: 13 bytes
Los Secrets son consumidos por los Contenedores en los Pods como volúmenes de datos montados, o como variables de entorno, y son referenciados en su totalidad o específicos key-values.
- Using Secrets as Environment Variables A continuación hacemos referencia sólo a la clave de la contraseña del my-password Secret y asignamos su valor a la variable de entorno WORDPRESS_DB_PASSWORD:
....
spec:
containers:
- image: wordpress:4.7.3-apache
name: wordpress
env:
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-password
key: password
....
- Usar los secretos como archivos de una cápsula También podemos montar un Secret como un Volumen dentro de un Pod. El siguiente ejemplo crea un archivo para cada clave de My-Password Secret (donde los archivos se nombran según los nombres de las claves), los archivos que contienen los valores del Secreto:
....
spec:
containers:
- image: wordpress:4.7.3-apache
name: wordpress
volumeMounts:
- name: secret-volume
mountPath: "/etc/secret-data"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-password
....
En un capítulo anterior, vimos cómo podemos acceder a nuestra aplicación desplegada en contenedores desde el mundo exterior a través de los Services. Entre los tipos de Services, el NodePort y el LoadBalancer son los más utilizados. Para el LoadBalancer ServiceType, necesitamos tener el apoyo de la infraestructura subyacente. Incluso después de tener el soporte, puede que no queramos usarlo para cada Service, ya que los recursos del LoadBalancer son limitados y pueden aumentar los costos significativamente. Administrar el NodePort ServiceType también puede ser difícil a veces, ya que necesitamos actualizar nuestra configuración de proxy y hacer un seguimiento de los puertos asignados. En este capítulo, exploraremos el recurso API Ingress, que representa otra capa de abstracción, desplegada frente a los recursos de API Service, ofreciendo un método unificado para gestionar el acceso a nuestras aplicaciones desde el mundo externo.
Con los Service, las reglas de enrutamiento están asociadas a un Service determinado. Existen mientras exista el Service, y hay muchas reglas porque hay muchos Service en el clúster. Si podemos desacoplar de alguna manera las reglas de enrutamiento de la aplicación y centralizar la gestión de las reglas, podemos entonces actualizar nuestra aplicación sin preocuparnos por su acceso externo. Esto se puede hacer utilizando el recurso Ingress.
De acuerdo con kubernetes.io,
"Un Ingress es un conjunto de reglas que permiten que las conexiones entrantes lleguen a los Services del cluster".
Para permitir que la conexión entrante llegue a los Service del clúster, Ingress configura un Load Balancer HTTP/HTTPS de Nivel 7 para los Services y proporciona lo siguiente:
- TLS (Transport Layer Security).
- Name-based virtual hosting .
- Fanout routing.
- Loadbalancing.
- Custom rules.
Con Ingress, los usuarios no se conectan directamente a un Service. Los usuarios llegan al Ingress endpoint y, desde allí, la solicitud es enviada al Service deseado. A continuación se puede ver un ejemplo de una definición de Ingress de muestra:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: virtual-host-ingress
namespace: default
spec:
rules:
- host: blue.example.com
http:
paths:
- backend:
serviceName: webserver-blue-svc
servicePort: 80
- host: green.example.com
http:
paths:
- backend:
serviceName: webserver-green-svc
servicePort: 80
En el ejemplo anterior, las solicitudes de los usuarios tanto a blue.example.com como a green.example.com irían al mismo Ingress endpoint y, desde allí, se remitirían al servidor web-azul-svc, y al servidor web-verde-svc, respectivamente. Este es un ejemplo de una regla de Ingress de Name-Based Virtual Hosting.
También podemos tener reglas de Fanout Ingress, cuando las peticiones a example.com/blue y example.com/green serían reenviadas a webserver-blue-svc y webserver-green-svc, respectivamente
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: fan-out-ingress
namespace: default
spec:
rules:
- host: example.com
http:
paths:
- path: /blue
backend:
serviceName: webserver-blue-svc
servicePort: 80
- path: /green
backend:
serviceName: webserver-green-svc
servicePort: 80
El recurso Ingress no realiza por sí mismo ningún reenvío de solicitudes, sino que se limita a aceptar las definiciones de las normas de enrutamiento del tráfico. El Ingress es realizado por un Ingress Controller.
Un Ingress Controller es una aplicación que vigila el API Server del Master Node para detectar cambios en los recursos de Ingress y actualiza el Load Balancer de Capa 7 en consecuencia. Kubernetes soporta diferentes Ingress Controller, y, si es necesario, también podemos construir el nuestro. GCE L7 Load Balancer Controller y Nginx Ingress Controller son los Ingress Controller más utilizados. Otros controladores son Istio, Kong, Traefik, etc.
- Start the Ingress Controller with Minikube Minikube se envía con la configuración del Nginx Ingress Controller como un addon, desactivado por defecto. Puede ser fácilmente activado ejecutando el siguiente comando:
$ minikube addons enable ingress
Una vez que el Ingress Controller es desplegado, podemos crear un recurso de entrada usando el comando kubectl create. Por ejemplo, si creamos un archivo virtual-host-ingress.yaml con la definición de la regla Name-Based Virtual Hosting que vimos en la sección Ingress II, entonces usamos el siguiente comando para crear un recurso Ingress:
$ kubectl create -f virtual-host-ingress.yaml
Con el recurso Ingress que acabamos de crear, ahora deberíamos poder acceder a los servicios webserver-azul-svc o webserver-verde-svc usando las URLs blue.example.com y green.example.com. Como nuestra configuración actual está en Minikube, necesitaremos actualizar el archivo de configuración del host (/etc/hosts en Mac y Linux) en nuestra estación de trabajo a la IP de Minikube para esas URLs. Después de la actualización, el archivo debería tener un aspecto similar a:
$ cat /etc/hosts
127.0.0.1 localhost
::1 localhost
192.168.99.100 blue.example.com green.example.com
Ahora podemos abrir blue.example.com y green.example.com en el navegador y acceder a cada aplicación.
Hasta ahora, en este curso, hemos dedicado la mayor parte de nuestro tiempo a entender los conceptos básicos de Kubernetes y los flujos de trabajo simples para construir una base sólida. Para dar soporte a las cargas de trabajo de producción de clase empresarial, Kubernetes también admite la Autoscaling, Rolling updates, rollbacks, la gestión de quotas, la autorización a través de RBAC, la gestión de paquetes, las políticas de red y seguridad, etc. En este capítulo, cubriremos brevemente un número limitado de estos temas avanzados, pero profundizar en los detalles estaría fuera del alcance de este curso.
Con las Annotations, podemos adjuntar metadata arbitrarios no identificables a cualquier objeto, en un formato de key-value:
"annotations": {
"key1" : "value1",
"key2" : "value2"
}
A diferencia de las Labels, las Annotations no se usan para identificar y seleccionar objetos.
- Store build/release IDs, PR numbers, git branch, etc.
- Phone/pager numbers of people responsible, or directory entries specifying where such information can be found.
- Pointers to logging, monitoring, analytics, audit repositories, debugging tools, etc.
- Etc.
Por ejemplo, al crear un Deployment, podemos añadir una descripción como se ve a continuación:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: webserver
annotations:
description: Deployment based PoC dates 2nd May'2019
....
Las Annotations se muestran mientras se describe un objeto:
$ kubectl describe deployment webserver
Name: webserver
Namespace: default
CreationTimestamp: Fri, 03 May 2019 05:10:38 +0530
Labels: app=webserver
Annotations: deployment.kubernetes.io/revision=1
description=Deployment based PoC dates 2nd May'2019
...
Un Job crea una o más Pods para realizar una tarea determinada. El objeto Job asume la responsabilidad de los fallos de los Pods. Se asegura de que la tarea dada se complete con éxito. Una vez que la tarea se ha completado, todos los Pods han terminado automáticamente. Las opciones de configuración del Job incluyen:
- parallelism - para establecer el número de Pods que pueden funcionar en paralelo;
- completions - para establecer el número de completions esperadas;
- activeDeadlineSeconds - para establecer la duración del Job;
- backoffLimit - para establecer el número de reintentos antes de que el Job sea marcado como fallido;
- ttlSecondsAfterFinished - para retrasar la limpieza de los trabajos terminados.
A partir de la versión 1.4 de Kubernetes, también podemos realizar Jobs a horas/fechas programadas con CronJobs, donde se crea un nuevo objeto (Job) aproximadamente una vez por cada ciclo de ejecución. Las opciones de configuración de CronJob incluyen:
- startingDeadlineSeconds - para establecer la fecha límite para iniciar un Job si se ha perdido la hora programada;
- concurrencyPolicy - para permitir o prohibir Job concurrentes o para reemplazar los antiguos Job con los nuevos.
Cuando hay muchos usuarios que comparten un determinado cluster de Kubernetes, siempre existe una preocupación por el uso justo. Un usuario no debe sacar provecho indebido. Para responder a esta preocupación, los administradores pueden utilizar el recurso de la ResourceQuota, que proporciona restricciones que limitan el consumo de recursos agregados por Namespace.
Podemos establecer los siguientes tipos de quotas por Namespace:
- Compute Resource Quota Podemos limitar la suma total de recursos de computación (CPU, memoria, etc.) que pueden ser solicitados en un determinado Namespace.
- Storage Resource Quota Podemos limitar la suma total de recursos de almacenamiento (PersistentVolumeClaims, requests.storage, etc.) que se pueden solicitar.
- Object Count Quota Podemos restringir el número de objetos de un tipo determinado (pods, ConfigMaps, PersistentVolumeClaims, ReplicationControllers, Services, Secrets, etc.).
Si bien es bastante fácil escalar manualmente unos pocos objetos de Kubernetes, puede que no sea una solución práctica para un cluster en producción en el que se despliegan cientos o miles de objetos. Necesitamos una solución de escalado dinámico que añada o elimine objetos del clúster en función de la utilización de los recursos, la disponibilidad y los requisitos.
El Autoscaling puede implementarse en un clúster de Kubernetes mediante controllers que ajustan periódicamente el número de objetos en ejecución basándose en métricas únicas, múltiples o personalizadas. Hay varios tipos de autoscalers disponibles en Kubernetes que pueden implementarse individualmente o combinarse para obtener una solución de autoscaling más robusta:
-
Horizontal Pod Autoscaler (HPA) HPA es un recurso API de controlador basado en algoritmos que ajusta automáticamente el número de réplicas en un ReplicaSet, Deployment o Replication Controller basado en la utilización de la CPU.
-
Vertical Pod Autoscaler (VPA) El VPA establece automáticamente los requisitos de recursos del contenedor (CPU y memoria) en un Pod y los ajusta dinámicamente en tiempo de ejecución, basándose en los datos históricos de utilización, la disponibilidad actual de recursos y los eventos en tiempo real.
-
Cluster Autoscaler El autoscaler de clúster redimensiona automáticamente el clúster de Kubernetes cuando no hay suficientes recursos disponibles para los nuevos Pods que se esperan programar o cuando hay nodos subutilizados en el clúster.
En los casos en que necesitemos recoger datos monitorización de todos los Nodes, o ejecutar un demonio de almacenamiento en todos los Nodes, entonces necesitamos un tipo específico de Pod que se ejecute en todos los Nodes en todo momento. Un DaemonSet es el objeto que nos permite hacer justamente eso. Es un recurso crítico de la API de controladores para clusters de Kubernetes multi-Nodes. El agente kube-proxy que se ejecuta como un Pod en cada uno de los Nodes del clúster es gestionado por un DaemonSet.
Cada vez que se añade un Node al clúster, se crea automáticamente un Pod de un DaemonSet determinado en él. Aunque asegura un proceso automatizado, los Pods del DaemonSet son colocados en los Nodes por el Scheduler predeterminado del cluster. Cuando el NOde muere o es removido del cluster, los respectivos Pods son basura recolectada. Si un DaemonSet es eliminado, todos los Pods que ha creado son eliminados también.
Una nueva característica del recurso DaemonSet permite que sus Pods sean programados solo en nodos específicos configurando nodeSelectors y reglas de affinity de Nodes. Similar a los recursos de Deployment, los DaemonSets soportan rolling updates y rollbacks.
El controlador StatefulSet se utiliza para aplicaciones de estado que requieren una identidad única, como el nombre, identificaciones de red, ordenamiento estricto, etc. Por ejemplo, Mysql Cluster, etcd cluster.
El controlador StatefulSet proporciona identidad y orden garantizado de despliegue y escalado a Pods. Similar a los Deployments, StatefulSets utiliza ReplicaSets como controladores intermedios de Pods y soporta rolling updates y rollbacks.
Con la Kubernetes Cluster Federation podemos gestionar varios clusters de Kubernetes desde un solo plano de control. Podemos sincronizar los recursos entre los clusters federados y tener un descubrimiento cruzado de los clusters. Esto nos permite realizar Deployments en todas las regiones, acceder a ellos mediante un registro DNS global y lograr HA.
Aunque sigue siendo una característica de Alpha, la Federación es muy útil cuando queremos construir una solución híbrida, en la que podemos tener un cluster corriendo dentro de nuestro centro de datos privado y otro en la nube pública, permitiéndonos evitar el bloqueo del proveedor. También podemos asignar pesos para cada cluster de la Federación, para distribuir la carga en base a reglas personalizadas.
En Kubernetes, un Resources es un endpoint de la API que almacena una colección de objetos de la API. Por ejemplo, un recurso de Pod contiene todos los objetos de Pod.
Aunque en la mayoría de los casos los recursos existentes de Kubernetes son suficientes para cumplir nuestros requisitos, también podemos crear nuevos recursos utilizando Custom Resources. Con los recursos personalizados, no tenemos que modificar el origen de Kubernetes.
Los recursos personalizados son de naturaleza dinámica, y pueden aparecer y desaparecer en un clúster ya en funcionamiento en cualquier momento.
Para hacer un recurso declarativo, debemos crear e instalar un Custom Controller, que puede interpretar la estructura del recurso y realizar las acciones necesarias. Los controladores personalizados pueden ser desplegados y administrados en un clúster ya en funcionamiento.
Hay dos maneras de agregar recursos personalizados:
- Custom Resource Definitions (CRDs) Esta es la forma más fácil de añadir Custom Resources y no requiere ningún conocimiento de programación. Sin embargo, construir el Custom Controller requeriría algo de programación.
- API Aggregation Para un control más fino, podemos escribir API Aggregation. Son API Servers subordinados que se encuentran detrás del API Server principal. El API Server principal actúa como proxy para todas las solicitudes de API entrantes; maneja las que se basan en sus capacidades y las proxies sobre las demás solicitudes destinadas a los API Server subordinados.
Para desplegar una aplicación, utilizamos diferentes manifest de Kubernetes, como Deployments, Services, Volume Claims, Ingress, etc. A veces, puede resultar cansado desplegarlos uno por uno. Podemos agrupar todos esos manifiestos después de tentarlos en un formato bien definido, junto con otros metadata. Este conjunto se conoce como Chart. Estos charts pueden ser servidos a través de repositorios, como los que tenemos para los paquetes rpm y deb.
Helm es un gestor de paquetes (análogo a yum y apto para Linux) para Kubernetes, que puede instalar/actualizar/eliminar esos Charts en el cluster de Kubernetes.
Helm tiene dos componentes:
- Un cliente llamado helm, que se ejecuta en el equipo del usuario.
- Un servidor llamado tiller, que corre dentro del cluster de Kubernetes.
El cliente de helm se conecta al tille server para manejar los charts.
A veces necesitamos definir privilegios específicos y configuraciones de control de acceso para los Pods y los Contenedores. Los Security Contexts nos permiten establecer un control de acceso discrecional para los permisos de acceso a los objetos, la ejecución privilegiada, las capacidades, las labels de seguridad, etc. Sin embargo, su efecto se limita a los Pods y Contenedores individuales donde tales ajustes de configuración de contexto se incorporan en la sección spec.
Para aplicar los ajustes de seguridad a múltiples Pods y Contenedores en todo el cluster, podemos definir Pod Security Policies. Éstas permiten una configuración de seguridad más precisa para controlar el uso del Namespace del host, la red y los puertos del host, file system group, el uso de los volume types, la aplicación de la identificación de usuario y de grupo del contenedor, el aumento de los privilegios de root, etc.
Kubernetes fue diseñado para permitir que todos los Pods se comuniquen libremente, sin restricciones, con todos los demás Pods en el cluster Namespaces. Con el tiempo se hizo evidente que no era un diseño ideal, y que era necesario establecer mecanismos para restringir la comunicación entre determinados Pods y las aplicaciones del cluster Namespace. Las Network Policies son conjuntos de reglas que definen la forma en que se permite a los Pods hablar con otros Pods y recursos dentro y fuera del cluster. Los Pods que no estén cubiertos por ninguna Network Policies continuarán recibiendo tráfico sin restricciones desde cualquier endpoint.
Las Network Policies son muy similares a los típicos Firewalls. Están diseñadas para proteger principalmente los activos ubicados dentro del Firewall pero también pueden restringir el tráfico saliente basándose en conjuntos de reglas y políticas.
El recurso Network Policy API especifica podSelectors, Ingress and/or Egress policyTypes, y reglas basadas en ipBlocks y ports de origen y destino. También se pueden definir políticas muy simplistas de permiso o denegación por defecto. Como buena práctica, se recomienda definir una política de denegación por defecto para bloquear todo el tráfico hacia y desde el Namespace, y luego definir conjuntos de reglas para que se permita la entrada y salida del tráfico específico en el Namespace.
Tengamos en cuenta que no todas las soluciones de red disponibles para los Kubernetes son compatibles con las políticas de red. Revise la sección de Pod-to-Pod Communication desde el capítulo de Kubernetes Architecture si es necesario. De forma predeterminada, las Network Policies son recursos de API del Namespace, pero ciertos complementos de red proporcionan características adicionales para que las Network Polidices se puedan aplicar en todo el clúster.
En los Kubernetes, tenemos que recopilar datos de uso de recursos por Pods, Services, Nodes, etc., para comprender el consumo general de recursos y tomar decisiones para escalar una aplicación determinada. Dos soluciones de monitorización de Kubernetes muy populares son el Kubernetes Metrics Server y Prometheus.
-
Metrics Server Metrics Server es un agregador de datos de uso de recursos en todo el cluster, una característica relativamente nueva en Kubernetes.
-
Prometheus Prometheus, ahora parte de la CNCF (Cloud Native Computing Foundation), también puede ser usado para scrape el uso de recursos de diferentes componentes y objetos de Kubernetes.
Otro aspecto importante para troubleshooting y debugging es el Logging, en el que recogemos los registros de los diferentes componentes de un sistema determinado. En Kubernetes, podemos recoger los registros de diferentes componentes del cluster, Objects, Nodes, etc. Sin embargo, desafortunadamente, Kubernetes no proporciona por defecto el Logging de todo el clúster, por lo que se necesitan herramientas de terceros para centralizar y agregar los registros del clúster. La forma más habitual de recopilar los registros es mediante el uso de Elasticsearch, que utiliza fluentd con una configuración personalizada como agente en los Nodes. fluentd es un recopilador de datos de código abierto, que también forma parte de CNCF.
Al igual que con cualquier otro proyecto de código abierto, la Community juega un papel vital en el desarrollo de los Kubernetes. La comunidad decide roadmap de los proyectos y trabaja para ello. La comunidad se involucra en diferentes foros online y offline, como Meetups, Slack, reuniones semanales, etc. En este capítulo, exploraremos la comunidad de Kubernetes y veremos cómo usted también puede formar parte de ella.
Con más de 53K estrellas de GitHub, Kubernetes es uno de los proyectos de código abierto más populares. Los miembros de la comunidad no sólo ayudan con el código fuente, sino que también ayudan a compartir el conocimiento. La comunidad participa en actividades tanto en línea como fuera de línea.
Actualmente, existe un proyecto llamado K8s Port, que reconoce y recompensa a los miembros de la comunidad por sus contribuciones a Kubernetes. Esta contribución puede ser en forma de código, asistiendo y hablando en reuniones, respondiendo a preguntas sobre el desbordamiento de la pila, etc.
A continuación, revisaremos algunos de los medios utilizados por la comunidad de Kubernetes.
Weekly Meetings Una reunión semanal de la comunidad se lleva a cabo usando herramientas de videoconferencia. Puedes solicitar una invitación para el calendario desde aquí.
Meetup Groups Hay muchos grupos de reunión en todo el mundo, donde los miembros de las comunidades locales se reúnen a intervalos regulares para discutir sobre los kubernetes y su ecosistema.
También hay algunos grupos de encuentro en línea, donde los miembros de la comunidad pueden reunirse virtualmente.
Slack Channels Los miembros de la comunidad son muy activos en el Kubernetes Slack. Hay diferentes canales basados en temas, y cualquiera puede unirse y participar en los debates. Puedes discutir con el equipo de Kubernetes en el canal #kubernetes-usuarios.
Mailing Lists Hay listas de correo de usuarios y desarrolladores de Kubernetes, a las que puede unirse cualquier persona interesada.
Special Interest Groups Los Grupos de Interés Especial (GIE) se centran en partes específicas del proyecto de los Kubernetes, como la programación, la autorización, el trabajo en red, la documentación, etc. Cada grupo puede tener un flujo de trabajo diferente, basado en sus requisitos específicos. Una lista con todos los SIG actuales se puede encontrar aquí.
Dependiendo de la necesidad, se puede crear un nuevo SIG.
Stack Overflow Además de Slack y las listas de correo, los miembros de la comunidad pueden obtener apoyo de Stack Overflow, también. Stack Overflow es un entorno online donde puedes publicar preguntas para las que no encuentras respuesta. El equipo de Kubernetes también monitoriza los mensajes etiquetados como Kubernetes.
El CNCF organiza numerosas conferencias internacionales sobre los gobernantes, así como otros proyectos del CNCF. Para obtener más información sobre estos eventos, por favor haga clic aquí.
Tres de las principales conferencias que organiza son:
- KubeCon + CloudNativeCon Europe
- KubeCon + CloudNativeCon Norteamérica
- KubeCon + CloudNativeCon China.
Ahora que conoce mejor a los Kubernetes, puede continuar su viaje por..:
- Participando en actividades y discusiones organizadas por la comunidad de Kubernetes
- Asistir a eventos organizados por la Fundación de Computación Nativa en la Nube y la Fundación Linux
- Amplíe su conocimiento y habilidades de Kubernetes inscribiéndose en el curso LFS258 - Fundamentos de Kubernetes, LFD259 - Kubernetes para desarrolladores, o el LFS458 - Administración de Kubernetes y LFD459 - Kubernetes para desarrolladores de aplicaciones, cursos pagados ofrecidos por la Fundación Linux.
- Preparación para los exámenes de Administrador Certificado de Kubernetes o Desarrollador Certificado de Aplicaciones de Kubernetes, ofrecidos por la Cloud Native Computing Foundation
- Y muchas otras opciones.