Henne oder Ei: Wir knacken IaC

Henne oder Ei: Wir knacken IaC
Was war zuerst da — das Huhn oder das Ei? Ein ziemlich seltsamer Auftakt für einen Artikel über Infrastructure-as-Code, oder?

Was ist ein Ei?

Meistens ist Infrastructure-as-Code (IaC) eine deklarative Methode zur Darstellung von Infrastruktur. Hier beschreiben wir den Zustand, den wir erreichen möchten, beginnend bei der Hardware bis hin zur Softwarekonfiguration. Daher wird IaC verwendet für:

  1. Ressourcenzuteilung. Das sind VMs, S3, VPC usw. Die Hauptwerkzeuge sind: Terraform und CloudFormation.
  2. Softwarekonfiguration. Die Hauptwerkzeuge: Ansible, Chef usw.

Jeder Code befindet sich in Git-Repositorys. Irgendwann wird der Teamleiter entscheiden, dass es Zeit ist, Ordnung in diese zu bringen. Er wird refaktorisieren. Und er wird eine bestimmte Struktur erstellen. Und er wird feststellen, dass das gut ist.

Es ist auch gut, dass es bereits einen GitLab und GitHub-Provider für Terraform (und das ist Softwarekonfiguration) gibt. Mit deren Hilfe kann man das gesamte Projekt verwalten: Teammitglieder, CI/CD, Git-Flow usw.

Woher stammt das Ei?

Hier nähern wir uns allmählich der zentralen Frage.

Zuerst sollte man mit dem Repository beginnen, das die Struktur anderer Repositories, einschließlich sich selbst, beschreibt. Und natürlich muss man im Rahmen von GitOps CI hinzufügen, damit Änderungen automatisch ausgeführt werden.

Wenn Git noch nicht erstellt ist?

  1. Wie speichert man es in Git?
  2. Wie fügt man CI hinzu?
  3. Wenn wir GitLab auch mit IaC bereitstellen und zusätzlich in Kubernetes?
  4. Und GitLab Runner ebenfalls in Kubernetes?
  5. Und Kubernetes bei einem Cloud-Anbieter?

Was war zuerst: GitLab, in das ich meinen Code hochlade, oder der Code, der beschreibt, welcher GitLab für mich benötigt wird?

Das Huhn mit Eiern

«Oyakodon3 mit einem Dinosaurier» [src]

Lass uns versuchen, ein Gericht zuzubereiten, indem wir als Cloud-Anbieter Managed Kubernetes Selectel.

TL;DR

Kann man das nicht gleich in ein Team zusammenfassen?

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

Zutaten:

  • Ein Konto von my.selectel.ru;
  • Token vom Konto;
  • Kubernetes-Kenntnisse;
  • Helm-Kenntnisse;
  • Terraform-Kenntnisse;
  • Helm-Chart GitLab;
  • Helm-Chart GitLab Runner.

Rezept:

  1. Erhalte MY_SELECTEL_TOKEN über das Dashboard my.selectel.ru.
  2. Erstelle einen Kubernetes-Cluster, indem du das Token von deinem Konto übergibst.
  3. Erhalte KUBECONFIG vom erstellten Cluster.
  4. Installiere GitLab in Kubernetes.
  5. Erhalte ein GitLab-Token von dem erstellten GitLab für den Benutzer. root.
  6. Erstelle eine Projektstruktur in GitLab mit dem GitLab-Token.
  7. Pushe den vorhandenen Code in GitLab.
  8. ???
  9. Gewinn!

Schritt 1. Das Token kann im Abschnitt API-Schlüssel.

Henne oder Ei: Wir knacken IaCSchritt 2. Bereiten wir unser Terraform für das "Backen" eines Clusters mit 2 Knoten vor. Wenn du dir sicher bist, dass du genügend Ressourcen hast, kannst du Auto-Quotas aktivieren:

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",
 }
}

Benutzer zum Projekt hinzufügen:

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
}

Ausgaben:

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
}

Wir starten:

$ 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

Henne oder Ei: Wir knacken IaC
Schritt 3. Wir erhalten die kubectl-Konfiguration.

Um KUBECONFIG programmgesteuert herunterzuladen, muss ein Token von OpenStack erhalten werden:

openstack token issue -c id -f value > token

Und mit diesem Token eine Anfrage an die Managed Kubernetes Selectel API machen. k8s_id gibt aus terraform:

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

Die kubectl-Konfiguration kann auch über das Dashboard abgerufen werden.

Henne oder Ei: Wir knacken IaC
Schritt 4. Nachdem der Cluster eingerichtet wurde und wir Zugang dazu haben, können wir dazu passende YAML hinzufügen.

Ich bevorzuge es, hinzuzufügen:

  • Namespace,
  • Speicherklasse,
  • Pod-Sicherheitsrichtlinie und weiteres.

Speicherklasse für Selectel kann aus dem offiziellen Repository.

Entnommen werden, da ich ursprünglich einen Cluster in der Zone ru-3a, gewählt habe, benötige ich auch die Speicherklasse aus dieser Zone.

Kind: StorageClass
apiVersion: storage.k8s.io/v1
Metadata:
 Name: fast.ru-3a
 Annotations:
   storageclass.kubernetes.io/is-default-class: "true"
Provisioner: cinder.csi.openstack.org
Parameters:
 Typ: fast.ru-3a
 Verfügbarkeit: ru-3a
AllowVolumeExpansion: true

Schritt 5. Wir stellen den Lastenausgleich ein.

Wir werden den Standard für viele verwenden nginx-ingress. Es gibt bereits genügend Anleitungen zu seiner Installation, also werden wir an dieser Stelle nicht verweilen.

$ 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

Wir warten, bis er in etwa 3-4 Minuten eine externe IP erhält:

Henne oder Ei: Wir knacken IaC
Externe IP erhalten:

Henne oder Ei: Wir knacken IaC
Schritt 6. Wir installieren 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"

Warten wir erneut, bis alle Pods hochgefahren sind.

kubectl get po -n gitlab
NAME                                      	READY   STATUS  	RESTARTS   ALTER
 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
Warten auf gitlab
./wait_gitlab.sh ../internal/gitlab/gitlab/.pods
Warten auf Pod...
Warten auf Pod...
Warten auf Pod...

Pods gestartet:

Henne oder Ei: Wir knacken IaC
Schritt 7. Wir erhalten das GitLab-Token.

Zuerst erfahren wir das Passwort für den Login:

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

Jetzt authentifizieren wir uns und erhalten das Token:

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

Schritt 8. Wir bringen die Git-Repositories in die richtige Hierarchie mit dem Gitlab Provider.

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

Leider gibt es im Terraform GitLab Provider ein schwankendes Fehler. Dann müssen wir die konfliktbehafteten Projekte manuell löschen, damit der tf.state repariert wird. Starten Sie dann den Befehl `$ make all` erneut.

Schritt 9. Wir übertragen die lokalen Repositories auf den Server.

$ make push

[master (root-commit) b61d977]  Erster Commit
 3 Dateien geändert, 46 Einfügungen(+)
 Erstelle Modus 100644 .gitignore
 Erstelle Modus 100644 values.yml
Zähle Objekte: 5, erledigt.
Zähle Objekte: 100% (5/5), erledigt.
Delta-Kompression mit bis zu 8 Threads
Komprimieren von Objekten: 100% (5/5), erledigt.
Schreibe Objekte: 100% (5/5), 770 Bytes | 770.00 KiB/s, erledigt.
Insgesamt 5 (Delta 0), wiederverwendet 0 (Delta 0)

Fertig:

Henne oder Ei: Wir knacken IaC
Henne oder Ei: Wir knacken IaC
Henne oder Ei: Wir knacken IaC

Fazit

Wir haben es geschafft, dass wir von unserem lokalen Rechner aus alles deklarativ steuern können. Jetzt möchten wir all diese Aufgaben in die CI übertragen und nur noch auf Knöpfe drücken. Dafür müssen wir unsere lokalen Zustände (Terraform-Zustand) in die CI übergeben. Wie das funktioniert, erläutern wir im nächsten Abschnitt.

Abonnieren Sie unseren Blogs, um die Veröffentlichungen neuer Artikel nicht zu verpassen!

Quelle: habr.com

60GB SSD 8Gb DDR4