Versión imprimible multipagina. Haga click aquí para imprimir.
Configuración
- 1: Prácticas Recomendadas de Configuración
- 2: ConfigMaps
- 3: Sobrecarga de Pod
- 4: Administrando los recursos de los contenedores
- 5: Secrets
- 6: Organizar el acceso a los clústeres utilizando archivos kubeconfig
1 - Prácticas Recomendadas de Configuración
Este documento destaca y consolida las prácticas recomendadas de configuración que se presentan a lo largo de la guía del usuario, la documentación de Introducción y los ejemplos.
Este es un documento vivo. Si se te ocurre algo que no está en esta lista pero que puede ser útil a otros, no dudes en crear un issue o enviar un PR.
Consejos Generales de Configuración
-
Al definir configuraciones, especifica la última versión estable de la API.
-
Los archivos de configuración deben almacenarse en el control de versiones antes de enviarse al clúster. Este le permite revertir rápidamente un cambio de configuración si es necesario. También ayuda a la recreación y restauración del clúster.
-
Escribe tus archivos de configuración usando YAML en lugar de JSON. Aunque estos formatos se pueden utilizarse indistintamente en casi todos los escenarios, YAML tiende a ser más amigable con el usuario.
-
Agrupa los objetos relacionados en un solo archivo siempre que tenga sentido. Un archivo suele ser más fácil de administrar que varios. Ver el archivo guestbook-all-in-one.yaml como un ejemplo de esta sintaxis.
-
Ten en cuenta también que se pueden llamar muchos comandos
kubectl
en un directorio. Por ejemplo, puedes llamarkubectl apply
en un directorio de archivos de configuración. -
No especifiques valores predeterminados innecesariamente: una configuración simple y mínima hará que los errores sean menos probables.
-
Coloca descripciones de objetos en anotaciones, para permitir una mejor introspección.
"Naked" Pods vs ReplicaSets, Deployments y Jobs
-
No usar "Naked" Pods (es decir, Pods no vinculados a un ReplicaSet o a un Deployment) si puedes evitarlo. Los Naked Pods no se reprogramará en caso de falla de un nodo.
Un Deployment, que crea un ReplicaSet para garantizar que se alcance la cantidad deseada de Pods está siempre disponible y especifica una estrategia para reemplazar los Pods (como RollingUpdate), es casi siempre preferible a crear Pods directamente, excepto por algunos explícitos
restartPolicy: Never
escenarios. Un Job también puede ser apropiado.
Servicios
-
Crea un Service antes de tus cargas de trabajo de backend correspondientes (Deployments o ReplicaSets) y antes de cualquier carga de trabajo que necesite acceder a él. Cuando Kubernetes inicia un contenedor, proporciona variables de entorno que apuntan a todos los Services que se estaban ejecutando cuando se inició el contenedor. Por ejemplo, si existe un Service llamado
foo
, todos los contenedores obtendrán las siguientes variables en su entorno inicial:FOO_SERVICE_HOST=<el host en el que se ejecuta el Service> FOO_SERVICE_PORT=<el puerto en el que se ejecuta el Service>
* Esto implica un requisito de ordenamiento - cualquier
Service
al que unPod
quiera acceder debe ser creado antes delPod
en sí mismo, de lo contrario, las variables de entorno no se completarán. El DNS no tiene esta restricción. -
Un cluster add-on opcional (aunque muy recomendable) es un servidor DNS. El servidor DNS observa la API de Kubernetes en busca de nuevos
Servicios
y crea un conjunto de registros DNS para cada uno. Si el DNS se ha habilitado en todo el clúster, todos losPods
deben ser capaces de hacer la resolución de nombres deServices
automáticamente. -
No especifiques un
hostPort
para un Pod a menos que sea absolutamente necesario. Cuando vinculas un Pod a unhostPort
, limita la cantidad de lugares en los que se puede agendar el Pod, porque cada combinación <hostIP
,hostPort
,protocol
> debe ser única. Si no especificas elhostIP
yprotocol
explícitamente, Kubernetes usará0.0.0.0
como elhostIP
predeterminado yTCP
como elprotocol
por defecto.Si solo necesitas acceder al puerto con fines de depuración, puedes utilizar el apiserver proxy o
kubectl port-forward
.Si necesitas exponer explícitamente el puerto de un Pod en el nodo, considera usar un NodePort Service antes de recurrir a
hostPort
. -
Evita usar
hostNetwork
, por las mismas razones quehostPort
. -
Usa headless Services (que tiene un
ClusterIP
deNone
) para el descubrimiento de servicios cuando no necesites balanceo de cargakube-proxy
.
Usando Labels
-
Define y usa labels que identifiquen atributos semánticos de tu aplicación o Deployment, como
{ app.kubernetes.io/name: MyApp, tier: frontend, phase: test, deployment: v3 }
. Puedes utilizar estas labels para seleccionar los Pods apropiados para otros recursos; por ejemplo, un Service que selecciona todo los Podstier: frontend
, o todos los componentesphase: test
deapp.kubernetes.io/name: MyApp
. Revisa el libro de visitas para ver ejemplos de este enfoque.Un Service puede hacer que abarque múltiples Deployments omitiendo las labels específicas de la versión de su selector. Cuando necesites actualizar un servicio en ejecución sin downtime, usa un Deployment.
Un estado deseado de un objeto se describe mediante una implementación, y si los cambios a esa especificación son aplicados, el controlador de implementación cambia el estado actual al estado deseado en un ritmo controlado.
-
Use las labels comunes de Kubernetes para casos de uso común. Estas labels estandarizadas enriquecen los metadatos de una manera que permite que las herramientas, incluyendo
kubectl
y el dashboard, trabajen de forma interoperable. -
Puedes manipular las labels para la depuración. Debido a que los controladores de Kubernetes (como ReplicaSet) y los Services coinciden con los Pods usando labels de selector, se detendrá la eliminación de las labels relevantes de un Pod que sea considerado por un controlador o que un Service sirva tráfico. si quitas las labels de un Pod existente, su controlador creará un nuevo Pod para ocupar su lugar. Esto es un forma útil de depurar un Pod previamente "vivo" en un entorno de "cuarentena". Para eliminar interactivamente o agregar labels, usa
kubectl label
.
Usando kubectl
-
Usa
kubectl apply -f <directorio>
. Esto busca la configuración de Kubernetes en todos los.yaml
,.yml
, y.json
en<directorio>
y lo pasa aapply
. -
Usa selectores de labels para las operaciones
get
ydelete
en lugar de nombres de objetos específicos. Ve las secciones en selectores de labels y usar labels de forma eficaz. -
Usa
kubectl create deployment
ykubectl expose
para crear rápidamente un contenedor único Deployments y Services. Consulta Usar un Service para Acceder a una Aplicación en un Clúster para un ejemplo.
2 - ConfigMaps
Un configmap es un objeto de la API utilizado para almacenar datos no confidenciales en el formato clave-valor. Los Pods pueden utilizar los ConfigMaps como variables de entorno, argumentos de la linea de comandos o como ficheros de configuración en un Volumen.
Un ConfigMap te permite desacoplar la configuración de un entorno específico de una imagen de contenedor, así las aplicaciones son fácilmente portables.
Motivo
Utiliza un ConfigMap para crear una configuración separada del código de la aplicación.
Por ejemplo, imagina que estás desarrollando una aplicación que puedes correr en
tu propio equipo (para desarrollo) y en el cloud (para mantener tráfico real).
Escribes el código para configurar una variable llamada DATABASE_HOST
.
En tu equipo configuras la variable con el valor localhost
.
En el cloud, la configuras con referencia a un kubernetes
Service que expone el componente
de la base de datos en tu cluster.
Esto permite tener una imagen corriendo en un cloud y tener el mismo código localmente para checkearlo si es necesario.
Objeto ConfigMap
Un ConfigMap es un objeto de la API
que permite almacenar la configuración de otros objetos utilizados. Aunque muchos
objetos de kubernetes que tienen un spec
, un ConfigMap tiene una sección data
para
almacenar items, identificados por una clave, y sus valores.
El nombre del ConfigMap debe ser un nombre de subdominio DNS válido.
ConfigMaps y Pods
Puedes escribir un Pod spec
y referenciarlo a un ConfigMap y configurar el contenedor(es)
de ese Pod en base a los datos del ConfigMap. El Pod y el ConfigMap deben estar en
el mismo Namespace.
Este es un ejemplo de ConfigMap que tiene algunas claves con un valor simple, y otras claves donde el valor tiene un formato de un fragmento de configuración.
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# property-like keys; each key maps to a simple value
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
#
# file-like keys
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
Hay cuatro maneras diferentes de usar un ConfigMap para configurar un contenedor dentro de un Pod:
- Argumento en la linea de comandos como entrypoint de un contenedor
- Variable de entorno de un contenedor
- Como fichero en un volumen de solo lectura, para que lo lea la aplicación
- Escribir el código para ejecutar dentro de un Pod que utiliza la API para leer el ConfigMap
Estos diferentes mecanismos permiten utilizar diferentes métodos para modelar los datos que se van a usar. Para los primeros tres mecanismos, el kubelet utiliza la información del ConfigMap cuando lanza un contenedor (o varios) en un Pod.
Para el cuarto método, tienes que escribir el código para leer el ConfigMap y sus datos. Sin embargo, como estás utilizando la API de kubernetes directamente, la aplicación puede suscribirse para obtener actualizaciones cuando el ConfigMap cambie, y reaccionar cuando esto ocurra. Accediendo directamente a la API de kubernetes, esta técnica también permite acceder al ConfigMap en diferentes namespaces.
En el siguiente ejemplo el Pod utiliza los valores de game-demo
para configurar el contenedor:
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: game.example/demo-game
env:
# Define the environment variable
- name: PLAYER_INITIAL_LIVES # Notice that the case is different here
# from the key name in the ConfigMap.
valueFrom:
configMapKeyRef:
name: game-demo # The ConfigMap this value comes from.
key: player_initial_lives # The key to fetch.
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# You set volumes at the Pod level, then mount them into containers inside that Pod
- name: config
configMap:
# Provide the name of the ConfigMap you want to mount.
name: game-demo
# An array of keys from the ConfigMap to create as files
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"
Un ConfigMap no diferencia entre las propiedades de una linea individual y un fichero con múltiples lineas y valores. Lo importante es como los Pods y otros objetos consumen estos valores.
Para este ejemplo, definimos un Volumen y lo montamos dentro del contenedor
demo
como /config
creando dos ficheros,
/config/game.properties
y /config/user-interface.properties
,
aunque haya cuatro claves en el ConfigMap. Esto es debido a que enla definición
del Pod se especifica el array items
en la sección volumes
.
Si quieres omitir el array items
entero, cada clave del ConfigMap se convierte en
un fichero con el mismo nombre que la clave, y tienes 4 ficheros.
Usando ConfigMaps
Los ConfigMaps pueden montarse como volúmenes. También pueden ser utilizados por otras partes del sistema, sin ser expuestos directamente al Pod. Por ejemplo, los ConfigMaps pueden contener información para que otros elementos del sistema utilicen para su configuración.
La manera más común de usar los Configmaps es para configurar los contenedores que están corriendo en un Pod en el mismo Namespace. También se pueden usar por separado.
Por ejemplo, quizá encuentres AddOns u Operadores que ajustan su comportamiento en base a un ConfigMap.
Usando ConfigMaps como ficheros en un Pod
Para usar un ConfigMap en un volumen en un Pod:
- Crear un ConfigMap o usar uno que exista. Múltiples Pods pueden utilizar el mismo ConfigMap.
- Modifica la configuración del Pod para añadir el volumen en
.spec.volumes[]
. Pon cualquier nombre al Volumen, y tienes un campo.spec.volumes[].configMap.name
configurado con referencia al objeto ConfigMap. - Añade un
.spec.containers[].volumeMounts[]
a cada contenedor que necesite el ConfigMap. Especifica.spec.containers[].volumeMounts[].readOnly = true
y.spec.containers[].volumeMounts[].mountPath
en un directorio sin uso donde quieras que aparezca el ConfigMap. - Modifica la imagen o el comando utilizado para que el programa busque los ficheros en el directorio. Cada clave del ConfigMap
data
se convierte en un un fichero en elmountPath
.
En este ejemplo, el Pod monta un ConfigMap como un volumen:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
configMap:
name: myconfigmap
Cada ConfigMap que quieras utilizar debe estar referenciado en .spec.volumes
.
Si hay múltiples contenedores en el Pod, cada contenedor tiene su propio
bloque volumeMounts
, pero solo un .spec.volumes
es necesario por cada ConfigMap.
ConfigMaps montados son actualizados automáticamente
Cuando un ConfigMap está siendo utilizado en un volumen y es actualizado, las claves son actualizadas también.
El kubelet comprueba si el ConfigMap montado está actualizado cada periodo de sincronización.
Sin embargo, el kubelet utiliza su caché local para obtener el valor actual del ConfigMap.
El tipo de caché es configurable usando el campo ConfigMapAndSecretChangeDetectionStrategy
en el
KubeletConfiguration struct.
Un ConfigMap puede ser propagado por vista (default), ttl-based, o simplemente redirigiendo
todas las consultas directamente a la API.
Como resultado, el retraso total desde el momento que el ConfigMap es actualizado hasta el momento
que las nuevas claves son proyectadas en el Pod puede ser tan largo como la sincronización del Pod
- el retraso de propagación de la caché, donde la propagación de la caché depende del tipo de caché elegido (es igual al retraso de propagación, ttl de la caché, o cero correspondientemente).
Kubernetes v1.18 [alpha]
La característica alpha de kubernetes Immutable Secrets and ConfigMaps provee una opción para configurar Secrets individuales y ConfigMaps como inmutables. Para los Clústeres que usan ConfigMaps como extensión (al menos decenas o cientos de un único ConfigMap montado en Pods), previene cambios en sus datos con las siguientes ventajas:
- protección de actualizaciones accidentales (o no deseadas) que pueden causar caídas de aplicaciones
- mejora el rendimiento del Clúster significativamente reduciendo la carga del kube-apiserver, cerrando las vistas para el ConfigMap marcado como inmutable.
Para usar esta característica, habilita el ImmutableEmphemeralVolumes
feature gate y configura
el campo del Secret o ConfigMap immutable
como true
. Por ejemplo:
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
immutable: true
data
. Solo se puede eliminar y recrear el ConfigMap.
Los Pods existentes mantiene un punto de montaje del ConfigMap eliminado - es recomendable
recrear los Pods.
Siguientes pasos
- Leer sobre Secrets.
- Leer Configure a Pod to Use a ConfigMap.
- Leer The Twelve-Factor App para entender el motivo de separar el código de la configuración.
3 - Sobrecarga de Pod
Kubernetes v1.16 [alpha]
Cuando se está ejecutando un Pod en un nodo, el Pod por sí mismo utiliza una cantidad de recursos del sistema. Estos recursos son adicionales a los recursos necesarios para hacer funcionar el/los contenedor(es) dentro del Pod. La Sobrecarga de Pod es una característica para contabilizar los recursos consumidos por la infraestructura de Pods que están por encima de los valores de Requests y Limits del/los contenedor(es).
Sobrecarga de Pod
En Kubernetes, la sobrecarga de Pod se configura en el tiempo de admisión con respecto a la sobrecarga asociada con el RuntimeClass del Pod.
Cuando se habilita la opción de sobrecarga de Pod, se considera tanto la propia sobrecarga como la suma de solicitudes de recursos del contenedor al programar el Pod. Del mismo modo, Kubelet incluirá la sobrecarga de Pod cuando se dimensione el cgroup del Pod, y cuando se realice la clasificación de la expulsión de Pods.
Configuración
Debe asegurarse de que el Feature Gate PodOverhead
esté activado (su valor está desactivado de manera predeterminada) en todo el clúster. Esto significa:
- en el kube-scheduler
- en el kube-apiserver
- en el kubelet de cada nodo
- en cualquier servidor de API personalizado que necesite Feature Gates.
4 - Administrando los recursos de los contenedores
Cuando especificas un Pod, opcionalmente puedes especificar los recursos que necesita un Contenedor. Los recursos que normalmente se definen son CPU y memoria (RAM); pero hay otros.
Cuando especificas el recurso request para Contenedores en un Pod, el Scheduler de Kubernetes usa esta información para decidir en qué nodo colocar el Pod. Cuando especificas el recurso limit para un Contenedor, Kubelet impone estos límites, así que el contenedor no puede utilizar más recursos que el límite que le definimos. Kubelet también reserva al menos la cantidad especificada en request para el contenedor.
Peticiones y límites
Si el nodo donde está corriendo un pod tiene suficientes recursos disponibles, es posible
(y válido) que el contenedor utilice más recursos de los especificados en request
.
Sin embargo, un contenedor no está autorizado a utilizar más de lo especificado en limit
.
Por ejemplo, si configuras una petición de memory
de 256 MiB para un contenedor, y ese contenedor está
en un Pod colocado en un nodo con 8GiB de memoria y no hay otros Pod, entonces el contenedor puede intentar usar
más RAM.
Si configuras un límite de memory
de 4GiB para el contenedor, kubelet)
(y
motor de ejecución del contenedor) impone el límite.
El Runtime evita que el contenedor use más recursos de los configurados en el límite. Por ejemplo:
cuando un proceso en el contenedor intenta consumir más cantidad de memoria de la permitida,
el Kernel del sistema termina el proceso que intentó la utilización de la memoria, con un error de out of memory (OOM).
Los límites se pueden implementar de forma reactiva (el sistema interviene cuando ve la violación) o por imposición (el sistema previene al contenedor de exceder el límite). Diferentes Runtimes pueden tener distintas implementaciones a las mismas restricciones.
Tipos de recursos
CPU y memoria son cada uno un tipo de recurso. Un tipo de recurso tiene una unidad base. CPU representa procesos de computación y es especificada en unidades de Kubernetes CPUs. Memoria es especificada en unidades de bytes. Si estás usando Kubernetes v1.14 o posterior, puedes especificar recursos huge page. Huge pages son una característica de Linux específica donde el kernel del nodo asigna bloques de memoria que son más grandes que el tamaño de paginación por defecto.
Por ejemplo, en un sistema donde el tamaño de paginación por defecto es de 4KiB, podrías
especificar un límite, hugepages-2Mi: 80Mi
. Si el contenedor intenta asignar
más de 40 2MiB huge pages (un total de 80 MiB), la asignación fallará.
hugepages-*
.
A diferencia de los recursos de memoria
y cpu
.
CPU y memoria son colectivamente conocidos como recursos de computación, o simplemente como recursos. Los recursos de computación son cantidades medibles que pueden ser solicitadas, asignadas y consumidas. Son distintas a los Recursos API. Los recursos API , como Pods y Services son objetos que pueden ser leídos y modificados a través de la API de Kubernetes.
Peticiones y límites de recursos de Pods y Contenedores
Cada contenedor de un Pod puede especificar uno o más de los siguientes:
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.limits.hugepages-<size>
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
spec.containers[].resources.requests.hugepages-<size>
Aunque las peticiones y límites pueden ser especificadas solo en contenedores individuales, es conveniente hablar sobre los recursos de peticiones y límites del Pod. Un limite/petición de recursos de un Pod para un tipo de recurso particular es la suma de peticiones/límites de cada tipo para cada contenedor del Pod.
Unidades de recursos en Kubernetes
Significado de CPU
Límites y peticiones para recursos de CPU son medidos en unidades de cpu. Una cpu, en Kubernetes, es equivalente a 1 vCPU/Core para proveedores de cloud y 1 hyperthread en procesadores bare-metal Intel.
Las peticiones fraccionadas están permitidas. Un contenedor con spec.containers[].resources.requests.cpu
de 0.5
tiene garantizada la mitad, tanto
CPU como otro que requiere 1 CPU. La expresión 0.1
es equivalente a la expresión 100m
, que puede ser leída como "cien millicpus". Algunas personas dicen
"cienmilicores", y se entiende que quiere decir lo mismo. Una solicitud con un punto decimal, como 0.1
, es convertido a 100m
por la API, y no se permite
una precisión mayor que 1m
. Por esta razón, la forma 100m
es la preferente.
CPU es siempre solicitada como una cantidad absoluta, nunca como una cantidad relativa;
0.1 es la misma cantidad de cpu que un core-simple, dual-core, o máquina de 48-core.
Significado de memoria
Los límites y peticiones de memoria
son medidos en bytes. Puedes expresar la memoria como
un número entero o como un número decimal usando alguno de estos sufijos:
E, P, T, G, M, k, m (millis). También puedes usar los equivalentes en potencia de dos: Ei, Pi, Ti, Gi,
Mi, Ki. Por ejemplo, los siguientes valores representan lo mismo:
128974848, 129e6, 129M, 128974848000m, 123Mi
Aquí un ejemplo. El siguiente Pod tiene dos contenedores. Cada contenedor tiene una petición de 0.25 cpu y 64MiB (226 bytes) de memoria. Cada contenedor tiene un límite de 0.5 cpu y 128MiB de memoria. Puedes decirle al Pod que solicite 0.5 cpu y 128MiB de memoria y un límite de 1 cpu y 256MiB de memoria.
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Cómo son programados los Pods con solicitudes de recursos
Cuando creas un Pod, el planificador de Kubernetes determina el nodo para correr dicho Pod. Cada nodo tiene una capacidad máxima para cada tipo de recurso: la cantidad de CPU y memoria que dispone para los Pods. El planificador de Kubernetes se asegura de que, para cada tipo de recurso, la suma de los recursos solicitados de los contenedores programados sea menor a la capacidad del nodo. Cabe mencionar que aunque la memoria actual o CPU en uso de los nodos sea muy baja, el planificador todavía rechaza programar un Pod en un nodo si la comprobación de capacidad falla. Esto protege contra escasez de recursos en un nodo cuando el uso de recursos posterior crece, por ejemplo, durante un pico diario de solicitud de recursos.
Cómo corren los Pods con límites de recursos
Cuando el kubelet inicia un contenedor de un Pod, este pasa los límites de CPU y memoria al runtime del contenedor.
Cuando usas Docker:
-
El
spec.containers[].resources.requests.cpu
es convertido a su valor interno, el cuál es fraccional, y multiplicado por 1024. El mayor valor de este número o 2 es usado por el valor de--cpu-shares
en el comandodocker run
. -
El
spec.containers[].resources.limits.cpu
se convierte a su valor en milicore y multiplicado por 100. El resultado es el tiempo total de CPU que un contenedor puede usar cada 100ms. Un contenedor no puede usar más tiempo de CPU que del solicitado durante este intervalo.Nota: El período por defecto es de 100ms. La resolución mínima de cuota mínima es 1ms. -
El
spec.containers[].resources.limits.memory
se convierte a entero, y se usa como valor de--memory
del comandodocker run
.
Si el contenedor excede su límite de memoria, este quizá se detenga. Si es reiniciable, el kubelet lo reiniciará, así como cualquier otro error.
Si un Contenedor excede su petición de memoria, es probable que ese Pod sea desalojado en cualquier momento que el nodo se quede sin memoria.
Un Contenedor puede o no tener permitido exceder el límite de CPU por algunos períodos de tiempo. Sin embargo, esto no lo destruirá por uso excesivo de CPU.
Para conocer cuando un Contenedor no puede ser programado o será destruido debido a límite de recursos, revisa la sección de Troubleshooting.
Monitorización del uso de recursos de computación y memoria.
El uso de recursos de un Pod es reportado como parte del estado del Pod.
Si herramientas opcionales para monitorización están disponibles en tu cluster, entonces el uso de recursos del Pod puede extraerse directamente de Métricas API o desde tus herramientas de monitorización.
Almacenamiento local efímero
Kubernetes v1.10 [beta]
Los nodos tienen almacenamiento local efímero, respaldado por
dispositivos de escritura agregados o, a veces, por RAM.
"Efímero" significa que no se garantiza la durabilidad a largo plazo.
.
Los Pods usan el almacenamiento local efímero para añadir espacio, caché, y para logs.
Kubelet puede proveer espacio añadido a los Pods usando almacenamiento local efímero para
montar emptyDir
volumes en los contenedores.
Kubelet también usa este tipo de almacenamiento para guardar logs de contenedores a nivel de nodo, imágenes de contenedores, y la capa de escritura de los contenedores.
Como característica beta, Kubernetes te deja probar, reservar y limitar la cantidad de almacenamiento local efímero que un Pod puede consumir.
Configuraciones para almacenamiento local efímero
Kubernetes soporta 2 maneras de configurar el almacenamiento local efímero en un nodo:
En esta configuración, colocas todos los tipos de datos (emptyDir
volúmenes, capa de escritura,
imágenes de contenedores, logs) en un solo sistema de ficheros.
La manera más efectiva de configurar Kubelet es dedicando este sistema de archivos para los datos de Kubernetes (kubelet).
Kubelet también escribe logs de contenedores a nivel de nodo y trata estos de manera similar al almacenamiento efímero.
Kubelet escribe logs en ficheros dentro del directorio de logs (por defecto /var/log
); y tiene un directorio base para otros datos almacenados localmente
(/var/lib/kubelet
por defecto).
Por lo general, /var/lib/kubelet
y /var/log
están en el sistema de archivos de root,
y Kubelet es diseñado con ese objetivo en mente.
Tu nodo puede tener tantos otros sistema de archivos, no usados por Kubernetes, como quieras.
Tienes un sistema de archivos en el nodo que estás usando para datos efímeros que
provienen de los Pods corriendo: logs, y volúmenes emptyDir
.
Puedes usar este sistema de archivos para otros datos (por ejemplo: logs del sistema no relacionados
con Kubernetes); estos pueden ser incluso del sistema de archivos root.
Kubelet también escribe logs de contenedores a nivel de nodo en el primer sistema de archivos, y trata estos de manera similar al almacenamiento efímero.
También usas un sistema de archivos distinto, respaldado por un dispositivo de almacenamiento lógico diferente. En esta configuración, el directorio donde le dices a Kubelet que coloque las capas de imágenes de los contenedores y capas de escritura es este segundo sistema de archivos.
El primer sistema de archivos no guarda ninguna capa de imágenes o de escritura.
Tu nodo puede tener tantos sistemas de archivos, no usados por Kubernetes, como quieras.
Kubelet puede medir la cantidad de almacenamiento local que se está usando. Esto es posible por:
- el
LocalStorageCapacityIsolation
feature gate está habilitado (esta caracterísitca está habilitada por defecto), y - has configurado el nodo usando una de las configuraciones soportadas para almacenamiento local efímero..
Si tienes una configuración diferente, entonces Kubelet no aplica límites de recursos para almacenamiento local efímero.
tmpfs
volúmenes emptyDir como uso de memoria de contenedor, en lugar de
almacenamiento local efímero.
Configurando solicitudes y límites para almacenamiento local efímero
Puedes usar ephemeral-storage para manejar almacenamiento local efímero. Cada contenedor de un Pod puede especificar uno o más de los siguientes:
spec.containers[].resources.limits.ephemeral-storage
spec.containers[].resources.requests.ephemeral-storage
Los límites y solicitudes para almacenamiento-efímero
son medidos en bytes. Puedes expresar el almacenamiento
como un numero entero o flotante usando los siguientes sufijos:
E, P, T, G, M, K. También puedes usar las siguientes equivalencias: Ei, Pi, Ti, Gi,
Mi, Ki. Por ejemplo, los siguientes representan el mismo valor:
128974848, 129e6, 129M, 123Mi
En el siguiente ejemplo, el Pod tiene dos contenedores. Cada contenedor tiene una petición de 2GiB de almacenamiento local efímero. Cada contenedor tiene un límite de 4GiB de almacenamiento local efímero. Sin embargo, el Pod tiene una petición de 4GiB de almacenamiento efímero , y un límite de 8GiB de almacenamiento local efímero.
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
volumeMounts:
- name: ephemeral
mountPath: "/tmp"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
volumeMounts:
- name: ephemeral
mountPath: "/tmp"
volumes:
- name: ephemeral
emptyDir: {}
Como son programados los Pods con solicitudes de almacenamiento efímero
Cuando creas un Pod, el planificador de Kubernetes selecciona un nodo para el Pod donde sera creado. Cada nodo tiene una cantidad máxima de almacenamiento local efímero que puede proveer a los Pods. Para más información, mira Node Allocatable.
El planificador se asegura de que el total de los recursos solicitados para los contenedores sea menor que la capacidad del nodo.
Manejo del consumo de almacenamiento efímero
Si Kubelet está manejando el almacenamiento efímero local como un recurso, entonces Kubelet mide el uso de almacenamiento en:
- volúmenes
emptyDir
, excepto tmpfs volúmenesemptyDir
- directorios que guardan logs de nivel de nodo
- capas de escritura de contenedores
Si un Pod está usando más almacenamiento efímero que el permitido, Kubelet establece una señal de desalojo que desencadena el desalojo del Pod.
Para aislamiento a nivel de contenedor, si una capa de escritura del contenedor y logs excede el límite de uso del almacenamiento, Kubelet marca el Pod para desalojo.
Para aislamiento a nivel de Pod, Kubelet calcula un límite de almacenamiento
general para el Pod sumando los límites de los contenedores de ese Pod.
En este caso, si la suma del uso de almacenamiento local efímero para todos los contenedores
y los volúmenes emptyDir
de los Pods excede el límite de almacenamiento general del
Pod, Kubelet marca el Pod para desalojo.
Si Kubelet no está midiendo el almacenamiento local efímero, entonces el Pod que excede este límite de almacenamiento, no será desalojado para liberar el límite del recurso de almacenamiento.
Sin embargo, si el espacio del sistema de archivos para la capa de escritura del contenedor,
logs a nivel de nodo o volúmenes emptyDir
decae, el
taints del nodo lanza la desalojo para
cualquier Pod que no tolere dicho taint.
Mira las configuraciones soportadas para almacenamiento local efímero.
Kubelet soporta diferentes maneras de medir el uso de almacenamiento del Pod:
Kubelet realiza frecuentemente, verificaciones programadas que revisan cada
volumen emptyDir
, directorio de logs del contenedor, y capa de escritura
del contenedor.
El escáner mide cuanto espacio está en uso.
En este modo, Kubelet no rastrea descriptores de archivos abiertos para archivos eliminados.
Si tú (o un contenedor) creas un archivo dentro de un volumen emptyDir
,
y algo mas abre ese archivo, y tú lo borras mientras este está abierto,
entonces el inodo para este archivo borrado se mantiene hasta que cierras
el archivo, pero Kubelet no cataloga este espacio como en uso.
Kubernetes v1.15 [alpha]
Las cuotas de proyecto están definidas a nivel de sistema operativo
para el manejo de uso de almacenamiento en uso de sistema de archivos.
Con Kubernetes, puedes habilitar las cuotas de proyecto para el uso
de la monitorización del almacenamiento. Asegúrate que el respaldo del
Sistema de archivos de los volúmenes emptyDir
, en el nodo, provee soporte de
cuotas de proyecto.
Por ejemplo, XFS y ext4fs ofrecen cuotas de proyecto.
Kubernetes usa IDs de proyecto empezando por 1048576
. Los IDs en uso
son registrados en /etc/projects
y /etc/projid
. Si los IDs de proyecto
en este rango son usados para otros propósitos en el sistema, esos IDs
de proyecto deben ser registrados en /etc/projects
y /etc/projid
para
que Kubernetes no los use.
Las cuotas son más rápidas y más precisas que el escáner de directorios. Cuando un directorio es asignado a un proyecto, todos los ficheros creados bajo un directorio son creados en ese proyecto, y el kernel simplemente tiene que mantener rastreados cuántos bloques están en uso por ficheros en ese proyecto. Si un fichero es creado y borrado, pero tiene un fichero abierto, continúa consumiendo espacio. El seguimiento de cuotas registra ese espacio con precisión mientras que los escaneos de directorios pasan por alto el almacenamiento utilizado por los archivos eliminados
Si quieres usar cuotas de proyecto, debes:
-
Habilitar el
LocalStorageCapacityIsolationFSQuotaMonitoring=true
feature gate en la configuración del kubelet. -
Asegúrese de que el sistema de archivos raíz (o el sistema de archivos en tiempo de ejecución opcional) tiene las cuotas de proyectos habilitadas. Todos los sistemas de archivos XFS admiten cuotas de proyectos. Para los sistemas de archivos ext4, debe habilitar la función de seguimiento de cuotas del proyecto mientras el sistema de archivos no está montado.
# For ext4, with /dev/block-device not mounted sudo tune2fs -O project -Q prjquota /dev/block-device
-
Asegúrese de que el sistema de archivos raíz (o el sistema de archivos de tiempo de ejecución opcional) esté montado con cuotas de proyecto habilitadas. Tanto para XFS como para ext4fs, la opción de montaje se llama
prjquota
.
Recursos extendidos
Los recursos extendidos son nombres de recursos calificados fuera del
dominio kubernetes.io
. Permiten que los operadores de clústers publiciten y los usuarios
consuman los recursos no integrados de Kubernetes.
Hay dos pasos necesarios para utilizar los recursos extendidos. Primero, el operador del clúster debe anunciar un Recurso Extendido. En segundo lugar, los usuarios deben solicitar el Recurso Extendido en los Pods.
Manejando recursos extendidos
Recursos extendido a nivel de nodo
Los recursos extendidos a nivel de nodo están vinculados a los nodos
Device plugin managed resources
Mira Plugins de Dispositivos para percibir como los plugins de dispositivos manejan los recursos en cada nodo.
Otros recursos
Para anunciar un nuevo recurso extendido a nivel de nodo, el operador del clúster puede
enviar una solicitud HTTP PATCH
al servidor API para especificar la cantidad
disponible en el status.capacity
para un nodo en el clúster. Después de esta
operación, el status.capacity
del nodo incluirá un nuevo recurso. El campo
status.allocatable
se actualiza automáticamente con el nuevo recurso
de forma asíncrona por el kubelet. Tenga en cuenta que debido a que el planificador
utiliza el valor de status.allocatable
del nodo cuando evalúa la aptitud del Pod, puede haber un breve
retraso entre parchear la capacidad del nodo con un nuevo recurso y el primer Pod
que solicita el recurso en ese nodo.
Ejemplo:
Aquí hay un ejemplo que muestra cómo usar curl
para formar una solicitud HTTP que
anuncia cinco recursos "example.com/foo" en el nodo k8s-node-1
cuyo nodo master
es k8s-master
.
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status
~ 1
es la codificación del carácter/
en la ruta del parche. El valor de la ruta de operación en JSON-Patch se interpreta como un
puntero JSON. Para obtener más detalles, consulte
IETF RFC 6901, sección 3.
Recursos extendidos a nivel de Clúster
Los recursos extendidos a nivel de clúster no están vinculados a los nodos. Suelen estar gestionados por extensores del scheduler, que manejan el consumo de recursos y la cuota de recursos.
Puedes especificar los recursos extendidos que son mantenidos por los extensores del scheduler en configuración de políticas del scheduler.
Ejemplo:
La siguiente configuración para una política del scheduler indica que el recurso extendido a nivel de clúster "example.com/foo" es mantenido por el extensor del scheduler.
- El scheduler envía un Pod al extensor del scheduler solo si la solicitud del Pod "example.com/foo".
- El campo
ignoredByScheduler
especifica que el schduler no compruba el recurso "example.com/foo" en su predicadoPodFitsResources
.
{
"kind": "Policy",
"apiVersion": "v1",
"extenders": [
{
"urlPrefix":"<extender-endpoint>",
"bindVerb": "bind",
"managedResources": [
{
"name": "example.com/foo",
"ignoredByScheduler": true
}
]
}
]
}
Consumiendo recursos extendidos
Los usuarios pueden consumir recursos extendidos en las especificaciones del Pod, como la CPU y la memoria. El planificador se encarga de la contabilidad de recursos para que no más de la cantidad disponible sea asignada simultáneamente a los Pods.
El servidor de API restringe las cantidades de recursos extendidos a números enteros.
Ejemplos de cantidades validas son 3
, 3000m
y 3Ki
. Ejemplos de
cantidades no válidas son 0.5
y 1500m
.
kubernetes.io
tenga reservado.
Para consumir un recurso extendido en un Pod, incluye un nombre de recurso
como clave en spec.containers[].resources.limits
en las especificaciones del contenedor.
Un pod se programa solo si se satisfacen todas las solicitudes de recursos, incluidas
CPU, memoria y cualquier recurso extendido. El Pod permanece en estado PENDING
siempre que no se pueda satisfacer la solicitud de recursos.
Ejemplo:
El siguiente Pod solicita 2CPUs y 1 "example.com/foo" (un recurso extendido).
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: myimage
resources:
requests:
cpu: 2
example.com/foo: 1
limits:
example.com/foo: 1
Solución de problemas
Mis Pods están en estado pendiente con un mensaje de failedScheduling
Si el planificador no puede encontrar ningún nodo donde pueda colocar un Pod, el Pod permanece no programado hasta que se pueda encontrar un lugar. Se produce un evento cada vez que el planificador no encuentra un lugar para el Pod, como este:
kubectl describe pod frontend | grep -A 3 Events
Events:
FirstSeen LastSeen Count From Subobject PathReason Message
36s 5s 6 {scheduler } FailedScheduling Failed for reason PodExceedsFreeCPU and possibly others
En el ejemplo anterior, el Pod llamado "frontend" no se puede programar debido a recursos de CPU insuficientes en el nodo. Mensajes de error similares también pueden sugerir fallo debido a memoria insuficiente (PodExceedsFreeMemory). En general, si un Pod está pendiente con un mensaje de este tipo, hay varias cosas para probar:
- Añadir más nodos al clúster.
- Terminar Pods innecesarios para hacer hueco a los Pods en estado pendiente.
- Compruebe que el Pod no sea más grande que todos los nodos. Por ejemplo, si todos los
los nodos tienen una capacidad de
cpu: 1
, entonces un Pod con una solicitud decpu: 1.1
nunca se programará.
Puedes comprobar las capacidades del nodo y cantidad utilizada con el comando
kubectl describe nodes
. Por ejemplo:
kubectl describe nodes e2e-test-node-pool-4lw4
Name: e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
cpu: 2
memory: 7679792Ki
pods: 110
Allocatable:
cpu: 1800m
memory: 7474992Ki
pods: 110
[ ... lines removed for clarity ...]
Non-terminated Pods: (5 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
kube-system fluentd-gcp-v1.38-28bv1 100m (5%) 0 (0%) 200Mi (2%) 200Mi (2%)
kube-system kube-dns-3297075139-61lj3 260m (13%) 0 (0%) 100Mi (1%) 170Mi (2%)
kube-system kube-proxy-e2e-test-... 100m (5%) 0 (0%) 0 (0%) 0 (0%)
kube-system monitoring-influxdb-grafana-v4-z1m12 200m (10%) 200m (10%) 600Mi (8%) 600Mi (8%)
kube-system node-problem-detector-v0.1-fj7m3 20m (1%) 200m (10%) 20Mi (0%) 100Mi (1%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
680m (34%) 400m (20%) 920Mi (11%) 1070Mi (13%)
EN la salida anterior, puedes ver si una solicitud de Pod mayor que 1120m CPUs o 6.23Gi de memoria, no cabrán en el nodo.
Echando un vistazo a la sección Pods
, puedes ver qué Pods están ocupando espacio
en el nodo.
La cantidad de recursos disponibles para los pods es menor que la capacidad del nodo, porque
los demonios del sistema utilizan una parte de los recursos disponibles. El campo allocatable
NodeStatus
indica la cantidad de recursos que están disponibles para los Pods. Para más información, mira
Node Allocatable Resources.
La característica resource quota se puede configurar para limitar la cantidad total de recursos que se pueden consumir. Si se usa en conjunto con espacios de nombres, puede evitar que un equipo acapare todos los recursos.
Mi contenedor está terminado
Es posible que su contenedor se cancele porque carece de recursos. Para verificar
si un contenedor está siendo eliminado porque está alcanzando un límite de recursos, ejecute
kubectl describe pod
en el Pod de interés:
kubectl describe pod simmemleak-hra99
Name: simmemleak-hra99
Namespace: default
Image(s): saadali/simmemleak
Node: kubernetes-node-tf0f/10.240.216.66
Labels: name=simmemleak
Status: Running
Reason:
Message:
IP: 10.244.2.75
Replication Controllers: simmemleak (1/1 replicas created)
Containers:
simmemleak:
Image: saadali/simmemleak
Limits:
cpu: 100m
memory: 50Mi
State: Running
Started: Tue, 07 Jul 2015 12:54:41 -0700
Last Termination State: Terminated
Exit Code: 1
Started: Fri, 07 Jul 2015 12:54:30 -0700
Finished: Fri, 07 Jul 2015 12:54:33 -0700
Ready: False
Restart Count: 5
Conditions:
Type Status
Ready False
Events:
FirstSeen LastSeen Count From SubobjectPath Reason Message
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {scheduler } scheduled Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD pulled Pod container image "registry.k8s.io/pause:0.8.0" already present on machine
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD created Created with docker id 6a41280f516d
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD started Started with docker id 6a41280f516d
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} spec.containers{simmemleak} created Created with docker id 87348f12526a
En el ejemplo anterior, Restart Count: 5
indica que el contenedor simmemleak
del Pod se reinició cinco veces.
Puedes ejecutar kubectl get pod
con la opción -o go-template=...
para extraer el estado
previos de los Contenedores terminados:
kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]
Puedes ver que el Contenedor fué terminado a causa de reason:OOM Killed
, donde OOM
indica una falta de memoria.
Siguientes pasos
-
Obtén experiencia práctica assigning Memory resources to Containers and Pods.
-
Obtén experiencia práctica assigning CPU resources to Containers and Pods.
-
Para más detalles sobre la diferencia entre solicitudes y límites, mira Resource QoS.
-
Lee Container referencia de API
-
Lee ResourceRequirements referencia de API
-
Lee sobre cuotas de proyecto en XFS
5 - Secrets
Los objetos de tipo Secret en Kubernetes te permiten almacenar y administrar información confidencial, como contraseñas, tokens OAuth y llaves ssh. Poniendo esta información en un Secret es más seguro y más flexible que ponerlo en la definición de un Pod o en un container image. Ver Secrets design document para más información.
Introducción a Secrets
Un Secret es un objeto que contiene una pequeña cantidad de datos confidenciales como contraseñas, un token, o una llave. Tal información podría ser puesta en la especificación de un Pod o en una imagen; poniendolo en un objeto de tipo Secret permite mayor control sobre como se usa, y reduce el riesgo de exposicición accidental.
Los usuarios pueden crear Secrets, y el sistema también puede crearlos.
Para usar un Secret, un Pod debe hacer referencia a este. Un Secret puede ser usado con un Pod de dos formas: como archivos en un volume montado en uno o más de sus contenedores, o utilizados por el kubelet al extraer imágenes del pod.
Secrets incorporados
Las Cuentas de Servicio Crean y Adjuntan Secrets con las Credenciales de la API
Kubernetes crea automaticamente Secrets que contienen credenciales para acceder a la API y modifica automáticamente sus pods para usar este tipo de Secret.
La creación y el uso automático de las credenciales de la API, pueden desabilitarse o anularse si se desea. Sin embargo, si todo lo que necesita hacer es acceder de forma segura al apiserver, este es el flujo de trabajo recomendado.
Ver la documentación de Service Account para más información sobre cómo funcionan las Cuentas de Servicio.
Creando tu propio Secret
Creando un Secret Usando kubectl create Secret
Pongamos como ejemplo el caso de una grupo de pods que necesitan acceder a una base de datos. El nombre y contraseña que los pods deberían usar están en los archivos:
./username.txt
y ./password.txt
en tu máquina local.
# Crear archivos necesarios para el resto del ejemplo.
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt
El comando kubectl create secret
empaqueta esos archivos en un Secret y crea el objeto en el Apiserver.
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
Secret "db-user-pass" created
Si la contraseña que está utilizando tiene caracteres especiales como por ejemplo $
, \
, *
, o !
, es posible que sean interpretados por tu intérprete de comandos y es necesario escapar cada carácter utilizando \
o introduciéndolos entre comillas simples '
.
Por ejemplo, si tú password actual es S!B\*d$zDsb
, deberías ejecutar el comando de esta manera:
kubectl create Secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=' S\!B*d$zDsb'
No necesita escapar de caracteres especiales en contraseñas de archivos (--from-file
).
Puedes comprobar que el Secret se haya creado, así:
kubectl get secrets
NAME TYPE DATA AGE
db-user-pass Opaque 2 51s
kubectl describe secrets/db-user-pass
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
kubectl get
y kubectl describe
evita mostrar el contenido de un Secret por defecto.
Esto es para proteger el Secret de ser expuesto accidentalmente a un espectador, o de ser almacenado en un registro de terminal.
Ver Decodificando un Secret para ver el contenido de un Secret.
Creando un Secret Manualmente
Puedes crear también un Secret primero en un archivo, en formato json o en yaml, y luego crear ese objeto. El Secret contiene dos mapas: data y stringData. El campo de data es usado para almacenar datos arbitrarios, codificado usando base64. El campo stringData se proporciona para su conveniencia, y le permite proporcionar datos secretos como cadenas no codificadas.
Por ejemplo, para almacenar dos cadenas en un Secret usando el campo data, conviértalos a base64 de la siguiente manera:
echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
Escribe un secret que se vea así:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
Ahora escribe un Secret usando kubectl apply
:
kubectl apply -f ./secret.yaml
secret "mysecret" created
Para ciertos escenarios, es posible que desee utilizar el campo de stringData field en su lugar. Este campo le permite poner una cadena codificada que no sea base64 directamente en el Secret, y la cadena será codificada para ti cuando el Secret es creado o actualizado.
Un ejemplo práctico de esto podría ser donde está implementando una aplicación que usa un Secret para almacenar un archivo de configuración, y desea completar partes de ese archivo de configuración durante su proceso de implementación.
Si su aplicación usa el siguiente archivo de configuración:
apiUrl: "https://my.api.com/api/v1"
username: "user"
password: "password"
Podrías almacenarlo en un Secret usando lo siguiente:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
config.yaml: |-
apiUrl: "https://my.api.com/api/v1"
username: {{username}}
password: {{password}}
Su herramienta de despliegue podría entonces reemplazar el {{username}}
y {{password}}
variables de plantilla antes de ejecutar kubectl apply
.
stringData es un campo de conveniencia de solo lectura. Nunca se muestran cuando se recuperan Secrets. Por ejemplo, si ejecuta el siguiente comando:
kubectl get secret mysecret -o yaml
La salida será similar a:
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:40:59Z
name: mysecret
namespace: default
resourceVersion: "7225"
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque
data:
config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
Si se especifica un campo tanto de data y stringData, el valor de StringData es usado. Por ejemplo, la siguiente definición de Secret:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
Los resultado en el siguiente Secret:
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:46:46Z
name: mysecret
namespace: default
resourceVersion: "7579"
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque
data:
username: YWRtaW5pc3RyYXRvcg==
Donde YWRtaW5pc3RyYXRvcg==
decodifica a administrator
.
Las llaves de data y stringData deben consistir en caracteres alfanuméricos, '-', '_' or '.'.
Nota de codificación: Los valores serializados JSON y YAML de los datos secretos estan codificadas como cadenas base64. Las nuevas lineas no son válidas dentro de esa cadena y debe ser omitido. Al usar base64
en Darwin/macOS, los usuarios deben evitar el uso de la opción -b
para dividir líneas largas. Por lo contratio los usuarios de Linux deben añadir la opción -w 0
a los comandos base64
o al pipeline base64 | tr -d '\n'
si la opción -w
no esta disponible.
Creando un Secret a partir de Generador
Kubectl soporta managing objects using Kustomize
desde 1.14. Con esta nueva característica,
puedes tambien crear un Secret a partir de un generador y luego aplicarlo para crear el objeto en el Apiserver. Los generadores deben ser especificados en un kustomization.yaml
dentro de un directorio.
Por ejemplo, para generar un Secret a partir de los archivos ./username.txt
y ./password.txt
# Crear un fichero llamado kustomization.yaml con SecretGenerator
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
files:
- username.txt
- password.txt
EOF
Aplica el directorio kustomization para crear el objeto Secret.
$ kubectl apply -k .
secret/db-user-pass-96mffmfh4k created
Puedes verificar que el secret fue creado de la siguiente manera:
$ kubectl get secrets
NAME TYPE DATA AGE
db-user-pass-96mffmfh4k Opaque 2 51s
$ kubectl describe secrets/db-user-pass-96mffmfh4k
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
Por ejemplo, para generar un Secret a partir de literales username=admin
y password=secret
,
puedes especificar el generador del Secret en kustomization.yaml
como:
# Crea un fichero kustomization.yaml con SecretGenerator
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
literals:
- username=admin
- password=secret
EOF
Aplica el directorio kustomization para crear el objeto Secret.
kubectl apply -k .
secret/db-user-pass-dddghtt9b5 created
Decodificando un Secret
Los Secrets se pueden recuperar a través del comando kubectl get secret
. Por ejemplo, para recuperar el Secret creado en la sección anterior:
kubectl get secret mysecret -o yaml
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: 2016-01-22T18:41:56Z
name: mysecret
namespace: default
resourceVersion: "164619"
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
Decodifica el campo de contraseña:
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df
Usando Secrets
Los Secrets se pueden montar como volúmenes de datos o ser expuestos como variables de entorno para ser usados por un contenedor en un pod. También pueden ser utilizados por otras partes del sistema, sin estar directamente expuesto en el pod. Por ejemplo, pueden tener credenciales que otras partes del sistema usan para interactuar con sistemas externos en su nombre.
Usando Secrets como Archivos de un Pod
Para consumir un Secret en un volumen en un Pod:
- Crear un Secret o usar uno existente. Múltiples pods pueden referenciar el mismo Secret.
- Modifique la definición del Pod para agregar un volumen debajo de
.spec.volumes[]
. Asigne un nombre al volumen y tenga un campo.spec.volumes[].secret.secretName
igual al nombre del objeto del Secret. - Agrega un
.spec.containers[].volumeMounts[]
a cada contenedor que necesite un Secret. Especifica.spec.containers[].volumeMounts[].readOnly = true
y.spec.containers[].volumeMounts[].mountPath
a un nombre de directorio no utilizado donde desea que aparezca los Secrets. - Modifique la imagen y/o linea de comando para que el programa busque archivos en ese directorio. Cada llave en el
data
map del los Secrets se convierte en el nombre del archivo bajomountPath
.
Este es un ejemplo de un pod que monta un Secret en un volumen:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
Cada Secret que desea usar debe mencionarse en .spec.volumes
.
Si hay múltiples contenedores en un Pod, entonces cada contenedor necesita su propio bloque volumeMounts
, pero solo un .spec.volumes
se necesita por Secret.
Puede empaquetar muchos archivos en un Secret, o usar muchos Secrets, lo que sea conveniente.
Proyección de llaves Secret a rutas específicas
También podemos controlar las rutas dentro del volumen donde se proyectan las llaves Secrets.
Puede usar el campo .spec.volumes[].secret.items
para cambiar la ruta de destino de cada clave:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
Lo que sucederá:
- El Secret
username
se almacena bajo el archivo/etc/foo/my-group/my-username
en lugar de/etc/foo/username
. - El Secret
password
no se proyecta
Si se utiliza .spec.volumes[].secret.items
, solo se proyectan las llaves específicadas en los items
.
Para consumir todas las llaves del Secret, Todas deben ser enumeradas en el campo items
.
Todas las llaves enumeradas deben existir en el Secret correspondiente. De lo contrario, el volumen no se crea.
Permisos de archivos Secrets
Tambien puede especificar el modo de permiso de los archivos de bits que tendrá una parte de un Secret.
Si no especifica ninguno, 0644
es usado por defecto. Puede especificar un modo predeterminado para todo el volumen del Secret y sobreescribir por llave si es necesario.
Por ejemplo, puede especificar un modo predeterminado como este:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
defaultMode: 256
Entonces, el Secret será montado en /etc/foo
y todos los archivos creados por el
montaje del volumen del Secret tendrán permiso 0400
.
Tenga en cuenta que la especificación JSON no soporta la notación octal, entonces use el valor 256 para permisos 0400. Si usa yaml en lugar de json para el pod, puede usar notación octal para especificar permisos de una manera más natural.
También puede usar el mapeo, como en el ejemplo anterior, y especificar diferentes permisos para diferentes archivos como:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
mode: 511
En este caso, el archivo resultante en /etc/foo/my-group/my-username
tendrá
un valor de permiso 0777
. Debido a las limitaciones de JSON, debe especificar el modo en notación decimal.
Tenga en cuenta que este valor de permiso puede mostrarse en notación decimal si lo lee después.
Consumir Valores Secrets de Volúmenes
Dentro del contenedor que monta un volumen del Secret, las llaves del Secret aparece como archivos y los valores del Secret son decodificados en base-64 y almacenados dentro de estos archivos. Este es el resultado de comandos ejecutados dentro del contenedor del ejemplo anterior:
ls /etc/foo/
username
password
cat /etc/foo/username
admin
cat /etc/foo/password
1f2d1e2e67df
El programa en un contenedor es responsable de leer los Secrets de los archivos.
Los Secrets Montados se actualizan automáticamente
Cuando se actualiza un Secret que ya se está consumiendo en un volumen, las claves proyectadas también se actualizan eventualmente.
Kubelet está verificando si el Secret montado esta actualizado en cada sincronización periódica.
Sin embargo, está usando su caché local para obtener el valor actual del Secret.
El tipo de caché es configurable usando el (campo ConfigMapAndSecretChangeDetectionStrategy
en
KubeletConfiguration struct).
Puede ser propagado por el reloj (default), ttl-based, o simplemente redirigiendo
todas las solicitudes a kube-apiserver directamente.
Como resultado, el retraso total desde el momento en que se actualiza el Secret hasta el momento en que se proyectan las nuevas claves en el Pod puede ser tan largo como el periodo de sincronización de kubelet + retraso de
propagación de caché, donde el retraso de propagación de caché depende del tipo de caché elegido.
(Es igual a mirar el retraso de propagación, ttl of cache, o cero correspondientemente).
Usando Secrets como Variables de Entorno
Para usar un Secret en una variable de entorno en un pod:
- Crea un Secret o usa uno existente. Múltiples pods pueden hacer referencia a un mismo Secret.
- Modifique la definición de su Pod en cada contenedor que desee consumir el valor de una llave Secret para agregar una variable de entorno para cada llave Secret que deseas consumir. La variable de entorno que consume la llave Secret debe completar el nombre y la llave del Secret en
env[].valueFrom.secretKeyRef
. - Modifique su imagen y/o linea de comandos para que el programa busque valores en las variables de entorno especificadas.
Esto es un ejemplo de un pod que usa Secrets de variables de entorno:
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
restartPolicy: Never
Consumiendo Valores Secrets a partir de Variables de Entorno
Dentro de un contenedor que consume un Secret en una variable de entorno, las claves Secrets aparecen como variables de entorno normal que contienen valores decodificados de base-64 de los datos del Secret. Este es el resultado de comandos ejecutados dentro del contenedor del ejemplo anterior.
echo $SECRET_USERNAME
admin
echo $SECRET_PASSWORD
1f2d1e2e67df
Usando imagePullSecrets
Una imagePullSecret es una forma de pasar a kubelet un Secret que contiene las credenciales para un registro de imagenes de Docker (u otro) para que pueda obtener una imagen privada en nombre de su pod.
Especificar manualmente una imagePullSecret
El uso de imagePullSecrets se desccriben en la documentación de las imágenes images documentation
Organización de imagePullSecrets para que se Adjunte Automáticamente
Puede crear manualmente un imagePullSecret, y hacer referencia a él desde un serviceAccount. Cualquier pod creado con ese serviceAccount o ese valor predeterminado para usar serviceAccount, obtendrá su campo imagePullSecret establecido en el service account. Ver Agregar ImagePullSecrets a una cuenta de servicio para una explicación detallada de ese proceso.
Montaje Automatico de Secrets Creados Manualmente
Secrets creados Manualmente, (por ejemplo uno que contiene un token para acceder a una cuenta github) se puede conectar automáticamente a los pods según su cuenta de servicio. Vea Inyección de infromación en pods usando un a PodPreset para una explicación detallada de este proceso.
Detalles
Restricciones
Las fuentes del volumen del Secret se validan para garantizar que la referencia del objeto especificado apunte a un objeto de tipo Secret
. Por lo tanto, se debe crear un Secret
antes de que cualquier pod dependa de él.
Los objetos API Secret residen en namespace. Solo pueden ser referenciados por pods en el mismo namespace.
Los Secrets
individuales estan limitados a 1MiB de tamaño. Esto es para desalentar la creación de Secrets
muy grandes que agotarían la memoria del apiserver y de kubelet.
Sin embargo la creación de muchos Secrets más pequeños también podría agotar la memoria. Límites más completos en el uso de memoria debido a Secret
es una característica planificada.
Kubelet solo admite el uso de Secret
para Pods que obtiene del API server. Esto incluye cualquier pods creado usando kubectl, o indirectamente a través de un contralador de replicación.
No incluye pods creados a través de los kubelets
--manifest-url
flag, its --config
flag, o su REST API (estas no son formas comunes de crear pods.)
Los Secrets deben crearse antes de que se consuman en pod como variables de entono a menos que estén marcados como optional. Referencias a Secrets que no existen evitarán que el pod inicie.
Las referencias a través de secretKeyRef
a claves que no existen en un Secret con nombre evitarán que el pod se inicie.
Los Secrets que se utilizan para poblar variables de entorno a través de envFrom
que tienen claves que se consideran nombres de variables de entorno no validos, tendran esas claves omitidas. El Pod se permitira reiniciar. Habrá un evento cuyo motivo es InvalidVariableNames
y el mensaje contendrá la lista de claves no validas que se omitieron. El ejemplo muestra un pod que se refiere al default/mysecret que contiene 2 claves no validas, 1 badkey y 2 alsobad.
kubectl get events
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON
0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentVariableNames kubelet, 127.0.0.1 Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.
Interacción del Secret y Pod de por vida
Cuando se crea un Pod a través de la API, no se verifica que exista un recreto referenciado. Una vez que se programa el Pod, kubelet intentará obtener el valor del Secret. Si el Secret no se puede recuperar será por que no existe o por una falla temporal de conexión al servidor API, kubelet volverá a intentarlo periodicamente. Enviará un evento sobre el pod explicando las razones por la que aún no se inició. Una vez que el Secret es encontrado, kubelet creará y montará el volumen que lo contiene. Ninguno de los contenedorees del pod se iniciará hasta que se monten todos los volúmes del pod.
Casos de uso
Caso de Uso: Pod con llaves ssh
Cree un fichero kustomization.yaml con SecretGenerator conteniendo algunas llaves ssh:
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
secret "ssh-key-secret" created
Ahora podemos crear un pod que haga referencia al Secret con la llave ssh key y lo consuma en un volumen:
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
labels:
name: secret-test
spec:
volumes:
- name: secret-volume
secret:
secretName: ssh-key-secret
containers:
- name: ssh-test-container
image: mySshImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
Cuando se ejecuta el comando del contenedor, las partes de la llave estarán disponible en:
/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey
El contenedor es libre de usar los datos del Secret para establecer conexión ssh.
Caso de uso: Pods con credenciales prod / test
Este ejemplo ilustra un pod que consume un Secret que contiene credenciales de prod y otro pod que consume un Secret con credenciales de entorno de prueba.
Crear un fichero kustomization.yaml con SecretGenerator
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
secret "prod-db-secret" created
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
secret "test-db-secret" created
Caracteres especiales como $
, \*
, y !
requieren ser escapados.
Si el password que estas usando tiene caracteres especiales, necesitas escaparlos usando el caracter \\
. Por ejemplo, si tu password actual es S!B\*d$zDsb
, deberías ejecutar el comando de esta forma:
kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=S\\!B\\\*d\\$zDsb
No necesitas escapar caracteres especiales en contraseñas de los archivos (--from-file
).
Ahora haz los pods:
cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
apiVersion: v1
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: prod-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
- kind: Pod
apiVersion: v1
metadata:
name: test-db-client-pod
labels:
name: test-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: test-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
EOF
Añade los pods a el mismo fichero kustomization.yaml
cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF
Aplique todos estos objetos en el Apiserver por
kubectl apply --k .
Ambos contenedores tendrán los siguientes archivos presentes en sus sistemas de archivos con valores para el entorno de cada contenedor:
/etc/secret-volume/username
/etc/secret-volume/password
observe cómo las especificaciones para los dos pods difieren solo en un campo; esto facilita la creación de pods con diferentes capacidades de una plantilla de configuración de pod común.
Deberías simplificar aún más la especificación del pod base utilizando dos Cuentas de Servicio:
uno llamado, prod-user
con el prod-db-secret
, y otro llamado,
test-user
con el test-db-secret
. Luego, la especificación del pod se puede acortar a, por ejemplo:
apiVersion: v1
kind: Pod
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
serviceAccount: prod-db-client
containers:
- name: db-client-container
image: myClientImage
Caso de uso: Dotfiles en el volume del Secret
Para hacer que los datos esten 'ocultos' (es decir, en un file dónde el nombre comienza con un caracter de punto), simplemente haga que esa clave comience con un punto. Por ejemplo, cuando el siguiente Secret es montado en un volumen:
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: registry.k8s.io/busybox
command:
- ls
- "-l"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
El secret-volume
contendrá un solo archivo, llamado .secret-file
, y
el dotfile-test-container
tendrá este fichero presente en el path
/etc/secret-volume/.secret-file
.
ls -l
;
tu debes usar ls -la
para verlos al enumerar los contenidos del directorio.
Caso de uso: Secret visible para un contenedor en un pod
Considere un programa que necesita manejar solicitudes HTTP, hacer una lógica empresarial compleja y luego firmar algunos mensajes con un HMAC. Debido a que tiene una lógica de aplicación compleja, puede haber una vulnerabilidad de lectura remota de archivos inadvertida en el servidor, lo que podría exponer la clave privada a un atacante.
Esto podría dividirse en dos procesos en dos contenedores: un contenedor de frontend que maneja la interacción del usuario y la lógica empresarial. pero que no puede ver la clave privada; y un contenedor de firmante que puede ver la clave privada, y responde a solicitudes de firma simples del frontend (ejemplo, a través de redes de localhost).
Con este enfoque particionado, un atacante ahora tiene que engañar a un servidor de aplicaciones para que haga algo bastante arbitrario, lo que puede ser más difícil que hacer que lea un archivo.
Mejores prácticas
Clientes que usan la API de Secrets
Al implementar aplicaciones que interactuan con los API Secrets, el acceso debe limitarse utilizando authorization policies como RBAC.
Los Secrets a menudo contienen valores que abarcan un espectro de importancia, muchos de los cuales pueden causar escalamientos dentro de Kubernetes (ejememplo, tokens de cuentas de servicio) y a sistemas externos. Incluso si una aplicación individual puede razonar sobre el poder de los Secrets con los que espera interactuar, otras aplicaciones dentro dle mismo namespace pueden invalidar esos supuestos.
Por esas razones las solicitudes de watch
y list
dentro de un espacio de nombres son extremadamente poderosos y deben evitarse, dado que listar Secrets permiten a los clientes inspecionar los valores de todos los Secrets que estan en el namespace. La capacidad para watch
and list
todos los Secrets en un cluster deben reservarse solo para los componentes de nivel de sistema más privilegiados.
Las aplicaciones que necesitan acceder a la API de Secrets deben realizar solicitudes de get
de los Secrets que necesitan. Esto permite a los administradores restringir el acceso a todos los Secrets mientras white-listing access to individual instances que necesita la aplicación.
Para un mejor rendimiento sobre un bucle get
, los clientes pueden diseñar recursos que hacen referencia a un Secret y luego un Secret watch
el recurso, al volver a solicitar el Secret cuando cambie la referencia. Además,, un "bulk watch" API
para que los clientes puedan watch
recursos individuales, y probablemente estará disponible en futuras versiones de Kubernetes.
Propiedades de seguridad
Protecciones
Debido a que los objetos Secret
se pueden crear independientemente de los Pods
que los usan, hay menos riesgo de que el Secret expuesto durante el flujo de trabajo de la creación, visualización, y edición de pods. El sistema también puede tomar precausiones con los objetosSecret
, tal como eviar escribirlos en el disco siempre que sea posible.
Un Secret solo se envía a un nodo si un pod en ese nodo lo requiere. Kubelet almacena el Secret en un tmpfs
para que el Secret no se escriba en el almacenamiento de disco. Una vez que se elimina el pod que depende del Secret, kubelet eliminará su copia local de los datos de Secrets.
Puede haber Secrets para varios Pods en el mismo nodo. Sin embargo, solo los Secrets que solicita un Pod son potencialmente visibles dentro de sus contenedores. Por lo tanto, un Pod no tiene acceso a los Secrets de otro Pod.
Puede haber varios contenedores en un Pod. Sin embargo, cada contenedor en un pod tiene que solicitar el volumen del Secret en su
volumeMounts
para que sea visible dentro del contenedor. Esto se puede usar para construir particiones de seguridad útiles en el Pod level](#use-case-secret-visible-to-one-container-in-a-pod).
En la mayoría de las distribuciones Kubernetes-project-maintained, la comunicación entre usuario a el apiserver, y del apiserver a kubelets, ista protegido por SSL/TLS. Los Secrets estan protegidos cuando se transmiten por estos canales.
Kubernetes v1.13 [beta]
Puedes habilitar encryption at rest para datos secretos, para que los Secrets no se almacenen en claro en etcd.
Riesgos
- En el servidor API, los datos de los Secrets se almacenan en etcd; por lo tanto:
- Los adminsitradores deben habilitar el cifrado en reposo para los datos del cluster (requiere v1.13 o posterior)
- Los administradores deben limitar el acceso a etcd a los usuarios administradores
- Los administradores pueden querer borrar/destruir discos usados por etcd cuando ya no estén en uso
- Si ejecuta etcd en un clúster, los administradores deben asegurarse de usar SSL/TSL para la comunicación entre pares etcd.
- Si configura el Secret a través de un archivo de (JSON o YAML) que tiene los datos del Secret codificados como base64, compartir este archivo o registrarlo en un repositorio de origen significa que el Secret está comprometido. La codificación Base64 no es un método de cifrado y se considera igual que un texto plano.
- Las aplicaciones aún necesitan proteger el valor del Secret después de leerlo del volumen, como no registrarlo accidentalmente o transmitirlo a una parte no confiable.
- Un usuario que puede crear un pod que usa un Secret también puede ver el valor del Secret. Incluso si una política del apiserver no permite que ese usuario lea el objeto Secret, el usuario puede ejecutar el pod que expone el Secret.
- Actualmente, cualquier persona con root en cualquier nodo puede leer cualquier secret del apiserver, haciéndose pasar por el kubelet. Es una característica planificada enviar Secrets a los nodos que realmente lo requieran, para restringir el impacto de una explosión de root en un single node.
Siguientes pasos
6 - Organizar el acceso a los clústeres utilizando archivos kubeconfig
Utilice los archivos kubeconfig para organizar la información acerca de los clústeres, los
usuarios, los Namespaces y los mecanismos de autenticación. La herramienta de
línea de comandos kubectl
utiliza los archivos kubeconfig para hallar la información que
necesita para escoger un clúster y comunicarse con el servidor API de un clúster.
kubeconfig
.
Por defecto, kubectl
busca un archivo llamado config
en el directorio $HOME/.kube
.
Puedes especificar otros archivos kubeconfig mediante la configuración de la variable
de entorno KUBECONFIG
o mediante la configuracion del flag
--kubeconfig
.
Para obtener instrucciones paso a paso acerca de cómo crear y especificar los archivos kubeconfig, consulte el recurso Configurar El Acceso A Múltiples Clústeres.
Compatibilidad con múltiples clústeres, usuarios y mecanismos de autenticación
Suponga que tiene diversos clústeres y que sus usuarios y componentes se autentican de diversas maneras. Por ejemplo:
- Un kubelet en ejecución se podría autenticar usando certificados.
- Un usuario se podría autenticar utilizando tokens.
- Los administradores podrían tener un conjunto de certificados que sean suministrados a los usuarios individualmente.
Con los archivos kubeconfig puedes organizar tus clústeres, usuarios y Namespaces. También puedes definir diferentes contextos para realizar de forma rápida y fácil cambios entre clústeres y Namespaces.
Contexto
Un elemento context en un archivo kubeconfig se utiliza para agrupar los parámetros de
acceso bajo un nombre apropiado. Cada contexto tiene tres parámetros: clúster, Namespace
y usuario.
Por defecto, la herramienta de línea de comandos kubectl
utiliza los parámetros del
contexto actual para comunicarse con el clúster.
Para seleccionar el contexto actual:
kubectl config use-context
Variable de entorno KUBECONFIG
La variable de entorno KUBECONFIG
contiene una lista de archivos kubeconfig.
En el caso de Linux y Mac, la lista está delimitada por dos puntos. Si se trata
de Windows, la lista está delimitada por punto y coma. La variable de entorno
KUBECONFIG
no es indispensable. Si la variable de entorno KUBECONFIG
no existe,
kubectl
utiliza el archivo kubeconfig por defecto $HOME/.kube/config
.
Si la variable de entorno KUBECONFIG
existe, kubectl
utiliza una
configuración eficiente que es el resultado de la fusión de los archivos
listados en la variable de entorno KUBECONFIG
.
Fusionando archivos kubeconfig
Para poder ver su configuración, escriba el siguiente comando:
kubectl config view
Como se ha descrito anteriormente, la respuesta de este comando podría resultar a partir de un solo archivo kubeconfig, o podría ser el resultado de la fusión de varios archivos kubeconfig.
A continuación se muestran las reglas que usa kubectl
cuando fusiona archivos kubeconfig:
-
Si el flag
--kubeconfig
está activado, usa solamente el archivo especificado. Sin fusionar. Sólo se permite una instancia con este flag.En caso contrario, si la variable de entorno
KUBECONFIG
está activada, sera usada como un listado de los archivos a ser fusionados. Fusionar los archivos listados en la variable de entornoKUBECONFIG
de acuerdo con estas reglas:- Ignorar nombres de archivo vacíos.
- Producir errores para archivos con contenido que no pueden ser deserializados.
- El primer archivo que establezca un valor particular o una clave se impone.
- Nunca cambie el valor o la clave.
Ejemplo: Conserva el contexto del primer archivo para configurar el
contexto actual
. Ejemplo: Si dos archivos especifican unred-user
, utilice sólo los valores del primer archivo. Incluso desechar el segundo archivo aunque tenga registros que no tengan conflictos.
Para obtener un ejemplo de configuración de la variable de entorno
KUBECONFIG
, consulte la sección Configuración de la variable de entorno KUBECONFIG.En caso contrario, utilice el archivo kubeconfig predeterminado
$HOME/.kube/config
, sin fusionar. -
Determinar el contexto a utilizar con base en el primer acierto en esta secuencia:
- Si es que existe, utilice el flag
---contexto
de la línea de comandos. - Utilice el
contexto actual
procedente de los archivos kubeconfig fusionados.
En este punto se permite un contexto vacío.
- Si es que existe, utilice el flag
-
Determinar el clúster y el usuario. En este caso, puede o no haber un contexto. Determine el clúster y el usuario con base en el primer acierto que se ejecute dos veces en esta secuencia: una para el usuario y otra para el clúster:
- Si es que existen, utilice el flag
--user
o--cluster
de la línea de comandos. - Si el contexto no está vacío, tome el usuario o clúster del contexto.
En este caso el usuario y el clúster pueden estar vacíos.
- Si es que existen, utilice el flag
-
Determinar la información del clúster a utilizar. En este caso, puede o no haber información del clúster. Se construye cada pieza de la información del clúster con base en esta secuencia, el primer acierto se impone:
- Si es que existen, use el flag
--server
,--certificate-authority
,--insecure-skip-tls-verify
en la línea de comandos. - Si existen atributos de información de clúster procedentes de los archivos kubeconfig fusionados, utilícelos.
- Falla si no existe la ubicación del servidor.
- Si es que existen, use el flag
-
Determinar la información del usuario a utilizar. Cree información de usuario utilizando las mismas reglas que la información de clúster, con la excepción de permitir sólo un mecanismo de autenticación por usuario:
- Si es que existen, utilice el flag
--client-certificate
,--client-key
,--username
,--password
,--token
de la línea de comandos. - Utilice los campos
user
de los archivos kubeconfig fusionados. - Falla si hay dos mecanismos de autenticación contradictorios.
- Si es que existen, utilice el flag
-
Si todavía falta información, utilice los valores predeterminados y solicite información de autenticación.
Referencias de archivos
Las referencias, así también como, las rutas de un archivo kubeconfig son relativas a la ubicación del archivo kubeconfig.
Las referencias de un archivo en la línea de comandos son relativas al directorio actual de trabajo.
Dentro de $HOME/.kube/config
, las rutas relativas se almacenan de manera relativa a la ubicación del archivo kubeconfig , al igual que las rutas absolutas
se almacenan absolutamente.