One place for hosting & domains

      implementar

      Cómo implementar y administrar su DNS usando DNSControl en Debian 10


      El autor seleccionó la Electronic Frontier Foundation Inc para recibir una donación como parte del programa Write for DOnations.

      Introducción

      DNSControl es una herramienta de infraestructura como código que le permite implementar y administrar sus zonas de DNS usando principios de desarrollo de software estándar, el control de versiones, pruebas e implementaciones automatizadas. DNSControl es una creación de Stack Exchange y se escribió en Go.

      El uso de DNSControl elimina muchas de las dificultades de la administración manual de DNS, ya que los archivos de zona se almacenan en un formato programable. Esto le permite implementar zonas en varios proveedores de DNS de manera simultánea, identificar errores de sintaxis y aplicar su configuración de DNS de manera automática, lo que reduce el riesgo que representan los errores humanos. Otro uso frecuente de DNSControl tiene que ver con la migración rápida de su DNS a un proveedor diferente; por ejemplo, en caso de un ataque DDoS o una interrupción del sistema.

      A través de este tutorial, instalará y configurará DNSControl, creará una configuración de DNS básica y comenzará a implementar registros de DNS en un proveedor activo. Como parte de este tutorial, usaremos DigitalOcean como proveedor DNS de ejemplo. Si desea usar un proveedor diferente, la configuración es muy similar. Al finalizar, podrá administrar y probar su configuración de DNS en un entorno seguro y sin conexión, y luego implementarla de manera automática en la producción.

      Requisitos previos

      Para completar esta guía, necesitará lo siguiente:

      • Un servidor de Debian 10 preparado conforme a la configuración inicial para servidores con Debian 10, incluidos un usuario sudo no root y un firewall habilitado para bloquear los puertos no esenciales. your-server-ipv4-address hace referencia a la dirección IP del servidor en donde aloja su sitio web o dominio. your-server-ipv6-address hace referencia a la dirección IPv6 del servidor en donde aloja su sitio web o dominio.
      • Un nombre de dominio registrado por completo con DNS alojado por un proveedor compatible. En este tutorial, se utilizará your_domain en todo momento y DigitalOcean será el proveedor de servicio.
      • Una clave de API de DigitalOcean (Token de acceso personal) con permisos de lectura y escritura. Para crear uno, consulte Cómo crear un Token de acceso personal.

      Una vez que tenga todo esto listo, para comenzar inicie sesión en su servidor como usuario no root.

      Paso 1: Instalar DNSControl

      DNSControl está escrito en Go, por lo que comenzará este paso instalando Go en su servidor y configurando su GOPATH.

      Go está disponible en los repositorios de software predeterminados de Debian, lo que permite la instalación con las herramientas convencionales de administración de paquetes.

      También deberá instalar Git, ya que es necesario para que en Go se descargue e instale el software DNSControl desde su repositorio en GitHub.

      Comience actualizando el índice de paquetes locales de modo que se refleje cualquier cambio anterior:

      Luego, instale los paquetes golang-go y git:

      • sudo apt install golang-go git

      Una vez confirmada la instalación, apt descargará e instalará Go y Git, así como todas las dependencias necesarias de estos.

      A continuación, configurará las variables de entorno de ruta necesarias para Go. Si desea obtener más información sobre esto, puede leer el siguiente tutorial de información sobre GOPATH. Comience editando el archivo ~/.profile:

      Añada las siguientes líneas al final de su archivo:

      ~/.profile

      ...
      export GOPATH="$HOME/go"
      export PATH="$PATH:$GOPATH/bin"
      

      Una vez que añada estas líneas al final del archivo, guárdelo y ciérrelo. Luego vuelva a cargar su perfil cerrando y volviendo a abrir la sesión, o bien volviendo a obtener el archivo:

      Ahora que instaló y configuró Go, puede instalar DNSControl.

      El comando go get se puede usar para obtener una copia del código, compilarlo automáticamente e instalarlo en su directorio de Go:

      • go get github.com/StackExchange/dnscontrol

      Una vez completado esto, puede verificar la versión instalada para asegurar que todo funcione:

      El resultado debe tener un aspecto similar al siguiente:

      Output

      dnscontrol 2.9-dev

      Si ve un error dnscontrol: command not found, verifique bien la configuración de su ruta de Go.

      Ahora que instaló DNSControl, puede crear un directorio de configuración y conectar DNSControl a su proveedor DNS para permitir que haga cambios en sus registros de DNS.

      Paso 2: Configurar DNSControl

      En este paso, creará los directorios de configuración necesarios para DNSControl y lo conectará a su proveedor DNS para que pueda comenzar a realizar cambios en tiempo real en sus registros de DNS.

      Primero, cree un directorio nuevo en el que pueda almacenar su configuración de DNSControl y luego posiciónese en este:

      • mkdir ~/dnscontrol
      • cd ~/dnscontrol

      Nota: En este tutorial, nos enfocaremos en la configuración inicial de DNSControl. Sin embargo, para el uso en producción se le recomienda almacenar su configuración de DNSControl en un sistema de control de versiones (VCS) como Git. Entre las ventajas de esto se incluyen un control de versiones completo, la integración con CI/CD para pruebas y las implementaciones de versiones anteriores sin problemas.

      Si planea usar DNSControl para escribir archivos de zona BIND, también deberá crear el directorio zones:

      Los archivos de zona BIND son una alternativa básica estándar para almacenar zonas y registros de DNS en formato de texto simple. Originalmente se utilizaron para el software del servidor DNS BIND, pero ahora se han adoptado ampliamente como el método estándar para almacenar zonas de DNS. Los archivos de zona BIND producidos por DNSControl son útiles si quiere importarlos a un servidor DNS personalizado o con alojamiento propio, o bien para fines de auditoría.

      Sin embargo, si solo quiere usar DNSControl para aplicar cambios de DNS a un proveedor administrado, no se necesitará el directorio zones.

      A continuación, deberá configurar el archivo creds.json, lo cual permitirá la autenticación de DNSControl en su proveedor DNS y la aplicación de cambios. El formato de creds.json varia ligeramente dependiendo del proveedor DNS que utilice. Consulte la lista de proveedores de servicio en la documentación oficial de DNSControl para hallar la configuración de su propio proveedor.

      Cree el archivo creds.json en el directorio ~/dnscontrol:

      • cd ~/dnscontrol
      • nano creds.json

      Añada la configuración de muestra creds.json para su proveedor DNS al archivo. Si utiliza DigitalOcean como su proveedor DNS, puede recurrir a lo siguiente:

      ~/dnscontrol/creds.json

      {
      "digitalocean": {
        "token": "your-digitalocean-oauth-token"
      }
      }
      

      En este archivo se indican a DNSControl los proveedores de DNS con los que desea establecer conexión.

      Deberá proporcionar alguna forma de autenticación para proveedor DNS. Normalmente, se utiliza una clave de API o un token de OAuth, pero algunos proveedores requieren información adicional, como se detalla en la lista de proveedores de servicio de la documentación oficial de DNSControl.

      Advertencia: Este token brindará acceso a la cuenta de su proveedor DNS, por lo que debe protegerlo como si se tratara de una contraseña. También compruebe que, si utiliza un sistema de control de versiones, el archivo que contenga el token esté excluido (por ejemplo, con .gitignore), o cifrado de forma segura de alguna manera.

      Si utiliza DigitalOcean como su proveedor DNS, puede usar el token de OAuth requerido en la configuración de su cuenta de DigitalOcean que generó como parte de los requisitos previos.

      Si tiene varios proveedores de DNS diferentes, por ejemplo, para diferentes nombres de dominio o zonas de DNS delegadas, puede definir todos estos en el mismo archivo creds.json.

      Con esto, estableció los directorios de configuración inicial de DNSControl y configuró creds.json para permitir que DNSControl se autentique en su proveedor DNS y realice cambios. A continuación, creará la configuración para sus zonas de DNS.

      Paso 3: Crear un archivo de configuración de DNS

      Durante este paso, creará un archivo de configuración de DNS inicial, que contendrá los registros de DNS para su nombre de dominio o zona de DNS delegada.

      dnsconfig.js es el archivo de configuración de DNS principal para DNSControl. En este archivo, las zonas de DNS y sus registros correspondientes se definen usando sintaxis de JavaScript. Esto se conoce como DS o lenguaje específico de dominio. En la página de DSL en JavaScript de la documentación oficial de DNSControl hay más información disponible.

      Para comenzar, cree el archivo de configuración de DNS en el directorio ~/dnscontrol:

      • cd ~/dnscontrol
      • nano dnsconfig.js

      Luego, añada la siguiente configuración de ejemplo al archivo:

      ~/dnscontrol/dnsconfig.js

      // Providers:
      
      var REG_NONE = NewRegistrar('none', 'NONE');
      var DNS_DIGITALOCEAN = NewDnsProvider('digitalocean', 'DIGITALOCEAN');
      
      // Domains:
      
      D('your_domain', REG_NONE, DnsProvider(DNS_DIGITALOCEAN),
        A('@', 'your-server-ipv4-address')
      );
      

      Mediante este archivo de ejemplo se define un nombre de dominio o una zona de DNS en un proveedor determinado, que en este caso es your_domain alojado en DigitalOcean. También se define un ejemplo de registro A para la zona root (@) con orientación hacia la dirección IPv4 del servidor en el que aloja su dominio y sitio web.

      Existen tres funciones principales que conforman un archivo básico de configuración de DNSControl

      • NewRegistrar(name, type, metadata): define el registrador de dominio para su nombre de dominio. DNSControl puede utilizar esto para realizar los cambios necesarios, como la modificación de los servidores de nombres autoritativos. Si solo quiere usar DNSControl para administrar sus zonas de DNS, generalmente puede dejar el valor NONE.

      • NewDnsProvider(name, type, metadata): define un proveedor de servicio de DNS para su nombre de dominio o zona delegada. Aquí es donde DNSControl aplicará los cambios de DNS que realice.

      • D(name, registrar, modifiers): define un nombre de dominio o una zona de DNS delegada para DNSControl se encargue de la administración, como también los registros de DNS presentes en la zona.

      Deberá configurar NewRegistrar(), NewDnsProvider() y D() respectivamente usando la lista de proveedores de servicio de la documentación oficial de DNSControl.

      Si utiliza DigitalOcean como su proveedor DNS y solo necesita poder realizar cambios en el DNS (en lugar de servidores de nombres autoritativos también), el ejemplo del bloque de código anterior ya es válido.

      Una vez que termine, guarde y cierre el archivo.

      En este paso, creó un archivo de configuración de DNS para DNSControl, con los proveedores correspondientes definidos. A continuación, completará el archivo con algunos registros de DNS útiles.

      Paso 4: Completar su archivo de configuración de DNS

      A continuación, podrá completar el archivo de configuración de DNS con registros de DNS útiles para su sitio web o servicio, con la sintaxis de DNSControl.

      A diferencia de los archivos de zona BIND tradicionales, en los cuales los registros DNS están escritos en un formato básico, línea por línea, los registros DNS dentro de DNSControl se definen como un parámetro de función (modificador de dominio) en la función D(), como se muestra en forma resumida en el paso 3.

      Existe un modificador de dominio para cada uno de los tipos de registro de DNS estándares, entre los que se incluyen A, AAAA, MX, TXT, NS y CAA. Se encuentra disponible una lista completa de los tipos de registro en la sección Modificadores de dominios de la documentación de DNSControl.

      También se encuentran disponibles los modificadores para los registros individuales (modificadores de registros). Actualmente, se utilizan principalmente para configurar el TTL (período de vida) de registros individuales. Se encuentra disponible una lista completa de los modificadores de registros en la sección Modificadores de registros de la documentación de DNSControl. Los modificadores de registros son opcionales y en la mayoría de los casos de uso básicos pueden quedar excluidos.

      La sintaxis para configurar registros DNS varía ligeramente según cada tipo de registro. A continuación, se muestran algunos ejemplos para los tipos de registro más comunes:

      • Registros A:

        • Propósito: apuntar a una dirección IPv4.
        • Sintaxis: A('name', 'address', optional record modifiers).
        • Ejemplo: A('@', 'your-server-ipv4-address', TTL(30)).
      • Registros AAAA:

        • Propósito: apuntar a una dirección IPv6.
        • Sintaxis: AAAA('name', 'address', optional record modifiers).
        • Ejemplo: AAAA('@', 'your-server-ipv6-address') (se omitió el modificador de registros, por lo que se utilizará el TTL predeterminado).
      • Registros CNAME:

        • Propósito: convertir su dominio o subdominio en un alias de otro.
        • Sintaxis: CNAME('name', 'target', optional record modifiers).
        • Ejemplo: CNAME('subdomain1', 'example.org.') (tenga en cuenta que se debe incluir un . al final si hay algún punto en el valor).
      • Registros MX:

        • Propósito: dirigir el correo electrónico hacía direcciones o servidores específicos.
        • Sintaxis: MX('name', 'priority', 'target', optional record modifiers).
        • Ejemplo: MX('@', 10, 'mail.example.net') (tenga en cuenta que se debe incluir un . “al final si hay algún punto en el valor).
      • Registros de TXT:

        • Propósito: agregar texto simple arbitrario, a menudo utilizado para las configuraciones sin su propio tipo de registro dedicado.
        • Sintaxis: TXT('name', 'content', optional record modifiers).
        • Ejemplo: TXT('@', 'This is a TXT record.').
      • Registros CAA:

        • Propósito: restringir las entidades de certificación (CA) que pueden emitir certificados TLS para su dominio y subdominios, y presentar informes sobre ellas.
        • Sintaxis: CAA('name', 'tag', 'value', optional record modifiers).
        • Ejemplo: CAA('@', 'issue', 'letsencrypt.org').

      Con el fin de comenzar a añadir registros de DNS para su dominio o zona de DNS delegada, edite su archivo de configuración DNS:

      A continuación, puede comenzar a completar los parámetros para la función D() existente usando la sintaxis descrita en la lista anterior, así como la sección Modificadores de dominio de la documentación oficial de DNSControl. Se debe utilizar una coma (,) entre cada registro.

      A modo de referencia, el bloque de código aquí contiene una configuración de ejemplo completa para una configuración básica e inicial de un DNS:

      ~/dnscontrol/dnsconfig.js

      ...
      
      D('your_domain', REG_NONE, DnsProvider(DNS_DIGITALOCEAN),
        A('@', 'your-server-ipv4-address'),
        A('www', 'your-server-ipv4-address'),
        A('mail', 'your-server-ipv4-address'),
        AAAA('@', 'your-server-ipv6-address'),
        AAAA('www', 'your-server-ipv6-address'),
        AAAA('mail', 'your-server-ipv6-address'),
        MX('@', 10, 'mail.your_domain.'),
        TXT('@', 'v=spf1 -all'),
        TXT('_dmarc', 'v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;')
      );
      

      Una vez que haya completado su configuración inicial de DNS, guarde y cierre el archivo.

      Durante este paso, creó el archivo de configuración de DNS inicial que contiene sus registros de DNS. A continuación, probará la configuración y la implementará.

      Paso 5: Probar e implementar su configuración de DNS

      En este paso, ejecutará una verificación de sintaxis local en su configuración de DNS y luego implementará los cambios en el servidor y proveedor DNS activos.

      Primero, posiciónese en el directorio dnscontrol:

      Luego, utilice la función preview de DNSControl para verificar la sintaxis de su archivo y mostrar los cambios que se realizarán (sin efectuarlos realmente):

      Si la sintaxis de su archivo de configuración de DNS es correcta, en DNSControl se mostrará un resumen de los cambios que se realizarán. Esto debe tener un aspecto similar a lo siguiente:

      Output

      ******************** Domain: your_domain ----- Getting nameservers from: digitalocean ----- DNS Provider: digitalocean...8 corrections #1: CREATE A your_domain your-server-ipv4-address ttl=300 #2: CREATE A www.your_domain your-server-ipv4-address ttl=300 #3: CREATE A mail.your_domain your-server-ipv4-address ttl=300 #4: CREATE AAAA your_domain your-server-ipv6-address ttl=300 #5: CREATE TXT _dmarc.your_domain "v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;" ttl=300 #6: CREATE AAAA www.your_domain your-server-ipv6-address ttl=300 #7: CREATE AAAA mail.your_domain your-server-ipv6-address ttl=300 #8: CREATE MX your_domain 10 mail.your_domain. ttl=300 ----- Registrar: none...0 corrections Done. 8 corrections.

      Si ve una advertencia de error en el resultado, DNSControl proporcionará detalles sobre el tipo de error y el punto en el que se encuentra dentro de su archivo.

      Advertencia: A través del siguiente comando se harán cambios en tiempo real en sus registros DNS y posiblemente en otras configuraciones. Asegúrese de estar preparado para esto; incluya una copia de seguridad de su configuración de DNS existente y verifique que disponga de los medios necesarios para revertir los cambios si es necesario.

      Por último, puede aplicar los cambios a su proveedor DNS activo:

      Verá un resultado similar al siguiente:

      Output

      ******************** Domain: your_domain ----- Getting nameservers from: digitalocean ----- DNS Provider: digitalocean...8 corrections #1: CREATE TXT _dmarc.your_domain "v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;" ttl=300 SUCCESS! #2: CREATE A your_domain your-server-ipv4-address ttl=300 SUCCESS! #3: CREATE AAAA your_domain your-server-ipv6-address ttl=300 SUCCESS! #4: CREATE AAAA www.your_domain your-server-ipv6-address ttl=300 SUCCESS! #5: CREATE AAAA mail.your_domain your-server-ipv6-address ttl=300 SUCCESS! #6: CREATE A www.your_domain your-server-ipv4-address ttl=300 SUCCESS! #7: CREATE A mail.your_domain your-server-ipv4-address ttl=300 SUCCESS! #8: CREATE MX your_domain 10 mail.your_domain. ttl=300 SUCCESS! ----- Registrar: none...0 corrections Done. 8 corrections.

      Ahora, si revisa las configuraciones de DNS para su dominio en el panel de control de DigitalOcean, podrá visualizar los cambios.

      Captura de pantalla del panel de control de DigitalOcean; se muestran algunos de los cambios de DNS realizados en DNSControl.

      También puede verificar la creación de registros ejecutando una solicitud de DNS para su dominio o zona delegando usando dig.

      Si no instaló dig, deberá instalar el paquete dnsutils:

      • sudo apt install dnsutils

      Una vez que instale dig, podrá utilizarlo con el propósito de realizar una búsqueda de DNS para su dominio. Verá que los registros se actualizaron de forma correspondiente:

      Verá un resultado que muestra la dirección IP y los registros de DNS pertinentes de su zona implementada usando DNSControl. Los registros DNS pueden tardar tiempo en propagarse, por lo cual es posible que necesite esperar y ejecutar este comando de nuevo.

      En este último paso, realizó una verificación de sintaxis local del archivo de configuración de DNS, luego lo implementó en su proveedor DNS activo y verificó mediante pruebas que los cambios se realizaran correctamente.

      Conclusión

      A lo largo de este artículo, configuró DNSControl e implementó una configuración de DNS en un proveedor activo. Ahora podrá administrar y probar sus cambios de configuración de DNS en un entorno seguro y sin conexión antes de implementarlos en la producción.

      Si desea profundizar en este tema, DNSControl está diseñado para integrarse en su proceso de CI/CD, lo que le permite realizar pruebas exhaustivas y tener más control sobre su implementación en la producción. También podría considerar la integración de DNSControl en sus procesos de compilación e implementación de infraestructuras, lo que le permitirá implementar servidores y agregarlos al DNS de manera completamente automática.

      Si desea dar un paso más con DNSControl, en los siguientes artículos de DigitalOcean se ofrecen algunos pasos interesantes que puede seguir para contribuir a la integración de DNSControl en sus flujos de trabajo de administración de cambios e implementación de infraestructura:



      Source link

      Cómo implementar y administrar su DNS usando OctoDNS en Debian 10


      El autor seleccionó la Electronic Frontier Foundation para recibir una donación como parte del programa Write for DOnations.

      Introducción

      OctoDNS es una herramienta de infraestructura como código que le permite implementar y administrar sus zonas de DNS usando principios de desarrollo de software estándar, control de versiones, pruebas e implementaciones automatizadas. OctoDNS es una creación de GitHub y está escrito en Python.

      El uso de OctoDNS elimina muchas de las dificultades de la administración de DNS manual, ya que los archivos de zona se almacenan en un formato estructurado (YAML). Esto le permite implementar zonas en varios proveedores de DNS de manera simultánea, identificar errores de sintaxis y aplicar su configuración de DNS de manera automática, lo que reduce el riesgo que representan los errores humanos. Otro uso común de OctoDNS tiene que ver con la sincronización de su configuración de DNS entre diferentes proveedores, como en el caso de un sistema de pruebas y producción o entre entornos activos y de conmutación por error.

      OctoDNS es similar a DNSControl, una herramienta equivalente creada por Stack Exchange y escrita en Go. A diferencia de OctoDNS, DNSControl utiliza un lenguaje de configuración basado en JavaScript para definir las zonas de DNS, lo cual le permite usar funciones de programación avanzadas como los bucles para especificar varios registros similares dentro de la misma zona. En el artículo Cómo implementar y administrar su DNS utilizando DNSControl en Debian 10 se abarcan la instalación y configuración básicas de DNSControl.

      A través de este tutorial, instalará y configurará OctoDNS, creará una configuración de DNS básica y comenzará a implementar registros de DNS en un proveedor activo. Como parte de este tutorial, usaremos DigitalOcean como proveedor DNS de ejemplo. Si quiere utilizar un proveedor diferente, la configuración es muy similar. Al finalizar, podrá administrar y probar su configuración de DNS en un entorno seguro y sin conexión, y luego implementarla de manera automática en la producción.

      Requisitos previos

      Para completar esta guía, necesitará lo siguiente:

      • Un servidor de Debian 10 preparado conforme la Configuración inicial para servidores con Debian 10, incluidos un usuario sudo no root y un firewall habilitado para bloquear los puertos no esenciales. your-server-ipv4-adress y your-server-ipv6-address hacen referencia a las direcciones IP del servidor en el que aloja su sitio web o dominio.
      • Un nombre de dominio registrado por completo con DNS alojado por un proveedor compatible. En este tutorial, se utilizará your_domain en todo momento y DigitalOcean será el proveedor de servicio.
      • Una clave de API de DigitalOcean (Token de acceso personal) con permisos de lectura y escritura. Para crear uno, consulte Cómo crear un Token de acceso personal.

      Una vez que tenga todo esto listo, inicie sesión en su servidor como usuario no root.

      Paso 1: Instalar OctoDNS

      OctoDNS se distribuye como paquete pip en Python y se ejecuta en un entorno virtual de Python (virtualenv), por lo que iniciará este paso instalando los paquetes necesarios para esto. Un virtualenv es un entorno aislado de Python que puede contar con bibliotecas y configuración propias, independientes de la instalación principal de Python en todo el sistema. Python y ​​​virtualenv están disponibles dentro de los repositorios de software predeterminados de Debian, lo que permite la instalación con herramientas convencionales de administración de paquetes.

      Comience actualizando el índice de paquetes locales de modo que se refleje cualquier cambio anterior:

      A continuación, instale los paquetes python y virtualenv:

      • sudo apt install python virtualenv

      Después de confirmar la instalación, apt descargará e instalará Python, virtualenv y todas sus dependencias necesarias.

      A continuación, creará los directorios necesarios para OctoDNS, en los que se almacenará su configuración de DNS y del programa. Comience creando los directorios ~/octodns y ~/octodns/config:

      • mkdir ~/octodns ~/octodns/config

      Ahora, posiciónese en ~/octodns:

      A continuación, es necesario crear el entorno virtual de Python; un entorno aislado con bibliotecas y configuración propias para ejecutar OctoDNS:

      Active su entorno con el siguiente comando:

      Con esto, se mostrará algo similar a lo siguiente:

      Output

      Running virtualenv with interpreter /usr/bin/python2 New python executable in /home/user/octodns/env/bin/python2 Also creating executable in /home/user/octodns/env/bin/python Installing setuptools, pkg_resources, pip, wheel...done.

      Ahora, su intérprete de comandos de shell de Bash también llevará como prefijo el nombre de su entorno virtual. Esto indica que actualmente realiza operaciones dentro de virtualenv:

      (env) user@digitalocean:~/octodns$
      

      Si quiere cerrar virtualenv, puede usar el comando deactivate en cualquier momento. Sin embargo, debe permanecer en su virtualenv para continuar con este tutorial.

      Ahora que instaló y configuró Python y virtualenv, puede instalar OctoDNS. OctoDNS se distribuye como un paquete pip de Python, que es la herramienta estándar de administración de paquetes y bibliotecas de Python.

      Puede instalar el paquete de pip de OctoDNS utilizando el siguiente comando dentro de su virtualenv:

      Una vez completado esto, puede verificar la versión instalada para asegurar que todo funcione:

      El resultado debe tener un aspecto similar al siguiente:

      Output

      octoDNS 0.9.9

      Si ve un error octodns-sync: command not found, compruebe que aún se encuentre dentro de su virtualenv.

      Ahora que instaló OctoDNS, podrá crear los archivos de configuración necesarios para conectar OctoDNS con su proveedor DNS para permitir que se realicen cambios en sus registros de DNS.

      Paso 2: Configurar OctoDNS

      En este paso, creará los archivos de configuración necesarios para OctoDNS y los conectará con su proveedor DNS para que pueda comenzar a realizar cambios en tiempo real en sus registros DNS.

      Nota: En este tutorial, nos enfocaremos en la configuración inicial de OctoDNS. Sin embargo, para el uso en producción se le recomienda almacenar su configuración de OctoDNS en un sistema de control de versiones (VCS) como Git. Entre las ventajas de esto se incluyen un control de versiones completo, la integración con CI/CD para pruebas y las implementaciones de versiones anteriores sin problemas.

      Primero, debe configurar el archivo config.yaml, que define las zonas de DNS para la administración a través de OctoDNS, y permite que este se autentique en su proveedor DNS y realice cambios.

      El formato de config.yaml varía ligeramente dependiendo del proveedor DNS que está utilizando. Consulte la lista de proveedores compatibles en la documentación oficial de OctoDNS para hallar la configuración de su propio proveedor. Si se observa este hiperenlace, los detalles de configuración se presentan como comentarios de código en el código real de Python para su proveedor, que está vinculado en la columna de “Provider” de la tabla. Una vez que encuentre el código de Python para su proveedor, como cloudflare.py o route53.py, podrá hallar el comentario de código pertinente directamente en la class ProviderNameProvider. Por ejemplo:

      Excerpt of octodns/provider/route53.py

      class Route53Provider(BaseProvider):
        '''
        AWS Route53 Provider
        route53:
            class: octodns.provider.route53.Route53Provider
            # The AWS access key id
            access_key_id:
            # The AWS secret access key
            secret_access_key:
            # The AWS session token (optional)
            # Only needed if using temporary security credentials
            session_token:
      

      Posiciónese en el directorio ~/octodns/config:

      Luego, cree y abra config.yaml para la edición:

      Añada la configuración de muestra config.yaml para su proveedor DNS al archivo. Si utiliza DigitalOcean como su proveedor DNS, puede recurrir a lo siguiente:

      ~/octodns/config/config.yaml

      ---
      providers:
        config:
          class: octodns.provider.yaml.YamlProvider
          directory: ./config
          default_ttl: 300
          enforce_order: True
        digitalocean:
          class: octodns.provider.digitalocean.DigitalOceanProvider
          token: your-digitalocean-oauth-token
      
      zones:
        your-domain.:
          sources:
            - config
          targets:
            - digitalocean
      

      A través de este archivo se indican a OctoDNS los proveedores DNS con los que desea establecer conexión y las zonas de DNS que el software debe administrar para esos proveedores.

      Deberá proporcionar alguna forma de autenticación para proveedor DNS. Normalmente, se trata de una clave de API o un token de OAuth.

      Si no desea almacenar su token de acceso en texto simple en el archivo de configuración, como alternativa puede pasarlo como variable de entorno cuando se ejecute el programa. Para hacer esto, debe usar la siguiente línea token: en config.yaml:

      ~/octodns/config/config.yaml

      token: env/DIGITALOCEAN_OAUTH_TOKEN
      

      A continuación, antes de ejecutar OctoDNS, fije la variable de entorno pertinente en su token de acceso. OctoDNS la leerá de allí cuando se ejecute:

      • export DIGITALOCEAN_OAUTH_TOKEN=your-digitalocean-oauth-token

      Advertencia: Este token brindará acceso a la cuenta de su proveedor DNS, por lo que debe protegerlo como si se tratara de una contraseña. También compruebe que, si utiliza un sistema de control de versiones, el archivo que contenga el token esté excluido (por ejemplo, con .gitignore), o cifrado de forma segura de alguna manera.

      Si utiliza DigitalOcean como su proveedor DNS, puede usar el token de OAuth requerido en la configuración de su cuenta de DigitalOcean que generó como parte de los requisitos previos.

      Si tiene varios proveedores DNS diferentes, por ejemplo, para varios nombres de dominio o zonas de DNS delegadas, puede definirlos todos en el mismo archivo config.yaml.

      Con esto, estableció el archivo de configuración inicial de OctoDNS para permitir que el programa se autentique en su proveedor DNS y realice cambios. A continuación, creará la configuración para sus zonas de DNS.

      Paso 3: Crear un archivo de configuración de DNS

      Durante este paso, creará un archivo de configuración de DNS inicial, que contendrá los registros de DNS para su nombre de dominio o zona de DNS delegada.

      Cada zona de DNS que quiere administrar utilizando OctoDNS tiene su propio archivo; por ejemplo your-domain.yaml. En este archivo, los registros de DNS de la zona se definen usando YAML.

      Para comenzar, acceda al directorio ~/octodns/config:

      Luego, cree y abra your-domain.yaml para la edición:

      Añada la siguiente configuración de ejemplo al archivo:

      ~/octodns/config/your-domain.yaml

      ---
      '':
        - type: A
          value: your-server-ipv4-address
      
      www:
        - type: A
          value: your-server-ipv4-address
      

      En este archivo de muestra se define una zona de DNS para your-domain con dos registros A, con orientación hacia la dirección IPv4 en la que aloja su dominio o sitio web. Un registro A es para el dominio root (por ejemplo, your-domain) y el otro es para el subdominio www (por ejemplo, www.your-domain).

      Una vez que termine, guarde y cierre el archivo.

      De esta manera, preparó un archivo de configuración básico de zona de DNS para OctoDNS, con dos registros A básicos orientados a la dirección IPv4 de su dominio o sitio web. A continuación, ampliará el archivo con algunos registros de DNS útiles.

      Paso 4: Completar su archivo de configuración DNS

      A continuación, podrá completar el archivo de configuración de DNS con un conjunto práctico de registros de DNS para su sitio web o servicio usando el lenguaje de configuración estructurado YAML.

      A diferencia de los archivos de zona BIND tradicionales, en los cuales los registros de DNS se escriben en un formato básico línea por línea, los registros de DNS dentro de OctoDNS se definen como claves y subclaves YAML con varios valores asociados, como se muestra brevemente en el paso 3.

      La clave de nivel superior suele ser el 'name', que es básicamente el identificador de registros. www, subdomain1 y mail son ejemplos de 'name' de DNS. En OctoDNS, hay dos nombres de uso especial: ", para el registro root (por lo general citado como @) y '*', para los registros de comodín. Un valor obligatorio de cada clave (registro de DNS) es type. Esto especifica el tipo de registro DNS que define dentro de esa clave de nivel superior YAML. Existe un type para cada uno de los tipos de registros de DNS estándares, entre los que se incluyen A, AAAA, MX, TXT, NS y CNAME. Se encuentra disponible una lista completa de los tipos de registro en la sección de registros de la documentación de OctoDNS.

      Los valores para sus registros de DNS se definen directamente como valores en las claves de nivel superior (si solo tiene un valor) o como una lista (si tiene varios valores, como varias direcciones IP o direcciones MX).

      Por ejemplo, para definir un valor podría usar la siguiente configuración:

      ~/octodns/config/your-domain.yaml

      'www':
        type: A
        value: 203.0.113.1
      

      Asimismo, para definir varios valores para un registro único:

      ~/octodns/config/your-domain.yaml

      'www':
        type: A
        values:
        - 203.0.113.1
        - 203.0.113.2
      

      La sintaxis para configurar registros de DNS varía ligeramente según cada tipo de registro. Los siguientes son algunos ejemplos para los tipos de registro más comunes:

      Registros A:

      Propósito: apuntar a una dirección IPv4.

      Sintaxis:

      'name':
        type: A
        value: ipv4-address
      

      Ejemplo:

      'www':
        type: A
        value: your-server-ipv4-address
      

      Registros AAA:

      Propósito: apuntar a una dirección IPv6.

      Sintaxis:

      'name':
        type: AAAA
        value: ipv6-address
      

      Ejemplo:

      'www':
        type: AAAA
        value: your-server-ipv6-address
      

      Registros CNAME:

      Propósito: convertir su dominio o subdominio en un alias de otro.

      Sintaxis:

      'name':
        type: CNAME
        value: fully-qualified-domain-name
      

      Ejemplo:

      'www':
        type: CNAME
        value: www.example.org
      

      Registros MX:

      Propósito: dirigir el correo electrónico a direcciones o servidores específicos.

      Sintaxis:

      'name':
        type: MX
        value:
          exchange: mail-server
          preference: priority-value
      

      Tenga en cuenta que un . final se debe incluir si hay puntos en el valor de MX.

      Ejemplo:

      '':
        type: MX
        value:
          exchange: mail.your-domain.
          preference: 10
      

      Registros TXT:

      Propósito: agregar texto simple arbitrario, a menudo utilizado para configuraciones sin su propio tipo de registro dedicado.

      Sintaxis:

      'name':
        type: TXT
        value: content
      

      Ejemplo:

      '':
        type: TXT
        value: This is a TXT record.
      

      Para comenzar a añadir registros de DNS para su dominio o zona de DNS delegada, edite su archivo de configuración de DNS:

      • cd ~/octodns/config
      • nano your-domain.yaml

      A continuación, podrá comenzar a completar su zona de DNS usando la sintaxis descrita en la lista anterior y en la sección de registros de la documentación oficial de OctoDNS.

      A modo de referencia, el bloque de código aquí contiene una configuración de ejemplo completa para una configuración inicial de un DNS:

      ~/octodns/config/your-domain.yaml

      ---
      '':
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      
        - type: MX
          value:
            exchange: mail.your-domain.
            preference: 10
      
        - type: TXT
          value: v=spf1 -all
      
      _dmarc:
        type: TXT
        value: v=DMARC1; p=reject; rua=mailto:abuse@your-domain; aspf=s; adkim=s;
      
      mail:
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      
      www:
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      

      Una vez que haya completado su configuración inicial de DNS, guarde y cierre el archivo.

      Durante este paso, creó el archivo de configuración de DNS inicial que contiene sus registros de DNS. A continuación, probará la configuración y la implementará.

      Paso 5: Probar e implementar su configuración de DNS

      En este paso, ejecutará una verificación de sintaxis local en su configuración de DNS y luego implementará los cambios en el servidor y proveedor DNS activos.

      Primero, acceda a su directorio octodns:

      Asegúrese de seguir operando dentro de su virtualenv de Python buscando el nombre de este delante de su mensaje de Bash:

      (env) user@digitalocean:~/octodns$
      

      A continuación, utilice el comando octodns-validate para verificar la sintaxis de sus archivos de configuración. Deberá especificar la ruta a su archivo de configuración:

      • octodns-validate --config=./config/config.yaml

      Si la sintaxis YAML de su archivo de configuración de DNS es correcta, OctoDNS no mostrará resultados. Si ve un error o una advertencia en el resultado, DNSControl proporcionará detalles sobre el tipo de error y el punto en el que se encuentra dentro de su archivo.

      A continuación, podrá realizar un simulacro de la implementación de la configuración de DNS, con lo cual se mostrarán los cambios que se realizarán sin aplicarlos:

      • octodns-sync --config=./config/config.yaml

      Con esto, debería aparecer un resultado similar al siguiente:

      Output

      ******************************************************************************** * your-domain. ******************************************************************************** * digitalocean (DigitalOceanProvider) * Create <ARecord A 300, mail.your-domain., ['your-server-ipv4-address']> (config) * Create <AaaaRecord AAAA 300, mail.your-domain., ['your-server-ipv6-address']> (config) * Create <TxtRecord TXT 300, your-domain., ['v=spf1 -all']> (config) * Create <AaaaRecord AAAA 300, your-domain., ['your-server-ipv6-address']> (config) * Create <ARecord A 300, your-domain., ['your-server-ipv4-address']> (config) * Create <ARecord A 300, www.your-domain., ['your-server-ipv4-address']> (config) * Create <MxRecord MX 300, your-domain., [''10 mail.your-domain.'']> (config) * Create <TxtRecord TXT 300, _dmarc.your-domain., ['v=DMARC1; p=reject; rua=mailto:abuse@your-domain; aspf=s; adkim=s;']> (config) * Create <AaaaRecord AAAA 300, www.your-domain., ['your-server-ipv6-address']> (config) * Summary: Creates=9, Updates=0, Deletes=0, Existing Records=2 ********************************************************************************

      Advertencia: A través del siguiente comando se harán cambios en tiempo real en sus registros DNS y posiblemente en otras configuraciones. Asegúrese de estar preparado para esto; incluya una copia de seguridad de su configuración de DNS existente y verifique que disponga de los medios necesarios para revertir los cambios si es necesario.

      Por último, puede aplicar los cambios en su proveedor DNS activo:

      • octodns-sync --config=./config/config.yaml --doit

      Nota: En algunos casos, OctoDNS no permitirá la introducción de cambios si realiza un número de ajustes considerable. Este es un mecanismo de protección automática para evitar configuraciones accidentales. Si se produce este rechazo, puede volver a ejecutar octodns-sync utilizando la opción --force. Sin embargo, asegúrese de estar listo para hacerlo.

      Verá un resultado como el del simulacro anterior de este paso, pero se agregará algo similar a lo siguiente:

      Output

      2019-07-07T23:17:27 INFO DigitalOceanProvider[digitalocean] apply: making changes 2019-07-07T23:17:30 INFO Manager sync: 9 total changes

      Ahora, si revisa las configuraciones de DNS para su dominio en el panel de control de DigitalOcean, verá los cambios.

      Captura de pantalla del panel de control de DigitalOcean; se muestran algunos de los cambios de DNS realizados en OctoDNS.

      También puede verificar la creación de registros ejecutando una solicitud de DNS para su dominio o zona delegada usando dig.

      Si no instaló dig, deberá instalar el paquete dnsutils:

      • sudo apt install dnsutils

      Una vez que instale dig, podrá utilizarlo con el propósito de realizar una búsqueda de DNS para su dominio. Verá que los registros se actualizaron de forma correspondiente:

      Verá un resultado que muestra la dirección IP y los registros de DNS pertinentes de su zona implementada usando OctoDNS. Los registros DNS pueden tardar tiempo en propagarse, por lo que es posible que necesite esperar y ejecutar este comando de nuevo.

      En este último paso, realizó una verificación de sintaxis local del archivo de configuración de DNS, luego lo implementó en su proveedor DNS activo y verificó mediante pruebas que los cambios se realizaran correctamente.

      Conclusión

      A lo largo de este artículo, configuró OctoDNS e implementó una configuración de DNS en un proveedor activo. Ahora podrá administrar y probar sus cambios de configuración de DNS en un entorno seguro y sin conexión antes de implementarlos en la producción.

      Si desea profundizar en este tema, Octo DNS está diseñado para integrarse en su proceso de CI/CD, lo que le permite realizar pruebas exhaustivas y tener más control sobre su implementación en la producción. También podría considerar la integración de OctoDNS en sus procesos de compilación e implementación de infraestructuras, lo que le permitirá implementar servidores y agregarlos al DNS de manera completamente automática.

      Si desea profundizar en el uso de OctoDNS, en los siguientes artículos de DigitalOcean se ofrecen algunos pasos interesantes que podrá seguir para contribuir a la integración de OctoDNS en sus flujos de trabajo de administración de cambios e implementación de infraestructura:



      Source link

      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

      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.

      Acceda al directorio definitions recién creado:

      Realice su servicio de PHP-FPM creando un archivo 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:

      Output

      service/php created

      Verifique que su servicio esté en ejecución:

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

      Output

      NAME 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:

      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:

      Output

      service/nginx created

      Puede ver todos los servicios en funcionamiento ejecutando lo siguiente:

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

      Output

      NAME 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:

      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:

      Output

      secret/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:

      Output

      NAME 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:

      Output

      storageclass.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:

      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.

      Output

      persistentvolumeclaim/code created

      Para visualizar volúmenes persistentes (VP) disponibles:

      Verá su VP enumerado:

      Output

      NAME 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:

      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:

      Output

      deployment.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:

      Verá el siguiente resultado:

      Output

      NAME 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:

      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:

      Output

      NAME 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:

      Output

      NAME 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.

      Output

      NAME 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:
      • 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:

      Output

      configmap/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:

      Output

      deployment.apps/nginx created

      Enumere sus implementaciones con el siguiente comando:

      Visualizará las implementaciones de Nginx y PHP-FPM:

      Output

      NAME 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:

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

      Output

      NAME 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:

      Output

      NAME 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.



      Source link