En la siguiente entrada vamos a configurar 3 máquinas Ubuntu Xenial para correr Kuberntes 1.4 y configuraremos el Traefik Ingress Controller para tener acceso a las aplicaciones.
Configuración inicial
Como decimos necesitamos tres máquinas para construir el clúster, una como master y las otras dos como workers. Las direcciones IP que usaré serán:
192.168.1.40 master
192.168.1.41 minion1
192.168.1.42 minion2
Esta configuración es importante ya que los certificados y las entradas DNS se generaran a partir de estos.
Hay que instalar estos paquetes en todas las máquinas del clúster
Instalamos la llave pública para el repositorio de Kubernetes
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
Y añadimos el repositorio a nuestros sources
echo deb http://apt.kubernetes.io/ kubernetes-xenial main | tee /etc/apt/sources.list.d/kubernetes.list
Actualizamos
apt-get update
Instalamos los paquetes necesarios
apt-get install -y kubelet kubeadm kubectl kubernetes-cni docker.io
En este punto ya estamos listo para desplegar Kubernetes
Desplegar Kubernetes
Desde la máquina master ejecutamos el siguiente comando:
kubeadm init --pod-network-cidr=172.30.0.0/16
Este comando inicializará el master con todos los contenedores necesarios para que Kubernetes funcione. Le estamos pasando el parámetro pod-network-cidr con la red que usaran los contenedores. Esto es porque voy a usar Flannel como networking overlay. Todos los contenedores que se desplieguen bajo este Kubernetes tendrán una IP de este rango. Este parámetro sólo es necesario para la red de Flannel, si queremos usar Calico, Weave, u otra no es necesario. Yo uso Flannel porque me ha dado buenos resultados y encuentro que es fácil de configurar.
Debemos ver la siguiente salida al terminar de crear contenedores:
Running pre-flight checks
generated token: "cfbebe.3e2eab74675b5426"
generated Certificate Authority key and certificate:
Issuer: CN=kubernetes | Subject: CN=kubernetes | CA: true
Not before: 2016-12-07 15:46:29 +0000 UTC Not After: 2026-12-05 15:46:29 +0000 UTC
Public: /etc/kubernetes/pki/ca-pub.pem
Private: /etc/kubernetes/pki/ca-key.pem
Cert: /etc/kubernetes/pki/ca.pem
generated API Server key and certificate:
Issuer: CN=kubernetes | Subject: CN=kube-apiserver | CA: false
Not before: 2016-12-07 15:46:29 +0000 UTC Not After: 2017-12-07 15:46:29 +0000 UTC
Alternate Names: [192.168.1.40 10.96.0.1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local]
Public: /etc/kubernetes/pki/apiserver-pub.pem
Private: /etc/kubernetes/pki/apiserver-key.pem
Cert: /etc/kubernetes/pki/apiserver.pem
generated Service Account Signing keys:
Public: /etc/kubernetes/pki/sa-pub.pem
Private: /etc/kubernetes/pki/sa-key.pem
created keys and certificates in "/etc/kubernetes/pki"
created "/etc/kubernetes/kubelet.conf"
created "/etc/kubernetes/admin.conf"
created API client configuration
created API client, waiting for the control plane to become ready
all control plane components are healthy after 36.174970 seconds
waiting for at least one node to register and become ready
first node is ready after 4.009025 seconds
attempting a test deployment
test deployment succeeded
created essential addon: kube-discovery, waiting for it to become ready
kube-discovery is ready after 19.002880 seconds
created essential addon: kube-proxy
created essential addon: kube-dns
Kubernetes master initialised successfully!
You can now join any number of machines by running the following on each node:
kubeadm join --token=cfbebe.3e2eab74675b5426 192.168.1.40
La última línea muestra el token que usaremos para unir nuevos workers al clúster, hay que guardarla en un lugar seguro.
Si nos preguntamos qué contenedores ha creado podemos verlos así:
$ kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
dummy-2088944543-ojfjy 1/1 Running 0 5m
etcd-master 1/1 Running 0 5m
kube-apiserver-master 1/1 Running 0 6m
kube-controller-manager-master 1/1 Running 0 6m
kube-discovery-1150918428-78wag 1/1 Running 0 5m
kube-dns-654381707-v4gsp 0/3 ContainerCreating 0 5m
kube-proxy-2ebl5 1/1 Running 0 5m
kube-scheduler-master 1/1 Running 0 5m
Construir el Networking Overlay
Antes de unir los nodos al cluster debemos configurar la red. Como hemos dicho vamos a usar Flannel, así nos descargamos el fichero YAML para configurarlo:
curl -o kube-flannel.yml https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Solo tenemos que modificar la línea
"Network": "10.244.0.0/16",
por
"Network": "172.30.0.0/16",
Y añadirlo a la configuración
kubectl apply -f kube-flannel.yml
Si ahora preguntamos por los contenedores, veremos que aparece uno nuevo que es el de Flannel, también observamos que el contenedor de DNS termina de crearse. Es necesaria la red para que funcione el DNS.
Añadiendo nuevos nuevos nodos al clúster
Ahora es el momento de usar el comando que guardamos cuando ejecutamos kubeadm init en cada uno de los workers que queramos usar en el clúster. En cada nodo:
kubeadm join --token=cfbebe.3e2eab74675b5426 192.168.1.40
Cuando haya terminado veremos los siguientes contenedores:
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
kube-system dummy-2088944543-ojfjy 1/1 Running 0 21m 192.168.1.40 master
kube-system etcd-master 1/1 Running 0 20m 192.168.1.40 master
kube-system kube-apiserver-master 1/1 Running 0 21m 192.168.1.40 master
kube-system kube-controller-manager-master 1/1 Running 0 21m 192.168.1.40 master
kube-system kube-discovery-1150918428-78wag 1/1 Running 0 21m 192.168.1.40 master
kube-system kube-dns-654381707-v4gsp 3/3 Running 0 20m 172.30.0.2 master
kube-system kube-flannel-ds-4iuct 2/2 Running 0 5m 192.168.1.40 master
kube-system kube-flannel-ds-hy10z 2/2 Running 1 1m 192.168.1.42 minion2
kube-system kube-flannel-ds-lf7a8 2/2 Running 1 1m 192.168.1.41 minion1
kube-system kube-proxy-2ebl5 1/1 Running 0 20m 192.168.1.40 master
kube-system kube-proxy-labsi 1/1 Running 0 1m 192.168.1.41 minion1
kube-system kube-proxy-lf770 1/1 Running 0 1m 192.168.1.42 minion2
kube-system kube-scheduler-master 1/1 Running 0 20m 192.168.1.40 master
Observar que existen pods (el de Flannel y el Kube-Proxy) por nodo, por cada nodo que se añade al clúster tendremos un pod que gobernara ese trabajo en ese nodo.
Llegados a este punto, ya tenemos un clúster de Kubernetes funcionando, pero nos interesa poder ofrecer servicios al exterior, así que ahora vamos a montar un Ingress Controller.
Accediendo a los servicios de Kubernetes: Ingress Controller
Para esta tarea necesitamos un pod especial, en realidad no es más que un proxy inverso que conectará el mundo exterior con lo que expongamos mediante los servicios. Existe una limitación conocida y hay abierto un issue en el GitHub de Kubernetes por la falta de transparencia y documentación para construir el Ingress Controller sobre Bare Metal. Aun así es posible usar un workaround para hacer que funcione. El truco consiste en usar NodePort para exponer el servicio de forma que Ingress conecte a este puerto en el nodo al no funcionar bien con ClusterIP o LoadBalancer. Esto no sucede si usamos un proveedor de cloud como AWS o GCE.
Lo primero que hacemos es descargar el YAML con la configuración de Traefik
curl -o traefik.yaml https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik.yaml
Y aplicamos
kubectl apply -f traefik.yaml
Y ya está. Podemos verlo en la lista de pods:
kube-system traefik-ingress-controller-2249976834-saoj1 1/1 Running 0 15s 192.168.1.41 minion1
Es súper sencillo. Como vemos se ha construido en el minion1, ahora debemos dirigir el tráfico HTTP a este worker al puerto 80 que es donde está escuchando Traefik, cada petición que reciba la enviará al servicio adecuado.
Vamos a probar que funciona lanzando un pod con NGiNX
Construimos el deployment de Nginx.
kubectl run nginx --image=nginx --port=80
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
default nginx-3449338310-xg88a 1/1 Running 0 30s 172.30.2.2 minion2
Ahora exponemos el servicio
kubectl expose deployment nginx --port=80 --target-port=80 --type=NodePort
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx 10.110.8.199 80/TCP 15s
Y creamos el ingress con el siguiente contenido:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
spec:
rules:
- host: nginx
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80
$ kubectl create -f nginx-ingress.yaml
Comprobamos que funciona:
$ curl -L --resolve nginx:80:192.168.1.41 http://nginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Y listo, en la siguiente entrada veremos como construir una aplicación algo más compleja utilizando las herramientas que nos proporciona Kubernetes.
Referencias
- http://kubernetes.io/docs/getting-started-guides/kubeadm/
- https://docs.traefik.io/user-guide/kubernetes/
- https://medium.com/@rothgar/exposing-services-using-ingress-with-on-prem-kubernetes-clusters-f413d87b6d34#.o7o65fwhf
- https://github.com/kubernetes/ingress/issues/17