Traefik v3 ile Docker Compose'da 5 Uygulama
Traefik v3 ve Docker Compose kullanarak tek IP üzerinden 5 uygulamayı yönetin. Host rule yönlendirme ve acme.json hatalarını giderme yöntemlerini öğrenin.
Tek IP, beş uygulama, tek port 443
VPS cihazınızın tek bir genel IPv4 adresi ve tek bir TCP 443 portu bulunmaktadır. Gitea, uygulamanızın staging kopyası, dahili bir dashboard, bir durum sayfası ve bir webhook alıcısı gibi beş farklı hostname'i tek bir sunucuda çalıştırmak istiyorsunuz. Reverse proxy, :80 ve :443 portlarını yöneten, her istekteki Host header bilgisini okuyan ve isteği doğru konteynere ileten süreçtir. Traefik bu işlemi gerçekleştirir; certbot'u manuel olarak çalıştırmanıza gerek kalmadan her hostname için sertifika alır ve yeniler.
Traefik'i bir nginx server {} bloğundan ayıran özellik, konfigürasyonun kaynağıdır. nginx kullanırken bir dosyayı düzenleyip yeniden yüklemeniz gerekir ve sertifika yaşam döngüsü ayrı bir iş olarak kalır; bu, nginx üzerinde certbot ile Let's Encrypt sertifikaları oluştururken izlediğiniz, yenileme zamanlayıcısının web sunucusunun tamamen dışında çalıştığı iş akışıdır. Traefik'in Docker sağlayıcısı, Docker event akışını izler ve konteynerlerinizdeki labels bilgilerini okur: Host() kural etiketi içeren bir konteyner başlattığınızda, bir saniye içinde yönlendirmeye hazır hale gelir; konteyneri durdurduğunuzda ise rota kaybolur. Bu durum aynı zamanda bir risk teşkil eder. Etiketlerde (labels) tutulan konfigürasyon beş farklı yerde aynı anda bulunur ve hatalı bir etiket sessiz kalır; konteyner sadece yönlendirilmez ve Traefik herhangi bir hata bildirimi yapmaz.
Dört temel kavram
- Entrypoints, dinleme yapan soketlerdir. İki adet tanımlanacaktır:
:80üzerindewebve:443üzerindewebsecure. - Routers, bir isteği (
Host(...)) eşleştirir ve onu bir servise bağlar. Sertifikalar,tls.certresolveraracılığıyla her router için istenir. - Services, backend kısmıdır; bir container ve Docker ağı içinde dinlediği porttur.
- Middlewares, router ile servis arasında yer alır: temel kimlik doğrulama, IP izin listeleri, header yeniden yazma işlemleri ve yönlendirmeler.
Statik yapılandırma (entrypoints, providers, ACME), Traefik komut satırından veya traefik.yml üzerinden iletilir; bu yapılandırmayı değiştirmek Traefik'in yeniden başlatılmasını gerektirir. Dinamik yapılandırma (routers, services, middlewares) ise container label'larından gelir ve anlık olarak (hot-reload) güncellenir. Bu ikisinin karıştırılması, "flag hiçbir işe yaramıyor" hatasının yaygın sebebidir.
Compose dosyası
proxy adlı paylaşımlı bir Docker ağı temel yapıyı oluşturur. Traefik bir konteynere yalnızca her iki bileşen de bu ağ üzerindeyse erişebilir.
name: edge
networks:
proxy:
name: proxy
services:
traefik:
image: traefik:v3.5
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --providers.docker.network=proxy
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --entryPoints.web.http.redirections.entryPoint.to=websecure
- --entryPoints.web.http.redirections.entryPoint.scheme=https
- --certificatesresolvers.le.acme.email=you@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
# while you iterate, point at staging so a mistake costs nothing:
# - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --api.dashboard=true
- --log.level=INFO
- --accesslog=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
- traefik.http.routers.dashboard.entrypoints=websecure
- traefik.http.routers.dashboard.tls.certresolver=le
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.routers.dashboard.middlewares=dashboard-auth
- traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$REPLACE$$THIS
gitea:
image: gitea/gitea:1 # major-only pin keeps this demo copy-pasteable; pin an exact release in production
restart: unless-stopped
volumes:
- ./gitea:/data
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.gitea.rule=Host(`git.example.com`)
- traefik.http.routers.gitea.entrypoints=websecure
- traefik.http.routers.gitea.tls.certresolver=le
- traefik.http.services.gitea.loadbalancer.server.port=3000Önce docker compose up -d, ardından docker compose logs -f traefik yapılandırılmalıdır. Her ek uygulama, kendi router ismine, kendi Host() değerine ve kendi iç portuna sahip olan gitea bloğunun bir kopyasıdır. Docker üzerinde TLS ve yedekleme ile çalışan Nextcloud kurulumu da aynı şekilde eklenir; yayınlanan portlar kaldırılır, proxy ağına bağlanır ve hostname ile sertifika işlemleri için router etiketleri kullanılır.
Buradaki beş detay önemlidir.
traefik.enable=true etiketine sahip olmayan bir konteyner, exposedByDefault=false nedeniyle Traefik tarafından görünmez kılınır. Bu etiketi bırakırsanız, kontrol amaçlı çalıştırılan geçici postgres dahil olmak üzere başlatılan her konteyner için bir rota oluşturulur.
providers.docker.network=proxy etiketi, bir konteyner birden fazla ağa bağlı olduğunda Traefik'in hangi ağı kullanacağını belirler. Bu etiket eksik olursa Traefik yanlış konteyner IP adresini seçebilir; bu durum, uygulama hatası gibi görünen bir 502 hatasına yol açar.
loadbalancer.server.port=3000 konteyner içindeki porttur; Gitea burada 3000 portunu dinler. Hiçbir uygulama konteynerinin port yayınlamadığına, yalnızca Traefik'in port yayınladığına dikkat edilmelidir.
web entrypoint üzerindeki yönlendirme, düz metin isteklerini 308 ile HTTPS'e dönüştürür. Port 80 yine de açık kalmalıdır: ACME HTTP doğrulaması ve sadece hostname yazan kullanıcılar için bu gereklidir.
basic-auth hash değerindeki çift $$ kullanımı bir yazım hatası değil, Compose kaçış karakteridir. Bu değer htpasswd -nbB admin 'your-password' (apache2-utils paketi) ile oluşturulmalı ve ardından her $ karakteri çift yazılmalıdır.
Sertifika ve acme.json tuzağı
tlschallenge=true, TLS-ALPN-01 protokolünü seçer: Let's Encrypt, 443 portu üzerinden sunucuya bağlanır ve Traefik, TLS el sıkışması (handshake) sırasında doğrulamayı yanıtlar. Alternatif yöntem ise 80 portu üzerindeki HTTP-01 yöntemidir; bu yöntem için Traefik'in command: listesindeki tlschallenge satırı şu iki seçenekle değiştirilmelidir:
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=webHer iki yöntem de çalışır. Her iki yöntem için de ana makine adı (hostname) için tanımlanmış olan genel DNS kaydının halihazırda VPS adresinize yönlenmiş olması gerekir; sertifika otoritesi ismi çözer ve dışarıdan bağlantı kurar. Önce A (ve AAAA) kaydını oluşturun, dig +short git.example.com ile doğrulayın ve ardından Traefik'i başlatın.
İnsanların bir akşamını kaybetmesine neden olan tuzak şudur: Traefik, ACME hesap anahtarını ve oluşturulan tüm sertifikaları tek bir acme.json dosyasında tutar. Eğer bu dosya grup veya diğer kullanıcılar tarafından okunabilir durumdaysa, Traefik şuna benzer bir hata satırı yazdırır ve durur:
error: unable to get ACME account: permissions 644 for /letsencrypt/acme.json are too open, please use 600Kesin çözüm yukarıda belirtilendir: dizini bind-mount yöntemiyle bağlayın ve dosyanın doğru mod ile Traefik tarafından oluşturulmasına izin verin. Eğer acme.json dosyasını touch ile oluşturduysanız, umask ayarı nedeniyle dosya 644 modunda kalmış olabilir. Dosyayı ana makinede şu komutla düzeltin:
chmod 600 ./letsencrypt/acme.json
docker compose restart traefikBu dizini uygulama hacimlerinizle (volumes) birlikte yedekleyin. Dosyanın kaybı telafi edilebilir (sertifikalar yeniden oluşturulabilir), ancak beş ana makine adını aynı anda yeniden oluşturmaya çalışmak hız sınırlarına (rate limits) takılmanıza neden olur.
Geliştirme aşamasındayken staging CA kullanın. caserver satırının yorum satırı olma durumunu kaldırın, tüm rotaların çalıştığından emin olun, ardından üretim sertifikalarının sıfırdan talep edilmesi için satırı tekrar yorum satırı yapın ve acme.json dosyasını silin. Üretim ortamındaki Let's Encrypt, aynı ana makine adı seti için haftada beş kopya sertifikaya izin verir ve aynı isim için tekrarlanan başarısız doğrulamaları kısıtlar. Staging, çok daha esnek limitlerle güvenilmeyen sertifikalar oluşturur; tarayıcınız uyarı verir ve bu uyarı işlemin başarılı olduğunun işaretidir.
Dashboard bir demo değil, bir kontrol yüzeyidir
Çoğu hızlı kurulum kılavuzu, dashboard'u port 8080 üzerinden kimlik doğrulaması olmadan sunan --api.insecure=true ayarını yapar. Bu durum, ağ topolojisini, hostname bilgilerini, middleware isimlerini ve backend portlarını tarama yapan herkese açıklayan halka açık IP adresine sahip bir sunucuda risk oluşturur.
Yukarıdaki traefik servisi bu duruma alternatif sunar: dashboard, diğer uygulamalar gibi gerçek bir hostname üzerinden, TLS ile ve basicauth arkasından yönlendirilir. service=api@internal, router'ı Traefik'in yerleşik API'sine bağlar. Güvenliği artırmak için soldan sağa uygulanan bir IP izin listesi (allow-list) zinciri eklenmelidir. Ofis adresiniz dinamik ise, aralığı aynı VPS üzerinde kendi host ettiğiniz bir WireGuard VPN tarafından sağlanan alt ağ (subnet) olarak ayarlayın ve dashboard'a yalnızca tünel üzerinden erişin:
- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.7/32
- traefik.http.routers.dashboard.middlewares=office,dashboard-authDocker socket root yetkisindedir
/var/run/docker.sock, host üzerinden / mount eden bir container oluşturabilen bir API'dir. Bu API'ye erişim, makinede root yetkisine eşdeğerdir; Traefik, label bilgilerini okumak için bu erişime ihtiyaç duyar.
:ro mount işlemi üzerinde tutulabilir, ancak sağladığı avantaj netleştirilmelidir: bu işlem socket dosyasını salt okunur yapar. Bu durum, Docker API'sine gönderilen POST isteklerini durdurmaz. Asıl çözüm, socket dosyasını Traefik'e doğrudan vermemek ve araya bir filtreleme proxy'si yerleştirmektir:
dockerproxy:
image: tecnativa/docker-socket-proxy # pin the current tag
restart: unless-stopped
environment:
CONTAINERS: 1
NETWORKS: 1
POST: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxySocket volume'u Traefik'ten kaldırın ve provider ayarını proxy'ye yönlendirin:
--providers.docker.endpoint=tcp://dockerproxy:2375Traefik, container ve network'ler için salt okunur erişimini korur ancak yeni bir nesne oluşturma yeteneğini kaybeder.
Firewall, portlar ve herkesin yanlış bildiği kural
İki açık port ve SSH:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableDocker tarafından yayınlanan portlar ufw kurallarını devre dışı bırakır. Docker, kendi iptables kurallarını ufw zincirlerinden önce değerlendirilecek şekilde ekler. Bu nedenle, ports: ["3000:3000"] ile başlatılan bir konteyner, ufw üzerinde bir deny kuralı bulunsa dahi internetten erişilebilir durumdadır. Sorun yapısal bir durumdur, firewall yapılandırmasıyla ilgili değildir: Sadece Traefik üzerinden port yayınlayın ve diğer tüm konteynerlere networks: [proxy] dışında bir yetki vermeyin. Eğer bir bileşenin host makinesine ulaşması gerekiyorsa, bunu loopback adresine bağlayın — "127.0.0.1:3000:3000".
Troubleshooting: karşılaşabileceğiniz hatalar
404 page not found, Traefik tarafından sunulur. Hiçbir router eşleşmedi. Olasılık sırasına göre: container traefik.enable=true (exposedByDefault=false ayarlanmış şekilde) eksik; Host() kuralı yazdığınız isimle eşleşmiyor; bir label içindeki router ismi ile diğerindeki router ismi farklı (routers.gitea.rule ve routers.gitea.entrypoints aynı kelime olmalıdır); veya hostname değerini backtick yerine tırnak içine aldınız. Traefik v3, matcher içlerinde backtick kullanımını gerektirir.
502 Bad Gateway. Bir router eşleşti ancak backend'e ulaşılamadı. Neredeyse her zaman container proxy ağında değildir — docker inspect -f '{{json .NetworkSettings.Networks}}' gitea kontrol edilmelidir. Diğer olası sebep hatalı bir loadbalancer.server.port kullanımıdır: yayınlanmış bir port verdiniz veya uygulama başka bir yerden dinleme yapıyor. Log, denemeyi şu şekilde belirtir: dial tcp 172.18.0.5:8080: connect: connection refused.
Tarayıcı uyarı verir ve sertifika TRAEFIK DEFAULT CERT için düzenlenir. Bu hostname için bir sertifika mevcut değil ve Traefik kendi imzalı placeholder sertifikasını sundu. ACME satırlarını okuyın:
unable to obtain ACME certificate for domains "git.example.com" ...
acme: error: 400 ... DNS problem: NXDOMAIN looking up A for git.example.comDNS henüz kutuya yönlendirilmiş değil. Kaydı düzeltin, TTL süresinin dolmasını bekleyin ve Traefik'i yeniden başlatın.
HTTP challenge üzerinde Invalid response from http://git.example.com/.well-known/acme-challenge/...: 80 portu dışarıdan Traefik'e ulaşmıyor — bu genellikle ufw değil, VPS önündeki sağlayıcı seviyesindeki bir firewall'dur.
Sertifikalar asla düzenlenmiyor ve DNS'iniz Cloudflare üzerinde turuncu bulut (orange cloud) modunda. Cloudflare TLS işlemini kendi uç noktasında sonlandırır ve TLS-ALPN-01 bu şekilde tamamlanamaz. Sertifika düzenlenirken kaydı DNS-only olarak ayarlayın veya bir API token ile DNS-01 challenge yöntemine geçin. DNS-01, wildcard sertifika sağlayan tek challenge yöntemidir.
Redirect loop. Traefik'in önündeki bir yapı TLS işlemini zaten sonlandırıyor ve :80 portuna düz metin (plaintext) iletiyor; entrypoint yönlendirmesi ise bunu tekrar HTTPS'e gönderiyor. İki yönlendirmeden birini kaldırın.
Çalışmaya devam etmesini sağlamak
Docker birimi boot sırasında etkinleştirilmelidir (systemctl is-enabled docker) ve restart: unless-stopped yeniden başlatma sonrası stack yapısını geri getirir. Daha belirgin bir yönetim için, RemainAfterExit=yes bayrağı ile docker compose -f /srv/edge/compose.yml up -d komutunu çalıştıran küçük bir systemd birimi, systemctl status edge ve sıralama kontrolü sağlar.
Traefik etiketini sabitleyin (traefik:v3.5, asla latest kullanmayın). v2'den v3'e geçiş, kural sözdizimini ve sağlayıcı isimlerini değiştirmiştir; denetimsiz bir latest, artık anlamadığı bir yapılandırmayı hatalı şekilde yeniden yükleyecektir. Güncellemeyi bilinçli yapın: geçiş notlarını okuyun, etiketi yükseltin, docker compose up -d traefik uygulayın ve logları izleyin. Eğer hala bir v2 etiketi kullanıyorsanız, Traefik v2'den v3'e geçiş kılavuzu tüm yeniden adlandırmaları, uyumluluk modunu ve sertifikaları koruyan geri alma işlemlerini detaylandırmaktadır.
./letsencrypt ve her uygulamanın veri birimini (data volume) yedekleyin. Traefik, compose dosyasından yeniden oluşturulamayacak başka bir durum bilgisi (state) tutmaz.
Ölçeklendirme aşamasında oluşan sorunlar
İlk kısıtlama veri işleme kapasitesi değil, tekil sunucudur: Bir VPS üzerinde çalışan tek bir Traefik, beş uygulama için tek hata noktasıdır (single point of failure) ve acme.json dosya tabanlı depolama kullanır; iki farklı Traefik örneğinin bu dosyaya yazması veri bozulmasına yol açar. Ölçeklendirme yapmak, sertifika depolamasını dosyadan taşımak veya TLS sonlandırma işlemini başka bir noktada gerçekleştirmek anlamına gelir.
İkinci kısıtlama uzun süreli bağlantılardır. Server-sent events, büyük yüklemeler ve yavaş istemciler, giriş noktasındaki (entrypoint) yanıt verme zaman aşımı sürelerine takılır; --entryPoints.websecure.transport.respondingTimeouts.readTimeout ve writeTimeout ile idleTimeout kardeşleri bu ayarların yapıldığı parametrelerdir. WebSockets, ek bir yapılandırma gerektirmeden iletilir.
Üçüncü kısıtlama disk alanıdır. --accesslog=true verileri stdout birimine yazar ve Docker'ın json-file sürücüsü, bir sınır konulmadığı sürece bu verileri sonsuza kadar saklar. Traefik servisi üzerinde logging.options.max-size değerini ayarlayın veya erişim günlüğünü (access log) bir dosyaya yazıp döndürün (rotate).
Tüm bunlar için bir orkestratör gerekmez. Ancak gerçek bir IP adresine sahip ve 80 ile 443 portları dünyaya açık olan, kontrol edilen bir sunucu gereklidir; tek bir küçük VPS tüm bağımlılık listesini oluşturur.
FAQ
Traefik kullanıyorsam hala certbot'a ihtiyacım var mı?
Hayır. Traefik'in ACME çözücüsü, yönlendirdiği her hostname için sertifika talep eder ve yeniler; tüm bunları acme.json içerisinde saklar. Nginx veya başka bir sunucu TLS işlemini kendisi sonlandırıyorsa Certbot doğru araçtır; her iki aracı aynı hostname'ler için çalıştırmak Let's Encrypt hız sınırlarını (rate limits) tüketir.
Konteynerim Traefik üzerinden neden 404 döndürüyor?
Traefik tarafından sunulan 404 hatası, hiçbir router'ın istekle eşleşmediği anlamına gelir. Konteynerin traefik.enable=true içerdiğinden (exposedByDefault=false ayarlandığında zorunludur), Host() değerinin yazdığınız isimle eşleştiğinden ve router isminin o uygulama için her etikette (label) aynı olduğundan emin olun. Traefik v3, matcher içerisinde tırnak işareti yerine backtick kullanılması gerekmektedir.
Buradaki 404 ile 502 arasındaki fark nedir?
404, yönlendirmenin hiç gerçekleşmediği anlamına gelir; 502 ise bir router'ın eşleştiği ancak backend'in bağlantıyı reddettiği anlamına gelir. Yaygın 502 nedenleri; bir konteynerin proxy ağına bağlı olmaması ve bir loadbalancer.server.port değerinin uygulamanın konteyner içinde dinlediği port yerine yayınlanmış bir porta işaret etmesidir. Erişim günlüğü (access log), Traefik'in aradığı tam adresi belirtir.
Docker socket'i read-only olarak bağlamak yeterli mi?
:ro bayrağı soket dosyasını read-only yapar, ancak arkasındaki API'yi yapmaz; POST istekleri hala üzerinden iletilir ve Docker API erişimi ana makinede root yetkisine eşdeğerdir. Daha güvenli yapı, yukarıda gösterilen ve Traefik'e yalnızca konteyner ve ağ okuma yetkisi veren, yazma işlemlerini ise tamamen engelleyen docker-socket-proxy konteyneridir.
Traefik wildcard sertifika oluşturabilir mi?
Yalnızca DNS-01 challenge yöntemiyle ve DNS sağlayıcınız için bir API token'ı ile mümkündür. TLS-ALPN-01 ve HTTP-01 yöntemleri yalnızca tek bir hostname doğrular ve wildcard sertifika oluşturamaz. Cloudflare gibi bir CDN, VPS'inizin önünde TLS işlemini sonlandırıyorsa ve diğer iki challenge tamamlanamıyorsa, çözüm DNS-01 yöntemidir.