Orquestación de contenedores

De Wiki de Sistemas Operativos
Saltar a: navegación, buscar

Paso 1: Minikube: versión reducida de Kubernetes

Minikube es una versión reducida de Kubernetes para poder ejecutar todo en la misma máquina y no necesitar 3 nodos como necesita Kubernetes.

Para poder instalar minikube necesitaremos una nueva máquina virtual con los siguientes requisitos:

  • Disco de mayor tamaño tamaño que los creados hasta ahora, unos 10-15 GB
  • Al menos 2 CPU y 2GB de memoria RAM
  • docker instalado
  • kubectl instalado

Puedes descargar la imagen como se describe en el Virtualización con libvirt. Esta imagen ocupa 2 Gbytes únicamente, para redimensionar la imagen, puedes seguir lo descrito en el Paso 9 de Sistemas de archivos.

Paso 2: Crear máquina virtual

Lo segundo será crear una máquina virtual, utilizando las imágenes creadas en el paso anterior y también muy importante, que la configuración de CPU sea igual o mayor a 2 y la memoria igual o mayor a 2048 MB.

Los pasos serán similares a los de la práctica 1, Paso 4, teniendo en cuentas los cambios de la configuración.

Paso 3: Iniciar máquina virtual e instalar dependencias

Kubernetes require docker. Instala docker en la máquina virtual como se describe en Contenedores con Docker, a partir del paso 1.

Paso 4: Instalación de kubernetes

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install kubectl

Instalar minikube:

curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo mv minikube /usr/local/bin/
sudo chmod +x /usr/local/bin/minikube

Paso 5: Iniciar un cluster de minikube

Con todas las dependencias instaladas, vamos a crear nuestro primer cluster de minikube. Vamos a utilizar una opción que tiene minikube para que el cluster, en vez de montarlo en una máquina virtual, lo monte en el propio sistema, porque sino el cluster estaría en una máquina virtual, dentro de otra máquina virtual.

sudo minikube start --vm-driver=none

Como nos dice en la salida si todo ha ido bien, vamos a darle permisos a nuestro usuario para poder ejecutar kubectl:

sudo mv /home/ubuntu/.kube /home/ubuntu/.minikube $HOME
sudo chown -R $USER $HOME/.kube $HOME/.minikube

Ahora, veremos la información del cluster que acabamos de crear:

kubectl cluster-info

Deberíamos de obtener una salida similar a:

Kubernetes master is running at https://192.168.122.46:8443
KubeDNS is running at https://192.168.122.46:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Paso 6: Crear un deployment

Un deployment es un controlador para los pods, que asegura que los pods tengan una correcta configuración y que exista el número correcto de ellos.

Un pod es un objeto de kubernetes que ejecuta uno o más contenedores.

Vamos a crear una imagen Docker de ejemplo a partir de python. Creamos el fichero endpoint.py con el siguiente contenido:

import hug

@hug.get('/welcome')
def welcome(username="unknow"):
    """ Say welcome to username """
    return "Welcome " + username

Creamos el fichero Dockerfile con el siguiente contenido:

FROM python:alpine

RUN mkdir /app
RUN pip install hug
COPY endpoint.py /app
WORKDIR /app
CMD hug -f endpoint.py

Ahora vamos a construir nuestra imagen y comprobar que se ha creado:

sudo docker build -t app:v1 .
docker images

Ahora utilizaremos esta imagen para crear un deployment:

kubectl create deployment my-server --image=app:v1

Ahora comprobaremos que el deployment está bien creado:

kubectl get deployments

También comprobaremos que el deployment ha creado un pod:

kubectl get pods

que muestra un status running que indica éxito al crear el deployment:

NAME                         READY   STATUS             RESTARTS   AGE
my-server-7c7cc5b4-gwtx7     1/1     Running            0          17m

Si el status muestra ImagePullBackOff, probablemente hemos especificado una imagen Docker inexistente. Para depurar problemas, podemos emplear la orden describe:

kubectl describe pod my-server

que mostrará algo parecido a:

Name:         my-server4-7c7cc5b4-gwtx7
Namespace:    default
Priority:     0
Node:         minikube/192.168.122.52
Start Time:   Wed, 15 Jan 2020 15:58:10 +0000
Labels:       app=my-server4
              pod-template-hash=7c7cc5b4
Annotations:  <none>
Status:       Running
IP:           172.17.0.7
IPs:
  IP:           172.17.0.7
Controlled By:  ReplicaSet/my-server4-7c7cc5b4
Containers:
  miapp:
    Container ID:   docker://1458bd042e0b1447f619c595241aaab9edf7f7101c4e6c1ec8d1f43a49ba43d2
    Image:          miapp:v1
    Image ID:       docker://sha256:9781b807af04b0586588f365252ef9a2583c4b6da6cd0e6f1949d46896b9f959
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 15 Jan 2020 15:58:11 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-wwcpg (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-wwcpg:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-wwcpg
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  13m   default-scheduler  Successfully assigned default/my-server4-7c7cc5b4-gwtx7 to minikube
  Normal  Pulled     13m   kubelet, minikube  Container image "miapp:v1" already present on machine
  Normal  Created    13m   kubelet, minikube  Created container miapp
  Normal  Started    13m   kubelet, minikube  Started container miapp

En particular, la parte de Events nos da pistas sobre qué puede haber ido mal. El ejemplo de arriba muestra que el deployment my-server que emplea la imagen miapp se ha lanzado con éxito.

Paso 7: Crear un servicio

Por defecto, un pod es accesible solo internamente desde el cluster, para hacerlo accesible hacía fuera, necesitaremos crear un servicio que exponga el el deployment:

kubectl expose deployment my-server --type=LoadBalancer --port=8000

Es importante que el puerto especificado con --port indique el puerto del servidor que se crea en el deployment. El puerto 8000 es empleado por python hug en el ejemplo de unos de los pasos anteriores.

Comprobaremos que se ha creado correctamente:

kubectl get services

Veremos una salida similar a la siguiente:

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP          4h25m
my-server    LoadBalancer   10.103.110.240   <pending>     8000:32607/TCP   5s

Veremos en la parte de puerto que el puerto 8000 interno lo está exponiendo en el 32607, así que vamos ahora a comprobar si esto funciona:

curl -X GET http://localhost:32607/welcome?username=LSO

Ya tenemos con kubernetes creado un deployment que está ejecutando un pod y se está exponiendo a través de un servicio.

Paso 8: Eliminando deployment, pods y services

Eliminemos primero el servicio:

kubectl delete service my-server

Comprobamos que ya no existe:

kubectl get services

Ahora vamos a intentar eliminar un pod y ver lo que ocurre. Para eliminarlo, vamos a listar los pods primero para saber el nombre:

kubectl get pods

Tenemos la siguiente salida:

NAME                        READY   STATUS    RESTARTS   AGE
my-server-bc9b8955b-llkth   1/1     Running   0          27m

Vamos a eliminar el pod:

kubectl delete pod my-server-bc9b8955b-llkth

Ahora veamos que ha pasado:

kubectl get pods

Tenemos la siguiente salida:

NAME                        READY   STATUS    RESTARTS   AGE
my-server-bc9b8955b-p82qc   1/1     Running   0          27m

Hemos borrado un pod, pero ahora hay otro levantado. Esto ocurre por que el deployment es el encargado de asegurar que siempre haya un pod levantado, así que lo eliminamos o falla por cualquier motivo, el deplyment levantá otro automáticamente. Esta es la magia de kubernetes, se da por hecho que los sistemas van a fallar y no vamos a tener tiempo para arreglarlos, así que lo que hacemos es olvidarnos del que está fallando o el que se ha eliminado y levantar uno nuevo.

Pasemos ahora a eliminar el deployment:

kubectl delete deployment my-server

Veamos si verdaderamente se ha eliminado:

kubectl get deployments

Vemos que el deplyment si es eliminado.


Paso 9: Creando deployments y services desde ficheros

Desde línea de comandos podemos realizar todo lo necesario, aunque lo recomendable para kubernetes es crear ficheros de configuración y aplicarlos.

Paso 9.1: Creando deployment a partir de fichero

Veamos un ejemplo para crear un deployment, para ello creamos el fichero deployment.yml:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-container
          image: app:v1
          ports:
            - containerPort: 8000
          resources:
            limits:
              cpu: "0.1"
          volumeMounts:
            - mountPath: /home/ubuntu/data
              name: data

Comentamos el fichero por línea:

  • apiVersion: es la versión de la API utilizada para definir este fichero
  • kind: el tipo de objeto de kubernetes, puede ser un Pod, Service o un Deployment
  • metadata: esto nos sirve para dar información al deployment
  • spec: aquí añadiremos la especificación del deployment
  • replicas: añadiremos la cantidad de réplicas/pods que habrá dentro del deployment
  • template: definimos el pod aquí
  • spec: especificación del pod
  • containers: especificación del contenedor (nombre, imagen, puerto, volumen).
 También podremos añadir la cantidad de recursos que consumirá el contenedor

Pasemos a crear el deployment creado:

kubectl create -f deployment.yml

Ahora veremos lo que ha creado:

kubectl get deployments

Aquí tenemos un deployment, el cual vemos que tiene dos pods, vamos a verlos:

kubectl get pods

Ahora, veamos que podemos modificar un fichero, aplicar los cambios, y estos se apicarán rápidamente. Vamos a modificar el fichero deployment.yml y vamos a poner 3 réplicas en vez de 2, después aplicamos los cambios y observamos

kubectl apply -f deployment.yml
kubectl get pods

Si hacemos rápidamente los dos comandos, veremos que rápidamente kubernetes se pone a crear otro pod más:

NAME                             READY   STATUS              RESTARTS   AGE
my-deployment-7595cb9c96-7wq46   0/1     ContainerCreating   0          2s
my-deployment-7595cb9c96-jb97b   1/1     Running             0          16m
my-deployment-7595cb9c96-x465b   1/1     Running             0          16m

Unos segundos después, ya está todo corriendo:

NAME                             READY   STATUS    RESTARTS   AGE
my-deployment-7595cb9c96-7wq46   1/1     Running   0          5s
my-deployment-7595cb9c96-jb97b   1/1     Running   0          16m
my-deployment-7595cb9c96-x465b   1/1     Running   0          16m

Podemos hacer ahora el cambio del revés y ver como kubernetes se encargará de dejar de nuevo dos réplicas y parará una de las que estén corriendo.

Paso 9.2: Creando services desde fichero

Ahora veamos como crear un servicio con ficheros de configuración, ya que un deployment sin servicio no nos servirá de mucho. Fichero service.yml:

---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 8000
  type: LoadBalancer

Como vemos, los datos son muy similares a los anteriores escritos en el fichero deployment.yml, aquí una de las cosas más importantes es el selector, que es donde tendremos que elegir el la app correcta. Después, también ver que en los puertos, le hemos dicho que el 8000 es por donde sale la información, kubernetes ya se encargará de exponerlo en algún puerto que lueggo veremos.

Vamos a ejecutar el servicio:

kubectl create -f service.yml

Ahora veamos el servicio:

kubectl get services

Aquí veremos nuestro servicio corriendo:

my-service   LoadBalancer   10.104.207.24   <pending>     8000:30819/TCP   1m

Y veremos que el puerto al que está expuesto es el 30819, vamos a realizar una petición para ver si funciona correctamente:

curl -X GET http://localhost:32607/welcome?username=LSO

También podremos ver desde fuera de nuestra máquina virtual, en nuestra máquina host, corriendo este servicio. Hacemos un ifconfig para ver nuestra ip, y una vez que la sepamos, accedemos con nuestro navegador a la dirección:

http://NUESTRA_IP:30819/welcome?username=LSO

Por último veamos de nuevo que funciona correctamente el deployment, y siempre vamos a tener ejecutándose el número de réplicas correcto y el servicio no dejará de funcionar. Mantegamos abierto el navegador, y hagamos lo siguiente en nuestra máquina virtual:

kubectl get pods

Vamos a borrar todos los pods y, seguidamente, comprobamos en nuestro navegador si sigue funcionando el servicio:

kubectl delete pod my-deployment-7595cb9c96-9pz4c my-deployment-7595cb9c96-dzxnt my-deployment-7595cb9c96-mq6pn

Veremos que el servicio sigue funcionando en todo momento, y si revisamos, veremos que tendremos de nuevo la cantidad correcta de pods funcionando:

kubectl get pods

Finalmente, vamos a apagar nuestro cluster:

sudo minikube stop