Docker Açıklaması: Nginx’i Konteynerize Etme ve Ters Proxy Olarak Kullanma
Giriş
Günümüzün hızla değişen yazılım dünyasında, uygulamaların geliştirilmesi, dağıtılması ve yönetilmesi süreçleri her zamankinden daha karmaşık hale gelmiştir. Geliştiriciler, “benim makinemde çalışıyor” sendromuyla sıkça karşılaşırken, operasyon ekipleri farklı bağımlılıklar ve ortam tutarsızlıklarıyla mücadele etmektedir. İşte tam bu noktada Docker ve konteynerizasyon devreye girerek, yazılım yaşam döngüsüne devrim niteliğinde bir çözüm sunar. Docker, uygulamaları bağımlılıklarıyla birlikte izole edilmiş, taşınabilir ve tutarlı bir ortamda çalıştırmayı mümkün kılar. Bu sayede, geliştirme ortamından test ortamına ve nihayetinde üretim ortamına kadar her aşamada aynı uygulamanın aynı şekilde çalışacağından emin olabiliriz.
Bu makalede, Docker’ın temel prensiplerini derinlemesine inceleyecek, ardından yüksek performanslı bir web sunucusu ve ters proxy olan Nginx‘i Docker ile nasıl konteynerize edeceğimizi adım adım göstereceğiz. Özellikle, Nginx’i birden fazla arka uç uygulamasını tek bir erişim noktası üzerinden yönlendiren bir ters proxy (reverse proxy) olarak nasıl yapılandıracağımızı detaylandıracağız. Docker Compose kullanarak çoklu konteyner uygulamalarını nasıl yöneteceğimizi de ele alarak, modern uygulama dağıtım pratiklerinin temel taşlarını öğrenmiş olacağız. Bu rehber, hem Docker’a yeni başlayanlar hem de Nginx’i konteynerize edilmiş bir ortamda etkin bir şekilde kullanmak isteyen deneyimli geliştiriciler ve sistem yöneticileri için kapsamlı bir kaynak olmayı hedeflemektedir.
Docker Temelleri
Docker, uygulamaları ve bağımlılıklarını küçük, hafif ve taşınabilir birimler olan konteynerler içinde paketlemek için kullanılan açık kaynaklı bir platformdur. Konteynerizasyon, sanal makineler (VM’ler) gibi geleneksel sanallaştırma yöntemlerine kıyasla önemli avantajlar sunar.
Docker Nedir?
Docker’ın temelini oluşturan birkaç anahtar kavram bulunmaktadır:
* Konteyner (Container): Bir uygulamanın çalıştırılabilen, bağımsız bir birimidir. Uygulamanın kodu, çalışma zamanı, sistem araçları, kütüphaneler ve ayarlar dahil olmak üzere çalışması için gereken her şeyi içerir. Konteynerler, ana işletim sistemi çekirdeğini paylaşır ancak kendi izole edilmiş dosya sistemlerine, ağ arayüzlerine ve süreç alanlarına sahiptir.
* İmaj (Image): Bir konteyner oluşturmak için kullanılan şablondur. İmajlar, uygulamanın çalışması için gereken tüm bağımlılıkları ve konfigürasyonları içerir ve genellikle bir Dockerfile kullanılarak oluşturulur. İmajlar sabittir (immutable) ve bir kez oluşturulduktan sonra değişmezler.
* Dockerfile: Bir Docker imajının nasıl oluşturulacağını tanımlayan metin tabanlı bir betiktir. Adım adım talimatlar içerir: hangi temel imajın kullanılacağı, hangi dosyaların kopyalanacağı, hangi komutların çalıştırılacağı vb.
* Docker Engine: Konteynerleri oluşturan, çalıştıran ve yöneten temel Docker bileşenidir. Bir sunucuda veya geliştirme makinesinde çalışır ve Docker CLI (Komut Satırı Arayüzü) aracılığıyla etkileşime girilir.
* Docker Hub: Docker imajları için bulut tabanlı bir kayıt defteridir (registry). Geliştiriciler, kendi imajlarını depolayabilir, paylaşabilir ve başkalarının imajlarını çekebilir.
Neden Docker?
Docker’ın popülaritesi, sunduğu bir dizi önemli avantajdan kaynaklanmaktadır:
* Tutarlılık (Consistency): Geliştirme, test ve üretim ortamları arasında tutarlılığı garanti eder. Uygulama, Docker imajı içinde paketlendiği için, her ortamda aynı bağımlılıklar ve konfigürasyonlarla çalışır. Bu, “benim makinemde çalışıyor” sorununu ortadan kaldırır.
* İzolasyon (Isolation): Her konteyner, diğer konteynerlerden ve ana sistemden izole edilmiştir. Bu, farklı uygulamaların veya farklı versiyonlarının aynı sunucuda çakışmadan çalışabilmesini sağlar.
* Taşınabilirlik (Portability): Bir Docker imajı, herhangi bir Docker Engine yüklü makinede çalışabilir. Bu, uygulamaların bulut ortamları, şirket içi sunucular veya geliştirici makineleri arasında kolayca taşınabilmesini sağlar.
* Kaynak Verimliliği (Resource Efficiency): Sanal makinelerden farklı olarak, konteynerler kendi işletim sistemlerini içermez; bunun yerine ana bilgisayarın çekirdeğini paylaşır. Bu, daha az disk alanı, bellek ve CPU tüketimi anlamına gelir, böylece aynı donanım üzerinde daha fazla uygulama çalıştırılabilir.
* Hızlı Dağıtım (Rapid Deployment): Konteynerler çok hızlı bir şekilde başlatılabilir ve durdurulabilir. Bu, CI/CD (Sürekli Entegrasyon/Sürekli Dağıtım) süreçlerini hızlandırır ve uygulamaların daha sık ve güvenilir bir şekilde dağıtılmasına olanak tanır.
* Versiyonlama ve Geri Dönüş (Versioning and Rollback): Docker imajları versiyonlanabilir. Bu, uygulamanın farklı versiyonlarını kolayca yönetmeyi ve sorun yaşandığında önceki bir versiyona hızla geri dönmeyi sağlar.
Nginx Temelleri ve Ters Proxy Kavramı
Nginx (Engine-X olarak telaffuz edilir), yüksek performanslı, açık kaynaklı bir web sunucusu, ters proxy, yük dengeleyici ve HTTP önbelleği olarak işlev gören popüler bir yazılımdır. Özellikle yoğun trafikli web siteleri için tasarlanmış olup, düşük bellek tüketimi ve eş zamanlı bağlantıları etkin bir şekilde yönetme yeteneği ile bilinir.
Nginx Nedir?
Nginx, ilk olarak 2004 yılında Igor Sysoev tarafından piyasaya sürüldü ve o zamandan beri web altyapısının önemli bir bileşeni haline geldi. Apache HTTP Sunucusu gibi geleneksel web sunucularından farklı olarak, Nginx olay tabanlı (event-driven) ve asenkron bir mimariye sahiptir. Bu mimari, Nginx’in aynı anda binlerce bağlantıyı çok daha az sistem kaynağı kullanarak işleyebilmesini sağlar.
Nginx’in başlıca kullanım alanları şunlardır:
* Statik İçerik Sunumu: HTML, CSS, JavaScript dosyaları ve görseller gibi statik içerikleri son derece hızlı bir şekilde sunar.
* Ters Proxy (Reverse Proxy): İstemci isteklerini bir veya daha fazla arka uç sunucusuna yönlendirir. Bu, Nginx’in ana konumuz olan kullanım şeklidir.
* Yük Dengeleyici (Load Balancer): Gelen trafiği birden fazla arka uç sunucusu arasında dağıtarak performansı artırır ve tek hata noktasını (single point of failure) ortadan kaldırır.
* HTTP Önbelleği (HTTP Cache): Sıkça istenen içerikleri önbelleğe alarak arka uç sunucularının yükünü azaltır ve yanıt sürelerini kısaltır.
* SSL/TLS Sonlandırma: SSL/TLS şifrelemesini Nginx üzerinde sonlandırarak arka uç sunucularının bu yükten kurtulmasını sağlar.
Ters Proxy (Reverse Proxy) Nedir ve Neden Kullanılır?
Bir ters proxy, istemciden gelen istekleri kabul eden ve bu istekleri bir veya daha fazla arka uç sunucusuna (genellikle web sunucuları veya uygulama sunucuları) ileten bir sunucudur. Arka uç sunucuları, isteği işler ve yanıtı ters proxy’ye geri gönderir; ters proxy de bu yanıtı istemciye iletir. İstemci, arka uç sunucularının varlığından haberdar değildir ve sadece ters proxy ile etkileşime girdiğini düşünür.
Ters proxy’lerin kullanımının birçok önemli faydası vardır:
* Güvenlik: Ters proxy, arka uç sunucularını doğrudan internete maruz kalmaktan korur. İstemciler sadece ters proxy’nin IP adresini görür, bu da arka uç sunucularının zafiyetlerini gizlemeye yardımcı olur. Ayrıca, güvenlik duvarı ve DDoS koruması gibi ek güvenlik önlemleri ters proxy seviyesinde uygulanabilir.
* Yük Dengeleme (Load Balancing): Gelen trafiği birden fazla arka uç sunucusu arasında dağıtarak sunucu yükünü dengeleyebilir. Bu, uygulamanın performansını ve ölçeklenebilirliğini artırır. Eğer bir sunucu arızalanırsa, ters proxy trafiği diğer çalışan sunuculara yönlendirebilir.
* SSL/TLS Sonlandırma: Tüm SSL/TLS şifreleme ve şifre çözme işlemleri ters proxy üzerinde yapılabilir. Bu, arka uç uygulama sunucularının bu işlem yükünden kurtulmasını ve sadece uygulama mantığına odaklanmasını sağlar.
* Önbellekleme (Caching): Sıkça istenen içerikleri önbelleğe alarak arka uç sunucularının yükünü azaltır ve istemcilere daha hızlı yanıt süreleri sunar.
* Mikroservis Yönlendirme: Mikroservis mimarilerinde, ters proxy, farklı mikroservisleri farklı URL yollarına veya alt alan adlarına göre yönlendirmek için kullanılır. Örneğin, /api/users isteği kullanıcı servisine, /api/products isteği ürün servisine yönlendirilebilir.
* Sıkıştırma ve Optimizasyon: Gelen yanıtları sıkıştırabilir veya diğer optimizasyonları uygulayarak bant genişliği kullanımını azaltabilir ve sayfa yükleme sürelerini iyileştirebilir.
* URL Yeniden Yazma (URL Rewriting): İstemciden gelen URL’leri arka uç sunucularının beklediği formata göre yeniden yazabilir.
Özetle, Nginx bir ters proxy olarak kullanıldığında, karmaşık uygulama mimarilerini basitleştirir, güvenliği artırır, performansı iyileştirir ve ölçeklenebilirliği destekler.
Nginx’i Docker ile Konteynerize Etme
Nginx’i Docker ile konteynerize etmek, onun dağıtımını ve yönetimini önemli ölçüde basitleştirir. Bu bölümde, Nginx’i temel bir konteyner olarak çalıştırmaktan, özel konfigürasyonlarla bir imaj oluşturmaya kadar adımları inceleyeceğiz.
Temel Nginx Konteyneri Oluşturma
İlk adım olarak, Docker Hub’dan resmi Nginx imajını çekip çalıştırmakla başlayabiliriz. Bu, Nginx’in varsayılan konfigürasyonuyla çalışan hızlı bir başlangıç sağlar.
1. Nginx İmajını Çekme:
Docker Hub’dan Nginx’in en son stabil imajını çekmek için aşağıdaki komutu kullanın:
docker pull nginx:latest
Bu komut, Nginx imajını yerel Docker deponuza indirir. latest etiketi, genellikle en son stabil sürümü işaret eder.
2. Nginx Konteynerini Çalıştırma:
İmajı indirdikten sonra, Nginx’i bir konteyner içinde çalıştırabiliriz. Konteyneri ana makinenin 80 portuna (HTTP varsayılan portu) bağlamak için -p bayrağını kullanacağız.
docker run -d -p 80:80 --name my-nginx-container nginx:latest
* -d: Konteyneri arka planda (detached mode) çalıştırır.
* -p 80:80: Ana makinenin 80 portunu konteynerin 80 portuna eşler. Böylece, ana makinenin IP adresine veya localhost‘a 80 portundan gelen istekler konteynerdeki Nginx’e yönlendirilir.
* --name my-nginx-container: Konteynera okunabilir bir isim veririz.
* nginx:latest: Çalıştırılacak imajın adıdır.
3. Çalıştığını Test Etme:
Tarayıcınızı açın ve http://localhost adresine gidin. Nginx’in varsayılan karşılama sayfasını görmelisiniz (“Welcome to Nginx!”). Bu, Nginx konteynerinizin başarıyla çalıştığını gösterir.
Konteynerin çalıştığını doğrulamak için docker ps komutunu da kullanabilirsiniz:
docker ps
Çıktıda my-nginx-container isimli konteyneri görmelisiniz.
Özel Nginx Konfigürasyonu Kullanma
Çoğu gerçek dünya senaryosunda, Nginx’i varsayılan konfigürasyonuyla değil, özel bir konfigürasyon dosyasıyla çalıştırmak isteriz. Özellikle ters proxy olarak kullanacağımızda bu kaçınılmazdır. Özel bir nginx.conf dosyası kullanarak kendi Nginx imajımızı nasıl oluşturacağımızı görelim.
1. Özel nginx.conf Dosyası Oluşturma:
Öncelikle, Nginx’in konfigürasyonunu tanımlayacak bir nginx.conf dosyası oluşturalım. Bu örnekte, Nginx’in varsayılan ayarlarını kullanarak basit bir konfigürasyon tanımlayacağız. Daha sonra ters proxy için bu dosyayı güncelleyeceğiz.
Yeni bir dizin oluşturun (örneğin, my-nginx-proxy) ve içine nginx.conf adında bir dosya oluşturun:
# my-nginx-proxy/nginx.conf
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
Bu konfigürasyon, Nginx’in varsayılan davranışını taklit eder: 80 numaralı portu dinler ve /usr/share/nginx/html dizininden statik dosyalar sunar.
2. Dockerfile Oluşturma:
Şimdi, bu özel nginx.conf dosyasını içeren kendi Nginx imajımızı oluşturmak için bir Dockerfile oluşturalım. nginx.conf dosyasının bulunduğu aynı dizine Dockerfile adında bir dosya ekleyin:
# my-nginx-proxy/Dockerfile
# Resmi Nginx imajını temel al
FROM nginx:latest
# Özel nginx.conf dosyamızı konteynerin doğru yerine kopyala
COPY nginx.conf /etc/nginx/nginx.conf
# Nginx'in dinleyeceği portu belirt (isteğe bağlı, dokümantasyon amaçlı)
EXPOSE 80
# Konteyner başlatıldığında Nginx'i çalıştır
# "daemon off;" Nginx'in bir foreground süreci olarak çalışmasını sağlar,
# bu da Docker'ın konteynerin çalıştığını anlaması için gereklidir.
CMD ["nginx", "-g", "daemon off;"]
3. İmajı Oluşturma (Build Etme):
Dockerfile ve nginx.conf dosyalarını oluşturduğunuz my-nginx-proxy dizininde, aşağıdaki komutu çalıştırarak kendi Nginx imajınızı oluşturun:
docker build -t custom-nginx-image .
* -t custom-nginx-image: Oluşturulan imaja custom-nginx-image adını verir.
* .: Dockerfile‘ın ve ilgili dosyaların mevcut dizinde olduğunu belirtir.
4. Özel Konteyneri Çalıştırma:
Oluşturduğunuz custom-nginx-image imajını kullanarak bir konteyner çalıştırın:
docker run -d -p 80:80 --name custom-nginx-container custom-nginx-image
Tarayıcınızdan http://localhost adresine tekrar gidin. Yine Nginx’in karşılama sayfasını görmelisiniz. Bu sefer, bu sayfa sizin özel imajınızdan gelmektedir. Eğer nginx.conf dosyasında bir hata olsaydı, Nginx başlamazdı ve docker logs custom-nginx-container komutu ile hata mesajlarını görebilirdiniz.
Bu adımlar, Nginx’i Docker ile temel seviyede konteynerize etmeyi ve özel bir konfigürasyonla çalıştırmayı gösterir. Bir sonraki bölümde, Nginx’i gerçek bir ters proxy olarak yapılandırmak için bu bilgiyi kullanacağız.
Nginx’i Ters Proxy Olarak Kullanma
Şimdi Nginx’i, birden fazla arka uç uygulamasını tek bir erişim noktası üzerinden yönlendiren bir ters proxy olarak yapılandırma konusuna geçelim. Bu senaryoda, iki basit web uygulamamız olacak ve Nginx bu uygulamalara gelen istekleri URL yollarına göre yönlendirecek.
Senaryo: İki Farklı Web Uygulamasını Proxy Etme
Hayal edelim ki, biri /app1 yolu üzerinden erişilecek bir “Uygulama 1” ve diğeri /app2 yolu üzerinden erişilecek bir “Uygulama 2” olmak üzere iki farklı web uygulamamız var. Her iki uygulama da kendi Docker konteynerlerinde çalışacak ve Nginx de ayrı bir Docker konteynerinde bu uygulamaların önünde bir ters proxy görevi görecek.
* Uygulama 1: Basit bir Node.js uygulaması, http://app1_service:3001 adresinden erişilebilir.
* Uygulama 2: Basit bir Node.js uygulaması, http://app2_service:3002 adresinden erişilebilir.
* Nginx Proxy: http://localhost/app1 isteklerini Uygulama 1’e, http://localhost/app2 isteklerini Uygulama 2’ye yönlendirecek.
Uygulama Konteynerlerini Oluşturma
Öncelikle, proxy edeceğimiz iki basit Node.js uygulamasını oluşturalım. Her uygulama için ayrı bir dizin ve Dockerfile oluşturacağız.
1. Uygulama 1 (app1):
my-nginx-proxy ana dizininin içine app1 adında bir dizin oluşturun.
app1/app.js dosyası:
// app1/app.js
const http = require('http');
const port = 3001;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Merhaba Uygulama 1! (Port 3001)\n');
});
server.listen(port, () => {
console.log(Uygulama 1 ${port} portunda çalışıyor.);
});
app1/Dockerfile dosyası:
# app1/Dockerfile
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
# package.json'ımız yok ama genel bir pratik olarak ekleyelim
# RUN npm install
COPY . .
EXPOSE 3001
CMD ["node", "app.js"]
2. Uygulama 2 (app2):
my-nginx-proxy ana dizininin içine app2 adında bir dizin oluşturun.
app2/app.js dosyası:
// app2/app.js
const http = require('http');
const port = 3002;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Merhaba Uygulama 2! (Port 3002)\n');
});
server.listen(port, () => {
console.log(Uygulama 2 ${port} portunda çalışıyor.);
});
app2/Dockerfile dosyası:
# app2/Dockerfile
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
# RUN npm install
COPY . .
EXPOSE 3002
CMD ["node", "app.js"]
Nginx Ters Proxy Konfigürasyonu
Şimdi my-nginx-proxy/nginx.conf dosyamızı ters proxy olarak işlev görecek şekilde güncelleyelim. Bu konfigürasyon, /app1 ve /app2 yollarına gelen istekleri ilgili arka uç uygulamalarına yönlendirecek.
# my-nginx-proxy/nginx.conf
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# Arka uç servislerini tanımlamak için upstream blokları
# Docker Compose veya Docker network içinde servis isimleri doğrudan kullanılabilir
upstream app1_service {
server app1:3001; # app1, Docker network içindeki servis adıdır
}
upstream app2_service {
server app2:3002; # app2, Docker network içindeki servis adıdır
}
server {
listen 80;
server_name localhost;
# /app1 yoluna gelen istekleri app1_service'e yönlendir
location /app1/ {
proxy_pass http://app1_service/;
# Gelen isteğin orijinal host başlığını arka uca ilet
proxy_set_header Host $host;
# Gelen isteğin orijinal IP adresini arka uca ilet
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Nginx'in /app1/ yolunu arka uçtan kaldırarak yönlendirmesi için
# rewrite ^/app1/(.*)$ /$1 break; # Bu, eğer arka uç /app1'i beklemiyorsa kullanılabilir.
# Ancak proxy_pass sonuna eğik çizgi eklemek genellikle yeterlidir.
}
# /app2 yoluna gelen istekleri app2_service'e yönlendir
location /app2/ {
proxy_pass http://app2_service/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Diğer tüm istekler için varsayılan bir sayfa veya hata sayfası sun
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ =404;
# Eğer ana dizine bir index.html koyarsak bu kısım çalışır.
# Şimdilik varsayılan Nginx sayfasını görmek için boş bırakabiliriz.
return 200 'Nginx Ters Proxy Çalışıyor!';
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
Önemli Not: proxy_pass http://app1_service/; ifadesindeki son eğik çizgi (/) önemlidir. Eğer proxy_pass URL’si bir eğik çizgi ile biterse, location bloğundaki eşleşen önek (bu durumda /app1/) arka uç sunucusuna gönderilen URL’den çıkarılır. Yani http://localhost/app1/somepath isteği http://app1_service/somepath olarak yönlendirilir. Eğer eğik çizgi olmazsa (proxy_pass http://app1_service;), location öneki korunur ve http://localhost/app1/somepath isteği http://app1_service/app1/somepath olarak yönlendirilir. Uygulamalarınızın URL yapılandırmasına göre doğru olanı seçmelisiniz. Genellikle ilk yöntem tercih edilir.
Docker Compose ile Orkestrasyon
Yukarıdaki senaryoda üç ayrı konteyneri (Nginx, app1, app2) manuel olarak yönetmek karmaşık olabilir. İşte bu noktada Docker Compose devreye girer. Docker Compose, çoklu konteyner Docker uygulamalarını tanımlamak ve çalıştırmak için bir araçtır. Bir YAML dosyası kullanarak uygulamanızın tüm servislerini, ağlarını ve birimlerini tek bir dosyada tanımlarsınız, ardından tek bir komutla tüm uygulamayı başlatabilirsiniz.
Neden Docker Compose?
* Tek Dosya Tanımı: Tüm uygulama yığını (servisler, ağlar, depolama) tek bir docker-compose.yml dosyasında tanımlanır. Bu, uygulamanın konfigürasyonunu sürdürmeyi ve paylaşmayı kolaylaştırır.
* Kolay Yönetim: Tek bir komutla (örneğin, docker-compose up) tüm servisleri başlatabilir, durdurabilir, yeniden oluşturabilir ve ölçeklendirebilirsiniz.
* Servis Keşfi: Docker Compose, servisler arasında otomatik olarak bir ağ oluşturur ve servis isimlerini DNS çözünürlüğü için kullanır. Bu, bir servisin diğer bir servise adıyla erişebileceği anlamına gelir (örneğin, Nginx’in app1 servisine app1:3001 ile erişmesi).
* Ortam Tutarlılığı: Geliştirme, test ve üretim ortamları arasında tutarlı bir uygulama yığını sağlar.
docker-compose.yml Oluşturma
Şimdi, my-nginx-proxy ana dizininde, tüm uygulamamızı tanımlayan bir docker-compose.yml dosyası oluşturalım:
# my-nginx-proxy/docker-compose.yml
version: '3.8' # Docker Compose dosyasının versiyonu
services:
# Uygulama 1 servisi
app1:
build: ./app1 # app1 dizinindeki Dockerfile'ı kullanarak imajı oluştur
container_name: app1_container # Konteynerin adı
ports:
- "3001:3001" # Host'un 3001 portunu konteynerin 3001 portuna eşle (isteğe bağlı, sadece test için)
networks:
- app_network # Bu servisi app_network ağına dahil et
# Uygulama 2 servisi
app2:
build: ./app2 # app2 dizinindeki Dockerfile'ı kullanarak imajı oluştur
container_name: app2_container # Konteynerin adı
ports:
- "3002:3002" # Host'un 3002 portunu konteynerin 3002 portuna eşle (isteğe bağlı, sadece test için)
networks:
- app_network # Bu servisi app_network ağına dahil et
# Nginx ters proxy servisi
nginx:
build: . # Mevcut dizindeki Dockerfile'ı kullanarak Nginx imajını oluştur
container_name: nginx_proxy_container # Konteynerin adı
ports:
- "80:80" # Host'un 80 portunu Nginx konteynerinin 80 portuna eşle
volumes:
# Nginx konfigürasyon dosyasını host'tan konteynere bağla
# Bu, nginx.conf dosyasında değişiklik yaptığınızda konteyneri yeniden oluşturmadan
# sadece Nginx'i yeniden yükleyerek değişiklikleri uygulamanızı sağlar.
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app1 # Nginx'in app1'den önce başlamasını bekle
- app2 # Nginx'in app2'den önce başlamasını bekle
networks:
- app_network # Bu servisi app_network ağına dahil et
Özel bir ağ tanımla
networks:
app_network:
driver: bridge # Varsayılan bridge ağ sürücüsünü kullan
Dosya Yapısı:
Makalenin başında bahsettiğimiz my-nginx-proxy dizin yapısı şöyle olmalı:
my-nginx-proxy/
├── app1/
│ ├── app.js
│ └── Dockerfile
├── app2/
│ ├── app.js
│ └── Dockerfile
├── nginx.conf
└── docker-compose.yml
Uygulamayı Çalıştırma
docker-compose.yml dosyasını oluşturduktan sonra, my-nginx-proxy dizininde terminali açın ve aşağıdaki komutu çalıştırın:
docker-compose up --build -d
* up: Servisleri oluşturur ve başlatır.
* --build: Servis imajlarını (eğer değişmişlerse veya yoklarsa) yeniden oluşturur. Bu, ilk çalıştırmada veya Dockerfile’larda değişiklik yapıldığında önemlidir.
* -d: Servisleri arka planda (detached mode) çalıştırır.
Bu komut, app1 ve app2 için imajları oluşturacak, Nginx için imajı oluşturacak ve üç konteyneri de app_network ağı içinde başlatacaktır. depends_on sayesinde, app1 ve app2 servisleri nginx servisi başlamadan önce başlatılacaktır.
Test Etme:
Tüm servisler çalışmaya başladıktan sonra, tarayıcınızı açın ve aşağıdaki adreslere gidin:
* http://localhost/app1/ adresine gittiğinizde “Merhaba Uygulama 1! (Port 3001)” çıktısını görmelisiniz.
* http://localhost/app2/ adresine gittiğinizde “Merhaba Uygulama 2! (Port 3002)” çıktısını görmelisiniz.
* http://localhost/ adresine gittiğinizde “Nginx Ters Proxy Çalışıyor!” çıktısını görmelisiniz.
Bu, Nginx’in istekleri doğru arka uç uygulamalarına başarıyla yönlendirdiğini kanıtlar.
Servisleri Durdurma ve Temizleme:
Uygulamayı durdurmak ve oluşturulan tüm konteynerleri, ağları ve imajları (eğer -v bayrağı ile belirtilirse) kaldırmak için:
docker-compose down
Eğer imajları da kaldırmak isterseniz:
docker-compose down --rmi all
Gelişmiş Nginx Proxy Ayarları
Nginx, ters proxy olarak kullanıldığında birçok gelişmiş özelliği destekler:
* SSL/TLS Sonlandırma: Nginx, istemci ile kendisi arasındaki iletişimi şifrelemek için SSL/TLS sertifikalarını barındırabilir. Bu, arka uç uygulamalarının SSL yükünden kurtulmasını sağlar. Let’s Encrypt gibi hizmetlerle entegre olarak otomatik sertifika yönetimi de yapılabilir.
* Yük Dengeleme (Load Balancing): upstream blokları içinde birden fazla arka uç sunucusu tanımlayarak ve farklı yük dengeleme algoritmaları (round-robin, least-connected, ip-hash) kullanarak trafiği dağıtabilirsiniz.
upstream my_backend_servers {
server backend1.example.com;
server backend2.example.com;
# server backend3.example.com backup; # Yedek sunucu
}
location /api/ {
proxy_pass http://my_backend_servers;
}
* Önbellekleme (Caching): Nginx, arka uçtan gelen yanıtları önbelleğe alarak tekrarlanan isteklerde arka uç sunucusuna gitme ihtiyacını ortadan kaldırır ve yanıt sürelerini önemli ölçüde hızlandırır.
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m use_temp_path=off;
server {
# ...
location /static/ {
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://my_backend;
}
}
* Hata Sayfaları: Arka uç sunucusu ulaşılamadığında veya bir hata döndürdüğünde özel hata sayfaları sunmak için error_page direktifi kullanılabilir.
* Gzip Sıkıştırma: Nginx, sunulan içeriği sıkıştırarak bant genişliği kullanımını azaltabilir ve sayfa yükleme sürelerini iyileştirebilir.
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
Bu gelişmiş ayarlar, Nginx’in bir ters proxy olarak potansiyelini tam olarak kullanmanızı sağlar ve modern web uygulamaları için sağlam ve performanslı bir altyapı oluşturmanıza yardımcı olur.
Sık Karşılaşılan Sorunlar ve Çözümleri
Docker ve Nginx ile çalışırken karşılaşabileceğiniz bazı yaygın sorunlar ve bunların çözümleri şunlardır:
* Port Çakışmaları:
* Sorun: docker run veya docker-compose up komutu çalıştırıldığında “port already in use” (port zaten kullanımda) hatası alırsınız.
* Çözüm: Bu, ana makinenizde belirtilen portun (örneğin, 80) zaten başka bir uygulama veya konteyner tarafından kullanıldığı anlamına gelir. Ya ana makinedeki çakışan uygulamayı durdurun ya da Docker komutunuzdaki port eşleşmesini değiştirin (örneğin, -p 8080:80 ile Nginx’e 8080 portundan erişin).
* netstat -tulnp | grep :80 (Linux) veya lsof -i :80 (macOS) gibi komutlarla hangi sürecin portu kullandığını bulabilirsiniz.
* Network İzolasyonu ve Servis Keşfi:
* Sorun: Nginx konteyneriniz, arka uç uygulama konteynerlerine erişemiyor (örneğin, proxy_pass hedefi bulunamıyor).
* Çözüm: Tüm ilgili konteynerlerin aynı Docker ağına bağlı olduğundan emin olun. Docker Compose kullanıyorsanız, networks anahtarını kullanarak tüm servislerinizi aynı özel ağa ekleyin. Nginx konfigürasyonunuzda proxy_pass içinde arka uç servislerinin konteyner isimlerini (Docker Compose’daki servis isimleri) kullanın. Docker Compose bu servis isimlerini otomatik olarak ağ içindeki IP adreslerine çözümler.
* Nginx Konfigürasyon Hataları:
* Sorun: Nginx konteyneri başlamıyor veya beklendiği gibi çalışmıyor. docker logs komutunda hata mesajları görüyorsunuz.
* Çözüm: Nginx konfigürasyon dosyanızda (nginx.conf) sözdizimi hataları olabilir. Nginx, konfigürasyon dosyasını test etmek için bir komut sağlar:
docker exec -it nginx -t
Bu komut, konteyner içindeki Nginx’in konfigürasyonunu test eder ve herhangi bir hata varsa size bildirir. Düzeltmeleri yaptıktan sonra konteyneri yeniden oluşturmanız veya Nginx’i yeniden yüklemeniz gerekebilir:
docker exec -it nginx -s reload
volumes kullanarak nginx.conf dosyasını ana makineden bağladıysanız, dosyayı düzenleyip nginx -s reload komutunu çalıştırmanız yeterlidir.
* Loglara Erişim:
* Sorun: Uygulamanızda veya Nginx’te bir sorun olduğunda ne olduğunu anlamak için loglara ihtiyacınız var.
* Çözüm: Docker, her konteyner için standart çıktı (stdout) ve standart hata (stderr) akışlarını yakalar. Konteynerin loglarını görmek için docker logs komutunu kullanın.
* docker logs nginx_proxy_container
* docker logs app1_container
* docker logs app2_container
* docker logs -f ile logları gerçek zamanlı olarak takip edebilirsiniz.
* Docker Compose kullanıyorsanız, docker-compose logs veya docker-compose logs -f komutları tüm servislerin loglarını veya belirli bir servisin loglarını gösterir.
* Cache Sorunları:
* Sorun: Nginx önbellekleme kullanıyorsunuz ancak yaptığınız değişiklikler hemen yansımıyor.
* Çözüm: Nginx önbelleği, proxy_cache_valid gibi direktiflerle belirlenen süreler boyunca yanıtları saklar. Değişikliklerin hemen görünmesi için önbelleği temizlemeniz gerekebilir. Bu genellikle proxy_cache_path içinde tanımlanan önbellek dizinini manuel olarak silerek yapılır. Üretim ortamlarında, önbellek temizleme için özel araçlar veya Nginx modülleri kullanılabilir.
Bu sorun giderme ipuçları, Docker ve Nginx ile çalışırken daha verimli olmanıza yardımcı olacaktır.
Sonuç
Bu kapsamlı makalede, Docker’ın temel prensiplerinden başlayarak, Nginx’in güçlü bir web sunucusu ve ters proxy olarak nasıl kullanılabileceğine dair derinlemesine bir yolculuk yaptık. Konteynerizasyonun modern yazılım geliştirme ve dağıtım süreçlerindeki kritik rolünü, Docker’ın sunduğu tutarlılık, izolasyon ve taşınabilirlik gibi avantajlarla birlikte ele aldık.
Nginx’i Docker ile konteynerize etme adımlarını, temel bir Nginx konteyneri çalıştırmaktan, özel konfigürasyonlarla kendi imajımızı oluşturmaya kadar detaylandırdık. Ardından, Nginx’i birden fazla arka uç uygulamasını tek bir erişim noktası üzerinden yönlendiren bir ters proxy olarak nasıl yapılandıracağımızı, pratik örnekler ve kod parçacıklarıyla gösterdik. Özellikle, Docker Compose kullanarak bu çoklu konteyner uygulamasını nasıl tek bir komutla yönetebileceğimizi öğrenmek, karmaşık mimarilerin basitleştirilmesi açısından önemli bir kilometre taşı oldu.
Docker ve Nginx’in birlikte kullanılması, günümüzün mikroservis tabanlı, bulut yerel uygulamaları için güçlü, esnek ve ölçeklenebilir bir altyapı sunar. Uygulama geliştiricileri, operasyon ekipleri ve sistem mimarları için bu iki teknolojinin entegrasyonu, dağıtım süreçlerini hızlandırır, ortam tutarsızlıklarını ortadan kaldırır ve genel sistem güvenilirliğini artırır.
Öğrendiğimiz bu bilgiler ışığında, aşağıdaki alanlarda daha fazla keşif yapmanız önerilir:
* SSL/TLS Entegrasyonu: Nginx üzerinde Let’s Encrypt ile ücretsiz SSL sertifikalarını otomatik olarak yapılandırma.
* Yük Dengeleme Stratejileri: Nginx’in farklı yük dengeleme algoritmalarını (least-connected, ip-hash vb.) daha detaylı inceleme.
* Gelişmiş Nginx Modülleri: Nginx’in sunduğu diğer modüllerle (örneğin, kimlik doğrulama, rate limiting) yeteneklerini genişletme.
* Docker Volume’ler ve Kalıcı Veri: Konteynerlerde kalıcı veri depolama için Docker volume’lerinin nasıl kullanılacağı.
* Docker Swarm veya Kubernetes: Büyük ölçekli konteyner orkestrasyonu için bu platformları inceleme.
Docker ve Nginx’in gücünü birleştirerek, uygulamalarınızı daha güvenilir, daha hızlı ve daha yönetilebilir bir şekilde dağıtabilir ve çalıştırabilirsiniz. Bu makalenin, bu yolda size sağlam bir temel sağladığını umuyoruz.