This shows you the differences between two versions of the page.
cc:temp [2022/11/11 22:40] florin.mihalache [Setup] |
cc:temp [2022/11/11 23:12] (current) florin.mihalache [Deployments & virtual machines în AWS] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laboratorul 05 - Terraform + AWS ====== | ====== Laboratorul 05 - Terraform + AWS ====== | ||
- | ===== AWS ===== | + | ===== Deployments & virtual machines în AWS ===== |
- | În cadrul acestui laborator veți învăța | + | Î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. |
- | ===== Setup ===== | ||
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. | 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. | ||
Line 19: | Line 18: | ||
Următorul pas este să mergem la "Access Keys" și să apăsăm pe butonul de ''Create New Access Key'': | 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 |}} | {{ :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> | ||
+ | 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> | ||
+ | |||
+ | Rulați comenzile cunoscute pentru a face deployment acestei infrastructuri: | ||
+ | <code bash> | ||
+ | terraform init | ||
+ | terraform fmt | ||
+ | terraform plan | ||
+ | terraform apply | ||
+ | </code> | ||
+ | |||
+ | Pentru a verifica ca mașina virtuală a fost deployed, căutăm serviciul EC2: | ||
+ | {{ :cc:laboratoare:lab-5-image7.png?700 |}} | ||
+ | |||
+ | 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 |}} | ||
+ | |||
+ | 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: | ||
+ | {{ :cc:laboratoare:lab-5-image2.png?700 |}} | ||
+ | |||
+ | <note tip> | ||
+ | În codul Terraform modificați numele masinii din ''labAwsTerraform'' în ''testUpdate''. | ||
+ | Rulatț comenzile obișnuite pentru a vedea planul. | ||
+ | Observați că această modificare nu implică distrugerea mașinii existente, ci doar modificarea in-place. | ||
+ | </note> | ||
+ | |||
+ | {{ :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> | ||
+ | |||
+ | 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. | ||