Traefik, reverse proxy, który sam znajduje usługi
Traefik to napisany w Go reverse proxy i load balancer, który konfigurację tras czyta wprost z Dockera, Kubernetesa, Consula czy Nomada, zamiast z pliku pisanego ręcznie. Bieżące wydanie to 3.7.11, licencja MIT, a certyfikaty od Let's Encrypt zamawia i odnawia sam proces serwera.
Skąd Traefik bierze konfigurację
Różnica między Traefikiem a klasycznym proxy sprowadza się do jednego pojęcia: provider. W nginxie albo w HAProxy plik konfiguracyjny jest jedynym źródłem prawdy o trasach, a każda zmiana wymaga edycji tego pliku i przeładowania procesu. Traefik dzieli konfigurację na dwie warstwy o zupełnie innym cyklu życia.
Warstwa statyczna to rzeczy, które ustala się przy starcie procesu i których nie da się zmienić bez restartu: jakie porty nasłuchują, jakie źródła konfiguracji są włączone, jak wygląda dostęp do urzędu certyfikacji. Warstwa dynamiczna to trasy, usługi i middleware, czyli wszystko, co zmienia się przy każdym wdrożeniu. Ta druga warstwa nie pochodzi z pliku, tylko z providera.
Provider to adapter do konkretnego systemu. Provider Docker podpina się do gniazda demona i obserwuje zdarzenia o startach i zatrzymaniach kontenerów, czytając z nich etykiety zaczynające się od traefik.. Provider Kubernetes CRD czyta zasoby IngressRoute z grupy traefik.io/v1alpha1. Są też providery dla Consula, Nomada, ECS, Redisa oraz zwykły provider plikowy, gdy trasy chce się jednak wpisać ręcznie. Kilka providerów może działać naraz i ich wyniki się sumują.
Skutek praktyczny jest taki, że nowy kontener z odpowiednim zestawem etykiet zaczyna odbierać ruch bez dotykania proxy. Nie ma polecenia reload, nie ma szablonu generującego plik, nie ma kroku w potoku wdrożeniowym, który mógłby ten plik zepsuć. Za wygładzenie serii zdarzeń odpowiada providers.providersThrottleDuration z domyślną wartością 2 sekund, więc dziesięć kontenerów wstających jednocześnie daje jedną przebudowę tablicy tras, a nie dziesięć.
Cena tego wygodnego mechanizmu też jest realna. Konfiguracja przestaje mieszkać w jednym miejscu. Rozjeżdża się po plikach docker-compose.yml, manifestach Kubernetesa i adnotacjach, a odpowiedź na pytanie „dlaczego ten host trafia tam, gdzie trafia" wymaga zajrzenia do panelu Traefika albo do API, a nie do jednego pliku tekstowego.
Wersja, licencja i trzeci dowód
Bieżące wydanie z gałęzi trzeciej to 3.7.11 z 21 sierpnia 2026 roku. Że utrzymywane są równolegle trzy linie, widać po 3 sierpnia, kiedy jednego dnia ukazały się 3.7.10, 3.6.25 oraz 2.11.55, czyli po jednym wydaniu z każdej z nich. Kod buduje się z go 1.26.0 zadeklarowanym w go.mod.
Licencję sprawdziłem w trzech miejscach, bo to najczęstsze źródło pomyłek w audytach zależności.
Pierwsze źródło to plik w repozytorium. github.com/traefik/traefik trzyma go pod nazwą LICENSE.md, nie LICENSE, i zawiera pełny tekst MIT z notą „Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs". Containous to dawna nazwa firmy, która przemianowała się na Traefik Labs w 2020 roku, i ten ślad widać wprost w nagłówku.
Drugie źródło to rejestr pakietów. Tu robi się nieprzyjemnie i temu poświęcam osobną sekcję poniżej, bo pakiet npm o nazwie traefik nie pochodzi od Traefik Labs.
Trzecie źródło to zawartość faktycznie dystrybuowanego artefaktu i tutaj wynik jest podzielony. Archiwum traefik_v3.7.11_linux_amd64.tar.gz z sekcji wydań waży około 48 MB i zawiera dokładnie trzy wpisy: CHANGELOG.md, LICENSE.md oraz binarium traefik o rozmiarze około 176 MB po rozpakowaniu. Tekst licencji jest więc na miejscu, w przeciwieństwie do wielu projektów dystrybuujących same binaria. Natomiast oficjalny obraz kontenera go nie zawiera. Dockerfile z repozytorium traefik/traefik-library-image dla wariantu alpine rozpakowuje archiwum poleceniem, które wyciąga z niego wyłącznie jeden wpis: tar xzvf /tmp/traefik.tar.gz -C /usr/local/bin traefik. LICENSE.md i CHANGELOG.md zostają w odrzuconym archiwum. Jeśli Twój skaner obrazów szuka pliku licencyjnego wewnątrz warstw, dla traefik:v3.7.11 go nie znajdzie, mimo że projekt jest czysto MIT. Trzeba wtedy wpisać licencję ręcznie albo wziąć ją z etykiet OCI, które obraz ustawia, w tym org.opencontainers.image.source wskazującej na repozytorium.
Pułapka nazwy: pakiet npm traefik
Pakiet traefik w rejestrze npm nie jest wydawany przez Traefik Labs. Najnowsza wersja to 1.0.0 opublikowana 2 sierpnia 2021 roku, całe repozytorium wpisu w rejestrze nie zmieniło się od maja 2022, a pole repository.url wskazuje na github.com/hello-seam/node-traefik. To nieoficjalna nakładka jednej osoby.
W odróżnieniu od kilku innych podszywek, ta ma kod: czternaście plików i około 14,8 kB po rozpakowaniu. index.js eksportuje funkcję start, która zamienia obiekt konfiguracji na TOML przez json2toml i uruchamia binarium jako proces potomny. Problem leży gdzie indziej. Plik download-traefik.js ma zaszytą na sztywno wersję do pobrania i pyta o nią interfejs programistyczny GitHuba bez uwierzytelnienia.
// node_modules/traefik/download-traefik.js, wersja 1.0.0 z 2021 roku
const releaseVersionToUse = "2.4.9"
const releaseAPIUrl =
`https://api.github.com/repos/traefik/traefik/releases/tags/v${releaseVersionToUse}`Instalacja pakietu w 2026 roku ściąga zatem Traefika 2.4.9 z 2021 roku, czyli wydanie starsze o pięć lat i o dwie gałęzie główne od tego, co jest utrzymywane. Pobieranie odbywa się w skrypcie install, więc dzieje się automatycznie podczas npm install, a niewierzytelnione zapytanie do GitHuba wpada w limit szybkości przy pierwszym zbiegu okoliczności w środowisku ciągłej integracji.
Osobna ciekawostka dotyczy licencji. Paczka npm deklaruje "license": "MIT" i zawiera plik LICENSE.md, ale jego treść to skopiowany tekst licencji Traefika z notą „Copyright (c) 2016-2020 Containous SAS; 2020-2021 Traefik Labs". Narzędzie zbierające metadane licencyjne z node_modules zaraportuje więc, że w projekcie jest komponent objęty prawami Traefik Labs, mimo że kod nakładki napisał ktoś inny. To sytuacja, w której wszystkie trzy źródła mówią „MIT", a mimo to obraz jest mylący.
# to NIE jest pakiet od Traefik Labs
npm view traefik version # 1.0.0
npm view traefik repository.url # git+https://github.com/hello-seam/node-traefik.git
npm view traefik time.modified # 2022-05-22T03:25:33.105Z
# tak instaluje się prawdziwy Traefik
docker pull traefik:v3.7.11
curl -sL https://github.com/traefik/traefik/releases/download/v3.7.11/traefik_v3.7.11_linux_amd64.tar.gz \
| tar -xz traefik LICENSE.mdTraefik jest programem w Go dystrybuowanym jako pojedyncze binarium albo obraz kontenera. Nie ma oficjalnego kanału npm, nie ma oficjalnego pakietu w PyPI i nie ma powodu, żeby proxy wjeżdżało do projektu przez package.json.
Konfiguracja statyczna i dynamiczna
Konfiguracja statyczna trafia do pliku, do zmiennych środowiskowych albo do flag wiersza poleceń. Domyślnie proces szuka pliku pod czterema ścieżkami bazowymi, w tej kolejności: /etc/traefik/traefik, $XDG_CONFIG_HOME/traefik, $HOME/.config/traefik i ./traefik, próbując rozszerzeń toml, yaml i yml. Własną lokalizację wskazuje flaga --configFile.
# /etc/traefik/traefik.yml, warstwa statyczna
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: "proxy"
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: webJedno pole z tego przykładu zasługuje na szczególną uwagę. providers.docker.exposedByDefault ma domyślną wartość true, co oznacza, że bez tej linii Traefik wystawi do internetu każdy kontener widoczny na gnieździe demona, w tym bazę danych i kolejkę. Domyślna reguła to Host({{ normalize .Name }}), czyli nazwa kontenera jako nazwa hosta. Ustawienie false odwraca logikę: routowane są tylko kontenery z etykietą traefik.enable=true.
Warstwa dynamiczna dla Dockera to etykiety. Nazwa routera i nazwa usługi w środku etykiety są dowolne i służą wyłącznie do wiązania powiązanych ze sobą wpisów.
services:
traefik:
image: traefik:v3.7.11
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./letsencrypt:/letsencrypt
networks: [proxy]
api:
image: ghcr.io/example/api:1.4.2
labels:
- traefik.enable=true
- traefik.http.routers.api.rule=Host(`api.example.com`)
- traefik.http.routers.api.entrypoints=websecure
- traefik.http.routers.api.tls.certresolver=letsencrypt
- traefik.http.services.api.loadbalancer.server.port=8080
- traefik.docker.network=proxy
networks: [proxy]W Kubernetesie ten sam opis przybiera postać zasobu własnego. Grupa to traefik.io/v1alpha1, a przed pierwszym użyciem trzeba wgrać definicje zasobów i reguły dostępu z katalogu dynamic-configuration w repozytorium.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-route
namespace: apps
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`api.example.com`) && PathPrefix(`/v1`)
priority: 10
services:
- kind: Service
name: api
port: 8080
passHostHeader: true
tls:
certResolver: letsencryptPole priority rozstrzyga kolizje między regułami. Bez niego Traefik sortuje reguły po długości ich zapisu tekstowego, co bywa zaskakujące, gdy dwie trasy pasują do tego samego żądania.
Certyfikaty ACME i ich granice
Certyfikatami zajmuje się mechanizm nazwany certificate resolver. Deklaruje się go raz w warstwie statycznej, a potem wskazuje po nazwie z poziomu trasy przez tls.certresolver. Wymagane pola to email i storage, przy czym storage domyślnie wskazuje na acme.json w katalogu roboczym.
Wyzwanie wybiera się jedno z trzech. httpChallenge.entryPoint wskazuje punkt wejścia nasłuchujący na porcie 80, który musi być osiągalny z zewnątrz. tlsChallenge wykorzystuje TLS-ALPN-01 na porcie 443. dnsChallenge.provider sięga po wpis TXT w strefie DNS i tylko ta droga daje certyfikaty wieloznaczne, bo do wpisu w DNS potrzebne są dane dostępowe do dostawcy, przekazywane przez zmienne środowiskowe zgodnie z listą biblioteki Lego.
Domyślny urząd to https://acme-v02.api.letsencrypt.org/directory, domyślny typ klucza to RSA4096, a Traefik zakłada certyfikaty 90-dniowe i rozpoczyna odnawianie 30 dni przed wygaśnięciem. Dla urzędów wystawiających certyfikaty o innym okresie ważności służy certificatesDuration podawane w godzinach, z wartością domyślną 2160.
Tu leży najważniejsze ograniczenie darmowego wariantu i trzeba je znać przed projektowaniem wdrożenia. Magazyn acme.json to zwykły plik na dysku, a nie rozproszony magazyn ze spójnością. Dwie repliki Traefika czytające ten sam plik przez współdzielony wolumin będą się o niego biły i obie zamówią własne certyfikaty, marnując limity urzędu. Rozwiązaniem po stronie producenta jest funkcja o nazwie Distributed Let's Encrypt, dostępna wyłącznie w płatnym Traefik Hub. W wariancie otwartym poprawne wyjścia to jedna replika terminująca TLS, certyfikaty wystawiane poza Traefikiem i wstrzykiwane jako sekrety, albo mechanizm zewnętrzny w rodzaju cert-managera w Kubernetesie.
Traefik Proxy, Hub i Enterprise
Dokumentacja i strona produktowa mieszają trzy produkty na jednym poziomie nawigacji, więc bardzo łatwo napisać przykład korzystający z funkcji, której w wersji darmowej po prostu nie ma.
| Funkcja | Traefik Proxy (MIT) | Traefik Hub API Gateway | Traefik Hub API Management |
|---|---|---|---|
| Automatyczne wykrywanie usług | tak | tak | tak |
| HTTP/2, HTTP/3, TCP, UDP, gRPC, WebSockets | tak | tak | tak |
| Metryki i ślady OpenTelemetry | tak | tak | tak |
| Wdrożenia kanarkowe, panel widoku ingressu | tak | tak | tak |
| OIDC, LDAP, JWT, HMAC, klucze API, Open Policy Agent | nie | tak | tak |
| Wbudowany firewall aplikacyjny | nie | tak | tak |
| Distributed Let's Encrypt, rozproszony rate limiting | nie | tak | tak |
| Buforowanie HTTP, panel widoku klastra, wiele klastrów | nie | tak | tak |
| FIPS 140-2 i 140-3 | nie | tak | tak |
| Portal deweloperski, wersjonowanie i plany API | nie | nie | tak |
| Tryb odcięty od sieci, mockowanie API | nie | nie | tak |
Cennika nie ma. Strona traefik.io/pricing zestawia trzy kolumny, ale przy Traefik Proxy jest przycisk pobrania i osobna oferta wsparcia dla wersji otwartej, a przy obu wariantach Hub widnieje wyłącznie „Get pricing". Żadnej liczby, żadnego progu, żadnej informacji o jednostce rozliczeniowej. Wycenę trzeba wyciągnąć rozmową z handlowcem, co przy porównywaniu z konkurencją oznacza tygodnie zamiast minut.
Traefik Enterprise, opisywany jeszcze przed rokiem jako osobny produkt, ma na własnej stronie baner mówiący wprost, że został przeniesiony do Traefik Hub. Dokumentacja pod doc.traefik.io/traefik-enterprise/ nadal odpowiada, więc istniejące wdrożenia mają gdzie zaglądać, ale nowy projekt nie ma po co tam wchodzić. To także sygnał o ryzyku przywiązania do dostawcy: linia płatna zmieniła nazwę i kształt, a klienci musieli przejść na nową.
Uczciwie trzeba dodać, że sam Traefik Proxy jest kompletny. Reverse proxy, load balancer, kontroler ingressu, middleware z przekierowaniami, nagłówkami, uwierzytelnianiem podstawowym i ograniczaniem liczby żądań na instancję, katalog wtyczek pisanych w Go. Dla pojedynczego serwera albo jednego klastra to komplet. Płatne warianty zaczynają mieć sens dopiero przy wielu klastrach i wymaganiach zgodnościowych.
Traefik a Caddy i nginx
Caddy rozwiązuje częściowo ten sam problem: też jest w Go, też sam bierze certyfikaty i też chce zastąpić parę nginx plus certbot. Różnica siedzi w tym, skąd bierze się lista tras.
| Kryterium | Traefik Proxy 3.7 | Caddy 2 | nginx z certbotem |
|---|---|---|---|
| Źródło tras | providery: Docker, Kubernetes, Consul, Nomad, plik | Caddyfile albo JSON przez admin API | pliki w katalogu konfiguracyjnym |
| Reakcja na nowy kontener | automatyczna, bez przeładowania | edycja pliku i przeładowanie | edycja pliku i przeładowanie |
| Certyfikaty | certificatesResolvers z magazynem plikowym | wbudowane, włączone domyślnie | osobny proces i harmonogram |
| Cała konfiguracja w jednym pliku | nie, trasy siedzą w etykietach | tak | tak |
| Licencja | MIT | Apache 2.0 | BSD 2-clause |
| Płatny wariant | Traefik Hub, cena na zapytanie | brak, finansowanie ze sponsoringu | brak w rdzeniu |
Wybór rozstrzyga zmienność środowiska, a nie sympatia do składni. Przy stałym zestawie kilku usług, gdzie adresy zmieniają się raz na kwartał, plik Caddy'ego jest po prostu lepszy: mieści się na ekranie, wchodzi do repozytorium, przegląda się go w minutę i przy awarii widać całą prawdę w jednym miejscu. Traefik w takim układzie dokłada demona obserwującego zdarzenia, żeby wykryć zmianę, która i tak nie zachodzi.
Przy dziesiątkach kontenerów wstających i gasnących w rytmie wdrożeń proporcje się odwracają. Generowanie pliku Caddy'ego z szablonu i przeładowywanie go przy każdej zmianie odtwarza dokładnie tę mechanikę, którą Traefik ma wbudowaną, tyle że własnym kodem, który trzeba utrzymywać i który potrafi zawieść po cichu. Etykieta przy kontenerze mieszka obok definicji usługi, więc wdrożenie i trasa zmieniają się w jednej rewizji.
Warto też odnotować, że wiele platform wdrożeniowych rozstrzyga ten wybór za Ciebie. Coolify stawia Traefika jako domyślne proxy i sam dorzuca etykiety do uruchamianych kontenerów. Railway i Fly.io mają własne warstwy sieciowe i tam pytanie w ogóle nie powstaje. Jeśli usługi za proxy to głównie PostgreSQL i Redis, czyli protokoły inne niż HTTP, potrzebny jest router TCP z regułą HostSNI, a nie zwykły router HTTP.
Typowe błędy
Pozostawienie exposedByDefault na wartości domyślnej. Efektem jest publiczny adres dla każdego kontenera w sieci, łącznie z tymi, które nigdy nie miały wyjść na zewnątrz.
Montowanie /var/run/docker.sock do zapisu. Dostęp do gniazda demona jest równoważny uprawnieniom administratora na hoście, więc proxy wystawione do internetu z takim woluminem to poważne ryzyko. Minimum to przyrostek :ro, lepiej pośrednik ograniczający zakres wywołań.
Dwie repliki na jednym pliku acme.json. Skutkiem są zdublowane zamówienia certyfikatów i wyczerpanie tygodniowego limitu urzędu, po czym domena zostaje bez ważnego certyfikatu do końca okna limitu.
Kontener i Traefik w różnych sieciach Dockera. Trasa powstaje, panel ją pokazuje, a odpowiedzią jest błąd bramy, bo proxy nie ma jak połączyć się z adresem docelowym. Etykieta traefik.docker.network albo pole providers.docker.network rozstrzyga sprawę.
Pominięcie loadbalancer.server.port, gdy obraz udostępnia więcej niż jeden port. Traefik zgaduje wtedy port i zgaduje źle.
Wystawienie panelu z api.insecure=true na publicznym punkcie wejścia. Panel pokazuje pełną tablicę tras i konfigurację middleware, czyli mapę infrastruktury.
Przenoszenie konfiguracji z gałęzi drugiej do trzeciej bez lektury przewodnika migracji. Nazwy providerów, składnia reguł i część pól przeszły zmiany niezgodne wstecz, a stary plik potrafi wstać z częścią tras po cichu pominiętych.
FAQ
Czy pakiet npm o nazwie traefik to oficjalny Traefik?
Nie. Wersja 1.0.0 z 2 sierpnia 2021 roku pochodzi z repozytorium hello-seam/node-traefik i jest nieoficjalną nakładką jednej osoby. Zawiera działający kod, ale ma zaszytą wersję Traefika 2.4.9 i pobiera ją z niewierzytelnionego zapytania do interfejsu GitHuba. Oficjalna dystrybucja to binarium z sekcji wydań albo obraz traefik.
Jaka jest licencja Traefika i gdzie jej szukać?
MIT, w pliku LICENSE.md w katalogu głównym repozytorium, z notą praw autorskich Containous SAS i Traefik Labs. Plik jest też w archiwum wydania, ale nie ma go w oficjalnym obrazie kontenera, bo Dockerfile wypakowuje z archiwum wyłącznie binarium.
Czy Traefik obsłuży automatyczne certyfikaty przy kilku replikach?
Nie w wersji otwartej. Magazyn acme.json to plik i nie ma w nim koordynacji między instancjami. Funkcja Distributed Let's Encrypt należy do płatnego Traefik Hub. Przy wielu replikach sensowniejsze jest wystawianie certyfikatów poza proxy, na przykład cert-managerem.
Ile kosztuje Traefik Hub?
Cennik nie jest publiczny. Strona cenowa przy obu wariantach Hub pokazuje wyłącznie przycisk „Get pricing", bez stawki, progu i jednostki rozliczeniowej. Wycenę trzeba uzyskać od działu handlowego.
Traefik czy Caddy do jednego serwera z kilkoma usługami?
Przy stałym zestawie usług Caddy zwykle wypada lepiej, bo cała konfiguracja mieści się w jednym krótkim pliku. Traefik zaczyna się opłacać, gdy usługi wstają i gasną same, a lista tras zmienia się przy każdym wdrożeniu.
Czy Traefik przepuści ruch inny niż HTTP?
Tak. Poza routerami HTTP są routery TCP z regułami opartymi o HostSNI oraz routery UDP. Dzięki temu za jednym punktem wejścia można postawić także usługi bazodanowe, choć bez nazwy w SNI rozróżnienie kilku usług na jednym porcie nie jest możliwe.
Źródła: repozytorium projektu, strona z cennikiem, wpis pakietu w rejestrze npm.