Terraform İle AWS Kullanımı ve EC2 Web Server Cluster Deploy

Terraform ile AWS iki adet EC2 Instance'dan oluşan bir web server cluster deploy etmek için neler yapmamız gerektiğini bu yazıda inceliyoruz.

Tek bir sunucu çalıştırmak iyi bir başlangıçtır, ancak gerçek dünyada tek bir sunucu tek bir başarısızlık noktasıdır. Bu sunucu çökerse veya çok fazla trafikten aşırı yüklenirse, kullanıcılar sitenize erişemez. Çözüm, bir sunucu kümesi çalıştırmak, azalan sunucular etrafında yönlendirme yapmak ve kümenin boyutunu trafiğe bağlı olarak yukarı veya aşağı doğru ayarlamaktır.

Böyle bir kümeyi manuel olarak yönetmek çok zor iştir. Neyse ki, Şekil 1'de gösterildiği gibi bir Auto Scaling Group (ASG) kullanarak AWS'nin bununla ilgilenmesine izin verebilirsiniz. Bir ASG, bir EC2 Instance kümesi (cluster) başlatmak, her bir Instance'ın durumunu izlemek, arızalı Instance'ları değiştirmek ve yüke göre kümenin boyutunu ayarlamak dahil olmak üzere birçok görevi sizin için tamamen otomatik olarak halleder.

Şekil 1: Tek bir web sunucusu yerine, bir Auto Scaling Group kullanarak web sunucusu kümesi (cluster) çalıştırın

Şimdi Terraform kısmına geçeceğiz fakat iki EC2 Instance'dan oluşan bir web server cluster deploy etmeden önce tek bir Ec2 Instance'dan oluşan web serveri nasıl deploy ettiğimizi hatırlamak adına şu yazımıza göz atabilirsiniz:

Terraform İle AWS Kullanımı ve EC2 Web Server Deploy
Terraform ile AWS kullanarak EC2 Instance web server nasıl oluşturulur ve nasıl deploy edilir detaylıca gösteriyoruz.

ASG oluşturmanın ilk adımı, ASG'deki her EC2 Instance'ın nasıl yapılandırılacağını belirten bir başlatma yapılandırması (launch configuration) oluşturmaktır. aws_launch_configuration kaynağı, aws_instance kaynağı ile neredeyse aynı parametreleri kullanır.  Farklı olan parametreler şunlardır:

  • ami: Artık image_id olarak kullanacağız.
  • vpc_security_group_ids: Artık security_groups olarak kullanacağız.
  • tags: Desteklenmiyor. Bunu aws_autoscaling_group kaynağında ele alacağız.

Son durumda aws_launch_configuration kaynağımız şu şekilde olacaktır:

resource "aws_launch_configuration" "example" {
  image_id        = "ami-0fb653ca2d3203ac1"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.instance.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, World" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF
}

Trafiğe izin vermek için security_groups ta referans aldığımız kaynağı da oluşturalım:

resource "aws_security_group" "instance" {
  name = "terraform-example-instance"

  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Artık aws_autoscaling_group kaynağını kullanarak ASG'nin kendisini oluşturabilirsiniz:

resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.name

  min_size = 2
  max_size = 10

  tag {
    key                 = "Name"
    value               = "terraform-asg-example"
    propagate_at_launch = true
  }
}

Bu ASG, her biri terraform-asg-example adıyla etiketlenmiş 2 ila 10 EC2 Instance (ilk başlatma için varsayılan olarak 2'dir) arasında çalışır. ASG'nin launch_configuration adını doldurmak için bir referans kullandığına dikkat edin. Bu bir soruna yol açar: launch configuration değişmezdir (immutable), bu nedenle launch configuration'ın herhangi bir parametresini değiştirirseniz, Terraform onu ​​değiştirmeye çalışır. Normalde, bir kaynağı değiştirirken, Terraform önce eski kaynağı siler ve ardından onun yerine yenisini oluşturur, ancak ASG'niz (aws_autoscaling_group) artık eski kaynağa bir referansa sahip olduğundan, Terraform onu ​​silemez.

Bu sorunu çözmek için bir yaşam döngüsü ayarı kullanabilirsiniz. Her Terraform kaynağı, o kaynağın nasıl oluşturulduğunu, güncellendiğini ve/veya silindiğini yapılandıran birkaç yaşam döngüsü ayarını destekler. Özellikle yararlı bir yaşam döngüsü ayarı create_before_destroy'dur. create_before_destroy'u true olarak ayarlarsanız, Terraform, kaynakları değiştirme sırasını tersine çevirir; ilk olarak yedek kaynağı oluşturur, eski kaynağa işaret eden tüm referansları bu yeni kaynakla günceller ve ardından eski kaynağı siler. aws_launch_configuration'ınıza lifecycle bloğunu aşağıdaki gibi ekleyin:

resource "aws_launch_configuration" "example" {
  image_id        = "ami-0fb653ca2d3203ac1"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.instance.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, World" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF

  # auto scaling group ile launch configuration kullanırken gereklidir.
  lifecycle {
    create_before_destroy = true
  }
}

Çalışması için ASG'nize eklemeniz gereken başka bir parametre daha var: subnet_ids. Bu parametre, EC2 Instance'ların hangi VPC subnetlerde dağıtılması gerektiğini ASG'ye belirtir. Her subnet, yalıtılmış bir AWS AZ'de (yani yalıtılmış veri merkezinde) yaşar; bu nedenle, Instance'larınızı birden çok subnette dağıtarak, bazı veri merkezlerinde kesinti olsa bile hizmetinizin çalışmaya devam etmesini sağlarsınız. Subnetlerin listesini statik olarak kodlayabilirsiniz, ancak bu sürdürülemez veya taşınabilir olmayacaktır. Bu nedenle AWS hesabınızdaki subnetlerin listesini almak için data source kullanmak daha iyi bir seçenektir.

Bir data source, Terraform'u her çalıştırdığınızda sağlayıcıdan (bu durumda AWS) alınan bir salt okunur bilgiyi temsil eder. Terraform konfigürasyonlarınıza bir data source eklemek yeni bir şey yaratmaz; bu sadece sağlayıcının API'lerini veri için sorgulamanın ve bu verileri Terraform kodunuzun geri kalanı için kullanılabilir hale getirmenin bir yoludur. Her Terraform sağlayıcısı, çeşitli data source'lar sunar. Örneğin, AWS sağlayıcısı, VPC verilerini, subnet verilerini, AMI kimliklerini, IP adresi aralıklarını, mevcut kullanıcının kimliğini ve çok daha fazlasını aramak için veri kaynakları içerir.

Bir data source sözdizimi, bir kaynağın sözdizimine çok benzer:

data "<PROVIDER>_<TYPE>" "<NAME>" {
  [CONFIG ...]
}

Burada;

  • PROVIDER bir sağlayıcının adıdır (ör. aws),
  • TYPE kullanmak istediğiniz veri kaynağının türüdür (ör. vpc),
  • NAME, bu data source'a atıfta bulunmak için Terraform kodu boyunca kullanabileceğiniz bir tanımlayıcıdır,
  • CONFIG, o veri kaynağına özgü bir veya daha fazla bağımsız değişkenden oluşur.

Örneğin, Varsayılan VPC'nizin verilerini aramak için aws_vpc data source'u şu şekilde kullanabilirsiniz:

data "aws_vpc" "default" {
  default = true
}

Data source'larında, ilettiğiniz argümanların genellikle veri kaynağına hangi bilgiyi aradığınızı belirten arama filtreleri olduğunu unutmayın. aws_vpc data source ile ihtiyacınız olan tek filtre default = true olur ve bu, Terraform'u AWS hesabınızdaki default VPC'yi aramaya yönlendirir.

Verileri bir data source'dan çıkarmak için aşağıdaki attribute referans sözdizimini kullanırsınız:

data.<PROVIDER>_<TYPE>.<NAME>.<ATTRIBUTE>

Örneğin, aws_vpc veri kaynağından VPC'nin kimliğini almak için aşağıdakileri kullanırsınız:

data.aws_vpc.default.id

Bu VPC içindeki subnetleri aramak için bunu başka bir data source olan aws_subnets ile birleştirebilirsiniz:

data "aws_subnets" "default" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
}

Son olarak, subnet kimliklerini aws_subnets data source'dan çıkarabilir ve ASG'nize (biraz garip bir şekilde adlandırılmış) vpc_zone_identifier argümanı aracılığıyla bu subnetleri kullanmasını söyleyebilirsiniz:

resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.name
  vpc_zone_identifier  = data.aws_subnets.default.ids

  min_size = 2
  max_size = 10

  tag {
    key                 = "Name"
    value               = "terraform-asg-example"
    propagate_at_launch = true
  }
}

Şimdi tüm Terraform kodumuz hazır olduğuna göre sırasıyla terraform init ve terraform apply komutlarını çalıştırarak deploy işlemine başlayalım. Ardından EC2 Instance panelini ziyaret ettiğinizde 2 adet instance'ın çalıştığını göreceksiniz. Stres testi yaparak bu instance sayılarının arttığını da gözlemleyebilirsiniz.


NOT: terraform apply komutu sonucu uzun sürüyorsa korkmayın. Yaklaşık 10dk sonra konsolunuzda bu problemin sebebini çıktı olarak göreceksiniz. Örneğin yeni bir AWS hesabı kullanıyorsanız şu hatayı almanız muhtemeldir:

{
	StatusCode: "Failed",
	StatusMessage: "You have requested more vCPU capacity than your current vCPU limit of 1 allows for the instance bucket that the specified instance type belongs to. Please visit http://aws.amazon.com/contact-us/ec2-request to request an adjustment to this limit. Launching EC2 instance failed."
}

Sorunun kaynağı hesabınıza tanımlanmış vCPU limitidir. Bu limiti EC2 panelindeki Limits sayfasından görebilirsiniz.

Şekil 2: Hesap limitlerini görüntüleme

Sizin de gördüğünüz gibi izin verilen vCPU limiti 1 dir. Fakat deploy etmeye çalıştığımız t2.micro instance'ın her biri 1 vCPU'a sahip olduğundan ikinci instance'ı deploy edemiyoruz. [Tablo Sayfası]

Şekil 3: AWS EC2 Instance özellikleri tablosu

Hesap limitlerinizi "Service Quotas/AWS services/Amazon Elastic Compute Cloud (Amazon EC2)" sayfasına giderek artırabilir ve limitiniz arttıktan sonra terraform apply komutunu tekrar çalıştırabilirsiniz.

Şekil 4: EC2 limitleri artırma sayfası