Kubernetes con IaC
Instalación y configuración básica de kubernetes utilizando Terraform como 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:
- Inicializar el proyecto
1
terraform init
Esto descarga los proveedores necesarios y prepara el entorno de trabajo.
- 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.
- Aplicar la infraestructura
1
terraform apply
Ejecuta el plan y realiza los cambios en la infraestructura.
- Ver el estado actual
1
terraform show
Muestra el estado actual de los recursos gestionados.
- 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-hostedTraefik
- Ingress ControllerCert-Manager
- Certificados SSL automáticosArgoCD
- 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!