Blog de Amazon Web Services (AWS)

Entrega Continua de clústeres de Amazon EKS mediante pipelines de AWS CDK y CDK

Por Jairo da Silva Junior, Arquitecto de Soluciones Senior en AWS y
Davi Garcia, Arquitecto de Soluciones Senior en AWS

Los clientes buscan formas de automatizar la implementación de clústeres de Amazon EKS en diferentes versiones, entornos, cuentas y regiones. La implementación de estos clústeres implica tareas como la creación de clústeres con la configuración de red y logs adecuada, la selección de versiones de los Amazon EKS Add-ons y, cuando finaliza, la implementación de otros componentes de infraestructura. En esta publicación se muestra cómo utilizar AWS CDK y CDK Pipelines para implementar clústeres de Amazon EKS.

Visión General

En esta publicación mostraremos un pipeline de ejemplo con CDK Pipelines, una biblioteca de constructs (constructores) de alto nivel que facilita la configuración de pipelines de implementación continua para aplicaciones CDK con AWS CodePipeline.

Este pipeline crea dos clústeres de Amazon EKS distintos, uno en la versión 1.20 y el otro en la versión 1.21, con un Grupo de Nodos Administrados para cada uno, e implementa algunos controladores y operadores que utilizan AWS CDK como el AWS Load Balancer Controller, Calico para Network Policies, ExternalDNS, Cluster AutoScaler, Metrics Server y CloudWatch Container Insights.

También incluye una etapa para configurar un registro de Amazon Route 53 que apunta a uno o dos subdominios administrados por ExternalDNS de cada clúster, e intercambia los usuarios de la aplicación de ejemplo utilizando una estrategia de despliegue Blue/Green.

 

 

Etapas del Pipeline:

  • Source: Esta etapa busca el código de la aplicación CDK del repositorio en GitHub e inicia una ejecución de pipeline cada vez que se realizan nuevos commits.
  • Build: Esta etapa compila el código (si es necesario) y ejecuta un synth de CDK. El resultado de este paso es un «ensamblaje en la nube» (plantilla de CloudFormation), que se utiliza para realizar todas las acciones en el resto del pipeline.
  • UpdatePipeline: En esta etapa se realizan cambios en el pipeline si es necesario. Por ejemplo, si actualiza el código para agregar una nueva etapa al pipeline o agrega un nuevo activo a su aplicación, se realiza el cambio automáticamente en el pipeline para reflejar los cambios que se hicieron.
  • PublishAssets: En esta etapa se preparan y publican todos los archivos de los activos que utilizan en la aplicación en Amazon Simple Storage Service (Amazon S3), y todas las imágenes de contenedores para el Amazon Elastic Container Registry (Amazon ECR) de todas las cuentas y regiones desde las que se consumirán, disponibilizándolos para ser utilizados en etapas posteriores.
  • Deploy (DeployEKSClusters): En esta etapa se implementa la aplicación CDK en dos stacks diferentes que describen los clústeres de Amazon EKS, su configuración y sus componentes.
  • Validate: En esta etapa se valida que las cargas de trabajo implementadas en las etapas anteriores estén funcionales.
  • Release (PromoteEnvironment): Esta etapa actualiza su registro de Amazon Route 53 para que apunte al clúster definido como el entorno de producción en su código. Requiere una aprobación manual para ejecutarse.

A alto nivel, usaremos los siguientes pasos para implementar la infraestructura anterior:

  • Fork del repositorio de ejemplo
  • Creación de parámetros y secretos específicos para el entorno.
  • Creación de subdominios para cada clúster (por ejemplo,my.domain y green.my.domain) en Amazon Route 53.
  • Ejecución de cambios en el fork y ejecución de un push de los mismos.
  • Implementación de los stacks de CDK.

Requisitos previos:

  1. Una cuenta de AWS
  2. Una cuenta de GitHub
  3. Una Public Hosted Zone en Amazon Route 53. Puede registrar un dominio nuevo con Amazon Route 53 o usarlo un dominio existente.
  4. AWS CDK versión 1.121.0 o superior.
  5. CLI de AWS para simplificar algunas actividades.

Empezando

$ git clone https://github.com/YOUR-USERNAME/aws-cdk-pipelines-eks-cluster

  • Crear un token de acceso personal en GitHub con los ámbitos repo y admin:repo_hook usando este enlace o siguiendo este paso a paso.
  • Almacenar el token creado en el paso anterior en AWS Secrets Manager mediante el siguiente comando:

$ aws secretsmanager create-secret --name github-oauth-token --description "Secret for GitHub" --secret-string TOKEN-GENERATED-PREVIOUS-STEP

$ aws ssm put-parameter --name '/eks-cdk-pipelines/hostZoneId' --type String --value YOUR-HOSTED-ZONE-ID
$ aws ssm put-parameter --name '/eks-cdk-pipelines/zoneName' --type String --value YOUR-ZONE-NAME

Navegar a Route 53 con la consola de administración de AWS para acceder a sus Hosted Zones y utilice el nombre de dominio como zoneName y el identificador de la Hosted Zone como hostZoneID.

 

 

  • Puede usar el script parameters.sh dentro del fork del proyecto para llenar de forma interactiva los datos anteriores:

$ chmod +x parameters.sh;. /parameters.sh

  • Crear un subdominio para cada clúster (por ejemplo,my.domain y green.my.domain) en el DNS:

Crearemos un subdominio por clúster, de modo que cada clúster tenga su propio dominio y se pueda generar tráfico clave entre el entorno blue y green mediante el pipeline. Para nuestro ejemplo, se crearán los subdominios blue y green dentro del dominio que usamos en el paso anterior.

Para un dominio administrado por Amazon Route 53, puede seguir los siguientes pasos:

  1. Primero, crear una Hosted Zone con el mismo nombre que el subdominio al que desea enrutar su tráfico, por ejemplo example.org (reemplace example.org por su propio dominio).
  2. Obtener los name servers que Route 53 asoció a la nueva Hosted Zone cuando se creó.

 

 

 

  1. Crear un nuevo registro NS en la Hosted Zone de su dominio principal (ejemplo.org), especificando los cuatro name servers obtenidos en el paso 3.

 

 

  1. Repetir los pasos 1 a 3 con green.example.com.

Al final, el dominio principal debería parecerse al siguiente ejemplo:

 

 

Hacer cambios en el fork

Solo se debe realizar un cambio obligatorio en el código:

  • Actualizar la definición del pipeline en lib/eks-pipeline-stack.ts para que apunte al fork propio y reemplazar aws-samples por el nombre de usuario de GitHub.
const pipeline = new CodePipeline(this, "Pipeline", {
    synth: new ShellStep("Synth", {
    input: CodePipelineSource.gitHub(
        "aws-samples/aws-cdk-pipelines-eks-cluster",
        "main",
        {
        authentication:
            cdk.SecretValue.secretsManager("github-oauth-token"),
        }
    ),
    commands: ["npm ci", "npm run build", "npx cdk synth"],
    }),
    pipelineName: "EKSClusterBlueGreen",
});
Nota: Se debe asegurar de realizar el commit y push del código después de modificarlo; de lo contrario, el pipeline se actualizará al último commit realizado durante la etapa de SelfMutate.

El código anterior creará las etapas Source, Build, UpdatePipeline y PublishAssets, y solo se tendrá que definir las siguientes etapas. En nuestro ejemplo tenemos dos etapas, una para cada clúster de EKS (blue y green) y se ejecutan en paralelo mediante Wave:

const clusterANameSuffix = "blue";
const clusterBNameSuffix = "green";
const eksClusterStageA = new EksClusterStage(this, "EKSClusterA", {
    clusterVersion: eks.KubernetesVersion.V1_20,
    nameSuffix: clusterANameSuffix,
});
const eksClusterStageB = new EksClusterStage(this, "EKSClusterB", {
    clusterVersion: eks.KubernetesVersion.V1_21,
    nameSuffix: clusterBNameSuffix,
});
const eksClusterWave = pipeline.addWave("DeployEKSClusters");

Estamos reutilizando EksClusterStage con dos parámetros: ClusterVersion y NameSuffix, que también se usan como subdominio para administrar nuestros registros DNS de nuestro clúster de Kubernetes con ExternalDNS. En esta etapa se implementa un stack que contiene la definición de nuestro clúster y sus componentes principales:

const cluster = new eks.Cluster(this, `acme-${props.nameSuffix}`, {
    clusterName: `acme-${props.nameSuffix}`,
    version: props.clusterVersion,
    defaultCapacity: 0,
    vpc,
    vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE }],
});

new EksManagedNodeGroup(this, "EksManagedNodeGroup", {
    cluster: cluster,
    nameSuffix: props.nameSuffix,
});

new AWSLoadBalancerController(this, "AWSLoadBalancerController", {
    cluster: cluster,
});

new ExternalDNS(this, "ExternalDNS", {
    cluster: cluster,
    hostZoneId: hostZoneId,
    domainFilters: [`${props.nameSuffix}.${zoneName}`],
});

new ClusterAutoscaler(this, "ClusterAutoscaler", {
    cluster: cluster,
});

new ContainerInsights(this, "ContainerInsights", {
    cluster: cluster,
});

new Calico(this, "Calico", {
    cluster: cluster,
});

new Prometheus(this, "Prometheus", {
    cluster: cluster,
});

new Echoserver(this, "EchoServer", {
    cluster: cluster,
    nameSuffix: props.nameSuffix,
    domainName: zoneName,
});

Puede explorar el código de cada Construct en lib/infrastructure y lib/app; son los componentes básicos de las aplicaciones de AWS CDK. Un construct representa un componente de la nube y encapsula todo lo que AWS CloudFormation necesita para crear el componente. La siguiente es una descripción detallada de la función de cada Construct que se definió:

Una vez que se haya completado los cambios en el código, se debe iniciar el entorno:

$ npm install
$ cdk bootstrap

Al finalizar, ejecutar el commit y push de los cambios realizados en el repositorio mediante:

$ git add .
$ git commit -m "Update cluster configuration."
$ git push

Primera implementación

Lo primero que se debe hacer es implementar el pipeline manualmente con CDK utilizando el comando cdk deploy. Después de eso, cada cambio que se realice en el repositorio activará el pipeline, que se actualizará y se ejecutará automáticamente.

La primera ejecución llevará un tiempo, ya que la creación inicial de los recursos como los clústeres de EKS y los Grupos de Nodos Administrados pueden tardar unos minutos en completarse. De igual modo, es posible realizar un seguimiento del progreso de la ejecución del pipeline mediante AWS CodePipeline.

 

 

Al comprobar los stacks de AWS CloudFormation, encontrará un stack para el pipeline (EksPipelineStack) y un stack (con Nested Stacks) para cada clúster de EKS.

 

 

El output del (los) stack (s) de los clústeres de EKS muestra comandos para configurar kubeconfig y así acceder a su clúster. Copie y ejecute, por ejemplo:

$ aws eks update-kubeconfig --name acme-green --region <CLUSTER_REGION> --role-arn arn:aws:iam::<ACCOUNT_ID>:role/EKSClusterB-EKSCluster-acmegreenMastersRole

Luego puede usar kubectl para ejecutar los siguientes comandos:

$ kubectl version
$ kubectl get po -A

 

 

Se puede acceder a la aplicación directamente desde el navegador usando la dirección echoserver.subdomain.my.domain (reemplazando subdominio.mi.dominio con su subdominio) o apuntando al ELB de Ingress de echoserver en el namespace echoserver. Usando curl obtenemos algo como lo siguiente:

$ curl -H “Host: app.<MI_SUBDOMINIO>” elb.<AWS_REGION>.elb.amazonaws.com

La infraestructura final aprovisionada para este pipeline será similar a la del siguiente diagrama:

 

 

La definición de pipeline utiliza la variable prodEnv para definir el clúster de destino. En otras palabras, a dónde accederán los usuarios de echoserver. Cuando sea definido y se ejecute un push al fork del repositorio, el cambio tendrá que aprobarse manualmente en CodePipeline cuando se haga clic en Review:

 

 

Una vez que termine de actualizar el registro DNS y se propague el cambio, puede comprobar a dónde apunta el registro app.my.domain y acceder a la aplicación a través de este endpoint.

 

Limpieza

$ cdk destroy -y
$ aws cloudformation delete-stack —stack-name EKSClusterA-EKSCluster
$ aws cloudformation delete-stack —stack-name EKSClusterB-EKSCluster
$ aws cloudformation delete-stack —stack-name UpdateDNS-AppDns

Conclusión

En esta publicación mostramos cómo utilizar AWS CDK y CDK Pipelines para implementar y administrar todo el ciclo de vida de sus clústeres de Amazon EKS. Este enfoque de Infraestructura como Código (IaC) ofrece cambios a través de un pipeline automatizado y estandarizado que también se define en AWS CDK. Así mismo, permite implementar y actualizar clústeres de manera consistente en diferentes versiones, entornos, cuentas y regiones, mientras realiza un seguimiento de estos cambios a través de Git. El código de ejemplo proporciona varios componentes de ejemplo que se instalan habitualmente en clústeres de EKS, como AWS Load Balancer Controller, Calico para Network Policies, ExternalDNS, Cluster AutoScaler, Metrics Server y CloudWatch Container Insights.

 

Este artículo fue traducido del  Blog de AWS en Portugués.

 


Sobre los autores

Jairo Silva Junior es Solutions Architect en el equipo del Sector Público de AWS centrado en Gobierno. Anteriormente, trabajó en varios roles en el ciclo de entrega de software como desarrollo, arquitectura y operación, en varios tipos de empresas. Tiene una maestría en Ciencias de la Computación y le apasionan los viajes y la comida.

 

 

 

 

Davi García es un Specialist Solutions Architect, centrado en la modernización de aplicaciones en AWS y ayuda a clientes de varios segmentos de América Latina. Tiene una amplia experiencia en plataformas de contenedores y automatización de infraestructuras. Le gusta contribuir a proyectos de código abierto, divertirse con la familia con juegos de mesa y navegar a vela.

 

 

 

 

Sobre el revisor

David Sandoval es un Solutions Architect basado en Colombia que trabaja con compañías del segmento Enterprise del Ecuador.