Cómo implementar una aplicación PHP con Kubernetes en Ubuntu 16.04

El autor seleccionó Open Internet/Free Speech para recibir una donación como parte del programa Write for DOnations.

Introducción

Kubernetes es un sistema de orquestación de contenedores de código abierto. Le permite crear, actualizar y escalar contenedores sin preocuparse por el tiempo de inactividad.

Para ejecutar una aplicación PHP, Nginx funciona como proxy para PHP-FPM. Disponer esta configuración en un solo contenedor puede ser un proceso engorroso, pero Kubernetes permitirá administrar ambos servicios en contenedores separados. Utilizar Kubernetes le permitirá lograr que sus contenedores sean reutilizables e intercambiables, y no tendrá que reconstruir la imagen de sus contenedores cada vez que haya una nueva versión de Nginx o PHP.

En este tutorial, implementará una aplicación PHP 7 en un clúster de Kubernetes con Nginx y PHP-FPM ejecutándose en contenedores separados. También aprenderá a mantener los archivos de configuración y el código de aplicación fuera de la imagen de contenedor que utilice el sistema de almacenamiento en bloque de DigitalOcean. Este enfoque le permitirá reutilizar la imagen de Nginx para cualquier aplicación que necesite un servidor web o proxy pasando un volumen de configuración en lugar de reconstruir la imagen.

Requisitos previos

  • Comprensión básica de los objetos de Kubernetes. Para obtener mayor información, consulte nuestro artículo Introducción a Kubernetes.
  • Un clúster de Kubernetes activo en Ubuntu 16.04. Puede configurarlo siguiendo el tutorial Cómo crear un clúster de Kubernetes 1.10 utilizando Kubeadm en Ubuntu 16.04.
  • Una cuenta de DigitalOcean y un token de acceso a la API con permisos de lectura y escritura para crear nuestro volumen de almacenamiento. Si no tiene su token de acceso a la API, puede crearlo desde aquí.
  • Su código de aplicación alojado en una URL de acceso público, como Github.

Paso 1: Crear los servicios de PHP-FPM y Nginx

En este paso, creará los servicios de PHP-FPM y Nginx. Un servicio permite acceder a un conjunto de pods desde el interior del clúster. Los servicios dentro de un clúster pueden comunicarse de forma directa a través de sus nombres, sin necesidad de direcciones IP. El servicio de PHP-FPM permitirá el acceso a los pods de PHP-FPM, mientras que el servicio Nginx permitirá el acceso a los pods de Nginx.

Debido a que los pods de Nginx representarán los pods de PHP-FPM, tendrá que indicar al servicio la forma de encontrarlos. En lugar de utilizar direcciones IP, aprovechará la detección automática de servicios de Kubernetes para utilizar nombres legibles para humanos para dirigir solicitudes al servicio apropiado.

Para crear el servicio, creará un archivo de definición de objeto. Cada definición de objeto de Kubernetes es un archivo YAML que contiene por lo menos los siguientes elementos:

  • apiVersion: versión de la API de Kubernetes a la que pertenece la definición.
  • kind: objeto de Kubernetes que este archivo representa. Por ejemplo,un pod o service.
  • metadata: contiene el name del objeto junto con cualquier labels que desee aplicarle.
  • spec: contiene una configuración específica según el tipo de objeto que cree, como la imagen del contenedor o los puertos en los cuales se podrá acceder a este.

Primero, creará un directorio para contener sus definiciones de objetos de Kubernetes.

Aplique SSH a su *nodo maestro *y cree el directorio definitions que contendrá sus definiciones de objetos de Kubernetes.

  • mkdir definitions

Acceda al directorio definitions recién creado:

  • cd definitions

Realice su servicio de PHP-FPM creando un archivo php_service.yaml:

  • nano php_service.yaml

Establezca kind como Service para especificar que este objeto es un servicio:

php_service.yaml

... apiVersion: v1 kind: Service 

Nombre el servicio como php, ya que proporcionará acceso a PHP-FPM:

php_service.yaml

... metadata:   name: php 

Agrupará de manera lógica diferentes objetos con etiquetas. En este tutorial, utilizará etiquetas para agrupar los objetos en “niveles”, como frontend o backend. Los pods de PHP se ejecutarán detrás de este servicio, por lo que le asignará la etiqueta tier: backend.

php_service.yaml

...   labels:     tier: backend 

Un servicio determina los pods a los que se debe acceder utilizando las etiquetas selector. Se servirá un pod que coincida con estas etiquetas, independiente de que este se cree antes o después del servicio. Agregará etiquetas para sus pods posteriormente en el tutorial.

Utilice la etiqueta tier: backend para asignar el pod al nivel de backend. También agregará la etiqueta app: php para especificar que este pod ejecuta PHP. Agregue estas dos etiquetas después de la sección de metadata.

php_service.yaml

... spec:   selector:     app: php     tier: backend 

A continuación, especifique el puerto utilizado para acceder a este servicio. En este tutorial, utilizará el puerto 9000. Añádalo al archivo php_service.yaml en spec:

php_service.yaml

...   ports:     - protocol: TCP       port: 9000 

Su archivo php_service.yaml completo tendrá el siguiente aspecto:

php_service.yaml

apiVersion: v1 kind: Service metadata:   name: php   labels:     tier: backend spec:   selector:     app: php     tier: backend   ports:   - protocol: TCP     port: 9000 

Presione CTRL + o para guardar el archivo y luego CTRL + x para cerrar nano.

Ahora que ha creado la definición de objeto para su servicio, para ejecutar el servicio utilizará el comando kubectl apply junto con el argumento -f y especificará su archivo php_service.yaml.

Cree su servicio:

  • kubectl apply -f php_service.yaml

Este resultado confirma la creación del servicio:

Outputservice/php created 

Verifique que su servicio esté en ejecución:

  • kubectl get svc

Observará su servicio de PHP-FPM en ejecución:

OutputNAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    10m php          ClusterIP   10.100.59.238   <none>        9000/TCP   5m 

Kubernetes admite varios tipos de servicios. Su servicio php utiliza el tipo de servicio predeterminado, clusterIP. Este tipo de servicio asigna una IP interna y permite acceder al servicio sólo desde el interior del clúster.

Ahora que el servicio de PHP-FPM está listo, creará el servicio de Nginx. Cree y abra un nuevo archivo llamado nginx_service.yaml con el editor:

  • nano nginx_service.yaml

Este servicio se orientará a los pods de Nginx, por lo que lo llamará nginx. También agregará una etiqueta tier: backend, ya que pertenece al nivel de backend:

nginx_service.yaml

apiVersion: v1 kind: Service metadata:   name: nginx   labels:     tier: backend 

De modo similar al servicio php, dirija los pods con las etiquetas de selección app: nginx y tier: backend. Permita que sea posible acceder a este servicio en el puerto 80, el puerto HTTP predeterminado.

nginx_service.yaml

... spec:   selector:     app: nginx     tier: backend   ports:   - protocol: TCP     port: 80 

Será posible acceder al servicio de Nginx de forma pública en Internet desde la dirección IP pública de su Droplet. Puede encontrar your_public_ip desde su panel en la nube de DigitalOcean. En spec.externalIPs, agregue lo siguiente:

nginx_service.yaml

... spec:   externalIPs:   - your_public_ip 

Su archivo nginx_service.yaml tendrá este aspecto:

nginx_service.yaml

apiVersion: v1 kind: Service metadata:   name: nginx   labels:     tier: backend spec:   selector:     app: nginx     tier: backend   ports:   - protocol: TCP     port: 80   externalIPs:   - your_public_ip     

Guarde y cierre el archivo. Cree el servicio de Nginx:

  • kubectl apply -f nginx_service.yaml

Observará el siguiente resultado cuando el servicio esté en ejecución:

Outputservice/nginx created 

Puede ver todos los servicios en funcionamiento ejecutando lo siguiente:

  • kubectl get svc

Observará los servicios de PHP-FPM y Nginx enumerados en el resultado:

OutputNAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    13m nginx        ClusterIP   10.102.160.47   your_public_ip 80/TCP     50s php          ClusterIP   10.100.59.238   <none>        9000/TCP   8m 

Tenga en cuenta que si desea eliminar un servicio puede ejecutar lo siguiente:

  • kubectl delete svc/service_name

Ahora que creó sus servicios de PHP-FPM y Nginx, deberá especificar el lugar de almacenamiento para el código de su aplicación y sus archivos de configuración.

Paso 2: Instalar el complemento de almacenamiento de DigitalOcean

Kubernetes proporciona diversos complementos de almacenamiento que pueden crear el espacio de almacenamiento para su entorno. En este paso, instalará el complemento de almacenamiento de DigitalOcean para crear un almacén en bloques en DigitalOcean. Una vez completada la instalación, agregará una clase de almacenamiento llamada do-block-storage que utilizará para crear su almacenamiento en bloques.

Primero configurará un objeto secreto de Kubernetes para almacenar su token de API de DigitalOcean. Los objetos secretos se utilizan para compartir información confidencial, como claves y contraseñas SSH, con otros objetos de Kubernetes dentro del mismo espacio de nombres. Los espacios de nombres ofrecen una alternativa lógica para separar sus objetos de Kubernetes.

Abra un archivo llamado secret.yaml con el editor:

  • nano secret.yaml

Nombre su objeto secreto digitalocean y agréguelo al namespace kube-system. kube-system es el espacio de nombres predeterminado para los servicios internos de Kubernetes y el complemento de almacenamiento de DigitalOcean también lo utiliza para iniciar varios componentes.

secret.yaml

apiVersion: v1 kind: Secret metadata:   name: digitalocean   namespace: kube-system 

En lugar de una clave spec, un secreto utiliza una clave data o stringData para contener la información necesaria. El parámetro data contiene datos codificados en base64 que se decodifican de manera automática cuando se recuperan. El parámetro stringData contiene datos no codificados que se codifican de manera automática durante la creación o actualización, y no muestra los datos al recuperar secretos. En este tutorial, utilizará stringData para una mayor practicidad.

Agregue el access-token como stringData:

secret.yaml

... stringData:   access-token: your-api-token 

Guarde y cierre el archivo.

Su archivo secret.yaml tendrá el siguiente aspecto:

secret.yaml

apiVersion: v1 kind: Secret metadata:   name: digitalocean   namespace: kube-system stringData:   access-token: your-api-token 

Cree el secreto:

  • kubectl apply -f secret.yaml

Verá este resultado al crear secretos:

Outputsecret/digitalocean created 

Puede visualizar el secreto con el siguiente comando:

  • kubectl -n kube-system get secret digitalocean

El resultado tendrá un aspecto similar a este:

OutputNAME           TYPE      DATA      AGE digitalocean   Opaque    1         41s 

El tipo Opaque implica que este secreto es de sólo lectura, parámetro estándar para los secretos de stringData. Puede obtener más información más acerca de ello en las especificaciones de diseño de secretos. El campo DATA muestra el número de elementos almacenados en este secreto. En este caso, muestra 1 porque tiene una sola clave almacenada.

Ahora que su secreto está implementado, instale el complemento de almacenamiento en bloques de DigitalOcean:

  • kubectl apply -f https://raw.githubusercontent.com/digitalocean/csi-digitalocean/master/deploy/kubernetes/releases/csi-digitalocean-v0.3.0.yaml

Verá resultados similares al siguiente:

Outputstorageclass.storage.k8s.io/do-block-storage created serviceaccount/csi-attacher created clusterrole.rbac.authorization.k8s.io/external-attacher-runner created clusterrolebinding.rbac.authorization.k8s.io/csi-attacher-role created service/csi-attacher-doplug-in created statefulset.apps/csi-attacher-doplug-in created serviceaccount/csi-provisioner created clusterrole.rbac.authorization.k8s.io/external-provisioner-runner created clusterrolebinding.rbac.authorization.k8s.io/csi-provisioner-role created service/csi-provisioner-doplug-in created statefulset.apps/csi-provisioner-doplug-in created serviceaccount/csi-doplug-in created clusterrole.rbac.authorization.k8s.io/csi-doplug-in created clusterrolebinding.rbac.authorization.k8s.io/csi-doplug-in created daemonset.apps/csi-doplug-in created 

Ahora que instaló el complemento de almacenamiento de DigitalOcean, puede crear almacenamiento en bloques para contener el código de su aplicación y sus archivos de configuración.

Paso 3: Crear el volumen persistente

Con su secreto implementado y el complemento de almacenamiento en bloques instalado, estará listo para crear su volumen persistente. Un volumen persistente, o VP, es un almacenamiento en bloques de un tamaño especifico que es independiente del ciclo de vida de un pod. Utilizar un volumen persistente le permitirá administrar o actualizar sus pods sin preocuparse por la posibilidad de perder el código de su aplicación. El acceso a un volumen persistente es posible utilizando un PersistentVolumeClaim, o PVC, que monta el VP en la ruta requerida.

Abra un archivo llamado code_volume.yaml con su editor:

  • nano code_volume.yaml

Dé el nombre code al PVC agregando los siguientes parámetros y valores a su archivo:

code_volume.yaml

apiVersion: v1 kind: PersistentVolumeClaim metadata:   name: code 

La spec para un PVC contiene los siguientes elementos:

  • accessModes que varían según el caso de uso. Son los siguientes:
    • ReadWriteOnce: monta el volumen con atributos de lectura y escritura para un solo nodo.
    • ReadOnlyMany: monta el volumen con atributos de sólo lectura para muchos nodos.
    • ReadWriteMany: monta el volumen con atributos de lectura y escritura para muchos nodos.
  • resources: espacio de almacenamiento que usted requiere.

El almacenamiento en bloques de DigitalOcean solo se monta en un único nodo, por lo que usted fijará accessModes en ReadWriteOnce. Este tutorial le servirá de guía para agregar una pequeña cantidad de código de aplicación. Por lo tanto 1 gigabyte será suficiente en este caso de uso. Si planea almacenar una mayor cantidad de código o datos en el volumen, puede modificar el parámetro storage para que se adapte a sus necesidades. Puede aumentar la cantidad de almacenamiento después de crear el volumen, pero no es posible reducir el disco.

code_volume.yaml

... spec:   accessModes:   - ReadWriteOnce   resources:     requests:       storage: 1Gi 

A continuación, especifique la clase de almacenamiento que Kubernetes utilizará para proporcionar los volúmenes. Utilizará la clase do-block-storage creada por el complemento de almacenamiento en bloques de DigitalOcean.

code_volume.yaml

...   storageClassName: do-block-storage 

Su archivo code_volume.yaml tendrá el siguiente aspecto:

code_volume.yaml

apiVersion: v1 kind: PersistentVolumeClaim metadata:   name: code spec:   accessModes:   - ReadWriteOnce   resources:     requests:       storage: 1Gi   storageClassName: do-block-storage 

Guarde y cierre el archivo.

Cree el elemento code PersistentVolumeClaim utilizando kubectl:

  • kubectl apply -f code_volume.yaml

El siguiente resultado le indica que el objeto se creó de forma correcta y está listo para montar su PVC de 1 GB como un volumen.

Outputpersistentvolumeclaim/code created 

Para visualizar volúmenes persistentes (VP) disponibles:

  • kubectl get pv

Verá su VP enumerado:

OutputNAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM          STORAGECLASS       REASON    AGE pvc-ca4df10f-ab8c-11e8-b89d-12331aa95b13   1Gi        RWO            Delete           Bound     default/code   do-block-storage             2m 

A excepción de Reclaim Policy y Status, en los campos anteriores se muestra un resumen de su archivo de configuración. Reclaim Policy define lo que se hace con el VP después de que el PVC que lo accede es eliminado. Delete elimina el VP de Kubernetes y la infraestructura de DigitalOcean. Puede obtener más información sobre Reclaim Policy y Status en la documentación de VP de Kubernetes.

De esta manera, habrá creado correctamente un volumen persistente utilizando el complemento de almacenamiento en bloques de DigitalOcean. Ahora que su volumen persistente está listo, creará sus pods con una implementación.

Paso 4: Crear una implementación de PHP-FPM

En este paso, aprenderá a utilizar una implementación para crear su pod de PHP-FPM. Las implementaciones proporcionan una manera uniforme de crear, actualizar y administrar pods utilizando ReplicaSets. Si una actualización no funciona como se espera, una implementación restaurará de manera automática sus pods a una imagen anterior.

La clave de despliegue spec.selector enumerará las etiquetas de los pods que administrará. También utilizará la clave template para crear los pods necesarios.

En este paso también se introducirá el uso de contenedores Init. Los contenedores Init ejecutan uno o más comandos antes que los contenedores regulares especificados en la clave template del pod. En este tutorial, su contenedor Init obtendrá un archivo de muestra de index.php de GitHub Gist utilizando wget. El archivo de muestra contiene lo siguiente:

index.php

<?php echo phpinfo(); 

Para crear su implementación, abra un nuevo archivo llamado php_deployment.yaml con su editor:

  • nano php_deployment.yaml

Esta implementación administrará sus pods de PHP-FPM, por lo que dará al objeto de implementación el nombre php. Los pods pertenecen al nivel de backend, por lo que agrupará la implementación en este grupo utilizando la etiqueta tier: backend:

php_deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata:   name: php   labels:     tier: backend 

Para la implementación spec, especificará la cantidad de copias de este pod que se crearán utilizando el parámetro replicas. El número de replicas variará según sus necesidades y los recursos disponibles. En este tutorial, creará una réplica:

php_deployment.yaml

... spec:   replicas: 1 

La implementación administrará pods que coincidan con app: php y las etiquetas de tier: backend. En la clave de selector agregue:

php_deployment.yaml

...   selector:     matchLabels:       app: php       tier: backend 

A continuación, la implementación spec requiere el elemento template para la definición de objeto de su pod. Esta plantilla definirá las especificaciones a partir de las cuales se creará el pod. Primero, agregará las etiquetas que se especificaron para el servicio php selectors y las matchLabels de la implementación. Agregue app: php y tier: backend en template.metadata.labels:

php_deployment.yaml

...   template:     metadata:       labels:         app: php         tier: backend 

Un pod puede tener varios contenedores y volúmenes, pero cada uno requerirá un nombre. Puede montar de manera selectiva volúmenes en un contenedor especificando una ruta de montaje para cada volumen.

Primero, especifique los volúmenes a los que accederá su contenedor. Creó un PVC llamado code para contener el código de su aplicación. Por lo tanto dé el nombre code a este volumen también. En spec.template.spec.volumes, agregue lo siguiente:

php_deployment.yaml

...     spec:       volumes:       - name: code         persistentVolumeClaim:           claimName: code 

A continuación, especifique el contenedor que desee ejecutar en este pod. Puede encontrar varias imágenes en la tienda de Docker, pero en este tutorial empleará la imagen php:7-fpm.

En spec.template.spec.containers, agregue lo siguiente:

php_deployment.yaml

...       containers:       - name: php         image: php:7-fpm 

A continuación, montará los volúmenes a los que el contenedor solicita acceso. Este contenedor ejecutará su código PHP, de modo que deberá acceder al volumen code. También utilizará mountPath para especificar /code como punto de montaje.

En spec.template.spec.containers.volumeMounts, agregue lo siguiente:

php_deployment.yaml

...         volumeMounts:         - name: code           mountPath: /code 

Ahora que montó su volumen, debe introducir el código de su aplicación en el volumen. Es posible que para hacerlo haya utilizado previamente FTP/SFTP o clonado el código a través de una conexión SSH, pero en este paso verá la forma de copiar el código utilizando un contenedor Init.

Según la complejidad de su proceso de configuración, puede utilizar un solo initContainer para ejecutar una secuencia de comandos que construya su aplicación, o puede utilizar un initContainer por comando. Asegúrese de que los volúmenes se monten en el initContainer.

En este tutorial, utilizará un contenedor Init único con busybox para descargar el código. busybox es una pequeña imagen que contiene la utilidad wget que utilizará para hacerlo.

En spec.template.spec, agregue su initContainer y especifique la imagen busybox:

php_deployment.yaml

...       initContainers:       - name: install         image: busybox 

Su contenedor Init necesitará acceso al volumen code para que pueda descargar el código en esa ubicación. En spec.template.spec.initContainers, monte el volumen code en la ruta /code:

php_deployment.yaml

...         volumeMounts:         - name: code           mountPath: /code 

Cada contenedor Init debe ejecutar un command. Su contenedor Init utilizará wget para descargar el código de Github en el directorio de trabajo /code. La opción -O asigna un nombre al archivo descargado, y usted dará a este archivo el nombre index.php.

Nota: Asegúrese de el código que extraerá sea confiable. Antes de introducir el código fuente en su servidor, inspecciónelo para asegurarse de que las funciones que realiza le parezcan adecuadas.

En el contenedor install en spec.template.spec.initContainers, agregue estas líneas:

php_deployment.yaml

...         command:         - wget         - "-O"         - "/code/index.php"         - https://raw.githubusercontent.com/do-community/php-kubernetes/master/index.php 

Su archivo php_deployment.yaml completo tendrá este aspecto:

php_deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata:   name: php   labels:     tier: backend spec:   replicas: 1   selector:     matchLabels:       app: php       tier: backend   template:     metadata:       labels:         app: php         tier: backend     spec:       volumes:       - name: code         persistentVolumeClaim:           claimName: code       containers:       - name: php         image: php:7-fpm         volumeMounts:         - name: code           mountPath: /code       initContainers:       - name: install         image: busybox         volumeMounts:         - name: code           mountPath: /code         command:         - wget         - "-O"         - "/code/index.php"         - https://raw.githubusercontent.com/do-community/php-kubernetes/master/index.php 

Guarde el archivo y salga del editor.

Cree la implementación de PHP-FPM con kubectl:

  • kubectl apply -f php_deployment.yaml

Visualizará el siguiente resultado al crear la implementación:

Outputdeployment.apps/php created 

A modo de resumen, esta implementación se iniciará descargando las imágenes especificadas. Luego, solicitará el PersistentVolume de su PersistentVolumeClaim y ejecutará en serie sus initContainers. Una vez completado el proceso, los contenedores ejecutarán y montarán los volumes en punto de montaje especificado. Una vez que todos estos pasos se hayan completado, su pod estará listo y en ejecución.

Puede ver su implementación ejecutando lo siguiente:

  • kubectl get deployments

Verá el siguiente resultado:

OutputNAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE php       1         1         1            0           19s 

Este resultado puede ayudarle a comprender el estado actual de la implementación. Deployment es uno de los controladores que conservan un estado deseado. El elemento template que creó especifica que un elemento replicas del pod del estado DESIRED se llamará php. El campo CURRENT indica cuántas réplicas se encuentran en ejecución, por lo que debe coincidir con el estado DESIRED. Puede leer información sobre los campos restantes en la documentación de implementación de Kubernetes.

Puede ver los pods iniciados por esta implementación con el siguiente comando:

  • kubectl get pods

El resultado de este comando varía según el tiempo transcurrido desde la creación de la implementación. Si la ejecuta poco después de su creación, el resultado probablemente tendrá este aspecto:

OutputNAME                   READY     STATUS     RESTARTS   AGE php-86d59fd666-bf8zd   0/1       Init:0/1   0          9s 

Las columnas representan la siguiente información:

  • Ready: número de replicas que ejecutan este pod.
  • Status: estado del pod. Init indica que los contenedores Init están en ejecución. En este resultado, del total de 1 contenedor Init, ninguno terminó de ejecutarse.
  • Restarts: cantidad de veces que este proceso se reinició para iniciar el pod. Este número aumentará si alguno de sus contenedores Init falla. La implementación se reiniciará hasta que alcance un estado deseado.

Según de la complejidad de sus secuencias de comandos de inicio, pueden pasar algunos minutos hasta que el estado cambie a podInitializing:

OutputNAME                   READY     STATUS            RESTARTS   AGE php-86d59fd666-lkwgn   0/1       podInitializing   0          39s 

Esto significa que los contenedores Init han finalizado y que se están iniciando los contenedores. Si ejecuta el comando cuando todos los contenedores estén en ejecución, verá que el estado de pod cambiará a Running.

OutputNAME                   READY     STATUS            RESTARTS   AGE php-86d59fd666-lkwgn   1/1       Running   0          1m 

Ahora verá que su pod se ejecuta de forma correcta. Si su pod no se inicia, puede realizar una depuración con los siguientes comandos:

  • Ver información detallada de un pod:
  • kubectl describe pods pod-name
  • Ver registros generados por un pod:
  • kubectl logs pod-name
  • Ver registros para un contenedor específico en un pod:
  • kubectl logs pod-name container-name

Su código de aplicación está montado y el servicio de PHP-FPM ya está listo para manejar conexiones. Ahora podrá crear su implementación de Nginx.

Paso 5: Crear la implementación de Nginx

En este paso, utilizará un ConfigMap para configurar Nginx. Un ConfigMap contiene su configuración en un formato de clave-valor al que puede hacer referencia en otras definiciones de objetos de Kubernetes. Este enfoque le brindará la flexibilidad necesaria para reutilizar o cambiar la imagen con una versión de Nginx distinta si es necesario. Actualizar ConfigMap replicará los cambios de manera automática en cualquier pod montado en él.

Con su editor, cree un archivo nginx_configMap.yaml para su ConfigMap:

  • nano nginx_configMap.yaml

Dé el nombre nginx-config a ConfigMap y agrúpelo en el microservicio de tier: backend:

nginx_configMap.yaml

apiVersion: v1 kind: ConfigMap metadata:   name: nginx-config   labels:     tier: backend 

A continuación, agregará data para ConfigMap. Dé a la clave el nombre config y añada el contenido de su archivo de configuración de Nginx como valor. Puede utilizar el ejemplo de configuración de Nginx de este tutorial.

Debido a que Kubernetes puede dirigir solicitudes al host adecuado para un servicio, puede ingresar el nombre de su servicio de PHP-FPM en el parámetro fastcgi_pass en lugar de su dirección IP. Agregue lo siguiente a su archivo nginx_configMap.yaml:

nginx_configMap.yaml

... data:   config : |     server {       index index.php index.html;       error_log  /var/log/nginx/error.log;       access_log /var/log/nginx/access.log;       root ^/code^;        location / {           try_files $uri $uri/ /index.php?$query_string;       }        location ~ .php$ {           try_files $uri =404;           fastcgi_split_path_info ^(.+.php)(/.+)$;           fastcgi_pass php:9000;           fastcgi_index index.php;           include fastcgi_params;           fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;           fastcgi_param PATH_INFO $fastcgi_path_info;         }     } 

Su archivo nginx_configMap.yaml tendrá el siguiente aspecto:

nginx_configMap.yaml

apiVersion: v1 kind: ConfigMap metadata:   name: nginx-config   labels:     tier: backend data:   config : |     server {       index index.php index.html;       error_log  /var/log/nginx/error.log;       access_log /var/log/nginx/access.log;       root /code;        location / {           try_files $uri $uri/ /index.php?$query_string;       }        location ~ .php$ {           try_files $uri =404;           fastcgi_split_path_info ^(.+.php)(/.+)$;           fastcgi_pass php:9000;           fastcgi_index index.php;           include fastcgi_params;           fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;           fastcgi_param PATH_INFO $fastcgi_path_info;         }     } 

Guarde el archivo y salga del editor.

Cree el ConfigMap:

  • kubectl apply -f nginx_configMap.yaml

Verá lo siguiente:

Outputconfigmap/nginx-config created 

Con esto, habrá terminado de crear su ConfigMap y ya podrá crear su implementación de Nginx.

Comience abriendo un nuevo archivo nginx_deployment.yaml en el editor:

  • nano nginx_deployment.yaml

Dé el nombre nginx a la implementación y añada la etiqueta tier: backend:

nginx_deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata:   name: nginx   labels:     tier: backend 

Especifique que desea un elemento replicas en la implementación spec. Esta implementación administra pods con etiquetas app: nginx y tier: backend. Agregue los siguientes parámetros y valores:

nginx_deployment.yaml

... spec:   replicas: 1   selector:     matchLabels:       app: nginx       tier: backend 

A continuación, agregue el elemento template del pod. Debe utilizar las mismas etiquetas que agregó para selector.matchLabels de la implementación. Agregue lo siguiente:

nginx_deployment.yaml

...   template:     metadata:       labels:         app: nginx         tier: backend 

Habilite el acceso de Nginx al PVC code que creó previamente. En spec.template.spec.volumes, agregue lo siguiente:

nginx_deployment.yaml

...     spec:       volumes:       - name: code         persistentVolumeClaim:           claimName: code 

Los pods pueden montar un ConfigMap como un volumen. Especificar un nombre de archivo y una clave creará un archivo con su valor como el contenido. Para usar ConfigMap, fije path en el nombre del archivo que tendrá el contenido de key. EL objetivo es crear un archivo site.conf a partir de la clave config. En spec.template.spec.volumes, agregue lo siguiente:

nginx_deployment.yaml

...       - name: config         configMap:           name: nginx-config           items:           - key: config             path: site.conf 

Advertencia: Si no se especifica un archivo, el contenido de key sustituirá el elemento mountPath del volumen. Esto quiere decir que si una ruta no está especificada de manera explícita, perderá todo el contenido de la carpeta de destino.

A continuación, especificará la imagen a partir de la cual se creará su pod. En este tutorial se utilizará la imagen nginx:1.7.9 por cuestiones de estabilidad, pero puede encontrar otras imágenes de Nginx en la tienda de Docker. Además, debe hacer que Nginx esté disponible en el puerto 80. En spec.template.spec, agregue lo siguiente:

nginx_deployment.yaml

...       containers:       - name: nginx         image: nginx:1.7.9         ports:         - containerPort: 80 

Nginx y PHP-FPM deben acceder al archivo en la misma ruta. Por ello, monte el volumen de code en /code:

nginx_deployment.yaml

...         volumeMounts:         - name: code           mountPath: /code 

La imagen nginx:1.7.9 cargará de manera automática cualquier archivo de configuración en el directorio /etc/nginx/conf.d. Si se monta el volumen de config en este directorio, se creará el archivo /etc/nginx/conf.d/site.conf. En volumeMounts, agregue lo siguiente:

nginx_deployment.yaml

...         - name: config           mountPath: /etc/nginx/conf.d 

Su archivo nginx_deployment.yaml tendrá el siguiente aspecto:

nginx_deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata:   name: nginx   labels:     tier: backend spec:   replicas: 1   selector:     matchLabels:       app: nginx       tier: backend   template:     metadata:       labels:         app: nginx         tier: backend     spec:       volumes:       - name: code         persistentVolumeClaim:           claimName: code       - name: config         configMap:           name: nginx-config           items:           - key: config             path: site.conf       containers:       - name: nginx         image: nginx:1.7.9         ports:         - containerPort: 80         volumeMounts:         - name: code           mountPath: /code         - name: config           mountPath: /etc/nginx/conf.d 

Guarde el archivo y salga del editor.

Cree la implementación de Nginx:

  • kubectl apply -f nginx_deployment.yaml

El siguiente resultado indica que se creó su implementación:

Outputdeployment.apps/nginx created 

Enumere sus implementaciones con el siguiente comando:

  • kubectl get deployments

Visualizará las implementaciones de Nginx y PHP-FPM:

OutputNAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE nginx     1         1         1            0           16s php       1         1         1            1           7m 

Enumere los pods administrados por ambas implementaciones:

  • kubectl get pods

Visualizará los pods que estén en ejecución:

OutputNAME                     READY     STATUS    RESTARTS   AGE nginx-7bf5476b6f-zppml   1/1       Running   0          32s php-86d59fd666-lkwgn     1/1       Running   0          7m 

Ahora que todos los objetos de Kubernetes están activos, podrá visitar el servicio de Nginx en su navegador.

Enumere los servicios en ejecución:

  • kubectl get services -o wide

Obtenga la IP externa para su servicio de Nginx:

OutputNAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE       SELECTOR kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    39m       <none> nginx        ClusterIP   10.102.160.47   your_public_ip 80/TCP     27m       app=nginx,tier=backend php          ClusterIP   10.100.59.238   <none>        9000/TCP   34m       app=php,tier=backend 

En su navegador, visite su servidor escribiendo http://your_public_ip. Verá el resultado de php_info() y habrá confirmado que sus servicios de Kubernetes están configurados y activos.

Conclusión

A través de esta guía , cargó en contenedores los servicios de PHP-FPM y Nginx para poder administrarlos de manera independiente. Este enfoque no solo mejorará la escalabilidad de su proyecto a medida que amplíe sus capacidades, sino también le permitirá utilizar los recursos de manera eficaz. También almacenó el código de su aplicación en un volumen para poder actualizar sus servicios de manera sencilla en el futuro.