Kubernetes Basics

Kubernetes Basics

Support HackTricks

El autor original de esta página es Jorge (lee su publicación original aquí)

Arquitectura y conceptos básicos

¿Qué hace Kubernetes?

  • Permite ejecutar contenedor/es en un motor de contenedores.

  • La programación permite que las misiones de los contenedores sean eficientes.

  • Mantiene los contenedores vivos.

  • Permite la comunicación entre contenedores.

  • Permite técnicas de despliegue.

  • Maneja volúmenes de información.

Arquitectura

  • Nodo: sistema operativo con pod o pods.

  • Pod: envoltura alrededor de un contenedor o múltiples contenedores. Un pod debe contener solo una aplicación (por lo general, un pod ejecuta solo 1 contenedor). El pod es la forma en que Kubernetes abstrae la tecnología de contenedores en ejecución.

  • Servicio: Cada pod tiene 1 dirección IP interna del rango interno del nodo. Sin embargo, también puede ser expuesto a través de un servicio. El servicio también tiene una dirección IP y su objetivo es mantener la comunicación entre pods, por lo que si uno muere, el nuevo reemplazo (con una IP interna diferente) será accesible expuesto en la misma IP del servicio. Puede configurarse como interno o externo. El servicio también actúa como un balanceador de carga cuando 2 pods están conectados al mismo servicio. Cuando se crea un servicio, puedes encontrar los puntos finales de cada servicio ejecutando kubectl get endpoints

  • Kubelet: Agente principal del nodo. El componente que establece la comunicación entre el nodo y kubectl, y solo puede ejecutar pods (a través del servidor API). El kubelet no gestiona contenedores que no fueron creados por Kubernetes.

  • Kube-proxy: es el servicio encargado de las comunicaciones (servicios) entre el apiserver y el nodo. La base es un IPtables para nodos. Los usuarios más experimentados podrían instalar otros kube-proxies de otros proveedores.

  • Contenedor Sidecar: Los contenedores sidecar son los contenedores que deben ejecutarse junto con el contenedor principal en el pod. Este patrón sidecar extiende y mejora la funcionalidad de los contenedores actuales sin cambiarlos. Hoy en día, sabemos que usamos tecnología de contenedores para envolver todas las dependencias para que la aplicación se ejecute en cualquier lugar. Un contenedor hace solo una cosa y la hace muy bien.

  • Proceso maestro:

  • Api Server: Es la forma en que los usuarios y los pods se comunican con el proceso maestro. Solo se deben permitir solicitudes autenticadas.

  • Scheduler: La programación se refiere a asegurarse de que los Pods estén emparejados con los Nodos para que Kubelet pueda ejecutarlos. Tiene suficiente inteligencia para decidir qué nodo tiene más recursos disponibles y asignar el nuevo pod a él. Ten en cuenta que el scheduler no inicia nuevos pods, solo se comunica con el proceso Kubelet que se ejecuta dentro del nodo, que lanzará el nuevo pod.

  • Kube Controller manager: Verifica recursos como conjuntos de réplicas o despliegues para comprobar si, por ejemplo, el número correcto de pods o nodos está en ejecución. En caso de que falte un pod, se comunicará con el scheduler para iniciar uno nuevo. Controla la replicación, tokens y servicios de cuenta para la API.

  • etcd: Almacenamiento de datos, persistente, consistente y distribuido. Es la base de datos de Kubernetes y el almacenamiento clave-valor donde mantiene el estado completo de los clústeres (cada cambio se registra aquí). Componentes como el Scheduler o el Controller manager dependen de estos datos para saber qué cambios han ocurrido (recursos disponibles de los nodos, número de pods en ejecución...)

  • Cloud controller manager: Es el controlador específico para el control de flujo y aplicaciones, es decir: si tienes clústeres en AWS o OpenStack.

Ten en cuenta que como puede haber varios nodos (ejecutando varios pods), también puede haber varios procesos maestros cuyo acceso al Api server está balanceado y su etcd sincronizado.

Volúmenes:

Cuando un pod crea datos que no deberían perderse cuando el pod desaparece, deben almacenarse en un volumen físico. Kubernetes permite adjuntar un volumen a un pod para persistir los datos. El volumen puede estar en la máquina local o en un almacenamiento remoto. Si estás ejecutando pods en diferentes nodos físicos, deberías usar un almacenamiento remoto para que todos los pods puedan acceder a él.

Otras configuraciones:

  • ConfigMap: Puedes configurar URLs para acceder a servicios. El pod obtendrá datos de aquí para saber cómo comunicarse con el resto de los servicios (pods). Ten en cuenta que este no es el lugar recomendado para guardar credenciales.

  • Secret: Este es el lugar para almacenar datos secretos como contraseñas, claves API... codificados en B64. El pod podrá acceder a estos datos para usar las credenciales requeridas.

  • Deployments: Aquí es donde se indican los componentes que serán ejecutados por Kubernetes. Un usuario generalmente no trabajará directamente con pods, los pods están abstraídos en ReplicaSets (número de pods idénticos replicados), que se ejecutan a través de despliegues. Ten en cuenta que los despliegues son para aplicaciones sin estado. La configuración mínima para un despliegue es el nombre y la imagen a ejecutar.

  • StatefulSet: Este componente está destinado específicamente a aplicaciones como bases de datos que necesitan acceder al mismo almacenamiento.

  • Ingress: Esta es la configuración que se utiliza para exponer la aplicación públicamente con una URL. Ten en cuenta que esto también se puede hacer utilizando servicios externos, pero esta es la forma correcta de exponer la aplicación.

  • Si implementas un Ingress, necesitarás crear Ingress Controllers. El Ingress Controller es un pod que será el punto final que recibirá las solicitudes y las verificará y las balanceará a los servicios. El controlador de ingreso enviará la solicitud según las reglas de ingreso configuradas. Ten en cuenta que las reglas de ingreso pueden apuntar a diferentes rutas o incluso subdominios a diferentes servicios internos de Kubernetes.

  • Una mejor práctica de seguridad sería usar un balanceador de carga en la nube o un servidor proxy como punto de entrada para no tener ninguna parte del clúster de Kubernetes expuesta.

  • Cuando se recibe una solicitud que no coincide con ninguna regla de ingreso, el controlador de ingreso la dirigirá al "Backend predeterminado". Puedes describe el controlador de ingreso para obtener la dirección de este parámetro.

  • minikube addons enable ingress

Infraestructura PKI - Autoridad de Certificación CA:

  • CA es la raíz de confianza para todos los certificados dentro del clúster.

  • Permite que los componentes se validen entre sí.

  • Todos los certificados del clúster son firmados por la CA.

  • ETCd tiene su propio certificado.

  • tipos:

  • certificado del apiserver.

  • certificado del kubelet.

  • certificado del scheduler.

Acciones Básicas

Minikube

Minikube se puede usar para realizar algunas pruebas rápidas en Kubernetes sin necesidad de desplegar un entorno completo de Kubernetes. Ejecutará los procesos maestro y nodo en una máquina. Minikube utilizará VirtualBox para ejecutar el nodo. Consulta aquí cómo instalarlo.

$ minikube start
😄  minikube v1.19.0 on Ubuntu 20.04
✨  Automatically selected the virtualbox driver. Other choices: none, ssh
💿  Downloading VM boot image ...
> minikube-v1.19.0.iso.sha256: 65 B / 65 B [-------------] 100.00% ? p/s 0s
> minikube-v1.19.0.iso: 244.49 MiB / 244.49 MiB  100.00% 1.78 MiB p/s 2m17.
👍  Starting control plane node minikube in cluster minikube
💾  Downloading Kubernetes v1.20.2 preload ...
> preloaded-images-k8s-v10-v1...: 491.71 MiB / 491.71 MiB  100.00% 2.59 MiB
🔥  Creating virtualbox VM (CPUs=2, Memory=3900MB, Disk=20000MB) ...
🐳  Preparing Kubernetes v1.20.2 on Docker 20.10.4 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎  Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by defaul

$ minikube status
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

---- ONCE YOU HAVE A K8 SERVICE RUNNING WITH AN EXTERNAL SERVICE -----
$ minikube service mongo-express-service
(This will open your browser to access the service exposed port)

$ minikube delete
🔥  Deleting "minikube" in virtualbox ...
💀  Removed all traces of the "minikube" cluster

Kubectl Basics

Kubectl es la herramienta de línea de comandos para clústeres de kubernetes. Se comunica con el servidor Api del proceso maestro para realizar acciones en kubernetes o para solicitar datos.

kubectl version #Get client and server version
kubectl get pod
kubectl get services
kubectl get deployment
kubectl get replicaset
kubectl get secret
kubectl get all
kubectl get ingress
kubectl get endpoints

#kubectl create deployment <deployment-name> --image=<docker image>
kubectl create deployment nginx-deployment --image=nginx
#Access the configuration of the deployment and modify it
#kubectl edit deployment <deployment-name>
kubectl edit deployment nginx-deployment
#Get the logs of the pod for debbugging (the output of the docker container running)
#kubectl logs <replicaset-id/pod-id>
kubectl logs nginx-deployment-84cd76b964
#kubectl describe pod <pod-id>
kubectl describe pod mongo-depl-5fd6b7d4b4-kkt9q
#kubectl exec -it <pod-id> -- bash
kubectl exec -it mongo-depl-5fd6b7d4b4-kkt9q -- bash
#kubectl describe service <service-name>
kubectl describe service mongodb-service
#kubectl delete deployment <deployment-name>
kubectl delete deployment mongo-depl
#Deploy from config file
kubectl apply -f deployment.yml

Minikube Dashboard

El panel de control te permite ver más fácilmente lo que está ejecutando minikube, puedes encontrar la URL para acceder a él en:

minikube dashboard --url


🔌  Enabling dashboard ...
▪ Using image kubernetesui/dashboard:v2.3.1
▪ Using image kubernetesui/metrics-scraper:v1.0.7
🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
http://127.0.0.1:50034/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/

Ejemplos de archivos de configuración YAML

Cada archivo de configuración tiene 3 partes: metadata, specification (lo que necesita ser lanzado), status (estado deseado). Dentro de la especificación del archivo de configuración de despliegue, puedes encontrar la plantilla definida con una nueva estructura de configuración que define la imagen a ejecutar:

Ejemplo de Deployment + Service declarados en el mismo archivo de configuración (de aquí)

Como un servicio generalmente está relacionado con un despliegue, es posible declarar ambos en el mismo archivo de configuración (el servicio declarado en esta configuración solo es accesible internamente):

apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017

Ejemplo de configuración de servicio externo

Este servicio será accesible externamente (ver los atributos nodePort y type: LoadBlancer):

---
apiVersion: v1
kind: Service
metadata:
name: mongo-express-service
spec:
selector:
app: mongo-express
type: LoadBalancer
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30000

Esto es útil para pruebas, pero para producción solo deberías tener servicios internos y un Ingress para exponer la aplicación.

Ejemplo de archivo de configuración de Ingress

Esto expondrá la aplicación en http://dashboard.com.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
spec:
rules:
- host: dashboard.com
http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 80

Ejemplo de archivo de configuración de secretos

Nota cómo las contraseñas están codificadas en B64 (¡lo cual no es seguro!)

apiVersion: v1
kind: Secret
metadata:
name: mongodb-secret
type: Opaque
data:
mongo-root-username: dXNlcm5hbWU=
mongo-root-password: cGFzc3dvcmQ=

Ejemplo de ConfigMap

Un ConfigMap es la configuración que se le da a los pods para que sepan cómo localizar y acceder a otros servicios. En este caso, cada pod sabrá que el nombre mongodb-service es la dirección de un pod con el que pueden comunicarse (este pod estará ejecutando un mongodb):

apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-configmap
data:
database_url: mongodb-service

Luego, dentro de una deployment config, esta dirección se puede especificar de la siguiente manera para que se cargue dentro del env del pod:

[...]
spec:
[...]
template:
[...]
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_SERVER
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
[...]

Ejemplo de configuración de volumen

Puedes encontrar diferentes ejemplos de archivos yaml de configuración de almacenamiento en https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumes. Nota que los volúmenes no están dentro de los espacios de nombres

Espacios de nombres

Kubernetes soporta múltiples clústeres virtuales respaldados por el mismo clúster físico. Estos clústeres virtuales se llaman espacios de nombres. Están destinados para su uso en entornos con muchos usuarios distribuidos en múltiples equipos o proyectos. Para clústeres con unos pocos a decenas de usuarios, no deberías necesitar crear o pensar en espacios de nombres en absoluto. Solo deberías comenzar a usar espacios de nombres para tener un mejor control y organización de cada parte de la aplicación desplegada en kubernetes.

Los espacios de nombres proporcionan un alcance para los nombres. Los nombres de los recursos deben ser únicos dentro de un espacio de nombres, pero no entre espacios de nombres. Los espacios de nombres no pueden estar anidados uno dentro de otro y cada recurso de Kubernetes puede estar en un solo espacio de nombres.

Hay 4 espacios de nombres por defecto si estás usando minikube:

kubectl get namespace
NAME              STATUS   AGE
default           Active   1d
kube-node-lease   Active   1d
kube-public       Active   1d
kube-system       Active   1d
  • kube-system: No está destinado para el uso de los usuarios y no deberías tocarlo. Es para procesos de master y kubectl.

  • kube-public: Datos accesibles públicamente. Contiene un configmap que contiene información del clúster.

  • kube-node-lease: Determina la disponibilidad de un nodo.

  • default: El namespace que el usuario utilizará para crear recursos.

#Create namespace
kubectl create namespace my-namespace

Tenga en cuenta que la mayoría de los recursos de Kubernetes (por ejemplo, pods, servicios, controladores de replicación y otros) están en algunos namespaces. Sin embargo, otros recursos como los recursos de namespace y recursos de bajo nivel, como nodos y persistenVolumes, no están en un namespace. Para ver qué recursos de Kubernetes están y no están en un namespace:

kubectl api-resources --namespaced=true #In a namespace
kubectl api-resources --namespaced=false #Not in a namespace

Puedes guardar el namespace para todos los comandos kubectl subsiguientes en ese contexto.

kubectl config set-context --current --namespace=<insert-namespace-name-here>

Helm

Helm es el gestor de paquetes para Kubernetes. Permite empaquetar archivos YAML y distribuirlos en repositorios públicos y privados. Estos paquetes se llaman Helm Charts.

helm search <keyword>

Helm también es un motor de plantillas que permite generar archivos de configuración con variables:

Secretos de Kubernetes

Un Secret es un objeto que contiene datos sensibles como una contraseña, un token o una clave. Tal información podría de otro modo ser colocada en una especificación de Pod o en una imagen. Los usuarios pueden crear Secrets y el sistema también crea Secrets. El nombre de un objeto Secret debe ser un nombre de subdominio DNS válido. Lea aquí la documentación oficial.

Los Secrets pueden ser cosas como:

  • API, claves SSH.

  • Tokens de OAuth.

  • Credenciales, contraseñas (texto plano o b64 + cifrado).

  • Información o comentarios.

  • Código de conexión a bases de datos, cadenas… .

Hay diferentes tipos de secretos en Kubernetes

Tipo incorporadoUso

Opaque

datos arbitrarios definidos por el usuario (Predeterminado)

kubernetes.io/service-account-token

token de cuenta de servicio

kubernetes.io/dockercfg

archivo ~/.dockercfg serializado

kubernetes.io/dockerconfigjson

archivo ~/.docker/config.json serializado

kubernetes.io/basic-auth

credenciales para autenticación básica

kubernetes.io/ssh-auth

credenciales para autenticación SSH

kubernetes.io/tls

datos para un cliente o servidor TLS

bootstrap.kubernetes.io/token

datos del token de arranque

El tipo Opaque es el predeterminado, el par clave-valor típico definido por los usuarios.

Cómo funcionan los secretos:

El siguiente archivo de configuración define un secret llamado mysecret con 2 pares clave-valor username: YWRtaW4= y password: MWYyZDFlMmU2N2Rm. También define un pod llamado secretpod que tendrá el username y password definidos en mysecret expuestos en las variables de entorno SECRET_USERNAME __ y __ SECRET_PASSWOR. También montará el secreto username dentro de mysecret en la ruta /etc/foo/my-group/my-username con permisos 0640.

secretpod.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
---
apiVersion: v1
kind: Pod
metadata:
name: secretpod
spec:
containers:
- name: secretpod
image: nginx
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
volumeMounts:
- name: foo
mountPath: "/etc/foo"
restartPolicy: Never
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
mode: 0640
kubectl apply -f <secretpod.yaml>
kubectl get pods #Wait until the pod secretpod is running
kubectl exec -it  secretpod -- bash
env | grep SECRET && cat /etc/foo/my-group/my-username && echo

Secrets in etcd

etcd es un almacén de clave-valor consistente y altamente disponible utilizado como almacenamiento de respaldo de Kubernetes para todos los datos del clúster. Accedamos a los secretos almacenados en etcd:

cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd

Verás que los certificados, claves y URL están ubicados en el sistema de archivos. Una vez que los obtengas, podrás conectarte a etcd.

#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] health

ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] health

Una vez que logres establecer comunicación, podrás obtener los secretos:

#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] get <path/to/secret>

ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] get /registry/secrets/default/secret_02

Agregar cifrado al ETCD

Por defecto, todos los secretos están almacenados en texto plano dentro de etcd a menos que apliques una capa de cifrado. El siguiente ejemplo se basa en https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

encryption.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: cjjPMcWpTPKhAdieVtd+KhG4NN+N6e3NmBPMXJvbfrY= #Any random key
- identity: {}

Después de eso, necesitas establecer la bandera --encryption-provider-config en el kube-apiserver para apuntar a la ubicación del archivo de configuración creado. Puedes modificar /etc/kubernetes/manifest/kube-apiserver.yaml y agregar las siguientes líneas:

containers:
- command:
- kube-apiserver
- --encriyption-provider-config=/etc/kubernetes/etcd/<configFile.yaml>

Desplázate hacia abajo en los volumeMounts:

- mountPath: /etc/kubernetes/etcd
name: etcd
readOnly: true

Desplácese hacia abajo en los volumeMounts hasta hostPath:

- hostPath:
path: /etc/kubernetes/etcd
type: DirectoryOrCreate
name: etcd

Verificando que los datos están cifrados

Los datos se cifran cuando se escriben en etcd. Después de reiniciar tu kube-apiserver, cualquier secreto creado o actualizado debería estar cifrado al almacenarse. Para verificar, puedes usar el programa de línea de comandos etcdctl para recuperar el contenido de tu secreto.

  1. Crea un nuevo secreto llamado secret1 en el espacio de nombres default:

kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
  1. Usando la línea de comandos etcdctl, lee ese secreto de etcd:

ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C

donde [...] debe ser los argumentos adicionales para conectarse al servidor etcd. 3. Verifica que el secreto almacenado esté precedido por k8s:enc:aescbc:v1: lo que indica que el proveedor aescbc ha cifrado los datos resultantes. 4. Verifica que el secreto se descifre correctamente al recuperarlo a través de la API:

kubectl describe secret secret1 -n default

debería coincidir con mykey: bXlkYXRh, mydata está codificado, consulta decodificando un secreto para decodificar completamente el secreto.

Dado que los secretos están cifrados al escribir, realizar una actualización en un secreto cifrará ese contenido:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

Consejos finales:

Referencias

Apoya a HackTricks

Last updated