Usar Terraform con Elastic Cloud

blog-thumb-cloud-blue.png

Resumen: Hoy nos sumergiremos en los dos proveedores de Terraform disponibles de Elastic. Terraform te permite definir tu infraestructura como un código y guardarla en repositorios para realizar cambios fácilmente.

Primeros pasos

La infraestructura como código (IaC) significa administrar tu infraestructura de TI a través de archivos para que los cambios se puedan aplicar automáticamente sin intervención y se documenten adecuadamente en todo momento. Terraform se ha convertido en una especie de estándar con algunos competidores como CloudFormation o Pulumi. Hoy nos centraremos en Terraform en combinación con Elastic Cloud, por lo que no necesitas usar la UI del cloud para activar, cambiar o eliminar clústeres.

Creación de una clave de API en el cloud

Para usar Terraform, necesitas una clave de API en el cloud. Asegúrate de que esta no sea una clave de API para una instancia de Elastic Cloud, sino para tu cuenta de Elastic Cloud. El proveedor de Elastic Cloud muestra la configuración con más detalle en la parte de Autenticación de su documentación.

Configuración inicial de Terraform

Comencemos con un archivo main.tf casi mínimo:

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.4.0"
    }
  }
}

provider "ec" {
}

resource "ec_deployment" "custom-deployment-id" {
  name                   = "My deployment identifier"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

After storing this you can run

terraform init
terraform validate
terraform apply auto-approve

La última llamada tardará de uno a tres minutos para que aparezcan los nodos de Kibana y Elasticsearch. Con eso, tenemos nuestra instancia en funcionamiento. Sin embargo, si queremos hacer uso de eso, definir las salidas en main.tf es muy útil:

output "elasticsearch_endpoint" {
  value = ec_deployment.custom-deployment-id.elasticsearch[0].https_endpoint
}

output "elasticsearch_username" {
  value = ec_deployment.custom-deployment-id.elasticsearch_username
}

output "elasticsearch_password" {
  value = ec_deployment.custom-deployment-id.elasticsearch_password
  sensitive = true
}

output "kibana_endpoint" {
  value = ec_deployment.custom-deployment-id.kibana[0].https_endpoint
}

Ahora, después de ejecutar la aplicación de terraform nuevamente, puedes ver los endpoints en la salida de terraform. Con esta salida, puedes ejecutar secuencias de comandos que parsean esa salida y luego realizar acciones como la creación de índices o plantillas.

Puedes usar jq para extraer partes del comando de salida de terraform (o incluso inspeccionar el archivo de estado de Terraform):

output=$(terraform output -json)
endpoint=$(echo $output | jq -r ".elasticsearch_endpoint.value")
username=$(echo $output | jq -r ".elasticsearch_username.value")
password=$(echo $output | jq -r ".elasticsearch_password.value")
curl $endpoint -u $username:$password

De esta manera, la ubicación del estado de Terraform también es irrelevante. Otra característica del proveedor de Elastic Cloud es la configuración de clústeres remotos. Veamos cómo funciona.

Configuración de clústeres remotos para CCS/CCR

Las funcionalidades Cross Cluster Search (CCS), así como Cross Cluster Replication (CCR), requieren la configuración de los llamados clústeres remotos, donde un cluster puede acceder a otro cluster. El siguiente fragmento de código de muestra omitirá por completo la configuración o las partes de salida innecesarias, pero solo mostrará lo que se requiere.

resource "ec_deployment" "cluster_1" {
  name                   = "cluster_1"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

resource "ec_deployment" "cluster_2" {
  name                   = "cluster_2"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {
    remote_cluster {
      deployment_id = ec_deployment.cluster_1.id
      alias         = ec_deployment.cluster_1.name
      ref_id        = ec_deployment.cluster_1.elasticsearch.0.ref_id
    }
  }

  kibana {}
}

En esta configuración, cluster_2 tendrá una conexión remota con cluster_1.

Es hora de configurar la replicación entre clústeres. Nota: ¡La línea de comentarios sobre el comando indica en qué clúster debe ejecutarse!

# Cluster 1
PUT my-leader-index

# Cluster 2
PUT my-follower-index/_ccr/follow?wait_for_active_shards=1
{"remote_cluster":"cluster_1","leader_index":"my-leader-index"}

# Cluster 1
PUT /my-leader-index/_doc/my-doc?refresh=true
{"key":"value"}

# Cluster 2, repeat until hit count > 0, should take less than a second usually
GET /my-follower-index/_search

Ahora tenemos un índice de seguidores en el cluster 2 basado en la configuración del cluster remoto en Terraform.

La búsqueda cruzada de clústeres es similar al uso aprovechando la conexión de eliminación de clústeres:

# Cluster 1
PUT /cluster1-index/_doc/1?refresh=true
{"cluster":"cluster1","name":"my name"}

# Cluster 2
PUT /cluster2-index/_doc/1?refresh=true
{"cluster":"cluster2","name":"my name"}

# Cluster 2
GET /cluster2-index,cluster_1:cluster1-index/_search

Además de administrar las instancias tú mismo, es posible que también desees cambiar la configuración de un clúster en particular. Aquí es donde el segundo proveedor de Terraform, es decir, el proveedor elasticstack de Terraform, permite configurar un cluster, independientemente de si está en Elastic Cloud o en las instalaciones.

Uso del proveedor de Elasticstack

El proveedor elasticstack permite administrar partes del Elastic Stack, por ejemplo, configuraciones de cluster, plantillas de índices y componentes, usuarios y roles o pipelines y procesadores de ingesta. En este ejemplo, crearemos un pipeline de índice con dos procesadores:

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.4.0"
    }
    elasticstack = {
      source = "elastic/elasticstack",
      version = "0.3.3"
    }
  }
}

provider "ec" {
}

resource "ec_deployment" "custom-deployment-id" {
  name                   = "custom-deployment-id"

  region                 = "gcp-europe-west3"
  version                = "8.1.3"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

provider "elasticstack" {
  elasticsearch {
    username  = ec_deployment.custom-deployment-id.elasticsearch_username
    password  = ec_deployment.custom-deployment-id.elasticsearch_password
    endpoints = [ec_deployment.custom-deployment-id.elasticsearch[0].https_endpoint]
  }
}

data "elasticstack_elasticsearch_ingest_processor_set" "set_field_terraform" {
  field = "pipeline-source"
  value = "terraform"
}

data "elasticstack_elasticsearch_ingest_processor_grok" "grok_the_log" {
  field    = "message"
  patterns = ["%%{TIMESTAMP_ISO8601:@timestamp} %%{LOGLEVEL:level} %%{GREEDYDATA:message}"]
}

resource "elasticstack_elasticsearch_ingest_pipeline" "ingest" {
  name = "my-ingest-pipeline"

  processors = [
    data.elasticstack_elasticsearch_ingest_processor_set.set_field_terraform.json,
    data.elasticstack_elasticsearch_ingest_processor_grok.grok_the_log.json
  ]
}

This creates a pipeline named my-ingest-pipeline after bringing up the cluster. You could now go to Management/Ingest Pipeline in Kibana and see that the pipeline has been created, or just run the following simulate pipeline call:

POST _ingest/pipeline/my-ingest-pipeline/_simulate
{
  "docs": [
    {
      "_source": {
        "message": "2022-03-03T12:34:56.789Z INFO ze message"
      }
    }
  ]
}

This will return the following document as part of the response:

"_source" : {
  "@timestamp" : "2022-03-03T12:34:56.789Z",
  "level" : "INFO",
  "pipeline-source" : "terraform",
  "message" : "ze message"
}

There is one final step, which is not yet as easy as it should be, but the following little trick does it at least for most of my PoCs.

Adición de un dashboard

Si bien el proveedor elasticstack de Terraform en este momento solo funciona para cambiar partes de la configuración de Elasticsearch, hay otras partes en la pila como Kibana.

Una de mis formas favoritas de hacer una demostración del Elastic Stack es proporcionar el ejemplo completo en un repositorio de GitHub y hacer que sea lo más fácil posible de poner en marcha.

Parte de esto es instalar un dashboard que muestra los datos que formaban parte de la demostración. Entonces, ¿cómo se puede hacer esto sin ningún ayudante de Kibana en el proveedor elasticstack de Terraform? Mediante el uso de un comando curl como parte de un .null_resource. Esto agrega cierta dependencia de la plataforma y el requisito de curl donde sea que se ejecute.

terraform {
  required_version = ">= 1.0.0"

  required_providers {
    ec = {
      source  = "elastic/ec"
      version = "0.4.0"
    }
  }
}

provider "ec" {
}

data "local_file" "dashboard" {
    filename = "${path.module}/dashboard.ndjson"
}

resource "ec_deployment" "custom-deployment-id" {
  name                   = "custom-deployment-id"

  region                 = "gcp-europe-west3"
  version                = "8.1.2"
  deployment_template_id = "gcp-memory-optimized-v2"

  elasticsearch {}

  kibana {}
}

resource "null_resource" "store_local_dashboard" {
  provisioner "local-exec" {
    command = "curl -X POST -u ${ec_deployment.custom-deployment-id.elasticsearch_username}:${ec_deployment.custom-deployment-id.elasticsearch_password} ${ec_deployment.custom-deployment-id.kibana[0].https_endpoint}/api/saved_objects/_import?overwrite=true -H \"kbn-xsrf: true\" --form file=@dashboard.ndjson"
  }
  depends_on = [ec_deployment.custom-deployment-id]
  triggers = {
    dashboard_sha1 = "${sha1(file("dashboard.ndjson"))}"
  }
}

También puedes notar la parte de desencadenantes en null_resource: esto tendrá en cuenta los cambios del archivo del dashboard y ejecutará la llamada curl nuevamente si cambia el sha1sum del archivo JSON.

Resumen

Espero que hayan disfrutado el viaje a través del proveedor ec de Terraform y el proveedor elasticstack de Terraform. Ambos están en desarrollo y puedes seguir los repositorios de GitHub correspondientes (ec, elasticstack).

Además, si encuentras algún problema, crea un problema en el repositorio de GitHub correspondiente y envía tus comentarios. ¡Gracias y feliz uso de Terraform!