AWS CLI ile EC2 Yönetimi: Komut Satırından Bulut Sunucu Kontrolü

Günlük EC2 yönetimini konsoldan yapmak başlangıçta mantıklı görünebilir, ama onlarca instance yönetir hale geldiğinizde ya da otomasyon kurmak istediğinizde AWS CLI kaçınılmaz oluyor. Ben de yıllarca konsoldan yönetim yaptım, ta ki sabah 3’te 20 instance’ı tek tek restart etmem gerekene kadar. O geceden sonra CLI benim vazgeçilmezim oldu.

Bu yazıda AWS CLI ile EC2 yönetimini, gerçek dünya senaryolarıyla birlikte ele alacağız. Temel komutlardan başlayıp otomasyona kadar gideceğiz.

AWS CLI Kurulum ve Yapılandırma

Önce temel kurulumu halledelim. AWS CLI v2 kullanacağız çünkü v1 artık aktif geliştirilmiyor.

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

# Versiyon kontrolü
aws --version
# aws-cli/2.15.0 Python/3.11.6 Linux/6.1.0 exe/x86_64.ubuntu.22

# Yapılandırma
aws configure
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name: eu-west-1
# Default output format: json

Birden fazla hesap veya region yönetiyorsanız, profil yapısını kullanmanızı şiddetle tavsiye ederim. Production ve staging ortamlarını birbirinden ayırmak için bu şart:

# Farklı profil eklemek
aws configure --profile production
aws configure --profile staging

# Profil ile komut çalıştırmak
aws ec2 describe-instances --profile production --region eu-central-1

# Varsayılan profili geçici olarak değiştirmek
export AWS_PROFILE=staging

Yapılandırma dosyaları ~/.aws/credentials ve ~/.aws/config altında tutuluyor. Bu dosyaları doğrudan düzenleyebilirsiniz, özellikle çok sayıda profil varsa bu daha pratik oluyor.

EC2 Instance Listeleme ve Filtreleme

CLI’nin en güçlü yanlarından biri filtreleme kapasitesi. Onlarca instance içinden aradığınızı bulmak için --filters parametresini öğrenmek gerekiyor.

# Çalışan tüm instance'ları listele
aws ec2 describe-instances 
  --filters "Name=instance-state-name,Values=running" 
  --query 'Reservations[*].Instances[*].[InstanceId,InstanceType,PublicIpAddress,Tags[?Key==`Name`].Value|[0]]' 
  --output table

# Belirli bir tag'e göre filtrele
aws ec2 describe-instances 
  --filters 
    "Name=tag:Environment,Values=production" 
    "Name=instance-state-name,Values=running" 
  --query 'Reservations[*].Instances[*].[InstanceId,PrivateIpAddress,Tags[?Key==`Name`].Value|[0]]' 
  --output text

--query parametresi JMESPath syntax kullanıyor ve başlangıçta karmaşık görünebilir, ama alıştıktan sonra output’u istediğiniz şekle sokabiliyorsunuz. Benim en çok kullandığım pattern şu:

# Instance ID ve isimlerini basit liste olarak al
aws ec2 describe-instances 
  --filters "Name=instance-state-name,Values=running" 
  --query 'Reservations[].Instances[].[InstanceId, Tags[?Key==`Name`] | [0].Value]' 
  --output text | column -t

Bu komut çıktısını pipe ile başka araçlara bağlayabiliyorsunuz. Mesela belirli bir instance grubunun IP listesini çekmek için:

# Production web sunucularının IP listesi
aws ec2 describe-instances 
  --filters 
    "Name=tag:Role,Values=webserver" 
    "Name=tag:Environment,Values=production" 
    "Name=instance-state-name,Values=running" 
  --query 'Reservations[].Instances[].PublicIpAddress' 
  --output text

Instance Başlatma ve Durdurma

Tek instance yönetmek basit, ama toplu işlemler yapmak istediğinizde script yazmak kaçınılmaz oluyor.

# Tek instance başlat
aws ec2 start-instances --instance-ids i-1234567890abcdef0

# Birden fazla instance'ı aynı anda başlat
aws ec2 start-instances 
  --instance-ids i-1234567890abcdef0 i-0987654321fedcba0 i-abcdef1234567890

# Instance durdur
aws ec2 stop-instances --instance-ids i-1234567890abcdef0

# Zorla durdur (normalde önerilmez ama bazen şart olur)
aws ec2 stop-instances --instance-ids i-1234567890abcdef0 --force

# Instance'ı sonlandır (terminate - geri dönüşü yok!)
aws ec2 terminate-instances --instance-ids i-1234567890abcdef0

Gerçek dünyada çok işime yarayan bir senaryo: Geliştirme ortamındaki tüm instance’ları mesai saatleri dışında durdurup sabah tekrar başlatmak. Bunu tag bazlı yapalım:

#!/bin/bash
# dev-instances-stop.sh
# Crontab: 0 19 * * 1-5 /opt/scripts/dev-instances-stop.sh

ENVIRONMENT="development"
REGION="eu-west-1"

echo "$(date): Dev instance'ları durduruluyor..."

INSTANCE_IDS=$(aws ec2 describe-instances 
  --region $REGION 
  --filters 
    "Name=tag:Environment,Values=$ENVIRONMENT" 
    "Name=instance-state-name,Values=running" 
  --query 'Reservations[].Instances[].InstanceId' 
  --output text)

if [ -z "$INSTANCE_IDS" ]; then
  echo "Durduracak instance bulunamadı."
  exit 0
fi

echo "Durdurulan instance'lar: $INSTANCE_IDS"
aws ec2 stop-instances --region $REGION --instance-ids $INSTANCE_IDS

echo "$(date): İşlem tamamlandı."

Yeni Instance Oluşturma

run-instances komutu oldukça kapsamlı parametreler alıyor. Temel kullanımdan başlayıp daha karmaşık yapılara gidelim:

# Basit instance oluşturma
aws ec2 run-instances 
  --image-id ami-0c02fb55956c7d316 
  --instance-type t3.micro 
  --key-name my-key-pair 
  --security-group-ids sg-12345678 
  --subnet-id subnet-12345678 
  --count 1 
  --tag-specifications 
    'ResourceType=instance,Tags=[{Key=Name,Value=web-server-01},{Key=Environment,Value=production}]'

Daha gerçekçi bir üretim senaryosu için, user-data ile birlikte instance başlatalım. Yeni bir nginx sunucusu kurduğumuzu varsayalım:

# User-data dosyası oluştur
cat > /tmp/user-data.sh << 'EOF'
#!/bin/bash
apt-get update -y
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
echo "Sunucu $(hostname) hazır" > /var/www/html/index.html
EOF

# Instance'ı user-data ile başlat
aws ec2 run-instances 
  --image-id ami-0c55b159cbfafe1f0 
  --instance-type t3.small 
  --key-name production-key 
  --security-group-ids sg-0123456789abcdef0 
  --subnet-id subnet-0123456789abcdef0 
  --iam-instance-profile Name=EC2-SSM-Role 
  --user-data file:///tmp/user-data.sh 
  --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3","DeleteOnTermination":true}}]' 
  --tag-specifications 
    'ResourceType=instance,Tags=[{Key=Name,Value=nginx-prod-01},{Key=Environment,Value=production},{Key=Role,Value=webserver}]' 
  --count 1

Instance oluşturulduktan sonra hazır olmasını beklemek için wait komutunu kullanabilirsiniz:

INSTANCE_ID="i-0123456789abcdef0"

echo "Instance başlatılıyor, bekleniyor..."
aws ec2 wait instance-running --instance-ids $INSTANCE_ID
echo "Instance çalışıyor!"

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

echo "Instance IP adresi: $PUBLIC_IP"

Security Group Yönetimi

EC2 yönetiminin ayrılmaz bir parçası güvenlik grupları. CLI ile security group oluşturmak ve kural eklemek oldukça pratik:

# Security group oluştur
SG_ID=$(aws ec2 create-security-group 
  --group-name "web-servers-sg" 
  --description "Web sunucuları için security group" 
  --vpc-id vpc-0123456789abcdef0 
  --query 'GroupId' 
  --output text)

echo "Oluşturulan SG ID: $SG_ID"

# HTTP ve HTTPS için inbound kuralı ekle
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --ip-permissions 
    'IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges=[{CidrIp=0.0.0.0/0,Description=HTTP}]' 
    'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=0.0.0.0/0,Description=HTTPS}]'

# SSH için ofis IP'sinden erişim
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --ip-permissions 
    'IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=203.0.113.0/24,Description=Ofis-VPN}]'

# Security group'u tag'le
aws ec2 create-tags 
  --resources $SG_ID 
  --tags Key=Name,Value=web-servers-sg Key=Environment,Value=production

Mevcut security group kurallarını gözden geçirmek için:

# Security group detaylarını listele
aws ec2 describe-security-groups 
  --group-ids $SG_ID 
  --query 'SecurityGroups[0].IpPermissions[]' 
  --output json

# Gereksiz bir kuralı sil
aws ec2 revoke-security-group-ingress 
  --group-id $SG_ID 
  --protocol tcp 
  --port 22 
  --cidr 0.0.0.0/0

EBS Volume Yönetimi

Disk yönetimi EC2 operasyonlarının önemli bir parçası. Snapshot alma, volume oluşturma ve instance’a bağlama işlemleri CLI ile çok hızlı yapılabiliyor:

# Mevcut instance'ın volume'larını listele
INSTANCE_ID="i-0123456789abcdef0"

aws ec2 describe-volumes 
  --filters "Name=attachment.instance-id,Values=$INSTANCE_ID" 
  --query 'Volumes[*].[VolumeId,Size,State,Attachments[0].Device]' 
  --output table

# Yeni volume oluştur
VOLUME_ID=$(aws ec2 create-volume 
  --availability-zone eu-west-1a 
  --size 100 
  --volume-type gp3 
  --iops 3000 
  --throughput 125 
  --tag-specifications 
    'ResourceType=volume,Tags=[{Key=Name,Value=data-volume-01}]' 
  --query 'VolumeId' 
  --output text)

# Volume hazır olana kadar bekle
aws ec2 wait volume-available --volume-ids $VOLUME_ID

# Volume'u instance'a bağla
aws ec2 attach-volume 
  --volume-id $VOLUME_ID 
  --instance-id $INSTANCE_ID 
  --device /dev/sdf

echo "Volume $VOLUME_ID, instance $INSTANCE_ID'ye bağlandı."

Snapshot yönetimi özellikle backup stratejisi için kritik. Benim kullandığım basit bir snapshot scripti:

#!/bin/bash
# snapshot-backup.sh - Production volume'larının günlük snapshot'ı

REGION="eu-west-1"
RETENTION_DAYS=7
TODAY=$(date +%Y-%m-%d)

# Production instance'larının volume'larını bul
VOLUME_IDS=$(aws ec2 describe-volumes 
  --region $REGION 
  --filters 
    "Name=tag:Environment,Values=production" 
    "Name=status,Values=in-use" 
  --query 'Volumes[].VolumeId' 
  --output text)

for VOLUME_ID in $VOLUME_IDS; do
  echo "Snapshot alınıyor: $VOLUME_ID"
  
  aws ec2 create-snapshot 
    --region $REGION 
    --volume-id $VOLUME_ID 
    --description "Otomatik backup - $TODAY" 
    --tag-specifications 
      "ResourceType=snapshot,Tags=[{Key=Name,Value=backup-$VOLUME_ID-$TODAY},{Key=AutoBackup,Value=true},{Key=RetentionDate,Value=$(date -d "+$RETENTION_DAYS days" +%Y-%m-%d)}]"
done

# Eski snapshot'ları temizle
echo "Eski snapshot'lar temizleniyor..."
OLD_SNAPSHOTS=$(aws ec2 describe-snapshots 
  --region $REGION 
  --owner-ids self 
  --filters "Name=tag:AutoBackup,Values=true" 
  --query "Snapshots[?StartTime<='$(date -d "-$RETENTION_DAYS days" --iso-8601)'].SnapshotId" 
  --output text)

for SNAPSHOT_ID in $OLD_SNAPSHOTS; do
  echo "Siliniyor: $SNAPSHOT_ID"
  aws ec2 delete-snapshot --region $REGION --snapshot-id $SNAPSHOT_ID
done

Instance Monitoring ve Durum Kontrolü

Canlı sistemlerde instance durumunu izlemek için CLI’yi monitoring araçlarıyla birlikte kullanabilirsiniz. Basit bir health check scripti:

#!/bin/bash
# health-check.sh - Production instance sağlık kontrolü

REGION="eu-west-1"

echo "=== Production Instance Sağlık Raporu - $(date) ==="

# Instance durumlarını kontrol et
aws ec2 describe-instance-status 
  --region $REGION 
  --include-all-instances 
  --query 'InstanceStatuses[*].[InstanceId,InstanceState.Name,SystemStatus.Status,InstanceStatus.Status]' 
  --output text | while read INSTANCE_ID STATE SYSTEM_STATUS INSTANCE_STATUS; do
    
    INSTANCE_NAME=$(aws ec2 describe-instances 
      --instance-ids $INSTANCE_ID 
      --query 'Reservations[0].Instances[0].Tags[?Key==`Name`].Value|[0]' 
      --output text 2>/dev/null)
    
    if [ "$SYSTEM_STATUS" != "ok" ] || [ "$INSTANCE_STATUS" != "ok" ]; then
      echo "UYARI: $INSTANCE_NAME ($INSTANCE_ID) - Durum: $STATE, Sistem: $SYSTEM_STATUS, Instance: $INSTANCE_STATUS"
    else
      echo "OK: $INSTANCE_NAME ($INSTANCE_ID) - $STATE"
    fi
done

# CloudWatch CPU metriklerini kontrol et
echo ""
echo "=== Yüksek CPU Kullanımı (>80%) ==="

INSTANCE_IDS=$(aws ec2 describe-instances 
  --region $REGION 
  --filters "Name=instance-state-name,Values=running" 
  --query 'Reservations[].Instances[].InstanceId' 
  --output text)

for INSTANCE_ID in $INSTANCE_IDS; do
  CPU_USAGE=$(aws cloudwatch get-metric-statistics 
    --region $REGION 
    --namespace AWS/EC2 
    --metric-name CPUUtilization 
    --dimensions Name=InstanceId,Value=$INSTANCE_ID 
    --start-time $(date -u -d "15 minutes ago" +%Y-%m-%dT%H:%M:%S) 
    --end-time $(date -u +%Y-%m-%dT%H:%M:%S) 
    --period 300 
    --statistics Average 
    --query 'Datapoints[0].Average' 
    --output text 2>/dev/null)
  
  if [ "$CPU_USAGE" != "None" ] && [ ! -z "$CPU_USAGE" ]; then
    CPU_INT=${CPU_USAGE%.*}
    if [ "$CPU_INT" -gt 80 ] 2>/dev/null; then
      echo "YUKSEK CPU: $INSTANCE_ID - %$CPU_USAGE"
    fi
  fi
done

Pratik Alias ve Fonksiyonlar

Günlük kullanımı kolaylaştırmak için .bashrc veya .zshrc dosyanıza ekleyebileceğiniz alias’lar:

# ~/.bashrc veya ~/.zshrc'e eklenecekler

# Çalışan instance'ları listele
alias ec2-list='aws ec2 describe-instances 
  --filters "Name=instance-state-name,Values=running" 
  --query "Reservations[].Instances[].[Tags[?Key==`Name`].Value|[0],InstanceId,InstanceType,PublicIpAddress,PrivateIpAddress]" 
  --output table'

# Instance'a SSH bağlan (isim ile)
ec2-ssh() {
  local INSTANCE_NAME=$1
  local KEY_PATH="${2:-~/.ssh/production-key.pem}"
  
  INSTANCE_IP=$(aws ec2 describe-instances 
    --filters 
      "Name=tag:Name,Values=$INSTANCE_NAME" 
      "Name=instance-state-name,Values=running" 
    --query 'Reservations[0].Instances[0].PublicIpAddress' 
    --output text)
  
  if [ "$INSTANCE_IP" = "None" ] || [ -z "$INSTANCE_IP" ]; then
    echo "Instance bulunamadı: $INSTANCE_NAME"
    return 1
  fi
  
  echo "$INSTANCE_NAME ($INSTANCE_IP) bağlanılıyor..."
  ssh -i "$KEY_PATH" -o StrictHostKeyChecking=no ubuntu@$INSTANCE_IP
}

# Instance'ı isimle bul
ec2-find() {
  aws ec2 describe-instances 
    --filters "Name=tag:Name,Values=*$1*" 
    --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value|[0],InstanceId,InstanceState.Name,PublicIpAddress]' 
    --output table
}

Bu fonksiyonları kullandıktan sonra ec2-ssh web-server-01 ya da ec2-find nginx gibi çok basit komutlarla işlem yapabiliyorsunuz. Ekip arkadaşlarımın favorisi bu fonksiyonlar oluyor her zaman.

AMI Yönetimi

Kendi özel AMI’larınızı oluşturmak ve yönetmek için:

# Mevcut instance'dan AMI oluştur
AMI_ID=$(aws ec2 create-image 
  --instance-id i-0123456789abcdef0 
  --name "web-server-base-$(date +%Y%m%d)" 
  --description "Web sunucu base image - $(date +%Y-%m-%d)" 
  --no-reboot 
  --tag-specifications 
    'ResourceType=image,Tags=[{Key=Environment,Value=production},{Key=AutoCreated,Value=true}]' 
  --query 'ImageId' 
  --output text)

echo "AMI oluşturuluyor: $AMI_ID"

# AMI hazır olana kadar bekle
aws ec2 wait image-available --image-ids $AMI_ID
echo "AMI hazır: $AMI_ID"

# Kendi AMI'larınızı listeleyin
aws ec2 describe-images 
  --owners self 
  --query 'Images[*].[ImageId,Name,CreationDate,State]' 
  --output table

# Eski AMI'ları temizle (örnek: 30 günden eski)
CUTOFF_DATE=$(date -d "30 days ago" +%Y-%m-%d)
aws ec2 describe-images 
  --owners self 
  --filters "Name=tag:AutoCreated,Values=true" 
  --query "Images[?CreationDate<='${CUTOFF_DATE}'].[ImageId]" 
  --output text | while read AMI_ID; do
    echo "Siliniyor: $AMI_ID"
    aws ec2 deregister-image --image-id $AMI_ID
done

Maliyet Optimizasyonu İçin CLI Kullanımı

CLI’nin önemli bir kullanım alanı maliyet analizi. Kullanılmayan kaynakları bulmak için:

# Durdurulan instance'ları listele (EBS bedeli devam ediyor!)
aws ec2 describe-instances 
  --filters "Name=instance-state-name,Values=stopped" 
  --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value|[0],InstanceId,InstanceType,LaunchTime]' 
  --output table

# Kullanılmayan elastic IP'leri bul (para gidiyor!)
aws ec2 describe-addresses 
  --query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' 
  --output table

# Bağlı olmayan EBS volume'larını bul
aws ec2 describe-volumes 
  --filters "Name=status,Values=available" 
  --query 'Volumes[*].[VolumeId,Size,VolumeType,CreateTime]' 
  --output table

Bu üç komut ayda birkaç yüz dolar fazladan ödemenizi engelleyebilir. Bir müşterimizde bu analizi yaptığımızda 47 tane orphan EBS volume ve 12 kullanılmayan Elastic IP bulduk, aylık 380 dolar tasarruf etmiş oldular.

Sonuç

AWS CLI ile EC2 yönetimi, başlangıçta biraz öğrenme eğrisi gerektiriyor ama bir kez oturduğunda konsol arayüzüne dönmek istemiyorsunuz. Özellikle şu noktalarda CLI’nin farkı çok belirgin oluyor:

  • Toplu işlemler: Onlarca instance üzerinde aynı anda işlem yapmak
  • Otomasyon: Cron job’larla backup, start/stop otomasyonu
  • Tekrarlanabilirlik: Aynı altyapıyı defalarca tutarlı şekilde kurabilmek
  • Hız: Bir kez yazdığınız komutları alias haline getirip saniyeler içinde çalıştırmak
  • Maliyet kontrolü: Kullanılmayan kaynakları programatik olarak tespit etmek

--query ve JMESPath’i iyi öğrenmek en büyük yatırımınız olacak. İlk başta karmaşık gelebilir ama AWS dökümantasyonundaki örneklerle pratik yaparak birkaç haftada rahat kullanmaya başlayabilirsiniz.

Son olarak şunu söylemeliyim: CLI komutlarınızı mutlaka bir Git reposunda saklayın. Hem kendiniz ileriye dönük kullanırsınız, hem de ekip arkadaşlarınızla paylaşabilirsiniz. Bir Bash script dosyası, en iyi dokümantasyon bazen.

Bir yanıt yazın

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