AWS EC2 Instance Oluşturma ve Yapılandırma Rehberi

AWS hesabını açtıktan sonra ilk yapmanız gereken şey muhtemelen bir EC2 instance oluşturmak oluyor. Kulağa basit geliyor, ama yanlış yapılandırılmış bir instance production ortamında ciddi sorunlara yol açabiliyor. Güvenlik açıkları, performans sorunları, beklenmedik maliyetler… Bunların hepsini baştan doğru yaparak engelleyebilirsiniz. Bu yazıda sıfırdan bir EC2 instance oluşturup üretim ortamına hazır hale getireceğiz.

EC2’ye Giriş: Temel Kavramlar

EC2, AWS’nin sanal sunucu hizmetidir. Fiziksel bir sunucu satın almak yerine ihtiyacınıza göre boyutlandırılmış sanal makineler kiralıyorsunuz. Ama bu tanım biraz yüzeysel kalıyor. EC2’yi anlamak için birkaç temel kavramı netleştirmek gerekiyor.

AMI (Amazon Machine Image): Instance’ınızın temel aldığı disk görüntüsü. İçinde işletim sistemi, gerekli yazılımlar ve konfigürasyonlar bulunuyor. AWS’nin hazır AMI’ları var, community AMI’ları var, ya da kendiniz özel bir AMI oluşturabilirsiniz.

Instance Type: Sanal makinenizin donanım özelliklerini belirliyor. t3.micro, m5.large, c6i.xlarge gibi isimler görüyorsunuz. Bu isimlendirme sisteminde harf ailesi (t, m, c, r), nesil numarası ve boyutu belirtiyor.

Security Group: EC2 için sanal güvenlik duvarı görevi görüyor. Hangi portların hangi IP adreslerinden erişilebilir olduğunu burada tanımlıyorsunuz.

Key Pair: SSH erişimi için kullanılan anahtar çifti. Private key’i kaybederseniz instance’a erişemezsiniz, bu yüzden güvenli saklamanız kritik.

EBS (Elastic Block Store): Instance’ınıza bağlı blok depolama birimleri. Instance’ı durdurup başlatsanız bile verileriniz burada korunaklı kalıyor.

AWS CLI Kurulumu ve Yapılandırması

Console üzerinden instance oluşturmak başlangıç için iyi ama otomasyon ve tekrarlanabilirlik açısından AWS CLI şart. Önce bunu kuralım.

# Linux için AWS CLI v2 kurulumu
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Kurulumu doğrula
aws --version
# aws-cli/2.x.x Python/3.x.x Linux/x86_64

Kurulumdan sonra credentials yapılandırması gerekiyor. IAM’dan bir kullanıcı oluşturup access key almanız gerekiyor. Root account’u asla direkt kullanmayın.

# AWS CLI yapılandırması
aws configure

# Sizi şu sorular karşılıyor:
# AWS Access Key ID: AKIAXXXXXXXXXXXXXXXX
# AWS Secret Access Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Default region name: eu-west-1
# Default output format: json

# Yapılandırmayı doğrula
aws sts get-caller-identity

Birden fazla AWS hesabıyla çalışıyorsanız profil kullanımı hayat kurtarıyor:

# Farklı profil oluşturma
aws configure --profile production
aws configure --profile staging

# Profil kullanarak komut çalıştırma
aws ec2 describe-instances --profile production

Security Group Oluşturma

Instance oluşturmadan önce security group hazırlamak daha temiz bir yaklaşım. Aşağıdaki örnekte web sunucusu için tipik bir security group oluşturuyoruz.

# VPC ID'yi öğren
VPC_ID=$(aws ec2 describe-vpcs 
  --filters "Name=isDefault,Values=true" 
  --query "Vpcs[0].VpcId" 
  --output text)

echo "Default VPC: $VPC_ID"

# Security group oluştur
SG_ID=$(aws ec2 create-security-group 
  --group-name "web-server-sg" 
  --description "Web sunucusu icin security group" 
  --vpc-id $VPC_ID 
  --query "GroupId" 
  --output text)

echo "Security Group ID: $SG_ID"

# HTTP erişimi aç (her yerden)
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --protocol tcp 
  --port 80 
  --cidr 0.0.0.0/0

# HTTPS erişimi aç
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --protocol tcp 
  --port 443 
  --cidr 0.0.0.0/0

# SSH erişimi sadece kendi IP'nizden
MYIP=$(curl -s https://api.ipify.org)
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --protocol tcp 
  --port 22 
  --cidr "${MYIP}/32"

echo "Security group hazir. ID: $SG_ID"

SSH portunu 0.0.0.0/0 ile açmayın. Bunu yapan instance’lar internette dakikalar içinde brute force saldırılarıyla karşılaşıyor. Kendi IP’niz veya VPN IP bloğunuzla sınırlı tutun.

Key Pair Oluşturma

# Key pair oluştur ve kaydet
aws ec2 create-key-pair 
  --key-name "my-web-server-key" 
  --key-type ed25519 
  --query "KeyMaterial" 
  --output text > ~/.ssh/my-web-server-key.pem

# Dosya izinlerini düzelt (SSH bu olmadan bağlanmayı reddeder)
chmod 400 ~/.ssh/my-web-server-key.pem

# Key pair'i doğrula
aws ec2 describe-key-pairs --key-names "my-web-server-key"

Ed25519 key type’ı RSA’ya göre daha güvenli ve daha küçük. Eğer eski sistemlerle uyumluluk gerekiyorsa RSA 4096 kullanın, ama modern ortamlarda Ed25519 tercih edin.

Instance Oluşturma

Artık instance oluşturmaya hazırız. Önce doğru AMI’yı bulalım:

# Amazon Linux 2023 AMI'sini bul
AMI_ID=$(aws ec2 describe-images 
  --owners amazon 
  --filters 
    "Name=name,Values=al2023-ami-*-x86_64" 
    "Name=state,Values=available" 
  --query "sort_by(Images, &CreationDate)[-1].ImageId" 
  --output text)

echo "Kullanilacak AMI: $AMI_ID"

Şimdi instance’ı oluşturalım. Basit bir web sunucusu senaryosu için t3.small yeterli olacak:

# Instance oluştur
INSTANCE_ID=$(aws ec2 run-instances 
  --image-id $AMI_ID 
  --instance-type t3.small 
  --key-name "my-web-server-key" 
  --security-group-ids $SG_ID 
  --block-device-mappings '[
    {
      "DeviceName": "/dev/xvda",
      "Ebs": {
        "VolumeSize": 20,
        "VolumeType": "gp3",
        "Encrypted": true,
        "DeleteOnTermination": true
      }
    }
  ]' 
  --tag-specifications '
    [
      {
        "ResourceType": "instance",
        "Tags": [
          {"Key": "Name", "Value": "web-server-01"},
          {"Key": "Environment", "Value": "production"},
          {"Key": "Team", "Value": "backend"}
        ]
      }
    ]
  ' 
  --metadata-options "HttpTokens=required,HttpEndpoint=enabled" 
  --query "Instances[0].InstanceId" 
  --output text)

echo "Instance olusturuldu: $INSTANCE_ID"

# Instance'in calismaya baslamasini bekle
aws ec2 wait instance-running --instance-ids $INSTANCE_ID
echo "Instance calisiyor!"

# Public IP adresini al
PUBLIC_IP=$(aws ec2 describe-instances 
  --instance-ids $INSTANCE_ID 
  --query "Reservations[0].Instances[0].PublicIpAddress" 
  --output text)

echo "Public IP: $PUBLIC_IP"

--metadata-options "HttpTokens=required" satırına dikkat edin. Bu IMDSv2’yi zorunlu kılıyor. IMDSv1, SSRF saldırılarına karşı savunmasızdı. Production ortamlarında her zaman IMDSv2 kullanın.

Ayrıca disk şifrelemeyi ("Encrypted": true) açtık. EBS şifrelemesi ek maliyet getirmiyor ama verilerinizi fiziksel disk erişimine karşı koruyor. Bunu açmamak için hiçbir neden yok.

User Data ile Otomatik Yapılandırma

Instance ilk başladığında otomatik olarak çalışmasını istediğiniz komutları user data olarak tanımlayabilirsiniz. Bu, infrastructure as code yaklaşımının temelini oluşturuyor.

# User data script dosyası oluştur
cat > user-data.sh << 'USERDATA'
#!/bin/bash
set -e

# Sistem güncellemesi
dnf update -y

# Nginx kurulumu
dnf install -y nginx

# Nginx'i başlat ve otomatik başlatmayı etkinleştir
systemctl start nginx
systemctl enable nginx

# Basit bir test sayfası oluştur
cat > /usr/share/nginx/html/index.html << 'HTML'
<html>
<body>
<h1>Sunucu Hazir!</h1>
<p>Instance: $(hostname)</p>
</body>
</html>
HTML

# CloudWatch agent kurulumu
dnf install -y amazon-cloudwatch-agent

# Tamamlandı logla
echo "User data tamamlandi: $(date)" >> /var/log/user-data.log
USERDATA

# Bu user data ile instance oluştur
INSTANCE_ID=$(aws ec2 run-instances 
  --image-id $AMI_ID 
  --instance-type t3.small 
  --key-name "my-web-server-key" 
  --security-group-ids $SG_ID 
  --user-data file://user-data.sh 
  --tag-specifications '[{"ResourceType":"instance","Tags":[{"Key":"Name","Value":"web-server-01"}]}]' 
  --metadata-options "HttpTokens=required,HttpEndpoint=enabled" 
  --query "Instances[0].InstanceId" 
  --output text)

User data loglarını /var/log/cloud-init-output.log dosyasından takip edebilirsiniz. Bir şeyler ters giderse ilk bakacağınız yer burası olmalı.

IAM Role ile Instance’a Yetki Verme

Instance’ınızın diğer AWS servislerine erişmesi gerekiyorsa, asla access key’leri instance içine hard-code etmeyin. Bunun yerine IAM role kullanın.

# IAM role için trust policy oluştur
cat > ec2-trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# Role oluştur
aws iam create-role 
  --role-name "EC2WebServerRole" 
  --assume-role-policy-document file://ec2-trust-policy.json

# S3 read-only erişimi ekle (örnek olarak)
aws iam attach-role-policy 
  --role-name "EC2WebServerRole" 
  --policy-arn "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"

# CloudWatch Logs erişimi ekle
aws iam attach-role-policy 
  --role-name "EC2WebServerRole" 
  --policy-arn "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"

# Instance profile oluştur (EC2 doğrudan role kullanamaz, instance profile üzerinden kullanır)
aws iam create-instance-profile 
  --instance-profile-name "EC2WebServerProfile"

# Role'ü profile'a ekle
aws iam add-role-to-instance-profile 
  --instance-profile-name "EC2WebServerProfile" 
  --role-name "EC2WebServerRole"

# Mevcut instance'a role ekle
aws ec2 associate-iam-instance-profile 
  --instance-id $INSTANCE_ID 
  --iam-instance-profile Name="EC2WebServerProfile"

IAM role kullandığınızda instance metadata servisi üzerinden otomatik olarak geçici credential alıyor. Credentials her birkaç saatte bir otomatik rotate ediliyor. Bu mekanizmayı kullanmak, static access key kullanmaktan çok daha güvenli.

Elastic IP ile Sabit IP Adresi

Instance’ı durdurduğunuzda public IP adresi değişiyor. Production ortamında bu sorun. Elastic IP (EIP) ile sabit bir IP adresi atayabilirsiniz.

# Elastic IP tahsis et
ALLOCATION_ID=$(aws ec2 allocate-address 
  --domain vpc 
  --query "AllocationId" 
  --output text)

echo "Elastic IP tahsis edildi: $ALLOCATION_ID"

# IP adresini göster
aws ec2 describe-addresses 
  --allocation-ids $ALLOCATION_ID 
  --query "Addresses[0].PublicIp" 
  --output text

# Instance'a bağla
aws ec2 associate-address 
  --instance-id $INSTANCE_ID 
  --allocation-id $ALLOCATION_ID

echo "Elastic IP instance'a baglandi"

Önemli uyarı: Elastic IP’ler bir instance’a bağlı olmadığında saatlik ücret kesiliyor. Instance’ı sildiğinizde EIP’yi de release etmeyi unutmayın, yoksa ayda birkaç dolar gereksiz ücret ödemiş olursunuz.

Instance Yapılandırmasını Doğrulama

Instance çalışıyor mu, doğru yapılandırılmış mı kontrol edelim:

# Instance detaylarını görüntüle
aws ec2 describe-instances 
  --instance-ids $INSTANCE_ID 
  --query 'Reservations[0].Instances[0].{
    ID:InstanceId,
    Type:InstanceType,
    State:State.Name,
    PublicIP:PublicIpAddress,
    PrivateIP:PrivateIpAddress,
    AMI:ImageId,
    LaunchTime:LaunchTime,
    IAMProfile:IamInstanceProfile.Arn
  }' 
  --output json

# SSH ile bağlan
ssh -i ~/.ssh/my-web-server-key.pem ec2-user@$PUBLIC_IP

# Bağlandıktan sonra sistem kontrolü
# Disk kullanımı
df -h

# Memory durumu
free -h

# Çalışan servisler
systemctl list-units --type=service --state=running

# User data logları
sudo cat /var/log/cloud-init-output.log

Monitoring ve Alarm Kurulumu

Instance oluşturmak yetmiyor, izlememiz de gerekiyor. CloudWatch ile temel alarmları kuralım:

# CPU kullanımı yüksek olduğunda alarm
aws cloudwatch put-metric-alarm 
  --alarm-name "WebServer-HighCPU" 
  --alarm-description "CPU kullanimi %80 ustu" 
  --metric-name CPUUtilization 
  --namespace AWS/EC2 
  --statistic Average 
  --period 300 
  --threshold 80 
  --comparison-operator GreaterThanThreshold 
  --evaluation-periods 2 
  --dimensions Name=InstanceId,Value=$INSTANCE_ID 
  --alarm-actions "arn:aws:sns:eu-west-1:123456789012:ops-alerts"

# Instance durumu kontrolü için alarm
aws cloudwatch put-metric-alarm 
  --alarm-name "WebServer-StatusCheck" 
  --alarm-description "Instance durum kontrolu basarisiz" 
  --metric-name StatusCheckFailed 
  --namespace AWS/EC2 
  --statistic Maximum 
  --period 60 
  --threshold 1 
  --comparison-operator GreaterThanOrEqualToThreshold 
  --evaluation-periods 2 
  --dimensions Name=InstanceId,Value=$INSTANCE_ID 
  --alarm-actions "arn:aws:sns:eu-west-1:123456789012:ops-alerts"

SNS ARN’ini kendi hesabınıza ve SNS topic’inize göre güncellemeniz gerekiyor. Alarm aksiyonlarını mail veya Slack notification’a yönlendirebilirsiniz.

Gerçek Dünya Senaryosu: Sıfırdan Web Sunucusu

Tüm bu adımları bir araya getiren tam bir senaryo yazalım. Bir startup için production web sunucusu kuruyorsunuz:

#!/bin/bash
# Tam web sunucusu kurulum scripti
set -euo pipefail

REGION="eu-west-1"
INSTANCE_TYPE="t3.small"
KEY_NAME="startup-prod-key"
ENVIRONMENT="production"

echo "=== Web Sunucusu Kurulumu Basliyor ==="

# Degiskenleri hazirla
VPC_ID=$(aws ec2 describe-vpcs 
  --filters "Name=isDefault,Values=true" 
  --region $REGION 
  --query "Vpcs[0].VpcId" 
  --output text)

AMI_ID=$(aws ec2 describe-images 
  --owners amazon 
  --filters "Name=name,Values=al2023-ami-*-x86_64" "Name=state,Values=available" 
  --region $REGION 
  --query "sort_by(Images, &CreationDate)[-1].ImageId" 
  --output text)

echo "VPC: $VPC_ID"
echo "AMI: $AMI_ID"

# Key pair olustur
aws ec2 create-key-pair 
  --key-name $KEY_NAME 
  --key-type ed25519 
  --region $REGION 
  --query "KeyMaterial" 
  --output text > ~/.ssh/${KEY_NAME}.pem
chmod 400 ~/.ssh/${KEY_NAME}.pem

# Security group
SG_ID=$(aws ec2 create-security-group 
  --group-name "prod-web-sg" 
  --description "Production web sunucusu SG" 
  --vpc-id $VPC_ID 
  --region $REGION 
  --query "GroupId" 
  --output text)

MYIP=$(curl -s https://api.ipify.org)
aws ec2 authorize-security-group-ingress --group-id $SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0 --region $REGION
aws ec2 authorize-security-group-ingress --group-id $SG_ID --protocol tcp --port 443 --cidr 0.0.0.0/0 --region $REGION
aws ec2 authorize-security-group-ingress --group-id $SG_ID --protocol tcp --port 22 --cidr "${MYIP}/32" --region $REGION

# Instance olustur
INSTANCE_ID=$(aws ec2 run-instances 
  --image-id $AMI_ID 
  --instance-type $INSTANCE_TYPE 
  --key-name $KEY_NAME 
  --security-group-ids $SG_ID 
  --block-device-mappings '[{"DeviceName":"/dev/xvda","Ebs":{"VolumeSize":20,"VolumeType":"gp3","Encrypted":true}}]' 
  --metadata-options "HttpTokens=required,HttpEndpoint=enabled" 
  --tag-specifications "[{"ResourceType":"instance","Tags":[{"Key":"Name","Value":"prod-web-01"},{"Key":"Environment","Value":"$ENVIRONMENT"}]}]" 
  --region $REGION 
  --query "Instances[0].InstanceId" 
  --output text)

echo "Instance olusturuldu: $INSTANCE_ID"
echo "Calismasi bekleniyor..."

aws ec2 wait instance-running --instance-ids $INSTANCE_ID --region $REGION

PUBLIC_IP=$(aws ec2 describe-instances 
  --instance-ids $INSTANCE_ID 
  --region $REGION 
  --query "Reservations[0].Instances[0].PublicIpAddress" 
  --output text)

echo "=== Kurulum Tamamlandi ==="
echo "Instance ID: $INSTANCE_ID"
echo "Public IP: $PUBLIC_IP"
echo "SSH: ssh -i ~/.ssh/${KEY_NAME}.pem ec2-user@${PUBLIC_IP}"

Maliyet Optimizasyonu Notları

Production ortamında maliyet kontrolü kritik. Birkaç pratik ipucu:

  • gp3 vs gp2: EBS volume’lerinizi gp3’e alın. gp3, gp2’den %20 daha ucuz ve daha iyi performans sunuyor.
  • Reserved Instance: Uzun süreli kullanım için 1 yıllık reserved instance alın, on-demand’e göre %40-60 tasarruf sağlar.
  • Spot Instance: Batch işlemler, test ortamları gibi kesintiye dayanıklı workload’lar için spot instance kullanın, %90’a kadar ucuz olabiliyor.
  • Kullanılmayan EIP’leri serbest bırakın: Atanmamış her Elastic IP saatlik ücret kesiyor.
  • Snapshots’ları temizleyin: Eski EBS snapshot’ları birikiyor ve depolama maliyeti yaratıyor.

Sonuç

Doğru yapılandırılmış bir EC2 instance için bir checklist oluşturacak olursak şunlar olmazsa olmazlar:

  • IMDSv2 zorunlu kılınmış olmalı
  • EBS şifrelemesi açık olmalı
  • SSH erişimi kısıtlı IP’lerden yapılmalı
  • IAM role üzerinden yetkilendirme yapılmalı, hard-coded credential olmamalı
  • CloudWatch ile temel alarmlar kurulmuş olmalı
  • Proper tag’leme yapılmış olmalı (maliyet takibi için şart)

AWS CLI ve script tabanlı yaklaşım başlangıçta zahmetli görünse de ilerleyen süreçte aynı konfigürasyonu tekrar etmeniz gerektiğinde büyük zaman tasarrufu sağlıyor. Bu scriptleri Git’e koyun, versiyon takibi yapın. Altı ay sonra “bu sunucuyu nasıl kurmuştum?” diye düşünmek zorunda kalmazsınız.

Bir sonraki adım olarak Auto Scaling Group ve Load Balancer konusuna bakabilirsiniz. Tek instance ile production yapmak riskli, yedeklilik şart.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir