miércoles, 11 de noviembre de 2015

Kubernetes en GCE

Creación de un laboratorio Kubernetes en GCE

(Siguiendo la guía de Kelsey Hightower - Kubernetes up and running)

Vamos a generar un laboratorio para usar Kubernetes como entorno de producción en la nube de Google GCE. Partimos del punto en que tenemos nuestro usuario con acceso al GCE donde podemos empezar a generar servidores para montar el cluster Kubernetes.

Importar la imagen que usaremos en los nodos de trabajo (contenedores docker) 

gcloud compute images create kubernetes-1-0-6-v20151004 --source-uri https://storage.googleapis.com/kuar/kubernetes-1-0-6-v20151004.tar.gz

Generar los nodos (4 nodos) 

gcloud compute instances create nodo1 --image kubernetes-1-0-6-v20151004 --boot-disk-size 200GB --machine-type n1-standard-1 --can-ip-forward --scopes compute-rw --zone europe-west1-b

Configuración del Docker Engine

Tengo problemas con la imagen y tengo que instalar el paquete docker.io. Después modifico el fichero de arranque del servicio.

[Unit] 
Description=Docker Application Container Engine Documentation=http://docs.docker.com 
After=network.target docker.socket 
Requires=docker.socket 

[Service] 
EnvironmentFile=-/etc/default/docker 
ExecStart=/usr/local/bin/docker --daemon
       --bip=10.10.10.1/24
       --iptables=false
       --ip-masq=false 
       --host=unix:///var/run/docker.sock
       --storage-driver=overlay 
Restart=on-failure 
RestartSec=5 
MountFlags=slave 
LimitNOFILE=1048576 
LimitNPROC=1048576 
LimitCORE=infinity 

[Install] 
WantedBy=multi-user.target

Los argumentos que se le pasan al engine hacen que la red de los contenedores sea la especificada como --bip, se desactiva el iptables y el ip-masq de modo que el engine no hace Nat sobre los paquetes de red que salen del nodo. Los paquetes saldrán con la IP especificado en --bip

Configuramos los tres nodos de la misma manera.

NodoIPNombre internoNombre externo
nodo010.10.0.1/24nodo0.c.grand-loop-111013.internalextern0
nodo110.10.10.1/24nodo1.c.grand-loop-111013.internalextern1
nodo210.10.20.1/24nodo2.c.grand-loop-111013.internalextern2
nodo210.10.30.1/24nodo3.c.grand-loop-111013.internalextern3

Ahora tenemos que indicarle a nuestras instancias como "hablar" entra ellas, para ello definimos rutas a nivel de nuestro proyecto. Definimos 4 rutas como la que se muestra a continuación.

gcloud compute routes create default-route-10-10-10-0-24 --destination-range 10.10.10.0/24 --next-hop-instance nodo1 --next-hop-instance-zone europe-west1-b

En este punto, las máquinas deben de tener comunicación entre las diferentes redes docker.

Para terminar, necesitamos que nuestros nodos enmascaren el tráfico que no fluye hacia la red de contenedores y entre los contenedores. Le hemos indicado a nuestro docker engine que no enmascare el tráfico y por lo tanto tenemos que hacer nosotros. En cada nodo:

sudo iptables -t nat -A POSTROUTING ! -d 10.0.0.0/8 -o eth0 -j MASQUERADE 
sudo iptables-save

En este punto nuestros contenedores deben tener salida a Internet y podrán "hablar" entre ellos por la red IP establecida en los contenedores.



Generamos el servicio Kubelet

Este servicio se encarga de manejar los pods (agrupación de contenedores similares que comparten volúmenes y red), los puntos de montaje, el registro de los nodos y de reportar las métricas y el estado de los servicios al servidor API.

[Unit]
Description=Kubelet 
Documentation=http://kubernetes.com 
After=docker.service 
Requires=docker.service

[Service]
ExecStartPre=/bin/mkdir -p /etc/kubernetes/manifest
ExecStart=/usr/local/bin/kubelet
     --api-servers=http://nodo0:8080
     --allow-privileged=true
     --cluster-dns=10.10.100.10
     --cluster-domain=cluster.local
     --config=/etc/kubernetes/manifest
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Kubernetes Controller node

El controller node lo montamos en el nodo0 creando el fichero etcd-pod.yaml en /etc/kubernetes/manifests/.

apiVersion: v1 
kind: Pod 
metadata: 
    name: etcd 
spec: 
   hostNetwork: true 
   volumes: 
      - name: "etcd-datadir" 
        hostPath: 
            path: "/var/lib/etcd" 
   containers: 
      - name: "etcd" 
        image: "b.gcr.io/kuar/etcd:2.2.0" 
        args: 
            - "--data-dir=/var/lib/etcd" 
            - "--advertise-client-urls=http://127.0.0.1:2379"
                        - "--listen-client-urls=http://127.0.0.1:2379"
                        - "--listen-peer-urls=http://127.0.0.1:2380"
                        - "--name=etcd"
               volumeMounts:
                        - mountPath: /var/lib/etcd
                          name: "etcd-datadir"

Necesitamos otro pod para el apiserver. Creamos el fichero kube-apiserver-pod.yaml en la misma carpeta anterior con este contenido:

apiVersion: v1 
kind: Pod 
metadata: 
    name: kube-apiserver 
spec: 
   hostNetwork: true 
   containers: 
       - name: "kube-apiserver" 
         image: "b.gcr.io/kuar/kube-apiserver:1.0.6" 
         args: 
             - "--allow-privileged=true" 
             - "--etcd-servers=http://127.0.0.1:2379" 
             - "--insecure-bind-address=0.0.0.0" 
             - "--service-cluster-ip-range=10.200.100.0/24" 
             - "--service-node-port-range=30000-37000"

Kube Controller manager

apiVersion: v1
kind: Pod
metadata:
     name: kube-controller-manager
spec:
     hostNetwork: true
     containers:
          - name: "kube-controller-manager"
            image: "b.gcr.io/kuar/kube-controller-manager:1.0.6"
            args:
                 - "--master=http://127.0.0.1:8080"

Kube scheduler

apiVersion: v1
kind: Pod
metadata:
     name: kube-scheduler
spec:
     hostNetwork: true
     containers:
          - name: "kube-scheduler"
            image: "b.gcr.io/kuar/kube-scheduler:1.0.6"
            args:
                 - "--master=http://127.0.0.1:8080"

Verificamos los componentes

curl http://127.0.0.1:8080/api/v1/namespaces/default/componentstatuses/controller-manager

{
   "kind": "ComponentStatus",
   "apiVersion": "v1",
   "metadata": {
          "name": "controller-manager",
          "selfLink": "/api/v1/namespaces/componentstatuses/controller-manager",
          "creationTimestamp": null
    },
    "conditions": [
          {
              "type": "Healthy",
              "status": "True",
              "message": "ok",
              "error": "nil"
           } ]

curl http://127.0.0.1:8080/api/v1/namespaces/default/componetstatuses/scheduler

{
    "kind": "ComponentStatus",
    "apiVersion": "v1",
    "metadata": {
          "name": "scheduler",
          "selfLink": "/api/v1/namespaces/componentstatuses/scheduler",
          "creationTimestamp": null
     },
     "conditions": [
           {
               "type": "Healthy",
               "status": "True",
               "message": "ok",
               "error": "nil"
      } ]

Verificamos el sistema

xxxxx@nodo0:/etc/kubernetes/manifest$ kubectl get nodes
NAME      LABELS                         STATUS
nodo0     kubernetes.io/hostname=nodo0   Ready
nodo1     kubernetes.io/hostname=nodo1   Ready
nodo2     kubernetes.io/hostname=nodo2   Ready
nodo3     kubernetes.io/hostname=nodo3   Ready

xxxxx@nodo0:/etc/kubernetes/manifest$ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
etcd-nodo0                      1/1       Running   0          3m
kube-apiserver-nodo0            1/1       Running   2          3m
kube-controller-manager-nodo0   1/1       Running   0          3m
kube-proxy-nodo0                1/1       Running   0          3m
kube-proxy-nodo1                1/1       Running   0          3m
kube-proxy-nodo2                1/1       Running   0          2m
kube-proxy-nodo3                1/1       Running   0          52s
kube-scheduler-nodo0            1/1       Running   0          3m

xxxxxx@nodo0:/etc/kubernetes/manifest$ kubectl get componentstatuses
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok                   nil
scheduler            Healthy   ok                   nil
etcd-0               Healthy   {"health": "true"}   nil


Arrancar la UI de Kubernetes

A partir de dos ficheros yaml generamos la interfaz gráfica:

Kube-ui-rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: kube-ui-v1
  namespace: kube-system
  labels:
    k8s-app: kube-ui
    version: v1
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    k8s-app: kube-ui
    version: v1
  template:
    metadata:
      labels:
        k8s-app: kube-ui
        version: v1
        kubernetes.io/cluster-service: "true"
    spec:
      containers:
      - name: kube-ui
        image: gcr.io/google_containers/kube-ui:v1.1
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
        ports:
        - containerPort: 8080

Kube-ui-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: kube-ui
  namespace: kube-system
  labels:
    k8s-app: kube-ui
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "KubeUI"
spec:
  selector:
    k8s-app: kube-ui
  ports:
  - port: 80
    targetPort: 8080

Ahora cargamos los ficheros:

kubectl create -f kube-ui-rc.yaml --namespace=kube-system 
kubectl create -f kube-ui-svc.yaml --namespace=kube-system

En nuestro cluster, el nodo0 tiene desplegada la UI de Kubernetes en el puerto 8080.