Kura czy jajko: łamiemy IaC

Kura czy jajko: łamiemy IaC
Co było pierwsze — kura czy jajko? To dość dziwne wprowadzenie do artykułu o Infrastructure-as-Code, prawda?

Co to jest jajko?

Najczęściej Infrastructure-as-Code (IaC) to deklaratywny sposób przedstawiania infrastruktury. Opisujemy w nim stan, który chcemy osiągnąć, poczynając od części sprzętowej, kończąc na konfiguracji oprogramowania. Dlatego IaC używa się do:

  1. Provisioning zasobów. To VMs, S3, VPC itd. Główne narzędzia do pracy to: Terraform i CloudFormation.
  2. Konfiguracja oprogramowania. Główne narzędzia: Ansible, Chef itd.

Każdy kod znajduje się w repozytoriach git. I prędzej czy później lider zespołu zdecyduje, że warto w nich zrobić porządek. I będzie refaktoryzować. Stworzy pewną strukturę. I zauważy, że to jest dobre.

To także dobrze, że już istnieje GitLab i GitHub- dostawca dla Terraform (a to jest Konfiguracja oprogramowania). Dzięki nim można zarządzać całym projektem: członkami zespołu, CI/CD, git-flow itd.

Skąd wzięło się jajko?

I tak stopniowo zbliżamy się do głównego pytania.

Przede wszystkim musisz zacząć od repozytorium, które opisuje strukturę innych repozytoriów, w tym siebie. I oczywiście w ramach GitOps należy dodać CI, aby automatycznie wprowadzać zmiany.

Co jeśli Git jeszcze nie został utworzony?

  1. Jak go przechować w Git?
  2. Jak dodać CI?
  3. Co jeśli GitLab też uruchamiamy za pomocą IaC, a do tego w Kubernetes?
  4. A GitLab Runner też w Kubernetes?
  5. A Kubernetes u dostawcy chmurowego?

Co było pierwsze: GitLab, na który wgram mój kod, czy kod opisujący, jaki GitLab potrzebuję?

Kura z jajkami

«Oyakodon3 z dinozaurem» [src]

Spróbujemy przygotować danie, używając jako dostawcy chmurowego Managed Kubernetes Selectel.

TL;DR

A czy można, żeby od razu w jedną komendę?

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

Składniki:

  • Konto z my.selectel.ru;
  • Token z konta;
  • Umiejętności Kubernetes;
  • Umiejętności Helm;
  • Umiejętności Terraform;
  • Helm chart GitLab;
  • Helm chart GitLab Runner.

Przepis:

  1. Uzyskaj MY_SELECTEL_TOKEN z panelu my.selectel.ru.
  2. Utwórz klaster Kubernetes, przekazując do niego token z konta.
  3. Uzyskaj KUBECONFIG z utworzonego klastra.
  4. Zainstaluj GitLab w Kubernetes.
  5. Uzyskaj GitLab-token z utworzonego GitLab dla użytkownika root.
  6. Utwórz strukturę projektów w GitLab, używając GitLab-token.
  7. Wgraj istniejący kod do GitLab.
  8. ???
  9. Zysk!

Krok 1. Token można uzyskać w sekcji Klucze API.

Kura czy jajko: łamiemy IaCKrok 2. Przygotowujemy nasz Terraform do 'wypiekania' klastra z 2 węzłami. Jeśli masz pewność, że masz wystarczająco dużo zasobów, to można włączyć automatyczne kwoty:

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

Dodajemy użytkownika do projektu:

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
}

Dane wyjściowe:

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
}

Uruchamiamy:

$ 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

Kura czy jajko: łamiemy IaC
Krok 3. Otrzymujemy kubekonfigurację.

Aby pobrać KUBECONFIG programowo, należy uzyskać token z OpenStack:

openstack token issue -c id -f value > token

A z tym tokenem wykonać zapytanie do Managed Kubernetes Selectel API. k8s_id wydaje terraform:

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

Kubeconfig można również uzyskać przez panel.

Kura czy jajko: łamiemy IaC
Krok 4. Po tym jak klaster został skonfigurowany i mamy do niego dostęp, można dodać dodatkowy yaml według uznania.

Wolę dodać:

  • namespace,
  • klasę pamięci,
  • politykę zabezpieczeń podów i inne.

Klasa pamięci dla Selectel można wziąć z oficjalnego repozytorium.

Ponieważ początkowo wybrałem klaster w strefie ru-3a, więc klasa pamięci również musi być z tej strefy.

typ: 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:
 type: fast.ru-3a
 availability: ru-3a
allowVolumeExpansion: true

Krok 5. Ustawiamy load balancer.

Będziemy korzystać z standardowego dla wielu nginx-ingress. Istnieje już wiele instrukcji dotyczących jego instalacji, więc nie będziemy się na tym zatrzymywać.

$ 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

Czekamy, aż uzyska zewnętrzny IP w przybliżeniu 3-4 minuty:

Kura czy jajko: łamiemy IaC
Uzyskaliśmy zewnętrzny IP:

Kura czy jajko: łamiemy IaC
Krok 6. Instalujemy 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"

Znów czekamy, aż wszystkie pody się uruchomią.

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
Czekamy na gitlab
./wait_gitlab.sh ../internal/gitlab/gitlab/.pods
czekając na pod...
czekając na pod...
czekając na pod...

Pody zostały uruchomione:

Kura czy jajko: łamiemy IaC
Krok 7. Uzyskujemy GitLab-token.

Najpierw dowiadujemy się hasła do logowania:

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

Teraz się logujemy i uzyskujemy token:

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

Krok 8. Organizujemy Git-repozytoria w odpowiedniej hierarchii z pomocą GitLab Provider.

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

Niestety, w terraform GitLab provider występuje pływający błąd. W takim razie trzeba usunąć konfliktujące projekty ręcznie, aby naprawić tf.state. Następnie uruchom polecenie `$ make all`

Krok 9. Przenosimy lokalne repozytoria na serwer.

$ make push

[master (root-commit) b61d977]  Initial commit
 3 files changed, 46 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 values.yml
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 770 bytes | 770.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)

Gotowe:

Kura czy jajko: łamiemy IaC
Kura czy jajko: łamiemy IaC
Kura czy jajko: łamiemy IaC

Podsumowanie

Dzięki naszym osiągnięciom z naszej lokalnej maszyny możemy deklaratywnie zarządzać wszystkim. Teraz chcielibyśmy przenieść wszystkie te zadania do CI i tylko naciskać przyciski. W tym celu musimy przekazać nasze lokalne stany (Terraform state) do CI. O tym, jak to zrobić, w następnej części.

Subskrybuj nasz blog, aby nie przegapić nowych artykułów!

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster