This shows you the differences between two versions of the page.
cc:laboratoare:05 [2021/11/14 21:39] alexandru.hogea [Testare] |
cc:laboratoare:05 [2022/11/11 23:12] (current) florin.mihalache |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 05. Kubernetes & Kubectl===== | + | ====== Laboratorul 05 - Terraform + AWS ====== |
- | ==== Introducere ==== | + | ===== Deployments & virtual machines în AWS ===== |
+ | În cadrul acestui laborator veți învăța cum să creați mașini virtuale în cadru AWS (Amazon Web Services), care reprezintă o platformă care furnizează servicii de tip cloud computing, baze de date, stocare de fișiere, device farms, etc. | ||
- | In acest laborator vom parcurge crearea de **pods** folosind comanda **kubectl**. Activitatea este echivalenta cu pornirea mai multor containere de Docker folosind **docker run**. | + | Pentru acest laborator presupunem că avem deja Terraform instalat de la laboratorul precedent. Dacă nu aveți Terraform instalat, puteți urma pașii din laboratorul anterior. |
- | ==== Aplicatia Propusa ==== | + | Pe langa Terraform, avem nevoie de AWS CLI instalat. Pentru a instala, urmati pasii de [[https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html | aici]]. |
- | Va propunem o mini aplicatie formata din 4 componente: | + | Pe lângă asta, avem nevoie de un cont pentru Amazon AWS. După ce ați făcut pașii pentru a avea propriul cont AWS, vrem sa conectăm AWS CLI la consola de AWS. |
- | - un API webserver | + | |
- | - un script care proceseaza informatii | + | |
- | - o baza de date | + | |
- | - un broker de rabbitMQ | + | |
- | {{:cc:laboratoare:schema_cod.png?800|}} | + | |
- | Aplicatia reprezinta un model simplificat de [[https://martinfowler.com/bliki/CQRS.html|CQRS]]. | + | Pentru a face asta, mergem în AWS și intrăm în secțiunea IAM: |
- | <note tip>CQRS - Command and Query Responsibility Segregation se refera la despartirea logica intre cereri si actiuni care modifica starea dintr-un sistem</note> | + | {{ :cc:laboratoare:lab-5-image3.png?700 |}} |
- | Fluxul este urmatorul: | + | Apoi mergem la "My security credentials": |
- | * Serverul **API** accepta doua __cereri HTTP__: un POST si un GET | + | {{ :cc:laboratoare:lab-5-image1.png?700 |}} |
- | * Cererile de tip POST sunt trimise catre **Procesator**. Procesatorul adauga un timestamp si apoi scrie intrarea in baza de date | + | |
- | * Cererile de tip GET sunt procesate direct de API care trage informatia din baza de date | + | |
- | Codul este accesibil pe [[https://gitlab.com/mobylab-cc/laborator-k8s|repo-ul nostru]]. | + | Următorul pas este să mergem la "Access Keys" și să apăsăm pe butonul de ''Create New Access Key'': |
+ | {{ :cc:laboratoare:lab-5-image5.png?700 |}} | ||
- | ==== Deployment folosind Kubernetes ==== | + | Păstrați fișierul generat. Rulați comanda ''aws configure'' și introduceți datele generate în fisierul creat anterior (''AWSAccessKeyId'' și ''AWSSecretKey''). |
- | In codul sursa aveti un exemplu de **docker-compose.yml**, pentru a avea o perspectiva familiara asupra cum va arata deploymentul local. In Kubernetes, fiecare serviciu va fi reprezentat de un Pod. | + | Pentru a crea o instanță simplă de EC2 (o mașina virtuală) folosim următorul fișier de Terraform: |
+ | <code terraform> | ||
+ | terraform { | ||
+ | required_providers { | ||
+ | aws = { | ||
+ | source = "hashicorp/aws" | ||
+ | version = "~> 4.16" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | required_version = ">= 1.2.0" | ||
+ | } | ||
+ | |||
+ | provider "aws" { | ||
+ | region = "us-west-2" # regiunea in care o sa se faca deploy la resurse | ||
+ | } | ||
+ | |||
+ | resource "aws_instance" "app_server" { | ||
+ | ami = "ami-830c94e3" # tipul de masina folosita (ami = Amazon Machine Image) | ||
+ | instance_type = "t2.micro" # tipul de instanta (resursele pe care le are masina) | ||
+ | |||
+ | tags = { | ||
+ | Name = "labAwsTerraform" | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
- | In continuare vom folosi comenzile de Kubectl, prezentate la laboratorul trecut, pentru a porni fiecare Pod independent. | + | Rulați comenzile cunoscute pentru a face deployment acestei infrastructuri: |
- | <note tip>La laboratorul urmator vom face deployment folosind configuratie declarativa yaml</note> | + | <code bash> |
+ | terraform init | ||
+ | terraform fmt | ||
+ | terraform plan | ||
+ | terraform apply | ||
+ | </code> | ||
- | ==== Testare ==== | + | Pentru a verifica ca mașina virtuală a fost deployed, căutăm serviciul EC2: |
+ | {{ :cc:laboratoare:lab-5-image7.png?700 |}} | ||
- | Pentru a testa aplicatia va recomandam sa folositi [[https://www.postman.com/|Postman]]. | + | Ajungem în pagina următoare în care vedem că nu avem nicio instanță up and running, deși planul nostru de Terraform a fost executat cu succes. De ce? |
+ | {{ :cc:laboratoare:lab-5-image4.png?700 |}} | ||
- | Testarea se realizeaza in felul urmator: | + | Observați diferența de regiuni (Frankfurt, în cazul din imaginea anterioară, față de us-west-2 în Terraform). Pentru a modifica regiunea în care facem deployment la resurse modificăm codul Terraform. În cazul de față, putem să facem click pe Frankfurt și să selectăm us-west-2 (Oregon). În acest moment putem vedea că avem o instanță up and running: |
- | - Adaugati o intrare, trimitand o cerere de tip POST pe linkul **http://localhost:PORT_ALES/api/v1/books** cu urmatorul body: | + | {{ :cc:laboratoare:lab-5-image2.png?700 |}} |
- | - | + | |
- | <code json> | + | <note tip> |
- | { | + | În codul Terraform modificați numele masinii din ''labAwsTerraform'' în ''testUpdate''. |
- | "name": "De ce iubim femeile?", | + | Rulatț comenzile obișnuite pentru a vedea planul. |
- | "author": "Mircea Cartarescu", | + | Observați că această modificare nu implică distrugerea mașinii existente, ci doar modificarea in-place. |
- | "price": 69, | + | </note> |
- | "description": "Autoironie" | + | |
+ | {{ :cc:laboratoare:lab-5-image6.png?700 |}} | ||
+ | |||
+ | ===== Cluster Kubernetes în AWS ===== | ||
+ | Pentru a face deployment unui cluster de Kubernetes în AWS avem 2 opțiuni. | ||
+ | - Facem deploy la mai multe mașini virtuale și instalăm și configurăm un cluster de mână. | ||
+ | - Folosim serviciul AWS special conceput pentru clusters de Kubernetes, AWS EKS Cluster | ||
+ | |||
+ | În cazul acestui laborator, o să facem deployment la un cluster folosind EKS. | ||
+ | |||
+ | Pentru a urma best practices recomandate de hashicorp, o să spargem codul în mai multe fișiere. | ||
+ | |||
+ | Fișierul ''variables.tf'' o sa conțină o simplă variabilă în care definim zona în care vrem să facem deployment: | ||
+ | <code terraform> | ||
+ | variable "region" { | ||
+ | description = "AWS region" | ||
+ | type = string | ||
+ | default = "us-west-2" | ||
} | } | ||
</code> | </code> | ||
- | - Vizualizati intrarea, trimitand o cerere de tip GET pe acelasi link. | + | |
- | <note tip>Observati ca intrarea va avea campul **insertedAt** adaugat</note> | + | Mai departe avem nevoie de un VPC, deci vom crea un nou fisier ''vpc.tf''. Mai multe despre ce este un VPC: [[https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html | aici]]. |
+ | <code> | ||
+ | module "vpc" { | ||
+ | source = "terraform-aws-modules/vpc/aws" | ||
+ | version = "3.14.2" | ||
+ | |||
+ | name = "cc-vpc" | ||
+ | |||
+ | cidr = "10.0.0.0/16" | ||
+ | azs = slice(data.aws_availability_zones.available.names, 0, 3) | ||
+ | |||
+ | private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] | ||
+ | public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"] | ||
+ | |||
+ | enable_nat_gateway = true | ||
+ | single_nat_gateway = true | ||
+ | enable_dns_hostnames = true | ||
+ | |||
+ | public_subnet_tags = { | ||
+ | "kubernetes.io/cluster/${local.cluster_name}" = "shared" | ||
+ | "kubernetes.io/role/elb" = 1 | ||
+ | } | ||
+ | |||
+ | private_subnet_tags = { | ||
+ | "kubernetes.io/cluster/${local.cluster_name}" = "shared" | ||
+ | "kubernetes.io/role/internal-elb" = 1 | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Mai multe detalii despre modulul de Terraform pentru VPC: [[https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc | aici]]. | ||
+ | |||
+ | În continuare avem nevoie de 2 security groups, pe care o să le folosească cluster-ul nostru. Așadar creăm un nou fișier cu numele ''securityGroups.tf'' cu următorul conținut: | ||
+ | <code> | ||
+ | resource "aws_security_group" "node_group_one" { | ||
+ | name_prefix = "node_group_one" | ||
+ | vpc_id = module.vpc.vpc_id | ||
+ | |||
+ | ingress { | ||
+ | from_port = 22 | ||
+ | to_port = 22 | ||
+ | protocol = "tcp" | ||
+ | |||
+ | cidr_blocks = [ | ||
+ | "10.0.0.0/8", | ||
+ | ] | ||
+ | } | ||
+ | } | ||
+ | |||
+ | resource "aws_security_group" "node_group_two" { | ||
+ | name_prefix = "node_group_two" | ||
+ | vpc_id = module.vpc.vpc_id | ||
+ | |||
+ | ingress { | ||
+ | from_port = 22 | ||
+ | to_port = 22 | ||
+ | protocol = "tcp" | ||
+ | |||
+ | cidr_blocks = [ | ||
+ | "192.168.0.0/16", | ||
+ | ] | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Ne putem gândi la un security group ca la un firewall. Mai multe detalii în [[https://aviatrix.com/learn-center/cloud-security/aws-security-groups/ | documentație]]. Documentația unui security group de AWS în Terraform: [[https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group | aici]]. | ||
+ | |||
+ | |||
+ | Până acum am definit doar componentele de care o să ne folosim în momentul în care configurăm cluster-ul nostru. Pentru a face asta, avem un nou fișier ''cluster.tf'' cu următoarea configurație: | ||
+ | <code> | ||
+ | module "eks" { | ||
+ | source = "terraform-aws-modules/eks/aws" | ||
+ | version = "18.26.6" | ||
+ | |||
+ | cluster_name = local.cluster_name | ||
+ | cluster_version = "1.22" | ||
+ | |||
+ | vpc_id = module.vpc.vpc_id | ||
+ | subnet_ids = module.vpc.private_subnets | ||
+ | |||
+ | eks_managed_node_group_defaults = { | ||
+ | ami_type = "AL2_x86_64" | ||
+ | |||
+ | attach_cluster_primary_security_group = true | ||
+ | |||
+ | # Disabling and using externally provided security groups | ||
+ | create_security_group = false | ||
+ | } | ||
+ | |||
+ | eks_managed_node_groups = { | ||
+ | one = { | ||
+ | name = "node-group-1" | ||
+ | |||
+ | instance_types = ["t3.small"] | ||
+ | |||
+ | min_size = 1 | ||
+ | max_size = 3 | ||
+ | desired_size = 2 | ||
+ | |||
+ | pre_bootstrap_user_data = <<-EOT | ||
+ | echo 'foo bar' | ||
+ | EOT | ||
+ | |||
+ | vpc_security_group_ids = [ | ||
+ | aws_security_group.node_group_one.id | ||
+ | ] | ||
+ | } | ||
+ | |||
+ | two = { | ||
+ | name = "node-group-2" | ||
+ | |||
+ | instance_types = ["t3.medium"] | ||
+ | |||
+ | min_size = 1 | ||
+ | max_size = 2 | ||
+ | desired_size = 1 | ||
+ | |||
+ | pre_bootstrap_user_data = <<-EOT | ||
+ | echo 'foo bar' | ||
+ | EOT | ||
+ | |||
+ | vpc_security_group_ids = [ | ||
+ | aws_security_group.node_group_two.id | ||
+ | ] | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Mai avem doar de creat un fișier ''main.tf'' cu următoarea configurație: | ||
+ | <code> | ||
+ | provider "kubernetes" { | ||
+ | host = module.eks.cluster_endpoint | ||
+ | cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) | ||
+ | } | ||
+ | |||
+ | provider "aws" { | ||
+ | region = var.region | ||
+ | } | ||
+ | |||
+ | data "aws_availability_zones" "available" {} | ||
+ | |||
+ | locals { | ||
+ | cluster_name = "cc-eks-${random_string.suffix.result}" | ||
+ | } | ||
+ | |||
+ | resource "random_string" "suffix" { | ||
+ | length = 8 | ||
+ | special = false | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Pentru a afișa câteva informații legate de infrastructura noastră avem nevoie de câteva outputs. Așa cum am învățat data trecută, facem un fișier separat în care definim acest outputs: | ||
+ | <code> | ||
+ | output "cluster_id" { | ||
+ | description = "EKS cluster ID" | ||
+ | value = module.eks.cluster_id | ||
+ | } | ||
+ | |||
+ | output "cluster_endpoint" { | ||
+ | description = "Endpoint for EKS control plane" | ||
+ | value = module.eks.cluster_endpoint | ||
+ | } | ||
+ | |||
+ | output "cluster_security_group_id" { | ||
+ | description = "Security group ids attached to the cluster control plane" | ||
+ | value = module.eks.cluster_security_group_id | ||
+ | } | ||
+ | |||
+ | output "region" { | ||
+ | description = "AWS region" | ||
+ | value = var.region | ||
+ | } | ||
+ | |||
+ | output "cluster_name" { | ||
+ | description = "Kubernetes Cluster Name" | ||
+ | value = local.cluster_name | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Rulăm comenzile obișnuite pentru a crea infrastructura. Observați ca sunt foarte multe resurse care urmează să se construiască. | ||
+ | |||
+ | In cazul in care ați realizat ca lipsește ceva (nu am definit niciunde ce versiuni ale provider-ilor vrem să folosim), best practices recomandă să avem un fișier numit ''terraform.tf'' care să conțină această configurație: | ||
+ | <code> | ||
+ | terraform { | ||
+ | required_providers { | ||
+ | aws = { | ||
+ | source = "hashicorp/aws" | ||
+ | version = "~> 4.15.0" | ||
+ | } | ||
+ | |||
+ | random = { | ||
+ | source = "hashicorp/random" | ||
+ | version = "~> 3.1.0" | ||
+ | } | ||
+ | |||
+ | tls = { | ||
+ | source = "hashicorp/tls" | ||
+ | version = "~> 3.4.0" | ||
+ | } | ||
+ | |||
+ | cloudinit = { | ||
+ | source = "hashicorp/cloudinit" | ||
+ | version = "~> 2.2.0" | ||
+ | } | ||
+ | |||
+ | kubernetes = { | ||
+ | source = "hashicorp/kubernetes" | ||
+ | version = "~> 2.12.1" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Asteptați ca planul să se termine de rulat sș verificați infrastructura creată în AWS (o să dureze în jur de 15-20 minute). Apoi, pentru a ne conecta la cluster-ul nostru proaspăt creat de Kubernetes, folosim următoarea comandă: | ||
+ | <code bash> | ||
+ | aws eks --region $(terraform output -raw region) update-kubeconfig \ | ||
+ | --name $(terraform output -raw cluster_name) | ||
+ | </code> | ||
+ | |||
+ | Verificați conexiunea cu următoarea comandă: ''kubectl cluster-info'' | ||
+ | |||
+ | În acest moment avem un cluster configurat în AWS EKS și suntem conectați la el. Orice comandă de kubectl rulată o să fie executată pe cluster-ul nostru. | ||
+ |