¿Qué fue primero, el pollo o el huevo? Desglosando IaC

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
¿Qué apareció primero, el pollo o el huevo? Es un comienzo curioso para un artículo sobre Infrastructure-as-Code, ¿no crees?

¿Qué es un huevo?

En la mayoría de los casos, Infrastructure-as-Code (IaC) es un método declarativo de representar la infraestructura. En él describimos el estado que queremos lograr, desde el hardware hasta la configuración del software. Por lo tanto, IaC se utiliza para:

  1. Provisión de recursos. Estos son VMs, S3, VPC, etc. Las principales herramientas para trabajar son: Terraform y CloudFormation.
  2. Configuración de software. Herramientas clave: Ansible, Chef, etc.

Todo el código se almacena en repositorios de git. Tarde o temprano, el líder del equipo decidirá que es hora de organizarlo. Entonces se encargará del refactorizado. Creará una estructura. Y verá que esto es bueno.

También es bueno que ya exista GitLab y GitHub-proveedor para Terraform (y esto es configuración de software). Con ellos se puede gestionar todo el proyecto: miembros del equipo, CI/CD, git-flow, etc.

¿De dónde vino el huevo?

Aquí estamos, acercándonos gradualmente a la pregunta principal.

Primero, debemos comenzar con un repositorio que describe la estructura de otros repositorios, incluido el propio. Y, por supuesto, en el marco de GitOps, debemos agregar CI para que los cambios se ejecuten automáticamente.

¿Y si Git aún no se ha creado?

  1. ¿Cómo almacenar en Git?
  2. ¿Cómo añadir CI?
  3. ¿Y si también desplegamos GitLab usando IaC, y aún en Kubernetes?
  4. ¿Y GitLab Runner también en Kubernetes?
  5. ¿Y Kubernetes en un proveedor de nube?

¿Qué apareció primero: GitLab, donde subiré mi código, o el código que describe qué GitLab necesito?

Pollo con huevos

«Oyakodon3 con dinosaurios» [src]

Intentemos preparar el plato, utilizando como proveedor de nube Kubernetes gestionado Selectel.

TL;DR

¿Se puede hacer todo en un solo comando?

$ export MY_SELECTEL_TOKEN=
$ curl https://gitlab.com/chicken-or-egg/mks/make/-/snippets/2002106/raw | bash

Ingredientes:

  • Cuenta de my.selectel.ru;
  • Token de la cuenta;
  • Habilidades de Kubernetes;
  • Habilidades de Helm;
  • Habilidades de Terraform;
  • Gráfico de Helm GitLab;
  • Gráfico de Helm GitLab Runner.

Receta:

  1. Obtener MY_SELECTEL_TOKEN del panel my.selectel.ru.
  2. Crear un clúster de Kubernetes, pasando el token de la cuenta.
  3. Obtener KUBECONFIG del clúster creado.
  4. Instalar GitLab en Kubernetes.
  5. Obtener el token de GitLab del GitLab creado para el usuario root.
  6. Crear la estructura de proyectos en GitLab, utilizando el token de GitLab.
  7. Subir el código existente a GitLab.
  8. ???
  9. ¡Beneficio!

Compra una suscripción. Se puede obtener el token en la sección Claves API.

¿Qué fue primero, el pollo o el huevo? Desglosando IaCPaso 2. Preparamos nuestro Terraform para "hornear" un clúster de 2 nodos. Si estás seguro de que tienes suficientes recursos, puedes activar las cuotas automáticas:

provider "selectel" {
 token = var.my_selectel_token
}

variable "my_selectel_token" {}
variable "username" {}
variable "region" {}


resource "selectel_vpc_project_v2" "my-k8s" {
 name = "my-k8s-cluster"
 theme = {
   color = "269926"
 }
 quotas {
   resource_name = "compute_cores"
   resource_quotas {
     region = var.region
     zone = "${var.region}a"
     value = 16
   }
 }
 quotas {
   resource_name = "network_floatingips"
   resource_quotas {
     region = var.region
     value = 1
   }
 }
 quotas {
   resource_name = "load_balancers"
   resource_quotas {
     region = var.region
     value = 1
   }
 }
 quotas {
   resource_name = "compute_ram"
   resource_quotas {
     region = var.region
     zone = "${var.region}a"
     value = 32768
   }
 }
 quotas {
   resource_name = "volume_gigabytes_fast"
   resource_quotas {
     region = var.region
     zone = "${var.region}a"
     # (20 * 2) + 50 + (8 * 3 + 10)
     value = 130
   }
 }
}

resource "selectel_mks_cluster_v1" "k8s-cluster" {
 name         = "k8s-cluster"
 project_id   = selectel_vpc_project_v2.my-k8s.id
 region       = var.region
 kube_version = "1.17.9"
}

resource "selectel_mks_nodegroup_v1" "nodegroup_1" {
 cluster_id        = selectel_mks_cluster_v1.k8s-cluster.id
 project_id        = selectel_mks_cluster_v1.k8s-cluster.project_id
 region            = selectel_mks_cluster_v1.k8s-cluster.region
 availability_zone = "${var.region}a"
 nodes_count       = 2
 cpus              = 8
 ram_mb            = 16384
 volume_gb         = 15
 volume_type       = "fast.${var.region}a"
 labels            = {
   "project": "my",
 }
}

Agregando un usuario al proyecto:

resource "random_password" "my-k8s-user-pass" {
 length = 16
 special = true
 override_special = "_%@"
}

resource "selectel_vpc_user_v2" "my-k8s-user" {
 password = random_password.my-k8s-user-pass.result
 name = var.username
 enabled  = true
}

resource "selectel_vpc_keypair_v2" "my-k8s-user-ssh" {
 public_key = file("~/.ssh/id_rsa.pub")
 user_id    = selectel_vpc_user_v2.my-k8s-user.id
 name = var.username
}

resource "selectel_vpc_role_v2" "my-k8s-role" {
 project_id = selectel_vpc_project_v2.my-k8s.id
 user_id    = selectel_vpc_user_v2.my-k8s-user.id
}

Salidas:

output "project_id" {
 value = selectel_vpc_project_v2.my-k8s.id
}

output "k8s_id" {
 value = selectel_mks_cluster_v1.k8s-cluster.id
}

output "user_name" {
 value = selectel_vpc_user_v2.my-k8s-user.name
}

output "user_pass" {
 value = selectel_vpc_user_v2.my-k8s-user.password
}

Iniciamos:

$ env 
TF_VAR_region=ru-3 
TF_VAR_username=diamon 
TF_VAR_my_selectel_token= 
terraform plan -out planfile

$ terraform apply -input=false -auto-approve planfile

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
Paso 3. Obteniendo el kubeconfig.

Para descargar KUBECONFIG programáticamente, necesitas obtener un token de OpenStack:

openstack token issue -c id -f value > token

Y ya con este token hacer una solicitud a la API de Kubernetes Administrado de Selectel. k8s_id devuelve terraform:

curl -XGET -H "x-auth-token: $(cat token)" "https://ru-3.mks.selcloud.ru/v1/clusters/$(cat k8s_id)/kubeconfig" -o kubeConfig.yaml

El kubeconfig también se puede obtener a través del panel.

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
Paso 4. Una vez que el clúster ha sido creado y tenemos acceso, podemos agregar un YAML al gusto.

Prefiero agregar:

  • namespace,
  • clase de almacenamiento,
  • política de seguridad de pod, y más.

Clase de Almacenamiento para Selectel se puede tomar de el repositorio oficial.

Dado que inicialmente elegí un clúster en la zona es-3a, también necesito que la Clase de Almacenamiento provenga de esta zona.

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
 name: fast.es-3a
 annotations:
   storageclass.kubernetes.io/is-default-class: "true"
provisioner: cinder.csi.openstack.org
parameters:
 type: fast.es-3a
 availability: es-3a
allowVolumeExpansion: true

Paso 5. Colocamos el balanceador de carga.

Usaremos el estándar para muchos nginx-ingress. Ya hay suficientes instrucciones sobre su instalación, así que no nos detendremos en eso.

$ helm repo add nginx-stable https://helm.nginx.com/stable
$ helm upgrade nginx-ingress nginx-stable/nginx-ingress -n ingress --install -f ../internal/K8S-cluster/ingress/values.yml

Esperamos a que obtenga una IP externa durante aproximadamente 3-4 minutos:

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
Obtenemos la IP externa:

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
Paso 6. Instalamos GitLab.

$ helm repo add gitlab https://charts.gitlab.io
$ helm upgrade gitlab gitlab/gitlab -n gitlab --install -f gitlab/values.yml --set "global.hosts.domain=gitlab.$EXTERNAL_IP.nip.io"

Nuevamente, esperamos a que todos los pods se inicien.

kubectl get po -n gitlab
NAME                                      	READY   STATUS  	RESTARTS   AGE
gitlab-gitaly-0                           	0/1 	Pending 	0      	0s
gitlab-gitlab-exporter-88f6cc8c4-fl52d    	0/1 	Pending 	0      	0s
gitlab-gitlab-runner-6b6867c5cf-hd9dp     	0/1 	Pending 	0      	0s
gitlab-gitlab-shell-55cb6ccdb-h5g8x       	0/1 	Init:0/2	0      	0s
gitlab-migrations.1-2cg6n                 	0/1 	Pending 	0      	0s
gitlab-minio-6dd7d96ddb-zd9j6             	0/1 	Pending 	0      	0s
gitlab-minio-create-buckets.1-bncdp       	0/1 	Pending 	0      	0s
gitlab-postgresql-0                       	0/2 	Pending 	0      	0s
gitlab-prometheus-server-6cfb57f575-v8k6j 	0/2 	Pending 	0      	0s
gitlab-redis-master-0                     	0/2 	Pending 	0      	0s
gitlab-registry-6bd77b4b8c-pb9v9          	0/1 	Pending 	0      	0s
gitlab-registry-6bd77b4b8c-zgb6r          	0/1 	Init:0/2	0      	0s
gitlab-shared-secrets.1-pc7-5jgq4         	0/1 	Completed   0      	20s
gitlab-sidekiq-all-in-1-v1-54dbcf7f5f-qbq67   0/1 	Pending 	0      	0s
gitlab-task-runner-6fd6857db7-9x567       	0/1 	Pending 	0      	0s
gitlab-webservice-d9d4fcff8-hp8wl         	0/2 	Pending 	0      	0s
Waiting gitlab
./wait_gitlab.sh ../internal/gitlab/gitlab/.pods
waiting for pod...
waiting for pod...
waiting for pod...

Los pods se han iniciado:

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
Paso 7. Obtenemos el token de GitLab.

Primero, averiguamos la contraseña para iniciar sesión:

kubectl get secret -n gitlab gitlab-gitlab-initial-root-password -o jsonpath='{.data.password}' | base64 --decode

Ahora nos autenticamos y obtenemos el token:

python3 get_gitlab_token.py root $GITLAB_PASSWORD http://gitlab.gitlab.$EXTERNAL_IP.nip.io

Paso 8. Organizamos los repositorios de Git en la jerarquía correcta con el Proveedor de GitLab.

cd ../internal/gitlab/hierarchy && terraform apply -input=false -auto-approve planfile

Desafortunadamente, en el proveedor de GitLab de terraform hay un problema flotante bug. Entonces, tendrás que eliminar los proyectos en conflicto manualmente para que el tf.state se repare. Luego, reinicia el comando `$ make all`

Paso 9. Transferimos los repositorios locales al servidor.

$ make push

[master (root-commit) b61d977]  Commit inicial
 3 archivos cambiados, 46 inserciones(+)
 modo de creación 100644 .gitignore
 modo de creación 100644 values.yml
Enumerando objetos: 5, hecho.
Contando objetos: 100% (5/5), hecho.
Compresión delta utilizando hasta 8 hilos
Comprimiendo objetos: 100% (5/5), hecho.
Escribiendo objetos: 100% (5/5), 770 bytes | 770.00 KiB/s, hecho.
Total 5 (delta 0), reutilizados 0 (delta 0)

Listo:

¿Qué fue primero, el pollo o el huevo? Desglosando IaC
¿Qué fue primero, el pollo o el huevo? Desglosando IaC
¿Qué fue primero, el pollo o el huevo? Desglosando IaC

Conclusión

Hemos logrado que desde nuestra máquina local podamos gestionar todo de manera declarativa. Ahora queremos trasladar todas estas tareas a CI y simplemente presionar botones. Para ello, necesitamos transferir nuestros estados locales (Terraform state) a CI. Sobre cómo hacerlo, hablaremos en la siguiente parte.

Suscríbete a nuestro blog, ¡para no perderte la publicación de nuevos artículos!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster