AWS RDS MySQL Kurulumu ve Yapılandırması

Üretim ortamında bir veritabanı kurarken en çok korkulan şey nedir? Yanlış yapılandırma, güvenlik açıkları, beklenmedik maliyetler… AWS RDS MySQL bu kaygıların büyük bölümünü ortadan kaldırmak için tasarlanmış yönetilen bir servis. Ama “yönetilen” demek “yapılandırmana gerek yok” demek değil. Aksine, doğru yapılandırılmamış bir RDS instance’ı hem cebini yakar hem de seni gece 3’te uyandırır. Bu yazıda sıfırdan başlayarak production-ready bir RDS MySQL kurulumu yapacağız, güvenlik gruplarından parametre gruplarına, yedekleme stratejilerinden monitoring’e kadar her şeyi ele alacağız.

RDS MySQL’e Neden Geçmeli?

Self-managed MySQL ile karşılaştırdığında RDS’in getirdiği en büyük avantaj operasyonel yükün azalmasıdır. Patch yönetimi, otomatik failover, point-in-time recovery, Multi-AZ replikasyonu bunların hepsi AWS tarafından yönetiliyor. Sen sadece veritabanı tasarımı ve uygulama performansına odaklanıyorsun.

Gerçek bir senaryo vermek gerekirse: 50 sunuculu bir e-ticaret platformu düşün. Her ay MySQL upgrade’leri, binary log yönetimi, slave replication sorunları derken DBA ekibi sürekli firefighting yapıyordu. RDS’e geçişin ardından bu operasyonel yük neredeyse sıfıra indi. Evet, maliyet biraz arttı, ama mühendislerin zamanı daha değerli işlere kaydı.

Tabii RDS’in de sınırlamaları var. Superuser yetkisi yok, bazı sistem değişkenlerine erişim kısıtlı, custom storage engine kullanamıyorsun. Bunları göz önünde bulundurarak karar vermek gerekiyor.

Ön Hazırlık: VPC ve Subnet Yapısı

RDS instance’ını oluşturmadan önce network altyapısını hazırlamak gerekiyor. Çoğu kişi bu adımı atlıyor ve sonradan sorun yaşıyor.

Önce mevcut VPC yapını kontrol et:

# Mevcut VPC'leri listele
aws ec2 describe-vpcs 
  --query 'Vpcs[*].{ID:VpcId,CIDR:CidrBlock,Name:Tags[?Key==`Name`].Value|[0]}' 
  --output table

# Subnet'leri listele - RDS için en az 2 farklı AZ'de subnet gerekli
aws ec2 describe-subnets 
  --query 'Subnets[*].{ID:SubnetId,AZ:AvailabilityZone,CIDR:CidrBlock,Public:MapPublicIpOnLaunch}' 
  --output table

RDS için DB Subnet Group oluşturmak zorunlu. Bu grup, RDS’in hangi subnet’lere yerleştirilebileceğini tanımlıyor:

# DB Subnet Group oluştur - private subnet'leri kullan
aws rds create-db-subnet-group 
  --db-subnet-group-name production-db-subnet-group 
  --db-subnet-group-description "Production RDS MySQL Subnet Group" 
  --subnet-ids subnet-0abc123def456789a subnet-0xyz987wvu654321b 
  --tags Key=Environment,Value=production Key=Team,Value=backend

Kritik nokta: RDS’i asla public subnet’e koyma. Internet’ten doğrudan erişilebilir bir veritabanı ciddi güvenlik riski oluşturuyor.

Security Group Yapılandırması

Security group yapılandırması çoğu zaman hatalı yapılıyor. Ya çok kısıtlayıcı olup uygulama sunucularının bağlanamamasına neden oluyor, ya da 0.0.0.0/0’a açık bırakılıp güvenlik deliği yaratılıyor.

# RDS için özel security group oluştur
aws ec2 create-security-group 
  --group-name rds-mysql-production-sg 
  --description "Production RDS MySQL Security Group" 
  --vpc-id vpc-0abc123def456789

# Security group ID'yi al
SG_ID=$(aws ec2 describe-security-groups 
  --filters "Name=group-name,Values=rds-mysql-production-sg" 
  --query 'SecurityGroups[0].GroupId' 
  --output text)

# Sadece uygulama sunucularının security group'undan MySQL portuna izin ver
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --protocol tcp 
  --port 3306 
  --source-group sg-app-server-security-group-id

# Bastion host'tan erişim için (yönetim amaçlı)
aws ec2 authorize-security-group-ingress 
  --group-id $SG_ID 
  --protocol tcp 
  --port 3306 
  --source-group sg-bastion-host-security-group-id

Parameter Group Oluşturma ve Optimizasyon

Default parameter group’ları üretim ortamı için yeterince optimize edilmemiş. Özelleştirilmiş bir parameter group oluşturmak hem performans hem de güvenlik açısından önemli.

# Custom parameter group oluştur
aws rds create-db-parameter-group 
  --db-parameter-group-name mysql80-production-params 
  --db-parameter-group-family mysql8.0 
  --description "Optimized MySQL 8.0 Parameters for Production"

# Kritik parametreleri ayarla
aws rds modify-db-parameter-group 
  --db-parameter-group-name mysql80-production-params 
  --parameters 
    "ParameterName=innodb_buffer_pool_size,ParameterValue={DBInstanceClassMemory*3/4},ApplyMethod=pending-reboot" 
    "ParameterName=max_connections,ParameterValue=500,ApplyMethod=pending-reboot" 
    "ParameterName=slow_query_log,ParameterValue=1,ApplyMethod=immediate" 
    "ParameterName=long_query_time,ParameterValue=2,ApplyMethod=immediate" 
    "ParameterName=log_queries_not_using_indexes,ParameterValue=1,ApplyMethod=immediate" 
    "ParameterName=innodb_log_file_size,ParameterValue=268435456,ApplyMethod=pending-reboot" 
    "ParameterName=character_set_server,ParameterValue=utf8mb4,ApplyMethod=pending-reboot" 
    "ParameterName=collation_server,ParameterValue=utf8mb4_unicode_ci,ApplyMethod=pending-reboot"

Önemli parametreler hakkında kısa açıklama:

  • innodb_buffer_pool_size: InnoDB’nin RAM’de tutabileceği veri miktarı, toplam RAM’in %75’i genellikle iyi bir başlangıç noktası
  • max_connections: Aynı anda açılabilecek maksimum bağlantı sayısı, instance boyutuna göre ayarla
  • slow_query_log: Yavaş sorguları logla, üretimde mutlaka aktif olmalı
  • long_query_time: 2 saniyeden uzun süren sorgular slow log’a düşsün
  • character_set_server: Türkçe karakter desteği için utf8mb4 kullan, utf8 değil

RDS Instance Oluşturma

Artık altyapı hazır, instance’ı oluşturabiliriz. Burada dikkat edilmesi gereken onlarca parametre var:

# Production RDS MySQL instance oluştur
aws rds create-db-instance 
  --db-instance-identifier production-mysql-01 
  --db-instance-class db.r6g.xlarge 
  --engine mysql 
  --engine-version 8.0.35 
  --master-username admin 
  --master-user-password "$(openssl rand -base64 32)" 
  --allocated-storage 200 
  --max-allocated-storage 1000 
  --storage-type gp3 
  --iops 3000 
  --storage-encrypted 
  --kms-key-id arn:aws:kms:eu-west-1:123456789:key/your-kms-key-id 
  --db-subnet-group-name production-db-subnet-group 
  --vpc-security-group-ids $SG_ID 
  --db-parameter-group-name mysql80-production-params 
  --backup-retention-period 7 
  --preferred-backup-window "02:00-03:00" 
  --preferred-maintenance-window "sun:04:00-sun:05:00" 
  --multi-az 
  --auto-minor-version-upgrade false 
  --deletion-protection 
  --enable-performance-insights 
  --performance-insights-retention-period 7 
  --enable-cloudwatch-logs-exports '["error","general","slowquery","audit"]' 
  --monitoring-interval 60 
  --monitoring-role-arn arn:aws:iam::123456789:role/rds-monitoring-role 
  --tags Key=Environment,Value=production Key=Project,Value=ecommerce Key=Owner,Value=dba-team

Bu komuttaki kritik seçimleri açıklayalım:

  • db.r6g.xlarge: Memory-optimized instance, MySQL için r serisi tercih edilmeli
  • gp3 storage: gp2’ye göre daha iyi performans ve maliyet oranı, baseline 3000 IOPS ücretsiz
  • storage-encrypted: KMS ile şifreleme, production’da zorunlu
  • multi-az: Otomatik failover için aktif olmalı, maliyet iki katına çıkıyor ama değer
  • auto-minor-version-upgrade false: Minor upgrade’leri otomatik yaptırma, test et sonra geçir
  • deletion-protection: Yanlışlıkla silme koruması, production’da her zaman aktif

Instance’ın hazır olmasını bekle:

# Instance durumunu izle
aws rds wait db-instance-available 
  --db-instance-identifier production-mysql-01

# Instance bilgilerini al
aws rds describe-db-instances 
  --db-instance-identifier production-mysql-01 
  --query 'DBInstances[0].{Endpoint:Endpoint.Address,Port:Endpoint.Port,Status:DBInstanceStatus,AZ:AvailabilityZone,MultiAZ:MultiAZ}' 
  --output json

İlk Bağlantı ve Güvenli Kullanıcı Yönetimi

Instance hazır olduğunda master kullanıcıyla bağlanıp gerekli kullanıcıları oluşturuyoruz. Master kullanıcıyı uygulama bağlantıları için kullanmak kötü bir pratik.

# Bastion host üzerinden bağlan
mysql -h production-mysql-01.cluster-xxxxx.eu-west-1.rds.amazonaws.com 
  -P 3306 
  -u admin 
  -p

MySQL’e girdikten sonra:

-- Uygulama için sınırlı yetkili kullanıcı oluştur
CREATE USER 'app_user'@'%' IDENTIFIED BY 'güçlü_şifre_buraya';
GRANT SELECT, INSERT, UPDATE, DELETE ON ecommerce.* TO 'app_user'@'%';

-- Read replica veya raporlama için read-only kullanıcı
CREATE USER 'reporting_user'@'%' IDENTIFIED BY 'başka_güçlü_şifre';
GRANT SELECT ON ecommerce.* TO 'reporting_user'@'%';

-- Migration işlemleri için geçici kullanıcı (kullandıktan sonra sil)
CREATE USER 'migration_user'@'%' IDENTIFIED BY 'geçici_şifre';
GRANT ALL PRIVILEGES ON ecommerce.* TO 'migration_user'@'%';

-- Değişiklikleri uygula
FLUSH PRIVILEGES;

-- Kullanıcıları doğrula
SELECT User, Host, authentication_string FROM mysql.user WHERE User NOT LIKE 'mysql%';

Önemli güvenlik notu: AWS RDS’de SUPER yetkisi master kullanıcıda bile kısıtlı. Bu yüzden bazı komutlar çalışmayabilir. Örneğin SHOW GRANTS FOR başka bir kullanıcı için çalışıyor ama SET GLOBAL gibi bazı komutlar parameter group üzerinden yapılmalı.

Read Replica Yapılandırması

Okuma yoğunluklu uygulamalarda read replica büyük fark yaratıyor. E-ticaret senaryomuzda ürün listeleme, arama gibi operasyonlar read replica’ya yönlendirildi, yazma operasyonları master’a gitti.

# Read replica oluştur
aws rds create-db-instance-read-replica 
  --db-instance-identifier production-mysql-01-replica 
  --source-db-instance-identifier production-mysql-01 
  --db-instance-class db.r6g.large 
  --availability-zone eu-west-1b 
  --storage-encrypted 
  --enable-performance-insights 
  --performance-insights-retention-period 7 
  --tags Key=Environment,Value=production Key=Type,Value=read-replica

# Replica'nın hazır olmasını bekle
aws rds wait db-instance-available 
  --db-instance-identifier production-mysql-01-replica

Uygulama tarafında connection pooler (örneğin ProxySQL veya AWS RDS Proxy) kullanarak okuma/yazma ayrımını otomatize edebilirsin. Bu pattern production’da çok işe yarıyor.

CloudWatch Alarmları ve Monitoring

RDS’i kurdun, ama izlemiyorsan bir anlamı yok. Şu metriklere mutlaka alarm kurmalısın:

# CPU alarmı
aws cloudwatch put-metric-alarm 
  --alarm-name "RDS-Production-HighCPU" 
  --alarm-description "RDS CPU %80 ustu" 
  --metric-name CPUUtilization 
  --namespace AWS/RDS 
  --statistic Average 
  --period 300 
  --threshold 80 
  --comparison-operator GreaterThanThreshold 
  --dimensions Name=DBInstanceIdentifier,Value=production-mysql-01 
  --evaluation-periods 2 
  --alarm-actions arn:aws:sns:eu-west-1:123456789:ops-alerts 
  --ok-actions arn:aws:sns:eu-west-1:123456789:ops-alerts

# Free storage alarmı - 20GB altına düşünce uyar
aws cloudwatch put-metric-alarm 
  --alarm-name "RDS-Production-LowStorage" 
  --alarm-description "RDS free storage 20GB altina dustu" 
  --metric-name FreeStorageSpace 
  --namespace AWS/RDS 
  --statistic Average 
  --period 300 
  --threshold 21474836480 
  --comparison-operator LessThanThreshold 
  --dimensions Name=DBInstanceIdentifier,Value=production-mysql-01 
  --evaluation-periods 1 
  --alarm-actions arn:aws:sns:eu-west-1:123456789:ops-alerts

# Database connections alarmı
aws cloudwatch put-metric-alarm 
  --alarm-name "RDS-Production-HighConnections" 
  --alarm-description "RDS baglanti sayisi 400 ustunde" 
  --metric-name DatabaseConnections 
  --namespace AWS/RDS 
  --statistic Average 
  --period 60 
  --threshold 400 
  --comparison-operator GreaterThanThreshold 
  --dimensions Name=DBInstanceIdentifier,Value=production-mysql-01 
  --evaluation-periods 3 
  --alarm-actions arn:aws:sns:eu-west-1:123456789:ops-alerts

Bu alarmlara ek olarak izlemeni öneririm:

  • FreeableMemory: Kullanılabilir bellek azalıyorsa instance boyutunu artırma zamanı
  • ReadLatency / WriteLatency: I/O gecikmeleri artıyorsa IOPS’u artır ya da storage tipini değiştir
  • ReplicaLag: Read replica geride kalıyorsa sorun var demek
  • BinLogDiskUsage: Binary log’lar disk doluyorsa tehlike sinyali

Yedekleme ve Point-in-Time Recovery

RDS’in otomatik backup’ı var ama bunu doğrulaman ve test etmen gerekiyor. Ayrıca manual snapshot almayı da bilmen lazım:

# Manual snapshot al - büyük deployment öncesi mutlaka yap
aws rds create-db-snapshot 
  --db-instance-identifier production-mysql-01 
  --db-snapshot-identifier production-mysql-01-pre-deployment-$(date +%Y%m%d-%H%M)

# Snapshot durumunu kontrol et
aws rds describe-db-snapshots 
  --db-instance-identifier production-mysql-01 
  --query 'DBSnapshots[*].{ID:DBSnapshotIdentifier,Status:Status,Created:SnapshotCreateTime,Size:AllocatedStorage}' 
  --output table

# Point-in-time recovery - veritabanını belirli bir zamana geri al
aws rds restore-db-instance-to-point-in-time 
  --source-db-instance-identifier production-mysql-01 
  --target-db-instance-identifier production-mysql-01-restored 
  --restore-time 2024-01-15T14:30:00Z 
  --db-instance-class db.r6g.xlarge 
  --db-subnet-group-name production-db-subnet-group 
  --vpc-security-group-ids $SG_ID

Point-in-time recovery’yi test etmeden güvenmemen gerekiyor. Ayda bir kez bu komutu çalıştır, restore işleminin çalıştığını doğrula, verilerin tutarlı olduğunu kontrol et, sonra bu test instance’ını sil. Bu test yapmadan “yedeklerim var” demek kendini kandırmak.

Maliyet Optimizasyonu

RDS maliyetleri kontrolsüz büyüyebilir. Birkaç pratik ipucu:

Storage autoscaling’i etkinleştirirken maksimum limiti gerçekçi tut. 200GB başlangıç verip 1TB maksimum koyarsan ve uygulaman veri sızdırıyorsa faturayı görünce şok olabilirsin.

Dev/test ortamları için instance’ı otomatik durdur ve başlat:

# Dev ortamı için otomatik durdurma (maksimum 7 gün durdurulabilir, sonra otomatik başlar)
aws rds stop-db-instance 
  --db-instance-identifier dev-mysql-01

# Sabah çalışma başlamadan önce başlat
aws rds start-db-instance 
  --db-instance-identifier dev-mysql-01

Bu işlemi Lambda + EventBridge ile otomatize ederek mesai saatleri dışında dev veritabanlarını kapatabilirsin. Büyük bir organizasyonda bu aylık yüzlerce dolar tasarruf sağlıyor.

Reserved instance satın almak da önemli bir maliyet avantajı. 1 yıllık rezervasyon yaklaşık %30-40, 3 yıllık rezervasyon yaklaşık %50-60 tasarruf sağlıyor. Production’da uzun süreli kullanacaksan mutlaka değerlendir.

Sık Karşılaşılan Sorunlar ve Çözümleri

Bağlantı zaman aşımı sorunu: Genellikle security group yanlış yapılandırılmış. Uygulama sunucusunun security group ID’sini değil, IP adresini kural olarak ekleme yanlışlığı sık yapılıyor. IP tabanlı kurallar deployment sırasında değişebilir, her zaman security group referansı kullan.

“Too many connections” hatası: max_connections parametreni artır veya daha önemlisi uygulama tarafında connection pooling uygula. PgBouncer veya ProxySQL bu iş için mükemmel. AWS RDS Proxy da iyi bir seçenek, özellikle Lambda gibi serverless ortamlarda.

Yavaş backup penceresinde performans düşüşü: Backup penceresini trafiğin en az olduğu saate al. Multi-AZ kullanıyorsan backup standby’dan alınır, master etkilenmez.

Character set sorunları: Türkçe karakter sorunlarının büyük bölümü utf8 vs utf8mb4 karışıklığından kaynaklanıyor. Parameter group’da character_set_server ve collation_server’ı düzgün ayarla, mevcut tablolarda da ALTER TABLE ile dönüşüm yap.

Güvenlik Sertleştirme Kontrol Listesi

Production’a almadan önce şu maddeleri tek tek işaretle:

  • Public accessibility kapalı mı kontrol et
  • Şifreleme at-rest ve in-transit aktif mi doğrula
  • Master kullanıcı adı olarak “admin” veya “root” kullanmaktan kaçın
  • IAM authentication’ı etkinleştir, böylece şifre yerine IAM token kullanabilirsin
  • CloudTrail ile tüm API çağrılarını logla
  • Deletion protection aktif mi kontrol et
  • Secrets Manager’ı veritabanı şifrelerini yönetmek için kullan, şifreleri hiçbir zaman kod içinde bırakma

IAM authentication şöyle etkinleştirilir:

# IAM authentication'ı etkinleştir
aws rds modify-db-instance 
  --db-instance-identifier production-mysql-01 
  --enable-iam-database-authentication 
  --apply-immediately

# IAM auth için MySQL kullanıcısı oluştur (MySQL'de)
# CREATE USER 'iam_user'@'%' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';

Sonuç

AWS RDS MySQL kurulumu ilk bakışta birkaç tıklamayla halledilecekmiş gibi görünse de production-ready bir yapı oluşturmak ciddi planlama ve yapılandırma gerektiriyor. VPC ve subnet yapısından başlayıp security group’lara, parameter group optimizasyonundan monitoring’e kadar her adım önemli.

Bu yazıda anlattıklarımı özetlemek gerekirse: veritabanını hiçbir zaman public subnet’e koyma, master kullanıcıyı uygulama bağlantıları için kullanma, Multi-AZ’yi production’da her zaman aktif tut, backup’larını düzenli test et ve maliyetleri kontrol altında tutmak için reserved instance ve otomatik durdurma seçeneklerini değerlendir.

RDS’in en büyük gücü, doğru kurulduğunda seni birçok operasyonel dertten kurtarması. Ama bu “kurup bırakabileceğin” bir servis değil. Monitoring’e, performance insights’a düzenli bak, slow query log’larını analiz et, storage ve connection trendlerini takip et. Proaktif olmak, saat 3’te pager’ın çalmasından her zaman iyidir.

Bir yanıt yazın

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