Terraform İle AWS Kullanımı ve EC2 Web Server Deploy
Terraform ile AWS EC2 Instance kullanarak basit bir web server deploy etmek için neler yapmamız gerektiğini bu yazıda inceliyoruz. Amacımız, mümkün olan en basit web mimarisiyle HTTP isteklerine yanıt verebilen tek bir web sunucusu deploy etmek olacak.
Gerçek dünya use case'lerinde, web sunucusunu muhtemelen Ruby on Rails veya Django gibi bir web framework kullanarak oluşturursunuz. Ancak bu örneği basit tutmak için, her zaman “Hello, World” döndüren basit bir Apache HTTP Server kullanalım.
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
Bu snippet, "Hello, World" metnini index.html
'ye yazan ve bu dosyayı sunmak için 8080
numaralı portta bir web sunucusunu başlatmak için busybox
(varsayılan olarak Ubuntu'yla yüklenir) adlı bir aracı çalıştıran Bash betiğidir. Web sunucusunun arka planda kalıcı olarak çalışması için busybox
komutunu nohup
ve bir ampersand işareti (&
) ile çalıştırıyoruz.
root
kullanıcı ayrıcalıkları gerektirmesidir. Bu bir güvenlik riskidir, çünkü sunucunuzun güvenliğini aşmayı başaran herhangi bir saldırgan da root
ayrıcalıklarına sahip olacaktır.Şimdi Terraform kısmına geçeceğiz fakat bilgilerinizi tazelemek ve Terraform temellerine daha iyi hakim olmak için öncelikle şu iki yazıya göz atabilirsiniz:
Peki bu scripti EC2 Instance üzerinde nasıl çalıştırabilirsiniz? Normalde, "Server Templating Tools" bölümünde tartıştığımız gibi, üzerinde web sunucusunun kurulu olduğu özel bir AMI oluşturmak için Packer gibi bir araç kullanırsınız. Bu örnekteki dummy web sunucusu, busybox
kullanan yalnızca tek satırlık kod olduğundan, düz bir Ubuntu 20.04 AMI kullanabilir ve EC2 Instance'ın User Data yapılandırmasının bir parçası olarak "Hello, World" komut dosyasını çalıştırabilirsiniz. Bir EC2 Instance'ı başlattığınızda, User Data içindeki shell komutları boot sırasında yürütülür. Terraform kodunuzdaki user_data
değişkenini aşağıdaki gibi ayarlayarak bir shell komut dosyasını User Data'ya iletirsiniz:
resource "aws_instance" "example" {
ami = "ami-0fb653ca2d3203ac1"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "terraform-example"
}
}
<<-EOF
ve EOF
, Terraform'un her yere \n
karakteri eklemek zorunda kalmadan çok satırlı dizeler oluşturmanıza izin veren heredoc syntaxıdır.
Bu web sunucusu çalışmadan önce bir şey daha yapmanız gerekiyor. Varsayılan olarak AWS, bir EC2 Instance'ından gelen veya giden trafiğe izin vermez. EC2 Instance'ı için 8080
numaralı bağlantı noktasında trafik almasına izin vermek için bir güvenlik grubu oluşturmanız gerekir:
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"]
}
}
Bu kod, aws_security_group
adlı yeni bir resource oluşturur (AWS provider için tüm resourceların aws_
ile başladığına dikkat edin) ve bu grubun CIDR bloğu 0.0.0.0/0'dan 8080 numaralı bağlantı noktasından gelen TCP isteklerine izin verdiğini belirtir. CIDR blokları, IP adresi aralıklarını belirtmenin kısa bir yoludur. Örneğin, 10.0.0.0/24'lük bir CIDR bloğu, 10.0.0.0 ile 10.0.0.255 arasındaki tüm IP adreslerini temsil eder. CIDR bloğu 0.0.0.0/0 tüm olası IP adreslerini içeren bir IP adresi aralığıdır, bu nedenle bu güvenlik grubu herhangi bir IP adresinden 8080 numaralı bağlantı noktasından gelen isteklere izin verir.
Yalnızca bir güvenlik grubu oluşturmak yeterli değildir; ayrıca güvenlik grubunun kimliğini aws_instance
kaynağının vpc_security_group_ids
değişkenine geçirerek EC2 Instance'ın onu gerçekten kullanmasını söylemeniz gerekir. Bunu yapmak için önce Terraform expressions'ları öğrenmeniz gerekir.
Terraform'daki bir expression, bir değer döndüren herhangi bir şeydir. Stringler (ör., "ami-0fb653ca2d3203ac1") ve sayılar (ör. 5) gibi en basit ifade türlerini zaten gördünüz. Terraform, ilerleyen yazılar boyunca göreceğiniz diğer birçok expression türünü destekler.
Özellikle yararlı bir expression türü olan referans, kodunuzun diğer bölümlerinden değerlere erişmenizi sağlar. Güvenlik grubu resource'unun ID'sine erişmek için aşağıdaki syntaxı kullanan bir resource attribute referansı kullanmanız gerekecektir:
<PROVIDER>_<TYPE>.<NAME>.<ATTRIBUTE>
PROVIDER
sağlayıcının adıdır (ör.aws
,do
),TYPE
resource türüdür (ör.instance
,security_group
),NAME
bu resource'un adıdır (ör. güvenlik grubunu "instance
" olarak adlandırdık)ATTRIBUTE
ya o kaynağın değişkenlerinden biri (örneğinname
) veya kaynak tarafından dışa aktarılan niteliklerden biridir (kullanılabilir niteliklerin listesini her kaynağın belgelerinde bulabilirsiniz). Güvenlik grubu,id
adlı bir attribute'ü dışa aktarır
Bu bilgiler eşiğinde bu güvenlik grubu resource'unun ID bilgisine EC2 Instance resource'u içinden şu şekilde erişebiliriz:
aws_security_group.instance.id
Bu güvenlik grubu kimliğini aws_instance
'ın vpc_security_group_ids
değişkeninde kullanabilirsiniz:
resource "aws_instance" "example" {
ami = "ami-0fb653ca2d3203ac1"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "terraform-example"
}
}
Bir kaynaktan diğerine referans eklediğinizde, örtük bir bağımlılık yaratırsınız. Terraform bu bağımlılıkları ayrıştırır, onlardan bir bağımlılık grafiği oluşturur ve bunu, kaynakları hangi sırayla oluşturması gerektiğini otomatik olarak belirlemek için kullanır. Örneğin, bu kodu sıfırdan dağıtıyorsanız, Terraform güvenlik grubunu EC2 Instance'ından önce oluşturması gerektiğini bilir çünkü EC2 Instance güvenlik grubunun kimliğine başvurur. terraform graph
komutunu çalıştırarak Terraform'un size bağımlılık grafiğini göstermesini bile sağlayabilirsiniz:
Çıktı, Şekil 2'de gösterilen bağımlılık grafiğine benzer şekilde, Graphviz gibi bir masaüstü uygulaması veya GraphvizOnline gibi bir web uygulaması kullanarak bir görüntüye dönüştürebileceğiniz DOT adlı bir grafik tanımlama dilindedir.
Terraform dependecy ağacınızda gezinirken, paralel olarak olabildiğince çok kaynak yaratır, bu da değişikliklerinizi oldukça verimli bir şekilde uygulayabileceği anlamına gelir. Declarative dilin güzelliği budur: Siz sadece ne istediğinizi belirtirsiniz ve Terraform bunu gerçekleştirmenin en verimli yolunu belirler.
Apply komutunu çalıştırırsanız, Terraform'un bir güvenlik grubu oluşturmak ve EC2 Instance'ı yeni kullanıcı verilerini içeren yenisiyle değiştirmek istediğini göreceksiniz:
$ terraform apply
(...)
Terraform will perform the following actions:
# aws_instance.example must be replaced
-/+ resource "aws_instance" "example" {
ami = "ami-0fb653ca2d3203ac1"
~ availability_zone = "us-east-2c" -> (known after apply)
~ instance_state = "running" -> (known after apply)
instance_type = "t2.micro"
(...)
+ user_data = "c765373..." # forces replacement
~ volume_tags = {} -> (known after apply)
~ vpc_security_group_ids = [
- "sg-871fa9ec",
] -> (known after apply)
(...)
}
# aws_security_group.instance will be created
+ resource "aws_security_group" "instance" {
+ arn = (known after apply)
+ description = "Managed by Terraform"
+ egress = (known after apply)
+ id = (known after apply)
+ ingress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = ""
+ from_port = 8080
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 8080
},
]
+ name = "terraform-example-instance"
+ owner_id = (known after apply)
+ revoke_rules_on_delete = false
+ vpc_id = (known after apply)
}
Plan: 2 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
Plan
çıktısındaki -/+
"replace" anlamına gelir. Terraform'u değiştirme yapmaya neyin zorladığını anlamak için plan çıktısında "forces replacement" metnini arayın. aws_instance
kaynağındaki argümanların çoğu, değiştirilirse yenisini zorunlu kılar. Yani bu, orijinal EC2 Instance'ın terminate edileceği ve tamamen yeni bir Instance oluşturulacağı anlamına gelir. Bu, "Server Templating Tools" da tartışılan immutable altyapı paradigmasının bir örneğidir. Web sunucusunun değiştirilmesine rağmen, o web sunucusunun herhangi bir kullanıcısının downtime yaşayacağını belirtmekte fayda var. İleride Terraform ile sıfır kesinti süreli dağıtımın nasıl yapıldığını göstereceğiz.
Plan iyi göründüğü için 'yes' yazın ve Şekil 3'te gösterildiği gibi yeni EC2 Instance'ın deploy edildiğini gözlemleyin.
Yeni Instance'a tıklarsanız, public IP adresini ekranın alt kısmındaki açıklama panelinde bulabilirsiniz. Instance'ı başlatması için bir veya iki dakika bekledikten sonra ve ardından 8080 numaralı bağlantı noktasında bu IP adresine bir HTTP isteği yapmak için bir web tarayıcısı veya curl gibi bir araç kullandığınızda metnimizi görebilirsiniz:
Tebrikler! Artık AWS'de çalışan basit bir web sunucunuz var!
terraform destroy
komutu ile tüm luşturduğunuz resource'ları temizleyebilirsiniz.