Capítulo 4. Programación y utillaje
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
La parte de programación de la CKA se centra en los efectos de definir los límites de los recursos cuando los evalúa el programador de Kubernetes. El comportamiento por defecto en tiempo de ejecución del planificador también puede modificarse definiendo reglas de afinidad de nodos, así como manchas y tolerancias. De estos conceptos, sólo se espera que comprendas los matices de los límites de recursos y su efecto en el planificador en diferentes escenarios. Por último, este dominio del plan de estudios menciona conocimientos de alto nivel sobre herramientas de gestión de manifiestos y plantillas.
A alto nivel, este capítulo abarca los siguientes conceptos:
-
Límites de recursos para Pods
-
Gestión de manifiestos imperativa y declarativa
-
Herramientas comunes de plantillas como Kustomize,
yq
, y Helm
Comprender cómo afectan los límites de recursos ala programación de Pods
Un clúster Kubernetes puede constar de varios nodos. Dependiendo de una serie de reglas (por ejemplo, selectores de nodo, afinidad de nodo, manchas y tolerancias), el programador de Kubernetes decide qué nodo elegir para ejecutar la carga de trabajo. El examen CKA no te pide que comprendas los conceptos de programación mencionados anteriormente, pero sería útil tener una idea aproximada de cómo funcionan a alto nivel.
Una métrica que entra en juego para la programación de la carga de trabajo es la solicitud de recursos definida por los contenedores de un Pod. Los recursos más comunes que se pueden especificar son la CPU y la memoria. El programador se asegura de que la capacidad de recursos del nodo pueda satisfacer las necesidades de recursos del Pod. Más concretamente, el programadordetermina la suma de solicitudes de recursos por tipo en todos los contenedores definidos en el Pod y las compara con los recursos disponibles del nodo. La Figura 4-1 ilustra el proceso de programación basado en las solicitudes de recursos.
Definir solicitudes de recursos de contenedores
Cada contenedor de un Pod puede definir sus propias peticiones de recursos. La Tabla 4-1 describe las opciones disponibles, incluyendo un valor de ejemplo.
Atributo YAML | Descripción | Valor de ejemplo |
---|---|---|
|
Tipo de recurso CPU |
|
|
Tipo de recurso de memoria |
|
|
Tipo de recurso de página enorme |
|
|
Tipo de recurso de almacenamiento efímero |
|
Kubernetes utiliza unidades de recursos para tipos de recursos que se desvían de las unidades de recursos estándar como megabytes y gigabytes. Explicar todos los entresijos de esas unidades va más allá del alcance de este libro, pero puedes leer los detalles en la documentación.
Para que el uso de esas peticiones de recursos sea transparente, echaremos un vistazo a un ejemplo de definición. El manifiesto Pod YAML que se muestra en el Ejemplo 4-1 define dos contenedores, cada uno con sus propias peticiones de recursos. Cualquier nodo al que se le permita ejecutar el Pod debe ser capaz de soportar una capacidad mínima de memoria de 320Mi y 1250m de CPU, la suma de los recursos de ambos contenedores.
Ejemplo 4-1. Configurar solicitudes de recursos del contenedor
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
rate-limiter
spec
:
containers
:
-
name
:
business-app
image
:
bmuschko/nodejs-business-app:1.0.0
ports
:
-
containerPort
:
8080
resources
:
requests
:
memory
:
"256Mi"
cpu
:
"1"
-
name
:
ambassador
image
:
bmuschko/nodejs-ambassador:1.0.0
ports
:
-
containerPort
:
8081
resources
:
requests
:
memory
:
"64Mi"
cpu
:
"250m"
En este escenario, se trata de un clúster Minikube Kubernetes formado por tres nodos, un nodo del plano de control y dos nodos trabajadores. El siguiente comando lista todos los nodos:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready control-plane,master 12d v1.21.2 minikube-m02 Ready <none> 42m v1.21.2 minikube-m03 Ready <none> 41m v1.21.2
En el siguiente paso, crearemos el Pod a partir del manifiesto YAML. El programador coloca el Pod en el nodo llamado minikube-m03
:
$ kubectl create -f rate-limiter-pod.yaml pod/rate-limiter created $ kubectl get pod rate-limiter -o yaml | grep nodeName: nodeName: minikube-m03
Tras una inspección más detallada del nodo, puedes inspeccionar su capacidad máxima, cuánta de esta capacidad es asignable y las peticiones de memoria de los Pods programados en el nodo. El siguiente comando enumera la información y condensa la salida a los fragmentos relevantes:
$ kubectl describe node minikube-m03 ... Capacity: cpu: 2 ephemeral-storage: 17784752Ki hugepages-2Mi: 0 memory: 2186612Ki pods: 110 Allocatable: cpu: 2 ephemeral-storage: 17784752Ki hugepages-2Mi: 0 memory: 2186612Ki pods: 110 ... Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits \ Memory Requests Memory Limits AGE --------- ---- ------------ ---------- \ --------------- ------------- --- default rate-limiter 1250m (62%) 0 (0%) \ 320Mi (14%) 0 (0%) 9m ...
Ciertamente, es posible que un Pod no pueda programarse debido a la insuficiencia de recursos disponibles en los nodos. En esos casos, el registro de eventos del Pod indicará esta situación con los motivos PodExceedsFreeCPU
o PodExceedsFreeMemory
. Para más información sobre cómo solucionar y resolver esta situación, consulta la sección correspondiente de la documentación.
Definir los límites de recursos de los contenedores
Otra métrica que puedes establecer para un contenedor son sus límites de recursos. Los límites de recursos garantizan que el contenedor no pueda consumir más recursos de los asignados. Por ejemplo, podrías expresar que la aplicación que se ejecuta en el contenedor debe limitarse a 1000m de CPU y 512Mi de memoria.
Dependiendo del tiempo de ejecución del contenedor utilizado por el clúster, superar cualquiera de los límites de recursos permitidos provoca la finalización del proceso de aplicación que se ejecuta en el contenedor o hace que el sistema impida por completo la asignación de recursos más allá de los límites. Para una discusión en profundidad sobre cómo son tratados los límites de recursos porel tiempo de ejecucióndel contenedor Docker, consulta la documentación.
La Tabla 4-2 describe las opciones disponibles, incluyendo un valor de ejemplo.
Atributo YAML | Descripción | Valor de ejemplo |
---|---|---|
|
Tipo de recurso CPU |
|
|
Tipo de recurso de memoria |
|
|
Tipo de recurso de página enorme |
|
|
Tipo de recurso de almacenamiento efímero |
|
El Ejemplo 4-2 muestra la definición de límites en acción. Aquí, el contenedor llamado business-app
no puede utilizar más de 512Mi de memoria y 2000m de CPU. El contenedor llamado ambassador
define un límite de 128Mi de memoria y 500m de CPU.
Ejemplo 4-2. Establecer límites de recursos del contenedor
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
rate-limiter
spec
:
containers
:
-
name
:
business-app
image
:
bmuschko/nodejs-business-app:1.0.0
ports
:
-
containerPort
:
8080
resources
:
limits
:
memory
:
"512Mi"
cpu
:
"2"
-
name
:
ambassador
image
:
bmuschko/nodejs-ambassador:1.0.0
ports
:
-
containerPort
:
8081
resources
:
limits
:
memory
:
"128Mi"
cpu
:
"500m"
Supongamos que el Pod se programó en el nodo minikube-m03
. La descripción de los detalles del nodo revela que los límites de CPU y memoria surtieron efecto. Pero aún hay más. Kubernetes asigna automáticamente la misma cantidad de recursos para las peticiones si sólo defines los límites:
$ kubectl describe node minikube-m03 ... Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits \ Memory Requests Memory Limits AGE --------- ---- ------------ ---------- \ --------------- ------------- --- default rate-limiter 1250m (62%) 1250m (62%) \ 320Mi (14%) 320Mi (14%) 11s ...
Definir solicitudes y límites de recursos del contenedor
Es una práctica recomendada que especifiques las solicitudes de recursos y los límites para cada contenedor. Determinar esas expectativas de recursos no siempre es fácil, especialmente para las aplicaciones que aún no se han ejercitado en un entorno de producción. Probar la carga de la aplicación al principio del ciclo de desarrollo puede ayudar a analizar las necesidades de recursos. Se pueden hacer más ajustes monitorizando el consumo de recursos de la aplicación después de desplegarla en el clúster. El Ejemplo 4-3 combina las solicitudes y los límites de recursos en un único manifiesto YAML.
Ejemplo 4-3. Establecer solicitudes y límites de recursos del contenedor
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
rate-limiter
spec
:
containers
:
-
name
:
business-app
image
:
bmuschko/nodejs-business-app:1.0.0
ports
:
-
containerPort
:
8080
resources
:
requests
:
memory
:
"256Mi"
cpu
:
"1"
limits
:
memory
:
"512Mi"
cpu
:
"2"
-
name
:
ambassador
image
:
bmuschko/nodejs-ambassador:1.0.0
ports
:
-
containerPort
:
8081
resources
:
requests
:
memory
:
"64Mi"
cpu
:
"250m"
limits
:
memory
:
"128Mi"
cpu
:
"500m"
Como resultado, puedes ver las diferentes configuraciones para las solicitudes y límites de recursos:
$ kubectl describe node minikube-m03 ... Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits \ Memory Requests Memory Limits AGE --------- ---- ------------ ---------- \ --------------- ------------- --- default rate-limiter 1250m (62%) 2500m (125%) \ 320Mi (14%) 640Mi (29%) 3s ...
Gestionar objetos
Los objetos de Kubernetes pueden crearse, modificarse y eliminarse utilizando comandos imperativos de kubectl
o ejecutando un comando de kubectl
contra un archivo de configuración que declara el estado deseado de un objeto, lo que se denomina manifiesto. El lenguaje principal de definición de un manifiesto es YAML, aunque puedes optar por JSON, que es el formato menos adoptado entre la comunidad de Kubernetes. Se recomienda que los equipos de desarrollo envíen y empujen esos archivos de configuración a repositorios de control de versiones, ya que ayudará con el seguimiento y la auditoría de los cambios a lo largo del tiempo.
Modelar una aplicación en Kubernetes suele requerir un conjunto de objetos de apoyo, cada uno de los cuales puede tener su propio manifiesto. Por ejemplo, puedes querer crear una Implementación que ejecute la aplicación en cinco Pods, un ConfigMap para inyectar datos de configuración como variables de entorno, y un Servicio para exponer el acceso a la red.
Esta sección se centra principalmente en el soporte declarativo de gestión de objetos con ayuda de manifiestos. Para profundizar en el soporte imperativo, consulta las partes pertinentes de la documentación. Además, tocaremos herramientas como Kustomize y Helm para darte una idea de sus ventajas, capacidades y flujos de trabajo.
Gestión declarativa de objetos mediante archivos de configuración
La gestión declarativa de objetos requiere uno o varios archivos de configuración en formato YAML o JSON que describan el estado deseado de un objeto. Con este enfoque creas, actualizas y eliminas objetos.
Crear objetos
Para crear nuevos objetos, ejecuta el comando apply
señalando un archivo, un directorio de archivos o un archivo referenciado por una URL HTTP(S) utilizando la opción -f
. Si uno o varios de los objetos ya existen, el comando sincronizará los cambios realizados en la configuración con el objeto vivo.
Para demostrar la funcionalidad, supondremos los siguientes directorios y archivos de configuración. Los siguientes comandos crean objetos a partir de un único archivo, de todos los archivos de un directorio y de todos los archivos de un directorio de forma recursiva:
. ├── app-stack │ ├── mysql-pod.yaml │ ├── mysql-service.yaml │ ├── web-app-pod.yaml │ └── web-app-service.yaml ├── nginx-deployment.yaml └── web-app ├── config │ ├── db-configmap.yaml │ └── db-secret.yaml └── web-app-pod.yaml
Crear un objeto a partir de un único archivo:
$ kubectl apply -f nginx-deployment.yaml deployment.apps/nginx-deployment created
Crear objetos a partir de varios archivos dentro de un directorio:
$ kubectl apply -f app-stack/ pod/mysql-db created service/mysql-service created pod/web-app created service/web-app-service created
Crear objetos a partir de un árbol de directorios recursivo que contenga archivos:
$ kubectl apply -f web-app/ -R configmap/db-config configured secret/db-creds created pod/web-app created
Crear objetos a partir de un archivo referenciado por una URL HTTP(S):
$ kubectl apply -f https://raw.githubusercontent.com/bmuschko/cka-study-guide/ \ master/ch04/object-management/nginx-deployment.yaml deployment.apps/nginx-deployment created
El comando apply
realiza un seguimiento de los cambios añadiendo o modificando la anotación con la clave kubectl.kubernetes.io/last-applied-configuration
. Puedes encontrar un ejemplo de anotación en la salida del comando get pod
aquí:
$ kubectl get pod web-app -o yaml apiVersion: v1 kind: Pod metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{}, \ "labels":{"app":"web-app"},"name":"web-app","namespace":"default"}, \ "spec":{"containers":[{"envFrom":[{"configMapRef":{"name":"db-config"}}, \ {"secretRef":{"name":"db-creds"}}],"image":"bmuschko/web-app:1.0.1", \ "name":"web-app","ports":[{"containerPort":3000,"protocol":"TCP"}]}], \ "restartPolicy":"Always"}} ...
Actualizar objetos
La actualización de un objeto existente se realiza con el mismo comando apply
. Lo único que tienes que hacer es modificar el archivo de configuración y luego ejecutar el comando contra él. El ejemplo 4-4 modifica la configuración existente de una Implementación en el archivo nginx-deployment.yaml
. Añadimos una nueva etiqueta con la clave team
y cambiamos el número de réplicas de 3 a 5.
Ejemplo 4-4. Archivo de configuración modificado para una Implementación
apiVersion
:
apps/v1
kind
:
Deployment
metadata
:
name
:
nginx-deployment
labels
:
app
:
nginx
team
:
red
spec
:
replicas
:
5
...
El siguiente comando aplica el archivo de configuración modificado. Como resultado, el número de Pods controlados por el ReplicaSet subyacente es 5. La anotación de la Implementación kubectl.kubernetes.io/last-applied-configuration
refleja el último cambio en la configuración:
$ kubectl apply -f nginx-deployment.yaml deployment.apps/nginx-deployment configured $ kubectl get deployments,pods NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx-deployment 5/5 5 5 10m NAME READY STATUS RESTARTS AGE pod/nginx-deployment-66b6c48dd5-79j6t 1/1 Running 0 35s pod/nginx-deployment-66b6c48dd5-bkkgb 1/1 Running 0 10m pod/nginx-deployment-66b6c48dd5-d26c8 1/1 Running 0 10m pod/nginx-deployment-66b6c48dd5-fcqrs 1/1 Running 0 10m pod/nginx-deployment-66b6c48dd5-whfnn 1/1 Running 0 35s $ kubectl get deployment nginx-deployment -o yaml apiVersion: apps/v1 kind: Deployment metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{}, \ "labels":{"app":"nginx","team":"red"},"name":"nginx-deployment", \ "namespace":"default"},"spec":{"replicas":5,"selector":{"matchLabels": \ {"app":"nginx"}},"template":{"metadata":{"labels":{"app":"nginx"}}, \ "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx", \ "ports":[{"containerPort":80}]}]}}}} ...
Borrar objetos
Aunque existe una forma de borrar objetos mediante el comando apply
proporcionando las opciones --prune -l <labels>
, se recomienda borrar un objeto mediante el comando delete
y dirigirlo al archivo de configuración. El siguiente comando elimina una Implementación y los objetos que controla (ReplicaSet y Pods):
$ kubectl delete -f nginx-deployment.yaml deployment.apps "nginx-deployment" deleted $ kubectl get deployments,replicasets,pods No resources found in default namespace.
Gestión Declarativa de Objetos con Kustomize
Kustomize es una herramienta introducida con Kubernetes 1.14 que pretende hacer más cómoda la gestión de manifiestos. Admite tres casos de uso diferentes:
-
Generar manifiestos a partir de otras fuentes. Por ejemplo, crear un ConfigMap y rellenar sus pares clave-valor a partir de un archivo de propiedades.
-
Añadir una configuración común a varios manifiestos. Por ejemplo, añadir un espacio de nombres y un conjunto de etiquetas para una Implementación y un Servicio.
-
Componer y personalizar una colección de manifiestos. Por ejemplo, establecer límites de recursos para múltiples Implementaciones.
El archivo central necesario para que Kustomize funcione es el archivo de kustomización. El nombre normalizado del fichero es kustomization.yaml
y no puede cambiarse. Un fichero de personalización define las reglas de procesamiento con las que trabaja la Personalización.
Kustomize está totalmente integrado con kubectl
y puede ejecutarse en dos modos: mostrando la salida del procesamiento en la consola o creando los objetos. Ambos modos pueden operar sobre un directorio, un tarball, un archivo Git o una URL, siempre que contengan el archivo de kustomización y los archivos de recursos referenciados:
- Renderización de la salida producida
-
El primer modo utiliza el subcomando
kustomize
para renderizar el resultado producido en la consola, pero no crea los objetos. Este comando funciona de forma similar a la opción de ejecución en seco que puedes conocer del comandorun
:$ kubectl kustomize <target>
- Crear los objetos
-
El segundo modo utiliza el comando
apply
junto con la opción de línea de comandos-k
para aplicar los recursos procesados por Kustomize, como se explica en la sección anterior:$ kubectl apply -k <target>
Las siguientes secciones demuestran cada uno de los casos de uso mediante un único ejemplo. Para una cobertura completa de todos los escenarios posibles, consulta la documentación o el repositorio GitHub de Personalizar.
Componer manifiestos
Una de las principales funcionalidades de Personalizar es crear un manifiesto compuesto a partir de otros manifiestos. Combinar varios manifiestos en uno solo puede no parecer tan útil por sí mismo, pero muchas de las otras funciones que se describen más adelante se basarán en esta capacidad. Supongamos que quieres componer un manifiesto a partir de un archivo de recursos de Implementación y un archivo de recursos de Servicio. Todo lo que tienes que hacer es colocar los archivos de recursos en la misma carpeta que el archivo de personalización:
. ├── kustomization.yaml ├── web-app-deployment.yaml └── web-app-service.yaml
El archivo de personalización enumera los recursos en la sección resources
, como se muestra en el Ejemplo 4-5.
Ejemplo 4-5. Un archivo de personalización que combina dos manifiestos
resources
:
-
web-app-deployment.yaml
-
web-app-service.yaml
Como resultado, el subcomando kustomize
muestra el manifiesto combinado que contiene todos los recursos separados por tres guiones (---
) para denotar las distintas definiciones de objetos:
$ kubectl kustomize ./ apiVersion: v1 kind: Service metadata: labels: app: web-app-service name: web-app-service spec: ports: - name: web-app-port port: 3000 protocol: TCP targetPort: 3000 selector: app: web-app type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: web-app-deployment name: web-app-deployment spec: replicas: 3 selector: matchLabels: app: web-app template: metadata: labels: app: web-app spec: containers: - env: - name: DB_HOST value: mysql-service - name: DB_USER value: root - name: DB_PASSWORD value: password image: bmuschko/web-app:1.0.1 name: web-app ports: - containerPort: 3000
Generar manifiestos a partir de otras fuentes
Anteriormente en este capítulo, aprendimos que los ConfigMap y los Secretos se pueden crear apuntándolos a un archivo que contenga los datos de configuración reales para él. Kustomize puede ayudar en el proceso mapeando la relación entre el manifiesto YAML de esos objetos de configuración y sus datos. Además, querremos inyectar el ConfigMap y el Secret creados en un Pod como variables de entorno. En esta sección, aprenderás cómo conseguirlo con la ayuda de Kustomize.
La siguiente estructura de archivos y directorios contiene el archivo de manifiesto del Pod y los archivos de datos de configuración que necesitamos para el ConfigMap y Secret. El archivo kustomization obligatorio vive en el nivel raíz del árbol de directorios:
. ├── config │ ├── db-config.properties │ └── db-secret.properties ├── kustomization.yaml └── web-app-pod.yaml
En kustomization.yaml
, puedes definir que el objeto ConfigMap y Secret se generen con el nombre dado. El nombre del ConfigMap será db-config
, y el nombre del Secreto será db-creds
. Ambos atributos del generador, configMapGenerator
y secretGenerator
, hacen referencia a un archivo de entrada utilizado para introducir los datos de configuración. Cualquier recurso adicional puede especificarse con el atributo resources
. El ejemplo 4-6 muestra el contenido del archivo de personalización.
Ejemplo 4-6. Un archivo de personalización utilizando un ConfigMap y un generador de secretos
configMapGenerator
:
-
name
:
db-config
files
:
-
config/db-config.properties
secretGenerator
:
-
name
:
db-creds
files
:
-
config/db-secret.properties
resources
:
-
web-app-pod.yaml
Kustomize genera ConfigMaps y Secretos añadiendo un sufijo al nombre. Puedes ver este comportamiento al crear los objetos utilizando el comando apply
. Se puede hacer referencia al ConfigMap y al Secreto por su nombre en el manifiesto del Pod:
$ kubectl apply -k ./ configmap/db-config-t4c79h4mtt unchanged secret/db-creds-4t9dmgtf9h unchanged pod/web-app created
Esta estrategia de nomenclatura puede configurarse con el atributo generatorOptions
en el archivo de personalización. Consulta la documentación para obtener másinformación.
Probemos también el subcomando kustomize
. En lugar de crear los objetos, el comando muestra la salida procesada en la consola:
$ kubectl kustomize ./ apiVersion: v1 data: db-config.properties: |- DB_HOST: mysql-service DB_USER: root kind: ConfigMap metadata: name: db-config-t4c79h4mtt --- apiVersion: v1 data: db-secret.properties: REJfUEFTU1dPUkQ6IGNHRnpjM2R2Y21RPQ== kind: Secret metadata: name: db-creds-4t9dmgtf9h type: Opaque --- apiVersion: v1 kind: Pod metadata: labels: app: web-app name: web-app spec: containers: - envFrom: - configMapRef: name: db-config-t4c79h4mtt - secretRef: name: db-creds-4t9dmgtf9h image: bmuschko/web-app:1.0.1 name: web-app ports: - containerPort: 3000 protocol: TCP restartPolicy: Always
Añadir una configuración común a varios manifiestos
Los desarrolladores de aplicaciones suelen trabajar en un conjunto de pilas de aplicaciones compuesto por múltiples manifiestos. Por ejemplo, una pila de aplicaciones podría constar de un microservicio frontend, un microservicio backend y una base de datos. Es práctica habitual utilizar la misma configuración transversal para cada uno de los manifiestos. Kustomize ofrece una serie de campos compatibles (por ejemplo, espacio de nombres, etiquetas o anotaciones). Consulta la documentación para conocer todos los campos admitidos.
Para el siguiente ejemplo, supondremos que una Implementación y un Servicio viven en el mismo espacio de nombres y utilizan un conjunto común de etiquetas. El espacio de nombres se llama persistence
y la etiqueta es el par clave-valor team: helix
. El Ejemplo 4-7 ilustra cómo establecer esos campos comunes en el archivo de personalización.
Ejemplo 4-7. Un fichero de personalización que utiliza un campo común
namespace
:
persistence
commonLabels
:
team
:
helix
resources
:
-
web-app-deployment.yaml
-
web-app-service.yaml
Para crear los objetos referenciados en el archivo de personalización, ejecuta el comando apply
. Asegúrate de crear previamente el espacio de nombres persistence
:
$ kubectl create namespace persistence namespace/persistence created $ kubectl apply -k ./ service/web-app-service created deployment.apps/web-app-deployment created
La representación YAML de los archivos procesados tiene el siguiente aspecto:
$ kubectl kustomize ./ apiVersion: v1 kind: Service metadata: labels: app: web-app-service team: helix name: web-app-service namespace: persistence spec: ports: - name: web-app-port port: 3000 protocol: TCP targetPort: 3000 selector: app: web-app team: helix type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: web-app-deployment team: helix name: web-app-deployment namespace: persistence spec: replicas: 3 selector: matchLabels: app: web-app team: helix template: metadata: labels: app: web-app team: helix spec: containers: - env: - name: DB_HOST value: mysql-service - name: DB_USER value: root - name: DB_PASSWORD value: password image: bmuschko/web-app:1.0.1 name: web-app ports: - containerPort: 3000
Personalizar una colección de manifiestos
Kustomize puede fusionar el contenido de un manifiesto YAML con un fragmento de código de otro manifiesto YAML. Los casos de uso típicos incluyen añadir la configuración del contexto de seguridad a la definición de un Pod o establecer límites de recursos para una Implementación. El archivo de personalización permite especificar distintas estrategias de parcheo comopatchesStrategicMerge
y patchesJson6902
. Para profundizar en las diferencias entre las estrategias de parcheado, consulta la documentación.
El ejemplo 4-8 muestra el contenido de un archivo de personalización que parchea una definición de Implementación en el archivo nginx-deployment.yaml
con el contenido del archivo security-context.yaml
.
Ejemplo 4-8. Un archivo de personalización que define un parche
resources
:
-
nginx-deployment.yaml
patchesStrategicMerge
:
-
security-context.yaml
El archivo de parche que se muestra en el Ejemplo 4-9 define un contexto de seguridad a nivel de contenedor para la plantilla Pod de la Implementación. En tiempo de ejecución, la estrategia de parche intenta encontrar el contenedor llamado nginx
y mejora la configuración adicional.
Ejemplo 4-9. El manifiesto YAML del parche
apiVersion
:
apps/v1
kind
:
Deployment
metadata
:
name
:
nginx-deployment
spec
:
template
:
spec
:
containers
:
-
name
:
nginx
securityContext
:
runAsUser
:
1000
runAsGroup
:
3000
fsGroup
:
2000
El resultado es una definición de Implementación parcheada, como se muestra en la salida del subcomandokustomize
que se muestra a continuación. El mecanismo del parche puede aplicarse a otros archivos que requieran una definición de contexto de seguridad uniforme:
$ kubectl kustomize ./ apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx:1.14.2 name: nginx ports: - containerPort: 80 securityContext: fsGroup: 2000 runAsGroup: 3000 runAsUser: 1000
Herramientas comunes de plantillas
Como se ha demostrado en la sección anterior, Kustomize ofrece la funcionalidad de plantillas. El ecosistema Kubernetes ofrece otras soluciones al problema que trataremos aquí. Hablaremos del procesador YAML yq
y del motor de plantillas Helm.
Utilizar el procesador YAML yq
La herramienta yq
se utiliza para leer, modificar y mejorar el contenido de un archivo YAML. Esta sección demostrará los tres casos de uso. Para ver una lista detallada de ejemplos de uso, consulta el repositorio de GitHub. Durante el examen CKA, es posible que se te pida que apliques esas técnicas, aunque no se espera que comprendas todos los entresijos de las herramientas en cuestión. La versión de yq
utilizada para describir la funcionalidad que se describe a continuación es la 4.2.1.
Valores de lectura
La lectura de valores de un archivo YAML existente requiere el uso de una expresión de ruta YAML. Una expresión de ruta te permite navegar en profundidad por la estructura YAML y extraer el valor de un atributo que estés buscando. El Ejemplo 4-10 muestra el manifiesto YAML de un Pod que define dos variables de entorno.
Ejemplo 4-10. El manifiesto YAML de un Pod
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
spring-boot-app
spec
:
containers
:
-
image
:
bmuschko/spring-boot-app:1.5.3
name
:
spring-boot-app
env
:
-
name
:
SPRING_PROFILES_ACTIVE
value
:
prod
-
name
:
VERSION
value
:
'1.5.3'
Para leer un valor, utiliza el comando eval
o la forma abreviada e
, proporciona la expresión de ruta YAML y apunta al archivo fuente. Los dos comandos siguientes leen el nombre del Pod y el valor de la segunda variable de entorno definida por un único contenedor. Ten en cuenta que la expresión de la ruta debe comenzar con un carácter obligatorio de punto (.
) para denotar el nodo raíz de la estructura YAML:
$ yq e .metadata.name pod.yaml spring-boot-app $ yq e .spec.containers[0].env[1].value pod.yaml 1.5.3
Modificar valores
Modificar un valor existente es tan fácil como utilizar el mismo comando y añadir la bandera -i
. La asignación del nuevo valor a un atributo se realiza asignándolo a la expresión de ruta. El siguiente comando cambia la segunda variable de entorno del archivo Pod YAML al valor 1.6.0:
$ yq e -i .spec.containers[0].env[1].value = "1.6.0" pod.yaml $ cat pod.yaml ... env: - name: SPRING_PROFILES_ACTIVE value: prod - name: VERSION value: 1.6.0
Fusionar archivos YAML
Al igual que Kustomize, yq
puede fusionar varios archivos YAML. Definitivamente, Personalizar es más potente y cómodo de usar; sin embargo, yq
puede resultar útil para proyectos más pequeños. Supongamos que quieres fusionar la definición de contenedor sidecar mostrada en el Ejemplo 4-11 en el archivo YAML de Pod.
Ejemplo 4-11. El manifiesto YAML de una definición de contenedor
spec
:
containers
:
-
image
:
envoyproxy/envoy:v1.19.1
name
:
proxy-container
ports
:
-
containerPort
:
80
El comando para conseguirlo es eval-all
. No entraremos en detalles dada la multitud de opciones de configuración de este comando. Para profundizar, consulta el manual de usuario de yq
sobre la operación "Multiplicar (Fusionar)". El siguiente comando añade el contenedor sidecar a la matriz de contenedores existente en el manifiesto Pod:
$ yq eval-all 'select(fileIndex == 0) *+ select(fileIndex == 1)' pod.yaml \ sidecar.yaml apiVersion: v1 kind: Pod metadata: name: spring-boot-app spec: containers: - image: bmuschko/spring-boot-app:1.5.3 name: spring-boot-app env: - name: SPRING_PROFILES_ACTIVE value: prod - name: VERSION value: '1.5.3' - image: envoyproxy/envoy:v1.19.1 name: proxy-container ports: - containerPort: 80
Usar Helm
Helm es un motor de plantillas y gestor de paquetes para un conjunto de manifiestos de Kubernetes. En tiempo de ejecución, sustituye los marcadores de posición de los archivos de plantilla YAML por valores reales definidos por el usuario final. El artefacto producido por el ejecutable Helm es un denominado archivo gráfico que agrupa los manifiestos que componen los recursos API de una aplicación. Este archivo gráfico puede cargarse en un gestor de paquetes para utilizarlo durante el proceso de implementación. El ecosistema Helm ofrece una amplia gama de gráficos reutilizables para casos de uso comunes en un repositorio central de gráficos (por ejemplo, para ejecutar Grafana o PostgreSQL).
Debido a la gran cantidad de funciones de que dispone Helm, sólo trataremos las más básicas. El examen CKA no espera que seas un experto en Helm, sino que estés familiarizado con sus ventajas y conceptos. Para obtener información más detallada sobre Helm, consulta la documentación de usuario. La versión de Helm utilizada para describir la funcionalidad aquí es la 3.7.0.
Estructura estándar del gráfico
Un gráfico debe seguir una estructura de directorios predefinida. Puedes elegir cualquier nombre para el directorio raíz. Dentro del directorio, deben existir dos archivos: Chart.yaml
y values.yaml
. El archivo Chart.yaml
describe la metainformación del gráfico (por ejemplo, nombre y versión). El archivo values.yaml
contiene los pares clave-valor utilizados en tiempo de ejecución para sustituir los marcadores de posición en los manifiestos YAML. Cualquier archivo de plantilla destinado a ser empaquetado en el archivo del gráfico debe colocarse en el directorio templates
. Los archivos ubicados en el directorio template
no tienen que seguir ninguna convención de nombres.
La siguiente estructura de directorios muestra un gráfico de ejemplo. El directorio templates
contiene un archivo para un Pod y un Servicio:
$ tree . ├── Chart.yaml ├── templates │ ├── web-app-pod-template.yaml │ └── web-app-service-template.yaml └── values.yaml
El archivo gráfico
El archivo Chart.yaml
describe el gráfico a alto nivel. Los atributos obligatorios incluyen la versión API del gráfico, el nombre y la versión. Además, se pueden proporcionar atributos opcionales. Para obtener una lista completa de atributos, consulta la documentación correspondiente. El Ejemplo 4-12 muestra lo mínimo de un archivo de gráfico.
Ejemplo 4-12. Un archivo básico de gráficos Helm
apiVersion
:
1.0.0
name
:
web-app
version
:
2.5.4
El archivo de valores
El archivo values.yaml
define los pares clave-valor que se utilizarán para sustituir los marcadores de posición en los archivos de plantilla YAML. El Ejemplo 4-13 especifica cuatro pares clave-valor. Ten en cuenta que el archivo puede estar vacío si no quieres sustituir valores en tiempo de ejecución.
Ejemplo 4-13. Un archivo de valores Helm
db_host
:
mysql-service
db_user
:
root
db_password
:
password
service_port
:
3000
Los archivos de plantilla
Los archivos de plantilla deben estar en el directorio templates
. Un archivo de plantilla es un manifiesto YAML normal que puede (opcionalmente) definir marcadores de posición con la ayuda de llaves dobles ({{ }}
). Para hacer referencia a un valor del archivo values.yaml
, utiliza la expresión {{ .Values.<key> }}
. Por ejemplo, para sustituir el valor de la clave db_host
en tiempo de ejecución, utiliza la expresión {{ .Values.db_host }}
. El ejemplo 4-14 define un Pod como plantilla y tres marcadores de posición que hacen referencia a valores de values.yaml
.
Ejemplo 4-14. El manifiesto de plantilla YAML de un Pod
apiVersion
:
v1
kind
:
Pod
metadata
:
labels
:
app
:
web-app
name
:
web-app
spec
:
containers
:
-
image
:
bmuschko/web-app:1.0.1
name
:
web-app
env
:
-
name
:
DB_HOST
value
:
{{
.Values.db_host
}}
-
name
:
DB_USER
value
:
{{
.Values.db_user
}}
-
name
:
DB_PASSWORD
value
:
{{
.Values.db_password
}}
ports
:
-
containerPort
:
3000
protocol
:
TCP
restartPolicy
:
Always
Ejecutar comandos de Helm
El ejecutable Helm viene con una amplia gama de comandos. Vamos a demostrar algunos de ellos. El comando template
representa localmente las plantillas de gráficos y muestra los resultados en la consola. Puedes ver la operación en acción en la siguiente salida. Todos los marcadores de posición se han sustituido por sus valores reales obtenidos del archivo values.yaml
:
$ helm template . --- # Source: Web Application/templates/web-app-service-template.yaml ... --- # Source: Web Application/templates/web-app-pod-template.yaml apiVersion: v1 kind: Pod metadata: labels: app: web-app name: web-app spec: containers: - image: bmuschko/web-app:1.0.1 name: web-app env: - name: DB_HOST value: mysql-service - name: DB_USER value: root - name: DB_PASSWORD value: password ports: - containerPort: 3000 protocol: TCP restartPolicy: Always
Cuando estés satisfecho con el resultado, deberás agrupar los archivos de plantilla en un archivo de gráficos. El archivo de gráficos es un archivo TAR comprimido con la terminación .tgz
. El comando package
evalúa la información de metadatos de Chart.yaml
para obtener el nombre del archivo de gráficos:
$ helm package . Successfully packaged chart and saved it to: /Users/bmuschko/dev/projects/ \ cka-study-guide/ch04/templating-tools/helm/web-app-2.5.4.tgz
Para obtener una lista completa de comandos y flujos de trabajo típicos, consulta la página de documentación de Helm.
Resumen
Los límites de recursos son uno de los muchos factores que el algoritmo kube-scheduler tiene en cuenta a la hora de tomar decisiones sobre en qué nodo se puede programar un Pod. Un contenedor puede especificar solicitudes y límites de recursos. El programador elige un nodo en función de su capacidad de hardware disponible.
La gestión declarativa de manifiestos es la forma preferida de crear, modificar y eliminar objetos en proyectos nativos en la nube del mundo real. El manifiesto YAML subyacente está pensado para ser comprobado en el control de versiones y rastrea automáticamente los cambios realizados en un objeto, incluyendo su marca de tiempo para un hash de confirmación correspondiente. Los comandos kubectl apply
y delete
pueden realizar esas operaciones para uno o varios manifiestos YAML.
Surgieron herramientas adicionales para una gestión más cómoda de los manifiestos. Kustomize está totalmente integrado en la cadena de herramientas de kubectl
. Ayuda a generar, componer y personalizar manifiestos. Las herramientas con capacidad de creación de plantillas, como yq
y Helm, pueden facilitar aún más diversos flujos de trabajo para gestionar pilas de aplicaciones representadas por un conjunto de manifiestos.
Aspectos esenciales del examen
- Comprender los efectos de los límites de los recursos en la programación
-
Un contenedor definido por un Pod puede especificar solicitudes y límites de recursos. Trabaja con escenarios en los que definas esos límites de forma individual y conjunta para Pods de uno o varios contenedores. Tras la creación del Pod, deberías ser capaz de ver los efectos sobre la programación del objeto en un nodo. Además, practica cómo identificar la capacidad de recursos disponible de un nodo.
- Gestionar objetos utilizando el enfoque imperativo y declarativo
-
Los manifiestos YAML son esenciales para expresar el estado deseado de un objeto. Tendrás que entender cómo crear, actualizar y eliminar objetos utilizando el comando
kubectl apply
. El comando puede apuntar a un único archivo de manifiesto o a un directorio que contenga varios archivos de manifiesto. - Tener una comprensión de alto nivel de las herramientas comunes de plantillas
-
Kustomize,
yg
y Helm son herramientas consolidadas para gestionar manifiestos YAML. Su funcionalidad de plantillas soporta escenarios complejos como la composición y fusión de múltiples manifiestos. Para el examen, echa un vistazo práctico a las herramientas, su funcionalidad y los problemas que resuelven.
Ejercicios de muestra
Las soluciones a estos ejercicios están disponibles en el Apéndice.
-
Escribe un manifiesto para un nuevo Pod llamado
ingress-controller
con un único contenedor que utilice la imagenbitnami/nginx-ingress-controller:1.0.0
. Para el contenedor, establece la petición de recursos en 256Mi de memoria y 1 CPU. Establece los límites de recursos en 1024Mi de memoria y 2,5 CPU. -
Utilizando el manifiesto, programa el Pod en un cluster con tres nodos. Una vez creado, identifica el nodo que ejecuta el Pod. Escribe el nombre del nodo en el archivo
node.txt
. -
Crea el directorio
manifests
. Dentro del directorio, crea dos archivos:pod.yaml
yconfigmap.yaml
. El archivopod.yaml
debe definir un Pod llamadonginx
con la imagennginx:1.21.1
. El archivoconfigmap.yaml
define un ConfigMap llamadologs-config
con el par clave-valordir=/etc/logs/traffic.log
. Crea ambos objetos con un único comando declarativo. -
Modifica el manifiesto ConfigMap cambiando el valor de la clave
dir
por/etc/logs/traffic-log.txt
. Aplica los cambios. Elimina ambos objetos con un único comando declarativo. -
Utiliza Personalizar para establecer un espacio de nombres común
t012
para el archivo de recursospod.yaml
. El archivopod.yaml
define el Pod llamadonginx
con la imagennginx:1.21.1
sin un espacio de nombres. Ejecuta el comando Kustomize que muestra el manifiesto transformado en la consola.
Get Guía de estudio del Administrador Certificado de Kubernetes (CKA) now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.