Azure API Management Kurulumu ve Temel Yapılandırması
Mikroservis mimarisine geçiş yapan veya mevcut API’larını merkezi bir noktadan yönetmek isteyen ekipler için Azure API Management (APIM) gerçekten hayat kurtarıcı bir servis. Ama kurulum aşamasında “nereden başlayacağım?” sorusuyla boğuşmak çok yaygın. Bu yazıda sıfırdan production’a hazır bir APIM kurulumunu adım adım ele alacağız, hem portal üzerinden hem de Infrastructure as Code yaklaşımıyla.
Azure API Management Nedir ve Neden Kullanmalısınız?
Kısaca özetlemek gerekirse APIM, backend servislerinizin önüne geçen bir proxy/gateway katmanıdır. API anahtarı yönetimi, rate limiting, IP kısıtlaması, SSL sonlandırma, önbellekleme ve izleme gibi cross-cutting concern’leri tek bir yerden halledebilirsiniz.
Gerçek dünya senaryosu şu: Diyelim ki 5 farklı microservice’iniz var ve bunların hepsine dışarıdan erişim gerekiyor. Her servis kendi auth mekanizmasını, kendi log formatını kullanıyor. Bir müşteri rate limit aşımı yapıyor, hangisinden geldiğini tespit etmek saatler alıyor. APIM bu kaosa düzen getirir.
Tier seçimi kritik bir karar:
- Consumption: Serverless model, düşük trafik için ideal, per-call ücretlendirme
- Developer: Test ve geliştirme ortamları için, SLA yok
- Basic: Küçük production iş yükleri, 1 unit
- Standard: Orta ölçekli workload’lar, VNet desteği yok
- Premium: Enterprise, multi-region, VNet entegrasyonu, private endpoint
Ön Gereksinimler
Kuruluma geçmeden önce elinizde şunlar olmalı:
- Aktif bir Azure subscription
- Contributor veya Owner rolü olan bir hesap
- Azure CLI kurulu ve login yapılmış
- Bir resource group (yoksa oluşturacağız)
- Varsa backend API URL’leri
Azure CLI versiyonunuzu kontrol edin, eski versiyonlarda bazı komutlar farklı çalışıyor:
az --version
az upgrade
# Login ve subscription seçimi
az login
az account list --output table
az account set --subscription "subscription-id-veya-ismi"
Resource Group ve Temel Altyapı
İyi bir naming convention ile başlamak ileride sizi kurtarır. Ben rg-{proje}-{ortam}-{region} formatını kullanıyorum:
# Resource group oluşturma
az group create
--name rg-myapp-prod-westeu
--location westeurope
--tags Environment=Production Project=MyApp [email protected]
# Mevcut resource group'ları listele
az group list --output table
APIM Instance Oluşturma
Portal üzerinden gitmek yerine CLI ile ilerleyeceğiz çünkü bu yaklaşım hem tekrarlanabilir hem de CI/CD pipeline’larına entegre edilebilir.
# APIM instance oluşturma (bu işlem 20-45 dakika sürebilir, sabırlı olun!)
az apim create
--name apim-myapp-prod
--resource-group rg-myapp-prod-westeu
--location westeurope
--publisher-email "[email protected]"
--publisher-name "Sirket Adi"
--sku-name Developer
--sku-capacity 1
--enable-managed-identity true
--tags Environment=Production Project=MyApp
# Oluşturma durumunu kontrol et
az apim show
--name apim-myapp-prod
--resource-group rg-myapp-prod-westeu
--query "provisioningState"
Önemli not: --enable-managed-identity true flag’ini unutmayın. Key Vault entegrasyonu, backend servislerine erişim gibi senaryolarda bu özellik şart haline geliyor.
Kurulum tamamlandıktan sonra endpoint URL’lerinizi alın:
az apim show
--name apim-myapp-prod
--resource-group rg-myapp-prod-westeu
--query "{gatewayUrl:gatewayUrl, portalUrl:developerPortalUrl, managementUrl:managementApiUrl}"
--output json
İlk API’yi Ekleme
APIM hazır olduğuna göre artık backend API’lerimizi ekleyebiliriz. Örnek olarak bir product API’si ekleyelim:
# API oluşturma
az apim api create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--api-id product-api
--display-name "Product API"
--description "Ürün yönetim servisi"
--service-url "https://backend-product-service.azurewebsites.net"
--path "products"
--protocols https
--subscription-required true
# API'ye operation ekleme (GET /products endpoint'i)
az apim api operation create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--api-id product-api
--operation-id list-products
--display-name "List Products"
--method GET
--url-template "/"
--description "Tüm ürünleri listeler"
Policy Yapılandırması
APIM’in asıl gücü policy sistemidir. XML tabanlı bu yapı biraz garip görünse de çok esnektir.
Rate Limiting Policy
Her API için rate limit uygulamak production ortamında zorunludur. Bir kullanıcının servisi çökertmesini engeller:
<!-- rate-limit-policy.xml -->
<policies>
<inbound>
<base />
<rate-limit-by-key
calls="100"
renewal-period="60"
counter-key="@(context.Subscription?.Key ?? context.Request.IpAddress)"
increment-condition="@(context.Response.StatusCode >= 200 && context.Response.StatusCode < 300)" />
<quota-by-key
calls="10000"
renewal-period="86400"
counter-key="@(context.Subscription?.Key ?? context.Request.IpAddress)" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-header name="X-RateLimit-Remaining" exists-action="override">
<value>@(context.Variables.GetValueOrDefault<int>("remainingCalls").ToString())</value>
</set-header>
</outbound>
<on-error>
<base />
</on-error>
</policies>
Bu policy’yi CLI ile uygulayalım:
# Policy dosyasını API'ye uygula
az apim api policy create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--api-id product-api
--xml-file ./rate-limit-policy.xml
# Mevcut policy'yi görüntüle
az apim api policy show
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--api-id product-api
JWT Doğrulama Policy’si
Gerçek dünya senaryosu: Frontend uygulamanız Azure AD’den token alıyor ve APIM bu token’ı doğrulamalı. Backend servisinizin bu yükü taşımasına gerek yok:
<!-- jwt-validation-policy.xml -->
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Yetkisiz erişim">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>api://your-app-id</audience>
</audiences>
<issuers>
<issuer>https://sts.windows.net/{tenant-id}/</issuer>
</issuers>
<required-claims>
<claim name="roles" match="any">
<value>API.Read</value>
<value>API.Write</value>
</claim>
</required-claims>
</validate-jwt>
<set-header name="X-User-Id" exists-action="override">
<value>@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Subject)</value>
</set-header>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Subscription ve Ürün Yapılandırması
APIM’de API’lere erişim “Product” kavramı üzerinden yönetilir. Bir ürün, birden fazla API’yi gruplar ve subscription key ile korunur:
# Product oluşturma
az apim product create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--product-id starter-plan
--product-name "Starter Plan"
--description "Temel API erişimi, günlük 10.000 istek"
--state published
--subscription-required true
--approval-required false
--subscriptions-limit 1
# Premium plan
az apim product create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--product-id premium-plan
--product-name "Premium Plan"
--description "Sınırsız API erişimi, öncelikli destek"
--state published
--subscription-required true
--approval-required true
--subscriptions-limit 5
# Product'a API ekle
az apim product api add
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--product-id starter-plan
--api-id product-api
Named Values ile Gizli Bilgi Yönetimi
Policy’lerde hardcode değerler kullanmak yerine Named Values kullanın. Key Vault ile entegre ettiğinizde bu değerler otomatik rotate edilebilir:
# Basit bir Named Value ekle
az apim nv create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--named-value-id backend-api-key
--display-name "Backend API Key"
--value "supersecretkey123"
--secret true
# Key Vault'tan değer çek (önce managed identity'ye KV erişimi verin)
KEYVAULT_ID=$(az keyvault show --name kv-myapp-prod --query id --output tsv)
az apim nv create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--named-value-id db-connection-string
--display-name "DB Connection String"
--secret true
--key-vault-secret-identifier "https://kv-myapp-prod.vault.azure.net/secrets/db-connection-string"
Policy’de Named Value kullanımı şöyle olur:
<set-header name="X-Internal-Key" exists-action="override">
<value>{{backend-api-key}}</value>
</set-header>
Monitoring ve Diagnostics Ayarları
APIM kurup bırakmak olmaz, neyin döndüğünü görmeniz lazım. Application Insights entegrasyonu kurun:
# Application Insights oluştur
az monitor app-insights component create
--app appi-myapp-prod
--resource-group rg-myapp-prod-westeu
--location westeurope
--kind web
--tags Environment=Production
# Instrumentation key al
APPINSIGHTS_KEY=$(az monitor app-insights component show
--app appi-myapp-prod
--resource-group rg-myapp-prod-westeu
--query instrumentationKey
--output tsv)
# APIM'e logger ekle
az apim logger create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--logger-id appinsights-logger
--logger-type applicationInsights
--credentials "{'instrumentationKey':'$APPINSIGHTS_KEY'}"
--description "Application Insights logger"
# API diagnostics ayarla
az apim api diagnostic create
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--api-id product-api
--diagnostic-id applicationinsights
--logger-id "/loggers/appinsights-logger"
--sampling-percentage 100
--always-log allErrors
--verbosity information
OpenAPI/Swagger Import
Mevcut bir backend servisinizin Swagger dosyası varsa manuel operation oluşturmak yerine import edin:
# Swagger URL'den import
az apim api import
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--path "orders"
--api-id order-api
--specification-format OpenApiJson
--specification-url "https://backend-order-service.azurewebsites.net/swagger/v1/swagger.json"
--protocols https
--service-url "https://backend-order-service.azurewebsites.net"
# Lokal dosyadan import
az apim api import
--resource-group rg-myapp-prod-westeu
--service-name apim-myapp-prod
--path "users"
--api-id user-api
--specification-format OpenApiJson
--specification-path "./user-api-spec.json"
--protocols https
--service-url "https://backend-user-service.azurewebsites.net"
Terraform ile Infrastructure as Code Yaklaşımı
CLI komutları tek seferlik kurulum için güzeldir ama production ortamında Terraform kullanmak daha sürdürülebilir:
# Terraform modülü için temel yapı
mkdir -p terraform/apim && cd terraform/apim
cat > main.tf << 'EOF'
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.80"
}
}
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "satfstatemyapp"
container_name = "tfstate"
key = "apim/terraform.tfstate"
}
}
provider "azurerm" {
features {}
}
resource "azurerm_api_management" "main" {
name = "apim-myapp-prod"
location = var.location
resource_group_name = var.resource_group_name
publisher_name = var.publisher_name
publisher_email = var.publisher_email
sku_name = "${var.sku_tier}_${var.sku_capacity}"
identity {
type = "SystemAssigned"
}
tags = var.tags
}
resource "azurerm_api_management_api" "product_api" {
name = "product-api"
resource_group_name = var.resource_group_name
api_management_name = azurerm_api_management.main.name
revision = "1"
display_name = "Product API"
path = "products"
protocols = ["https"]
service_url = var.product_backend_url
subscription_required = true
}
EOF
# Terraform init ve plan
terraform init
terraform plan -var-file="prod.tfvars"
terraform apply -var-file="prod.tfvars" -auto-approve
Sık Karşılaşılan Sorunlar ve Çözümleri
Backend sertifika hatası: Self-signed sertifika kullanan bir backend’e bağlanıyorsanız APIM bu sertifikayı reddeder. Policy’de bunu devre dışı bırakabilirsiniz (development’ta) veya sertifikayı APIM’e trust ettirin.
CORS hataları: Frontend uygulamanız APIM üzerinden API’ye erişirken CORS hatası alıyorsa, global policy’ye CORS konfigürasyonu ekleyin. All API’s seviyesindeki policy’ye eklemek en temizi.
Subscription key nereden geliyor: İki yol var; Ocp-Apim-Subscription-Key header’ı veya URL’de subscription-key query parameter. Postman ile test ederken bunu unutmak çok can sıkıcı.
Deployment yavaş: APIM deployment’ları gerçekten uzun sürüyor, Developer tier’da 20-30 dakika, Premium tier’da 45 dakikaya kadar çıkabiliyor. Terraform’da timeouts bloğuna en az 60 dakika koyun.
429 hatası beklenmeden geliyor: Rate limit counter’ları global olarak çalışıyor. Birden fazla unit çalıştırıyorsanız counter’ların nasıl senkronize edildiğini anlamak önemli.
Güvenlik Kontrol Listesi
Production’a almadan önce şunları gözden geçirin:
- Subscription key zorunlu: Tüm API’lerde
subscription-required: trueolsun - HTTPS zorunlu: HTTP protokolünü product seviyesinde de, API seviyesinde de devre dışı bırakın
- IP kısıtlaması: Eğer API’ler sadece belirli IP’lerden çağrılacaksa
ip-filterpolicy’si ekleyin - Minimum TLS: TLS 1.2 altını devre dışı bırakın, bu portal üzerinden Custom Domains altında yapılıyor
- DDoS koruması: Standard tier APIM’ler için Azure DDoS Protection Plan düşünün
- Named Values: Policy içinde hardcode key/secret bırakmayın, mutlaka Named Values kullanın
- Managed Identity: Backend’e erişim için service principal yerine managed identity tercih edin
- Diagnostic log retention: Log retention süresini iş gereksinimlerine göre ayarlayın, varsayılan genellikle yeterli değil
Sonuç
Azure API Management, doğru yapılandırıldığında API altyapınızın bel kemiği haline gelir. Bu yazıda temel kurulumdan policy yönetimine, monitoring’den IaC yaklaşımına kadar production’da gerçekten ihtiyaç duyacağınız konuları ele aldık.
Birkaç kritik çıkarım: Tier seçimini aceleyle yapmayın, geliştirme ortamında bile Developer tier yetersiz gelebilir ama Premium tier’ın maliyeti ciddi. Named Values ve Key Vault entegrasyonunu baştan kur, sonradan eklemek zahmetli. Application Insights bağlantısını da kurulumun ilk günü yapın çünkü ilk haftalarda ne kadar trafik geldiğini, hangi endpoint’lerin sorun çıkardığını görmek çok değerli.
Terraform modülünü oluşturup state’i merkezi bir storage account’ta tutmak, özellikle birden fazla ortam (dev/staging/prod) yönetirken büyük kolaylık sağlıyor. CLI komutları işe yarasa da bir süre sonra “bu değişikliği kim ne zaman yaptı?” sorusu kaçınılmaz geliyor ve o noktada IaC’nin değeri net olarak ortaya çıkıyor.
Sorularınız olursa yorumlardan yazabilirsiniz, özellikle VNet entegrasyonu veya multi-region APIM kurulumu hakkında ayrı bir yazı isteyenler olursa o konuları da ele alabiliriz.
