En la siguiente entrada vamos a explicar como podemos desplegar una legacy application, como WordPress, en un entorno de cloud como es Kubernetes.
Kubernetes se basan en una unidad mínima funcional llamada Pod, un Pod es uno o más contenedores, los contenedores pueden ser Docker o más recientemente, LXC. ¿Qué ocurre? que los contenedores son volátiles. Es decir, están condenados a desaparecer y volver a recrearse para siempre. Así, ¿qué pasa con la información que se va creando dentro de los Pods? Desaparece. Para evitar esta situación introducimos el concepto de persistencia. Básicamente consiste en configurarar el contenedor para que la información sensible que se va generando se guarde en un sitio inmune a la muerte y renacimiento de los contenedores. Para el caso que vamos a explicar aquí trataremos el sistema de ficheros y la base de datos. Por un lado, necesitamos que el WordPress siempre tenga acceso a su código PHP (aunque lo ideal sería conservar simplemente el wp-content) y por otro la base de datos, que es donde se guarda las entradas y demás.
La base de datos
Vamos a crear una máquina que funcionara como servidor de base de datos de MariaDB, configuramos para que acepte conexiones remotas.
Volvemos a nuestro master de Kubernetes y vamos a crear un secret
Un secret es un trozo de configuración para nuestras aplicaciones que introducimos en el fichero YAML. De está forma podemos pasar credenciales e información sensible en nuestro clúster sin que esté expuesta. En nuestro ejemplo vamos a crear un secret para pasar las credenciales de nuestra base de datos.
Primero codificamos la contraseña en base64, para este ejemplo, la contraseña de root es root01
$ echo -n root01 | base64 cm9vdDAx
Y escribimos el contenido el fichero de secretes
apiVersion: v1 kind: Secret metadata: name: mysql-secrets type: Opaque data: mysql-root-password: cm9vdDAx
Añadimos esta configuración a Kubernetes
$ kubectl create -f mysql-secrets.yaml secret "mysql-secrets" created
Vemos los secrets haciendo:
kubectl get secrets NAME TYPE DATA AGE default-token-thlxa kubernetes.io/service-account-token 3 1d mysql-secrets Opaque 3 23s
Ahora, cada vez que necesitemos de una aplicación que haga uso de la base de datos, podemos pasar las credenciales de root utilizando este endpoint.
Persistencia de Sistema de Ficheros
Para este ejemplo hemos montado un servidor NFS que ofrecemos a la red de los nodos de Kubernetes, así en nuestro fichero /etc/exports tendremos algo así:
/srvnfs 192.168.1.0/24(rw)
Ahora tenemos que presentar este volumen a Kubernetes, para ello hacemos uso de dos nuevos recursos, Persistent Volume y Persistent Volume Claim. La definición de la documentación de Kubernetes es muy buena, así que aquí lo explicaremos rápidamente:
- Persistent Volume Es el pastel que entregamos al clúster, entero, sin partir, en bruto, tal cual.
- Persistent Volume Claim Es cada porción de pastel que vamos a partir con cada pod que necesite persistencia.
Para crear estos recursos tenemos los siguientes YAML.
apiVersion: v1 kind: PersistentVolume metadata: name: pv-wp-content spec: capacity: storage: 20Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /srvnfs server: 192.168.1.43
Y para el claim
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc-wp-content spec: accessModes: - ReadWriteMany resources: requests: storage: 2Gi
Nota: Hay que instalar nfs-client en todos los nodos.
Se pueden usar otros drivers para persistencia como Ceph o Gluster, el procedimiento es el mismo.
Los creamos:
$ kubectl create -f pv-persistent.yaml persistentvolume "pv-wp-content" created
$ kubectl create -f pvc-persistent.yaml persistentvolumeclaim "pvc-wp-content" created
Lo verificamos:
$ kubectl get pv,pvc NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE pv/pv-wp-content 20Gi RWX Retain Bound default/pvc-wp-content 3m NAME STATUS VOLUME CAPACITY ACCESSMODES AGE pvc/pvc-wp-content Bound pv-wp-content 20Gi RWX 2m
Crear un deployment para WordPress
Ahora que está todo listo para que nuestro WordPress pueda desplegarse vamos a escribir el YAML para hacer uso de estos recursos. Partimos del ejemplo que nos da el propio Kubernetes aquí pero adaptado al clúster que nos hemos construido:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: wordpress-deployment labels: app: wordpress spec: strategy: type: Recreate template: metadata: labels: app: wordpress tier: frontend spec: containers: - image: wordpress name: wordpress env: - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysql-secrets key: mysql-root-password - name: WORDPRESS_DB_HOST value: 192.168.1.44 ports: - containerPort: 80 name: wordpress volumeMounts: - name: wordpress-persistent-storage mountPath: /var/www/html volumes: - name: wordpress-persistent-storage persistentVolumeClaim: claimName: pvc-wp-content
Vemos como le pasamos la contraseña de root en una variable de entorno, pero se carga a través de un secret, de forma que no está expuesta a simple vista. También le indicamos donde va a montar /var/www/html para escribir el código PHP de WordPress.
Ahora simplemente exponemos el servicio, recordando usar NodePort.
apiVersion: v1 kind: Service metadata: name: wordpress-svc labels: app: wordpress spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: wordpress tier: frontend type: NodePort
Y construimos el ingress.
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: wordpress-ingress spec: rules: - host: wordpress http: paths: - path: / backend: serviceName: wordpress-svc servicePort: 80
Podemos comprobar que funciona:
$ curl -I --resolve wordpress:80:192.168.1.41 http://wordpress HTTP/1.1 302 Found Cache-Control: no-cache, must-revalidate, max-age=0 Content-Type: text/html; charset=UTF-8 Date: Sat, 10 Dec 2016 11:43:28 GMT Expires: Wed, 11 Jan 1984 05:00:00 GMT Location: http://wordpress/wp-admin/install.php Server: Apache/2.4.10 (Debian) X-Powered-By: PHP/5.6.28
Conclusiones
- Hemos montado una legacy app sobre kuberntes teniendo en cuenta la persistencia de la información.
- Hay que tener en cuenta que este ejemplo coloca todos los ficheros de wordpress en el directorio compartido, por lo que si desplegamos otro sobrescribe lo anterior, si queremos tener varios WP corriendo en este entorno tendremos que configurara para que cada uno utilice una carpeta.
- También como dije al principio lo ideal es poner en persistencia el directorio de wp-contents que es el que conserva los datos. Esto implica cambiar el Docker y complica el ejemplo.
- El traefik a veces se queda pillado, de momento lo que hago es destruir el controlador para que el deplyment lo cree de nuevo.
- Si el traefik cambia de nodo hay que cambiar los dns para que apunten a la IP nueva. Puedes usar tier para crear afinidad con el nodo, de forma que traefik siempre funcione en el mismo nodo.
- También puedes tener un traefik por nodo para hacer balanceo o HA.