This shows you the differences between two versions of the page.
cc:temp [2022/10/30 12:18] florin.mihalache [Injectare variabile din linia de comandă] |
cc:temp [2022/11/11 23:12] (current) florin.mihalache [Deployments & virtual machines în AWS] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laboratorul 4 - Terraform ====== | + | ====== Laboratorul 05 - Terraform + AWS ====== |
- | ===== Ce este Terraform? ===== | + | |
- | ===== Instalare Terraform ===== | + | |
- | Folosim site-ul oficial al Terraform pentru a instala tool-ul. Găsiți toate detaliile necesare aici: https://www.terraform.io/downloads | + | |
- | Pentru a instala Terraform pe o mașinp cu un sistem de operare Ubuntu / Debian, folosim următoarele comenzi: | + | ===== Deployments & virtual machines în AWS ===== |
- | <code bash> | + | Î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. |
- | wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg | + | |
- | echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list | + | |
- | sudo apt update && sudo apt install terraform | + | |
- | </code> | + | |
- | Pentru a verifica dacă instalarea fost efectuată cu succes, încercăm să obținem versiunea de Terraform instalată: ''terraform -version'' | + | 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. |
- | === Terraform autocomplete === | + | 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]]. |
- | Pentru a putea utiliza funcția de autocomplete cu Terraform: ''terraform -install-autocomplete'' | + | |
- | ===== Terraform + Docker ===== | + | 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. |
- | În laboratorul curent o să folosim Terraform împreună cu Docker, cu care suntem deja familiari. Începem prin a crea un director nou pentru configuratia noastră: | + | |
- | <code bash> | + | |
- | mkdir lab4cc | + | |
- | cd lab4cc | + | |
- | </code> | + | |
- | Creăm un fișier cu numele ''main.tf'' și introducem următorul cod: | + | Pentru a face asta, mergem în AWS și intrăm în secțiunea IAM: |
+ | {{ :cc:laboratoare:lab-5-image3.png?700 |}} | ||
+ | |||
+ | Apoi mergem la "My security credentials": | ||
+ | {{ :cc:laboratoare:lab-5-image1.png?700 |}} | ||
+ | |||
+ | 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 |}} | ||
+ | |||
+ | Păstrați fișierul generat. Rulați comanda ''aws configure'' și introduceți datele generate în fisierul creat anterior (''AWSAccessKeyId'' și ''AWSSecretKey''). | ||
+ | |||
+ | Pentru a crea o instanță simplă de EC2 (o mașina virtuală) folosim următorul fișier de Terraform: | ||
<code terraform> | <code terraform> | ||
terraform { | terraform { | ||
required_providers { | required_providers { | ||
- | docker = { | + | aws = { |
- | source = "kreuzwerker/docker" | + | source = "hashicorp/aws" |
- | version = "~> 2.13.0" | + | version = "~> 4.16" |
} | } | ||
} | } | ||
+ | |||
+ | required_version = ">= 1.2.0" | ||
} | } | ||
- | provider "docker" {} | + | provider "aws" { |
- | + | region = "us-west-2" # regiunea in care o sa se faca deploy la resurse | |
- | resource "docker_image" "nginx" { | + | |
- | name = "nginx:latest" | + | |
- | keep_locally = false | + | |
} | } | ||
- | resource "docker_container" "nginx" { | + | resource "aws_instance" "app_server" { |
- | image = docker_image.nginx.latest | + | ami = "ami-830c94e3" # tipul de masina folosita (ami = Amazon Machine Image) |
- | name = "tutorial" | + | instance_type = "t2.micro" # tipul de instanta (resursele pe care le are masina) |
- | ports { | + | |
- | internal = 80 | + | tags = { |
- | external = 8000 | + | Name = "labAwsTerraform" |
} | } | ||
} | } | ||
</code> | </code> | ||
+ | Rulați comenzile cunoscute pentru a face deployment acestei infrastructuri: | ||
+ | <code bash> | ||
+ | terraform init | ||
+ | terraform fmt | ||
+ | terraform plan | ||
+ | terraform apply | ||
+ | </code> | ||
- | Să analizăm pe rând fiecare bloc de cod introdus: | + | Pentru a verifica ca mașina virtuală a fost deployed, căutăm serviciul EC2: |
- | * ''terraform {}'' - conține setarile de Terraform, inclusiv provider-ul pe care urmează să îl folosim. Pentru fiecare provider, câmpul ''source'' definește numele provider-ului. By default, Terraform folosește Terraform Registry pentru a instala un provider. Astfel, în exemplul nostru, ''kreuzwerker/docker'' este un provider care se găsește în registrul Terraform la path-ul ''registry.terraform.io/kreuzwerker/docker''. Câmpul ''version'' este opțional, dacă nu îl folosim, Terraform o să descarce by default ultima versiune disponibilă. | + | {{ :cc:laboratoare:lab-5-image7.png?700 |}} |
- | * ''provider {}'' - un block de tipul provider conține configurările necesare pentru ca acel provider să poată fi folosit. În cazul nostru, am folosit ''docker''. Un provider este doar un plugin pe care Terraform îl folosește pentru a crea și pentru a gestiona resursele. | + | |
- | * Blocurile de tip resource - un astfel de bloc, așa cum sugerează și numele, este folosit pentru a defini resurse ale infrastructurii noastre. Așa cum putem observa, un bloc de tip resursă are 2 labels: ''resource "docker_image" "nginx"''. În acest exemplu, ''docker_image'' este tipul de resursă, iar ''nginx'' este numele resursei. Fiecare astfel de bloc are mai multe proprietăți, acestea diferă de la resursă la resursă. Exemplu: în laboratorul viitor o sa configurăm o mașină virtuală într-un provider de cloud unde o să folosim parametrii ca tipul de masină, hard disk, dimensiune hard disk, regiunea în care să fie deployed masina, etc. | + | |
- | Recapitulare cod: | + | 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? |
- | - Am populat block-ul ''terraform'' cu config-ul unde am specificat providerul ''docker'' și versiunea dorită. | + | {{ :cc:laboratoare:lab-5-image4.png?700 |}} |
- | - Am inițializat providerul ''docker''. | + | |
- | - Am creat o resursă de tipul ''docker_image'' cu numele ''nginx'' (numele variabilei). | + | |
- | - Am creat o resursă de tipul ''docker_container'' cu numele ''nginx''. În această resursă, pentru imagine am folosit imaginea definită mai sus, iar numele container-ului este dat de câmpul ''name''. | + | |
- | Pentru a pune in picioare infrastructura: | + | 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: |
- | - Initializare: ''terraform init''. Urmăriți output-ul comenzii pentru a înțelege ce face această comandă. | + | {{ :cc:laboratoare:lab-5-image2.png?700 |}} |
- | - Formatarea codului. Acest pas este opțional, dar foarte util: ''terraform fmt'' | + | |
- | - Validarea configurării (și acest pas este optional, în cazul în care mergem mai departe cu o configurare invalidă, o să primim eroare): ''terraform validate'' | + | |
- | - Rulăm comanda ''terraform plan''. Această comandă este un dry-run, deci putem observa cum o să arate infrastructura noastră după ce o să aplicăm configurarea creată anterior. **Atenție**, această comandă nu modifică infrastructura. | + | |
- | - Aplicăm configurația: ''terraform apply'' | + | |
<note tip> | <note tip> | ||
- | Task: urmati instrucțiunile din shell-ul interactiv și faceți deploy la infrastructură. Observați output-ul comenzii. | + | În codul Terraform modificați numele masinii din ''labAwsTerraform'' în ''testUpdate''. |
- | + | Rulatț comenzile obișnuite pentru a vedea planul. | |
- | Pentru a observa state-ul: ''terraform show'' | + | Observați că această modificare nu implică distrugerea mașinii existente, ci doar modificarea in-place. |
</note> | </note> | ||
- | <note tip> | + | {{ :cc:laboratoare:lab-5-image6.png?700 |}} |
- | Task: modificarea unei infrastructuri existente - modificati fișierul anterior, astfel încât container-ul să folosească portul 8080, nu 8000 | + | |
- | Rulați comanda pentru a vedea planul. Modificarea este facută in-place? | + | ===== 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 | ||
- | Ștergere infrastructură - rulați comanda ''terraform destroy'' | + | În cazul acestui laborator, o să facem deployment la un cluster folosind EKS. |
- | </note> | + | |
+ | Pentru a urma best practices recomandate de hashicorp, o să spargem codul în mai multe fișiere. | ||
- | ===== Variabile în Terraform ===== | + | Fișierul ''variables.tf'' o sa conțină o simplă variabilă în care definim zona în care vrem să facem deployment: |
- | Avem următorul fișier'' outputs.tf'', unde avem acest bloc: | + | |
<code terraform> | <code terraform> | ||
- | output "Lab" { | + | variable "region" { |
- | value="lab4 first output" | + | description = "AWS region" |
+ | type = string | ||
+ | default = "us-west-2" | ||
} | } | ||
</code> | </code> | ||
- | Am creat primul output în Terraform. Pentru a observa comportamentul, urmați pașii din exercițiul anterior pentru a "aplica" infrastructura. | + | 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> | ||
- | Avem acest bloc cu următorul cod: | + | Mai multe detalii despre modulul de Terraform pentru VPC: [[https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc | aici]]. |
- | <code terraform> | + | |
- | variable "LabCCTerraform" { | + | Î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: |
- | description = "primul lab in terraform" | + | <code> |
- | default = "valoare default a primului lab in terraform" | + | 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> | </code> | ||
- | Aplicați planul și observați comportamentul. | ||
- | ==== Prioritatea variabilelor ==== | + | 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]]. |
- | Un alt mod de a seta variabile în Terraform este prin variabile de mediu. By default, Terraform caută variabilele de mediu care încep cu ''F_VAR_''. De exemplu, o variabilă de mediu cu numele ''TF_VAR_MYTESTVAR=myvalue'' o să atribuie valoarea myvalue unei variabile cu numele ''MYTESTVAR''. Variabila cu numele ''MYTESTVAR'' trebuie sa fie totuși declarată in configurația noastră de Terraform. | + | |
- | <note tip> | ||
- | Task: | ||
- | * Creați o variabilă de mediu care sa suprascrie variabilă folosită de noi: ''export TF_VAR_LabCCTerraform="Value from environment"'' | ||
- | * Rulați din nou comanda ''terraform apply'' pentru a observa comportamentul. | ||
- | </note> | ||
- | ==== Fișiere tfvars ==== | + | 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: |
- | <note tip> | + | <code> |
- | Task: | + | module "eks" { |
- | * Creați un nou fișier cu numele terraform.tfvars si adăugați următoarea linie de cod: ''LabCCTerraform="Value from tfvars file"''. Acest fișier, deși are extensia diferită față de cele anterioare foloseste exact aceeași sintaxă. | + | source = "terraform-aws-modules/eks/aws" |
- | * Folosiți din nou comanda terraform apply pentru a observa comportamentul. | + | version = "18.26.6" |
- | </note> | + | |
+ | 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> | ||
- | ==== Injectare variabile din linia de comandă ==== | + | Mai avem doar de creat un fișier ''main.tf'' cu următoarea configurație: |
- | Un alt mod prin care putem atribui o valoare unei variabile este prin linia de comandă. | + | <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> | ||
- | Rulați următoarea comandă: ''terraform apply -var LabCCTerraform=valueFromCommandLine'' | + | Verificați conexiunea cu următoarea comandă: ''kubectl cluster-info'' |
- | In urma acestui exercițiu, am aflat ca ordinea de prioritizare a variabilelor este următoarea: | + | Î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. |
- | - linie de comandă | + | |
- | - fișier tfvars | + | |
- | - varibila de mediu | + | |
- | - valoarea default | + | |
- | ==== Tipuri de variabile ==== | ||
- | ==== Organizarea codului Terraform ==== |