- Tabla de contenidos
- 01. Introduccion
- 02. Core Concepts
- 03. Scheduling
- 04. Logging & monitoring
- 05. Application Lifecycle Management
- 06. Cluster maintenance
- 07. Security
- 08. Storage
- 09. Networking
- 10. Install kubernetes hard way
- 11. Install kubernetes the kubeadm
- 12. End to End test on a Kubernetes cluster
- 13. Troubleshooting
- 14. Other Topics
La finalidad de este repositorio es tener una guia para obtener la certificación de Kubernetes CKA.
Introducción al curso:
- El examen se puede comprar en la siguiente web: https://www.cncf.io/certification/cka/
- En caso de examen fallido, podrá presentarse de nuevo sin coste, en un plazo máximo de 12 meses.
- No es un examen convencional, sino ejercicios que ponen a prueba sus conocimientos.
- Podrá consultar la documentación oficial de kubernetes en el examen
Algunas referencias al examen:
- Administrador Certificado de Kubernetes: https://www.cncf.io/certification/cka/
- Plan de estudios del examen (temas): https://github.com/cncf/curriculum
- Manual del candidato: https://www.cncf.io/certification/candidate-handbook
- Consejos para el examen: http://training.linuxfoundation.org/go//Important-Tips-CKA-CKAD
Use the code - show code in Udemy - while registering for the CKA or CKAD exams at Linux Foundation to get a 15% discount.
El propósito de Kubernetes es alojar sus aplicaciones en forma de Pods de manera automatizada, para que pueda implementar tantas instancias como necesite, y facilitar la comunicación entre sus aplicaciones.
Un cluster de kubernetes consta de un conjunto de nodos (fisicos o virtuales), que alojan aplicaciones en forma de Pods.
- Los nodos Masters, son los responsables de administrar el cluster, y todas las tareas las lleva a través de un conjunto de componentes, llamados Control Plane (Plano de Control).
- Los nodos Workers, están destinados a alojar los Pods.
Componentes del Plano de Control:
- ETCD: es una base de datos que almacena la información en un formato clave-valor (key-value).
- kube-scheduler: es un componente del cluster que se encarga de planificar donde se alojarán los Pods, en base a etiquetas del Nodo o del Pod, capacidad, políticas o restricciones, y afinidad entre el Nodo y el Pod.
- Node-Controller: se encarga de los Nodos, es el responsable de comprobar si los nodos están disponibles o no.
- Replication-Controller: se encarga de los Pods, y de que se estén ejecutando el número de Pods que indica su Replication-Groups.
- kube-apiserver: es el componente de gestión principal, es el responsable de orquestar todas las operaciones del cluster. Expone el API de Kubernetes que utilizan los usuarios externos para realizar operaciones de administración, asi como los distintos controladores para monitorizar el cluster.
Dado que las aplicaciones se ejecutan en contendores, los nodos (Master y Workers) necesitarán tener un motor para ejecutarlos (Containe Runtime), uno popular es Docker. Kubernetes es compatible con otros Containers Runtime, como ContainerD o Rocket (RKT).
- Kubelet: es un servicio que se ejecuta en todos los nodos, escucha las intrucciones del kube-apiserver, y destruye o crea contenedores en base a la información que le proporciona el kube-apiserver.
- kube-apiserver: solicita información sobre el estado de los Pods que se encuentran en los nodos, a través de Kubelet.
- kube-proxy: es un servicio que corre en todos los nodos, y mantiene la comunicación entre nodos, para la comunicación entre Pods.
Ejemplo de arquitectura con buques.
ETCD es una base de datos clave-valor distribuida que es simple, segura y rápida.
Puede usar ETCD ejecutando los siguientes pasos:
# Download file
curl -L https://github.com/coreos/etcd/releases/download/v3.3.1/etcd-v3.3.1-linux-amd64.tar.gz -o etcd-v3.3.1-linux-amd64.tar.gz
# decompress:
tar xzvf etcd-v3.3.1-linux-amd64.tar.gz
# Execute:
./etcd
Cuando ejecuta ETCD inicia un servicio que escucha en el puerto 2380 por defecto.
Existe un cliente de linea de comando que permite interactuar con la BBDD:
# Set value
./etcdctl set key1 value1
# Get value
./etcdctl get key1
ETCD almacena información sobre Nodos, Pods, Roles, Secrets, etc.
Toda la información que proviene de kubectl (cliente de kubernetes en linea de comandos), proviene de ETCD. Cada cambio que realicemos en el cluster actualiza el ETCD, solo una vez que se actualice ETCD, el cambio se considerará completo.
Puede configurar ETCD de distintas formas, en este caso configuraremos ETCD desde cero, y con kubeadm.
- Configuración manual:
# Descargamos el fichero
wget -q --https-only \ "https://github.com/coreos/etcd/releases/download/v3.3.9/etcd-v3.3.9-linux-amd64.tar.gz"
# Show etcd.service
etcd.service
ExecStart=/usr/local/bin/etcd \\
--name ${ETCD_NAME} \\
--cert-file=/etc/etcd/kubernetes.pem \\
--key-file=/etc/etcd/kubernetes-key.pem \\
--peer-cert-file=/etc/etcd/kubernetes.pem \\
--peer-key-file=/etc/etcd/kubernetes-key.pem \\
--trusted-ca-file=/etc/etcd/ca.pem \\
--peer-trusted-ca-file=/etc/etcd/ca.pem \\
--peer-client-cert-auth \\
--client-cert-auth \\
--initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
--advertise-client-urls https://${INTERNAL_IP}:2379 \\
--initial-cluster-token etcd-cluster-0 \\
--initial-cluster-controller-0=https://${CONTROLLER0_IP}:2380,controller-1=https://${CONTROLLER1_IP}:2380 \\ # ETCD Nodes IPs
--initial-cluster-state new \\
--data-dir=/var/lib/etcd
- Configuración con kubeadm:
# Implementación como pod
kubectl get pods -n kube-system | grep etc
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system etcd-master 1/1 Running 0 1h
# Interactua con la BBDD desde dentro del pod
kubectl exec etcd-master –n kube-system etcdctl get / --prefix –keys-only
/registry/apiregistration.k8s.io/apiservices/v1.
/registry/apiregistration.k8s.io/apiservices/v1.apps
/registry/apiregistration.k8s.io/apiservices/v1.authentication.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1.authorization.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1.autoscaling
/registry/apiregistration.k8s.io/apiservices/v1.batch
/registry/apiregistration.k8s.io/apiservices/v1.networking.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1.rbac.authorization.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1.storage.k8s.io
En un entorno de alta disponibilidad (HA), tendrá varios nodos Masters, y tendrá varias instancias de ETCD distribuidas entre los nodos Masters. En ese caso, tendrá que configurar la declaración y conexión entre los nodos del cluster de ETCD
# Show etcd.service
etcd.service
ExecStart=/usr/local/bin/etcd \\
--name ${ETCD_NAME} \\
--cert-file=/etc/etcd/kubernetes.pem \\
--key-file=/etc/etcd/kubernetes-key.pem \\
--peer-cert-file=/etc/etcd/kubernetes.pem \\
--peer-key-file=/etc/etcd/kubernetes-key.pem \\
--trusted-ca-file=/etc/etcd/ca.pem \\
--peer-trusted-ca-file=/etc/etcd/ca.pem \\
--peer-client-cert-auth \\
--client-cert-auth \\
--initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
--advertise-client-urls https://${INTERNAL_IP}:2379 \\
--initial-cluster-token etcd-cluster-0 \\
--initial-cluster controller-0=https://${CONTROLLER0_IP}:2380,controller-1=https://${CONTROLLER1_IP}:2380 \\
--initial-cluster-state new \\
--data-dir=/var/lib/etcd
etcdctl
es la herramienta CLI utilizada para interactuar con ETCD.
ETCDCTL puede interactuar con el Servidor ETCD utilizando 2 versiones de la API - Versión 2
y Versión 3
.
Por defecto está configurado para usar la versión 2. Cada versión tiene diferentes conjuntos de comandos.
Soporta los siguientes comandos:
# Version 2
etcdctl backup
etcdctl cluster-health
etcdctl mk
etcdctl mkdir
etcdctl set
# Version 3
etcdctl snapshot save
etcdctl endpoint health
etcdctl get
etcdctl put
# Para establecer la versión correcta de la API establezca la variable de entorno ETCDCTL_API
export ETCDCTL_API=3
Debes especificar la ruta a los archivos de certificado para que ETCDCTL pueda autenticarse en el servidor de la API de ETCD. Los archivos de certificados están disponibles en el etcd-master en la siguiente ruta.
--cacert /etc/kubernetes/pki/etcd/ca.crt
--cert /etc/kubernetes/pki/etcd/server.crt
--key /etc/kubernetes/pki/etcd/server.key
Comando final:
kubectl exec etcd-master -n kube-system -- sh -c "ETCDCTL_API=3
API Server, es el componente de adminstración principal del cluster de kubernetes.
Cuando ejecuta comandos con kubectl, la solicitud llega al APIServer, este primero autentica la solicitud y la valida, obtiene los datos del ETCD y responde con la información solicitada.
Flujo de creación de objetos (para realizar los cambios se sigue un flujo similar):
- APIServer crea un Pod sin asignarlo a ningún nodo.
- APIServer actualiza el estado del pod en cluster de ETCD.
- APIServer responde al usuario sobre el estado de su solicitud.
- Scheduler monitoriza el APIServer, y se da cuenta de que hay un nuevo Pod sin asignar a ningún nodo.
- Scheduler identifica el nodo correcto, y se lo comunica al APIServer.
- APIServer actualiza ETCD con la nueva información.
- APIServer se comunica con el nodo donde se va colocar el pod, a través de kubelet.
- Kubelet crea el pod y comunica al Container Runtime la imagen o imagenes del Pod.
- Kubelet actualiza el estado del Pod al APIServer.
- APIServer actualiza el estado del Pod a ETCD.
APIServer es el responsable de autenticar, validar solicitudes, recuperar y actualizar datos en el cluster de ETCD (es el único componente que se comunica con el).
La arquitectura de Kubernetes consiste en una gran cantidad de componentes diferentes que trabajan entre si.
Depende como configure su cluster, podrá ver las opciones de kube-APIServer.
- Manifiesto YAML
cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC
- --advertise-address=172.17.0.32
- --allow-privileged=true
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --disable-admission-plugins=PersistentVolumeLabel
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=0
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
...
- Como servicio:
cat /etc/systemd/system/kube-apiserver.service
[Service] ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${INTERNAL_IP} \\
--allow-privileged=true \\
--apiserver-count=3 \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/var/log/audit.log \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--client-ca-file=/var/lib/kubernetes/ca.pem \\
--enable-admission-plugins=Initializers,NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,Defa ultStorageClass,ResourceQuota \\
--enable-swagger-ui=true \\
--etcd-cafile=/var/lib/kubernetes/ca.pem \\
--etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
--etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
--etcd-servers=https://10.240.0.10:2379,https://10.240.0.11:2379,https://10.240.0.12:2379 \\
--event-ttl=1h \\
--experimental-encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
--kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
--kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
- Como proceso:
ps -aux | grep kube-apiserver
root 2348 3.3 15.4 399040 315604 ? Ssl 15:46 1:22 kube-apiserver --authorization-mode=Node,RBAC -- advertise-address=172.17.0.32 --allow-privileged=true --client-ca-file=/etc/kubernetes/pki/ca.crt
--disable- admission-plugins=PersistentVolumeLabel --enable-admission-plugins=NodeRestriction--enable-bootstrap-token- auth=true --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd- client.crt --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 -- insecure-port=0
--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --kubelet-client- key=/etc/kubernetes/pki/apiserver-kubelet-client.key
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt --proxy- client-key-file=/etc/kubernetes/pki/front-proxy-client.key--requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
--requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User --secure- port=6443 --service-account-key-file=/etc/kubernetes/pki/sa.pub --service-cluster-ip-range=10.96.0.0/12
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
En terminos de kubernetes, un controlador es un proceso que monitoriza el estado de varios componentes dentro del sistema y trabaja para llevar todo el sistema al estado deseado.
- Node Controller: verifica el estado de los nodos cada 5 segundos. Si deja de recibir datos de un nodo, espera 40 segundos antes de marcarlo como inalcanzable. Después de marcarlo como inalcanzable, espera 5 minutos para ver que reconecte, si no lo hace elimina los Pods asignados a ese nodo, y lo hace en otro sano.
- Replication Controller: se encarga de monitorizar los ReplicaSets y garantiza el número deseados.
Hay muchos otros Controllers, y están empaquetados en un solo proceso conocido como Controller Manager.
- Puede encontrarlo como Pod:
kubectl get pods -n kube-system
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-78fcdf6894-hwrq9 1/1 Running 0 1h
kube-system coredns-78fcdf6894-rzhjr 1/1 Running 0 1h
kube-system etcd-master 1/1 Running 0 1h
kube-system kube-apiserver-master 1/1 Running 0 1h
kube-system kube-controller-manager-master 1/1 Running 0 1h
kube-system kube-proxy-lzt6f 1/1 Running 0 1h
kube-system kube-proxy-lz1sf 1/1 Running 0 1h
kube-system kube-scheduler-master 1/1 Running 0 1h
kube-system weave-net-12ffc 1/1 Running 0 1h
- Como servicio:
cat /etc/kubernetes/manifests/kube-controller-manager.yaml
...
spec:
containers:
- command:
- kube-controller-manager
- --address=127.0.0.1
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
- --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
- --controllers=*,bootstrapsigner,tokencleaner
- --kubeconfig=/etc/kubernetes/controller-manager.conf
- --leader-elect=true
- --root-ca-file=/etc/kubernetes/pki/ca.crt
- --service-account-private-key-file=/etc/kubernetes/pki/sa.key
- --use-service-account-credentials=true
...
El Scheduler, es el proceso que indica donde irá cada Pod, no los coloca el mismo, en este caso lo hace Kubelet.
El Scheduler pasa por dos fases para identificar el mejor nodo:
- Filtra los nodos que no se ajustan al perfil del Pod. (Por ejemplo, descarta los nodos que no tienen suficientes recursos)
- Con los nodos restantes, utiliza una función de prioridad puntuando a los nodos de 0 a 10. Calculando el espacio que quedará disponible en un nodo si el pod se desplegase en el (a más espacio disponible - mejor puntuación).
Hay más factores a que tener en cuenta como, Limits, Taints y Tolerations, NodeSelectors o Affinity.
- Instalando Scheduler como servicio:
kube-scheduler.service
ExecStart=/usr/local/bin/kube-scheduler \\
--config=/etc/kubernetes/config/kube-scheduler.yaml \\
--v=2
- Opciones de schuduler:
cat /etc/kubernetes/manifests/kube-scheduler.yaml
...
spec:
containers:
- command:
- kube-scheduler
- --address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true
...
Kubelet es el agente de Kubernetes desplegado en cada nodo y se encarga de:
- Registar el nodo en cluster.
- Cuando recibe la orden de crear un contenedor, se lo indica al Container Runtime (Docker, ContainerD, RLT, etc), para que descargue la imagen y lo instancie en el nodo.
- Monitoriza los Pods, y envía informes al API Server.
Kubelet no se instala automáticamente, el agente se instala en cada nodo como servicio.
kubelet.service
ExecStart=/usr/local/bin/kubelet \\
--config=/var/lib/kubelet/kubelet-config.yaml \\
--container-runtime=remote \\
--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
--image-pull-progress-deadline=2m \\
--kubeconfig=/var/lib/kubelet/kubeconfig \\
--network-plugin=cni \\
--register-node=true \\
--v=2
Las opciones de kubelet pueden verse si observamos el proceso:
ps -aux | grep kubelet
root 2095 1.8 2.4 960676 98788 ? Ssl 02:32 0:36 /usr/bin/kubelet --bootstrap- kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf -- config=/var/lib/kubelet/config.yaml --cgroup-driver=cgroupfs --cni-bin-dir=/opt/cni/bin --cni- conf-dir=/etc/cni/net.d --network-plugin=cni
Dentro de un cluster de Kubernetes, cada Pod puede tener comunicación con cualquier otro Pod.
Esto se logra mediante la implementación de una red entre los Pods. Una red virtual se extiende por todo el cluster, a la que se conectan todos los Pods, cuando se crea un Service, kube-proxy lo escanea y crea las reglas necesarias, una forma de hacerlo es con Iptables.
Kube-proxy se implementa como DaemonSet (Pod por nodo).
Un Pod un conjunto de propiedades definidas en un fichero YAML, este fichero contiene la definición del objeto y agrupa uno o varios contenedores.
Un Pod, es la unidad más pequeña en Kubernetes.
#Versión de la API de Kubernetes que estás usando para crear el objeto (String). Es una propiedad obligatoria. Ej: apps/v1, v1, ...
apiVersion: v1
#Tipo de objeto que tratamos de crear (String). Es una propiedad obligatoria. Ej: ReplicaSet, Deployment, Service, ...
kind: Pod
#Permite indicar datos sobre el objecto (diccionario), como el nombre, namespace, labels, etc. Es una propiedad obligatoria.
metadata:
name: myapp-pod
#Permite identificar un objeto y relacionarlo con otros, por ejemplo un Service, podría apuntar a este pod a través de sus Labels.
labels:
app: myapp
type: front-end
#Bajo esta propiedad se indican configuracaciones específicas para este Pod. Es una propiedad obligatoria
spec:
#Propiedad que define los contenedores que se declaran en este Pod (List), dado que puede haber uno o más.
containers:
- name: nginx-container
image: nginx
Definimos algunos comando importantes relativos a los Pods.
- Crear a partir de la definición en un fichero
kubectl create -f pod.yaml
pod/myapp-pod created!
- Obtener todos los Pods de un namespace:
kubectl -n kube-system get pod
NAME READY STATUS RESTARTS AGE
coredns-74ff55c5b-twd8d 1/1 Running 0 20s
etcd-minikube 1/1 Running 0 31s
kube-apiserver-minikube 1/1 Running 0 31s
kube-controller-manager-minikube 1/1 Running 0 31s
kube-proxy-h29mh 1/1 Running 0 20s
kube-scheduler-minikube 1/1 Running 0 31s
storage-provisioner 1/1 Running 0 33s
- Obtener información de los Pods:
kubectl -n kube-system get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-74ff55c5b-twd8d 1/1 Running 0 13m 172.17.0.2 node01 <none> <none>
etcd-minikube 1/1 Running 0 13m 192.168.49.2 node01 <none> <none>
kube-apiserver-minikube 1/1 Running 0 13m 192.168.49.2 node01 <none> <none>
kube-controller-manager-minikube 1/1 Running 0 13m 192.168.49.2 node01 <none> <none>
kube-proxy-h29mh 1/1 Running 0 13m 192.168.49.2 node01 <none> <none>
kube-scheduler-minikube 1/1 Running 0 13m 192.168.49.2 node01 <none> <none>
storage-provisioner 1/1 Running 1 13m 192.168.49.2 node01 <none> <none>
- Obtener información sobre un Pod existente en el cluster (Etiquetas, variables, Contanedores, IP, volumenes, puntos de montaje, etc)
kubectl -n kube-system describe pod kube-proxy-h29mh
Name: kube-proxy-h29mh
Namespace: kube-system
Priority: 2000001000
Priority Class Name: system-node-critical
Node: minikube/192.168.49.2
Start Time: Mon, 03 May 2021 15:39:13 +0200
Labels: controller-revision-hash=b89db7f56
k8s-app=kube-proxy
pod-template-generation=1
Annotations: <none>
Status: Running
IP: 192.168.49.2
IPs:
IP: 192.168.49.2
Controlled By: DaemonSet/kube-proxy
Containers:
kube-proxy:
Container ID: docker://86b1f246816bd3fc3354df5e7d6ffcace8ba9826f240c3936e3cb2ccbf0c6445
Image: k8s.gcr.io/kube-proxy:v1.20.2
Image ID: docker-pullable://k8s.gcr.io/kube-proxy@sha256:326fe8a4508a5db91cf234c4867eff5ba458bc4107c2a7e15c827a74faa19be9
Port: <none>
Host Port: <none>
Command:
/usr/local/bin/kube-proxy
--config=/var/lib/kube-proxy/config.conf
--hostname-override=$(NODE_NAME)
State: Running
Started: Mon, 03 May 2021 15:39:14 +0200
Ready: True
Restart Count: 0
Environment:
NODE_NAME: (v1:spec.nodeName)
Mounts:
/lib/modules from lib-modules (ro)
/run/xtables.lock from xtables-lock (rw)
/var/lib/kube-proxy from kube-proxy (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-proxy-token-kntxm (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-proxy:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: kube-proxy
Optional: false
xtables-lock:
Type: HostPath (bare host directory volume)
Path: /run/xtables.lock
HostPathType: FileOrCreate
lib-modules:
Type: HostPath (bare host directory volume)
Path: /lib/modules
HostPathType:
kube-proxy-token-kntxm:
Type: Secret (a volume populated by a Secret)
SecretName: kube-proxy-token-kntxm
Optional: false
QoS Class: BestEffort
Node-Selectors: kubernetes.io/os=linux
Tolerations: op=Exists
CriticalAddonsOnly op=Exists
node.kubernetes.io/disk-pressure:NoSchedule op=Exists
node.kubernetes.io/memory-pressure:NoSchedule op=Exists
node.kubernetes.io/network-unavailable:NoSchedule op=Exists
node.kubernetes.io/not-ready:NoExecute op=Exists
node.kubernetes.io/pid-pressure:NoSchedule op=Exists
node.kubernetes.io/unreachable:NoExecute op=Exists
node.kubernetes.io/unschedulable:NoSchedule op=Exists
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m default-scheduler Successfully assigned kube-system/kube-proxy-h29mh to minikube
Normal Pulled 119s kubelet Container image "k8s.gcr.io/kube-proxy:v1.20.2" already present on machine
Normal Created 119s kubelet Created container kube-proxy
Normal Started 119s kubelet Started container kube-proxy
- Crear Pods en linea de comandos:
kubectl run nginx --imagen=nginx
- Crear un manifiesto de un pod a través de kubectl
kubectl run redis --image=redis --dry-run=client -o yaml > pod.yaml
# Mostramos el fichero generado
cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: redis
name: redis
spec:
containers:
- image: redis
name: redis
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
El objeto ReplicaSet, garantiza que el número de replicas indicadas en el objeto, esté siempre disponibles. Permite escalar y balancear si fuera necesarios, creando más replicas en el mismo nodo, incluso en otros, de tal forma que para el usuario es transparente.
En Clusters con versiones más antiguas, podemos encontranos Replication Controller, y en cluster con versiones más modernas, ReplicaSet.
- Replication Controller:
apiVersion: v1
kind: ReplicationController
metadata: # Metadata para el ReplicationController.
name: myapp-rc
labels:
app: myapp
type: front-end
spec: # Especificaciones para el ReplicacionController.
template: # Definimos una plantilla para las réplicas que controlará este objeto. Que contendrá las mismas propiedades que un Pod.
metadata: # Metadata para el Pod.
name: myapp-pod
labels:
app: myapp
type: front-end
spec: # Especificaciones para el Pod.
containers:
- name: nginx-container
image: nginx
replicas: 3 # Número de réplicas del Pod.
- Creamos el Replication Controller definido:
kubectl create -f rc-definition.yaml
replicationconrtoller "myapp-rc" created!
# Listamos los Replication Controllers
kubectl get replicationcontrollers
NAME DESIRED CURRENT READY AGE
myapp-rc 3 3 3 72s
# Listamos los pods
kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-rc-gr4hm 1/1 Running 0 33s
myapp-rc-kwm72 1/1 Running 0 33s
myapp-rc-szjkd 1/1 Running 0 33s
- Replica Set:
apiVersion: apps/v1 # apiVersion cambia con respecto a los RCs
kind: ReplicationSet
metadata: # Metadata para el ReplicaSet.
name: myapp-replicaset
labels:
app: myapp
type: front-end
spec: # Especificaciones para el ReplicaSet.
template: # Definimos una plantilla para las réplicas que controlará este objeto. Que contendrá las mismas propiedades que un Pod.
metadata: # Metadata para el Pod.
name: myapp-pod
labels:
app: myapp
type: front-end
spec: # Especificaciones para el Pod.
containers:
- name: nginx-container
image: nginx
replicas: 3 # Número de réplicas del Pod.
selector: # Esta propiedad es la diferencia entre RS y RC, prmite identificar que partes se encuentran bajo el
matchLabels:
type: front-end
- Creamos el ReplicationSet definido:
kubectl create -f rc-definition.yaml
replicationconrtoller "myapp-replicaset" created!
# Listamos los ReplicaSet
kubectl get replicaset
NAME DESIRED CURRENT READY AGE
myapp-replicaset 3 3 3 72s
# Listamos los pods
kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-replicaset-gr4hm 1/1 Running 0 33s
myapp-replicaset-kwm72 1/1 Running 0 33s
myapp-replicaset-szjkd 1/1 Running 0 33s
Los Labels, permiten identificar los objetos de kubernetes, a través de estas etiquetas proporcionamos un filtro para los ReplicaSets.
Podemos escalar las réplicas de distintas formas:
# Editar el manifiesto replicaset-definition.yaml
vim replicaset-definition.yaml # set a new value
kubectl replace -f replicaset-definition.yaml
# Editar el Replicaset and change replica property
kubectl edit myapp-replicaset
# Seteando nuevo valor con kubectl
kubectl scale --replicas=6 -f replicast-definition.yaml
kubectl scale --replicas=6 replicaset myapp-replicaset
Los Deployments, son objectos de kubernetes, que permiten ejecutar Rolling update, Roll-backs, actualizar la versión de sus aplicaciones, cambiar asignaciones de recursos, variables de entorno, escalado, pausar y reanudar cambios según la necesidad, etc.
Para comprender que otros objectos controlan los Deployments observemos la siguiente imagen. IMAGEN DEPLOYMENT
Un ejemplo de manifiesto de Deployment:
apiVersion: apps/v1
# Con respecto a los RS, cambia la propiedad Kind
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end
- Creamos el Deployment definido:
kubectl create -f deployment-definition.yaml
deployment "myapp-deployment" created!
# Listamos los Deployments
kubectl get deployments
NAME DESIRED CURRENT READY AGE
myapp-deployment 3 3 3 72s
# Listamos los Replicationset
kubectl get replicaset
NAME DESIRED CURRENT READY AGE
myapp-deployment-325423452345 3 3 3 72s
# Listamos los pods
kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-deployment-325423452345-gr4hm 1/1 Running 0 33s
myapp-deployment-325423452345-kwm72 1/1 Running 0 33s
myapp-deployment-325423452345-szjkd 1/1 Running 0 33s
# todos los objetos creados
kubectl get all
NAME DESIRED CURRENT READY AGE
deploy/myapp-deployment 3 3 3 72s
NAME DESIRED CURRENT READY AGE
rs/myapp-deployment-325423452345 3 3 3 72s
NAME READY STATUS RESTARTS AGE
po/myapp-deployment-325423452345-gr4hm 1/1 Running 0 33s
po/myapp-deployment-325423452345-kwm72 1/1 Running 0 33s
po/myapp-deployment-325423452345-szjkd 1/1 Running 0 33s
Algunos comando necesarios:
# Crear un pod
kubectl run nginx --image=nginx
# Crear el manifiesto de un pod a partir de kubectl
kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml
# Crear un deployment
kubectl create deploymetn --image=nginx nginx
# Crear manifiesto de un deployment a partir de kubectl
kubectl create deploymetn --image=nginx nginx --dry-run=client -o yaml > deployment.yaml
# Crear manifiesto de un deployment con 4 réplicas a partir de kubectl
kubectl create deployment nginx --image=nginx --replicas=4 --dry-run=client -o yaml > deployment.yaml
Si varios usuarios y equipos utilizan el mismo clúster de Kubernetes podemos dividir el clúster en subclústeres virtuales utilizando Namespaces. El nombre de los recursos/objetos creados dentro de un Namespace son únicos, pero no en los Namespaces del cluster.
Para listar todos los Namespaces, podemos ejecutar el siguiente comando:
$ kubectl get namespaces
NAME STATUS AGE
default Active 11h
kube-node-lease Active 11h
kube-public Active 11h
kube-system Active 11h
Generalmente, Kubernetes crea cuatro Namespaces por defecto:
- kube-system: Contiene los objetos creados por el sistema, principalmente los agentes del plano de control.
- kube-public: Es un namespace inseguro y legible por cualquiera, utilizado para la exposición de información pública no sensible sobre el cluster.
- default: Contiene los objetos y recursos creados por los administradores y desarrolladores.
Puede asignar Quota de recursos a cada uno de los Namespaces.
Los recursos en el mismo Namespace, puede referise entre si por sus nombres. Ej: una aplicación (app-a) se conectará con otra (app-b) a través de un Service (b-service).
mysql.connect("b-service")
Es posible conectar aplicaciones de Namespaces distintos.
mysql.connect("<service-name>.<namespace>.svc.cluster.local")
mysql.connect("b-service.dev.svc.cluster.local")
Objeto namespace en YAML:
apiVersion: v1
kind: Namespace
metadata:
name: dev
Algunos comandos:
# Listar Pods namespace por defecto
kubectl get pods
# Listar Pods nampespace específico
kubectl get pods --namespace kube-system
# Crear Pod a través de un manifiesto en el namespace por defecto
kubectl create -f pod-definition.yaml
pod/my-app created!
# Crear Pod a través de un manifiesto en un namespace específico
kubectl create -f pod-definition.yaml --namespace dev
pod/my-app created!
# Crear Namespace con kubectl
kubectl create namespace dev
namespace/dev created!
# Cambiar namespace por defecto
kubectl config set-context $(kubectl config current context) --namespace dev
# Listar Pods de todos los namespaces
kubectl get pods --all-namespaces
kubectl get pods -A
Para limitar los recursos asignados a su namespace, puede definir otro objeto llamado ResourceQuota:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: dev
spec:
hard:
pods: "10" # 10 Pods como máximo en el namespace
requests.cpu: "4" # Request CPU. La suma de las request de todos los Pods del namespace, no puede exceder de 4.
requests.memory: 5Gi # Request Memory. La suma de las request de todos los Pods del namespace, no puede exceder de 5Gi
limits.cpu: "10" # Limits CPU. La suma de las limits de todos los Pods del namespace, no puede exceder de 10
limits.memory: 10Gi # Limits Memory. La suma de las limits de todos los Pods del namespace, no puede exceder de 10Gi
Creamos el objeto en kubernetes
kubectl create -f compute.quota.yaml
Los Services son objectos de Kubernetes que permiten la comunicación dentro y fuera del cluster.
Los Service de tipo NodePort, permiten dedicar un puerto alto (30000-32767) en todos los nodos del cluster. El propio cluster detectará todos los Pods con las etiquetas indicadas y balancerá la carga entre todos ellos.
Se accedería a la aplicación
http://node-ip:30008
- Ejemplo de manifiesto YAML:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
# Tipo de objeto, podría ser ClusterIP, LoadBalancer o NodePort
type: NodePort
selector:
# Se declaran las mismas labels que contiene el Pod, para que el Service, apunte a la aplicación o aplicaciones correctas.
app: myapp
type: front-end
# Puede tener uno o más puertos
ports:
# Si no se indica targePort, por defecto tendrá el mismo que el valor de Port
- port: 80
targetPort: 80
# Si no se indica, el Control Plan de Kubernetes asignará un puerto alto del rango (default: 30000-32767)
nodePort: 30007
Comprobamos que el SVC ha sido creado y realizamos una petición curl a todos los nodos con el mismo puerto proporcionado por el SVC:
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14s
myapp-service NodePort 10.106.127.123 <none> 80:30007 4s
# Realizamos una petición a las IPs y puertos de los nodos
curl http://192.168.1.2:30007
Hello World!
curl http://192.168.1.3:30007
Hello World!
curl http://192.168.1.4:30007
Hello World!
Pueden encontrarse distintos componentes, en una aplicación. Por ejemplo: Front-end, back-end y redis. Todos ellos son Pods de nuestro cluster y necesitarán tener conectividad.
Todos los Pods de nuestro cluster, tiene IP, pero, ¿que necesitariamos para conectar los Pods?
Necesitaremos un Service de tipo ClusterIP, permite la comunicación entre distintas capas. Cada capa puedes escalar sin afectar a la comunicación. Estos servicios definene una IP dentro del cluster, a través del nombre proporcionado para el Service, se establece la comunicación.
- Ejemplo de definición de Service ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP
selector:
app: myapp
type: back-end
ports:
- protocol: TCP
port: 80
targetPort: 80
- Creación del SVC:
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14s
back-end ClusterIP 10.106.127.123 <none> 80/TCP 4s
Como hemos visto, si los usuarios quisieran acceder a una aplicación del cluster, podriamos utilizar un Service de tipo NodePort, les facilitariamos las IPs de los nodos y el puerto asignado, pero, ¿que ocurre si los nodos no son accesibles? ¿y si hay multitud de puertos para los mismos nodos? Podriamos crear una máquina virtual con el propósito de equilibrar la carga entre todos los nodos con nginx, y enrutar hacia los nodos del cluster, pero esto puede ser una tarea tediosa.
Sin embargo, si estuvieramos en una plataforma Cloud, podriamos aprovechar los Load Balancer que nos proporcionan. Kubernetes, tiene soporte para integrase con los balanceadores de carga de estas plataformas, y configurarlo para nosotros.
- Definición de SVC LoadBalancer:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
type: front-end
ports:
- nodePort: 80
port: 80
targetPort: 30007
type: LoadBalancer
Los comandos imperativos realizan una tarea concreta, como por ejemplo crear o borrar un objecto en Kubernetes. Los comandos declarativos son más inteligentes, permiten crear un objeto si no existe o actualizarlo si existe.
Los comandos imperativos, de cara al examen, le ayudan a realizar una tarea con mayor rapidez. Algunos comandos:
- Imperativos:
# Crear un objeto a partir de un fichero
kubectl create -f nginx.yaml
# Edita un fichero alojado en memoria
kubectl edit deployment nginx
# Reemplaza un objeto a partir de un fichero
kubectl replace -f nginx.yaml
# Recrear objetos
kubectl replace --force -f nginx.yaml
# No permite crear un objeto que ya existe
kubectl create -f nginx.yaml
`Error from Server (AlreadyExists): error when creating "nginx.yaml": pods "myapp-pod" already exists`
# No permite reemplazar si el objeto no existe
kubectl replace -f nginx.yaml
`Error from Server (Conflict): error when replacing "nginx.yaml": Operation cannot be fulfilled on pods "myapp-pod"`
- Declarativos
# Crear un objeto si no existe
kubectl apply -f nginx.yaml
# Crear todos los objetos de un path
kubectl apply -f .
# Actualizar un objeto si existe
kubectl apply -f nginx.yaml
- Tips
# Mostar configuración de kubeconfig
kubectl config view
# Mostrar los contextos
kubectl config get-contexts
# Crear o actualizar objetos a partir de un fichero
kubectl apply -f file.yaml
# Crear un Pod
kubectl run nginx --image=nginx
# Crear un Deployment
kubectl create deployment nginx --image=nginx --replicas=4
# Exponer un Deployment
kubectl expose deployment nginx --port 80
# Editar un Deployment
kubectl edit deployment nginx
# Escalar un Deployment
kubectl scale deployment nginx --replicas=5
# Setear imagen en un Deployment
kubectl set image deployment nginx=nginx:1.18
# Crear un Job
kubectl create job hello --image=busybox -- echo "Hello World"
# Crear pod con shell interactiva
kubectl run -ti busybox --image=busybox -- sh
# Crear manifiesto para Pod
kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml
# Crear manifiesto para SVC
kubectl expose pod redis --port=6379 --name redis-service --dry-run=client -o yaml
kubeclt create service clusterip redis --tcp=6379:6379 --dry-run=client -o yaml
En el manifiesto de Pods, hay una propiedad que normalmente no se especifica ,nodeName, si no se indica, Kubernetes lo agrega automáticamente si existe un Scheduler.
El Scheduler es el encargado de revisar los Pods buscando los que no tienen esta propiedad, estos serán programados, el Scheduler identitifica el nodo correcto, y añade esta propiedad.
nodeName: node02
Cuando no hay un Scheduler en el cluster, el estado de los Pods creados resultará Pending.
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 3S
Puedes indicar manualmente esta propiedad en tu manifiesto, de esta forma provisionarás el Pod en el nodo indicado:
- Manifiesto:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: node02
kubectl get pods
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Pending 0 3S 10.40.0.4 node02
Cuando asignas la propiedad manualmente, no permite su modificación.
Las Labels son muy útiles para filtrar y ver diferentes objetos por diferentes categorías, como agrupar por su tipo o ver objetos por aplicación o por funcionalidad.
Puede agrupar objetos usando Labels según el tipo de objeto, necesidad, función, etc. En el manifiesto de su objeto, en la sección de Metadata, indique tantas Labels como necesite con un formato key-value.
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
labels:
app: App1
function: Front-end
spec:
containers:
- name: simple-webapp
image: nginx
ports:
- containerPort: 8080
Puede seleccionar objetos usando Selectors, para filtrar objetos específicos.
kubectl get pod --selector app=App1
NAME READY STATUS RESTARTS AGE
simple-webapp 1/1 Running 0 11s
Los objetos de Kubernetes utilizan Labels y Selectors internamente para conectar diferentes objetos entre si.
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-webapp
# Labels definidas para el Deployment
labels:
app: App1
function: Front-end
spec:
replicas: 3
# Sección que une el Deployment con los Pods que controlará. Añadir las mismas Labels que contiene el Pod.
selector:
matchLabels:
app: App1
function: Front-end
template:
metadata:
# Labels definidas para los Pods.
labels:
app: App1
function: Front-end
spec:
containers:
- name: simple-webapp
image: nginx
ports:
- containerPort: 80
Funciona igual con otros objetos:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
# Sección que une el Service con los Pods.
selector:
app: App1
function: Front-end
ports:
- protocol: TCP
port: 80
targetPort: 9376
Mientras que las Labels y Selectors, se usan para agrupar y seleccionar, las Annotations se usan para indicar cualquier dato, como detalles de herramientas, nombres, contactos, números de telefono, correos electrónicos, etc, que pueden usarse para algún tipo de propósito de integración.
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-webapp
labels:
app: App1
# Annotations, permiten indicar datos, incluso la integración con terceros
## https://github.com/stakater/Reloader
annotations:
secret.reloader.stakater.com/reload: "foo-secret,bar-secret,baz-secret"
spec:
replicas: 3
selector:
matchLabels:
app: App1
template:
metadata:
labels:
app: App1
spec:
containers:
- name: simple-webapp
image: nginx
Por defecto, no existen restricciones ni limitaciones en los Nodes, por lo tanto cualquier Pod puede ser ubicado en cualquier Node.
Para evitar que un Node, contenga Pods que no son deseados, se marca con la propiedad Taint, y para que ciertos recursos caigan sobre esos nodos, necesitan una propiedad Tolerations.
# Taints - Node
kubectl taint nodes node-name key=value:taint-effect
Existen distintos efectos sobre los Taints:
- NoSchedule: El sistema no provisionará ningún Pod sin Tolerations, en el Nodo.
- PreferNoSchedule: El sistema intentará evitar colocar un Pod en ese nodo, pero no lo garantiza.
- NoExecute: Los nuevos Pods provisionados en el cluster no se añadirán a este nodo, y si ya existen, drenará los Pods si no tienen el Toleration correcto.
# Taint con NoSchedule
kubectl taint nodes node1 app=blue:NoSchedule
# Pod con Toleration
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect: "NoSchedule"
Los Nodos Masters, también tienen capacidad para tener Pods, sin embargo no se recomienda programar Pods en el. Estos, tambíen tienen la propiedad Taint.
kubectl describe node master01 | grep Taint
Taints: node-role.kubernetes.io/master:NoSchedule
Podemos indicar en los Pods, el Nodo en el que tienen que programarse. NodeSelector, es la propiedad que permite esto, es la unión entre un Pod y un Nodo.
Debe tener un Label en el Nodo
kubectl label nodes node01 size=Large
# Pod
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
size: Large
Si quisieramos indicar al Scheduler que un Pod debe ser provisionado en un nodo con LAbel size=Large o size=Medium, necesitariamos utilizar otra propiedad, nodeAffinity.
# Pod con affinity In
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: In # In, NotIn
values:
- Large
- Medium
---
# Pod con Affinity NotIn
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: NotIn
values:
- Small
---
# Solo comprobar que existe una key
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: Exists
Existen tipos de Node Affinity:
- requiredDuringSchedulingIgnoredDuringExecution: No se provisionará cuando se crea, pero será ignorado si ya se encuentra provisinado.
- preferredDuringSchedulingIgnoredDuringExecution: Evitará provisionarse si se crea, y será ignorado si ya se encuentra provisinado.
- requiredDuringSchedulingRequiredDuringExecution: No se provisionará cuando se crea, si ya existe será drenado a otro nodo.
DuringScheduling: estado donde no existe un Pod y se crea por primera vez. DuringExecution: estado donde ya existe un Pod programado.
Cada Nodo tiene un conjunto de recursos de CPU, Memoria y Disco. Cada Pod consume un conjunto de recursos.
El Scheduler trata de programar el Pod en el Nodo con menos carga para balancear el uso de los nodos, pero además, también evalua el tamaño de los recursos del Pod, evitando los nodos que no pueden alojar el Pod por recursos insuficientes.
Cuando un Pod no pueda ser alojado por motivos de recursos, podrá ver el siguiente log en el Pod:
Events:
Reason Message
------ -------
FailedScheduling No nodes are available: Insufficient cpu (3).
Por defecto, Kubernetes supone que un Pod requiere 0.5CPU y 256Mi. Pero se pueden indicar otros valores:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "1Gi"
cpu: 1
Los recursos de un Pod se pueden establecer:
- CPU: 1m (valor mínimo), 100m, 0.1, 1, etc
- Memory: 1Mi(1,048,576 bytes), 1M(1,000,000 bytes), 1G, 1Gi,
También puede establecer un límite de recursos para el Pod
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "1Gi"
cpu: 1
limits:
memory: "2Gi"
cpu: 2
Si no define límites en sus Pods, podrán utilizar si en algún momento lo requieren, toda la CPU o Memoria del nodo, sin embargo, puede limitar el uso a través de los Limits.
Para la CPU, el nodo limitará al Pod, sin embargo para el consumo de memoria, si el Pod excede del límite, el Pod será eliminado.
Puede establecer el límite por defecto para un Pod, creando objetos LimitRange.
# Default Memory
apiVersion: v1
kind: LimitRange
metadata:
name: default-mem-limits
spec:
limits:
- default:
memory: 512Mi
defaultRequest:
memory: 256Mi
type: Container
---
# Default CPU
apiVersion: v1
kind: LimitRange
metadata:
name: default-cpu-limits
spec:
limits:
- default:
cpu: 1
defaultRequest:
cpu: 0.5
type: Container
Además, podemos limitar la asignación de recursos en un Namespace.
# Min Max Memory
apiVersion: v1
kind: LimitRange
metadata:
name: mem-min-max
spec:
limits:
- max:
memory: 500Mi
min:
memory: 128Mi
type: Container
---
# Min Max CPU
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-min-max
spec:
limits:
- max:
cpu: "500m"
min:
cpu: "100m"
type: Container
Un DaemonSets es un objeto de Kubernetes, que a su vez, contiene un ReplicaSet. Permite programar una réplica del Pod gestionado en cada Nodo del cluster, además de asegurar de añadir nuevas réplicas en los nuevos nodos que se añadan al cluster.
Kube-proxy o Calico, son ejemplos de DaemonSet, se programan en cada nodo.
# Ejemplo con algunas de las propiedades descritas
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: monitoring-daemon
namespace: kube-system
labels:
k8s-app: monitoring
spec:
selector:
matchLabels:
name: monitoring-agent
template:
metadata:
labels:
name: monitoring-agent
spec:
containers:
- name: fluentd
image: gcr.io/fluentd-elasticsearch/fluentd:v2.5.1
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
Para garantizar que cada nodo contiene las réplicas del DaemonSet, se le añade la propiedad NodeName.
Si no hubiese Control Planne, un nodo podría funcionar por si mismo a través de Kubelet. Sabemos que la única misión de kubelet, es crear Pods, pero en este caso no tenemos API Server para comunicarle la creación de Pods. Podemos configurar kubelet para leer los ficheros de una ruta en el nodo /etc/kubernetes/manifest/, kubelet revisará periódicamente este diretorio y creará los Pods (solo puede crear Pods) que alli se encuentren definidos, también se ocupará de que se mantenga vivos.
[Service]
ExecStart=/usr/local/bin/kubelet \\
--container-runtime=remote \\
--container-runtime-endpoint=unix://var/run/containerd/containerd.sock \\
# Con esta propiedad se define donde se encuentran los Statics Pods
--pod-manifest-path=/etc/kubernetes/manifest \\
--kubeconfig=/var/lib/kubelet/kubeconfig \\
--network-plugin=cni \\
--register-node=true \\
--v=2
También puede indicarse en el fichero kubeconfig
cat /var/lib/kubelet/kubeconfig
...
staticPodPath: /etc/kubernetes/manifest
...
Una vez creados, podriamos visualizar nuestros Statics Pods a través de Docker, recordemos que no tenemos un API Server.
Si tuvieramos un API Server, podriamos ver los Statics Pods creados pero no podriamos editarlos a través de kubectl.
Un caso de uso para tener múltiples Schedulers, es que el algoritmo y comprobraciones que hace el actual Scheduler no satisfacen sus necesidades.
# kube-scheduler.service
ExecStart=/usr/local/bin/kube-scheduler \\
--config=/etc/kubernetes/config/kube-scheduler.yaml \\
--scheduler-name= default-scheduler
# my-custom-scheduler.service
ExecStart=/usr/local/bin/kube-scheduler \\
--config=/etc/kubernetes/config/kube-scheduler.yaml \\
--scheduler-name= default-scheduler
Podemos copiar el Pod del actual Scheduler y añadirle algunos parámetros más:
# /etc/kubernetes/manifest/kube-scheduler.yaml
apiVersion: v1
kind: Pod
metadata:
name: kube-scheduler
namespace: kube-system
spec:
containers:
- command:
- kube-scheduler
- --address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
image: k8s.gcr.io/kube-scheduler-amd64:v1.11.3
name: kube-scheduler
---
# my-custom-scheduler.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-custom-scheduler
namespace: kube-system
spec:
containers:
- command:
- kube-scheduler
- --address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
- --scheduler-name=my-custom-scheduler
- --lock-object-name=my-custom-scheduler
image: k8s.gcr.io/kube-scheduler-amd64:v1.11.3
name: kube-scheduler
NAME READY STATUS RESTARTS AGE
coredns-78fcdf6894-bk4ml 1/1 Running 0 1h
coredns-78fcdf6894-ppr6m 1/1 Running 0 1h
etcd-master 1/1 Running 0 1h
kube-apiserver-master 1/1 Running 0 1h
kube-controller-manager-master 1/1 Running 0 1h
kube-proxy-dgbgv 1/1 Running 0 1h
kube-proxy-fptbr 1/1 Running 0 1h
kube-scheduler-master 1/1 Running 0 1h
my-custom-scheduler 1/1 Running 0 9s
weave-net-4tfpt 2/2 Running 1 1h
weave-net-6j6zs 2/2 Running 1 1h
Para usar el nuevo Scheduler, basta con indicarselo al Pod:
apiVersion: v1
kind: Pod
metadata:
name: annotation-default-scheduler
labels:
name: multischeduler-example
spec:
# Define la propiedad schedulerName
schedulerName: my-custom-scheduler
containers:
- name: pod-with-default-annotation-container
image: k8s.gcr.io/pause:2.0
Mostramos los eventos del namespace donde corre el pod
kubectl get events -n default
LAST SEEN COUNT NAME KIND TYPE REASON SOURCE MESSAGE
9s 1 nginx.15 Pod Normal Scheduled my-custom-scheduler Successfully assigned default/nginx to node01
8s 1 nginx.15 Pod Normal Pulling kubelet, node01 pulling image "nginx"
2s 1 nginx.15 Pod Normal Pulled kubelet, node01 Successfully pulled image "nginx"
Kubernetes no facilita por defecto un sistema de monitorización, existen multitud de ellos
- Metrics Server
- Prometheus
- Elastic Stack
- DataDog
- DynaTrace
Heapster fue uno de los proyectos originales que habilitó las funciones de monitorización, cuando realice búsquedas, verá muchas referencias a este componente. Sin embargo, se encuentra deprecada. Se formó una versión reducida como Metrics Server.
Metrics-server recupera las métricas de cada uno de los Nodos y Pods en memoria, no puede ver el histórico.
Kubelete tiene un subcomponente conocido como cAdvisor o Container Advisor, es el responsable de recuperar las métricas de rendimiento, y exponerlas a través de la API de kubelet.
Puede instalar este servicio instalando los objetos que lo componen:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Una vez creado metrics-server, podemos obtener métricas de los nodos y de los Pods:
# Nodes
kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
kubemaster 166m 8% 1337Mi 70%
kubenode1 36m 1% 1046Mi 55%
kubenode2 39m 1% 1048Mi 55%
# Pods
kubectl top pod
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
nginx 166m 8% 1337Mi 70%
redis 36m 1% 1046Mi 55%
Al igual que Docker, Kubernetes nos permite visualizar los logs de nuestras aplicaciones.
# -f logs en tiempo real
# -c seleccionar un contenedor si nuestro Pod contiene más de uno.
kubectl logs –f event-simulator-pod -c event-simulator
2018-10-06 15:57:15,937 - root - INFO - USER1 logged in
2018-10-06 15:57:16,943 - root - INFO - USER2 logged out
2018-10-06 15:57:17,944 - root - INFO - USER2 is viewing page2
2018-10-06 15:57:18,951 - root - INFO - USER3 is viewing page3
2018-10-06 15:57:19,954 - root - INFO - USER4 is viewing page1
2018-10-06 15:57:20,955 - root - INFO - USER2 logged out
2018-10-06 15:57:21,956 - root - INFO - USER1 logged in
2018-10-06 15:57:22,957 - root - INFO - USER3 is viewing page2
2018-10-06 15:57:23,959 - root - INFO - USER1 logged out
2018-10-06 15:57:24,959 - root - INFO - USER2 is viewing page2
2018-10-06 15:57:25,961 - root - INFO - USER1 logged in
2018-10-06 15:57:26,965 - root - INFO - USER4 is viewing page3
2018-10-06 15:57:27,965 - root - INFO - USER4 is viewing page3
2018-10-06 15:57:28,967 - root - INFO - USER2 is viewing page1
2018-10-06 15:57:29,967 - root - INFO - USER3 logged out
2018-10-06 15:57:30,972 - root - INFO - USER1 is viewing page2
2018-10-06 15:57:31,972 - root - INFO - USER4 logged out
2018-10-06 15:57:32,973 - root - INFO - USER1 logged in
2018-10-06 15:57:33,974 - root - INFO - USER1 is viewing page3
Cuando se crea un Deployment, se crea una versión (PE: v1) (replicaSet), en el futuro cuando el Deployment es actualizado, las versiones también son actualizadas. Esto nos ayuda a realizar el seguimiento de nuestro Deploymente, y retroceder a una versión anterior si fuera necesario.
# Conocer el estado de un Deployment
kubectl rollout status deployment/myapp-deployment
Waiting for rollout to finish: 0 of 10 updated replicas are available...
Waiting for rollout to finish: 1 of 10 updated replicas are available...
Waiting for rollout to finish: 2 of 10 updated replicas are available...
Waiting for rollout to finish: 3 of 10 updated replicas are available...
Waiting for rollout to finish: 4 of 10 updated replicas are available...
Waiting for rollout to finish: 5 of 10 updated replicas are available...
Waiting for rollout to finish: 6 of 10 updated replicas are available...
Waiting for rollout to finish: 7 of 10 updated replicas are available...
Waiting for rollout to finish: 8 of 10 updated replicas are available...
Waiting for rollout to finish: 9 of 10 updated replicas are available...
deployment "myapp-deployment" successfully rolled out
# Conocer el historial de estado de un Dployment
kubectl rollout history deployment/myapp-deployment
deployments "myapp-deployment"
REVISION CHANGE-CAUSE
1 <none>
2 kubectl apply --filename=deployment-definition.yml --record=true
Existen dos tipos de estrategias de despliegues:
- Recreate: destruir primero los Pods, e implementar la nueva versión. Utilizar este método causa que la aplicación no esté disponible.
- Rolling Update: destruir y crear la nueva versión Pod a Pod, sin causar que la aplicación no esté disponible.
# Recreate
...
spec:
replicas: 3
strategy:
type: Recreate
...
# RollingUpdate
...
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # Cuantos Pods se añaden al mismo tiempo
maxUnavailable: 0 # Cuantos Pods puede no estar disponibles durante la actualización
...
Cuando una actualización no funciona como esperabamos, podemos utilizar la estrategia de Rollback,
kubectl rollout undo deployment/myapp-deployment
deployment "deployment/myapp-deployment" rolled back
# Alternativamente puedes indicarle la revisión a la que quieres volver
kubectl rollout undo deployment/myapp-deployment --to-revision=2
deployment "deployment/myapp-deployment" rolled back
Una imagen pueden contener parámetros que indiquen como se comportará el contendor cuando se lanza:
# Dockerfile ubuntu-sleeper
FROM Ubuntu # imagen
ENTRYPOINT ["sleep"] # Comando que se lanza
CMD ["5"] # argumento para el comando por defecto
# Ejecución
docker run ubuntu-sleeper 10
La misma imagen, usada en un Pod:
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper-pod
spec:
containers:
- name: ubuntu-sleeper
image: ubuntu-sleeper # imagen
command: ["sleep2.0"] # Reemplazamos el ENTRYPOINT de la imagen
args: ["10"] # Indicamos un argumento
Podemos proporcionar variables a un Pod de distintas formas.
- Texto plano:
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
env:
- name: APP_COLOR
value: green
- Carga de variables a través de Configmap
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: configmap-color
key: color
- Carga de variables a través de Secrets
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
env:
- name: APP_COLOR
valueFrom:
secretKeyRef:
name: secret-color
key: color
Si tenemos muchos datos que pasar a un Pod, podemos configurar objetos configMaps para almacenarlos.
- Crear configMap:
# Sintaxis Imperativa
kubectl create configmap <config-name> --from-literal=<key>=<value>
# Ejemplos
## Configmap con una clave
kubectl create configmap \
app-config --from-literal=APP_COLOR=blue
## Configmap con varias claves
kubectl create configmap \
app-config --from-literal=APP_COLOR=blue \
--from-literal=ENVIRONMENT=pro \
[email protected]
## Configmap a partir de un fichero
kubectl create configmap \
app-config --from-file=app_config.properties
- Componer manifiesto configMap:
# app-config
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_COLOR: blue
APP_MODE: prod
Para referenciarlos basta con detallarlo en el Pod:
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: app-config
Existen distintas formas de pasar un ConfigMap a un Pod:
# Carga como variable de entorno
envForm:
- configMapRef:
name: app-config
# Carga como variable simple
env:
- name: APP_COLOR
valueFrom:
configMapRefKey:
name: app-config
key: APP_COLOR
# Carga de variables como un volumen
volumes:
- name: app-config-volume
configMap:
name: app-config
Los Secrets de Kubernetes, son objetos que codifican su contenido en base64, son útiles para almacenar datos sensibles, que posteriormente se utilizarán en los servicios que implementen.
# Sintaxis creación de secretos
kubectl create secret generic \
<secret-name> --from-literal=<key>=<value>
# Ejemplos
## Crear secreto con una clave
kubectl create secret generic \
app-secret --from-literal=DB_PASS=pass
## Crear secreto con múltiples claves
kubectl create secret generic \
app-secret --from-literal=DB_PASS=pass \
app-secret --from-literal=DB_HOST=host
## Crear secreto a partir de un fichero
kubectl create secret generic \
app-secret --from-file=db.properties
Cuando se crea el secreto a través de comando imperativo, el contenido se codifica en Base64.
kubectl describe secret app-secret
...
DB_PASS: ZXMgdW4gc2VjcmV0bwo=
...
Crear un manifiesto de un secreto se vería así, debemos indicar el contenido codificado:
apiVersion: v1
kind: Secret
metadata:
name: app-secret
data:
DB_PASS: ZXMgdW4gc2VjcmV0bwo= # base64
Para pasar un secreto a un Pod, lo hacemos:
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: app-secret
# Secreto como variables
envFrom:
- secretRef:
name: app-secret
# Secreto como única variable
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_Pass
# Secreto como volumen (se crearán en el punto de montaje tantos ficheros como keys contenta el screto)
volumes:
- name: app-secret-volume
secret:
secretName: app-secret
Para codificar y decodificar un texto:
echo "pass" | base64 #Codificar
echo "ZXMgdW4gc2VjcmV0bwo=" | base 64 -d
En ocasiones puede necesitar más de un contenedor en un Pod, por ejemplo, puede necesitar un contenedor para recoger los logs.
apiVersion: v1
kind: Pod
metadata:
name: multiple-container
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
- name: agent-log
image: agent-lob
En un Pod con múltiples contenedores, se espera que cada uno ejecute un proceso que permanezca vivo mientras dure el ciclo de vida del POD.
Los initContainer, es un contendor que se inicia cuando el Pod es provisionado, permite ejecutar tareas antes de que la aplicación principal esté disponible, un caso de uso puede ser un proceso que espera a que un servicio externo o una base de datos esté disponible antes de que se inicie la aplicación real. Es posible declarar múltiples initContainers y su ejecución es secuencial.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'git clone <some-repository-that-will-be-used-by-application> ; done;']
Existe múltiples tareas de mantenimiento sobre los nodos de un cluster, pero estos nodos es posible que estén dando servicio. Kubectl, permite indicar a kubernetes el drenado de Pods de un nodo.
kubectl drain node01
Cuando drenamos un nodo, lo que estamos haciendo es acordonarlo (ningún Pod será programado en el nodo), y terminar los Pods que están programados en el. Kubernetes los recreará en otro nodo.
Una vez terminemos las tareas de mantenimiento sobre el nodo, podrá volver a estar disponible para el programar Pods.
# Permite programar Pods en el nodo
kubectl uncordon node01
# Evita que se programen Pods en el nodo
kubectl cordon node01
Existe diferentes estrategias para la actualización de las versiones que corren en los nodos. Los primeros nodos a actualizar, sería los Master. Partiendo de que tenemos los Master, actualizados:
- Todos a la vez, supone que ninguna de sus aplicaciones estarán disponibles durante el proceso de actualización.
- Uno a uno, moviendo los Pods entre nodos para evitar que no estén disponibles las aplicaciones.
- Agregar nuevos nodos actualizados, moviendo los Pods a estos, y eliminando los desactualizados.
Con Kubeadm podemos actualizar los nodos con el siguente comando:
# Mostramos el plan de actualización
$ kubeadm upgrade plan
[preflight] Running pre-flight checks.
[upgrade] Making sure the cluster is healthy:
[upgrade/config] Making sure the configuration is correct:
[upgrade] Fetching available versions to upgrade to
[upgrade/versions] Cluster version: v1.11.8
[upgrade/versions] kubeadm version: v1.11.3
[upgrade/versions] Latest stable version: v1.13.4
[upgrade/versions] Latest version in the v1.11 series: v1.11.8
Components that must be upgraded manually after you have
upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 3 x v1.11.3 v1.13.4
Upgrade to the latest stable version:
COMPONENT CURRENT AVAILABLE
API Server v1.11.8 v1.13.4
Controller Manager v1.11.8 v1.13.4
Scheduler v1.11.8 v1.13.4
Kube Proxy v1.11.8 v1.13.4
CoreDNS 1.1.3 1.1.3
Etcd 3.2.18 N/A
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.13.4
apt-get upgrade -y kubeadm=1.12.0-00
kubeadm upgrade apply v1.12.0
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.12.0". Enjoy!
[upgrade/kubelet] Now that your control plane is upgraded, please proceed with
upgrading your kubelets if you havent already done so.
# Mostramos la versión de nuestros nodos
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 1d v1.11.3
node-1 Ready <none> 1d v1.11.3
node-2 Ready <none> 1d v1.11.3
$ apt-get upgrade -y kubelet=1.12.0-00
$ systemctl restart kubelet
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 1d v1.12.0
node-1 Ready <none> 1d v1.11.3
node-2 Ready <none> 1d v1.11.3
- Configuraciones Lo más seguro es almacenar todos nuestos manifiestos de Kubernetes en un sistema de control de versiones, de esta forma será mantenido más facilmente por un equipo.
Si alguien ha creado algún recurso de forma imperativa, puede obtener un backup consultando al API Server.
kubectl get all --all-namespaces -o yaml > all-deploy-services.yaml
Algunos sitemas se encarga de realizar estas tareas Velero
- ETCD
ETCD cuenta con un comando para realizar snapshots de sus datos
ETCD_API=3 etcdctl snapshot save snapshot.db
Restaurar una copia de ETCD
# Para el servicio kube-apiserver
$ service kube-apiserver stop
Service kube-apiserver stopped
# Restauramos la copia en una ruta
ETCDCTL_API=3 etcdctl \
snapshot restore snapshot.db \
--data-dir /var/lib/etcd-from-backup \
# Configuramos el servicio para que obtenga los datos de la nueva ruta
etcd.service
ExecStart=/usr/local/bin/etcd \\
--name ${ETCD_NAME} \\
--cert-file=/etc/etcd/kubernetes.pem \\
--key-file=/etc/etcd/kubernetes-key.pem \\
--peer-cert-file=/etc/etcd/kubernetes.pem \\
--peer-key-file=/etc/etcd/kubernetes-key.pem \\
--trusted-ca-file=/etc/etcd/ca.pem \\
--peer-trusted-ca-file=/etc/etcd/ca.pem \\
--peer-client-cert-auth \\
--client-cert-auth \\
--initial-advertise-peer-urls https://${INTERNAL_IP}:2380
--listen-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379
--advertise-client-urls https://${INTERNAL_IP}:2379 \\
--initial-cluster-token etcd-cluster-0 \\
--initial-cluster controller-0=https://${CONTROLLER0_IP}:2380,controller
--initial-cluster-state new \\
--data-dir=/var/lib/etcd-from-backup
# Reiniciamos el demonio
$ systemctl daemon-reload
$ systemctl etcd restart
# Finalmente, inicie el servicio kube-apiserver
$ service kube-apiserver start
Service kube-apiserver started
# Recuerde indicar todos los parámetros necesario para interactuar con ETCD
ETCDCTL_API=3 etcdctl \
snapshot restore snapshot.db \
--data-dir /var/lib/etcd-from-backup \
--initial-cluster master-1=https://192.168.5.11:2380,master-2=https://192.168.5.12:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-advertise-peer-urls https://${INTERNAL_IP}:2380
etcdctl
es un cliente de línea de comandos para etcd.
Cuando realice acciones sobre ETCD v3, asegurse de indicar la versión: export ETCDCTL_API=3
.
Puede pasar el flag -h para conocer todas las opciones de un comando.
Algunos puntos a tener en cuenta con respecto a la seguridad en kubernetes, son los siguientes:
Debe deshabilitar el acceso al usuario root, deshabilitar la password de usuarios y permitir solo la autenticación solo por clave ssh.
Para comenzar, necesitamos tomar dos tipos de decisiones: quien puede acceder al cluster y que puede hacer.
-
Autenticación: los mecanismos de autenticación definen quien puede acceder al API Server. Existen diferentes formas de autenticarse contra el API Server.
- Usuario y password
- Usuario y token
- Certificados
- Provedores externos - LDAP
- Service Accounts
-
Autorización: los mecanismos de autorización definen que puede hacer una vez se han autenticado en el sistema:
- RBAC Authorization
- ABAC Authorization
- Node Authorization
- Webhook Mode
Todas las comunicaciones entre los distintos componentes del clusters, se garantiza mediante el cifrado TLS.
Las Pods del cluster, tienen conectividad entre si. Pero es posible restringir el acceso entre ellos utilizando Network Policies.
Kubernetes no administra usuario de forma nativa, se basa en fuentes externas como un archivo con detalles de usuarios, certificados o cuentas de identidad de terceros.
Por lo tanto, no puede crear usuarios.
Puede crear una lista de usuarios y sus contraseñas en formato CSV.
# User details
## password,user,ID_user,group(optional)
password123,user1,u0001
password123,user2,u0002
password123,user3,u0003
password123,user4,u0004
Dentro el servicio kube-apiserver, puede indicar lo siguiente:
# kube-apiserver.service
ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${INTERNAL_IP} \\
--allow-privileged=true \\
--apiserver-count=3 \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--enable-swagger-ui=true \\
--etcd-servers=https://127.0.0.1:2379 \\
--event-ttl=1h \\
--runtime-config=api/all \\
--service-cluster-ip-range=10.32.0.0/24 \\
--service-node-port-range=30000-32767 \\
--v=2 \\
--basic-auth-file=user-details.csv
curl -v -k https://master-node-ip:6443/api/v1/pods -u "user1:password123"
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/pods",
"resourceVersion": "3594"
},
"items": [
{
"metadata": {
"name": "nginx-64f497f8fd-krkg6",
"generateName": "nginx-64f497f8fd-",
"namespace": "default",
"selfLink": "/api/v1/namespaces/default/pods/nginx-64f497f8fd-krkg6",
"uid": "77dd7dfb-2914-11e9-b468-0242ac11006b",
"resourceVersion": "3569",
"creationTimestamp": "2019-02-05T07:05:49Z",
"labels": {
"pod-template-hash": "2090539498",
"run": "nginx"
...
De manera similar, podemos tener un fichero con tokens estáticos.
# User details
## token,user,ID_user,group(optional)
ad34gdfg66hn&7MK,user1,u0001
ad34gdfg66hnffad,user2,u0002
ad34gdfg66hasdfn,user3,u0003
ad34gdfg66hn4rff,user4,u0004
La linea a indicar en el servicio kube-apiserver.service sería:
--token-auth-file=user-details.csv
Para realizar la petición al API server:
curl -v -k https://master-node-ip:6443/api/v1/pods --header "Authorization: Bearer ad34gdfg66hasdfn"
Estos dos tipos de autenticación no son seguras, por lo tanto no es recomendable usarlas.
Se utiliza un certificado para garantizar la confianza entre dos partes durante una transacción.
Tenemos una web a la que accedemos con usuario y contraseña, pero esta, no utiliza HTTPS. Cuando enviamos nuestras credenciales a través de la red, van en texto plano, por lo que si alguien escucha el tráfico de la red, puede obtener el usuario y la contraseña.
Los datos se cifran utilizando una clave, que basicamente es un conjunto de números y letras aleatorias. Si alguien que está escuchando el tráfico de red obtiene estos datos no sabrá descifrarlos, pero esto mismo le ocurre al servidor destino, por ello tenemos que enviar también una copia de la clave, y del mismo modo, si el atacante la obtiene, podrá descifrar nuestros datos.
Esto se conoce como encriptación simétrica. Es una forma segura de cifrado, pero dado que se usa la misma clave para cifrar y descifrar datos y que la clave debe intercambiarse entre el remitente y el receptor, existe un riesgo.
El cifrado asimétrico, utiliza un par de claves, Private Key y Public Key. Si algo es cifrado con la Public Key, solo podrá ser descifrado con la Private Key, por ello nunca debe compartirse.
Asegurar acceso ssh a un servidor Lo primero, será generar el par de claves:
ssh-keygen
id_rsa # private key
id_rsa.pub # public key
Asegure su servidor para que el acceso solo pueda ser por clave.
Añada su clave pública en el servidor, normalmente se añade al fichero .ssh/authorized_keys
Si más usuarios necesitan acceder al servidor, podrán generar sus propias claves y añadirlas al fichero del servidor.
Aqui utilizamos el comando openssl para generar un par de claves pública y privada.
# Genera clave privada
openssl genrsa -out my-bank.key 1024
# Genera clave pública
openssl rsa -in my-bank.key -pubout > mybank.pem
Cuando el usuario accede por primera vez al servidor web, mediante https, obtiene la clave pública del servidor, como el pirata informático está escuchando la red, el también recibe una copia de la clave pública.
El navegador del usuario encripta la clave simétrica, utilizando la clave pública proporcionada por el servidor. La clave simétrica ahora es segura, el usuario la envía al servidor.
Sin embargo no tiene la clave privada del servidor para descifrar el mensaje.
Puede ser muy confuso saber que ficheros son claves públicas o privadas.
Certificate (Public Key) | Private Key |
---|---|
*.crt *.pem | *.key *-key.pem |
server.crt | server.key |
server.pem | server-key.pem |
client.crt | client.key |
client.pem | cliente-key.pem |
Un cluster se compone de nodos Master y nodos Worker, y la comunicación entre ellos debe ser segura y debe estar encriptada.
Dos requisitos principales son tener todos los ervicios dentro del cluster para usar Server Certificates for Servers y que todos los clientes usen Client Certificates for Clients.
Crearemos los certificados para el cluster.
- Certificate Authority (CA):
# Clave privada
openssl genrsa -out ca.key 2048
# Solicitud de firma con la clave privada
openssl req -new ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr
# Firmamos el certificado (autofirmado)
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
- Admin User
# Clave privada
openssl genrsa -out admin.key 2048
# Solicitud de firma con la clave privada
openssl req -new -key admin.key -subj "CN=kube-admin" -out admin.csr
# Firma de certificado con la clave privada de la CA
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt
-
Certificados de clientes, ejecutamos lo mismo para los distintos componentes:
- SYSTEM:KUBE-SCHEDULER
- SYSTEM:KUBE-CONTROLLER-MANAGER
- SYSTEM:KUBE-PROXY
-
Server Certificates for Servers:
- ETCD:
- ETCD-SERVER
- ETCD-PEER (conexión entre las réplicas)
- API-SEVER
- KUBELET
- ETCD:
# Ver los detalles del certificado
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3147495682089747350 (0x2bae26a58f090396)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=kubernetes # CA que emitió el certificado
Validity
Not Before: Feb 11 05:39:19 2019 GMT
Not After : Feb 11 05:39:20 2020 GMT # Validez
Subject: CN=kube-apiserver # Nombre del certificado
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d9:69:38:80:68:3b:b7:2e:9e:25:00:e8:fd:01:
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:master, DNS:kubernetes, DNS:kubernetes.default, #
DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP ## Nombre alternativos
Address:10.96.0.1, IP Address:172.17.0.27 #
Necesitamos darle acceso al cluster a un nuevo compañero. Crea su clave privada, y genera una solicitud de firma para la CA lo firme, nos lo envía porque somos los únicos administradores, lo firmamos y se lo devolvemos, y entonces puede acceder al cluster.
Kubernetes tiene una API de Certificados que puede realizar estas tareas. Puede enviar un CertificateSigningRequest, a través de la API de kubernetes. Las peticiones pueden ser revisadas por los administradores y ser aprobadas con kubectl.
# El usuario crea una clave
openssl genrsa -out jane.key 2048
# Solicitud de firma
openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr
# cifra el contenido de la solicitud en base64
cat jane.csr | base64
Crea el fichero para kubernetes
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: jane
spec:
groups:
- system:authenticated
usages:
- digital signature
- key encipherment
- server auth
request:
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0
tLS0KTUlJQ1dEQ0NBVUFDQVFBd0V6RVJNQThHQTFVRU
F3d0libVYzTFhWelpYSXdnZ0VpTUEwR0NTcUdTSWIzR
FFFQgpBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRE8wV0pX
K0RYc0FKU0lyanBObzV2UklCcGxuemcrNnhjOStVVnd
rS2kwCkxmQzI3dCsxZUVuT041TXVxOTlOZXZtTUVPbn
Puedes visualizar y aprobar las solicitudes:
kubectl get csr
NAME AGE REQUESTOR CONDITION
jane 10m [email protected] Pending
kubectl certificate approve jane
jane approved!
El comando genera un certificado para el usuario, este puede ser extraido y compartido con el ususario.
We have seen that the k8s API Server can be accessed:
curl https://my-kube:6443/api/v1/pods \
--key admin.key
--cert admin.crt
--cacert ca.crt
With kubectl we can do the following:
kubectl get pods
--server my-kube:6443
--client-key admin.key
--client-certificate admin.crt
--certificate-authority ca.crt
This is a tedious task, so we can use the kubeconfig file, which by default is located in the $HOME/.kube/config
kubectl get pods --kubeconfig config
apiVersion: v1
kind: Config
# Current context
current-context: my-kube-dev@my-kube-admin
# Cluster definition
clusters:
# Cluster DEV definition
- name: my-kube-dev
cluster:
# Relative path
certificate-authority: ca.crt
server: https://my-kuybe-dev:6443
# Cluster PRE definition
- name: my-kube-pre
cluster:
# Absolute path
certificate-authority: /etc/kubernetes/pki/ca.crt
server: https://my-kuybe-pre:6443
# Cluster PRO definition
- name: my-kube-pro
cluster:
# Base64 certificate
certificate-authority-data: Y2VydGlmaWNhdGVhc2RmYXNkZmFkc2Zhc2RmYWRzZmFzZGZhc2RmYXNkZmFzZGZhc2RmYWRzZmFzZGZhc2RmYXNkZmFzZGZ3cmV3MzRydDI=
server: https://my-kuybe-pro:6443
# Link between cluster and user
contexts:
- name: my-kube-dev@my-kube-admin-dev
context:
cluster: my-kube-dev
user: my-kube-admin-dev
# Set default namespace from context
namespace: finance
- name: my-kube-pre@my-kube-admin-pre
context:
cluster: my-kube-pre
user: my-kube-admin-pre
namespace: marketing
- name: my-kube-pro@my-kube-admin-pro
context:
cluster: my-kube-pro
user: my-kube-admin-pro
namespace: devops
# Users definition
users:
# User DEV definition
- name: my-kube-admin-dev
user:
client-certificate: admin.crt
client-key: admin.key
# User DEV definition
- name: my-kube-admin-dev
user:
client-certificate: admin.crt
client-key: admin.key
# User PRO definition
- name: my-kube-admin-pro
user:
client-certificate: admin.crt
client-key: admin.key
There are many commands to interact with the kubeconfig file:
- Help:
kubectl config -h
- Viewing the file:
kubectl config view
- Change of context:
kubectl config use-context my-kube-pro@my-kube-admin-pro
To use RBAC, the first thing we need is a Role:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
# Role name
name: developer
# Namespace to role apply
namespace: develop
# Rules list
rules:
- apiGroups: [""]
# Resources name. Example: pods, ConfigMap, Deployment, etc.
resources: ["pods"]
# Actions from resoureces. Example: list, watch, delete, get, etc.
verbs: ["list", "get", "create", "update", "delete"]
- apiGroups: [""]
resources: ["ConfigMap"]
verbs: ["create"]
# Allows you to limit the name of the resources on which you can perform actions.
resourceNames: ["develop", "test"]
Next step, link role with user to the RoleBinding object:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
# RoleBinding name
name: devuser-developer-binding
# Namespace to RoleBinding apply
namespace: develop
subjects:
# User definition
- kind: User
name: dev-user
apiGroup: rbac.authorization.k8s.io
# Role definition
roleRef:
kind: Role
# Role name definition
name: developer
apiGroup: rbac.authorization.k8s.io
We can execute the following actions:
- Get Roles
kubectl get roles
- Get RoleBindings
kubectl get rolebindings
- Obtain role details
kubectl describe role developer
- Obtain rolebindings details
kubectl describe rolebinding devuser-developer-binding
- Check access from my user
kubectl auth can-i create deployments
- Check access as user in namespace
kubectl auth can-i delete nodes --as dev-user --namespace test
- Know what authorization the cluster uses:
kubectl -n kube-system get pod kube-api -oyaml | grep -i auth
We can identify two types of levels for the 'api-groups'.
- At the level of namespace. The 'Role' and 'RoleBinding' resources are used, and applied to resources at the level of namespace (pods, replicasets, jobs, deployments, PVC, etc).
- At cluster level. The 'ClusterRole' and 'ClusterRoleBinding' resources are used and applied to resources at cluster level (nodes, PV, namespaces, etc).
A list of the resources can be obtained:
- At namespace level.
kubectl api-resources --namespaced=true
- At cluster level.
kubectl api-resources --namespaced=false
Example of the creation of ClusterRole
y ClusterRoleBinding
:
- ClusterRole.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# Role name
name: role-admin
# Namespace to role apply
namespace: admin
# Rules list
rules:
- apiGroups: [""]
# Resources name. Example: nodes, namespaces, etc.
resources: ["nodes"]
# Actions from resoureces. Example: list, watch, delete, get, etc.
verbs: ["list", "get", "create", "delete"]
- ClusterRoleBinding.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
# RoleBinding name
name: cluster-admin-role-binding
# Namespace to RoleBinding apply
namespace: admin
subjects:
# User definition
- kind: User
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
# Role definition
roleRef:
kind: ClusterRole
# Role name definition
name: role-admin
apiGroup: rbac.authorization.k8s.io
Sometimes we may need to access images from a private registry. By default kubernetes points to Docker Hub's public registry.
- Docker Hub nginx pull:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
- Another Registry:
- Secret
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
data:
.dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson
- Pod
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullSecrets: # Property to indicate how you will download images from a registry
- name: myregistrykey
You can choose to configure the safety settings at pod or container level.
- Pod. Will be transferred to all containers.
apiVersion: v1
kind: Pod
metadata:
name: security-context-pod
spec:
securityContext:
runAsUser: 1000 # Run all container as UID 1000
runAsGroup: 3000
containers:
- name: testing
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
allowPrivilegeEscalation: false
- Container
apiVersion: v1
kind: Pod
metadata:
name: security-context-container
spec:
containers:
- name: testing
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
runAsUser: 1000 # Run this container as UID 1000
allowPrivilegeEscalation: false # Dont allow privilege escalation
capabilities: # Allos capabilities
add: ["MAC_ADMIN"]
By default in kubernetes all traffic is allowed between pods. You can indicate by means of Network policies
, from where a pod is reachable.
It is assigned the same as other kuberrnetes objects, through Selectors
and Labels
.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: api-pod
ports:
- protocol: TCP
port: 3306
The containers are intended to last for a short period of time. They are requested when they are needed to die, and their data are destroyed with the container. To ensure that the data persists, we attach volumes when they are created, preventing their loss even if the container is disposed of. Kubernetes has the same sharing.
This configuration is not recommended when there is more than one node.
- POD with host path
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: alpine
name: alpine
command: ["/bin/sh","-c"]
args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
volumeMounts: # Allows a volume to be mounted
- mountPath: /opt # Indicates the mounting point in POD
name: test-volume # Name of the volume
volumes: # Declaration of volumes
- name: test-volume # Name of the volume
hostPath: # It is created on a path of the host
path: /data # Directory location on host
type: Directory
- POD with AWS EBS:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: alpine
name: alpine
command: ["/bin/sh","-c"]
args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
volumeMounts: # Allows a volume to be mounted
- mountPath: /opt # Indicates the mounting point in POD
name: test-volume # Name of the volume
volumes: # Declaration of volumes
- name: test-volume # Name of the volume
awsElasticBlockStore: # Volume type: hostPath, awsElasticBlockStore, gcePersistentiDisk, etc
volumeID: <volume-id>
fsType: ext4
When the kernel environment is very large, it is not recommended to apply the previous configuration, it would imply creating a volume for each POD. A POD is a set of volumes for the entire cluster. Users can select the storage of this group through volume claims.
- HostPath PV example:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol-host-path
spec:
accessModes: # Access Mode: `ReadOnlyMany`, `ReadWriteOnce` and `ReadWriteMany`.
- ReadWriteOnce
capacity:
storage: 1Gi # Storage capacity
hostPath: # Host Path Volume type
path: /opt/data
- AWS EBS PV example:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol1-aws
spec:
accessModes: # Access Mode: `ReadOnlyMany`, `ReadWriteOnce` and `ReadWriteMany`.
- ReadWriteOnce
capacity:
storage: 1Gi # Storage capacity
awsElasticBlockStore: # AWS EBS Volume type
volumeID: <volumen-id>
fsType: ext4
PVCs are storage claims to PVs. They are requested by users.
Kubernetes will try to assign the correct PV according to the request (capacity, access mode, volume mode, storage class, etc).
If you want to use a specific PV for a particular type of feature You can configure selectors
and labels
.
- PV labels/selector
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol1-aws
labels:
name: my-pv
spec:
accessModes: # Access Mode: `ReadOnlyMany`, `ReadWriteOnce` and `ReadWriteMany`.
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain # Options:
# Retain: It will be removed by the administrator
# Delete: will be automatically removed
# Recycle: The content will be removed before it can be reused
capacity:
storage: 1Gi # Storage capacity
awsElasticBlockStore: # AWS EBS Volume type
volumeID: <volumen-id>
fsType: ext4
- PVC labels/selector
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
selector:
matchLabels:
name: my-pv
If there are no better options, a PVC can be linked to a PV with a larger size. There is a one-to-one relationship, so no other claim can use the remaining capacity. It will remain pending until there is a PV to be linked to.
- PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
Once you create a PVC use it in a POD definition file by specifying the PVC Claim name under persistentVolumeClaim section in the volumes section like this. The same is true for ReplicaSets or Deployments. Add this to the pod template section of a Deployment on ReplicaSet.
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
Storage Class, allows the creation of a proxy between kubernetes and the cloud. Let's take Google Cloud as an example.
- Static Each time an application requires storage, it must first be manually provisioned.
gcloud beta compute disk create \
--size 1GB
--region us-east1
pd-disk
- GCE PersistentDisk PV example:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol1-gce
spec:
accessModes: # Access Mode: `ReadOnlyMany`, `ReadWriteOnce` and `ReadWriteMany`.
- ReadWriteOnce
capacity:
storage: 1Gi # Storage capacity
gcePersistentiDisk: # GCE Volume type
pdName: pd-disk # PD Name
fsType: ext4
- Dynamic For dynamic provisioning we created a Storage Class for GCE
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: google-storage
provisioner: kubernetes.io/gce-pd # Provisioner type. Example: kubernetes.io/aws-ebs, kubernetes.io/no-provisioner (local), etc
- PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
storageClassName: google-storage
resources:
requests:
storage: 500Mi
- POD
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
Dos máquinas (A y B) pueden ser conectadas mediante un switch, a través de sus interfaces eth0
- Para ver las interfaces del host ejecutamos:
ip link
1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
link/ether 9c:b6:d0:9b:5f:f3 brd ff:ff:ff:ff:ff:ff
- La red que tenemos es la 192.168.1.0, y las máquinas tienen asignadas las IPs 192.168.1.10 (A) y 192.168.1.11 (B). Linkamos IP con interface eth0
# Node A
ip addr add 192.168.1.10/24 dev eth0
# Node B
ip addr add 192.168.1.11/24 dev eth0
Ahora podremos entrar paquetes entre las máquinas A y B.
Queremos conectar dos redes con dos máquinas cada red, 192.168.1.0 y 192.168.2.0. Un router permite comunicar estas dos redes. El enrutador posee una IP en cada red 192.168.1.1 y 192.168.2.1
Para que la red A pueda llegar a la red B, necesitamos añadir rutas de destino.
route
Kernel IP routing table
Destination Gateway GenmaskFlags Metric Ref Use Iface
# Add new route
ip route add 192.168.2.0/24 via 192.168.1.1
# Show routes
route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.2.0 192.168.1.1 255.255.255.0 UG 0 0 0 eth0
La siguiente cuestión, es comunicar el router con Internet, por ejemplo Google 172.217.194.0.
ip route add 172.217.194.0/24 via 192.168.2.1
Se le puede indicar que para las direcciones que no conozca, utilice una ruta por defecto
ip route add default via 192.168.2.1
Para las IPs no reconocidas lo mejor es añadir como destino 0.0.0.0/0
Dadas dos máquinas A y B en la misma red, comprobamos que obtienen conexión por ping a través de sus IPs. Para no recordar la IP podemos identificar el nodo mediante un nombre, por ejemplo db.
- No conoce ningún host llamado db
ping db
ping: db: Name or service not known
- Para decirle al sistema que nombre db corresponde con una IP, podemos añadir una entrada al fichero /etc/hosts
cat /etc/hosts
192.168.1.11 db
- Ahora nuestro sistema si reconoce el nombre del host
ping db
PING db(192.168.1.11) 56(84) bytes of data.
64 bytes from db(192.168.1.11): icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from db(192.168.1.11): icmp_seq=2 ttl=64 time=0.079 m
- Nuestros se fia ciegamente de lo que indiquemos en nuestro fichero hosts, pero puede ser cierto o no. Podemos engañar al sistema indicando que la máquina A es Google.
cat /etc/hosts
192.168.1.11 db
192.168.1.11 www.google.com
- Ahora nuestro sistema si reconoce el nombre del host
ping www.google.com
PING www.google.com(192.168.1.11) 56(84) bytes of data.
64 bytes from www.google.com(192.168.1.11): icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from www.google.com(192.168.1.11): icmp_seq=2 ttl=64 time=0.079 m
Cuando un entorno crece y existen muchas máquinas, no es una buena forma, de mantener la conexión entre ellas. Podemos utilizar una máquina demoninada DNS para que obtenga un listado de máquinas e IPs y desde nuestros hosts, podamos consultarle.
- En cada uno de los servidores se ubica un fichero que señala a un servidor de DNS.
cat /etc/resolv.conf
nameserver 192.168.1.100
Ya no es necesario que tengan los servidores entradas en el fichero /etc/hosts, pero esto no significa que no pueda tenerlas. Mediante un archivo de configuración podemos indicar si queremos que resuelva primera con el fichero /etc/hosts o con el fichero /etc/resolv.conf. Para ello la configuración se aplica en el fichero /etc/nsswitch.conf
Nuestros sistema desde este momento conoce las declaracionse en el fichero /etc/hosts y las declaraciones en el servidor de DNS. Pero si tratamos de llegar a una dirección que no conoce fallará. Podemos agregar una nueva entrada a nuestro fichero /etc/resolv.conf, 8.8.8.8 DNS público alojado por Google que conoce todos los sitios webs de internet.
cat /etc/resolv.conf
nameserver 192.168.1.100
nameserver 8.8.8.8
Una solución sería añadir una linea a nuestro servidor DNS para que todo lo desconocido lo reenvie a 8.8.8.8
Se llama nombre de dominio a la traducción de una IP. La razón de que un dominio esté separado por puntos es para agrupar.
La última parte de un nombre de dominio .org, .com, .net, etc son los dominios de nivel superior, y representan la intención del sitio web.
El resto del nombre se entiende por subdominio. Por ejemplo: www.google.com, maps.google.com, drive.google.com.
Cuando queremos acceder internamente a los servicios de una empresa, y queremos solamente utilizar el nombre de los servicios, podemos añadir a nuestro fichero:
cat /etc/resolv.conf
nameserver 192.168.1.100
search mycompany.com prod.mycompany.com
¿Como se almacenan los registros DNS?
Types | Description | ||
---|---|---|---|
A | web-server | 192.168.1.1 | Vinculan un nombre con una IP |
AAAA | web-server | 2001:asdf:dfgh:9616:316651:fgh45 | Vinculan un nombre con una IPv6 |
CNAME | foo.web-server | eat.web-server, hungry.web-server | Contiene un alias o nombre alternativo para otro registro A o AAAA |
Otros registros:
Types | Description | |
---|---|---|
SOA | ns1.dnsimple.com admin.dnsimple.com 2013022001 86400 7200 604800 300 | Start of Authority Información sobre la zona que se organiza, importantes para la trasnferencia de zonas |
MX | ASPMX.L.GOOGLE.COM | Mail Exchange, intercambio que se produce mediante un servidor SMTP, es posible configurar varios con distintas prioridades para compensar fallos |
PTR | 34.216.184.93.in-addr.arpa. IN PTR example.org. | Reverse lookup, el servidor DNS puede indnicar quenombre de host pertenece a una IP |
NS | ns1.subname.example.com | Servidor de nombres de una zona, determina donde recae la responsabilidad de una zona concreta |
TXT | v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.123 ip6:2620:0:860::/46 a -all | Contienen texto, como información para usuarios, también pueden añadirse detalles sobre la empresa |
SRV | _sip._tcp.example.com. | Informa sobre los servicios disponibles del dominio |
LOC | LOC record statdns.net. IN LOC 52 22 23.000 N 4 53 32.000 E -2.00m 0.00m 10000m 10m | Ubicación fisica del servidor |
- nslookup: consultar el nombre de host de un servidor DNS
nslookup www.google.es
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
Name: www.google.es
Address: 74.125.193.94
Name: www.google.es
Address: 2a00:1450:400b:c01::5e
- dig: comprueba la resolución de nombres.
dig www.google.es
; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> www.google.es
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56212
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.google.es. IN A
;; ANSWER SECTION:
www.google.es. 106 IN A 74.125.193.94
;; Query time: 51 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Tue Nov 03 15:28:07 CET 2020
;; MSG SIZE rcvd: 58
In the previous lecture we saw why you need a DNS server and how it can help manage name resolution in large environments with many hostnames and Ips and how you can configure your hosts to point to a DNS server. In this article we will see how to configure a host as a DNS server.
We are given a server dedicated as the DNS server, and a set of Ips to configure as entries in the server. There are many DNS server solutions out there, in this lecture we will focus on a particular one – CoreDNS.
So how do you get core dns? CoreDNS binaries can be downloaded from their Github releases page or as a docker image. Let’s go the traditional route. Download the binary using curl or wget. And extract it. You get the coredns executable.
Run the executable to start a DNS server. It by default listens on port 53, which is the default port for a DNS server.
Now we haven’t specified the IP to hostname mappings. For that you need to provide some configurations. There are multiple ways to do that. We will look at one. First we put all of the entries into the DNS servers /etc/hosts file.
And then we configure CoreDNS to use that file. CoreDNS loads it’s configuration from a file named Corefile. Here is a simple configuration that instructs CoreDNS to fetch the IP to hostname mappings from the file /etc/hosts. When the DNS server is run, it now picks the Ips and names from the /etc/hosts file on the server.
CoreDNS also supports other ways of configuring DNS entries through plugins. We will look at the plugin that it uses for Kubernetes in a later section.
Read more about CoreDNS here:
https://github.com/kubernetes/dns/blob/master/docs/specification.md
https://coredns.io/plugins/kubernetes/
Los contenedores se separan del host mediante namespaces, lo cual permite que los procesos que corren dentro, no puedan acceder a los procesos de fuera, a menos que se indique lo contrario. Sin embargo el host, si puede ver los procesos de todos los contendores que corren en el.
ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 226388 9948 ? Ss 08:00 0:46 /sbin/init splash
root 2 0.0 0.0 0 0 ? S 08:00 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? I< 08:00 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? I< 08:00 0:00 [mm_percpu_wq]
root 7 0.0 0.0 0 0 ? S 08:00 0:00 [ksoftirqd/0]
root 8 0.1 0.0 0 0 ? I 08:00 0:44 [rcu_sched]
root 9 0.0 0.0 0 0 ? I 08:00 0:00 [rcu_bh]
root 10 0.0 0.0 0 0 ? S 08:00 0:00 [migration/0]
Kubernetes espera que:
- Cada Pod obtenga su propia IP
- Cada Pod debería poder alcanzar cualquier otro Pod dentro del mismo nodo utilizando esa dirección IP, o de otro nodo.
- Cada Pod debería poder alcanzar cualquier otro Pod dentro de otro nodo sin usar NAT.
Existen muchas soluciones que permite esto:
- weave
- flannel
- cilium
- Network Namespaces
- CNI
Kubelet es el encargado de crear contenedores en los nodos. Cuando un contenedor se crea se invoca al CNI que actua como intermediario como argumentos, indicando:
--cni-conf-dir=/etc/cni/net.d
--cni-bin-dir=/etc/cni/bin
Los estandares de CNI indican como debe añadir y eliminar un contendor a nivel de red. Una visión del script podría ser la siguiente:
ADD)
# Create veth pair
# Attach veth pair
# Assing IP Address
# Bring Up Interface
ip -n <namespace> link set ...
DEL)
# Delete veth pair
ip link del ...
La invocación del scrit podría ser algo así:
./net-script.sh add <container> <namespace>
CNI define las responsabilidades del tiempo de ejcución del contenedor.
- Container Runtime must create network namespace
- Identify network the container must attach to
- Container Runtime to invoke Network Plugin (bridge) whtn container is ADDed
- Container Runtime to invoke Network Plugin (bridge) whtn container is DELeted.
- JSON format of the Network Configuration
El complemento CNI se configura en el servicio kubelet en cada nodo del cluster. Si observamos el fichero de servicio de kubelet, podremos ver:
kubelet.service
...
--network-plugin=cni \\
--cni-bin-dir=/opt/cni/bin \\
--cni-conf-dir=/etc/cni/net.d \\
...
También es posible ver la configuración del proceso mediante:
ps -aux | grep kubelet
En el directorio cni/bin se ubican todos los complemtos CNI compatibles como ejecutables.
ls /opt/cni/bin
bridge dhcp flannel host-device host-local ipvlan loopback mcvlan ptp sample tunning vlan
En el directorio /etc/cni/net.d, se ubica la configuración a aplicar, si hubiera varios ficheros, se aplicaría en orden alfabetico. Contiene configuraciones relacionadas con Bridge, enrutamiento y enmascaramiento NAT. También definie si la interfaz de red debe tener asignada una dirección IP, ...
ls /etc/cni/net.d
10-bridge.conf
# Show file
cat 10-bridge.conf
{
"cniVersion": "0.2.0",
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.22.0.0/16",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
}
La solución que inicialmente se exponía, utilizaba un enrutador para enviar el tráfico, esto funciona cuando la red es muy pequeña y simple. Para entornos granes con cientos de nodos, y cientos de PODs en cada nodo no es práctico, ya que es posible que la tabla de enrutamiento no adminta tantas entradas.
El complemento CNI Weave se implementa en cada nodo. Y se comunican entre si para intercambiar información sobre los nodos, la red y los PODs. Cada agente conoce los PODs y sus IPs de los otros nodos.
Weave crea su propio puente sobre los nodos y lo nombra como Weave. Luego, asigna la dirección IP a cada red. Un pod puede estar en varias redes, como por ejemplo en la red de Weave o en la red de docker, la ruta que toma un paquete para llegar al destino, depende de la ruta configurada en el contendor.
Weave se asegura de que todos los PODs obtengan la ruta correcta configurada para llegar al agente, cuando se envia un paquete de un pod a otro nodo, Weave intercepta el paquete e identifica que está en una red separada. Luego, encapsula el paquete en uno nuevo con nuevo origen y destino, y lo envia a traves de la red.
Se puede implementar como servicios o daemon en cada nodo del cluster de forma manual. O si kubernetes ya está configurado se puede implementar como daemonset.
kubectlapply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created
daemonset.extensions/weave-net created
Si implementó su cluster con kubeadm, puede verlo en cada nodo:
kubectlget pods –n kube-system
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
coredns-78fcdf6894-99khw 1/1 Running 0 19m 10.44.0.2 master <none>
coredns-78fcdf6894-p7dpj 1/1 Running 0 19m 10.44.0.1 master <none>
etcd-master 1/1 Running 0 18m 172.17.0.11 master <none>
kube-apiserver-master 1/1 Running 0 18m 172.17.0.11 master <none>
kube-scheduler-master 1/1 Running 0 17m 172.17.0.11 master <none>
weave-net-5gcmb 2/2 Running 1 19m 172.17.0.30 node02 <none>
weave-net-fr9n9 2/2 Running 1 19m 172.17.0.11 master <none>
weave-net-mc6s2 2/2 Running 1 19m 172.17.0.23 node01 <none>
weave-net-tbzvz 2/2 Running 1 19m 172.17.0.52 node03 <none>
kubectllogs weave-net-5gcmb weave –n kube-system
INFO: 2019/03/03 03:41:08.643858 Command line options: map[status-addr:0.0.0.0:6782 http-addr:127.0.0.1:6784 ipalloc-range:10.32.0.0/12 name:9e:96:c8:09:bf:c4 nickname:node02 conn-limit:30 datapath:datapathdb-prefix:/weavedb/weave-net host-root:/host port:6783 docker-api: expect-npc:trueipalloc-init:consensus=4 no-dns:true]
INFO: 2019/03/03 03:41:08.643980 weave 2.2.1
INFO: 2019/03/03 03:41:08.751508 Bridge type is bridged_fastdp
INFO: 2019/03/03 03:41:08.751526 Communication between peers is unencrypted.
INFO: 2019/03/03 03:41:08.753583 Our name is 9e:96:c8:09:bf:c4(node02)
INFO: 2019/03/03 03:41:08.753615 Launch detected -using supplied peer list: [172.17.0.11 172.17.0.23 172.17.0.30 172.17.0.52]
INFO: 2019/03/03 03:41:08.753632 Checking for pre-existing addresses on weave bridge
INFO: 2019/03/03 03:41:08.756183 [allocator 9e:96:c8:09:bf:c4] No valid persisted data
INFO: 2019/03/03 03:41:08.761033 [allocator 9e:96:c8:09:bf:c4] Initialisingvia deferred consensus
INFO: 2019/03/03 03:41:08.761091 Sniffing traffic on datapath(via ODP)
INFO: 2019/03/03 03:41:08.761659 ->[172.17.0.23:6783] attempting connection
INFO: 2019/03/03 03:41:08.817477 overlay_switch->[8a:31:f6:b1:38:3f(node03)] using fastdp
INFO: 2019/03/03 03:41:08.819493 sleeve ->[172.17.0.52:6783|8a:31:f6:b1:38:3f(node03)]: Effective MTU verified at 1438
INFO: 2019/03/03 03:41:09.107287 Weave version 2.5.1 is available; please update at https://github.com/weaveworks/weave/releases/download/v2.5.1/weave
INFO: 2019/03/03 03:41:09.284907 Discovered remote MAC 8a:dd:b5:14:8f:a3 at 8a:dd:b5:14:8f:a3(node01)
INFO: 2019/03/03 03:41:09.331952 Discovered remote MAC 8a:31:f6:b1:38:3f at 8a:31:f6:b1:38:3f(node03)
INFO: 2019/03/03 03:41:09.355976 Discovered remote MAC 8a:a5:9c:d2:86:1f at 8a:31:f6:b1:38:3f(node0
Esta sección cubre la herramienta que elimina redes y los nodos a asignados a una subred.
CNI dice que el responsabilidad del proveedor se soluciones de red ocuparse de asignar IPs a los contendores.
En el fichero de configuración de CNI, tiene una sección llamada "ipam" donde se indica el tipo de complemento y la ruta que utilizará.
cat /etc/cni/net.d/10-bridge.conf
{
"cniVersion": "0.2.0",
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.22.0.0/16",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
}
Recapitulando anteriores temas, rara vez configuraremos dos pods para comunicarse directamente entre si. Si necesitamos que un pod acceda a servicios de otro pod, siempre utilizará un objecto SERVICE. El objecto Service se crea "delante" de un POD, y obtiene una dirección IP y un nombre, otros PODs, accederán mediante la IP o el nombre del Service.
Cuando se crea un service es accesible desde todo el cluster, independientemente del nodo en el que se encuentren.
Cuando necesitemos que un servicio se accesible desde fuera del cluster, utilizamos Service de tipo NodePort. También se le asigna una dirección IP y un nombre y es accesible desde el cluster, pero además expone la aplicación en un puerto en todos los nodos.
El servicio kubelet es el encargado de crear los PODs, y observa los cambios del cluster a través del API Server. Cada vez que se crea un POD se invoca al complemento CNI para configurar la red para ese POD.
Del mismo modo, cada nodo ejecuta otro componente kube-proxy. Kube-proxy observa los cambios en el cluster a través del API Server, y cada vez que se crea un nuevo servicio, kube-proxy entra en acción.
Cuando se crea un service, se le asigna una IP de un rango predefinido. Los componentes de kube-proxy que se ejecutan en cada nodo, obtienen esa IP y crean las reglas de reenvio a cada nodo del cluster, indicando que cada tráfico que llegue a la IP del Service debe ir a la IP del POD.
Kube-proxy, adminte diferentes formas de crear reglas:
- Como el userspace donde kube-proxy escucha en un puerto para cada servicio y conecta las conexiones de proxy a los pods.
- Reglas ipvs, tablas IPs.
- Iptables
El valor predeterminado es iptables. Podría verse el valor establecido visualizando los logs de los PODs de kube-proxy. Puedes ver las reglas ejecutando:
iptables -L -t net | grep <service-name>
KUBE-SVC-XA5OGUC7YRHOS3PU tcp -- anywhere 10.103.132.104 /* default/db-service: cluster IP */ tcp dpt:3306
DNAT tcp -- anywhere anywhere /* default/db-service: */ tcp to:10.244.1.2:3306
KUBE-SEP-JBWCWHHQM57V2WN7 tcp -- anywhere anywhere /* default/db-service: */
También es posible ver, como kube-proxy crea estas reglas de entrada:
cat /var/log/kube-proxy.log
I0307 04:29:29.883941 1 server_others.go:140] Using iptables Proxier.
I0307 04:29:29.912037 1 server_others.go:174] Tearing down inactive rules.
I0307 04:29:30.027360 1 server.go:448] Version: v1.11.8
I0307 04:29:30.049773 1 conntrack.go:98] Set sysctl 'net/netfilter/nf_conntrack_max' to 131072
I0307 04:29:30.049945 1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0307 04:29:30.050701 1 conntrack.go:83] Setting conntrack hashsize to 32768
I0307 04:29:30.050701 1 proxier.go:294] Adding new service “default/db-service:3306" at 10.103.132.104:3306/TCP
Kubernetes integra un servicio de DNS predeterminado. Cada vez que se crea un objecto Service, se crea un registro con nombre y IP del servicio. Por ello cualquier pod, puede llegar al servicio utilizando su nombre.
Para cada namespace, el servicio de DNS crea un subdominio. Todos los servicio se agrupan en otro subdominio SVC.
curl http://web-service.apps
Welcome to NGINX!
curl http://web-service.apps.svc
Welcome to NGINX!
curl http://web-service.apps.svc.cluster.local
Welcome to NGINX!
Con los PODs, ocurre lo mismo, sin embargo kubernetes sustituye los puntos de la IP por guiones.
En las versionse previas a v1.12, kubernetes implementaba un servicio llamado kube-dns, desde esa versión hacia delante, el sevicio es llamado CoreDNS.
El servidor CoreDNS se implementan como POD en el namespace kube-system.
Utiliza un fichero de configuración /etc/coredns/Corefile
, obtenido a través de un ConfigMap, y su contenido es el siguiente:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
Cuando se crea un POD, se registra una nueva entrada, sustituyendo los puntos por guiones, para su resolución
10-244-1-5 10.224.1.5
10-244-2-5 10.224.2.5
CoreDNS también despliega un SERVICE, y permite que los PODs apunten a este SERVICE en su configuración, el encargado de crear esta configuración en los PODs es kubelet:
cat /etc/resolv.conf
nameserver 10.96.0.10
Si observamos el archivo de configuración de kubelet, podremos ver la IP del servidor DNS y el dominio:
cat /var/lib/kubelet/config
...
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
Desde un POD puedes obtener el FQDN de un SERVICE:
host web-service
web-service.default.svc.cluster.local has address 10.97.206.196
Porque en el fichero /etc/resolv.conf se indica el nombre del cluster:
cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster cluster.local
Pero esto solo ocurre con los SERVICE, para un pod, debe indicar el FQDN:
host 10-224-2-5
Host 10-224-2-5 not found: 3(NXDOMAIN)
# FQDN
host 10-224-2-5.default.pod.cluster.local
10-224-2-5.default.pod.cluster.local has address 10.224.2.5
Los Ingress ayudan a los usuarios a llegar a su aplicación, usando una única URL accesible externamente, que puede configurar para enrutar a diferente servicios dentro de su cluster. La ruta URL al mismo tiempo, implementa seguridad SSL, un Ingress es como un Loadl Balancer capa 7.
Aún utilizando Ingress necesita exponerlo para que sea accesible desde fuera del cluster.
Kubernetes despliega una solución compatible, como pueden ser Nginx, HAProxy o Traefik, luego configura un conjunto de reglas para configurar el Ingress. Estas soluciones se llaman Ingress Controller, y el conjunto de reglas Ingress Resources (Objeto que desplegamos con nuestras aplicaciones).
Por defecto, nuestro cluster no viene con ningún Ingress Controller.
Existen varias soluciones disponibles:
- GCP HTTP(S) Load Balancer (apoyado y mantenido por el proyecto Kubernetes)
- Nginx (apoyado y mantenido por el proyecto Kubernetes)
- Contour
- HAProxy
- Traefik
- Istio
Ejemplo de definición de Ingress Controller, para una versión de Nginx creada específicamnte como Ingress Controller para kubernetes.
- Necesitaremos un ConfigMap con la configuración el Ingres (puede estar vacío), en el que se configura el path para los logs, configuración SSL, etc;
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
data:
proxy-connect-timeout: "10"
proxy-read-timeout: "120"
proxy-send-timeout: "120"
- Objeto Deployment para el Ingress Controller:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
labels:
app.kubernetes.io/name: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
spec:
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
- Necesitaremos un SERVICE para exponer el Ingress Controller:
apiVersion: v1
kind: Service
metadata:
name: nginx-ingrress
spec:
type: NodePort
selector:
name: nginx-ingress
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
El Ingress Controller tiene inteligencia adicional para monitorear el cluster en busca de Ingress Resources y configurar el servidor Nginx. Para que el Ingress Controller pueda hacer todo esto, necesita una Service Account, con un conjunto de permisos.
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- update
- watch
- apiGroups:
- extensions
- "networking.k8s.io" # k8s 1.14+
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- extensions
- "networking.k8s.io" # k8s 1.14+
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- "networking.k8s.io" # k8s 1.14+
resources:
- ingressclasses
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nginx-controller-clusterrolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
Un Ingress Resource es un conjunto de reglas y configuraciones aplicadas en el Ingress Controller.
Puede configurar reglas simplemente para reenviar todo el tráfico entrante a una sola aplicación o enrutar el tráfico a difrentes apilicaciones basado en URLs.
Se crea mediante un archivo de definición de kubernetes:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-wear
spec:
backend:
serviceName: wear-service
servicePort: 80
- Ingress para diferentes paths
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-wear-watch
spec:
rules:
- http:
paths:
- path: /wear
backend:
serviceName: wear-service
servicePort: 80
- path: /watch
backend:
serviceName: watch-service
servicePort: 80
- Otro tipo de configuración, partiendo de dos dominios:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-wear-watch
spec:
rules:
- host: wear.my-online-store.com
http:
paths:
- backend:
serviceName: wear-service
servicePort: 80
- host: watch.my-online-store.com
http:
paths:
- backend:
serviceName: watch-service
servicePort: 80
Los diferentes Ingress Controller tienen difrentes opciones que pueden ser utilizadas para personalizar su comportamiento.
- Rewrite Target
Una web muestra su contenido en: http://<watch-service>:<port>/
Y la otra muestra su contenido en http://<wear-service>:<port>/
Para lograr lo anterior, necesitamos configurar el Ingress Controller para que podamos reenviar el tráfico al backendo correcto. Las aplicaciones no poseen ese path:
http://<ingress-service>:<ingress-port>/watch --> http://<watch-service>:<port>/
http://<ingress-service>:<ingress-port>/wear --> http://<wear-service>:<port>/
Without the rewrite-target option, this is what would happen:
Sin la opción rewrite-target, lo que pasaría sería lo siguiente:
http://<ingress-service>:<ingress-port>/watch --> http://<watch-service>:<port>/watch
http://<ingress-service>:<ingress-port>/wear --> http://<wear-service>:<port>/wear
Ejemplo de rewrite-target:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: critical-space
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /pay
backend:
serviceName: pay-service
servicePort: 8282
In another example given here, this could also be:
replace("/something(/|$)(.*)", "/$2")
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something(/|$)(.*)
Algunas preguntas que debe realizar para poder diseñar un cluster.
- ¿Propósito?
- Aprendizaje
- Desarrollo y testeo
- Producción
- Cloud o OnPrem?
- ¿Que cargas va a soportar el cluster?
- ¿Cuantas aplicaciones se alojarán?
- Tipo de aplicaciones
- Aplicaciones web
- Big Data / Analítica
- Requisitos
- CPU
- Memoria
- Tráfico
- Continuo
- Pesado
-
Soluciones para fines educativos:
- Minikube
- Mono-nodo con kubeadm/GCP/AWS
-
Soluciones para fines de desarrollo y testeo:
- Multi-nodo con un Master y multiples workers
- Setup usando kubeadm tool o provisión mediante GKE(Google Cloud), EKS(AWS) o AKS(Azure)
-
Soluciones para fines productivos:
- HA Multi-nodo con múltiples masters
- kubeamd, GCP, Kops con AWS o plataformas soportadas
- Hasta 5000 nodos
- Hasta 150K PODs en el cluster
- Hasta 300K contenedores
- Hasta 100 PODs por nodo.
Para cloud hay multitud de herramientas para implementar un cluster. Pero para clusters OnPrem, kubeadm es una herramienta muy util.
Dependiendo de las cargas de trabajo configuradas, sus configuraciones de nodo y disco serán diferentes.
- High Perfomance - SSD Backed storage
- Multiple Concurrent connections - Network based storage
- Persistent shared volumes for shared access across multiple PODs
- Label nodes with specific disk types
- Use Node Selector to assing applications to nodes with specific disk types
- Virtual o physical Machines
- Minimum of 4 Node Cluster (size based on workload)
- Master vs Worker Nodes
- Linux X86_64 Architecture
En clusters pequeños es posible que almacene todos los componentes del plano de control en los masters, sin embargo en cluster grandes, es conveniente separar ETCD del nodo maestro
Si tuvieramos dos masters, cada API Server tendría una dirección distinta:
https://master1:6443
https://master2:6443
Desde el fichero kubeconfig, apuntabamos a un master, cuando existe más de uno utilizamos un Balanceador de carga por delante y este equilibra la carga.
Con respecto al Scheduler o al Controller Manager, no se ejecutan en todos los Master Nodes activamente. Cuando se programa algún objecto, uno de ellos se vuelve pasivo, y se bloquea hasta para no aceptar la misma solicitud. Para configurar el estado de estos servicios
kube-controller-manager --leader-elect true # Posibilidad de ser lider
--leader-elect-lease-duration 15s # Si otro master, se vuelve en lider, se bloquea durante 15s
--leader-elect-renew-deadline 10s # El proceso se renueva cada 10s
--leader-elect-retry-period 2s # Tratan de convertirse en lider cada 2s
[other options]
Sobre ETCD, es interesante desacoplarlo de los masters, permitiendo así tener un menor riesgo a peder redudancia de datos si un nodo cae.
ETCD es un sistema distribuido clave-valor, simple seguro y rápido.
- Distribuido Es posible tener sus datos almacenamos en varias replicas en ETCD, todos mantienen una copia idéntica.
- Consistencia ETCD asegura tener una copia consistenten en cada servidor. Puede leer de todas las instancias pero ETCD no procesa las escrituras en cada nodo, solo una de las intancias es responsable del procesamiento de las escrituras, de todos los nodos disponibles, uno es el lider, y el resto se convierten en seguidores. El lider asegura que todos los nodos posean los mismos datos.internamente se reenvian las operacionse de escritura al lider.
Para la elección del lider se implementa consenso distribuido utilizando el protocolo RAFT.
RAFT utiliza temporizadoresa aleatorios para iniciar solicitudes en cada nodo, el primero en terminar envia una solicitud al resto pidiendo ser el lider, el resto responden la solicitud, y el nodo principal asume su rol de Lider. En caso de caer uno de los nodos, los restantes vuelven a evaluar quien será lider.
En el caso de que un nodo caiga, cuando el nodo Lider, evalue si las copias enviadas al resto de nodos han sido enviadas correctamente, se evalua un número MAJORITY (quorum), mínimo de nodos disponibles para que el cluster pueda funcionar. Por ello se recomienda tener al menos 3 nodos, o números impares para evitar posibles problemas con la segmentación de redes:
Quorum = N/2 + 1
# Examples
Quorum of 2 = 2/2 +1 = 2
Quorum of 3 = 3/2 +1 = 2.5 ~= 2
Quorum of 5 = 5/3 +1 = 3.5 ~= 3
Partiendo de una aplicación web con dos componentes: Servidor Web y BBDD. Debemos comprender todos los componentes/objetos que existen en todo el flujo.
- Compruebe que su aplicación es accesible, por ejemplo con un curl.
curl http://web-service-ip:node-port
curl: (7) Failed to connect to web-service-ip port node-port: Connection timed out
- Compruebe el servicio (Service aka SVC), fijese si contiene Endpoints asociados y los selectores del servicio y del pod.
kubectl describe service web-service
Name: web-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: name=webapp-mysql
Type: NodePort
IP: 10.96.0.156
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
NodePort: <unset> 31672/TCP
Endpoints: 10.32.0.6:8080
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
apiVersion: v1
kind: Pod
metadata:
name: webapp-mysql
labels:
app: example-app
name: webapp-mysql
spec:
containers:
- name: webapp-mysql
image: simple-webapp-mysql
ports:
- containerPort: 8080
- Compruebe el estado del Pod, fijese en el número de reinicios, realice un describe al pod, y compruebe los logs
kubectl get pod
NAME READY STATUS RESTARTS AGE
Web 1/1 Running 5 50m
kubectl describe pod web
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 52m default-scheduler Successfully assigned webapp-mysql to worker-1
Normal Pulling 52m kubelet, worker-1 pulling image "simple-webapp-mysql"
Normal Pulled 52m kubelet, worker-1 Successfully pulled image "simple-webapp-mysql"
Normal Created 52m kubelet, worker-1 Created container
Normal Started 52m kubelet, worker-1 Started container
kubectl logs web -f(same tail -f) --previous (show previous pod)
...
10.32.0.1 - - [01/Apr/2019 12:51:55] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:55] "GET /static/img/success.jpg HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:55] "GET /favicon.ico HTTP/1.1" 404 -
10.32.0.1 - - [01/Apr/2019 12:51:57] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:57] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:58] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:58] "GET / HTTP/1.1" 200 –
10.32.0.1 - - [01/Apr/2019 12:51:55] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:55] "GET /static/img/success.jpg HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:55] "GET /favicon.ico HTTP/1.1" 404 -
10.32.0.1 - - [01/Apr/2019 12:51:57] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:57] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:58] "GET / HTTP/1.1" 200 -
10.32.0.1 - - [01/Apr/2019 12:51:58] "GET / HTTP/1.1" 200 –
10.32.0.1 - - [01/Apr/2019 12:51:58] "GET / HTTP/1.1" 400 – Some Database Error application exiting!
- Continuando con el análisis, compruebe el servicio de la BBDD y el Pod que contiene la BBDD.
- Puede consultar la página de Kubernetes (https://kubernetes.io/docs/tasks/debug-application-cluster/debug-application/), aparcen algunos ejemplos más sobre troubleshooting de aplicaciones.
En este punto veremos comprobar fallos en los componentes del Plano de Control (Control Plane).
- Comprobaremos el estado de los nodos
kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker-1 Ready <none> 8d v1.13.0
worker-2 Ready <none> 8d v1.13.0
- Comprobaremos el estado de los pods del Cluster
kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql 1/1 Running 0 113m
webapp-mysql 1/1 Running 0 113m
kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-78fcdf6894-5dntv 1/1 Running 0 1h
coredns-78fcdf6894-knpzl 1/1 Running 0 1h
etcd-master 1/1 Running 0 1h
kube-apiserver-master 1/1 Running 0 1h
kube-controller-manager-master 1/1 Running 0 1h
kube-proxy-fvbpj 1/1 Running 0 1h
kube-proxy-v5r2t 1/1 Running 0 1h
kube-scheduler-master 1/1 Running 0 1h
weave-net-7kd52 2/2 Running 1 1h
weave-net-jtl5m 2/2 Running 1 1h
- Si tiene los componentes del Plano de Control como servicio, compruebe su estado
service kube-apiserver status
● kube-apiserver.service - Kubernetes API Server
Loaded: loaded (/etc/systemd/system/kube-apiserver.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-03-20 07:57:25 UTC; 1 weeks 1 days ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 15767 (kube-apiserver)
Tasks: 13 (limit: 2362)
service kube-controller-manager status
● kube-controller-manager.service - Kubernetes Controller Manager
Loaded: loaded (/etc/systemd/system/kube-controller-manager.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-03-20 07:57:25 UTC; 1 weeks 1 days ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 15771 (kube-controller)
Tasks: 10 (limit: 2362)
service kube-scheduler status
● kube-scheduler.service - Kubernetes Scheduler
Loaded: loaded (/etc/systemd/system/kube-scheduler.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2019-03-29 01:45:32 UTC; 11min ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 28390 (kube-scheduler)
Tasks: 10 (limit: 2362)
service kubelet status
● kubelet.service - Kubernetes Kubelet
Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-03-20 14:22:06 UTC; 1 weeks 1 days ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 1281 (kubelet)
Tasks: 24 (limit: 1152)
service kube-proxy status
● kube-proxy.service - Kubernetes Kube Proxy
Loaded: loaded (/etc/systemd/system/kube-proxy.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-03-20 14:21:54 UTC; 1 weeks 1 days ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 794 (kube-proxy)
Tasks: 7 (limit: 1152)
- Comprueba los logs de los distintos pods del Plano de control.
kubectl logs kube-apiserver-master -n kube-system
I0401 13:45:38.190735 1 server.go:703] external host was not specified, using 172.17.0.117
I0401 13:45:38.194290 1 server.go:145] Version: v1.11.3
I0401 13:45:38.819705 1 plugins.go:158] Loaded 8 mutating admission controller(s) successfully in the following order:
NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,Priority,DefaultTolerationSeconds,DefaultStorageClass,MutatingAdmissionWebhook.
I0401 13:45:38.819741 1 plugins.go:161] Loaded 6 validating admission controller(s) successfully in the following order:
LimitRanger,ServiceAccount,Priority,PersistentVolumeClaimResize,ValidatingAdmissionWebhook,ResourceQuota.
I0401 13:45:38.821372 1 plugins.go:158] Loaded 8 mutating admission controller(s) successfully in the following order:
NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,Priority,DefaultTolerationSeconds,DefaultStorageClass,MutatingAdmissionWebhook.
I0401 13:45:38.821410 1 plugins.go:161] Loaded 6 validating admission controller(s) successfully in the following order:
LimitRanger,ServiceAccount,Priority,PersistentVolumeClaimResize,ValidatingAdmissionWebhook,ResourceQuota.
I0401 13:45:38.985453 1 master.go:234] Using reconciler: lease
W0401 13:45:40.900380 1 genericapiserver.go:319] Skipping API batch/v2alpha1 because it has no resources.
W0401 13:45:41.370677 1 genericapiserver.go:319] Skipping API rbac.authorization.k8s.io/v1alpha1 because it has no resources.
W0401 13:45:41.381736 1 genericapiserver.go:319] Skipping API scheduling.k8s.io/v1alpha1 because it has no resources.
sudo journalctl -u kube-apiserver
Mar 20 07:57:25 master-1 systemd[1]: Started Kubernetes API Server.
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.553377 15767 flags.go:33] FLAG: --address="127.0.0.1"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558273 15767 flags.go:33] FLAG: --admission-control="[]"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558325 15767 flags.go:33] FLAG: --admission-control-config-file=""
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558339 15767 flags.go:33] FLAG: --advertise-address="192.168.5.11"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558353 15767 flags.go:33] FLAG: --allow-privileged="true"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558365 15767 flags.go:33] FLAG: --alsologtostderr="false"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558413 15767 flags.go:33] FLAG: --anonymous-auth="true"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558425 15767 flags.go:33] FLAG: --api-audiences="[]"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558442 15767 flags.go:33] FLAG: --apiserver-count="3"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558454 15767 flags.go:33] FLAG: --audit-dynamic-configuration="false"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558464 15767 flags.go:33] FLAG: --audit-log-batch-buffer-size="10000"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558474 15767 flags.go:33] FLAG: --audit-log-batch-max-size="1"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558484 15767 flags.go:33] FLAG: --audit-log-batch-max-wait="0s"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558495 15767 flags.go:33] FLAG: --audit-log-batch-throttle-burst="0"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558504 15767 flags.go:33] FLAG: --audit-log-batch-throttle-enable="false"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558514 15767 flags.go:33] FLAG: --audit-log-batch-throttle-qps="0"
Mar 20 07:57:25 master-1 kube-apiserver[15767]: I0320 07:57:25.558528 15767 flags.go:33] FLAG: --audit-log-format="json"
Cuando experimentamos fallos en los Nodos Worker, es un buen comienzo, realizar el siguiente análisis.
- Comprobamos el estado de los nodos
kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker-1 Ready <none> 8d v1.13.0
worker-2 NotReady <none> 8d v1.13.0
- Comprobamos el estado de nodo
NotReady
Cada nodo tiene una serie de condiciones, en la que pueden indicarnos el tipo de fallo, dependiendo del estado, se establecenTrue
,False
oUnknown
.
Cuando el nodo no tiene espacio
OutOfDisk
esTrue
Cuando el nodo no tiene memoriaMemoryPressure
esTrue
Cuando el nodo tiene poca capacidadDiskPressure
esTrue
Cuando en el nodo hay demasiados procesosPIDPressure
esTrue
Si el nodo en conjunto es saludable el indicadorReady
se establece enTrue
kubectl describe node worker-1
...
Conditions:
Type Status LastHeartbeatTime Reason Message
---- ------ ----------------- ------ -------
OutOfDisk False Mon, 01 Apr 2019 14:30:33 +0000 KubeletHasSufficientDisk kubelet has sufficient disk space available
MemoryPressure False Mon, 01 Apr 2019 14:30:33 +0000 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Mon, 01 Apr 2019 14:30:33 +0000 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Mon, 01 Apr 2019 14:30:33 +0000 KubeletHasSufficientPID kubelet has sufficient PID available
Ready True Mon, 01 Apr 2019 14:30:33 +0000 KubeletReady kubelet is posting ready status. AppArmor enabled
Cuando los estados se muestran Unknown
, puede deberse a una pérdida del nodo, o puede que esté bloqueado.
kubectl describe node worker-1
...
Conditions:
Type Status LastHeartbeatTime Reason Message
---- ------ ----------------- ------ -------
OutOfDisk Unknown Mon, 01 Apr 2019 14:20:20 +0000 NodeStatusUnknown Kubelet stopped posting node status.
MemoryPressure Unknown Mon, 01 Apr 2019 14:20:20 +0000 NodeStatusUnknown Kubelet stopped posting node status.
DiskPressure Unknown Mon, 01 Apr 2019 14:20:20 +0000 NodeStatusUnknown Kubelet stopped posting node status.
PIDPressure False Mon, 01 Apr 2019 14:20:20 +0000 KubeletHasSufficientPID kubelet has sufficient PID available
Ready Unknown Mon, 01 Apr 2019 14:20:20 +0000 NodeStatusUnknown Kubelet stopped posting node status.
-
Compruebe el estado del nodo entrando en el, con comandos como
top
,df -h
,free -mta
-
Compruebe el estado de kubelet
service kubelet status
● kubelet.service - Kubernetes Kubelet
Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-03-20 14:22:06 UTC; 1 weeks 1 days ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 1281 (kubelet)
Tasks: 24 (limit: 1152)
sudo journalctl –u kubelet
-- Logs begin at Wed 2019-03-20 05:30:37 UTC, end at Mon 2019-04-01 14:42:42 UTC. --
Mar 20 08:12:59 worker-1 systemd[1]: Started Kubernetes Kubelet.
Mar 20 08:12:59 worker-1 kubelet[18962]: Flag --tls-cert-file has been deprecated, This parameter should be set via the config file specified by
the Kubele
Mar 20 08:12:59 worker-1 kubelet[18962]: Flag --tls-private-key-file has been deprecated, This parameter should be set via the config file
specified by the
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.915179 18962 flags.go:33] FLAG: --address="0.0.0.0"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.918149 18962 flags.go:33] FLAG: --allow-privileged="true"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.918339 18962 flags.go:33] FLAG: --allowed-unsafe-sysctls="[]"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.918502 18962 flags.go:33] FLAG: --alsologtostderr="false"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.918648 18962 flags.go:33] FLAG: --anonymous-auth="true"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.918841 18962 flags.go:33] FLAG: --application-metrics-count-limit="100"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.918974 18962 flags.go:33] FLAG: --authentication-token-webhook="false"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.919096 18962 flags.go:33] FLAG: --authentication-token-webhook-cache-ttl="2m0s"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.919299 18962 flags.go:33] FLAG: --authorization-mode="AlwaysAllow"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.919466 18962 flags.go:33] FLAG: --authorization-webhook-cache-authorized-ttl="5m0s"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.919598 18962 flags.go:33] FLAG: --authorization-webhook-cache-unauthorized-ttl="30s"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.919791 18962 flags.go:33] FLAG: --azure-container-registry-config=""
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.919971 18962 flags.go:33] FLAG: --boot-id-file="/proc/sys/kernel/random/boot_id"
Mar 20 08:12:59 worker-1 kubelet[18962]: I0320 08:12:59.920102 18962 flags.go:33] FLAG: --bootstrap-checkpoint-path=""
- Compruebes los certificados de kubelete, caducidad, CA correcta.
openssl x509 -in /var/lib/kubelet/worker-1.crt -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
ff:e0:23:9d:fc:78:03:35
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = KUBERNETES-CA
Validity
Not Before: Mar 20 08:09:29 2021 GMT
Not After : Apr 29 08:09:29 2021 GMT
Subject: CN = system:node:worker-1, O = system:nodes
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b4:28:0c:60:71:41:06:14:46:d9:97:58:2d:fe:
a9:c7:6d:51:cd:1c:98:b9:5e:e6:e4:02:d3:e3:71:
58:a1:60:fe:cb:e7:9b:4b:86:04:67:b5:4f:da:d6:
6c:08:3f:57:e9:70:59:57:48:6a:ce:e5:d4:f3:6e:
b2:fa:8a:18:7e:21:60:35:8f:44:f7:a9:39:57:16:
4f:4e:1e:b1:a3:77:32:c2:ef:d1:38:b4:82:20:8f:
11:0e:79:c4:d1:9b:f6:82:c4:08:84:84:68:d5:c3:
e2:15:a0:ce:23:3c:8d:9c:b8:dd:fc:3a:cd:42:ae:
5e:1b:80:2d:1b:e5:5d:1b:c1:fb:be:a3:9e:82:ff:
a1:27:c8:b6:0f:3c:cb:11:f9:1a:9b:d2:39:92:0e:
47:45:b8:8f:98:13:c6:4d:6a:18:75:a4:01:6f:73:
f6:f8:7f:eb:5d:59:94:46:d8:da:37:75:cf:27:0b:
39:7f:48:20:c5:fd:c7:a7:ce:22:9a:33:4a:30:1d:
95:ef:00:bd:fe:47:22:42:44:99:77:5a:c4:97:bb:
37:93:7c:33:64:f4:b8:3a:53:8c:f4:10:db:7f:5f:
2b:89:18:d6:0e:68:51:34:29:b1:f1:61:6b:4b:c6:
...
Kubernetes utiliza plugins CNI para configurar la red. El kubelet se encarga de ejecutar los plugins.
cni-bin-dir
: Kubelet busca este directorio en busca de plugins en el arranquenetwork-plugin
: El plugin de red a utilizar desde cni-bin-dir. Debe coincidir con el nombre reportado por un plugin del directorio de plugins.
Hay varios plugins disponibles y estos son algunos:
- Weave: Es el único plugin mencionado en la documentación de Kubernetes.
- Flannel: Por ahora no soporta las
Network Policies
de Kubernetes. - Calico: Es el CNI más maduro.
Nota: Si hay varios archivos de configuración CNI en el directorio, kubelet utilizará el archivo de configuración que viene primero por nombre en orden lexicográfico.
Kubernetes utiliza CoreDNS
, es un servidor DNS flexible y extensible que puede servir como DNS del clúster Kubernetes.
En clusters Kubernetes a gran escala, el uso de memoria de CoreDNS se ve afectado predominantemente por el número de Pods y Servicios en el cluster. Otros factores incluyen el tamaño de la caché de respuestas DNS llena, y la tasa de consultas recibidas (QPS) por instancia CoreDNS.
Los recursos de Kubernetes para coreDNS son:
- Service Account, coredns
- ClusterRoles, coredns y kube-dns
- ClusterRoleBindings, coredns y kube-dns
- Deployment, coredns
- Configmap, coredns
- Service, kube-dns
Al analizar el despliegue de coreDNS se puede ver que el plugin Corefile
consiste en una configuración importante que se define como un configmap.
El puerto 53 es el usado para la resolución DNS.
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
Este es el backend de k8s para cluster.local y dominios inversos.
proxy . /etc/resolv.conf
Reenvía los dominios fuera del clúster directamente al servidor DNS autoritativo correcto.
-
Si encuentra pods
CoreDNS
en estadoPending
, compruebe primero que el plugin de red está instalado. -
Los Pods de
CoreDNS
tienen el estadoCrashLoopBackOff
oError
:
Si tiene nodos que ejecutan SELinux con una versión antigua de Docker puede experimentar un escenario en el que los pods de coredns no se inician. Para solucionarlo puedes probar una de las siguientes opciones:
- Actualizar a una versión más reciente de Docker.
- Desactivar SELinux.
- Modificar el despliegue de CoreDNSara establecer
allowPrivilegeEscalation
aTrue
:
kubectl -n kube-system get deployment coredns -o yaml | \
sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | | \
kubectl apply -f -
-
Otra causa para que CoreDNS tenga
CrashLoopBackOff
es cuando un Pod CoreDNS desplegado en Kubernetes detecta un bucle. Hay muchas maneras de solucionar este problema, algunas de ellas se enumeran aquí:-
Añadir lo siguiente a su kubelet config yaml: resolvConf: path-to-your-real-resolv-conf-file Esta flag le dice a kubelet que pase un resolv.conf alternativo a Pods. Para los sistemas que utilizan systemd-resolved,
/run/systemd/resolve/resolv.conf
suele ser la ubicación del resolv.conf "real", aunque puede ser diferente dependiendo de su distribución. -
Desactive la caché local de DNS en los nodos anfitriones, y restaure
/etc/resolv.conf
al original. -
Una solución rápida es editar su Corefile, sustituyendo
forward . /etc/resolv.conf
por la dirección IP de su DNS de origen, por ejemploforward . 8.8.8.8
. Pero esto sólo arregla el problema para CoreDNS, kubelet continuará reenviando el resolv.conf inválido a todos los Pods dnsPolicy por defecto, dejándolos incapaces de resolver DNS.
-
- Si los pods CoreDNS y el servicio kube-dns están funcionando bien, compruebe que el servicio kube-dns tiene endpoints válidos.
kubectl -n kube-system get ep kube-dns
Si no hay Endpoints
en el Service
, inspeccionle y compruebe que utiliza Selectors
y puertos correctos.
- Compruebe la conectividad desde un pod al Service de CoreDNS.
- Compruebe la resolución DNS del cluster desde un pod a un servicio.
nslookup <service-name>.<namespace>
kube-proxy
es un proxy de red que se ejecuta en cada nodo del cluster y mantiene reglas de red en los nodos.
Estas reglas de red permiten la comunicación de red a los Pods desde sesiones de red dentro o fuera del cluster.
En un cluster configurado con kubeadm, puede encontrar kube-proxy como un daemonset.
kubeproxy se encarga de vigilar los servicios y el endpoint asociado a cada servicio. Cuando el cliente se va a conectar al servicio utilizando la IP virtual, el kubeproxy se encarga de enviar el tráfico a los pods reales.
Puede ver que el binario kube-proxy
se ejecuta con el siguiente comando dentro del contenedor kube-proxy.
kubectl describe ds kube-proxy -n kube-system
...
Command:
/usr/local/bin/kube-proxy
--config=/var/lib/kube-proxy/config.conf
--hostname-override=$(NODE_NAME)
...
La configuración se obtiene del fichero /var/lib/kube-proxy/config.conf
y podemos sustituir el nombre de host con el nombre del nodo en el que se está ejecutando el pod.
En el archivo de configuración definimos el clusterCIDR
, el modo kubeproxy
, ipvs
, iptables
, bindaddress
, kube-config
, etc.
Solución de problemas relacionados con kube-proxy
- Compruebe que el Pod
kube-proxy
en el namespacekube-system
se encuentraRunning
. - Compruebe los logs de
kube-proxy
. - Compruebe que el
Configmap
está correctamente definido y que el archivo de configuración para el binario kube-proxy en ejecución es correcto. kube-config
está definido en elConfimap
.- Compruebe que
kube-proxy
se está ejecutando dentro del contenedor.
# netstat -plan | grep kube-proxy
tcp 0 0 0.0.0.0:30081 0.0.0.0:* LISTEN 1/kube-proxy
tcp 0 0 127.0.0.1:10249 0.0.0.0:* LISTEN 1/kube-proxy
tcp 0 0 172.17.0.12:33706 172.17.0.12:6443 ESTABLISHED 1/kube-proxy
tcp6 0 0 :::10256 :::*