Blog de Amazon Web Services (AWS)

Como desplegar WordPress sobre contenedores en AWS utilizando CDK

Por Rodrigo Monge, Solutions Architect en AWS Public Sector

 

Introducción

WordPress es una plataforma gratuita y open-source para crear sitios webs. El 43% de los sitios webs en internet utilizan esta plataforma, desde blogs de pasatiempos hasta sitios de entidades gubernamentales de alta demanda y criticidad. Esta tendencia es creciente cada año y dando respuesta a esas necesidades, AWS ha publicado un whitepaper sobre Mejores prácticas para WordPress en AWS. En este último se detallan diferentes estrategias de despliegues, una arquitectura de referencia para sitios con requerimientos de alta disponibilidad y escalabilidad, opciones para acelerar el rendimiento del sitio, información sobre plugins de la plataforma y muchas consideraciones más.

En este blog vamos a explicar como desplegar, en menos de 15 minutos, un sitio WordPress sobre contenedores utilizando infraestructura como código a través del servicio AWS CDK (Cloud Development Kit). Una de las principales ventajas de AWS CDK, es su grado de abstracción en la definición del código de nuestra infraestructura utilizando nuestro lenguaje de programación favorito; en unas pocas líneas de código podemos definir un equivalente a cientos de líneas que necesitaríamos en un template de AWS CloudFormation para lograr el mismo objetivo. No sólo eso, sino que además, AWS CDK utiliza “patrones” o “patterns” sobre los que están embebidas mejores prácticas de arquitectura sobre AWS.

Para nuestro sitio vamos a describir la infraestructura como código en lenguaje de programación Typescript (aunque existen otros lenguajes soportados); y luego que ese código es sintetizado en un template de AWS CloudFormation y desplegado automáticamente sobre nuestra cuenta de AWS.

Por último dejaremos planteadas una serie de ideas para mejorar diversos aspectos de nuestra solución, que no fueron consideradas en el alcance de este blog.

 

Arquitectura de la solución

  1. El Application Load Balancer distribuye tráfico entrante hacia los contenedores, escalando automáticamente de acuerdo a la demanda.
  2. El NAT Gateway se utiliza para que los contenedores que están corriendo en subredes privadas puedan conectarse con servicios fuera de la VPC.
  3. Los contenedores de WordPress se apoyan en un orquestador administrado como Amazon ECS (Elastic Container Service) para gestionar su despliegue, administración y escalabilidad.
  4. Amazon ECR (Elastic Container Registry) es un servicio administrado que nos permite almacenar, administrar, compartir y desplegar nuestras imágenes docker de WordPress.
  5. La definición de las tareas de Amazon ECS incluye información sobre la imagen docker a utilizar, los recursos de cómputo que la imagen requiere, el tipo de infraestructura donde se va a desplegar, volúmenes de almacenamiento, logging, etc.
  6. Para proteger las credenciales de la base de datos, utilizaremos el servicio AWS Secrets Manager, que además de permitir un acceso controlado al secreto a través de políticas de seguridad, nos ofrece la opción de rotación de credenciales en forma automática.
  7. AWS Fargate nos ofrece la posibilidad de ejecutar los contenedores WordPress sobre un cómputo serverless, en lugar de gestionar instancias Amazon EC2.
  8. Para poder desacoplar el contenido de WordPress (posts, plugins, themes, etc) del contenedor, Amazon EFS (Elastic File System) nos ofrece un almacenamiento altamente disponible, durable y escalable que puede ser compartido por las diferentes réplicas de los contenedores WordPress que se estén ejecutando en un determinado momento.
  9. Amazon Aurora es un motor de base de datos relacional completamente administrado compatible con MySQL y PostgreSQL. Nuestra solución contempla un Cluster Aurora compuesto por una instancia de escritura y una réplica de solo lectura para aumentar la disponibilidad del servicio.

 

¿Qué es CDK?

AWS CDK (Cloud Development Kit) es un framework de desarrollo de software open-source que sirve para definir como infraestructura como código, los recursos destinados a aplicaciones en la nube mediante lenguajes de programación conocidos.

El desarrollador inicializa el proyecto AWS CDK y luego define sus 3 componentes principales: App, Stack y Construct. Estos últimos poseen diferentes niveles de abstracción (L1, L2 y L3) dando la posibilidad al desarrollador de definir el servicio que desea desplegar tanto con recursos de CloudFormation (L1 -> mayor cantidad de líneas de código), como a través de “patterns” (L3 -> menor cantidad de líneas de código), que consolidan múltiples servicios con mejores prácticas embebidas en su parametrización por defecto.

Mediante el comando cdk deploy, el servicio compila el código y traduce esa definición en un template de CloudFormation, para luego ser desplegado automáticamente sobre la cuenta de AWS.

Puede acceder a nuestro de blog de Mejores Prácticas para desarrollo de aplicaciones en la nube con AWS CDK para profundizar estos conceptos.

 

Pre-requisitos:

  • Poseer una cuenta de AWS
  • Acceso a un entorno AWS Cloud9. Recomendamos este servicio debido a que ya cuenta con los pre-requisitos de AWS CDK. En caso de preferir utilizar una infraestructura diferente, revisar los siguientes requisitos.

 

Desplegando la solución:

1. Acceder a la consola de AWS Cloud9 y abrir un entorno para codificar nuestro proyecto.

 

2. Una vez en la terminal de AWS Cloud9, inicializamos el proyecto.

mkdir wordpress-cdk && cd wordpress-cdk
cdk init --language typescript
cdk bootstrap

 

3. Luego de inicializar el proyecto, deberíamos visualizar una estructura de archivos similar a la siguiente:

donde los archivos principales son:

  • lib/wordpress-cdk-stack.tscontiene la definición de nuestro Stack principal de CDK. En este archivo es donde vamos a trabajar la mayor parte del tiempo.
  • bin/wordpress-cdk.tscontiene el punto de entrada de nuestra App CDK. El proceso de inicialización completa automáticamente la referencia hacia el archivo lib/wordpress-cdk-stack.ts
  • package.json contiene el manifiesto de nuestro módulo npm
  • cdk.json ordena a la herramienta como ejecutar nuestra aplicación
  • tsconfig.json contiene la configuración de nuestro proyecto Typescript
  • node_moduleses mantenido por npm e incluye todas las dependencias de nuestro proyecto.

 

4. Verificamos que el proceso de inicialización haya generado el archivo bin/wordpress-cdk.ts.

cat ./bin/wordpress-cdk.ts

 

5. En este paso vamos a modificar el contenido del archivo lib/wordpress-cdk-stack.ts, para describir nuestra infraestructura utilizando el lenguaje Typescript. El código contiene comentarios para comprender el objetivo de cada bloque. Favor de prestar especial atención al bloque de ecsPatterns, el cual combina el patrón de arquitectura compuesto por el servicio Amazon ECS, AWS Fargate y Application Load Balancer. El siguiente código es presentado a modo ejemplo y puede variar de acuerdo a las necesidades de cada entorno.

cat <<'EOF' &gt; ./lib/wordpress-cdk-stack.ts
   
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs'; 
import * as ec2 from 'aws-cdk-lib/aws-ec2'; 
import * as ecs from 'aws-cdk-lib/aws-ecs'; 
import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns'; 
import * as efs from 'aws-cdk-lib/aws-efs'; 
import * as rds from 'aws-cdk-lib/aws-rds'; 
import * as secretsManager from 'aws-cdk-lib/aws-secretsmanager';
   
export class WordPressCdkStack extends Stack {
   constructor(scope: Construct, id: string, props?: StackProps) {
     super(scope, id, props);

     // Setup Amazon VPC:  2 Public subnets
     //                    2 Private Subnets (with NAT GW access)
     //                    2 Private Subnets (isolated)
     const vpc = new ec2.Vpc(this, 'wordpress-vpc', {
         natGateways: 2,
         maxAzs: 2,
         subnetConfiguration: [
          {             cidrMask: 24,             name: 'ingress',             subnetType: ec2.SubnetType.PUBLIC,           },           {             cidrMask: 24,  
          name: 'application',
          subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
          },
          {
             cidrMask: 28,
             name: 'rds',
             subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
           }
        ]
     });
 
     // Store Database wordpress credentials safely in AWS Secrets Manager
     // dbname: wordpress
     // username: adminwp
     // password: this is autogenerated by the service
     const credSecret = new secretsManager.Secret(this, 'db-secret', {
       secretName: '/wordpress',
       generateSecretString: { 
         secretStringTemplate: '{"dbname": "wordpress", "username": "adminwp"}',
         generateStringKey: 'password',
         passwordLength: 20,
         excludePunctuation: true
       }
     });
  
     // Create wordpress database in Amazon Aurora
     // VER_3_01_0 Version "8.0.mysql_aurora.3.01.0".
     const db = new rds.DatabaseCluster(this, 'wp-db', {
       engine: rds.DatabaseClusterEngine.auroraMysql({version: rds.AuroraMysqlEngineVersion.VER_3_01_0 }),
       instanceProps: {
           instanceType: ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE),
          vpc,
           vpcSubnets: {
             subnetType: ec2.SubnetType.PRIVATE_ISOLATED
           },
       },
       credentials: rds.Credentials.fromPassword('adminwp', credSecret.secretValueFromJson('password')),
       defaultDatabaseName: 'wordpress'
     });
 
       // Create Shared Filesystem (NFS) for WordPress content on Amazon EFS
     const fs = new efs.FileSystem(this, 'fs', {
       fileSystemName: 'wordpress',
       vpc,
       vpcSubnets: {
         subnetType: ec2.SubnetType.PRIVATE_ISOLATED
       }
     });
 
        // Setup ALB + Farget using ECS Patterns
        // You could use DeploymentController attribute to specify how a new version is deployed
        //    When this attribute is omited, it defaults to Rolling update (ECS)
        // The use of "secrets" sections enables ECS definition to reference AWS Secrets Manager
     const wp = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'wpsvc', {
       taskImageOptions: {
         image: ecs.ContainerImage.fromRegistry('library/wordpress:latest'),
         environment: {
           WORDPRESS_DB_HOST: db.clusterEndpoint.hostname,
           WORDPRESS_TABLE_PREFIX: 'wp_'
         },
         secrets: {
           WORDPRESS_DB_NAME: ecs.Secret.fromSecretsManager(credSecret, 'dbname'),
           WORDPRESS_DB_USER: ecs.Secret.fromSecretsManager(credSecret, 'username'),
           WORDPRESS_DB_PASSWORD: ecs.Secret.fromSecretsManager(credSecret, 'password'),
         }
       },
       cpu: 256,
       memoryLimitMiB: 1024,
       desiredCount: 2,
       vpc,
       taskSubnets: {
         subnetType: ec2.SubnetType.PRIVATE_WITH_NAT
       },
     });
  
         // Allow access to database ONLY from WordPress containers
db.connections.allowDefaultPortFrom(wp.service.connections);

       // Allow access to Amazon EFS ONLY from WordPress containers     fs.connections.allowDefaultPortFrom(wp.service.connections);

       // Add shared storage volumes to ECS task definition
     wp.taskDefinition.addVolume({
       efsVolumeConfiguration: {
         fileSystemId: fs.fileSystemId
       },
       name: 'wp-vol',
     });

       // Add mount point to the WordPress container
     wp.taskDefinition.defaultContainer?.addMountPoints({
       containerPath: '/var/www/html',
       readOnly: false,
       sourceVolume: 'wp-vol'
     });

       // WordPress might return 301,301 codes depending on the configuration
     // these codes recognized as Healthy avoid recurrent ECS tasks launches
     wp.targetGroup.configureHealthCheck({
       path: '/',
       healthyHttpCodes: "200-399"
     });

     }
 }
 EOF

Nota: presionar la Tecla “Enter” para confirmar el comando

 

6. Paso Opcional – Para verificar que componentes se van a desplegar, podemos ejecutar el siguiente comando que nos permite comparar los componentes definidos en el stack local, con el stack desplegado anteriormente sobre la cuenta de AWS

cdk diff

 

7. Paso Opcional – Con el comando siguiente comando podemos visualizar el template de AWS CloudFormation que se va a desplegar sobre la cuenta de AWS

cdk synth

 

8. Desplegamos nuestro código sobre la cuenta de AWS, debemos confirmar el proceso ante la pregunta.

cdk deploy

 

9. Mientras nuestro Stack se está desplegando, podemos ver en la consola de AWS CloudFormation el estado del proceso.

 

10. Al terminar el proceso, la salida del comando anterior nos devuelve el acceso a la URL de nuestro sitio WordPress.

 

11. Finalmente accedemos a la configuración inicial de nuestro sitio WordPress. Esta configuración no forma parte del alcance de este blog.

 

Como mejorar la solución

Cada solución es diferente y se debe ajustar a las necesidades de cada cliente. Sin embargo podemos plantear algunas ideas con el fin de mejorar diferentes aspectos de la solución propuesta en este blog. Para ello, vamos a clasificar estas ideas dentro los pilares de nuestro AWS Well Architected Framework:

Excelencia Operativa

Seguridad

Fiabilidad

Eficacia del rendimiento

Optimización de Costos

Sostenibilidad

 

Resumen

Si bien existen diferentes estrategias a la hora de desplegar sitios WordPress en AWS, en este blog demostramos lo fácil y rápido que es hacerlo sobre Amazon ECS + AWS Fargate a través del servicio AWS CDK (Cloud Development Kit). En menos de 15 minutos desplegamos un sitio WordPress utilizando configuraciones de alta disponibilidad, aislación de cargas en subredes privadas, almacenamiento durable y compartido por los contenedores, y demás cosas.

Puede descubrir +1100 librerías de constructores publicados por la comunidad en Construct Hub, donde se encuentran arquitecturas de referencia y patrones de diseño de aplicaciones utilizando AWS CDK. Adicionalmente la documentación AWS CDK es un punto de referencia para revisar ejemplos, documentación sobre APIs en cada lenguaje soportado y acceso al Construct Hub.

Para adquirir más conocimientos sobre los servicios utilizados en esta solución, recomendamos acceder a entrenamientos gratuitos sobre AWS Cloud Development Kit, AWS Fargate, Managing the Application Lifecycle in Amazon ECS y Amazon Aurora.

 

 


Sobre el autor

Rodrigo Monge es arquitecto de soluciones en Amazon Web Services