Acebit expanderar
Acebit expanderar – automationsess från Dalarna öppnar i Stockholm
Infrastructure as code med AWS & Terraform
Vi ska prova sätta upp en enkel setup med terraform för att få lite kläm på grundkoncepten. Terraform är ett verktyg för att arbeta med infrastruktur som kod, terraform kommer med funktionalitet för att kunna skapa, ändra och versionshantera infrastruktur såsom nätverk eller hela cloudmiljöer.
Nu ska vi först sätta upp terraform, sedan ska vi bygga en miljö i Amazon AWS med VPC’er, security groups, subnets och EC2 instanser.
Först installerar vi så klart terraform på maskinen vi ska använda, jag kör mac så det görs enkelt med homebrew:
johanlahti@MBP ~ % brew tap hashicorp/tap
johanlahti@MBP ~ % brew install hashicorp/tap/terraform
==> Installing terraform from hashicorp/tap
terraform 1.0.0 is installed but outdated
==> Upgrading hashicorp/tap/terraform
1.0.0 -> 1.0.5
==> Downloading https://releases.hashicorp.com/terraform/1.0.5/terraform_1.0.5_darwin_amd64.zip
######################################################################## 100.0%
🍺 /usr/local/Cellar/terraform/1.0.5: 3 files, 74.9MB, built in 4 seconds
Terraform använder sig av state som lagras i en fil för att kunna hålla reda på vilka resurser den skapat och om en managerad resurs driftat från vad som är deklarerat. Nu tänker du “vadå state? Hur ska jag kunna använda det här i en pipeline? Hur ska vi kunna köra terraform som ett team?” Jo, det kommer vi till nu. Terraform har nämligen stöd för något de kallar remote state. Vi kan alltså lagra state externt, och i det här fallet kommer vi lagra state i en S3 bucket. Vi kommer gå igenom allt det här, bara lugn.
Vi sätter upp en helt vanlig S3 bucket, vi slarvar inte med rättigheterna här. Återigen bör man vara restriktiv med vilka konton som har access till bucketen, och den behöver inte ha public access utan du kan bocka i “block all public access”
Providern som vi använder förutsätter att vi har aws cli installerat och konfigurerat med rätt behörigheter. Det skulle bli ett ganska sulligt inlägg om vi skulle gå igenom installation av alla komponenter, så jag kommer också förutsätta att du redan använder aws cli, annars får du helt enkelt pinga mig så skriver vi ett inlägg om det också!
Vi förutsätter också att du satt upp ett konto och har skapat credentials. Självklart skapar vi ju ett separat konto som bara används av terraform här så att vi enkelt kan låsa kontot i IAM om nyckeln skulle komma på villovägar.
Sedan kör vi en vanlig aws configure och säkerställer att det funkar som det ska.
johanlahti@MBP awsdemo % aws configure
AWS Access Key ID [None]: AKIAXGZJVRWX3VQPUCU4
AWS Secret Access Key [None]: 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
Default region name [None]: eu-north-1
Default output format [None]:
Du behöver inte testa om du kan använda kontot, när du läser det här inlägget har jag raderat kontot för längesen. Men bra tänkt!
Vi kan också verifiera att aws cli fungerar:
johanlahti@MBP awsdemo % aws s3 ls
2021-08-26 16:12:12 acebit-tfdemo
2021-07-07 16:00:50 blog.acebit.se
Därefter skapar vi först en katalog där vi kommer ha alla filer för att hantera den här infrastrukturen. I katalogen skapar vi tre filer - main.tf, infra.tf och variables.tf
johanlahti@MBP dev % mkdir awsdemo && for file in main.tf infra.tf variables.tf; do touch awsdemo/$file; done
johanlahti@MBP dev % cd awsdemo
johanlahti@MBP awsdemo % ls
main.tf infra.tf variables.tf
Hur man lägger upp strukturen är helt upp till dig. Det är en god idé att tänka till innan man sätter igång och provisionerar sin produktionsmiljö, men vi kan labba lite med den här enkla uppsättningen där vi har grundinställningar i main, variabler i variables och själva infrastrukturdeklarationen i infra.tf
Nu skapar vi lite innehåll i dessa filer.
I main lägger jag information om vilken provider som ska användas och att vi vill använda en s3 bucket till remotestate.
# main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.27"
}
}
}
provider "aws" {
profile = "default"
region = "eu-north-1"
}
terraform {
backend "s3" {
key = "medium-terraform/stage/terraform.tfstate"
# ...
}
}
I variables lägger jag in lite variabler som vi kanske skulle kunna vilja ändra framöver. Dels skapar jag variabler för uppenbara saker såsom CIRD range för VPC och vilken instance type jag vill använda för mina kommande EC2 instanser.
Vi skapar också en variabel som heter instance_count, den kommer vi använda för att tala om hur många instanser vi kommer skapa av subnets och ec2.
Jag skapar också en data source, i det här fallet en sådan för att hitta en AMI som senare kommer användas för de Ec2 instanser vi skapar.
# variables.tf:
variable "cidr_range" {
type = string
default = "10.0.0.0/16"
description = "The prefix cidr range used in the vpc"
}
variable "instance_count" {
default = 3
}
variable "instance_type" {
type = string
default = "t3.micro"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
}
Slutligen kommer själva infrafilen, här definierar jag upp:
Jag mappar också ihop dessa resurser. Till exempel så knyter vi subnets till vpc enkelt genom att när vi skapar subnet, anger vi vpc-resursens id som vpc_id.
Vi tar just den biten som exempel:
resource "aws_vpc" "acebit" {
cidr_block = var.cidr_range # här hämtar vi cidr range från variabeln
instance_tenancy = "default"
tags = {
Name = "acebit"
}
}
resource "aws_subnet" "webA" {
vpc_id = aws_vpc.acebit.id # <-- här refererar vi till vpc'n vi skapar ovan.
cidr_block = "10.0.1.0/24"
}
Det fina här är att om man är van att arbeta med objektsorienterad programmering så ser man snabbt hur resurserna skapas med dynamiska attribut som vi kan peka på innan vi känner till värdet av dom. Här har vi för avsikt att skapa en resurs (objekt) av typen “aws_vpc”, den instansen döper vi till “acebit”.
När terraform sedan ska skapa subnätet pekar vi på just den instansen “aws_vpc.acebit”, och för att fiska ut id’t från just den vpc’n skriver vi bara “aws_vpc_acebit.id”.
Vi nyttjar också ett bra koncept som terraform kallar för “count”, vi kan helt enkelt definiera upp en resurs och ange count 3, så skapar terraform 3 instanser så att vi inte behöver repetera oss i koden. Sedan kan vi använda variabeln “count.index” i tex namnen för att hålla isär dom.
ex:
resource "aws_subnet" "web" {
count = 3 # <-- vi vill ha tre stycken.
vpc_id = aws_vpc.acebit.id
cidr_block = "10.0.${count.index + 1}.0/24" # <-- tredje okteten ska representera index.
tags = {
Name = "subnet${count.index + 1}"
}
}
Men vi är inte klara där, utan vi lägger även in resten av infrastrukturen för demon här:
# infra.tf
resource "aws_vpc" "acebit" {
cidr_block = var.cidr_range
instance_tenancy = "default"
tags = {
Name = "acebit"
}
}
resource "aws_subnet" "web" {
count = var.instance_count
vpc_id = aws_vpc.acebit.id
cidr_block = "10.0.${count.index + 1}.0/24"
tags = {
Name = "subnet${count.index + 1}"
}
}
resource "aws_security_group" "allow_http_https" {
name = "allow http/https"
description = "Allow http and http inbound"
vpc_id = aws_vpc.acebit.id
}
resource "aws_security_group_rule" "permit_443" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
security_group_id = aws_security_group.allow_http_https.id
}
resource "aws_security_group_rule" "permit_80" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
security_group_id = aws_security_group.allow_http_https.id
}
resource "aws_network_interface" "int" {
count = var.instance_count
subnet_id = aws_subnet.web[count.index].id
security_groups = [aws_security_group.allow_http_https.id]
tags = {
Name = "primary_network_interface"
}
}
resource "aws_instance" "server" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
network_interface {
network_interface_id = aws_network_interface.int[count.index].id
device_index = 0
}
tags = {
Name = "web-${count.index + 1}"
}
}
Sedan kör vi kommandona terraform plan för att se vad terraform tycker sig behöva göra samt terraform apply för att bygga det vi precis deklarera och vi kan se i aws consolen att vi fått precis vad vi bett om:
När vi lekt klart tar vi bort hela kalaset med kommandot terraform destroy. Så himla enkelt!
Hela exemplet finns så klart också att kika på här:
Framöver tänkte jag att vi går lite djupare på terraform och ska bland annat kika på hur vi kan arbeta med moduler men även mot andra plattformar än AWS.
Vi hörs!
Acebit expanderar – automationsess från Dalarna öppnar i Stockholm
Acebit rekryterar Isak Ljunggren – Acebit Trainee från Högskolan Dalarna
Även Falu-Kuriren har intresserat sig för Acebit och besökt oss i Falun!
Emanuel Lipschütz ny styrelseordförande i Acebit
Nyligen stötte jag på ett scenario som innebar att ca 200 Meraki-enheter behövde byta inställning från DHCP till statisk IP-adressering. Istället för att gör...
I mina tidigare inlägg om virtuella port-channels kikade vi på vad det är och vilka delar de utgör. Det här avsnittet kommer beröra hur en grundkonfiguration...
Bakgrund Idag tänkt jag skriva några rader om att skriva concurrent kod, eller “samtidighet” om vi prompt ska översätta det till svenska. För att enklare för...
Scrapli Automation - trunk ports