Bienvenido al segundo artículo sobre Infraestructura como Código (IaC) utilizando lenguajes de programación populares y de alto nivel. El primero presentaba el AWS CDK. En este artículo presentaremos Pulumi, una herramienta similar que ofrece soporte multi-cloud.
La Infraestructura como código (IaC) es una de las mayores revoluciones en el panorama de la computación en la nube, ya que permite automatizar el despliegue de infraestructura en base a la definición especificada en el código. El hecho de utilizar código como fuente de verdad para la infraestructura tiene varios beneficios:
- La infraestructura puede versionarse, como normalmente se hace con el código, proporcionando trazabilidad de la misma.
- Los despliegues, rollbacks, y procedimientos de recuperación ante desastres pueden automatizarse fácilmente, reduciendo la intervención manual y por tanto la probabilidad de errores humanos.
- Los costes se reducen, ya que los recursos pueden redimensionarse modificando el código para adaptarlos a las fluctuaciones de tráfico y uso, y los entornos pueden apagarse cuando no se están utilizando.
- Los procesos de despliegue se aceleran de forma significativa, haciendo el ciclo de vida de desarrollo del software más eficiente, ya que los entornos pueden desplegarse cuando se necesitan.
La IaC puede implementarse por medio de diferentes herramientas, siendo Terraform, AWS CloudFormation, Ansible, Chef o Puppet algunas de las más utilizadas. Estas herramientas utilizan normalmente lenguajes declarativos (o dialectos), basados en YAML, JSON y derivados del JSON para la definición de la infraestructura. Trabajar con estas herramientas y lenguajes puede ser todo un reto, especialmente para aquellos con un background más cercano al desarrollo.
Por ello, tiene sentido el desplegar infraestructura utilizando lenguajes de alto nivel, que son más naturales para los desarrolladores. Este enfoque puede incrementar la productividad, ya que los patrones de desarrollo comunes pueden reutilizarse con facilidad. Este modelo de trabajo permite a los desarrolladores provisionar infraestructura de forma sencilla e intuitiva, sin necesidad de ser expertos en la materia. Además, los desarrolladores pueden provisionar stacks completos, conteniendo las aplicaciones y la infraestructura sobre la que corren, utilizando su IDE y lenguaje de preferencia. Esto puede ser particularmente útil en pequeñas y medianas empresas y proyectos, en las que un solo equipo se encarga de gestionar y operar la parte 'Dev' y 'Ops'.

A lo largo de este artículo se analizará Pulumi y sus funcionalidades. Pulumi es un framework de código abierto que permite crear y gestionar infraestructura y aplicaciones sobre proveedores de nube pública utilizando lenguajes de programación comunes y ampliamente extendidos, como Typescript, Javascript, Python, Go y C#.
Para los ejemplos que se muestran en el artículo, se escogió Python como lenguaje de programación, desplegando la infraestructura resultante sobre diferentes proveedores de nube pública. Puedes encontrar el código que se emplea para desplegar los ejemplos que se muestran a continuación en el repositorio de ejemplos de Pulumi.
Configurando Pulumi⚙️
El primer paso es instalar Python 3, específicamente Python 3.6 o versiones posteriores. Puedes descargar la última versión de Python 3 en aquí. Una vez que tengas Python 3 instalado, el siguiente paso es instalar Pulumi en tu máquina. Para las distribuciones de Linux, puedes ejecutar el siguiente comando. Para cualquier otro SO, puedes encontrar el manual de instalación en aquí.
curl -fsSL https://get.pulumi.com | sh
Para poder usar Pulumi en tu línea de comandos, necesitaras registrarte primero en la consola web de Pulumi, para que el estado de tu stack pueda ser persistido. No te preocupes, ya que Pulumi es gratuito para uso personal, y puedes registrarte utilizando tus cuentas de GitLab, GitHub, Atlassian o tu correo.

Una vez que te hayas registrado, es el momento de generar un token de acceso, ya que el CLI necesita uno para poder acceder a la cuenta y almacenar la información del stack.

Finalmente, ejecuta el siguiente comando y pega el token de acceso que acabas de generar el paso anterior. Si todo va bien, deberías ver una salida en el terminal similar a la que se muestra en la siguiente imagen.
pulumi login

Estructura del proyecto🔖
Si tuvieras que generar tu primer proyecto con Pulumi, tendrías que crear un directorio y ejecutar los siguientes comandos, especificando el proveedor de cloud a utilizar y el lenguaje de programación elegido.
mkdir test-project
cd test-project
pulumi new <provider>-<language>
En este caso, el CLI de Pulumi te guiaría a través de un proceso de setup bastante sencillo, donde se pueden especificar varios parámetros, como el nombre del proyecto, su descripción, el nombre del stack y la región en la que se desplegaría. Sin embargo, ten en cuenta que para los ejemplos que se muestran en este artículo, se emplean proyectos preconfigurados, y por ello, no es necesario que lleves a cabo este procedimiento de setup, es sencillamente una indicación por si quisieras desarrollar tu propio proyecto desde cero.

Después de configurar el proyecto (usando Python en este caso), los ficheros que se encuentran en el directorio del proyecto serán parecidos a los que se ven en la siguiente imagen. Pulumi.yaml y Pulumi.dev.yaml son los ficheros de configuración del proyecto y del stack respectivamente, mientras que __main__.py es el Programa de Python en el que se define el stack. El fichero requirements.txt especifica las dependencias que el stack necesita para funcionar y el directorio venv es un Python virtual environment, que contiene todos los ejecutables y las librerías necesarias para hacer uso de los paquetes importados en el proyecto de Python.

El siguiente extracto muestra el contenido de un fichero Pulumi.yaml . Para hacer que Pulumi funcione, es necesario definir al menos name y runtime. Bajo la ruta template.config, se pueden especificar valores obligatorios para que el stack pueda crearse.
name: aws-py-eks
runtime: python
description: A minimal AWS Python EKS example cluster
template:
config:
aws:region:
description: The AWS region to deploy into
default: us-east-2
Desplegando el Stack🤖
Para probar Pulumi con diferentes proveedores de cloud, utilizaremos el repositorio de ejemplos de Pulumi. Clónalo en tu máquina utilizando git para poder empezar.
git clone https://github.com/pulumi/examples.git
cd examples
AWS☁️🔶
Antes de desplegar el stack, necesitarás configurar tus credenciales de AWS, utilizando el AWS CLI (puedes descargarlo en aquí).Para ello, necesitarás un par de access key y secret key de AWS. Si no dispones de uno, puedes generarlo en la consola de IAM de AWS. Consulta la documentación oficial como referencia si lo necesitas.
aws configure
Ten en cuenta que el usuario al que se le asignen dichas claves de acceso debe tener permisos para crear la infraestructura. Para este ejemplo puedes utilizar la policy gestionada AdministratorAccess . Guarda bien el par de claves, y una vez que termines de probar estos ejemplos y este usuario no se vaya a utilizar de nuevo, elimínala, para evitar riesgos innecesarios.

Para AWS, utilizaremos el ejemplo de EKS con Python. Este ejemplo contiene tres ficheros con el código de Python: __main__.py, iam.py y vpc.py, en los que se define respectivamente, el clúster de EKS, sus roles y políticas de IAM asociados y los recursos de red necesarios para poder crear las instancias. El código que se muestra a continuación es el contenido de __main__.py , que define un clúster de EKS con dos workers. Tómate tu tiempo para inspeccionar los diferentes ficheros, y así poder tener una idea general del stack completo a desplegar.
import iam
import vpc
import pulumi
from pulumi_aws import eks
## EKS Cluster
eks_cluster = eks.Cluster(
'eks-cluster',
role_arn=iam.eks_role.arn,
tags= {'Name':'pulumi-eks-cluster'},
vpc_config = {
'publicAccessCidrs': ['0.0.0.0/0'],
'security_group_ids': [vpc.eks_security_group.id],
'subnet_ids': vpc.subnet_ids,
}
)
eks_node_group = eks.NodeGroup(
'eks-node-group',
cluster_name=eks_cluster.name,
node_group_name='pulumi-eks-nodegroup',
node_role_arn=iam.ec2_role.arn,
subnet_ids=vpc.subnet_ids,
tags={
'Name' : 'pulumi-cluster-nodeGroup'
},
scaling_config = {
'desired_size': 2,
'max_size': 2,
'min_size': 1,
},
)
pulumi.export('cluster-name', eks_cluster.name)
Puedes encontrar los ficheros para llevar a cabo el despliegue en el directorio aws-py-eks .
cd aws-py-eks/
Crea el virtual environment y actívalo. Una vez hecho esto, instala los paquetes especificados en requirements.txt.
virtualenv -p python3 venv
source venv/bin/activate
pip3 install -r requirements.txt
Una vez que toda la parte de Python esté lista, inicializa el stack. Si accedes a la consola de Pulumi tras hacerlo, puedes comprobar tu nuevo stack y sus tags.
pulumi stack init python-eks-testing

Selecciona la región de AWS en la que se desplegará el stack.
pulumi config set aws:region <aws-region>
Ejecuta el siguiente comando para que Pulumi genere el plan de despliegue. Si el plan tiene buena pinta, selecciona yes y espera hasta que el stack sea desplegado⌛.
pulumi up

Actualiza tu Kubeconfig local para poder acceder al nuevo clúster.
aws eks --region <aws-region> update-kubeconfig --name $(pulumi stack output cluster-name)
Y voilà, el clúster fue creado correctamente!

Una vez que termines de jugar con el clúster, destruye la infraestructura y elimina el stack. Comprueba que los recursos a destruir coinciden con los creados anteriormente. Ejecutando el comando deactivatedesactivará el virtual environment utilizado anteriormente.
pulumi destroy
pulumi stack rm python-eks-testing
deactivate
GCP☁️ 🌈
Al igual que en el ejemplo anterior, es necesario configurar las credenciales (en este caso de GCP) para que puedan ser utilizadas por Pulumi. Puedes utilizar el gcloud CLI para configurar las credenciales de acceso. Ejecutando los siguientes comandos establecerás las credenciales por defecto, el proyecto de GCP y te autenticarás utilizando el gcloud SDK.
gcloud auth login
gcloud config set project <project-name>
gcloud auth application-default login
El código que se muestra a continuación es el contenido en __main__.py que puede encontrarse dentro del repo de ejemplos en esta ruta. Este fichero de Python genera un clúster de GKE, el servicio de Kubernetes gestionado de GCP, y crea un despliegue canary de nginx. Para crearlo, genera un Kubeconfig (k8s_config) que permite a Pulumi autenticarse y acceder al clúster.
from pulumi import Config, export, get_project, get_stack, Output, ResourceOptions
from pulumi_gcp.config import project, zone
from pulumi_gcp.container import Cluster, get_engine_versions
from pulumi_kubernetes import Provider
from pulumi_kubernetes.apps.v1 import Deployment
from pulumi_kubernetes.core.v1 import Service
from pulumi_random import RandomPassword
config = Config(None)
NODE_COUNT = config.get('node_count') or 3
NODE_MACHINE_TYPE = config.get('node_machine_type') or 'n1-standard-1'
USERNAME = config.get('username') or 'admin'
PASSWORD = config.get_secret('password') or RandomPassword("password", length=20, special=True).result
MASTER_VERSION = config.get('master_version')
k8s_cluster = Cluster('gke-cluster',
initial_node_count=NODE_COUNT,
node_version=MASTER_VERSION,
min_master_version=MASTER_VERSION,
master_auth={ 'username': USERNAME, 'password': PASSWORD },
node_config={
'machine_type': NODE_MACHINE_TYPE,
'oauth_scopes': [
'https://www.googleapis.com/auth/compute',
'https://www.googleapis.com/auth/devstorage.read_only',
'https://www.googleapis.com/auth/logging.write',
'https://www.googleapis.com/auth/monitoring'
],
},
)
k8s_info = Output.all(k8s_cluster.name, k8s_cluster.endpoint, k8s_cluster.master_auth)
k8s_config = k8s_info.apply(
lambda info: """apiVersion: v1
clusters:
- cluster:
certificate-authority-data: {0}
server: https://{1}
name: {2}
contexts:
- context:
cluster: {2}
user: {2}
name: {2}
current-context: {2}
kind: Config
preferences: {{}}
users:
- name: {2}
user:
auth-provider:
config:
cmd-args: config config-helper --format=json
cmd-path: gcloud
expiry-key: '{{.credential.token_expiry}}'
token-key: '{{.credential.access_token}}'
name: gcp
""".format(info[2]['clusterCaCertificate'], info[1], '{0}_{1}_{2}'.format(project, zone, info[0])))
k8s_provider = Provider('gke_k8s', kubeconfig=k8s_config)
labels = { 'app': 'canary-{0}-{1}'.format(get_project(), get_stack()) }
canary = Deployment('canary',
spec={
'selector': { 'matchLabels': labels },
'replicas': 1,
'template': {
'metadata': { 'labels': labels },
'spec': { 'containers': [{ 'name': 'nginx', 'image': 'nginx' }] },
},
}, __opts__=ResourceOptions(provider=k8s_provider)
)
ingress = Service('ingress',
spec={
'type': 'LoadBalancer',
'selector': labels,
'ports': [{'port': 80}],
}, __opts__=ResourceOptions(provider=k8s_provider)
)
export('kubeconfig', k8s_config)
export('ingress_ip', Output.all(ingress.status['load_balancer']['ingress'][0]['ip']))
Prepara el entorno de Python e inicializa el stack tal y como hiciste en el ejemplo anterior.
cd ../gcp-py-gke
virtualenv -p python3 venv
source venv/bin/activate
pip3 install -r requirements.txt
pulumi stack init python-gke-testing
Este ejemplo emplea algunas variables a las que se les debe asignar un valor antes de generar el plan de despliegue. Para hacerlo, puedes utilizar el comando pulumi config set . Ten en cuenta que la contraseña que introduzcas debe tener al menos 16 caracteres!
pulumi config set gcp:project <gcp-project>
pulumi config set gcp:zone <gcp-zone>
pulumi config set password --secret <cluster-password>
pulumi config set master_version <master-version>
Una vez que asignes valores a dichas variables, puedes generar el plan de despliegue y aceptarlo si todo va bien.
pulumi up
Si accedes a la consola de Pulumi tras crear la infraestructura podrás ver los diferentes recursos que se crearon, así como los outputs generados y las variables que se definieron para el stack.

El Kubeconfig que se crea en el código puede obtenerse como un output de Pulumi. Sin embargo, se trata de un output cifrado dentro del stack, por lo que es necesario utilizar el flag –show-secrets para poder obtenerlo en claro🔐.
pulumi stack output kubeconfig --show-secrets > kubeconfig.yaml
Utilizando este Kubeconfig, podrás acceder al clúster utilizando kubectly comprobar cómo el despliegue canary se encuentra presente.

Al igual que en el ejemplo anterior, una vez que hayas terminado de probar el clúster, elimina los recursos, el stack de Pulumi y desactiva el virtual environment de Python.
pulumi destroy
pulumi stack rm python-gke-testing
deactivate
Azure☁️🔷
El repositorio de ejemplos de Pulumi incluye un ejemplo similar a los anteriores pero utilizando el servicio de Kubernetes gestionado de Azure (AKS). No obstante, estoy seguro de que ya te has hecho una idea general de como utilizar Pulumi para desplegar clústers gestionados😉 . Por ello, si tienes interés en probarlo, puedes seguir las instrucciones que encontrarás en el propio repositorio y desplegar un clúster de AKS.
Pulumi y Kubernetes☸️
Por último, Pulumi puede interaccionar directamente con clústers de Kubernetes
(tanto gestionados como no gestionados) para desplegar aplicaciones y recursos. El siguiente código crea un deployment de nginx simple en Kuberneres utilizando Python.
import pulumi
from pulumi_kubernetes.apps.v1 import Deployment
config = pulumi.Config()
nginxLabels = { "app": "nginx" }
nginxDeployment = Deployment(
"nginx-deployment",
spec={
"selector": { "matchLabels": nginxLabels },
"replicas": 2 if config.get_int("replicas") is None else config.get_int("replicas"),
"template": {
"metadata": { "labels": nginxLabels },
"spec": {
"containers": [{
"name": "nginx",
"image": "nginx:1.7.9",
"ports": [{ "containerPort": 80 }],
}],
},
},
})
pulumi.export("nginx", nginxDeployment.metadata["name"])
Para poder interaccionar con el clúster solo necesitarás establecer el valor de la variable KUBECONFIG con el path del fichero Kubeconfig del clúster que quieras usar. Una vez que lo hayas configurado, ejecuta los siguientes comandos para desplegar el stack.
cd ../kubernetes-py-nginx
virtualenv -p python3 venv
source venv/bin/activate
pip3 install -r requirements.txt
pulumi stack init python-k8s-testing
pulumi up

Si inspeccionas los pods y los despliegues dentro del clúster, verás que los recursos definidos en el fichero de Python han sido creados correctamente por Pulumi.

Haz limpieza una vez que hayas terminado de probar!
pulumi destroy
pulumi stack rm python-k8s-testing
deactivate
Sigue aprendiendo👩💻👨💻
Si te ha gustado Pulumi, tómate tu tiempo para investigar dentro del repositorio de ejemplos, ya que encontrarás un montón de casos de uso interesantes. utilizando diferentes lenguajes de programación y diferentes proveedores de cloud.
Algunos de los casos de uso más interesantes son:
- Desplegando con Helm con Typescript
- Acortador de URLs Serverless en Azure con Typescript
- Cloud functions en GCP con Python y Go
- Desplegando un servidor de CI Jenkins en k8s con Python
- Desplegando un Slackbot en GCP con Typescript
Conviértete en un 'Políglota de la IaC', confía en mí, merece la pena😎!
Otros Artículos
Karpenter vs Cluster Autoscaler ☸️ ⚒️
Getting the size of a Kubernetes cluster right is not an easy task, if the number of nodes provisioned is too high, resources might be…
Read MoreAutenticación a nivel de clúster para tus registries privados 🌐🔐
Using private container image registries is a common practice in the industry, as it ensures applications packaged within the images are just accessible for users…
Read MoreKaniko: construyendo imágenes sin Docker🐳🔫
Building container images is a pretty common stage within the modern CI flows, in which these images are the main artifact used to distribute and…
Read MoreKubernetes en 5 Minutos ⏱️☸️
English https://www.youtube.com/watch?v=N8LDO9pHY8I Spanish https://www.youtube.com/watch?v=TC6VkqQ835U Other Articles
Read MoreKsniff: capturando tráfico en Kubernetes 🕵️♂️📦
Troubleshooting containers in Kubernetes is a recurring topic that you may face regularly. Logging and monitoring tools come in really handy here as they help…
Read MoreHelmfile: superpoderes para Helm ☸️🔧
Helm is definitely one the most used tools in the Kubernetes world. It comes in pretty handy as it is a deployment tool, a package…
Read MoreTerraboard: Gestionando estados de Terrafom visualmente🌍🖥
Dealing with multiple terraform remote states can become a rather complex task. Besides, querying resources with terraform CLI isn’t very visual 😅. In this post…
Read Moreeksctl: el CLI para EKS ☸️ 🎮
Let’s be honest, managing Kubernetes clusters is not an easy task, even when it comes to managed Kubernetes services. Controlling the overall infrastructure, performing cluster…
Read MoreWerf: GitOps totalmente customizable 🛥️⚙️
This is the third post of a collection of GitOps tools articles. In this post, a continuous integration and continuous deployment tool for Kubernetes is…
Read More