Entrada

Kubernetes con IaC

Instalación y configuración básica de kubernetes utilizando Terraform como IaC

Kubernetes con IaC

En esta ocasión me gustaría comenzar con una serie de posts relacionados a desplegar Infraestructura y aplicaciones como código siguiendo buenas prácticas y permitiendo replicar el mismo resultado en distintos entornos. Para comenzar utilizaremos Terraform que es uno de los actores más conocidos y con más tiempo dentro de la industria, además de tener la ventaja de ser Cloud-agnostic, de esta forma no estamos limitados a operar con un Cloud Provider en particular e incluso y más importante, podemos utilizarlo para en entornos Self-hosted. Más adelante estaré evaluando otras alternativas como OpenTofu y Pulumi.

¿Qué es Terraform?

Terraform es una herramienta de infraestructura como código (IaC) desarrollada por HashiCorp que permite definir, desplegar y gestionar infraestructura de forma declarativa. Con Terraform puedes automatizar la creación de recursos en múltiples plataformas (nube pública, local, containers, etc.) utilizando archivos .tf que describen el estado deseado del sistema.

Instalación de Terraform

Para comenzar con Terraform es necesario instalar su CLI, en la documentación oficial se pueden encontrar las instrucciones para instalarlo dependiendo del sistema operativo.

Una vez instalado, ya se pueden ejecutar comandos básicos como:

  1. Inicializar el proyecto
    1
    
    terraform init
    

    Esto descarga los proveedores necesarios y prepara el entorno de trabajo.

  2. Revisar el plan de ejecución
    1
    
    terraform plan
    

    Muestra qué recursos se crearán, modificarán o destruirán sin aplicar cambios aún.

  3. Aplicar la infraestructura
    1
    
    terraform apply
    

    Ejecuta el plan y realiza los cambios en la infraestructura.

  4. Ver el estado actual
    1
    
    terraform show
    

    Muestra el estado actual de los recursos gestionados.

  5. Destruir la infraestructura
    1
    
    terraform destroy
    

    Elimina todos los recursos gestionados por Terraform.

¿Qué es un proveedor en Terraform?

En Terraform, un proveedor es el componente que permite a Terraform interactuar con APIs externas para crear, leer, actualizar y eliminar recursos.

Cada proveedor actúa como un puente entre Terraform y una plataforma específica: puede ser una nube pública como AWS, GCP, Azure, o herramientas como Kubernetes, GitHub, Cloudflare, Proxmox, etc.

Ejemplo: Cuando usas Terraform para desplegar recursos en un clúster Kubernetes, necesitas declarar el proveedor kubernetes o helm. Si vas a interactuar con Cloudflare, se requiere el proveedor cloudflare.

Los proveedores se pueden encontrar en el registry de terraform, providers. Para efectos de este post se estarán utilizando los siguientes providers:

Hands-on

Para el post de hoy he preparado un repositorio en Github que contiene código de Terraform para desplegar la infraestructura básica para un cluster de Kubernetes recién creado. La idea es replicar lo que se instaló y configuró en los posts kubernetes-selfhosted-con-k3s y https-para-kubernetes con el extra de la instalación de ArgoCD. Es decir, basicamente instalaremos lo siguiente:

  • Metallb - Load balanacer y asignación de IP dinámicas para entornos self-hosted
  • Traefik - Ingress Controller
  • Cert-Manager - Certificados SSL automáticos
  • ArgoCD - Herramienta GitOps para despliegue de aplicaciones

El repositorio donde está todo esto es en homelab-infra que se irá actualizando a medida que vaya generando código nuevo. El repositorio tiene su propio README.md en donde se indica como utilizarlo pero se explicará acá de igual manera.

Para comenzar clonaremos el repositorio y entraremos al directorio k8s/ingress-controller

1
2
git clone https://github.com/Brsalcedom/homelab-infra
cd homelab-infra/k8s/ingress-controller

main.tf

Una vez teniendo los archivos lo primero a revisar es el archivo main.tf, que es el que tiene el código que se va a ejecutar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
module "metallb_namespace" {
  source = "./modules/namespace"
  name   = var.metallb_namespace
  labels = {
    "pod-security.kubernetes.io/enforce" = "privileged"
    "pod-security.kubernetes.io/audit"   = "privileged"
    "pod-security.kubernetes.io/warn"    = "privileged"
  }
}

module "metallb" {
  source        = "./modules/metallb"
  namespace     = var.metallb_namespace
  chart_version = var.metallb_version
  ip_range      = var.metallb_ip_range

  depends_on = [module.metallb_namespace]
}


module "traefik_namespace" {
  source = "./modules/namespace"
  name   = var.traefik_namespace
}

module "traefik" {
  source        = "./modules/traefik"
  namespace     = var.traefik_namespace
  chart_version = var.traefik_version
  acme_email    = var.traefik_acme_email

  depends_on = [module.traefik_namespace]
}

module "cert_manager_namespace" {
  source = "./modules/namespace"
  name   = var.cert_manager_namespace
}

module "cert_manager" {
  source               = "./modules/cert-manager"
  namespace            = var.cert_manager_namespace
  chart_version        = var.cert_manager_version
  issuer_name          = var.cert_manager_issuer
  cloudflare_api_token = var.cloudflare_api_token
  cloudflare_email     = var.cloudflare_email

  depends_on = [module.cert_manager_namespace]
}

module "argocd_namespace" {
  source = "./modules/namespace"
  name   = var.argocd_namespace
}

module "argocd" {
  source              = "./modules/argocd"
  namespace           = var.argocd_namespace
  chart_version       = var.argocd_version
  fqdn                = var.argocd_fqdn
  traefik_entrypoint  = var.traefik_entrypoint
  cert_manager_issuer = var.cert_manager_issuer

  depends_on = [module.argocd_namespace]
}

Como se comentó anteriormente, esto instala Traefik, Metallb, Cert-Manager y Argocd junto con sus respectivos namespaces. El código está estructurado para llamar a módulos que son piezas de código reutilizables en donde se les pasan variables y se despliega infraestructura.

En caso de que no quieras instalar todo esto, debes modificar main.tf para eliminar los módulos que no desees.

Es importante tener en cuenta que no en todas las ocasiones necesitaremos instalar todo esto, hay casos en que una instalación de kubernetes ya trae un ingress controler por defecto, como es el caso de k3s que viene con Traefik. También es posible que tú no quieras instalar ArgoCD por ejemplo, y es totalmente válido, en ese caso es solo modificar main.tf para reflejar lo que quieres instalar.

variables.tf

A continuación deberemos verificar las variables que solicita el código, para ello debemos inspeccionar el archivo variables.tf.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
variable "kubeconfig" {
  description = "Path to the kubeconfig file"
  type        = string
  default     = "~/.kube/config"
}

variable "kubeconfig_context" {
  description = "Kubernetes context to use from the kubeconfig file"
  type        = string
  default     = "default"
}

variable "metallb_namespace" {
  description = "Namespace for the MetalLB installation"
  type        = string
  default     = "metallb-system"
}

variable "metallb_version" {
  description = "Version of the MetalLB Helm chart"
  type        = string
  default     = "v0.15.2"
}

variable "metallb_ip_range" {
  description = "IP range for MetalLB to use for LoadBalancer services"
  type        = string
}

variable "traefik_namespace" {
  description = "Namespace for the Traefik ingress controller"
  type        = string
  default     = "traefik"
}

variable "traefik_version" {
  description = "Version of the Traefik Helm chart"
  type        = string
  default     = "v36.1.0"
}

variable "traefik_acme_email" {
  description = "Email address for ACME registration with Traefik"
  type        = string
}


variable "cert_manager_namespace" {
  description = "Namespace for the cert-manager release"
  type        = string
  default     = "cert-manager"
}

variable "cert_manager_version" {
  description = "Version of the cert-manager Helm chart"
  type        = string
  default     = "v1.18.0"
}

variable "cert_manager_issuer" {
  description = "Cluster issuer name for cert-manager to use with Traefik"
  type        = string
  default     = "cloudflare-clusterissuer"
}

variable "cloudflare_api_token" {
  description = "Cloudflare API token for cert-manager"
  type        = string
}

variable "cloudflare_email" {
  description = "Email address associated with the Cloudflare account"
  type        = string
}

variable "argocd_namespace" {
  description = "Namespace for the ArgoCD installation"
  type        = string
  default     = "argocd"
}

variable "argocd_version" {
  description = "Version of the ArgoCD Helm chart"
  type        = string
  default     = "8.1.1"
}

variable "argocd_fqdn" {
  description = "Fully qualified domain name for the ArgoCD web interface"
  type        = string
}

variable "traefik_entrypoint" {
  description = "Traefik entrypoint to use for ArgoCD"
  type        = string
  default     = "websecure"
}

Las variables mínimas que deberemos entregar para ejecutar el código son las que no tienen definido un campo default. También se pueden enviar las otras variables para sobrescribir el valor por defecto.

Con respecto a las versiones de las aplicaciones, se ha seleccionado la más reciente al momento de escribir este post

Para enviar estas variables tenemos dos opciones, enviarlas a través del prompt de terraform cada vez que hagamos terraform plan o terraform apply, o mejor aún y lo recomendado es crear un archivo llamado terraform.tfvars en el mismo directorio de main.tf, de esta forma se inyectarán automáticamente las variables cada vez que ejecutemos terraform.

1
2
3
4
5
6
7
8
9
# mínimo
metallb_ip_range      = "192.168.1.200-192.168.1.220"
traefik_acme_email    = "[email protected]"
cloudflare_api_token  = "<TOKEN>"
cloudflare_email      = "[email protected]"
argocd_fqdn           = "argocd.home.cervant.net"
# recomendado
kubeconfig            = "C:\\Users\\Cervant\\.kube\\config"
kubeconfig_context    = "default"

Si son como yo que tienen más de un conexto configurado en su kubeconfig, querrán específicar manualmente el PATH y el contexto para evitar aplicar configuraciones a un cluster de kubernetes incorrecto.

terraform init

Ya teniendo todo lo anterior listo y con el archivo terraform.tfvars con nuestras variables, ejecutaremos terraform init para comenzar el proceso

1
terraform init

Este comando inicializa Terraform en el directorio de trabajo actual. Lee los archivos de configuración como providers.tf y versions.tf, descarga los providers especificados y configura el backend (si está definido). También valida que las versiones indicadas de Terraform y los providers sean compatibles, y deja el entorno listo para ejecutar otros comandos como terraform plan o terraform apply.

terraform plan

Luego, toca ejecutar terraform plan que analiza los archivos de configuración y muestra un plan detallado de los cambios que Terraform aplicaría sobre la infraestructura si se ejecutara terraform apply. Compara el estado actual (según el archivo terraform.tfstate o backend configurado) con la configuración declarada en los archivos .tf. No realiza ningún cambio real, pero permite revisar qué recursos se crearán, modificarán o destruirán antes de aplicarlos.

Verás que al ejecutar este comando se creará el archivo terraform.tfstate que mantiene un registro de la infraestructura actual. Este archivo es fundamental: permite a Terraform llevar un seguimiento de los recursos que ha creado y de su configuración actual. No debe eliminarse ni modificarse manualmente, ya que Terraform lo utiliza para determinar qué cambios son necesarios al comparar el estado real con el deseado. Gracias a esto, se evita recrear recursos que ya existen y se aplican solo los cambios necesarios

Si verificamos el estado actual del cluster de kubernetes, se puede apreciar de que está completamente en blanco, solo cuenta con los namespaces y pods por defecto.

Esto cambiará una vez apliquemos la infraestructura con Terraform.

terraform apply

Finalmente con terraform apply desplegaremos los cambios que han sido indicados en el plan anterior. Solicitará confirmación antes de comenzar.

Puedes utilizar el comando terraform apply -auto-approve para que no solicite confirmación

Una vez completado veremos los outputs que han sido definidos en outputs.tf. Estos corresponden a las versiones de los aplicativos instalados y los datos de acceso a ArgoCD.

Resultado

Si verificamos nuevamente los recursos en el cluster de kubernetes veremos que se han creado nuevos namespaces, pods, servicios, crds, etc.

Para validar que la instalación fué un exito, ingresaré a la URL indicada en el output en donde me recibe el login de ArgoCD.

Para ingresar a argocd se debe utilizar el usuario admin y la contraseña que puede ser obtenida con el comando indicado en el output argocd_admin_password_command. Pero todo lo relacionado a ArgoCD es algo que veremos más adelante.

Construye, automatiza, repite. ¡Nos vemos en el próximo post!

Esta entrada está licenciada bajo CC BY 4.0 por el autor.