AWS RDS PostgreSQL Kurulumu ve Yapılandırması
Bulut ortamlarında veritabanı yönetimi artık çoğu şirketin günlük rutininin bir parçası haline geldi. AWS RDS PostgreSQL, özellikle yönetim yükünü azaltmak isteyen ekipler için güçlü bir seçenek sunuyor. Kendi sunucunda PostgreSQL kurup yönetmek yerine, yedekleme, patch yönetimi ve yüksek erişilebilirlik gibi operasyonel ağırlıkları AWS’ye devretmek çekici geliyor. Ama bu “yönetilen servis” kavramı bazen yanıltıcı olabiliyor. AWS her şeyi otomatik yaparken, yanlış yapılandırılmış bir instance, beklenmedik maliyetlere ve performans sorunlarına yol açabiliyor. Bu yazıda sıfırdan bir RDS PostgreSQL instance’ı kuracağız, güvenliğini ayarlayacağız, izlemeyi yapılandıracağız ve production ortamında dikkat etmeniz gereken kritik noktaları ele alacağız.
Başlamadan Önce: Temel Kavramlar
AWS RDS üzerinde çalışmadan önce birkaç temel kavramı netleştirmek gerekiyor. RDS PostgreSQL, standart bir PostgreSQL kurulumundan farklı olarak bazı kısıtlamalar getiriyor. superuser yetkisine sahip olamıyorsunuz, bunun yerine rds_superuser rolü kullanılıyor. Bazı sistem seviyesi işlemler, yani pg_hba.conf düzenleme veya dosya sistemi erişimi, doğrudan mümkün değil.
Bununla birlikte şunları kazanıyorsunuz:
- Otomatik yedekleme: Point-in-time recovery ile belirli bir ana dönebilirsiniz
- Multi-AZ deployment: Otomatik failover ile yüksek erişilebilirlik
- Read replica: Okuma yükünü dağıtmak için kolayca replica oluşturabilirsiniz
- Storage autoscaling: Disk dolmadan önce otomatik genişleme
- IAM entegrasyonu: Veritabanı erişimini IAM ile yönetme imkanı
VPC ve Network Altyapısını Hazırlamak
Her şeyden önce, RDS instance’ınızın nerede çalışacağını belirlemeniz gerekiyor. Production ortamı için RDS’i asla public subnet’e koymayın. Bu, sık yapılan ve ciddi güvenlik açığı oluşturan bir hata.
Önce Terraform ile VPC ve subnet yapısını oluşturalım. Eğer Terraform kullanmıyorsanız, AWS Console üzerinden aynı adımları takip edebilirsiniz, ancak infrastructure-as-code kullanımını şiddetle tavsiye ediyorum.
# AWS CLI ile mevcut VPC'leri listele
aws ec2 describe-vpcs
--query 'Vpcs[*].{VpcId:VpcId,CidrBlock:CidrBlock,Name:Tags[?Key==`Name`].Value|[0]}'
--output table
# RDS için özel subnet group oluştur
aws rds create-db-subnet-group
--db-subnet-group-name production-rds-subnet-group
--db-subnet-group-description "Production RDS Subnet Group"
--subnet-ids subnet-0a1b2c3d4e5f6789a subnet-0b2c3d4e5f6789ab1
--tags Key=Environment,Value=production Key=ManagedBy,Value=terraform
Subnet group oluştururken dikkat etmeniz gereken nokta, en az iki farklı Availability Zone’da subnet belirtmeniz gerektiğidir. Multi-AZ deployment için bu zorunlu, single-AZ için bile AWS bunu öneriyor.
Security Group Yapılandırması
Security group, RDS’in güvenliğinin en kritik parçası. Sık yapılan hata, 0.0.0.0/0 kaynak adresiyle 5432 portunu açmak. Bunu asla yapmayın.
# RDS için security group oluştur
aws ec2 create-security-group
--group-name rds-postgresql-sg
--description "Security group for RDS PostgreSQL"
--vpc-id vpc-0123456789abcdef0
# Sadece uygulama sunucularının security group'undan erişime izin ver
aws ec2 authorize-security-group-ingress
--group-id sg-0rds1234567890abc
--protocol tcp
--port 5432
--source-group sg-0app1234567890abc
# Bastion host üzerinden yönetim erişimi için
aws ec2 authorize-security-group-ingress
--group-id sg-0rds1234567890abc
--protocol tcp
--port 5432
--source-group sg-0bastion1234567890
RDS Instance Oluşturma
Network altyapısı hazır olduktan sonra instance’ı oluşturabiliriz. AWS Console yerine CLI kullanmak, tüm parametreleri görmenizi ve tekrar kullanılabilir script’ler oluşturmanızı sağlıyor.
# Parameter group oluştur (özel PostgreSQL ayarları için)
aws rds create-db-parameter-group
--db-parameter-group-name production-postgres15-params
--db-parameter-group-family postgres15
--description "Custom parameters for production PostgreSQL 15"
# Kritik parametreleri ayarla
aws rds modify-db-parameter-group
--db-parameter-group-name production-postgres15-params
--parameters
"ParameterName=shared_buffers,ParameterValue={DBInstanceClassMemory/32768},ApplyMethod=pending-reboot"
"ParameterName=work_mem,ParameterValue=65536,ApplyMethod=immediate"
"ParameterName=maintenance_work_mem,ParameterValue=524288,ApplyMethod=immediate"
"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate"
"ParameterName=log_connections,ParameterValue=1,ApplyMethod=immediate"
"ParameterName=log_disconnections,ParameterValue=1,ApplyMethod=immediate"
Şimdi asıl instance’ı oluşturalım:
# RDS PostgreSQL instance oluştur
aws rds create-db-instance
--db-instance-identifier production-postgres-01
--db-instance-class db.r6g.large
--engine postgres
--engine-version 15.4
--master-username dbadmin
--master-user-password "$(aws secretsmanager get-secret-value --secret-id prod/rds/master-password --query SecretString --output text)"
--allocated-storage 100
--max-allocated-storage 500
--storage-type gp3
--storage-encrypted
--kms-key-id arn:aws:kms:eu-west-1:123456789:key/mrk-abc123
--db-subnet-group-name production-rds-subnet-group
--vpc-security-group-ids sg-0rds1234567890abc
--db-parameter-group-name production-postgres15-params
--backup-retention-period 7
--preferred-backup-window "02:00-03:00"
--preferred-maintenance-window "sun:04:00-sun:05:00"
--multi-az
--no-publicly-accessible
--deletion-protection
--enable-performance-insights
--performance-insights-retention-period 7
--enable-cloudwatch-logs-exports postgresql upgrade
--tags Key=Environment,Value=production Key=Team,Value=backend
Burada dikkat edilmesi gereken birkaç önemli parametre var:
- –multi-az: Production için mutlaka aktif edin. Maliyet artar ama downtime riskini minimize eder
- –deletion-protection: Yanlışlıkla silmeye karşı koruma. Silmeden önce bu özelliği kapatmanız gerekir
- –storage-encrypted: KMS ile şifreleme. KVKK ve GDPR uyumu için zorunlu kabul edin
- –max-allocated-storage: Storage autoscaling üst limiti. Sınırsız bırakmayın, kontrolsüz maliyet artışına yol açar
- –enable-performance-insights: Sorgu performansını analiz etmek için vazgeçilmez
Şifre Yönetimi: AWS Secrets Manager Entegrasyonu
Master şifreyi doğrudan CLI’a yazmak yerine Secrets Manager kullanmak best practice. Üstelik RDS artık Secrets Manager ile native entegrasyon sunuyor.
# Master şifreyi Secrets Manager'a kaydet
aws secretsmanager create-secret
--name prod/rds/postgres-master
--description "RDS PostgreSQL master credentials"
--secret-string '{"username":"dbadmin","password":"SuperSecurePassword123!"}'
--kms-key-id arn:aws:kms:eu-west-1:123456789:key/mrk-abc123
# Uygulama için ayrı bir kullanıcı şifresi
aws secretsmanager create-secret
--name prod/rds/app-user
--description "Application database user credentials"
--secret-string '{"username":"appuser","password":"AnotherSecurePassword456!","host":"production-postgres-01.cluster.eu-west-1.rds.amazonaws.com","port":"5432","dbname":"appdb"}'
Uygulamalarınızın Secrets Manager’dan şifreyi otomatik alması için IAM policy oluşturun:
# Uygulama için IAM policy
cat > app-rds-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": [
"arn:aws:secretsmanager:eu-west-1:123456789:secret:prod/rds/app-user*"
]
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"arn:aws:kms:eu-west-1:123456789:key/mrk-abc123"
]
}
]
}
EOF
aws iam create-policy
--policy-name AppRDSAccessPolicy
--policy-document file://app-rds-policy.json
Veritabanı ve Kullanıcı Yapılandırması
Instance ayağa kalktıktan sonra bastion host veya VPN üzerinden bağlanarak veritabanı ve kullanıcıları oluşturmanız gerekiyor. RDS endpoint’ini öğrenmek için:
# Instance durumunu ve endpoint bilgisini al
aws rds describe-db-instances
--db-instance-identifier production-postgres-01
--query 'DBInstances[0].{Status:DBInstanceStatus,Endpoint:Endpoint.Address,Port:Endpoint.Port}'
# Bastion üzerinden bağlantı testi
psql -h production-postgres-01.abcdef123456.eu-west-1.rds.amazonaws.com
-U dbadmin
-d postgres
-c "SELECT version();"
Bağlandıktan sonra production için gerekli kullanıcı ve veritabanı yapısını oluşturalım:
# PostgreSQL içinde çalıştırılacak komutlar
psql -h $RDS_ENDPOINT -U dbadmin -d postgres << 'PSQL'
-- Uygulama veritabanı oluştur
CREATE DATABASE appdb
ENCODING 'UTF8'
LC_COLLATE 'en_US.UTF-8'
LC_CTYPE 'en_US.UTF-8';
-- Read-only kullanıcı (raporlama için)
CREATE USER readonly_user WITH PASSWORD 'ReadOnlyPassword789!';
-- Uygulama kullanıcısı
CREATE USER appuser WITH PASSWORD 'AppPassword321!';
-- Uygulama kullanıcısına gerekli yetkiler
GRANT CONNECT ON DATABASE appdb TO appuser;
c appdb
GRANT USAGE ON SCHEMA public TO appuser;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO appuser;
-- Gelecekte oluşturulacak tablolar için default privilege
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO appuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO appuser;
-- Read-only kullanıcı için
GRANT CONNECT ON DATABASE appdb TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly_user;
-- pg_stat_statements extension ekle (sorgu istatistikleri için)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
PSQL
CloudWatch Alarmleri ve İzleme
RDS’i ayağa kaldırmak işin yarısı. Asıl önemli olan, sorunları oluşmadan önce fark etmek. CloudWatch alarmlarını mutlaka yapılandırın.
# CPU kullanımı için alarm
aws cloudwatch put-metric-alarm
--alarm-name "RDS-CPU-High-production-postgres-01"
--alarm-description "RDS CPU usage above 80%"
--metric-name CPUUtilization
--namespace AWS/RDS
--statistic Average
--period 300
--threshold 80
--comparison-operator GreaterThanThreshold
--evaluation-periods 3
--dimensions Name=DBInstanceIdentifier,Value=production-postgres-01
--alarm-actions arn:aws:sns:eu-west-1:123456789:production-alerts
--ok-actions arn:aws:sns:eu-west-1:123456789:production-alerts
# Boş depolama alanı için alarm (10GB altına düşünce)
aws cloudwatch put-metric-alarm
--alarm-name "RDS-Storage-Low-production-postgres-01"
--alarm-description "RDS free storage below 10GB"
--metric-name FreeStorageSpace
--namespace AWS/RDS
--statistic Average
--period 300
--threshold 10737418240
--comparison-operator LessThanThreshold
--evaluation-periods 2
--dimensions Name=DBInstanceIdentifier,Value=production-postgres-01
--alarm-actions arn:aws:sns:eu-west-1:123456789:production-alerts
# Bağlantı sayısı için alarm
aws cloudwatch put-metric-alarm
--alarm-name "RDS-Connections-High-production-postgres-01"
--alarm-description "RDS connection count above 400"
--metric-name DatabaseConnections
--namespace AWS/RDS
--statistic Average
--period 60
--threshold 400
--comparison-operator GreaterThanThreshold
--evaluation-periods 5
--dimensions Name=DBInstanceIdentifier,Value=production-postgres-01
--alarm-actions arn:aws:sns:eu-west-1:123456789:production-alerts
Read Replica Oluşturma
Okuma yoğun workload’larda single instance yetmeyebiliyor. Read replica ile okuma isteklerini dağıtabilirsiniz. Raporlama sorgularını ana instance’a yönlendirmek yerine replica’ya yönlendirmek, production performansını ciddi ölçüde artırıyor.
# Read replica oluştur
aws rds create-db-instance-read-replica
--db-instance-identifier production-postgres-01-replica
--source-db-instance-identifier production-postgres-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=Role,Value=read-replica
# Replica'nın lag'ini izle
aws cloudwatch put-metric-alarm
--alarm-name "RDS-ReplicaLag-High"
--alarm-description "Read replica lag above 30 seconds"
--metric-name ReplicaLag
--namespace AWS/RDS
--statistic Average
--period 60
--threshold 30
--comparison-operator GreaterThanThreshold
--evaluation-periods 3
--dimensions Name=DBInstanceIdentifier,Value=production-postgres-01-replica
--alarm-actions arn:aws:sns:eu-west-1:123456789:production-alerts
Backup ve Recovery Stratejisi
Otomatik yedekleme aktif olsa bile, önemli değişikliklerden önce manuel snapshot almak iyi bir alışkanlık.
# Manuel snapshot al
aws rds create-db-snapshot
--db-instance-identifier production-postgres-01
--db-snapshot-identifier production-postgres-01-before-migration-20241215
# Snapshot durumunu kontrol et
aws rds describe-db-snapshots
--db-snapshot-identifier production-postgres-01-before-migration-20241215
--query 'DBSnapshots[0].{Status:Status,Progress:PercentProgress,Size:AllocatedStorage}'
# Point-in-time recovery - belirli bir ana dön
aws rds restore-db-instance-to-point-in-time
--source-db-instance-identifier production-postgres-01
--target-db-instance-identifier production-postgres-01-restored
--restore-time 2024-12-15T10:30:00Z
--db-instance-class db.r6g.large
--db-subnet-group-name production-rds-subnet-group
--vpc-security-group-ids sg-0rds1234567890abc
--no-publicly-accessible
--tags Key=Environment,Value=recovery
Point-in-time recovery konusunda önemli bir not: Restored instance, yeni bir endpoint ile gelir. Uygulamalarınızı doğrudan bu endpoint’e yönlendirmek yerine, Route 53 CNAME veya uygulama katmanında bir yapılandırma mekanizması kullanın. Bu sayede failover senaryolarında DNS üzerinden geçiş yapabilirsiniz.
Performans Tuning: Gözden Kaçırdıklarınız
RDS üzerinde performance insights açık olsa bile, bazı konular doğrudan veritabanı tarafında ele alınmalı.
# Yavaş sorguları bul (pg_stat_statements üzerinden)
psql -h $RDS_ENDPOINT -U dbadmin -d appdb << 'PSQL'
-- En yavaş 10 sorguyu listele
SELECT
round(total_exec_time::numeric, 2) AS total_time_ms,
calls,
round(mean_exec_time::numeric, 2) AS avg_time_ms,
round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 2) AS percentage,
left(query, 100) AS query_preview
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
-- Bloklanan sorgular
SELECT
pid,
now() - pg_stat_activity.query_start AS duration,
query,
state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes'
AND state != 'idle';
-- Index kullanımı
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE idx_scan = 0
AND schemaname = 'public'
ORDER BY pg_relation_size(indexrelid) DESC;
PSQL
Bağlantı yönetimi de sık atlanan bir konu. RDS’te her bağlantı bellek tüketir. db.r6g.large için maksimum bağlantı sayısı yaklaşık 800 civarında. PgBouncer gibi bir connection pooler kullanmak, özellikle çok sayıda uygulama sunucusu olan ortamlarda kritik önem taşıyor.
Maliyet Optimizasyonu
RDS maliyetleri farkında olmadan hızla artabiliyor. Kontrol altında tutmak için şu adımları takip edin:
- Reserved Instance kullanın: 1 veya 3 yıllık rezervasyon ile on-demand fiyatına göre yüzde 40-60 tasarruf edebilirsiniz. Uzun vadeli kullanım planlıyorsanız mutlaka değerlendirin
- Storage tipini doğru seçin:
gp3artıkgp2‘den hem daha ucuz hem daha performanslı. Eskigp2instance’larınız varsa migrate edin - Snapshot retention süresini optimize edin: 7 günlük backup genellikle yeterli. 35 güne çıkarmak maliyeti önemli ölçüde artırır
- Development/test ortamları için schedule kullanın: Mesai dışında RDS’i durdurmak maliyeti ciddi ölçüde düşürür. Production için geçerli değil tabii
- Performance Insights retention: 7 gün ücretsiz, daha uzun süre için ücret alınıyor. İhtiyacınıza göre ayarlayın
- Multi-AZ’ı sadece production’da kullanın: Dev ve staging ortamlarında single-AZ yeterli
# Durdurulan RDS instance'larını listele (21 günden uzun durdurulmuş olanlar otomatik başlatılır!)
aws rds describe-db-instances
--query 'DBInstances[?DBInstanceStatus==`stopped`].{ID:DBInstanceIdentifier,Status:DBInstanceStatus,Class:DBInstanceClass}'
--output table
Sonuç
AWS RDS PostgreSQL, doğru yapılandırıldığında ciddi operasyonel yük azaltıyor. Ancak “yönetilen servis” kelimesi, “dikkat gerektirmez” anlamına gelmiyor. Bu yazıda anlattığımız adımları özetlersek:
Network katmanında RDS’i private subnet’e koyun ve security group’ları minimum gerekli erişimle sınırlandırın. Şifreleri asla kod içinde veya environment variable olarak tutmayın, Secrets Manager kullanın. Deletion protection ve storage encryption’ı production’da her zaman aktif edin. CloudWatch alarmlarını en baştan yapılandırın, sorun olduktan sonra değil. Read replica ve point-in-time recovery mekanizmalarını test edin, gerçekten ihtiyaç duyduğunuz anda nasıl çalıştığını bilmeniz gerekiyor.
Performance tarafında, pg_stat_statements’ı aktif tutun ve düzenli olarak yavaş sorguları review edin. Connection pool’lamayı uygulamayı büyütmeden önce düşünün. Storage olarak gp3 tercih edin ve autoscaling limitini kontrol altında tutun.
RDS kurulumu bir kez yapılıp unutulan bir şey değil. Düzenli olarak maliyet raporlarını inceleyin, minor version upgrade’lerini takip edin ve özellikle major version upgrade’leri için iyi bir test planı hazırlayın. Bu yazıda anlattıklarımı uyguladığınızda, hem güvenli hem de verimli bir PostgreSQL altyapısına sahip olacaksınız.
