Brendan Burns

Guía práctica de Kubernetes


Скачать книгу

      apiVersion: v1 kind: Service metadata: labels: app: frontend name: frontend namespace: default spec: ports: - port: 8080 protocol: TCP targetPort: 8080 selector: app: frontend type: ClusterIP

      Después de haber definido Service, podemos definir el recurso Ingress. A diferencia de los recursos de Service, Ingress necesita que un contenedor del controlador Ingress se ejecute en el clúster. Hay una serie de aplicaciones diferentes entre las que podemos elegir, ya sea las que proporciona el proveedor de la nube o las que se implementan utilizando servidores de código abierto. Si decidimos instalar un proveedor de Ingress de código abierto, es una buena idea utilizar el administrador de paquetes Helm (https://helm.sh) para su instalación y mantenimiento. Los proveedores de Ingress nginx o haproxy son las opciones más habituales:

      apiVersion: extensions/v1beta1 kind: Ingress metadata: name: frontend-ingress spec: rules: - http: paths: - path: /api backend: serviceName: frontend servicePort: 8080

      Cada aplicación necesita un cierto grado de configuración. Esta configuración puede estar relacionada con el número de entradas de la publicación para mostrarlas por páginas, el color de un fondo en particular, una presentación especial para un día festivo o muchos otros tipos de configuraciones. Normalmente, una buena práctica a seguir es la de separar la información de la configuración de la propia aplicación.

      Hay un par de razones para esta separación. La primera es que podríamos desear configurar el mismo código binario de la aplicación con diferentes configuraciones en función del escenario. Es posible, por ejemplo, que en Europa deseemos iluminar un especial de Pascua mientras que en China seguramente querremos mostrar un especial para el Año Nuevo Chino. Además de esta especialización en cuanto al entorno, hay razones de agilidad para realizar la separación. Por lo general, la versión en código binario contiene múltiples características nuevas y diferentes. Si activamos estas características mediante código, la única manera de modificar las características activas es crear y lanzar un nuevo binario, que puede ser un proceso costoso y lento.

      El uso de la configuración para activar un conjunto de características significa que podemos, de forma inmediata (incluso de forma dinámica), activar y desactivar características en respuesta a las necesidades del usuario o a fallos en el código de la aplicación. Dependiendo de cada característica, se puede desplegar o replegar. Esta flexibilidad asegura que podamos progresar continuamente con la mayor parte de las características, incluso en el caso de que algunas necesiten replegarse para mejorar el rendimiento o corregir problemas.

      En Kubernetes este tipo de configuración está representado por un recurso llamado ConfigMap. ConfigMap contiene pares clave/valor que representan la información de configuración o un archivo. Esta información de configuración se puede presentar a un contenedor en una cápsula, bien a través de archivos o bien mediante variables de entorno. Imaginemos que queremos configurar la aplicación de la publicación en línea para visualizar un número configurable de entradas por página. Para lograrlo, podemos definir ConfigMap como se indica a continuación:

      kubectl create configmap frontend-config --from-literal=journalEntries=10

      Para configurar la aplicación, presentamos la información de configuración como una variable de entorno en la propia aplicación. Para hacer esto, podemos añadir lo siguiente al recurso container en el Deployment que definimos antes:

      ... # The containers array in the PodTemplate inside the Deployment containers: - name: frontend ... env: - name: JOURNAL_ENTRIES valueFrom: configMapKeyRef: name: frontend-config key: journalEntries ...

      Aunque esto muestra cómo podemos usar ConfigMap para configurar la aplicación, en el mundo real de Deployments puede que queramos poner en marcha cambios periódicos en la configuración con actualizaciones semanales o incluso con mayor frecuencia. Podría ser tentador hacer esto simplemente cambiando el propio ConfigMap, pero no sería una buena práctica. Hay varias razones para ello. La primera es que cambiar la configuración no desencadena una actualización de las cápsulas existentes; solo se aplica la configuración cuando la cápsula se vuelve a arrancar. Por este motivo, la actualización no se realiza correctamente y puede ser ad hoc o aleatoria.

      Un enfoque más adecuado es poner un número de versión en el nombre del propio ConfigMap. En lugar de llamarlo frontend-config, lo llamamos frontendconfig-v1. Cuando queramos hacer un cambio, en lugar de actualizar el ConfigMap en uso, creamos una nueva v2 de ConfigMap y, a continuación, actualizamos el recurso Deployment de nuevo para utilizar esa configuración. Al hacerlo, se desencadena automáticamente la puesta en marcha del Deployment, utilizando las adecuadas health checking (pruebas de funcionamiento correcto) y las pausas entre cambios. Además, si alguna vez necesitamos hacer rollback (repliegue), la configuración v1 está disponible en el clúster y el rollback es tan simple como actualizar Deployment de nuevo.

      Hasta ahora no hemos entrado en detalle en el servicio Redis al que se conecta nuestro frontend. Pero en cualquier aplicación real necesitamos que las conexiones entre nuestros servicios sean seguras. En parte, se trata de garantizar la seguridad de la comunicación de los usuarios y de sus datos. Además, es esencial evitar errores como, por ejemplo, conectar un frontend de desarrollo con una base de datos de producción.

      La autenticación en la base de datos de Redis se realiza mediante una simple contraseña. Parecería conveniente pensar en almacenar esta contraseña en el código fuente de la aplicación, o en un archivo en la imagen, pero ninguna de estas opciones es buena idea por múltiples razones. La primera es que hemos filtrado nuestro secreto (la contraseña) en un entorno en el que no estamos necesariamente pensando en el control de acceso. Si ponemos una contraseña en el control del código fuente, estamos alineando el acceso al código fuente con el acceso a todos los datos secretos. Esto seguramente no sea correcto. Es probable que tengamos un conjunto más amplio de usuarios que puedan acceder a nuestro código fuente del que realmente debería tener acceso a nuestra instancia de Redis. De la misma manera, alguien que tiene acceso a la imagen de nuestro contenedor no necesariamente debe tener acceso a nuestra base de datos de producción.

      Además de las preocupaciones sobre el control de acceso, otra razón para evitar la vinculación de los datos secretos al control del código fuente y/o a las imágenes es la parametrización. Deseamos poder utilizar el mismo código fuente e imágenes en una gran variedad de entornos (por ejemplo, desarrollo, canario y producción). Si los datos secretos están estrechamente ligados con el código fuente o con la imagen, se necesita un archivo imagen diferente (o un código diferente) para cada entorno.

      Como acabamos de ver ConfigMaps en la sección anterior, el primer pensamiento podría ser el de almacenar la contraseña como si se tratara de una configuración y, a continuación, introducirla en la aplicación como una configuración específica de la misma. Estamos en lo cierto al creer que la separación de la configuración de la aplicación es la misma que la separación de los datos secretos de la aplicación. Pero la verdad es que los datos secretos son un concepto importante en sí mismo. Es probable que deseemos gestionar el control de acceso, la gestión y las actualizaciones de los datos secretos de una manera diferente a la de una configuración. Y lo que es más importante aún, queremos que nuestros desarrolladores piensen de forma diferente cuando accedan a los datos secretos y cuando accedan a la configuración. Por estas razones Kubernetes tiene incorporado el recurso Secret (secreto) para gestionar datos secretos.

      Podemos crear una contraseña secreta para nuestra base de datos Redis de la siguiente manera:

      kubectl create secret generic redis-passwd --from-literal=passwd=${RANDOM}

      Obviamente, es posible que deseemos utilizar algo más que un número aleatorio para la contraseña. Además, es probable que tengamos interés en utilizar un servicio de gestión de secret/key (secreto/clave), ya sea a través del proveedor de cloud computing (computación en la nube)