User Tools

Site Tools


informatica:linux:docker:kubernetes

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
informatica:linux:docker:kubernetes [2022/07/09 17:50] – [Nginx NodePort daemonset (usar este)] javiinformatica:linux:docker:kubernetes [2022/09/24 07:41] (current) – removed javi
Line 1: Line 1:
-====== Instalación ====== 
- 
-El proceso instalar un cluster de kubernetes en "**bare metal**", es decir, sobre servidores, sin ningún tipo de plataforma cloud como GCP, Amazon AWS o Azure, es el siguiente: 
- 
-1. Instalar nodos (al menos un control plane y un worker) 
- 
-2. Iniciar el cluster (se ejecuta en el primer control plane) 
- 
-3. (Opcional, solo para alta disponibilidad) Unir control plane adicionales al cluster 
- 
-4. Unir worker (al menos uno) al cluster 
- 
-===== Nodo (común para control plane y worker) ===== 
- 
-Estas instrucciones detallan como instalar un nodo de kubernetes, por tanto es COMÚN para todos los nodos, con independencia de su posterior rol dentro del cluster (control plane o worker). 
- 
-1. Verificar que el nodo cumple todos los siguientes requisitos: 
- 
-https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#before-you-begin 
- 
-2. Instalar un runtime 
- 
-**IMPORTANTE**: leer la siguientes consideraciones 
- 
-* Kubernetes usa CRI, que NO es compatible con Docker Engine, por eso mejor NO usarlo (Docker Engine) 
- 
-* Para instalar containerd usaremos los paquetes, que los distribuye Docker Inc. 
- 
-* Habrá que instalar luego los plugins CNI, que NO vienen en los paquetes que distribuye Docker Inc. 
- 
-2.1. Instalar containerd 
- 
-Este ejemplo es para ubuntu 
- 
-  sudo apt-get remove docker docker-engine docker.io containerd runc 
- 
-  sudo apt-get update 
- 
-<code> 
-sudo apt-get install \ 
-    ca-certificates \ 
-    curl \ 
-    gnupg \ 
-    lsb-release 
-</code> 
- 
-  sudo mkdir -p /etc/apt/keyrings 
- 
-<code> 
-curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 
-</code> 
- 
-<code> 
-echo \ 
-  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ 
-  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 
-</code> 
- 
-  sudo apt-get update 
- 
-**IMPORTANTE**: NO instalar docker engine, solo containerd 
- 
-  sudo apt install containerd.io 
- 
-Verificar: 
- 
-  systemctl status containerd 
- 
-  q 
-   
-2.2. Redirigir IPv4 y permitir a iptables ver el trafico "bridged" 
- 
-<code> 
-cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf 
-overlay 
-br_netfilter 
-EOF 
-</code> 
- 
-<code> 
-sudo modprobe overlay 
-sudo modprobe br_netfilter 
-</code> 
- 
-# sysctl params required by setup, params persist across reboots 
- 
-<code> 
-cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf 
-net.bridge.bridge-nf-call-iptables  = 1 
-net.bridge.bridge-nf-call-ip6tables = 1 
-net.ipv4.ip_forward                 = 1 
-EOF 
-</code> 
- 
-# Apply sysctl params without reboot 
- 
-<code> 
-sudo sysctl --system 
-</code> 
- 
-2.3. Configurar systemd cgroup driver 
- 
-2.3.1. Copia de seguridad y gener archivo nuevo de trinca 
- 
-  sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.bak 
-  sudo containerd config default | sudo tee /etc/containerd/config.toml 
- 
-2.3.2. Reemplazar: 
- 
-  sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml 
- 
-Esto es el equivalente a: 
- 
-  sudo vim /etc/containerd/config.toml 
- 
-Y dejar esta linea tal que así: 
- 
-<code> 
-            # anyadido 
-            #SystemdCgroup = false 
-            SystemdCgroup = true 
-</code> 
- 
-2.3.3. Reiniciar el servicio: 
- 
-  sudo systemctl restart containerd 
- 
-3. Instalar plugins CNI 
- 
-En este ejemplo la arquitectura es ARM 
- 
-  sudo mkdir -p /opt/cni/bin/ 
- 
-Escoger de: 
- 
-https://github.com/containernetworking/plugins/releases 
- 
-**OJO**: en este ejemplo la arquitectura es ARM 
- 
-  sudo wget https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-arm64-v1.1.1.tgz 
- 
-  sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-arm64-v1.1.1.tgz 
- 
-  sudo systemctl restart containerd 
- 
-4. Instalar kubeadm, kubelet and kubectl 
- 
-<code> 
-sudo apt-get update 
-sudo apt-get install -y apt-transport-https ca-certificates curl 
-</code> 
- 
-  sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg 
- 
-  echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list 
- 
-<code> 
-sudo apt-get update 
-sudo apt-get install -y kubelet kubeadm kubectl 
-sudo apt-mark hold kubelet kubeadm kubectl 
-</code> 
- 
-5. Configurar driver cgroup 
- 
-5.1. Configurar container runtime cgroup driver 
- 
-Ya hecho en pasos anteriores, eb este caso containerd 
- 
-5.2. Configurar kubelet cgroup driver 
- 
-https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/#configuring-the-kubelet-cgroup-driver 
- 
-Entiendo que NO es necesario porque desde la versión 1.22 configurará por defecto systemd 
-===== Primer control plane ===== 
- 
-Este paso solo se tiene que hacer **UNA VEZ POR CLUSTER**, y se ejecutará en aquel nodo, ya instalado, que vaya a ser el **PRIMER CONTROL PLANE**. 
- 
-==== Sin alta disponibilidad ==== 
- 
-1. Conectarse al servidor 
- 
-  ssh k8s2 
-   
-2. Iniciar el cluster. Ejecutar: 
- 
-<code> 
-sudo kubeadm init \ 
-  --control-plane-endpoint "k8s2.local:6443" \ 
-  --pod-network-cidr=10.244.0.0/16 \ 
-  --upload-certs \ 
-  --v=5 
-</code> 
- 
-Comentarios: 
- 
-  * Aunque no sea alta disponibilidad es conveniente usar 'control-plane-endpoint' por si más adelante quiere usarse 
-  * Debe ser un nombre que todos los nodos (control plane y workers) resuelvan, o bien la IP privada del control plane 
-  * El puerto es el 6443, es en el que escucha el servicio 'kube-apiserver' 
-  * El parámetro 'pod-network-cidr' es requerido por flannel, y le pasamos el rango de IPs de los PODS es el '10.244.0.0/16' 
- 
-3. Anotar de la salida del comando anterior los comandos para unir control planes y workers al cluster: 
- 
-3.1. Anotar el comando para unir un control plane: 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s2.local:6443 \ 
-  --token r6mawr.wsgooc91h45a55i8 \ 
-  --discovery-token-ca-cert-hash sha256:94e15af3476146deb36c0f9a53f33a9ea3c441470a2f9e3aefdf538ca7fb4443 \ 
-  --control-plane \ 
-  --certificate-key 7b04bab444d4a5515c514a8b8eeb7a2df6629ee80d46b7f1ed5bd4b1aa3d80ed \ 
-  --v=8 
-</code> 
- 
-Opciones: 
- 
-  * Añadir 'sudo' al comando 
-  * Añadir '--v' para ver más cosas en la salida del comando cuando se ejecute 
- 
-3.2. Anoter el comando para unir un worker: 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s2.local:6443 \ 
-  --token 9sljzd.hm5i967gzi802cah \ 
-  --discovery-token-ca-cert-hash sha256:0a3ac17a0a2c093aca67b2cdc8c99e3cc9c374a48fe762385faf705932785de8 \ 
-  --v=8 
-</code> 
- 
-Opciones: 
- 
-  * Añadir 'sudo' al comando 
-  * Añadir '--v' para ver más cosas en la salida del comando cuando se ejecute 
- 
-4. Instalar plugin CNI, en este ejemplo "flannel". Ejecutar: 
- 
-  kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 
- 
-5. Configurar archivos para poder usar 'kubectl'. Ejecutar: 
- 
-<code> 
-mkdir -p $HOME/.kube 
-sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 
-sudo chown $(id -u):$(id -g) $HOME/.kube/config 
-</code> 
- 
-<code> 
-sudo mkdir -p /root/.kube 
-sudo cp -i /etc/kubernetes/admin.conf /root/.kube/config 
-</code> 
- 
-6. Comprobar: 
- 
-  kubectl get nodes 
-   
-Salida esperada similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE    VERSION 
-k8s2   Ready    control-plane,master   22h    v1.21.1 
-</code> 
- 
-==== Con alta disponibilidad ==== 
- 
-En este ejemplo tendremos 3 nodos que actuarán como control plane: 
- 
-^ DNS ^ IP ^ Puerto ^ Comentario ^ 
-| k8s.local    | 192.168.95.69 | 8443 | 'kube-apiserver' cluster.  | 
-| k8s2.local | 192.168.95.71 | 6443 | Primer control plane | 
-| k8s3.local | 192.168.95.72 | 6443 | Control plane adicional | 
-| k8s3.local | 192.168.95.73 | 6443 | Control plane adicional | 
- 
-Antes de empezar para cada uno de los tres control plane [[informatica:linux:docker:kubernetes#balanceador_capa_4_kube-apiserver|confifurar balanceador capa 4 kube-apiserver]] 
- 
-1. Conectarse al servidor 
- 
-  ssh k8s2 
-   
-2. Iniciar el cluster. Ejecutar: 
- 
-<code> 
-sudo kubeadm init \ 
-  --control-plane-endpoint "k8s.local:8443" \ 
-  --pod-network-cidr=10.244.0.0/16 \ 
-  --upload-certs \ 
-  --v=5 
-</code> 
- 
-Comentarios: 
- 
-  * El parámetro 'control-plane-endpoint' debe ser un nombre que todos los nodos (control plane y workers) resuelvan, no se recomienda usar una dirección IP. Es el nombre DNS del balanceador capa 4 kube-apiserver 
-  * El puerto es el 8443, es en el que escucha balanceador capa 4 kube-apiserver 'kube-apiserver' 
-  * El parámetro 'pod-network-cidr' es requerido por flannel, y le pasamos el rango de IPs de los PODS es el '10.244.0.0/16' 
- 
-3. Anotar de la salida del comando anterior los comandos para unir control planes y workers al cluster: 
- 
-3.1. Anotar el comando para unir un control plane: 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s.local:8443 \ 
-  --token r6mawr.wsgooc91h45a55i8 \ 
-  --discovery-token-ca-cert-hash sha256:94e15af3476146deb36c0f9a53f33a9ea3c441470a2f9e3aefdf538ca7fb4443 \ 
-  --control-plane \ 
-  --certificate-key 7b04bab444d4a5515c514a8b8eeb7a2df6629ee80d46b7f1ed5bd4b1aa3d80ed \ 
-  --v=8 
-</code> 
- 
-Opciones: 
- 
-  * Añadir 'sudo' al comando 
-  * Añadir '--v' para ver más cosas en la salida del comando cuando se ejecute 
- 
-3.2. Anoter el comando para unir un worker: 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s.local:8443 \ 
-  --token 9sljzd.hm5i967gzi802cah \ 
-  --discovery-token-ca-cert-hash sha256:0a3ac17a0a2c093aca67b2cdc8c99e3cc9c374a48fe762385faf705932785de8 \ 
-  --v=8 
-</code> 
- 
-Opciones: 
- 
-  * Añadir 'sudo' al comando 
-  * Añadir '--v' para ver más cosas en la salida del comando cuando se ejecute 
- 
-4. Instalar plugin CNI, en este ejemplo "flannel". Ejecutar: 
- 
-  kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 
- 
-5. Configurar archivos para poder usar 'kubectl'. Ejecutar: 
- 
-<code> 
-mkdir -p $HOME/.kube 
-sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 
-sudo chown $(id -u):$(id -g) $HOME/.kube/config 
-</code> 
- 
-<code> 
-sudo mkdir -p /root/.kube 
-sudo cp -i /etc/kubernetes/admin.conf /root/.kube/config 
-</code> 
- 
-6. Comprobar: 
- 
-  kubectl get nodes 
-   
-Salida esperada similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE    VERSION 
-k8s2   Ready    control-plane,master   22h    v1.21.1 
-</code> 
- 
-===== Unir nodos al cluster ===== 
- 
-Una vez se ha iniciado el cluster en el primer control plane hay que añadir nodos al cluster: 
- 
-  * Control plane. Solo en alta disponibilidad. Si no vamos a configurar alta disponibilidad, un control plane por cluster de kubernetes es suficiente. 
-  * Workers. Al menos uno. 
- 
-==== Control plane (solo alta disponibilidad) ==== 
- 
-Solo debemos añadir control plane adicionales al cluster si vamos a configurar alta disponibilidad. 
- 
-1. Conectarse al control plane: 
- 
-  ssh k8s3 
-   
-2. Ejecutar el comando obtenido en el paso 3.1. de [[informatica:linux:docker:kubernetes#con_alta_disponibilidad|primer control plane con alta disponibilidad]] con las siguientes modificaciones: 
- 
-  * Si hemos configurado alta disponibilidad con [[https://github.com/kubernetes/kubeadm/blob/master/docs/ha-considerations.md#kube-vip|kube-vip]] el directorio '/etc/kubernetes/manifests' no estará vacío, y el comando 'sudo kubeadm join' fallará. Por ese motivo, y solo en este caso, debemos añadir al comando 'sudo kubeadm join' el parámetro '--ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests' 
-  * Añadir 'sudo' 
-  * (Opcional) Añadir '--v=8' para que la salida del comando muestre más información 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s.local:8443 \ 
-  --token r6mawr.wsgooc91h45a55i8 \ 
-  --discovery-token-ca-cert-hash sha256:94e15af3476146deb36c0f9a53f33a9ea3c441470a2f9e3aefdf538ca7fb4443 \ 
-  --control-plane \ 
-  --certificate-key 7b04bab444d4a5515c514a8b8eeb7a2df6629ee80d46b7f1ed5bd4b1aa3d80ed \ 
-  --ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests \ 
-  --v=8 
-</code> 
- 
-3. Pasos opcionales. Los pasos que se listan a continuación son opcionales, y solo son necesarios si queremos usar el comando 'kubectl' desde el segundo y subsiguientes control plane, lo que es acertado, pues igual nos hace falta si el primer control plane se cae por el motivo que sea. 
- 
-3.1. Configurar archivos para poder usar 'kubectl'. Ejecutar: 
- 
-<code> 
-mkdir -p $HOME/.kube 
-sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 
-sudo chown $(id -u):$(id -g) $HOME/.kube/config 
-</code> 
- 
-<code> 
-sudo mkdir -p /root/.kube 
-sudo cp -i /etc/kubernetes/admin.conf /root/.kube/config 
-</code> 
- 
-3.2. Comprobar: 
- 
-  kubectl get nodes 
-   
-Salida esperada similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE    VERSION 
-k8s2   Ready    control-plane,master   22h    v1.21.1 
-k8s3   Ready    control-plane,master   22h    v1.21.1 
-</code> 
- 
-4. Repetir los pasos 1 a 3 con el resto de control plane que queramos añadir al cluster. 
- 
-==== Worker ==== 
- 
-Al menos un nodo debe tener el rol de worker. Las instrucciones son casi idénticas se haya configurado o no alta diponibilidad, pero se muestran de forma separada para hacerlas más legibles. 
- 
-=== Sin alta disponibilidad === 
- 
-1. Conectarse al worker: 
- 
-  ssh k8s3 
-   
-2. Ejecutar el comando obtenido en el paso 3.2. de [[http://wiki.legido.com/doku.php?id=informatica:linux:docker:kubernetes#sin_alta_disponibilidad|primer control plane sin alta disponibilidad]] con las siguientes modificaciones: 
- 
-  * Añadir 'sudo' 
-  * (Opcional) Añadir '--v=8' para que la salida del comando muestre más información 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s2.local:6443 \ 
-  --token 9sljzd.hm5i967gzi802cah \ 
-  --discovery-token-ca-cert-hash sha256:0a3ac17a0a2c093aca67b2cdc8c99e3cc9c374a48fe762385faf705932785de8 \ 
-  --v=8 
-</code> 
- 
-3. Comprobar 
- 
-3.1. Conectarse al control plane: 
- 
-  ssh k8s2 
-   
-3.2. Ejecutar: 
- 
-  kubectl get nodes 
-   
-Salida esperada similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE     VERSION 
-k8s2   Ready    control-plane,master   2d18h   v1.21.1 
-k8s3   Ready    <none>                 46h     v1.21.1 
-</code> 
- 
-4. (Opcional) Repetir los pasos 1 a 4 con el resto de workers que se quiera unir al cluster de kubernetes. 
- 
-=== Con alta disponibilidad === 
- 
-1. Conectarse al worker: 
- 
-  ssh k8s5 
-   
-2. Ejecutar el comando obtenido en el paso 3.2. de [[informatica:linux:docker:kubernetes#con_alta_disponibilidad|primer control plane con alta disponibilidad]] con las siguientes modificaciones: 
- 
-  * Añadir 'sudo' 
-  * (Opcional) Añadir '--v=8' para que la salida del comando muestre más información 
- 
-<code> 
-sudo kubeadm join \ 
-  k8s.local:8443 \ 
-  --token 9sljzd.hm5i967gzi802cah \ 
-  --discovery-token-ca-cert-hash sha256:0a3ac17a0a2c093aca67b2cdc8c99e3cc9c374a48fe762385faf705932785de8 \ 
-  --v=8 
-</code> 
- 
-3. Comprobar 
- 
-3.1. Conectarse a cualquiera de los control plane: 
- 
-  ssh k8s4 
-   
-3.2. Ejecutar: 
- 
-  kubectl get nodes 
-   
-Salida esperada similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE     VERSION 
-k8s2   Ready    control-plane,master   2d18h   v1.21.1 
-k8s3   Ready    control-plane,master   2d18h   v1.21.1 
-k8s4   Ready    control-plane,master   2d18h   v1.21.1 
-k8s5   Ready    <none>                 46h     v1.21.1 
-</code> 
- 
-4. (Opcional) Repetir los pasos 1 a 4 con el resto de workers que se quiera unir al cluster de kubernetes. 
-====== Balanceador capa 4 kube-apiserver ====== 
- 
-https://github.com/kubernetes/kubeadm/blob/master/docs/ha-considerations.md#kube-vip 
- 
-Paso requerido para alta disponibilidad. 
- 
-Vamos a crear un balanceador de carga capa 4 para el servicio [[https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver|kube-apiserver]], en este caso mediante un pod. 
- 
-El objetivo es crear una IP virtual o flotante ('192.168.95.69' en este ejemplo) asociada a un nombre DNS ('k8s.local' en este ejemplo) que irá pasando de un control plane a otro en caso de que el control plane que tenga la IP virtual se caiga. 
- 
-Las peticiones TCP que lleguen a ese nombre ('k8s.local') y ese puerto ('8443' en este ejemplo) serán dirigidas al puerto TCP 6443 del control plane que en ese momento tenga la IP virtual. En ese puerto, TCP 6443, es en el que escucha el servicio 'kube-apiserver'. 
- 
-Todos los nodos (control plane o workers) deben resolver el nombre que elijamos para el balanceador de carga, en este ejemplo 'k8s'. 
- 
-Todos los miembros de ese balanceador de carga, los 3 control plane en este ejemplo, deben ser capaces de resolver el nombre DNS de todos y cada uno de sus miembros, ya que usaremos nombres DNS. 
- 
-En este ejemplo tendremos 3 nodos que actuarán como control plane: 
- 
-^ DNS ^ IP ^ Puerto ^ Comentario ^ 
-| k8s.local    | 10.0.0.200 | 8443 | 'kube-apiserver' cluster.  | 
-| k8s2.local | 10.0.0.2 | 6443 | Primer control plane | 
-| k8s3.local | 10.0.0.3 | 6443 | Control plane adicional | 
-| k8s3.local | 10.0.0.4 | 6443 | Control plane adicional | 
- 
-===== Oracle cloud ===== 
- 
-Oracle cloud ofrece por la patilla y para siempre un balanceador de capa 4 con IP pública y estática. 
- 
-Resumen de máquinas, nombres, IPs, etc. (las IPs públicas están cambiadas) 
- 
-^ DNS ^ IP pública ^ IP privada ^ Comentario ^ 
-| k8s.legido.com  | 1.2.3.4 | 10.0.0.194 | Balanceador de carga. Escucha (listener) puerto 6443 | 
-| k8s1 | 1.2.3.5 | 10.0.0.2 | Primer control plane | 
-| k8s2 | 1.2.3.6 | 10.0.0.3 | Segundo control plane | 
-| k8s3 | 1.2.3.7 | 10.0.0.4 | Tercer control plane | 
- 
-==== Load balancer ==== 
- 
-Listener 
- 
-^ Protocol ^ Ingress Traffic Port ^ Backend Set ^ 
-| TCP  | Specify the port, 6443 | listener-kube-apiserver | 
- 
-Backend set 
- 
-^ Load Balancing Policy ^ Preserve Source IP ^ Backend Set ^ 
-| 5-tuple hash  | Checked | listener-kube-apiserver | 
- 
-Backends 
- 
-^ Name ^ IP address ^ Port ^ Weight ^ 
-| k8s1  | 10.0.0.3 | 6443 | 100 | 
-| k8s2  | 10.0.0.4 | 6443 | 99 | 
-| k8s3  | 10.0.0.5 | 6443 | 99 | 
- 
- 
- 
- 
- 
- 
- 
-==== Security list ==== 
- 
-Se creó una VNC siguiendo el asistente. 
- 
-El load balancer usa la subnet pública, "Public Subnet-vnc". 
- 
-Esta tiene una security list, que hay que retocar para que: 
- 
-  * No permita conexiones desde la DMZ al listener kube-apiserver 
-  * Permita que lleguen los health check del load balancer a los nodos 
- 
-Las IPs púbicas son inventadas 
- 
-^ Stateless ^ Source ^ IP Protocol ^ Source Port Range ^ Destination Port Range ^ Description ^ 
-| No | 0.0.0.0/0 | TCP | All | 31342 | nginx-ingress-controller-https | 
-| No | 0.0.0.0/0 | TCP | All | 32386 | nginx-ingress-controller-http | 
-| No | 0.0.0.0/0 | TCP | All | 443 | listener-https | 
-| No | 0.0.0.0/0 | TCP | All | 80 | listener-http | 
-| No | 1.2.3.5/32 | All | All | All | k8s1 | 
-| No | 1.2.3.6/32 | All | All | All | k8s2 | 
-| No | 1.2.3.7/32 | All | All | All | k8s3 | 
- 
-Seguramente se puede restringir una miaja, pero así funciona. 
- 
-Como se juega también con los listeners no es tab abierta como parece. Por ejemplo desde la DMZ NO se llega al puerto 31342 (porque no hay listener publicado). 
- 
- 
- 
- 
- 
- 
-===== Haproxy y keepalived como servicios ===== 
- 
-1. Keepalived 
- 
-1.1. Instalar 
- 
-  sudo apt install keepalived 
-   
-1.2. Crear: 
- 
-  sudo vim /etc/keepalived/keepalived.conf 
- 
-Con el siguiente contenido: 
- 
-<code> 
-! /etc/keepalived/keepalived.conf 
-! Configuration File for keepalived 
-global_defs { 
-    router_id LVS_DEVEL 
-} 
-vrrp_script check_apiserver { 
-  script "/etc/keepalived/check_apiserver.sh" 
-  interval 3 
-  weight -2 
-  fall 10 
-  rise 2 
-} 
- 
-vrrp_instance VI_1 { 
-    state MASTER 
-    interface enp0s3 
-    virtual_router_id 51 
-    priority 101 
-    authentication { 
-        auth_type PASS 
-        auth_pass 42 
-    } 
-    virtual_ipaddress { 
-        10.0.0.200 
-    } 
-    track_script { 
-        check_apiserver 
-    } 
-} 
-</code> 
- 
-Explicación: 
- 
-  * STATE: MASTER para master, BACKUP para el resto 
-  * INTERFACE: enp0s3 
-  * ROUTER_ID: 51 
-  * PRIORITY: 101 para el master, 100 para el resto 
-  * AUTH_PASS: 42 
-  * APISERVER_VIP: 10.0.0.200 
- 
-1.2. Crear: 
- 
-  sudo vim /etc/keepalived/check_apiserver.sh 
- 
-Con el siguiente contenido: 
- 
-<code> 
-#!/bin/sh 
- 
-APISERVER_VIP="10.0.0.200" 
-APISERVER_DEST_PORT="8443" 
- 
-errorExit() { 
-    echo "*** $*" 1>&2 
-    exit 1 
-} 
- 
-curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/" 
-if ip addr | grep -q ${APISERVER_VIP}; then 
-    curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/" 
-fi 
-</code> 
- 
-2. haproxy 
- 
-2.1. Instalar 
- 
-  sudo apt install haproxy 
-   
-2.2. Crear: 
- 
-  sudo vim /etc/haproxy/haproxy.cfg 
- 
-Con el siguiente contenido: 
- 
-<code> 
-# /etc/haproxy/haproxy.cfg 
-#--------------------------------------------------------------------- 
-# Global settings 
-#--------------------------------------------------------------------- 
-global 
-    log /dev/log local0 
-    log /dev/log local1 notice 
-    daemon 
- 
-#--------------------------------------------------------------------- 
-# common defaults that all the 'listen' and 'backend' sections will 
-# use if not designated in their block 
-#--------------------------------------------------------------------- 
-defaults 
-    mode                    http 
-    log                     global 
-    option                  httplog 
-    option                  dontlognull 
-    option http-server-close 
-    option forwardfor       except 127.0.0.0/8 
-    option                  redispatch 
-    retries                 1 
-    timeout http-request    10s 
-    timeout queue           20s 
-    timeout connect         5s 
-    timeout client          20s 
-    timeout server          20s 
-    timeout http-keep-alive 10s 
-    timeout check           10s 
- 
-#--------------------------------------------------------------------- 
-# apiserver frontend which proxys to the control plane nodes 
-#--------------------------------------------------------------------- 
-frontend apiserver 
-    bind *:8443 
-    mode tcp 
-    option tcplog 
-    default_backend apiserver 
- 
-#--------------------------------------------------------------------- 
-# round robin balancing for apiserver 
-#--------------------------------------------------------------------- 
-backend apiserver 
-    option httpchk GET /healthz 
-    http-check expect status 200 
-    mode tcp 
-    option ssl-hello-chk 
-    balance     roundrobin 
-        server k8s1 k8s1.local:6443 check 
-        server k8s2 k8s2.local:6443 check 
-        server k8s3 k8s3.local:6443 check 
-</code> 
- 
-3. Arrancar servicios: 
- 
-  sudo systemctl enable haproxy --now 
-  sudo systemctl enable keepalived --now 
- 
-IMPORTANTE: no va a funcionar hasta que el backend (apiserver) empiece a escuchar en el puerto 6443 
-   
- 
-===== Kube-vip ===== 
- 
-Antiguo 
-No funciona en Oracle OCI 
- 
-^ DNS ^ IP ^ Puerto ^ Comentario ^ 
-| k8s.local    | 192.168.95.69 | 8443 | 'kube-apiserver' cluster.  | 
-| k8s2.local | 192.168.95.71 | 6443 | Primer control plane | 
-| k8s3.local | 192.168.95.72 | 6443 | Control plane adicional | 
-| k8s3.local | 192.168.95.73 | 6443 | Control plane adicional | 
- 
- 
-1. Conectarse al control plane. Ejecutar: 
- 
-  ssh k8s2 
- 
-2. Ejecutar: 
- 
-<code> 
-sudo mkdir -p /etc/kube-vip 
-sudo vim /etc/kube-vip/config.yaml 
-</code> 
- 
-Con el siguiente contenido, ajustando: 
- 
-  * localPeer. Id lo que queramos, address la IP privada del control plane. 
-  * remotePeers. Igual que 'localPeer' pero para el resto de control planes. 
-  * vip. La misma para todo el cluster. 
-  * startAsLeader. Para el primer control plane 'true', para el resto 'false'. 
-  * interface. En este ejemplo se tiene solo una interfaz, por tanto la 'eth0'. 
-  * loadBalancers. El cluster escucha en el puerto 8433, cada uno de los control plane escucha en el puerto 6443 
- 
-<code> 
-localPeer: 
-  id: k8s2.local 
-  address: 192.168.95.71 
-  port: 10000 
-remotePeers: 
-- id: k8s3.local 
-  address: 192.168.95.72 
-  port: 10000 
-- id: k8s4.local 
-  address: 192.168.95.73 
-  port: 10000 
-vip: 192.168.95.69 
-gratuitousARP: true 
-singleNode: false 
-startAsLeader: true 
-interface: eth0 
-loadBalancers: 
-- name: API Server Load Balancer 
-  type: tcp 
-  port: 8443 
-  bindToVip: false 
-  backends: 
-  - port: 6443 
-    address: 192.168.95.71 
-  - port: 6443 
-    address: 192.168.95.72 
-  - port: 6443 
-    address: 192.168.95.73 
-</code> 
- 
-3. Ejecutar 
- 
-<code> 
-docker run -it --rm plndr/kube-vip:0.1.1 /kube-vip sample manifest \ 
-    | sed "s|plndr/kube-vip:'|plndr/kube-vip:0.1.1'|" \ 
-    | sudo tee /etc/kubernetes/manifests/kube-vip.yaml 
-</code> 
- 
-Resultado esperado similar a: 
- 
-<code> 
-apiVersion: v1 
-kind: Pod 
-metadata: 
-  creationTimestamp: null 
-  name: kube-vip 
-  namespace: kube-system 
-spec: 
-  containers: 
-  - command: 
-    - /kube-vip 
-    - start 
-    - -c 
-    - /vip.yaml 
-    image: 'plndr/kube-vip:0.1.1' 
-    name: kube-vip 
-    resources: {} 
-    securityContext: 
-      capabilities: 
-        add: 
-        - NET_ADMIN 
-        - SYS_TIME 
-    volumeMounts: 
-    - mountPath: /vip.yaml 
-      name: config 
-  hostNetwork: true 
-  volumes: 
-  - hostPath: 
-      path: /etc/kube-vip/config.yaml 
-    name: config 
-status: {} 
-</code> 
- 
-**IMPORTANTE**: cuando se tengan que unir el segundo y tercer control plane al cluster (con el comando 'sudo kubeadm join') hay que pasar el parámetro: 
- 
-<code> 
-  --ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests 
-</code> 
- 
-4. Repetir los pasos 1 a 4, ajustando los parámetros que se indica en cada paso, hasta que no queden más control plane por añadir al balanceador de carga de capa 4. 
- 
-Cuando se inicie el cluster (primer control plane) o se una el control plane al cluster (segundo y subsiguientes control planes) se iniciará un pod que gestionará la IP virtual ('192.168.95.69' en este ejemplo). 
- 
-====== Deployment ====== 
- 
-Vamos a desplegar un POD con la imagen [[https://hub.docker.com/r/containous/whoami whoami]]. 
- 
-1. Conectarse al master 
- 
-  ssh master 
- 
-2. Ejecutar: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: apps/v1 
-kind: Deployment 
-metadata: 
-  name: deployment-whoami 
-  labels: 
-    app: deployment-whoami 
-spec: 
-  replicas: 1 
-  selector: 
-    matchLabels: 
-      app: deployment-whoami 
-  template: 
-    metadata: 
-      labels: 
-        app: deployment-whoami 
-    spec: 
-      containers: 
-      - name: whoami 
-        image: containous/whoami 
-        ports: 
-        - containerPort: 80 
-EOF 
-</code> 
- 
-3. Comprobar 
- 
-  kubectl get deployments 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                READY   UP-TO-DATE   AVAILABLE   AGE 
-deployment-whoami   1/               1           40h 
-</code> 
- 
-====== Service ====== 
- 
-Un [[https://kubernetes.io/docs/concepts/services-networking/service/|Service]] expone un [[https://kubernetes.io/docs/concepts/workloads/controllers/deployment/|Deployment]] de forma que se pueda acceder al 'Deployment' desde fuera del cluster de Kubernetes. 
- 
-Requisitos: 
- 
-  * Un [[informatica:linux:docker:kubernetes#deployment|Deployment]] con 'metadata.name' igual a 'deployment-whoami' 
- 
-1. Conectarse al master 
- 
-  ssh master 
- 
-2. Ejecutar: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  name: service-whoami 
-spec: 
-  selector: 
-    app: deployment-whoami 
-  ports: 
-    - protocol: TCP 
-      port: 80 
-EOF 
-</code> 
- 
-3. Comprobar 
- 
-  kubectl get services --field-selector metadata.name=service-whoami 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE 
-service-whoami   ClusterIP   10.103.221.128   <none>        80/TCP    42h 
-</code> 
- 
-====== Metallb ====== 
- 
-Se trata de un balanceador de carga por software para instalaciones "bare metal", es decir, aquellas que NO se producen en ningún proveedor en la nube, como Amazon AWS, Google Cloud Services o Azure. 
- 
-Es necesario instalarlo para poder instalar un servicio de tipo "LoadBalancer" y que obtenga una "EXTERNAL-IP". 
- 
-Más información [[https://metallb.universe.tf/ aquí]]. 
- 
-En este ejemplo vamos a usar: 
- 
-  * Layer 2 
-  * IPs privadas 
- 
-===== Layer 2 ===== 
- 
-Requisitos: 
- 
-  * Tener un rango de IPs **que no se usen**. Es decir, no vale ni las que tienen los nodos del cluster, ni las que usa flannel. En este ejemplo tenemos: 
-   * 192.168.95.68 
-   * 192.168.95.69 
- 
-1. Conectarse al master 
- 
-  ssh master 
-   
-2. Instalar. Ejecutar: 
- 
-  kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.6/manifests/namespace.yaml 
- 
-  kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.6/manifests/metallb.yaml 
- 
-Solo la primera vez: 
- 
-  kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)" 
- 
-3. Configuración. Ajustar: 
- 
-  * Nombre del pool de direcciones. En este ejemplo "default" 
-  * IPs. En este ejemplo "192.168.95.68" y "192.168.95.69" 
-  * Protocolo. En este ejemplo "layer2" 
- 
-Ejecutar: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: ConfigMap 
-metadata: 
-  namespace: metallb-system 
-  name: config 
-data: 
-  config: | 
-    address-pools: 
-    - name: default 
-      protocol: layer2 
-      addresses: 
-      - 192.168.95.68-192.168.95.69 
-EOF 
-</code> 
- 
-4. Probar 
- 
-4.1. Crear un "Service", no importa que apunte a un "Deployment" valido. Ajustar 'metadata.annotations.metallb.universe.tf/address-pool' para que coincida con el valor del nombre del pool de direcciones, en este ejemplo "default". 
- 
-Ejecutar: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  name: test-service-canberemoved 
-  annotations: 
-    metallb.universe.tf/address-pool: default 
-spec: 
-  ports: 
-  - port: 80 
-    targetPort: 80 
-  selector: 
-    app: nonexistantdeployment 
-  type: LoadBalancer 
-EOF 
-</code> 
- 
-4.2. Comprobar: 
- 
-  kubectl get services --field-selector metadata.name=test-service-canberemoved 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAMESPACE   NAME                        TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE 
-default     test-service-canberemoved   LoadBalancer   10.108.71.69   192.168.95.69   80:31652/TCP   3m52s 
-</code> 
- 
-El campo 'EXTERNAL-IP' tiene asignada una IP del pool de direcciones. 
- 
-4.3. Limpieza 
- 
-  kubectl delete services --field-selector metadata.name=test-service-canberemoved  
- 
-===== Cambiar 'externalTrafficPolicy' ===== 
- 
-[[https://metallb.universe.tf/usage/#layer2|Fuente]] 
- 
-Requisitos: 
- 
-  * Tener instalado [[informatica:linux:docker:kubernetes#nginx|nginx controller]] 
- 
-1. Conectarnos al master 
- 
-  ssh k8s2 
-   
-2. Editar el servicio, en nuestro caso se llama 'ingress-nginx-controller'. Ejecutar: 
- 
-  kubectl edit services -n ingress-nginx ingress-nginx-controller 
- 
-3. Cambiar: 
- 
-  externalTrafficPolicy: Cluster 
- 
-Por: 
- 
-  externalTrafficPolicy: Local 
- 
-**IMPORTANTE**: NO hace falta hacer un rollout restart de los pods, los cambios los toma inmediatamente. 
- 
-4. Comprobar. En mi caso accediendo al nombre DNS público: 
- 
-https://example.com 
- 
-El valor de 'X-Forwarded-For' ha cambiado: 
- 
-  * Cluster => aparecía la IP de la interfaz de flannel del nodo que estaba ejecutando el pod 'ingress-nginx-controller-7c6b6c6fb8-bvvcf', en este caso '10.244.1.1' 
-  * Local => aparece la IP privada del edge router. Todavía tengo que afinar, debería aparecer la IP pública del cliente que hizo la petición HTTPS. 
- 
- 
- 
-====== Addons ====== 
- 
-[[https://kubernetes.io/docs/concepts/cluster-administration/addons/|Addons]] 
- 
-===== Cert manager ===== 
- 
-[[https://github.com/jetstack/cert-manager/|Cert-manager]] es un [[https://kubernetes.io/docs/concepts/cluster-administration/addons/|addon]] que sirve para generar certificados SSL Letsencrypt. 
- 
-Para instalarlo: 
- 
-1. Conectarse al master 
- 
-  ssh master 
-   
-2. Instalar los manifiestos de la [[https://github.com/jetstack/cert-manager/releases/|versión]] marcada como 'Latest release', en este ejemplo la 'v1.8.1': 
- 
-  kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.8.1/cert-manager.yaml 
-  kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.8.1/cert-manager.crds.yaml 
- 
-3. Probar 
- 
-3.1. Ejecutar: 
- 
-  watch -n 5 kubectl get pods --namespace cert-manager 
- 
-Hastq que el pod 'webhook' esté corriendo. Salida esperada similar a: 
- 
-<code> 
-NAME                                       READY   STATUS    RESTARTS   AGE 
-cert-manager-7dd5854bb4-zz8wj              1/1     Running            71s 
-cert-manager-cainjector-64c949654c-j7s9h   1/    Running            71s 
-cert-manager-webhook-6b57b9b886-9ghg5      1/1     Running            71s 
-</code> 
- 
-Suele tardar menos de un minuto. 
- 
-3.2. Crear: 
- 
-  * Namespace 
-  * Issuer 
-  * Certificate 
- 
-Ejecutar: 
- 
-<code> 
-cat <<EOF > test-resources.yaml 
-apiVersion: v1 
-kind: Namespace 
-metadata: 
-  name: cert-manager-test 
---- 
-apiVersion: cert-manager.io/v1 
-kind: Issuer 
-metadata: 
-  name: test-selfsigned 
-  namespace: cert-manager-test 
-spec: 
-  selfSigned: {} 
---- 
-apiVersion: cert-manager.io/v1 
-kind: Certificate 
-metadata: 
-  name: selfsigned-cert 
-  namespace: cert-manager-test 
-spec: 
-  dnsNames: 
-    - example.com 
-  secretName: selfsigned-cert-tls 
-  issuerRef: 
-    name: test-selfsigned 
-EOF 
-</code> 
- 
-3.3. Cargar el manifiesto creado en el paso anterior. 
- 
-**IMPORTANTE**: esperar un par de minitos desde el paso anterior, o de otra forma podemos obtener un error: 
- 
-  x509: certificate signed by unknown authority 
- 
-Yo no fui capaz de resolver ese error. 
- 
-Ejecutar: 
- 
-  kubectl apply -f test-resources.yaml 
- 
-3.4. Ver el certificado. Ejecutar: 
- 
-  kubectl describe certificate -n cert-manager-test | grep Message 
- 
-Resultado esperado similar a: 
- 
-  Message:               Certificate is up to date and has not expired 
- 
-3.5. Limpieza. Ejecutar: 
- 
-  kubectl delete -f test-resources.yaml 
- 
-====== Issuer ====== 
- 
-Se trata de un [[https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/|custom resource]] creado durante el [[informatica:linux:docker:kubernetes#cert_manager|proceso de instalación]] del [[https://kubernetes.io/docs/concepts/cluster-administration/addons/|Addon]] llamado [[https://github.com/jetstack/cert-manager/|cert-manager]]. 
- 
-Requisitos: 
- 
-  * Tener instalado [[informatica:linux:docker:kubernetes#cert_manager|cert-manager]] 
- 
-Para instalarlo: 
- 
-1. Conectarse al control plane 
- 
-  ssh k8s2 
-   
-2. Ejecutar: 
- 
-Ajustando: 
- 
-  * email. Si es del dominio 'example.com' no generará las claves tras llamar a letsencrypt 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: cert-manager.io/v1 
-kind: Issuer 
-metadata: 
-  name: letsencrypt-production 
-  namespace: default 
-spec: 
-  acme: 
-    # Staging 
-    #server: https://acme-staging-v02.api.letsencrypt.org/directory 
-    # Production 
-    server: https://acme-v02.api.letsencrypt.org/directory 
-    # Email address used for ACME registration 
-    email: letsencrypt@example.com 
-    # Name of a secret used to store the ACME account private key 
-    privateKeySecretRef: 
-      name: letsencrypt-production 
-    # Enable the HTTP-01 challenge provider 
-    solvers: 
-    # An empty 'selector' means that this solver matches all domains 
-    - selector: {} 
-      http01: 
-        ingress: 
-          class: nginx 
-EOF 
-</code> 
- 
-En este ejemplo estamos usando la API de producción de letsencrypt: 
- 
-https://acme-v02.api.letsencrypt.org/directory 
- 
-Pero podemos usar si queremos la de staging: 
- 
-https://acme-staging-v02.api.letsencrypt.org/directory 
- 
-3. Comprobar. Ejecutar: 
- 
-  watch -n 5 kubectl get issuers.cert-manager.io 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                     READY   AGE 
-letsencrypt-production   True    43h 
-</code> 
- 
-Tarda menos de un minuto. 
- 
-====== Edge router ====== 
- 
-Se trata de un servidor completamente por fuera de kubernetes, pero se menciona aquí para completar el ejemplo. 
- 
-Se encarga de escuchar peticiones en los puertos TCP 80 y TCP 443 y dirigirlas al cluster kuberntes. Los nombres DNS de los servicios con certificados SSL válidos, por ejemplo con letsencrypt, deben resolver a la IP pública del edge router. Por ejemplo "example.com" debe resolver a la IP pública del edge router, de forma que la petición finalmente le llegue al cluster de kubernetes a través del siguiente esquema: 
- 
-[Cliente] => example.com => [Edge router] => [Worker] => [Ingress route] => [Service] => [Deployment] 
- 
-Requisitos: 
- 
-  * IP pública 
-  * IP privada en el mismo rango que los nodos del clúster de kubernetes ("192.168.95.0/24" en este ejemplo) 
-  * (Opcional) Docker instalado. Usamos docker para hacer más rápido y sencillo el despliegue de nginx 
- 
-===== NodePort (usar este) ===== 
- 
-Requisitos: 
- 
-  * IPs privadas de los workers del clúster 
-  * Puertos en los que escuchan los Nodeport tanto HTTP como HTTPS. Ver paso 8 de [[informatica:linux:docker:kubernetes#nginx_nodeport_daemonset_usar_este|Nginx ingress Nodeport daemonset]] 
-  * [[https://docs.docker.com/engine/install/debian|Docker]] 
- 
-1. Acceder al servidor 
- 
-  ssh k8s1 
-   
-2. Crear el archivo 'default.conf': 
- 
-  vim default.conf 
- 
-Con el siguiente contenido: 
- 
-<code> 
-proxy_set_header X-Real-IP       $proxy_protocol_addr; 
-proxy_set_header X-Forwarded-For $remote_addr; 
-# To avoid 404. Credits: 
-# https://serverfault.com/a/407983/570054 
-proxy_set_header Host $host:$server_port; 
- 
-server { 
-        listen 80; 
-        location / { 
-                proxy_pass http://k8s; 
-        } 
-        error_page 404 /404.html; 
-        location = /404.html { 
-                root /usr/share/nginx/html/; 
-                internal; 
-        } 
-} 
-</code> 
- 
-3. Crear el archivo 'nginx.conf': 
- 
-  vim nginx.conf 
- 
-Con el siguiente contenido: 
- 
-<code> 
-user  nginx; 
-worker_processes  1; 
- 
-error_log  /var/log/nginx/error.log warn; 
-pid        /var/run/nginx.pid; 
- 
- 
-events { 
-    worker_connections  1024; 
-} 
- 
- 
-http { 
-    include       /etc/nginx/mime.types; 
-    default_type  application/octet-stream; 
- 
-    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' 
-                      '$status $body_bytes_sent "$http_referer" ' 
-                      '"$http_user_agent" "$http_x_forwarded_for"'; 
- 
-    access_log  /var/log/nginx/access.log  main; 
- 
-    sendfile        on; 
-    #tcp_nopush     on; 
- 
-    keepalive_timeout  65; 
- 
-    #gzip  on; 
- 
-    include /etc/nginx/conf.d/*.conf; 
- 
- upstream k8s { 
- # IP: kubernetes node private IP. Port: nginx ingress controller in NodePort port for HTTP traffic 
- server 192.168.95.74:31104; 
- server 192.168.95.75:31104; 
- server 192.168.95.76:31104; 
- } 
-} 
- 
-stream { 
- 
-    server { 
-        listen 443; 
- 
-        proxy_pass k8s_https; 
- # Requires 'use-proxy-protocol' to 'true' in configmap of nginx ingress controller 
- # https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-proxy-protocol 
- proxy_protocol on; 
- 
-    } 
- 
-        upstream k8s_https { 
-                # IP: kubernetes node private IP. Port: nginx ingress controller in NodePort port for HTTP traffic 
-                server 192.168.95.74:30893; 
-                server 192.168.95.75:30893; 
-                server 192.168.95.76:30893; 
-        } 
- 
-    log_format basic '$remote_addr - [$time_local] ' 
-                        '$status ' 
-                        ''; 
- 
-    access_log  /var/log/nginx/access.log  basic; 
- 
-} 
-</code> 
- 
-Modificar: 
- 
-  * IPs y puertos de los workers del clúster de kubernetes. En este ejemplo las IPs son: 
-    * 192.168.95.74 
-    * 192.168.95.75 
-    * 192.168.95.76 
-  * Puerto en el que escucha el servicio NodePort para las conexiones HTTP. En este ejemplo '31104' 
-  * Puerto en el que escucha el servicio NodePort para las conexiones HTTPS. En este ejemplo '30893' 
-   
-4. Arrancar el contenedor docker, ajustando la ruta a los archivos creados en el paso anterior: 
- 
-<code> 
-docker run \ 
-  --name edge-router \ 
-  --v /home/debian/nginx.conf:/etc/nginx/nginx.conf:ro \ 
-  -v /home/debian/default.conf:/etc/nginx/conf.d/default.conf \ 
-  -p 80:80 \ 
-  -p 443:443 \ 
-  -d nginx 
-</code> 
- 
-5. Probar 
- 
-Para esta prueba necesitamos los siguientes requisitos: 
- 
-  * Ingress de nginx instalado y configurado [[informatica:linux:docker:kubernetes#nginx_nodeport_con_afinitty_usar_este|así]] 
-  * Nombre DNS (en este ejemplo 'example.com') apuntando a la IP pública del servidor 
-  * [[informatica:linux:docker:kubernetes#ejemplo_ingress_con_letsencrypt|Ingress]] con letsencrypt y nombre de host 'example.com' 
- 
-5.1. Pronbar que el contenedor está corriendo: 
- 
-  docker logs -f edge-router 
- 
-5.2. Abrir un navegador y acceder a la URL configurada en el ingress route, en este ejemplo "example.com": 
- 
-  https://example.com 
-   
-Resultado esperado similar a: 
- 
-<code> 
-Hostname: deployment-whoami-6b6dc7c84-tbv7d 
-IP: 127.0.0.1 
-IP: 10.244.2.4 
-RemoteAddr: 10.244.2.8:42052 
-GET / HTTP/1.1 
-Host: example.com 
-User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 
-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
-Accept-Encoding: gzip, deflate, br 
-Accept-Language: en-US,en;q=0.5 
-Upgrade-Insecure-Requests: 1 
-X-Forwarded-For: 8.8.8.8 
-X-Forwarded-Host: example.com 
-X-Forwarded-Port: 443 
-X-Forwarded-Proto: https 
-X-Real-Ip: 8.8.8.8 
-X-Request-Id: 4d47507a04aa3e8ab3647c5e03983e41 
-X-Scheme: https 
-</code> 
- 
-Donde '8.8.8.8' es la IP pública desde donde hemos lanzado la petición. 
- 
-===== LoadBalancer ===== 
- 
-**AVISO**: funciona, pero no devuelve la IP origen. Igual cambiando algo se puede conseguir 
- 
- 
-En nuestro caso se van a redirigir todas las peticiones no a un servicio de tipo 'NodePort' como se menciona [[https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#using-a-self-provisioned-edge|aquí]], sino a la "EXTERNAL-IP" (en nuestro ejemplo una IP privada, la '192.168.95.68') de un servicio de tipo 'LoadBalancer', [[informatica:linux:docker:kubernetes#nginx|este]]. 
- 
-Esquema: 
- 
-[Cliente DMZ] => https://example.com => [Edge router] => http://192.168.95.68 => [ingress-nginx-controller] => [ingress-whoami] => [service-whoami] => [deployment-whoami] 
- 
-Si lo hiciéramos con servicios de tipo [[https://kubernetes.io/docs/concepts/services-networking/service|NodePort]] deberíamos estar continuamente tocando la configuración del edge router para especificar cada puerto, pues cada servicio tomaría un puerto distinto. 
- 
-Nuestro edge router: 
- 
-  * Tiene una IP pública. 
-  * Tiene una IP privada del rango de IPs del [[informatica:linux:docker:kubernetes#layer_2|pool de IPs]] de metallb. 
-  * Tiene instalado docker (porque el servicio nginx va a ser creado mediante un contenedor). 
- 
-Instalación: 
- 
-1. Conectarnos al edge router 
- 
-  ssh edgerouter 
-   
-2. Crear el siguiente archivo, ajustando la IP a la que le ha sido asignada al [[informatica:linux:docker:kubernetes#nginx|servicio 'ingress-nginx-controller']] como 'EXTERNAL-IP', en este ejemplo '192.168.95.68': 
- 
-  vim nginx.conf 
-   
-Con el siguiente contenido: 
- 
-<code> 
-user  nginx; 
-worker_processes  1; 
- 
-error_log  /var/log/nginx/error.log warn; 
-pid        /var/run/nginx.pid; 
- 
- 
-events { 
-    worker_connections  1024; 
-} 
- 
- 
-http { 
-    include       /etc/nginx/mime.types; 
-    default_type  application/octet-stream; 
- 
-    log_format main '$remote_addr - [$time_local] ' 
-                        '"$request" $status $body_bytes_sent ' 
-                        '"$http_referer" "$http_user_agent"'; 
- 
-    access_log  /var/log/nginx/access.log  main; 
- 
-    sendfile        on; 
-    #tcp_nopush     on; 
- 
-    keepalive_timeout  65; 
- 
-    #gzip  on; 
- 
-    include /etc/nginx/conf.d/*.conf; 
-} 
- 
-stream { 
- 
-    map $ssl_preread_server_name $targetBackend { 
-        ~^(.*)$ 192.168.95.68:443; 
-    } 
- 
-    server { 
-        listen 443; 
- 
-        proxy_pass $targetBackend; 
-        ssl_preread on; 
-    } 
- 
-    log_format basic '$remote_addr - [$time_local] ' 
-                        '$status ' 
-                        ''; 
- 
-    access_log  /var/log/nginx/access.log  basic; 
-} 
-</code> 
- 
-3. Crear el siguiente archivo, ajustando la IP a la que le ha sido asignada al [[informatica:linux:docker:kubernetes#nginx|servicio 'ingress-nginx-controller']] como 'EXTERNAL-IP', en este ejemplo '192.168.95.68': 
- 
-  vim default.conf 
-   
-Con el siguiente contenido: 
- 
-<code> 
-proxy_set_header X-Real-IP       $proxy_protocol_addr; 
-proxy_set_header X-Forwarded-For $remote_addr; 
- 
-server { 
- listen 80; 
- location / { 
-    #proxy_pass http://192.168.95.68; 
-    proxy_pass http://php; 
- proxy_set_header Host            $host; 
- proxy_set_header X-Forwarded-For $remote_addr; 
- } 
-        error_page 404 /404.html; 
-        location = /404.html { 
-                root /usr/share/nginx/html/; 
-                internal; 
-        } 
-} 
-</code> 
- 
-4. Arrancar el contenedor, ajustando las rutas a los archivos de configuración, ejecutando: 
- 
-<code> 
-docker run \ 
-  --name reverse-proxy \ 
-  -v /home/debian/nginx.conf:/etc/nginx/nginx.conf:ro \ 
-  -v /home/debian/default.conf:/etc/nginx/conf.d/default.conf \ 
-  -p 80:80 \ 
-  -p 443:443 \ 
-  -d nginx 
-docker logs -f reverse-proxy 
-</code> 
- 
-===== Redundancia ===== 
- 
-Requisitos: 
- 
-  * Dos servidores 
-  * Cada uno de los cuales tiene una IP pública 
-  * Existe una tercera IP pública ("virtual" o "flotante") 
-  * Las 3 IPs públicas (las de cada servidor y la flotante) están en la misma subred 
- 
-1. Instalar los dos servidores [[informatica:linux:docker:kubernetes#nodeport_usar_este|con NodePort]] 
- 
-2. Instalar y configurar en ambos servidores [[informatica:linux:keepalived|Keepalived]] 
-====== Ingress ====== 
- 
-Según su propia [[https://kubernetes.io/docs/concepts/services-networking/ingress|definición]]: 
- 
- 
-  An API object that manages external access to the services in a cluster, typically HTTP. 
- 
-===== Nginx ===== 
- 
-[[https://kubernetes.github.io/ingress-nginx|Ingress nginx]] 
- 
-==== Nginx NodePort daemonset (usar este) ==== 
- 
-Con este procedimiento podremos obtener la IP de origen en los pods, con las otras aproximaciones todavía no lo he conseguido. 
- 
-Características: 
- 
-  * Usaremos [[https://kubernetes.io/docs/concepts/services-networking/service/#nodeport|NodePort]], de forma que todos los nodos, incluidos el master, expondrán a la DMZ un puerto para HTTP (el mismo en todos los nodos), y otro para HTTPS. 
-  * Usaremos daemonset, de forma que por cada nodo worker, en este ejemplo 3, se creará un pod. 
-  * Usaremos "externalTrafficPolicy: Local" para preservar la IP origen. La explicación detallada se puede encontrar [[https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/|aquí]] y [[https://kubernetes.io/docs/tutorials/services/source-ip/|aquí]] 
- 
-1. Conectarse al master 
- 
-  ssh k8s2 
- 
-2. Obtener el [[https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal-clusters|archivo]]: 
- 
-  wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml 
-   
-3. Forzar que los pods solo envien tráfico a los deployments, según su ingress route, que estén corriendo en el mismo worker node. Añadir 'spec.externalTrafficPolicy: Local'. Editar: 
- 
-  vim deploy.yaml 
-   
-Y cambiar: 
- 
-<code> 
-# Source: ingress-nginx/templates/controller-service.yaml 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  annotations: 
-  labels: 
-    helm.sh/chart: ingress-nginx-3.30.0 
-    app.kubernetes.io/name: ingress-nginx 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/version: 0.46.0 
-    app.kubernetes.io/managed-by: Helm 
-    app.kubernetes.io/component: controller 
-  name: ingress-nginx-controller 
-  namespace: ingress-nginx 
-spec: 
-</code> 
- 
-Por: 
- 
-<code> 
-# Source: ingress-nginx/templates/controller-service.yaml 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  annotations: 
-  labels: 
-    helm.sh/chart: ingress-nginx-3.30.0 
-    app.kubernetes.io/name: ingress-nginx 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/version: 0.46.0 
-    app.kubernetes.io/managed-by: Helm 
-    app.kubernetes.io/component: controller 
-  name: ingress-nginx-controller 
-  namespace: ingress-nginx 
-spec: 
-  # this setting is t make sure the source IP address is preserved. 
-  externalTrafficPolicy: Local 
-</code> 
- 
-4. **AVISO** En la última versión ya es "NodePort", pero dejo esto documentado por si Ascaso 
- 
-Reemplazar para el tipo de balanceo para el servicio 
- 
-  vim deploy.yml 
-   
-Y cambiar: 
- 
-<code> 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  labels: 
-    app.kubernetes.io/component: controller 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/name: ingress-nginx 
-    app.kubernetes.io/part-of: ingress-nginx 
-    app.kubernetes.io/version: 1.2.0 
-  name: ingress-nginx-controller 
-  namespace: ingress-nginx 
-spec: 
-  externalTrafficPolicy: Local 
-  ports: 
-  - appProtocol: http 
-    name: http 
-    port: 80 
-    protocol: TCP 
-    targetPort: http 
-  - appProtocol: https 
-    name: https 
-    port: 443 
-    protocol: TCP 
-    targetPort: https 
-  selector: 
-    app.kubernetes.io/component: controller 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/name: ingress-nginx 
-  type: LoadBalancer 
-</code> 
- 
-Por: 
- 
-<code> 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  labels: 
-    app.kubernetes.io/component: controller 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/name: ingress-nginx 
-    app.kubernetes.io/part-of: ingress-nginx 
-    app.kubernetes.io/version: 1.2.0 
-  name: ingress-nginx-controller 
-  namespace: ingress-nginx 
-spec: 
-  externalTrafficPolicy: Local 
-  ports: 
-  - appProtocol: http 
-    name: http 
-    port: 80 
-    protocol: TCP 
-    targetPort: http 
-  - appProtocol: https 
-    name: https 
-    port: 443 
-    protocol: TCP 
-    targetPort: https 
-  selector: 
-    app.kubernetes.io/component: controller 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/name: ingress-nginx 
-  # anyadido 
-  #type: LoadBalancer 
-  type: NodePort 
-</code> 
- 
-5. Instalar. Ejecutar: 
- 
-  kubectl apply -f deploy.yaml 
- 
-6. Probar 
- 
-6.1. Ejecutar: 
- 
-  kubectl get pods \ 
-  -n ingress-nginx \ 
-  -l app.kubernetes.io/name=ingress-nginx \ 
-  -o wide \ 
-  --watch 
- 
-Resultado esperado similar a: 
- 
-<code> 
-ingress-nginx-controller-55bc4f5576-78rmm   1/    Running              45h   10.244.5.3   k8s7     <none>           <none> 
-ingress-nginx-controller-55bc4f5576-f27v5   1/    Running              45h   10.244.3.3   k8s5     <none>           <none> 
-ingress-nginx-controller-55bc4f5576-v7f6h   1/    Running              45h   10.244.4.3   k8s6     <none>           <none> 
-ingress-nginx-controller-6f7c7fb6f8-xvzcs   0/    Pending              45h   <none>       <none>   <none>           <none> 
-</code> 
- 
-**IMPORTANTE**: debe haber al menos una réplica por cada node worker. 
- 
-6.2. Verificar que efectivamente cada servicio (ingress controller) corre en un node worker distinto. Ejecutar: 
- 
-  kubectl get nodes 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE     VERSION 
-k8s2   Ready    control-plane,master   2d19h   v1.21.1 
-k8s3   Ready    control-plane,master   2d19h   v1.21.1 
-k8s4   Ready    control-plane,master   2d18h   v1.21.1 
-k8s5   Ready    <none>                 46h     v1.21.1 
-k8s6   Ready    <none>                 46h     v1.21.1 
-k8s7   Ready    <none>                 45h     v1.21.1 
-</code> 
- 
-Tenemos pues tres workers: 
- 
-  * k8s5 
-  * k8s6 
-  * k8s7 
- 
-Todo correcto, cada worker tiene al menos un pod corriendo. 
- 
-6.3. Comprobar la versión. Ejecutar: 
- 
-<code>   
-POD_NAMESPACE=ingress-nginx 
-POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}') 
-kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version 
-</code> 
- 
-Resultado esperado similar a: 
- 
-<code> 
-------------------------------------------------------------------------------- 
-NGINX Ingress controller 
-  Release:       v0.46.0 
-  Build:         6348dde672588d5495f70ec77257c230dc8da134 
-  Repository:    https://github.com/kubernetes/ingress-nginx 
-  nginx version: nginx/1.19.6 
- 
-------------------------------------------------------------------------------- 
-</code> 
- 
-7. Obtener los puertos expuestos. Esto es necesario para instalar y configura el edge router. Ejecutar: 
- 
-  kubectl get services -n ingress-nginx --field-selector metadata.name=ingress-nginx-controller 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                       TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE 
-ingress-nginx-controller   NodePort   10.110.176.170   <none>        80:31104/TCP,443:30893/TCP   55m 
-</code> 
- 
-Por lo tanto en este ejemplo: 
- 
-  * HTTP => 31104 
-  * HTTPS => 30893 
- 
-8. **AVISO** Este paso está deprecated. Solo hay que hacerlo si se va a usar un edge router con balanceador capa 7. NO hay que hacerlo, eso lo hice en un primer intento. El balanceador que hay que poner en el edge router tiene que ser capa 4, por tanto NO hay que habilitar proxy protocol. 
- 
-[[informatica:linux:docker:kubernetes#editar_configmaps_para_habilitar_use-proxy-protocol|Editar ConfigMaps para habilitar 'use-proxy-protocol']] 
- 
-=== Actualizar nginx ingress controller === 
- 
-1. Comprobar si es posible una actualización 
- 
-1.1. Obtemer la versión actual del nginx ingress controller 
- 
-  kubectl describe daemonsets.apps -n ingress-nginx ingress-nginx-controller  | grep vers 
- 
-Resultado esperado similar a: 
- 
-<code> 
-                        app.kubernetes.io/version=1.2.0 
-</code> 
- 
-Por tanto la versión actual es "1.2.0" 
- 
-1.2. Determinar si hay una nueva versión: 
- 
-https://github.com/kubernetes/ingress-nginx#changelog 
- 
-La hay, "v1.2.1", compatible con las siguientes versiones de kubectl: 
- 
-``` 
-1.23, 1.22, 1.21, 1.20, 1.19 
-``` 
- 
-1.3. Determinar la versión actual de kubectl: 
- 
-  kubectl version --short 
- 
-Resultado esperado similar a: 
- 
-<code> 
-Client Version: v1.24.2 
-Kustomize Version: v4.5.4 
-Server Version: v1.24.2 
-</code> 
- 
-Tenemos kubectl versión "1.24.x", que está por encima de "1.23", asumo que podemos ir hacia delante. 
- 
-2. Actualizar la versión de la imagen del daemonset (recordad que cambiamos el "Deployment" por "Daemonset") 
- 
-2.1. Comprobar que la imagen de docker (tag) existe, y obtener el SHA256, todo esto desde la máquina local 
- 
-  docker pull registry.k8s.io/ingress-nginx/controller:v1.2.1 
- 
-Resultado esperado similar a: 
- 
-<code> 
-v1.2.1: Pulling from ingress-nginx/controller 
-8663204ce13b: Pull complete  
-897a18b2d257: Pull complete  
-3cb02f360cf3: Pull complete  
-2b63816a7692: Pull complete  
-d61ce16aa3b6: Pull complete  
-4391833fbf2c: Pull complete  
-4f4fb700ef54: Pull complete  
-bb397308bcd5: Pull complete  
-803395581751: Pull complete  
-153d402a7263: Pull complete  
-c815f058cf7b: Pull complete  
-a872540e4aca: Pull complete  
-4972574251d0: Pull complete  
-30197fe775a6: Pull complete  
-b059831ea274: Pull complete  
-Digest: sha256:5516d103a9c2ecc4f026efbd4b40662ce22dc1f824fb129ed121460aaa5c47f8 
-Status: Downloaded newer image for registry.k8s.io/ingress-nginx/controller:v1.2.1 
-registry.k8s.io/ingress-nginx/controller:v1.2.1 
-</code> 
- 
-La imagen existe, y el SHA256 es: 
- 
-  sha256:5516d103a9c2ecc4f026efbd4b40662ce22dc1f824fb129ed121460aaa5c47f8 
- 
-2.2. Limpieza 
- 
-  docker image rm registry.k8s.io/ingress-nginx/controller:v1.2.1 
- 
-3. (k8s server) Actualizar la versión de la imagen del daemonset (paso 1.2.) y el SHA256 (paso 2.1.) 
- 
-3.1. Actualizar el daemonset: 
- 
-<code> 
-kubectl set image daemonsets/ingress-nginx-controller \ 
-  controller=registry.k8s.io/ingress-nginx/controller:v1.2.1@sha256:5516d103a9c2ecc4f026efbd4b40662ce22dc1f824fb129ed121460aaa5c47f8 \ 
-  -n ingress-nginx 
-</code> 
- 
-3.2. Verificar: 
- 
-  kubectl get pods -n ingress-nginx -o wide 
- 
-ETA: 3' 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                                   READY   STATUS      RESTARTS   AGE    IP            NODE 
-   NOMINATED NODE   READINESS GATES 
-ingress-nginx-admission-create-zxqvn   0/    Completed            139m   10.244.0.75   k8s1 
-   <none>           <none> 
-ingress-nginx-admission-patch-c9t4w    0/1     Completed            139m   10.244.0.74   k8s1 
-   <none>           <none> 
-ingress-nginx-controller-6thbj         1/    Running              2m8s   10.244.0.77   k8s1 
-   <none>           <none> 
-ingress-nginx-controller-tv9zd         1/    Running              63s    10.244.1.16   k8s3 
-   <none>           <none> 
-ingress-nginx-controller-zgfw5         1/    Running              95s    10.244.2.21   k8s2 
-   <none>           <none> 
-</code> 
-==== Nginx LoadBalancer ==== 
- 
-En este ejemplo asumimos que tenemos un LoadBalancer habilitado, porque hemos instalado [[informatica:linux:docker:kubernetes#layer_2|un balanceador de carga por software]]. 
- 
-1. Conectarse al master: 
- 
-  ssh master 
- 
-2. Obtener el manifiesto: 
- 
-  wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.46.0/deploy/static/provider/baremetal/deploy.yaml 
-   
-3. Editarlo: 
- 
-  vim deploy.yaml 
-   
-Y substituir "type: NodePort": 
- 
-<code> 
-# Source: ingress-nginx/templates/controller-service.yaml 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  annotations: 
-  labels: 
-    helm.sh/chart: ingress-nginx-3.30.0 
-    app.kubernetes.io/name: ingress-nginx 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/version: 0.46.0 
-    app.kubernetes.io/managed-by: Helm 
-    app.kubernetes.io/component: controller 
-  name: ingress-nginx-controller 
-  namespace: ingress-nginx 
-spec: 
-  type: NodePort 
-</code> 
- 
-por "type: LoadBalancer": 
- 
-<code> 
-# Source: ingress-nginx/templates/controller-service.yaml 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  annotations: 
-  labels: 
-    helm.sh/chart: ingress-nginx-3.30.0 
-    app.kubernetes.io/name: ingress-nginx 
-    app.kubernetes.io/instance: ingress-nginx 
-    app.kubernetes.io/version: 0.46.0 
-    app.kubernetes.io/managed-by: Helm 
-    app.kubernetes.io/component: controller 
-  name: ingress-nginx-controller 
-  namespace: ingress-nginx 
-spec: 
-  type: LoadBalancer 
-</code> 
- 
-4. Instalar el manifiesto: 
- 
-  kubectl apply -f deploy.yaml 
- 
-5. Probar. 
- 
-5.1. Ejecutar: 
- 
-  kubectl get pods -n ingress-nginx \ 
-  -l app.kubernetes.io/name=ingress-nginx --watch 
- 
-Hasta que el 'controller' esté corriendo: 
- 
-<code> 
-NAME                                        READY   STATUS      RESTARTS   AGE 
-ingress-nginx-admission-create-jw7f8        0/1     Completed            4d18h 
-ingress-nginx-admission-patch-b99q4         0/    Completed            4d18h 
-ingress-nginx-controller-55bc4f5576-77bh8   1/    Running              4d18h 
-</code> 
- 
-5.2. Ejecutar: 
- 
-  kubectl get services --field-selector metadata.name=ingress-nginx-controller -n ingress-nginx  
- 
-El resultado esperado es similar a: 
- 
-<code> 
-NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE 
-ingress-nginx-controller   LoadBalancer   10.109.52.158   192.168.95.68   80:31936/TCP,443:31079/TCP   4d18h 
-</code> 
- 
-Vemos que ha tomado una 'EXTERNAL-IP' del pool de direcciones definida en el pool de direcciones de [[informatica:linux:docker:kubernetes#layer_2|metallb]]. 
- 
-5.3. Ejecutar: 
- 
-<code> 
-POD_NAMESPACE=ingress-nginx 
-POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}') 
-kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version 
-</code> 
-   
-Resultado esperado similar a: 
- 
-<code> 
-------------------------------------------------------------------------------- 
-NGINX Ingress controller 
-  Release:       v0.46.0 
-  Build:         6348dde672588d5495f70ec77257c230dc8da134 
-  Repository:    https://github.com/kubernetes/ingress-nginx 
-  nginx version: nginx/1.19.6 
- 
-------------------------------------------------------------------------------- 
-</code> 
- 
-Por tanto en este ejemplo las peticiones que lleguen a la IP "192.168.95.68" serán enrutaddas en función de las reglas que se definan en el ingress (ver más adelante). 
- 
-===== Ejemplo ingress ===== 
- 
-Este ejemplo requiere: 
- 
-  * Un [[informatica:linux:docker:kubernetes#service|Service]] cuyo 'metadata.name' sea 'service-whoami' 
-  * Tener instalado un [[https://kubernetes.io/docs/concepts/services-networking/ingress/|Ingress]] controller, como [[http://wiki.legido.com/doku.php?id=informatica:linux:docker:kubernetes#nginx|Nginx]] 
- 
-1. Conectarse al master 
- 
-  ssh master 
-   
-2. Ejecutar: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: networking.k8s.io/v1 
-kind: Ingress 
-metadata: 
-  annotations: 
-    kubernetes.io/ingress.class: nginx 
-  name: ingress-whoami 
-spec: 
-  rules: 
-  - host: example.com 
-    http: 
-      paths: 
-      - backend: 
-          service: 
-            name: service-whoami 
-            port: 
-              number: 80 
-        path: / 
-        pathType: Prefix 
-EOF 
-</code> 
- 
-3. Verificar 
- 
-  kubectl get ingress --field-selector metadata.name=ingress-whoami 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME             CLASS    HOSTS           ADDRESS         PORTS     AGE 
-ingress-whoami   <none>   k8s.kedu.coop   192.168.95.73   80, 443   42h 
-</code> 
- 
-Con esta configuración: 
- 
-  * Las peticiones que le lleguen a la IP que tiene le servicio 'ingress-nginx-controller', en este ejemplo la '192.168.95.68' pasarán por las reglas definidas en este 'ingress' 
-  * Si se recibe una petición HTTP en el puerto TCP 80 o TCP 443 con un 'header' con clave 'Host' y valor 'example.com' se enrutará al servicio con nombre 'service-whoami' 
- 
-===== Ejemplo ingress con letsencrypt ===== 
- 
-Este ejemplo requiere: 
- 
-  * Un [[informatica:linux:docker:kubernetes#service|Service]] cuyo 'metadata.name' sea 'service-whoami' 
-  * Tener instalado un [[https://kubernetes.io/docs/concepts/services-networking/ingress/|Ingress]] controller, como [[http://wiki.legido.com/doku.php?id=informatica:linux:docker:kubernetes#nginx|Nginx]] 
-  * Tener instalado el [[https://kubernetes.io/docs/concepts/cluster-administration/addons/|addon]] [[http://wiki.legido.com/doku.php?id=informatica:linux:docker:kubernetes#cert_manager|Cert-manager]] 
-  * Tener instalado un [[informatica:linux:docker:kubernetes#issuer|issuer]] llamado 'letsencrypt-production' 
-  * [[informatica:linux:docker:kubernetes#edge_router|Edge router]]: 
-    * Nginx 
-    * Reverse proxy a ingress-nginx-controller 'EXTERNAL-IP' 
-    * Que no termine las conexiones SSL, es decr, que redirija en capa 4 lo que le llegue a TCP 443 
-  * Un nombre DNS, en este ejemplo "example.com", que apunte a la IP pública del edge router, en este ejemplo '8.8.8.8' 
- 
-1. Conectarse al master: 
- 
-  ssh master 
-   
-2. Ejecutar: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: networking.k8s.io/v1 
-kind: Ingress 
-metadata: 
-  annotations: 
-    # Should match 'Issuer.metadata.name' 
-    cert-manager.io/issuer: letsencrypt-production 
-    kubernetes.io/ingress.class: nginx 
-  name: ingress-whoami 
-spec: 
-  rules: 
-  - host: example.com 
-    http: 
-      paths: 
-      - path: / 
-        pathType: Prefix 
-        backend: 
-          service: 
-            name: service-whoami 
-            port: 
-              number: 80 
-  tls: 
-  - hosts: 
-    - example.com 
-    secretName: quickstart-example-tls 
-EOF 
-</code> 
- 
-3. Comprobar 
- 
-3.1. Ejecutar: 
- 
-  watch -n 5 kubectl get certificate 
- 
-El resultado esperado, tras aproximadamente un minuto, es: 
- 
-<code> 
-NAME                     READY   SECRET                   AGE 
-quickstart-example-tls   True    quickstart-example-tls   63s 
-</code> 
- 
-3.2. Se ha creado un secret llamado 'quickstart-example-tls'. Para ver lo que contiene ejecutar: 
- 
-  kubectl describe secrets quickstart-example-tls 
-   
-Salida esperada similar a: 
- 
-<code> 
-Name:         quickstart-example-tls 
-Namespace:    default 
-Labels:       <none> 
-Annotations:  cert-manager.io/alt-names: k8s.kedu.coop 
-              cert-manager.io/certificate-name: quickstart-example-tls 
-              cert-manager.io/common-name: k8s.kedu.coop 
-              cert-manager.io/ip-sans:  
-              cert-manager.io/issuer-group: cert-manager.io 
-              cert-manager.io/issuer-kind: Issuer 
-              cert-manager.io/issuer-name: letsencrypt-production 
-              cert-manager.io/uri-sans:  
- 
-Type:  kubernetes.io/tls 
- 
-Data 
-==== 
-tls.crt:  5587 bytes 
-tls.key:  1675 bytes 
-</code> 
- 
-3.3. Abrir un navegador e ir a: 
- 
-https://example.com 
- 
-Debería aparecer el contenido servido por el [[informatica:linux:docker:kubernetes#deployment|deployment]] 
- 'deployment-whoami' con un certificado SSL válido. 
- 
-===== Ejemplo ingress con modsecurity ===== 
- 
-Requisitos: 
- 
-  * Crear un servicio "service-echoserver" 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: networking.k8s.io/v1 
-kind: Ingress 
-metadata: 
-  annotations: 
-    # https://awkwardferny.medium.com/enabling-modsecurity-in-the-kubernetes-ingress-nginx-controller-111f9c877998 
-    nginx.ingress.kubernetes.io/enable-modsecurity: "true" 
-    nginx.ingress.kubernetes.io/modsecurity-snippet: | 
-      SecRuleEngine On 
-      SecRequestBodyAccess On 
-      SecAuditEngine RelevantOnly 
-      SecAuditLogParts ABIJDEFHZ 
-      SecAuditLog /var/log/modsec_audit.log 
-      SecRule REQUEST_HEADERS:User-Agent \"fern-scanner\" \"log,deny,id:107,status:403,msg:\'Fern Scanner Identified\'\" 
-  name: ingress-echoserver 
-spec: 
-  rules: 
-  - host: example.com 
-    http: 
-      paths: 
-      - path: / 
-        pathType: Prefix 
-        backend: 
-          service: 
-            name: service-echoserver 
-            port: 
-              number: 80 
-EOF 
-</code> 
- 
-Probar: 
- 
-  curl http://example.com -k -H "user-agent: fern-scanner" 
- 
-Respuesta esperada: 403 
-====== ConfigMaps ====== 
- 
-[[https://kubernetes.io/docs/concepts/configuration/configmap/|ConfigMaps]] 
- 
-===== Editar ConfigMaps para habilitar 'use-proxy-protocol' ===== 
- 
-**IMPORTANTE**: si habilitamos proxy protocol mediante configmap lo haremos tanto para HTTP como para HTTPS. Eso quiere decir que el "edge router" que esté por delante del cluster de kubernetes debe tener esos protocolos activados. En el ejemplo que hay en esta página NO lo está (en el edge router) para HTTP, pero sigue funcionando porque la aplicación (whoami) redirige el tráfico de HTTP a HTTPS. Pero si se hace con curl (está documentado en esta página) fallará. Para que funcione (el curl), tal y como está documentado, habrá que deshabilitar momentáneamente proxy protocol, hacer la prueba con curl, y volver a habilitar. Ver [[https://github.com/kubernetes/ingress-nginx/issues/7194|7194]] 
- 
-En este ejemplo: 
- 
-  * Vamos a editar un ConfigMap 
-  * Vamos a ver los cambios que se aplican inmediatamente en el pod 
- 
-1. Identificar el ConfigMap. En nuestro caso queremos modificar [[https://github.com/kubernetes/ingress-nginx/blob/master/deploy/static/provider/baremetal/deploy.yaml#L26-L39 | este]] 'ConfigMap', por lo que filtramos por el 'Namespace' llamado 'ingress-controller': 
- 
-  kubectl get configmaps -n ingress-nginx 
-   
-Resultado esperado similar a: 
- 
-<code> 
-NAME                              DATA   AGE 
-ingress-controller-leader-nginx        5d 
-ingress-nginx-controller          0      5d 
-kube-root-ca.crt                  1      5d 
-</code> 
- 
-En nuestro caso es [[https://github.com/kubernetes/ingress-nginx/blob/master/deploy/static/provider/baremetal/deploy.yaml#L37 | ingress-nginx-controller ]]. 
- 
-2. Editamos el 'ConfigMap': 
- 
-  kubectl edit configmaps -n ingress-nginx ingress-nginx-controller 
- 
-Vamos a seguir [[https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap | estas instrucciones]], por lo que le vamos a añadir una sección 'data' que incluya [[https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-proxy-protocol|use-proxy-protocol]]: 
- 
-<code> 
-data: 
-  use-proxy-protocol: "true" 
-</code> 
- 
-3. Comprobar 
- 
-3.1. Buscar el pod que usa ese 'ConfigMap'. En este caso hacemos un poco de atajo: 
- 
-  kubectl get pods -n ingress-nginx 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                                        READY   STATUS      RESTARTS   AGE 
-ingress-nginx-admission-create-glxnx        0/1     Completed            12h 
-ingress-nginx-admission-patch-njb2p         0/    Completed            12h 
-ingress-nginx-controller-598dd75597-lkpp8   1/    Running              138m 
-ingress-nginx-controller-598dd75597-s9kwg   1/    Running              137m 
-</code> 
- 
-3.2. Ver los logs 
- 
-  kubectl logs -f -n ingress-nginx ingress-nginx-controller-598dd75597-lkpp8  
- 
-Resultado similar a: 
- 
-<code> 
-I0520 15:47:25.546777       9 event.go:282] Event(v1.ObjectReference{Kind:"ConfigMap", Namespace:"ingress-nginx", Name:"ingress-nginx-controller", UID:"e84d9bc4-bee4-47f0-a687-bac67a95c8d0", APIVersion:"v1", ResourceVersion:"69461", FieldPath:""}): type: 'Normal' reason: 'UPDATE' ConfigMap ingress-nginx/ingress-nginx-controller 
-I0520 15:47:25.549723       9 controller.go:146] "Configuration changes detected, backend reload required" 
-I0520 15:47:25.618572       9 controller.go:163] "Backend successfully reloaded" 
-I0520 15:47:25.623095       9 event.go:282] Event(v1.ObjectReference{Kind:"Pod", Namespace:"ingress-nginx", Name:"ingress-nginx-controller-598dd75597-lkpp8", UID:"eda84159-3520-48be-8220-c4e0491afb07", APIVersion:"v1", ResourceVersion:"60184", FieldPath:""}): type: 'Normal' reason: 'RELOAD' NGINX reload triggered due to a change in configuration 
-</code> 
- 
-3.3. Comprobar si se ha añadido un setting [[https://nginx.org/en/docs/http/ngx_http_core_module.html?&_ga=2.124121161.2132845062.1621488296-1101867539.1620734415#var_proxy_protocol_addr|proxy_protocol]] por algún lado. Ejecutar: 
- 
-  kubectl exec -ti -n ingress-nginx ingress-nginx-controller-598dd75597-lkpp8 -- cat /etc/nginx/nginx.conf | grep proxy_protocol | grep listen 
- 
-Salida esperada similar a: 
- 
-<code> 
- listen 80 proxy_protocol default_server reuseport backlog=511 ; 
- listen 443 proxy_protocol default_server reuseport backlog=511 ssl http2 ; 
- listen 80 proxy_protocol ; 
- listen 443 proxy_protocol ssl http2 ; 
-</code> 
- 
-Por tanto el 'ConfigMap' ha funcionado. 
-====== Desconectar nodo ====== 
- 
-{{https://kubernetes.io/images/docs/kubectl_drain.svg}} 
- 
-===== Worker ===== 
- 
-1. Conectarnos a un control plane 
- 
-  ssh k8s2 
-   
-2. Listar nodos: 
- 
-  kubectl get nodes 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE     VERSION 
-k8s2   Ready    control-plane,master   5d14h   v1.21.1 
-k8s3   Ready    control-plane,master   5d14h   v1.21.1 
-k8s4   Ready    control-plane,master   5d14h   v1.21.1 
-k8s5   Ready    <none>                 4d18h   v1.21.1 
-k8s6   Ready    <none>                 4d17h   v1.21.1 
-k8s7   Ready    <none>                 4d17h   v1.21.1 
-</code> 
- 
-3. Vaciar de pods el worker node: 
- 
-  kubectl drain k8s7 
- 
-Em este caso nos aparece el siguiente error: 
- 
-<code> 
-node/k8s7 cordoned 
-error: unable to drain node "k8s7", aborting command... 
- 
-There are pending nodes to be drained: 
- k8s7 
-error: cannot delete DaemonSet-managed Pods (use --ignore-daemonsets to ignore): kube-system/kube-flannel-ds-n47nd, kube-system/kube-proxy-cghwq 
-</code> 
- 
-4. Reintentar el comando anterior añadiendo algunos parámetros: 
- 
-  kubectl drain k8s7 --ignore-daemonsets 
- 
-Resultado esperado similar a: 
- 
-<code> 
-node/k8s7 already cordoned 
-WARNING: ignoring DaemonSet-managed Pods: kube-system/kube-flannel-ds-n47nd, kube-system/kube-proxy-cghwq 
-evicting pod ingress-nginx/ingress-nginx-controller-55bc4f5576-78rmm 
-evicting pod cert-manager/cert-manager-7dd5854bb4-td892 
-evicting pod cert-manager/cert-manager-webhook-6b57b9b886-pwhhq 
-evicting pod ingress-nginx/ingress-nginx-admission-patch-t4dm6 
-pod/ingress-nginx-admission-patch-t4dm6 evicted 
-pod/cert-manager-7dd5854bb4-td892 evicted 
-pod/cert-manager-webhook-6b57b9b886-pwhhq evicted 
-pod/ingress-nginx-controller-55bc4f5576-78rmm evicted 
-node/k8s7 evicted 
-</code> 
- 
-En este punto: 
- 
-  * El worker 'k8s7' no acepta que se programen despliegues de nuevos pods 
-  * Todavía está corriendo pods de tipo [[https://kubernetes.io/docs/concepts/workloads/controllers/daemonset|DaemonSet]] 
- 
-5. (Opcional) Comprobar el estado de los nodos: 
- 
-  kubectl get nodes 
-   
-Resultado esperado similar a: 
- 
-<code> 
-NAME   STATUS                     ROLES                  AGE     VERSION 
-k8s2   Ready                      control-plane,master   5d15h   v1.21.1 
-k8s3   Ready                      control-plane,master   5d14h   v1.21.1 
-k8s4   Ready                      control-plane,master   5d14h   v1.21.1 
-k8s5   Ready                      <none>                 4d18h   v1.21.1 
-k8s6   Ready                      <none>                 4d18h   v1.21.1 
-k8s7   Ready,SchedulingDisabled   <none>                 4d17h   v1.21.1 
-</code> 
- 
-6. (Opcional) Listar los pods que todavía están corriendo en el worker 'k8s7': 
- 
-  kubectl get pods --all-namespaces -o wide | grep k8s7 
- 
-Resultado esperado similar a: 
- 
-<code> 
-kube-system     kube-flannel-ds-n47nd                       1/    Running              4d17h   192.168.95.76   k8s7     <none>           <none> 
-kube-system     kube-proxy-cghwq                            1/1     Running              4d17h   192.168.95.76   k8s7     <none>           <none> 
-</code> 
- 
-7. Eliminar el nodo 
- 
-  kubectl delete node k8s7 
- 
-Resultado esperado similar a: 
- 
-  node "k8s7" deleted 
- 
-8. Comprobar: 
- 
-kubectl get nodes -w 
- 
-Después de un minuto aproximadamente habrá desaparecido: 
- 
-<code> 
-NAME   STATUS   ROLES                  AGE     VERSION 
-k8s2   Ready    control-plane,master   5d15h   v1.21.1 
-k8s3   Ready    control-plane,master   5d15h   v1.21.1 
-k8s4   Ready    control-plane,master   5d14h   v1.21.1 
-k8s5   Ready    <none>                 4d18h   v1.21.1 
-k8s6   Ready    <none>                 4d18h   v1.21.1 
-</code> 
- 
-====== Definir tiempo pod salta a otro worker ====== 
- 
-Por defecto un pod saltará a otro worker tras **5 minutos** de que el worker esté marcado como "NotReady". 
- 
-**AVISO**: el parámetro [[https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager|pod-eviction-timeout]] parece ser que no funciona. Ver [[https://github.com/kubernetes/kubernetes/issues/74651|#74651]] y [[https://github.com/kubernetes-sigs/kubespray/issues/7112|#7112]] 
- 
-1. Conectarse al control plane 
- 
-  ssh k8s2 
-   
-2. Realizar una copia de seguridad: 
- 
-  sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml ~ 
- 
-3. Editar: 
- 
-  sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml 
- 
-Y añadir '.spec.containers.0.command': 
- 
-<code> 
-    # anyadido 
-    #- --enable-admission-plugins=NodeRestriction 
-    - --enable-admission-plugins=NodeRestriction,DefaultTolerationSeconds 
-    - --default-not-ready-toleration-seconds=30 
-    - --default-unreachable-toleration-seconds=30 
-</code> 
- 
-Ejemplo: 
- 
-<code> 
-apiVersion: v1 
-kind: Pod 
-metadata: 
-  annotations: 
-    kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.95.71:6443 
-  creationTimestamp: null 
-  labels: 
-    component: kube-apiserver 
-    tier: control-plane 
-  name: kube-apiserver 
-  namespace: kube-system 
-spec: 
-  containers: 
-  - command: 
-    - kube-apiserver 
-    - --advertise-address=192.168.95.71 
-    # anyadido 
-    - --default-not-ready-toleration-seconds=30 
-    - --default-unreachable-toleration-seconds=30 
-... 
-</code> 
- 
-4. Repetir los pasos 1 a 3 para el resto de control plane, en el caso de que haya más por ser un entorno de alta disponibilidad 
- 
-5. Comprobar 
- 
-5.1. Comprobar que todos y cada uno de los control plane tienen un pod 'kube-apiserver' corriendo: 
- 
-  watch -n 5 "kubectl get pods  --all-namespaces  | grep kube-api" 
- 
-Resultado esperado similar a: 
- 
-<code> 
-kube-system     kube-apiserver-k8s2                         1/    Running            43s 
-kube-system     kube-apiserver-k8s3                         1/    Running            5d20h 
-kube-system     kube-apiserver-k8s4                         1/    Running            5d20h 
-</code> 
- 
-5.2. Identificar los pods que corren en un worker: 
- 
-  kubectl get pods -o wide 
-   
-Resultado esperado similar a: 
- 
-<code> 
-NAME                                    READY   STATUS    RESTARTS   AGE    IP            NODE   NOMINATED NODE   READINESS GATES 
-deployment-whoami-6b6dc7c84-2swm9       1/    Running            101m   10.244.3.37   k8s5   <none>           <none> 
-</code> 
- 
-5.3. Detener el worker donde está corriendo el pod 
- 
-5.3.1. Conectarse al worker 
- 
-  ssh k8s5 
-   
-5.3.2. Apagarlo 
- 
-  sudo shutdown -h now 
-   
-5.3.3. Anotar la hora 
- 
-  date 
-   
-5.4. Monitorizar nodos 
- 
-  watch -n 5 kubectl get nodes 
- 
-Resultado esperado similar a **tras aproximadamente 30 segundos**: 
- 
-<code> 
-NAME   STATUS     ROLES                  AGE     VERSION 
-k8s2   Ready      control-plane,master   5d22h   v1.21.1 
-k8s3   Ready      control-plane,master   5d22h   v1.21.1 
-k8s4   Ready      control-plane,master   5d22h   v1.21.1 
-k8s5   NotReady   <none>                 5d2h    v1.21.1 
-k8s6   Ready      <none>                 5d1h    v1.21.1 
-</code> 
- 
-5.5. Monitorizar pods 
- 
-  watch -n 5 kubectl get pods -o wide 
- 
-Resultado esperado similar a **tras aproximadamente 60 segundos**: 
- 
-<code> 
-NAME                                    READY   STATUS        RESTARTS   AGE    IP            N 
-ODE   NOMINATED NODE   READINESS GATES 
-deployment-whoami-6b6dc7c84-2swm9       1/    Terminating            104m   10.244.3.37   k 
-8s5   <none>           <none> 
-deployment-whoami-6b6dc7c84-mrjjw       1/    Running                19s    10.244.4.31   k 
-8s6   <none>           <none> 
-</code> 
-====== LOCAL ====== 
-Bajamos código fuente de kubernetes e instalamos: 
- 
-  # git clone https://github.com/kubernetes/kubernetes 
-  # cd kubernetes 
-  # make quick-release 
- 
-Ahora para acabar de instalar hay que decir en que proveedor crea las máquinas virtuales. Lista sacada de https://get.k8s.io/ 
- 
-  Google Compute Engine [default] 
-  KUBERNETES_PROVIDER=gce 
-   
-  Google Container Engine 
-  KUBERNETES_PROVIDER=gke 
-   
-  Amazon EC2 
-  KUBERNETES_PROVIDER=aws 
-   
-  Libvirt (with CoreOS as a guest operating system) 
-  KUBERNETES_PROVIDER=libvirt-coreos 
-   
-  Microsoft Azure 
-  KUBERNETES_PROVIDER=azure-legacy 
-   
-  Vagrant (local virtual machines) 
-  KUBERNETES_PROVIDER=vagrant 
-   
-  VMWare Photon Controller 
-  KUBERNETES_PROVIDER=photon-controller 
-   
-  Rackspace 
-  KUBERNETES_PROVIDER=rackspace 
-   
-  OpenStack-Heat 
-  KUBERNETES_PROVIDER=openstack-heat 
- 
-En nuestro caso elegimos vagrant que es virtualbox. Instalamos virtualbox: 
-  # apt-get install vagrant virtualbox 
- 
-Para que nos cree las dos máquinas virtuales de virtualbox y configure kubernetes ejecutamos este script que se baja los binarios: 
-  # export KUBERNETES_PROVIDER=vagrant 
-  # wget -q -O - https://get.k8s.io | bash 
- 
-Nos dice que pongamos la ruta en el PATH y volvamos a ejecutarlo: 
-  Add '/home/jose/kubernetes/kubernetes/client/bin' to your PATH to use newly-installed binaries. 
- 
-Lo ponemos y ejecutamos: 
-  # export PATH=$PATH:/home/jose/kubernetes/kubernetes/client/bin 
-  # wget -q -O - https://get.k8s.io | bash 
- 
-Crea dos máquinas con fedora: 
-  # VBoxManage list vms 
-  "kubernetes_master_1486996921273_58743" {0cae89a8-08f2-4c48-8f02-1ca5df33c5b9} 
-  "kubernetes_node-1_1486997158445_96359" {11a27a45-3683-4173-b746-2eb111ad7915} 
- 
-La instalación acabo con: 
-<code> 
-Each machine instance has been created/updated. 
-  Now waiting for the Salt provisioning process to complete on each machine. 
-  This can take some time based on your network, disk, and cpu speed. 
-  It is possible for an error to occur during Salt provision of cluster and this could loop forever. 
-Validating master 
-.................................. 
-</code> 
-Parece que se queda colgado..... 
- 
-Lo lanzo en casa y funciona: 
-<code> 
-Each machine instance has been created/updated. 
-  Now waiting for the Salt provisioning process to complete on each machine. 
-  This can take some time based on your network, disk, and cpu speed. 
-  It is possible for an error to occur during Salt provision of cluster and this could loop forever. 
-Validating master 
-Validating node-1 
- 
-Waiting for each node to be registered with cloud provider 
-Flag --api-version has been deprecated, flag is no longer respected and will be deleted in the next release 
-Validating we can run kubectl commands. 
-No resources found. 
- 
-Kubernetes cluster is running. 
- 
-The master is running at: 
- 
-  https://10.245.1.2 
- 
-Administer and visualize its resources using Cockpit: 
- 
-  https://10.245.1.2:9090 
- 
-For more information on Cockpit, visit http://cockpit-project.org 
- 
-The user name and password to use is located in /root/.kube/config 
- 
-... calling validate-cluster 
-Found 1 node(s). 
-NAME                STATUS    AGE 
-kubernetes-node-1   Ready     38s 
-Validate output: 
-NAME                 STATUS    MESSAGE              ERROR 
-scheduler            Healthy   ok                    
-controller-manager   Healthy   ok                    
-etcd-0               Healthy   {"health": "true"   
-etcd-1               Healthy   {"health": "true"   
-Cluster validation succeeded 
-Done, listing cluster services: 
- 
-Kubernetes master is running at https://10.245.1.2 
-Heapster is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/heapster 
-KubeDNS is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/kube-dns 
-kubernetes-dashboard is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard 
-Grafana is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/monitoring-grafana 
-InfluxDB is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/monitoring-influxdb 
- 
-To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. 
- 
-Kubernetes binaries at /home/jose/kubernetes/kubernetes/cluster/ 
-You may want to add this directory to your PATH in $HOME/.profile 
-Installation successful! 
- 
-</code> 
- 
-Arrancamos el proxy y ya podemos acceder a la UI del dashboard de Kubernetes: 
-  # ./platforms/linux/amd64/kubectl proxy 
-   
-  Starting to serve on 127.0.0.1:8001 
- 
-  http://127.0.0.1:8001/ui 
- 
-  usuario: vagrant 
-  contraseña: vagrant 
- 
- 
-====== MINIKUBE ====== 
-https://kubernetes.io/docs/tasks/tools/install-minikube/ 
- 
-1. Instalamos kubectl 
- 
-https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-binary-via-curl 
- 
-  curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 
- 
-Lo ponemos en el path: 
-  chmod +x ./kubectl 
-  sudo mv ./kubectl /usr/local/bin/kubectl 
- 
-2. Instalamos minikube 
-https://github.com/kubernetes/minikube/releases 
- 
-Ojo que puede cambiar versión, id a web: 
-  curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.20.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/ 
- 
-Creamos el cluster. Primero crea la VM en virtualbox y luego la levanta y configura los servicios: 
-  # minikube start 
-   
-  Starting local Kubernetes cluster... 
-  Downloading Minikube ISO 
-  84.07 MB / 84.07 MB [==============================================] 100.00% 0s 
-  Kubectl is now configured to use the cluster. 
- 
- 
-  # vboxmanage list vms 
-   
-  "minikube" {e1e5e6f9-e3cc-437d-81f8-80a038f15ac5} 
- 
- 
-Para conectar a la VM de virtualbox llamada minikube:  
-  ssh docker@192.168.99.100 
-  docker/tcuser 
-Miramos que tiene desplegado: 
-  # kubectl get pods --all-namespaces 
-   
-  NAMESPACE     NAME                          READY     STATUS              RESTARTS   AGE 
-  kube-system   kube-addon-manager-minikube   0/      ContainerCreating            3m 
-Instalamos el dashboard: 
-  # kubectl create -f https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml 
-   
-  deployment "kubernetes-dashboard" created 
-  service "kubernetes-dashboard" created 
- 
-  # kubectl get services --all-namespaces 
-   
-  NAMESPACE     NAME                   CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE 
-  default       kubernetes             10.0.0.1     <none>        443/TCP        4m 
-  kube-system   kubernetes-dashboard   10.0.0.66    <nodes>       80:32220/TCP   21s 
-   
-**Dashboard:** 
-  minikube dashboard 
-Te abre http://192.168.99.100:30000 
- 
-También he hecho un tunel 8080: 
-  ssh -L 8080:localhost:8080 docker@192.168.99.100 
- 
-Url de acceso: http://localhost:8080/ui 
- 
-====== Autoescalado ====== 
-https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ 
- 
-http://blog.kubernetes.io/2016/07/autoscaling-in-kubernetes.html 
- 
-https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/ 
- 
-https://github.com/kubernetes/heapster/blob/master/docs/influxdb.md 
- 
-Autoescalamos la aplicación: 
-  kubectl autoscale deployment apache --min=2 --max=5 --cpu-percent=20 
- 
-  deployment "apache" autoscaled 
- 
-Vemos el autoescalado. Desde la UI se ve en deployments: 
-  kubectl get hpa 
- 
-  NAME      REFERENCE           TARGETS          MINPODS   MAXPODS   REPLICAS   AGE 
-  apache    Deployment/apache   <unknown> / 1%                            6m 
- 
-Con mas detalle: 
-  kubectl describe hpa apache 
-<code> 
-Name: apache 
-Namespace: default 
-Labels: <none> 
-Annotations: <none> 
-CreationTimestamp: Fri, 23 Jun 2017 17:51:17 +0200 
-Reference: Deployment/apache 
-Metrics: ( current / target ) 
-  resource cpu on pods  (as a percentage of request): <unknown> / 1% 
-Min replicas: 2 
-Max replicas: 5 
-Events: 
-  FirstSeen LastSeen Count From SubObjectPath Type Reason Message 
-  --------- -------- ----- ---- ------------- -------- ------ ------- 
-  6m 6m 1 horizontal-pod-autoscaler Normal SuccessfulRescale New size: 2; reason: Current number of replicas below Spec.MinReplicas 
-  5m 27s 12 horizontal-pod-autoscaler Warning FailedGetResourceMetric unable to get metrics for resource cpu: failed to get pod resource metrics: the server could not find the requested resource (get services http:heapster:) 
-  5m 27s 12 horizontal-pod-autoscaler Warning FailedComputeMetricsReplicas failed to get cpu utilization: unable to get metrics for resource cpu: failed to get pod resource metrics: the server could not find the requested resource (get services http:heapster:) 
-</code> 
- 
-Daba el error: 
-  kubectl describe hpa 
- 
-  35m 18s 72 horizontal-pod-autoscaler Warning FailedComputeMetricsReplicas failed to get cpu utilization: missing request for cpu on container httpd in pod default/httpd-796666570-2h1c6 
- 
-El problema es que en el deployment hay que decir que quiere monitorizar, en mi caso dentro de resources he añaido las dos líneas de requests: cpu:400m 
-<code> 
-...... 
-spec: 
-  containers: 
-    name: httpd 
-    image: httpd 
-    resources: 
-      requests: 
-        cpu:400m 
-...... 
-</code> 
- 
-Después da este error, pero tarda un poco en coger las métricas de heapster:  
-  kubectl describe hpa 
- 
-  2m 1m 3 horizontal-pod-autoscaler Warning FailedComputeMetricsReplicas failed to get cpu utilization: unable to get metrics for resource cpu: no metrics returned from heapster 
- 
-====== LOCAL kubeadm ====== 
-Instalamos docker 
- 
-  apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common 
-  curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - 
-  add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" 
- 
- 
- 
-====== Local en Centos ====== 
-https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ 
- 
-Installing kubelet and kubeadm 
- 
-<code> 
-cat <<EOF > /etc/yum.repos.d/kubernetes.repo 
-[kubernetes] 
-name=Kubernetes 
-baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 
-enabled=1 
-gpgcheck=1 
-repo_gpgcheck=1 
-gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg 
-        https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg 
-EOF 
-setenforce 0 
-yum install -y docker kubelet kubeadm kubernetes-cni 
-systemctl enable docker && systemctl start docker 
-systemctl enable kubelet && systemctl start kubelet 
-</code> 
- 
-====== Debian ====== 
- 
-<code> 
-apt-get update && apt-get install -y apt-transport-https 
-curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - 
-cat <<EOF >/etc/apt/sources.list.d/kubernetes.list 
-deb http://apt.kubernetes.io/ kubernetes-xenial main 
-EOF 
-apt-get update 
-# Install docker if you don't have it already. 
-apt-get install -y docker-engine 
-apt-get install -y kubelet kubeadm kubernetes-cni 
-</code> 
- 
-Arrancamos kubeadm 
-  kubeadm init 
- 
-<code> 
-Your Kubernetes master has initialized successfully! 
- 
-To start using your cluster, you need to run (as a regular user): 
- 
-  sudo cp /etc/kubernetes/admin.conf $HOME/ 
-  sudo chown $(id -u):$(id -g) $HOME/admin.conf 
-  export KUBECONFIG=$HOME/admin.conf 
- 
-You should now deploy a pod network to the cluster. 
-Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: 
-  http://kubernetes.io/docs/admin/addons/ 
- 
-You can now join any number of machines by running the following on each node 
-as root: 
- 
-  kubeadm join --token bad112.b2514df7739b0845 192.168.1.133:6443 
-</code> 
- 
-Como usuario normal ejecutamos: 
- 
-  sudo cp /etc/kubernetes/admin.conf $HOME/ 
-  sudo chown $(id -u):$(id -g) $HOME/admin.conf 
-  export KUBECONFIG=$HOME/admin.conf 
-   
-Ahora podemos ver los pods: 
-  kubectl get pods --all-namespaces 
- 
-Debug errores: 
-   sudo journalctl -r -u kubelet 
- 
-Vemos que el pod de dns se queda colgado, hay que configurar la RED 
-   
-Plugin RED https://www.weave.works/docs/net/latest/kube-addon/ 
-  kubectl apply -n kube-system -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')" 
-   
-Ahora miramos que todos los pods están activos 
-  kubectl get pods --all-namespaces 
- 
-  NAMESPACE     NAME                              READY     STATUS    RESTARTS   AGE 
-  kube-system   etcd-avtp110                      1/1       Running            26m 
-  kube-system   kube-apiserver-avtp110            1/1       Running            26m 
-  kube-system   kube-controller-manager-avtp110   1/      Running            26m 
-  kube-system   kube-dns-692378583-dn3qg          3/3       Running            26m 
-  kube-system   kube-proxy-vdpmv                  1/1       Running            26m 
-  kube-system   kube-scheduler-avtp110            1/1       Running            26m 
-  kube-system   weave-net-1s7wd                   2/      Running            2m 
- 
- 
-Vemos que el nodo está funcionando 
-  kubectl get nodes 
-  NAME      STATUS    AGE       VERSION 
-  avtp110   Ready     25m       v1.6.6 
- 
- 
-Instalamos dashboard 
-  kubectl create -f https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml 
- 
-Para mirar el estado: 
-  kubectl describe services kubernetes-dashboard --namespace=kube-system 
- 
-Arrancamos proxy para poder acceder: 
-  kubectl proxy 
-  Starting to serve on 127.0.0.1:8001 
- 
-Ya se puede acceder en http://127.0.0.1:8001/ui 
- 
- 
-Al desplegar cualquier cosa me da error: 
-  No nodes are available that match all of the following predicates:: PodToleratesNodeTaints (1). 
- 
-Solución: 
-  kubectl describe node 
- 
-<code> 
-Name: avtp110 
-Role:  
-Labels: beta.kubernetes.io/arch=amd64 
- beta.kubernetes.io/os=linux 
- kubernetes.io/hostname=avtp110 
- node-role.kubernetes.io/master= 
-Annotations: node.alpha.kubernetes.io/ttl=0 
- volumes.kubernetes.io/controller-managed-attach-detach=true 
-Taints: node-role.kubernetes.io/master:NoSchedule 
-CreationTimestamp: Sat, 24 Jun 2017 00:33:04 +0200 
-Phase:  
-Conditions: 
-  Type Status LastHeartbeatTime LastTransitionTime Reason Message 
-  ---- ------ ----------------- ------------------ ------ ------- 
-  OutOfDisk False Sat, 24 Jun 2017 01:00:18 +0200 Sat, 24 Jun 2017 00:33:04 +0200 KubeletHasSufficientDisk kubelet has sufficient disk space available 
-  MemoryPressure False Sat, 24 Jun 2017 01:00:18 +0200 Sat, 24 Jun 2017 00:33:04 +0200 KubeletHasSufficientMemory kubelet has sufficient memory available 
-  DiskPressure False Sat, 24 Jun 2017 01:00:18 +0200 Sat, 24 Jun 2017 00:33:04 +0200 KubeletHasNoDiskPressure kubelet has no disk pressure 
-  Ready True Sat, 24 Jun 2017 01:00:18 +0200 Sat, 24 Jun 2017 00:34:54 +0200 KubeletReady kubelet is posting ready status 
-Addresses: 192.168.1.133,192.168.1.133,avtp110 
-Capacity: 
- cpu: 4 
- memory: 20445912Ki 
- pods: 110 
-Allocatable: 
- cpu: 4 
- memory: 20343512Ki 
- pods: 110 
-System Info: 
- Machine ID: 72d18515f62d4c7186558889b32712f9 
- System UUID: 4C4C4544-0035-3010-8042-B8C04F504332 
- Boot ID: c529b79b-7f1c-4e1c-95f6-6187bdc57f6a 
- Kernel Version: 4.9.0-3-amd64 
- OS Image: Debian GNU/Linux 9 (stretch) 
- Operating System: linux 
- Architecture: amd64 
- Container Runtime Version: docker://1.11.2 
- Kubelet Version: v1.6.6 
- Kube-Proxy Version: v1.6.6 
-ExternalID: avtp110 
-Non-terminated Pods: (8 in total) 
-  Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits 
-  --------- ---- ------------ ---------- --------------- ------------- 
-  kube-system etcd-avtp110 0 (0%) 0 (0%) 0 (0%) 0 (0%) 
-  kube-system kube-apiserver-avtp110 250m (6%) 0 (0%) 0 (0%) 0 (0%) 
-  kube-system kube-controller-manager-avtp110 200m (5%) 0 (0%) 0 (0%) 0 (0%) 
-  kube-system kube-dns-692378583-f0sk0 260m (6%) 0 (0%) 110Mi (0%) 170Mi (0%) 
-  kube-system kube-proxy-1j5hd 0 (0%) 0 (0%) 0 (0%) 0 (0%) 
-  kube-system kube-scheduler-avtp110 100m (2%) 0 (0%) 0 (0%) 0 (0%) 
-  kube-system kubernetes-dashboard-2039414953-s1k99 0 (0%) 0 (0%) 0 (0%) 0 (0%) 
-  kube-system weave-net-sw6k7 20m (0%) 0 (0%) 0 (0%) 0 (0%) 
-Allocated resources: 
-  (Total limits may be over 100 percent, i.e., overcommitted.) 
-  CPU Requests CPU Limits Memory Requests Memory Limits 
-  ------------ ---------- --------------- ------------- 
-  830m (20%) 0 (0%) 110Mi (0%) 170Mi (0%) 
-Events: 
-  FirstSeen LastSeen Count From SubObjectPath Type Reason Message 
-  --------- -------- ----- ---- ------------- -------- ------ ------- 
-  27m 27m 1 kubelet, avtp110 Normal Starting Starting kubelet. 
-  27m 27m 1 kubelet, avtp110 Warning ImageGCFailed unable to find data for container / 
-  27m 27m 28 kubelet, avtp110 Normal NodeHasSufficientDisk Node avtp110 status is now: NodeHasSufficientDisk 
-  27m 27m 28 kubelet, avtp110 Normal NodeHasSufficientMemory Node avtp110 status is now: NodeHasSufficientMemory 
-  27m 27m 28 kubelet, avtp110 Normal NodeHasNoDiskPressure Node avtp110 status is now: NodeHasNoDiskPressure 
-  27m 27m 1 kube-proxy, avtp110 Normal Starting Starting kube-proxy. 
-  25m 25m 1 kubelet, avtp110 Normal NodeReady Node avtp110 status is now: NodeReady 
-</code> 
- 
-Nos fijamos en la línea: 
-  Taints: node-role.kubernetes.io/master:NoSchedule 
-Lanzamos el comando: 
-  kubectl taint nodes avtp110 node-role.kubernetes.io/master:NoSchedule- 
-  node "avtp110" tainted 
- 
-Para monitorización: 
-  kubectl create -f https://raw.githubusercontent.com/luxas/kubeadm-workshop/master/demos/monitoring/heapster.yaml 
- 
-====== Balanceador de carga ====== 
-La gracia de Kubernetes es que no hay que poner ips, se configura por nombres. 
- 
-Por ejemplo, deplegamos un apache y lo llamamos httpd. Si levantamos varios pods serán accesibles a sus ips por el puerto 80 
- 
-Podemos crear un balanceador con el siguiente fichero apuntado a la aplicación httpd: 
-<code> 
-    { 
-      "kind": "Service", 
-      "apiVersion": "v1", 
-      "metadata": { 
-        "name": "ejemplo-balanceador" 
-      }, 
-      "spec": { 
-        "ports": [{ 
-          "port": 8080, 
-          "targetPort": 80 
-        }], 
-        "selector": { 
-          "app": "httpd" 
-        }, 
-        "type": "LoadBalancer" 
-      } 
-    } 
-</code> 
- 
-Lo desplegamos en kubernetes: 
-  kubectl create -f balancecador.yaml 
-Vemos su configuración: 
-  kubectl describe services ejemplo-balanceador 
- 
-<code> 
-Name: ejemplo-balanceador 
-Namespace: default 
-Labels: <none> 
-Annotations: <none> 
-Selector: app=httpd 
-Type: LoadBalancer 
-IP: 10.96.97.216 
-Port: <unset> 8080/TCP 
-NodePort: <unset> 32249/TCP 
-Endpoints: 10.32.0.22:80,10.32.0.23:80 
-Session Affinity: None 
-Events: <none> 
-</code> 
- 
-Si accedemos a http://10.96.97.216:8080 nos balancea entre los dos PODS que tenemos desplegados de apache 
- 
-====== CLOUD ====== 
-Sitios para instalar en cloud:\\ 
-https://kubernetes.io/docs/setup/pick-right-solution/#hosted-solutions 
- 
-====== Seguridad ====== 
-https://kubernetes.io/docs/admin/authentication/ 
- 
-====== Balanceo de un nodo físico ====== 
-https://kubernetes.io/docs/tasks/administer-cluster/cluster-management/ 
- 
-Si queremos parar un nodo por mantenimiento: 
- 
-  kubectl --server=192.168.2.1:8001 drain kubernetes2 
-   
-  node "kubernetes2" cordoned 
-  error: DaemonSet-managed pods (use --ignore-daemonsets to ignore): kube-proxy-648gj, weave-net-3z537 
-Parece que no funciona. Lo lanzamos con el comando: 
-<code> 
-kubectl  --ignore-daemonsets --server=192.168.2.1:8001 drain kubernetes2 
-node "kubernetes2" already cordoned 
-WARNING: Ignoring DaemonSet-managed pods: kube-proxy-648gj, weave-net-3z537 
- 
-pod "servidorweb-3127718498-tpts3" evicted 
-pod "servidorweb-3127718498-3trvp" evicted 
-pod "heapster-57121549-zx3dt" evicted 
-pod "kubernetes-dashboard-2039414953-hsvkn" evicted 
-pod "apache-4284376775-7z96m" evicted 
-pod "kube-dns-692378583-x9kzv" evicted 
-node "kubernetes2" drained 
-</code> 
- 
-Para volver a dejarlo funcionando: 
-  kubectl   --server=192.168.2.1:8001 uncordon kubernetes2 
- 
- 
-====== Flecos ====== 
- 
-**TODO**: revisar si lo que hay aquí anotado se conserva o se suprime 
- 
-Mesos fleet tutum grafana 
- 
-Monitorización: http://www.sysdig.org/ 
- 
-System metrics: Datadog \\ 
-Container logs: Papertrail 
- 
-====== Ejemplo ====== 
- 
-En este ejemplo: 
- 
-  * Configuramos kubernetes en alta disponibilidad 
-  * Exponemos un servicio a través de un certificado SSL válido 
-  * El pod que recibe la petición muestra la IP pública del cliente 
- 
-Máquinas: 
- 
-^ DNS ^ IP privada ^ IP pública ^ Comentario ^ 
-| k8s.local    | 192.168.95.69 | - | Cluster 'kube-apiserver' | 
-| k8s1.local | 192.168.95.71 | 8.8.8.8 | Edge router. La IP pública es figurada | 
-| k8s2.local | 192.168.95.71 | - | Primer control plane | 
-| k8s3.local | 192.168.95.72 | - | Control plane adicional | 
-| k8s3.local | 192.168.95.73 | - | Control plane adicional | 
-| k8s4.local | 192.168.95.74 | - | Worker | 
-| k8s5.local | 192.168.95.75 | - | Worker | 
-| k8s6.local | 192.168.95.76 | - | Worker | 
- 
-1. Crear el cluster 
- 
-1.1. [[https://docs.docker.com/engine/install/debian|Instalar docker]] en todos los nodos, los tres control plane (k8s2.local, k8s3.local y k8s4.local) y los tres workers (k8s5.local, k8s6.local y k8s7.local). 
- 
-1.2. [[informatica:linux:docker:kubernetes#nodo_comun_para_control_plane_y_worker|Instalar paquetes de kubernetes]] en todos los nodos, los tres control plane (k8s2.local, k8s3.local y k8s4.local) y los tres workers (k8s5.local, k8s6.local y k8s7.local). 
- 
-1.3. Crear un [[informatica:linux:docker:kubernetes#balanceador_capa_4_kube-apiserver|balanceador de kube-apiserver]] y unir los tres control plane al mismo. 
- 
-1.4. [[informatica:linux:docker:kubernetes#con_alta_disponibilidad|Iniciar el cluster]] en el primer control plane 
- 
-1.5. [[informatica:linux:docker:kubernetes#control_plane_solo_alta_disponibilidad|Unir al cluster]] el resto de control plane (k8s3.local y k8s4.local). 
- 
-1.6. [[informatica:linux:docker:kubernetes#worker|Unir al cluster]] los tres workers. 
- 
-En este momento ya tenemos el cluster de kubernetes montado. 
- 
-2. Exponer un servicio con certificado SSL válido y que muestre la IP pública del cliente 
- 
-2.1. Instalar [[informatica:linux:docker:kubernetes#nginx_nodeport_con_afinitty_usar_este|Nginx ingress con nodeport, afinitty y proxy protocol]]. 
- 
-Como dice el paso 8, anotar los puertos HTTP y HTTPS que exponen cada uno de los nodos. 
- 
-2.2. Instalar [[informatica:linux:docker:kubernetes#cert_manager|Cert-manager addon]] 
- 
-2.3. Crear [[informatica:linux:docker:kubernetes#issuer|issuer]] para letsencrypt en producción 
- 
-2.4. Crear un [[informatica:linux:docker:kubernetes#deployment|deployment]] que despliegue el contenedor "whoami" 
- 
-2.5. Exponer el anterior deployment a través de un [[informatica:linux:docker:kubernetes#service|service]]. 
- 
-2.6. Crear un [[http://wiki.legido.com/doku.php?id=informatica:linux:docker:kubernetes#ejemplo_ingress_con_letsencrypt|ingress con letsencrypt]] para que un nombre DNS público, "example.com" llegue al servicio definido en el paso anterior. 
- 
-En este punto si: 
- 
-  * Se deshabilita momentáneamente proxy protoco (revirtiendo [[informatica:linux:docker:kubernetes#editar_configmaps_para_habilitar_use-proxy-protocol|este paso]] 
-  * Se obtiene el worker donde está corriendo el pod 'whoami' 
-  * Se obtiene la IP privada de ese worker 
-  * Se obtiene el puerto en el que escucha el servicio HTTP Nodeport (ver paso 2.1. de estas instrucciones) 
-  * Se le pasa la cabecera "Host" con el mombre adecuado (en este ejemplo "example.com"9 
- 
-Se podría llegar al pod. Ejemplo: 
- 
-<code> 
-_HOST=192.168.95.75 
-_PORT=32079 
-curl -L -s -i -H "Host: example.com" http://$_HOST:$_PORT 
-</code> 
- 
-3. Configurar edge router 
- 
-En este ejemplo vamos a instalar dos servidores, que van a compartir una IP flotante para tener redundancia. 
- 
-3.1. Seguir [[informatica:linux:docker:kubernetes#redundancia|estas instrucciones]]. 
- 
-3.2. Crear un nombre DNS, "example.com", que apunte a la IP pública flotante, por ejemplo '8.8.8.8' 
- 
-4. Probar 
- 
-https://example.com 
- 
-Resultado esperado: 
- 
-  * Debería mostrar un certificado SSL válido 
-  * Debería mostrar la IP pública del equipo que realizó la petición 
- 
-====== Bash completion ====== 
- 
-https://kubernetes.io/es/docs/tasks/tools/included/optional-kubectl-configs-bash-linux/ 
- 
-<code> 
-source /usr/share/bash-completion/bash_completion 
-echo 'source <(kubectl completion bash)' >>~/.bashrc 
-</code> 
- 
-<code> 
-sudo su 
-kubectl completion bash >/etc/bash_completion.d/kubectl 
-exit 
-</code> 
- 
-  exit 
-   
-====== Desplegar pods en control plane ====== 
- 
-No es lo recomendable, pero si tenemos un cluster con 3 nodos (stacked etc), los 3 control plane, y sin workers, podemos desplegar pods en los control plane si les quitamos los taints que precisamente marcan que NO se puedan desplegar pods allí. 
- 
-Para cada uno de los nodos del cluster, en este caso "k8s1": 
- 
-  kubectl taint nodes k8s1 node-role.kubernetes.io/control-plane:NoSchedule- 
-  kubectl taint nodes k8s1 node-role.kubernetes.io/master:NoSchedule- 
- 
-====== Volúmenes ====== 
- 
-TODO 
- 
-===== Persistentes ===== 
- 
-==== kadalu (usar este) ==== 
- 
-[[https://kadalu.io/docs/k8s-storage/devel/quick-start|Fuente]] 
- 
-1. Instalar cliente glusterfs. 
- 
-**TODO**: igual solo con el cliente mejor, pero así funciona 
- 
-  sudo apt install -y glusterfs-server 
- 
-2. Instalar kadalu 
- 
-  curl -fsSL https://github.com/kadalu/kadalu/releases/latest/download/install.sh | sudo bash -x 
- 
-Comprobar: 
- 
-  kubectl kadalu version 
- 
-Salida esperada similar a: 
- 
-<code> 
-kubectl-kadalu plugin: 0.8.15 
-</code> 
- 
-Listar pods: 
- 
-  kubectl get pods -n kadalu 
- 
-Salida esperada similar a: 
- 
-<code> 
-NAME                          READY   STATUS    RESTARTS   AGE 
-kadalu-csi-nodeplugin-h8nm8   3/    Running            74s 
-kadalu-csi-nodeplugin-kfp5c   3/    Running            74s 
-kadalu-csi-nodeplugin-sfgn9   3/    Running            74s 
-kadalu-csi-provisioner-0      5/5     Running            63s 
-operator-57b47b555f-h6gdm     1/    Running            75s 
-</code> 
- 
-3. (En cada uno de los nodos de kubernetes) Crear directorio donde se almacenará el cluster de glusterfs 
- 
- sudo mkdir -p /opt/kadalu/brick1/gv1 
- 
-4. Crear el volumen del cluster de glusterfs 
- 
-<code> 
-kubectl kadalu storage-add \ 
-  storage-pool1 \ 
-  --verbose \ 
-  --type=Disperse \ 
-  --data 2 \ 
-  --redundancy 1 \ 
-  --path k8s1:/opt/kadalu/brick1/gv1 \ 
-  --path k8s2:/opt/kadalu/brick1/gv1 \ 
-  --path k8s3:/opt/kadalu/brick1/gv1 
-</code> 
- 
-Salida esperada similar a: 
- 
-<code> 
-The following nodes are available: 
-  k8s1, k8s2, k8s3 
- 
-Storage Yaml file for your reference: 
- 
-apiVersion: "kadalu-operator.storage/v1alpha1" 
-kind: "KadaluStorage" 
-metadata: 
-  name: "storage-pool1" 
-spec: 
-  type: "Disperse" 
-  storage: 
-    - node: "k8s1" 
-      path: "/opt/kadalu/brick1/gv1" 
-    - node: "k8s2" 
-      path: "/opt/kadalu/brick1/gv1" 
-    - node: "k8s3" 
-      path: "/opt/kadalu/brick1/gv1" 
-  disperse: 
-    data: 2 
-    redundancy: 1 
- 
-Is this correct?(Yes/No): 
-</code> 
- 
-Teclear "Yes" y pulsar "Enter" 
- 
-Salida esperada similar a: 
- 
-<code> 
-Storage add request sent successfully 
-kadalustorage.kadalu-operator.storage/storage-pool1 created 
-</code> 
- 
-Listar pods: 
- 
-  kubectl get pods -n kadalu -o wide 
- 
-Salida esperada similar a: 
- 
-<code> 
-NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES 
-kadalu-csi-nodeplugin-h8nm8   3/    Running            15m   10.244.2.13   k8s2   <none>           <none> 
-kadalu-csi-nodeplugin-kfp5c   3/    Running            15m   10.244.0.26   k8s1   <none>           <none> 
-kadalu-csi-nodeplugin-sfgn9   3/    Running            15m   10.244.1.6    k8s3   <none>           <none> 
-kadalu-csi-provisioner-0      5/5     Running            15m   10.244.0.25   k8s1   <none>           <none> 
-operator-57b47b555f-h6gdm     1/    Running            15m   10.244.0.24   k8s1   <none>           <none> 
-server-storage-pool1-0-0      1/1     Running            83s   10.244.0.27   k8s1   <none>           <none> 
-server-storage-pool1-1-0      1/1     Running            82s   10.244.2.14   k8s2   <none>           <none> 
-server-storage-pool1-2-0      1/1     Running            82s   10.244.1.7    k8s3   <none>           <none> 
-</code> 
- 
-5. Repetir para cada pod: 
- 
-5.1. Crear PVC (Persistent Volume Claim): 
- 
-**IMPORTANTE**: la documentación dice que "storageClassName:" debe ser "kadalu.disperse", pero no, tiene que ser el nombre del volumen de glusterfs creado anteriormente. 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-kind: PersistentVolumeClaim 
-apiVersion: v1 
-metadata: 
-  name: persistent-volume-claim-1 
-spec: 
-  storageClassName: kadalu.storage-pool1 
-  accessModes: 
-    - ReadWriteMany 
-  resources: 
-    requests: 
-      storage: 1Gi 
-EOF 
-</code> 
- 
-Un nuevo volumen persistente se ha creado: 
- 
-  kubectl get persistentvolume 
- 
-Salida esperada similar a: 
- 
-<code> 
-NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                               STORAGECLASS           REASON   AGE 
-pvc-5df9ed7d-e77d-4910-b73b-a6145efb9c96   1Gi        RWX            Delete           Bound    default/persistent-volume-claim-1   kadalu.storage-pool1            52s 
-</code> 
- 
-Una nueva reclamación de volumen persistente se ha creado: 
- 
-  kubectl get persistentvolumeclaims 
- 
-Salida esperada similar a: 
- 
-<code> 
-NAME                        STATUS   VOLUME                                     CAPACITY   ACCE 
-SS MODES   STORAGECLASS           AGE 
-persistent-volume-claim-1   Bound    pvc-5df9ed7d-e77d-4910-b73b-a6145efb9c96   1Gi        RWX 
-           kadalu.storage-pool1   22s 
-</code> 
- 
-5.2. Crear el pod referenciando la reclamación de volumen persistente: 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: Pod 
-metadata: 
-  name: nginx-test 
-spec: 
-  containers: 
-    - name: nginx-test 
-      image: nginx 
-      volumeMounts: 
-        - mountPath: "/usr/share/nginx/html" 
-          name: nginx-test-volume 
-  volumes: 
-    - name: nginx-test-volume 
-      persistentVolumeClaim: 
-        claimName: persistent-volume-claim-1 
-EOF 
-</code> 
- 
-==== Usando glusterfs externo ==== 
- 
-Requisitos: 
- 
-  * Un [[informatica:linux:docker:kubernetes#cluster_externo_glusterfs|Cluster glusterfs]] funcionando. En este ejemplo los 3 nodos serán a la vez: 
-    * Control Plane 
-    * Workers 
-    * Nodos del cluster glusterfs 
- 
- 
-[[https://github.com/kubernetes/examples/tree/master/volumes/glusterfs|Fuente]] 
- 
-1. Crear endpoints 
- 
-  * "ip" es la IP de cada nodo. En nuestro caso es el mismo que los nodos de kubernetes 
-  * "port" un número arbitrario, "1" es suficiente 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: Endpoints 
-metadata: 
-  name: glusterfs-cluster 
-subsets: 
-- addresses: 
-  - ip: 10.0.0.2 
-  ports: 
-  - port: 1 
-- addresses: 
-  - ip: 10.0.0.3 
-  ports: 
-  - port: 1 
-- addresses: 
-  - ip: 10.0.0.4 
-  ports: 
-  - port: 1 
-EOF 
-</code> 
- 
-Comprobar: 
- 
-  kubectl get endpoints 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                ENDPOINTS                          AGE 
-glusterfs-cluster   10.0.0.2:1,10.0.0.3:1,10.0.0.4:  22s 
-</code> 
- 
-2. Crear servicio 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: Service 
-metadata: 
-  name: glusterfs-cluster 
-spec: 
-  ports: 
-  - port: 1 
-EOF 
-</code> 
- 
-Comprobar: 
- 
-  kubectl get services 
- 
-Resultado esperado similar a: 
- 
-<code> 
-NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE 
-glusterfs-cluster   ClusterIP   10.105.75.133    <none>        1/TCP     51s 
-</code> 
- 
-3. Crear pod 
- 
-  * Campo "path" debe ser el nombre de un volumen de glusterfs **previamente** creado 
- 
-<code> 
-cat <<EOF | kubectl apply -f - 
-apiVersion: v1 
-kind: Pod 
-metadata: 
-  name: glusterfs 
-spec: 
-  containers: 
-  - name: glusterfs 
-    image: nginx 
-    volumeMounts: 
-    - mountPath: "/usr/share/nginx/html" 
-      name: glusterfsvol 
-  volumes: 
-  - name: glusterfsvol 
-    glusterfs: 
-      endpoints: glusterfs-cluster 
-      path: gv1 
-      readOnly: false 
-EOF 
-</code> 
- 
-Comprobar (puede tardar 1 minuto): 
- 
-  kubectl get pods 
-   
-Resultado esperado similar a: 
- 
-<code> 
-NAME                                 READY   STATUS    RESTARTS   AGE 
-glusterfs                            1/1     Running            13s 
-</code> 
-     
-**Resumen**: 
- 
-  * El pod "glusterfs" tiene montado localmente un directorio, "/usr/share/nginx/html" 
-  * En este directorio puede escribir 
-  * Lo que escriba se escribirá en el volumen "gv1" del cluster glusterfs, que a su vez se escribirá en el brick "/opt/brick1/gv1" de cada uno de los nodos del cluster de glusterfs 
-  * Se pueden montar más pods, o este mismo si se tiene que recrear (porque se arrancla con "replica 1" o similar), que escriban en ese volumen de glusterfs 
- 
-**TODO**: 
- 
-  * Nótese que la provisión de volúmenes de glusterfs NO es dinámica. Primero se crea manualmente el volumen de glusterfs y luego se referencia, manualmente, en el pod de kubernetes 
-  * Parece ser que para que se pueda provisionar dinámicamente parece ser que se requiere Heketi, que es un proyecto que está de capa caída 
- 
-=== Cluster externo Glusterfs === 
- 
-Vamos a instalar un cluster de Glusterfs "externo", o como servicio, en lugar de montarlo como daemonset, que parece que requiere de un servicio (heketi) que no parece tener mucho futuro. 
- 
-1. (Cada nodo) Editar: 
- 
-  sudo vim /etc/hosts 
- 
-Y añadir: 
- 
-<code> 
-10.0.0.2 k8s1 
-10.0.0.3 k8s2 
-10.0.0.4 k8s3 
-</code> 
- 
-2. (Cada nodo) Instalar: 
- 
-  sudo apt install -y glusterfs-server 
- 
-  sudo systemctl enable glusterd --now 
- 
-  sudo service glusterd status 
- 
-  q 
-   
-3. (Desde un nodo, en este caso k8s1) Ejecutar: 
- 
-  sudo gluster peer probe k8s2 
-  sudo gluster peer probe k8s3 
- 
-4. (Desde otro nodo, en este caso k8s3) Ejecutar: 
- 
-  sudo gluster peer probe k8s1 
- 
-5. (Desde cualquier nodo) Ejecutar: 
- 
-  sudo gluster peer status 
- 
-6. Crear brick. Estas instrucciones se pueden ejecutar desde cualquier nodo del cluster 
- 
-6.1. Ejecutar: 
- 
-  sudo mkdir -p /opt/brick1/gv1 
- 
-**WARNING**: vamos a usar "force" porque usaremos la misma partición de sistema 
- 
-Vamos a usar el modo disperse con redundancia 1, que significa que podemos perder un máximo de un nodo y todo seguirá funcionando. 
- 
-  sudo gluster volume create gv1 disperse 3 redundancy 1 k8s{1..3}:/opt/brick1/gv1 force 
- 
-  sudo gluster volume start gv1 
- 
-  sudo gluster volume info 
  
informatica/linux/docker/kubernetes.1657389037.txt.gz · Last modified: 2022/07/09 17:50 by javi