Kubernetes で Traefik proxy を使う
Traefik proxy はモダンで多機能な OSS の proxy サーバーです。nginx のように別サーバーへの proxy や負荷分散、ユーザー認証を行うことができ、k8s で使用する場合は ingress controller としても使用できます。
traefik を docker で使用する記事は割と多いですが、kubernetes 上で使用する記事はあまりなさそうだったので試してみます。
インストール
k8s クラスタ上に traefik をデプロイして svc や ingress リソースを管理するためには ClusterRole や ServiceAccount の設定 の作成が必要になります。ただこのあたりのリソースは helm chart でまとめてインストールできるのでこちらを使うのが楽です。
リポジトリを追加してインストール
helm repo add traefik https://traefik.github.io/charts
helm install -n traefik traefik traefik/traefik --create-namespace
これで単一の traefik pod が起動します
$ k get -n traefik pod
NAME READY STATUS RESTARTS AGE
traefik-857b5bbcd6-lglrl 1/1 Running 0 75s
svc は LoadBalancer
として作成されます。
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik LoadBalancer 10.103.153.81 <pending> 80:31810/TCP,443:30906/TCP 27m
kubectl describe pod
で見て分かるとおりイメージには dockerhub 上の通常の traefik が使用されています。 kubernetes 用に特別にカスタマイズされているわけではなく、traefik の機能の一つである provider のうち kubernetes ingress provider と kubernetes CRD provider を利用して kubernetes 関連のリソースを処理するようになっています。helm でデプロイされる pod では引数の --providers.kubernetesingress
と --providers.kubernetescrd
でそれぞれ有効化しています。
Containers:
traefik:
Container ID: containerd://2a3f5e5af4659db69d3e8909c938c1e832fc552bf2d005673be2c2f85e859043
Image: docker.io/traefik:v3.0.4
Image ID: docker.io/library/traefik@sha256:a208c74fd80a566d4ea376053bff73d31616d7af3f1465a7747b8b89ee34d97e
Ports: 9100/TCP, 9000/TCP, 8000/TCP, 8443/TCP
Host Ports: 0/TCP, 0/TCP, 0/TCP, 0/TCP
Args:
--global.checknewversion
--global.sendanonymoususage
--entryPoints.metrics.address=:9100/tcp
--entryPoints.traefik.address=:9000/tcp
--entryPoints.web.address=:8000/tcp
--entryPoints.websecure.address=:8443/tcp
--api.dashboard=true
--ping=true
--metrics.prometheus=true
--metrics.prometheus.entrypoint=metrics
--providers.kubernetescrd
--providers.kubernetesingress
--entryPoints.websecure.http.tls=true
--log.level=INFO
現時点では traefik のバージョンとして v3 系が使用されます。メジャーバージョンが異なる場合は動作が変わったり対応していない機能があるので注意。
また、ログレベルを DEBUG
に設定すると pod ログでより詳細な情報が出力されるようになるため、traefik 側でどのような処理が行われているかを調べながら検証したい場合には DEBUG に設定するのがおすすめです。こちらは helm chart の values より変更できます。
Kubernetes 関連の機能を使ってみる
上記でインストールされた traefik では docker で使うときと同様に普通の proxy や LoadBalancer (LB) として使用できますが、ドキュメントでは kubernetes のリソースを管理する機能として以下の 3 つが記載されています。
- Kubernetes Ingress
- Kubernetes CRD
- Kubernetes Gateway API
これらの機能をそれぞれ試してみます。
Kubernetes Ingress
Kubernetes Ingress はいわゆる k8s の ingress controller の機能となっており、ingress リソースのプロパティを元に traefik の構成を動的に変更することで対象サービスへの proxy や LB を実現します。helm インストール時に traefik の ingressClass traefik
が自動で作成されるため、ingress リソースにこれを割り当てることで traefik により管理されます。
ingress の動作確認のため、nginx 用の pod と svc を作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
selector:
app: nginx
上記の svc にルーティングするための ingress を作成。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
spec:
rules:
- host: nginx.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
name: http
ingress を作成すると ingressClass に traefik が割り当てられます (インストール時にデフォルト class に設定されるため)。
$ k get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx traefik * 80 95s
traefik LB svc にアクセスすると、traefik を経由して nginx pod にルーティングされます。
$ curl -H "host: nginx.test" 10.109.84.42
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
svc type が LoadBalancer
なので、クラスタ外部からは traefik pod が稼働している node の IP address と nodePort でアクセスできます。
$ curl -H "host: nginx.test" http://192.168.3.124:31839
...
<title>Welcome to nginx!</title>
このあたりは nginx ingress controller などの他 ingress controller と比較して目新しい要素はなく普通に使用することができます。
TLS 通信
traefik では他の多くの ingress controller と同様に、ingress や svc に専用の annotations を追加することでいろいろな設定を行うことができます。例えばクライアントから traefik pod への通信を TLS にするには ingress の annotations に traefik.ingress.kubernetes.io/router.tls: "true"
を追加します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
rules:
- host: ingress.centre.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
name: http
この場合、サーバ証明書には Traefik 側で自動で生成される証明書が使用されます。curl 等で詳細を確認すると issuer: CN=TRAEFIK DEFAULT CERT
となっていることが確認できます。
$ curl https://ingress.centre.com -vk
* Trying 10.109.84.42:443...
* Connected to ingress.centre.com (10.109.84.42) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: CN=TRAEFIK DEFAULT CERT
* start date: Jul 10 14:35:23 2024 GMT
* expire date: Jul 10 14:35:23 2025 GMT
* issuer: CN=TRAEFIK DEFAULT CERT
* SSL certificate verify result: self-signed certificate (18), continuing anyway
...
独自の証明証を使用する場合は secret に格納した後、ingress の spec.tls.secretName
で指定します。この場合 annotations の指定は不要です。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
spec:
tls:
- secretName: ingress-secret
rules:
- host: ingress.centre.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
name: http
リクエストを実行すると issuer が変更され、自己署名証明書の issuer となっていることが確認できます。なお、リクエストと traefik 側のドメインが一致しない場合には fallback して traefik デフォルトの証明証が使用されるようになっています。
$ curl https://ingress.centre.com -vk
* Trying 10.109.84.42:443...
* Connected to ingress.centre.com (10.109.84.42) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: C=JP; ST=japan; L=tokyo; O=centre; OU=centre; CN=ingress.centre.com
* start date: Oct 9 11:02:43 2023 GMT
* expire date: Sep 15 11:02:43 2123 GMT
* issuer: C=JP; ST=japan; O=myroot; OU=myroot; CN=rootca.local
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
...
Traefik - pod 間を TLS にする
上記の方法では TLS は traefik で終端されるため、traefik とバックエンド (nginx) の通信は HTTP で行われます。traefik - バックエンド間の通信も TLS で行うには以下の 3 つの方法があります。
There are 3 ways to configure Traefik to use HTTPS to communicate with pods:
If the service port defined in the ingress spec is 443 (note that you can still use targetPort to use a different port on your pod).
If the service port defined in the ingress spec has a name that starts with https (such as https-api, https-web or just https).
If the service spec includes the annotation traefik.ingress.kubernetes.io/service.serversscheme: https.
こちらについても動作を見てみます。
まず nginx pod を HTTPS に対応するため、サーバ証明書の secret を作成。
kubectl create secret generic nginx-secret \
--from-file=tls.crt=nginx.centre.com.crt \
--from-file=tls.key=nginx.centre.com.key
Port 443 で HTTPS を待ち受けるように nginx の config ファイルを準備。
server {
listen 443 ssl;
server_name nginx.centre.com;
ssl_certificate /etc/nginx/ssl/tls.crt;
ssl_certificate_key /etc/nginx/ssl/tls.key;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
HTTP 通信は HTTPS にリダイレクトするように設定。
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
return 301 https://$host$request_uri;
}
ファイルを configmap として作成。
$ kubectl create configmap nginx-config --from-file=nginx.conf=./nginx.conf
$ kubectl create configmap default-config --from-file=default.conf=./default.conf
configmap と証明書の secret をコンテナにマウントするように pod を設定。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx-tls
template:
metadata:
labels:
app: nginx-tls
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/nginx-tls.conf
subPath: nginx.conf
- name: default-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
- name: tls-secret
mountPath: /etc/nginx/ssl
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: default-config
configMap:
name: default-config
- name: tls-secret
secret:
secretName: nginx-secret
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx-tls
ports:
- protocol: TCP
port: 443
targetPort: 443
これで HTTPS のみでリクエストを受け付ける nginx pod が起動します。pod の IP address に HTTPS アクセスすると接続が確認できます。
$ curl https://10.96.68.112 -k
...
<title>Welcome to nginx!</title>
次にクライアント → traefik → nginx の経路で接続しようとすると traefik → nginx は http 通信で行われるので The plain HTTP request was sent to HTTPS port
となります。
$ curl https://ingress.centre.com
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.27.0</center>
</body>
</html>
traefik を HTTPS に対応するために、 svc と ingress の target name をそれぞれ https
に合わせます。
---
apiVersion: v1
kind: Service
ports:
- name: https # Set to https
protocol: TCP
port: 4444
targetPort: 443
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-tls
annotations:
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
rules:
- host: ingress.centre.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
name: https # Set to https
この場合は port 名に https
が含まれるという以下の条件に一致するので、traefik が自動で検出して traefik → nginx を HTTPS で通信します。
If the service port defined in the ingress spec has a name that starts with https (such as https-api, https-web or just https).
ただ今回は nginx pod 側で自己証明書を使用しているため検証に失敗し Internal Server Error
となります。
$ curl https://ingress.centre.com
Internal Server Error%
traefik pod のログを見ると自己証明書が信頼できないため接続に失敗していることがわかります。
2024-07-11T10:06:08Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: 038e85d3daa88262
2024-07-11T10:06:08Z DBG github.com/traefik/traefik/v3/pkg/server/service/proxy.go:100 > 500 Internal Server Error error="tls: failed to verify certificate: x509: cannot validate certificate for 10.244.1.25 because it doesn't contain any IP SANs"
この場合は traefik pod に対象の rootCA 証明書を配置するか、検証をスキップするよう insecureSkipVerify: true に設定する必要があります。
ここでは簡単のため insecureSkipVerify を追加する方法で対応します。k edit deployments.apps traefik
で deployment を編集し、traefik コンテナの引数に serversTransport.insecureSkipVerify=true
を追加します。
spec:
automountServiceAccountToken: true
containers:
- args:
- --global.checknewversion
- --global.sendanonymoususage
- --entryPoints.metrics.address=:9100/tcp
- --entryPoints.traefik.address=:9000/tcp
- --entryPoints.web.address=:8000/tcp
- --entryPoints.websecure.address=:8443/tcp
- --api.dashboard=true
- --ping=true
- --metrics.prometheus=true
- --metrics.prometheus.entrypoint=metrics
- --providers.kubernetescrd
- --providers.kubernetesingress
- --entryPoints.websecure.http.tls=true
- --log.level=DEBUG
+ - --serversTransport.insecureSkipVerify=true
kubectl rollout restart deployment traefik
で pod を更新すると通信が成功するようになります。
$ curl https://ingress.centre.com -I
HTTP/2 200
accept-ranges: bytes
content-type: text/html
date: Thu, 11 Jul 2024 10:07:43 GMT
etag: "6655da96-267"
last-modified: Tue, 28 May 2024 13:22:30 GMT
server: nginx/1.27.0
content-length: 615
nginx pod のアクセスログを見ると traefik pod の IP アドレス 10.244.1.29
からの通信が成功していることが確認できます。
$ k get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-58dc469c55-t4lz9 1/1 Running 0 13m 10.244.1.25 k8s-w1 <none> <none>
traefik-b47cfbc96-9l9c5 1/1 Running 0 31s 10.244.1.29 k8s-w1 <none> <none>
# nginx のログ
10.244.1.29 - - [11/Jul/2024:10:07:43 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1" "10.244.0.0"
よって traefik → バックエンド 間も HTTPS 通信できることが確認できました。
ingress provider の機能は ingress の annotations に基づいて自動的に traefik の設定を更新するため、他の ingress controller を既に使い慣れている場合は特に問題なく使えます。設定可能な annotations などはドキュメントの以下を参照。
kubernetes CRD
kubernetes CRD は CRD を用いて traefik における routers や middleware, 各種 options などに相当するリソースを作成する機能となっています。ingress controller では ingress や svc の annotation などに基づいて自動で設定する機能となっていましたが、kubernetes CRD では CRD を使って直接 traefik 側のリソースを作成することができます。
ドキュメントによると、ingress 側で多数の annotations をつけてリソースを管理することなく traefik の機能を活用したいという需要から、CRD を使って直接 traefik のオブジェクトを作成できるようにしたという背景があったとのこと。
However, as the community expressed the need to benefit from Traefik features without resorting to (lots of) annotations, the Traefik engineering team developed a Custom Resource Definition (CRD) for an IngressRoute type, defined below, in order to provide a better way to configure access to a Kubernetes cluster.
例えば、ingress controller 検証時のように nginx pod に HTTPS で通信するように設定するには以下の IngressRoute
カスタムリソースを作成します。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: nginx
spec:
entryPoints:
- websecure
routes:
- match: Host(`ingress.centre.com`) && PathPrefix(`/`)
kind: Rule
services:
- name: nginx-service
port: 4444
この IngressRoute
を作成してリクエストを送信すると、先程と同様に nginx pod に HTTPS で接続できます。
$ curl https://ingress.centre.com -k
...
<title>Welcome to nginx!</title>
このときも先程と同様に クライアント → traefik pod → nginx pod という通信経路となっており、各 pod 間の通信は HTTPS で行われます。
kubernetes CRD では CRD を使うことで traefik の各種リソースを作成できるため、用途に応じて ingress controller よりも詳細なルーティング設定や traefik 独自の機能を利用することができます。traefik の機能はドキュメントに豊富に記載されていますが、以下ではよく使いそうな機能や面白そうなものをいくつか選んで検証してみます。
負荷分散
IngressRoute の svc で複数のサービスを指定するとラウンドロビンでリクエストが負荷分散されるようになります。試しに 2 つの flask svc に負荷分散するように services を指定します。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flask-lb
spec:
entryPoints:
- web
routes:
- match: Host(`flask.test`) && PathPrefix(`/`)
kind: Rule
services:
- name: flask1
port: 5000
- name: flask2
port: 5000
flask1, flask2 svc はさらにそれぞれ 3 つの pod にルーティングされるように設定します。
$ k get pod
NAME READY STATUS RESTARTS AGE
flask1-5d778f5dd5-47xg2 1/1 Running 0 3m47s
flask1-5d778f5dd5-4nsxc 1/1 Running 0 3m47s
flask1-5d778f5dd5-784ll 1/1 Running 0 3m47s
flask2-7d79bb7c54-788jc 1/1 Running 0 3m47s
flask2-7d79bb7c54-9x6lb 1/1 Running 0 3m47s
flask2-7d79bb7c54-lhhp8 1/1 Running 0 3m47s
Flask pod ではレスポンスで POD_ID
を返すように設定し、リクエスト時にどの pod にルーティングされたか分かるようにします。
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask1
spec:
selector:
matchLabels:
app: flask
task: task1
replicas: 3
template:
metadata:
labels:
app: flask
task: task1
spec:
containers:
- name: flask
image: registry.centre.com:5000/flask-k8s-test
ports:
- containerPort: 5000
env:
- name: POD_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
通信経路のイメージとしては以下のようになります。
通信経路のイメージ図
この構成でクライアントからリクエスト 10000 回送信した結果、flask1, flask2 に対してちょうど 5000 リクエストずつ分散されていることが確認できました。また、各 pod に対するリクエスト数を集計すると以下のようになっていました。
- flask1-5d778f5dd5-47xg2 : 1666
- flask1-5d778f5dd5-4nsxc : 1667
- flask1-5d778f5dd5-784ll : 1667
- flask2-7d79bb7c54-788jc : 1666
- flask2-7d79bb7c54-9x6lb : 1668
- flask2-7d79bb7c54-lhhp8 : 1667
これにより負荷分散の機能が正常に機能し、リクエストが各 pod に均等に分散されていることがわかります。この検証では flask1, 2 はいずれも同一サービスなので負荷分散するメリットはあまりないですが、特に複雑な設定を行わず、単に services に分散させたい svc を複数指定することで負荷分散できることがわかります。
加重ラウンドロビン
負荷分散の際に加重ラウンドロビン (Weighted Round Robin: WRR) により重み付けした上でラウンドロビンを実行することも可能です。
traefik の Services オブジェクトに対応する TraefikService
リソースを作成し、宛先の svc と weight をそれぞれ指定します。
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: wrr1
spec:
weighted:
services:
- name: flask1
port: 5000
weight: 3
- name: flask2
port: 5000
weight: 2
IngressRoute では services に上記の TraefikService を指定します。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flask-lb
spec:
entryPoints:
- web
routes:
- match: Host(`flask.test`) && PathPrefix(`/`)
kind: Rule
services:
- name: wrr1
kind: TraefikService
ラウンドロビンの検証と同様にリクエストを 10000 回送ってみると、ちょうど flask1: 6000, flask2: 4000 となっていました。確かに TraefikService で指定した通り 3:2 の割合でリクエストが分散されています。
TCP ルーティング
IngressRouteTCP リソースを作成すると tcp のルーティング設定を行うことができます。
ここでは検証として 2 つの postgres svc を用意し、クライアントからの接続が IngressRouteTCP によって分散されることを見てみます。
IngressRouteTCP リソースを作成。weight の設定は WRR と同様に 3:2 に設定します。
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: postgres
spec:
entryPoints:
- web
routes:
- match: "HostSNI(`psql.test`)"
priority: 1
services:
- name: postgres-1
port: 5432
weight: 3
- name: postgres-2
port: 5432
weight: 2
tls: {}
pod と svc
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-1
spec:
replicas: 3
selector:
matchLabels:
app: postgres1
template:
metadata:
labels:
app: postgres1
spec:
containers:
- name: postgres
image: postgres
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: postgres
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
---
apiVersion: v1
kind: Service
metadata:
name: postgres-1
spec:
ports:
- port: 5432
targetPort: 5432
selector:
app: postgres1
type: ClusterIP
pod は postgres1, 2 で各 3 つずつ起動。
postgres-1-6787577bd7-drh9f 1/1 Running 0 9m40s 10.244.1.50 k8s-w1 <none> <none>
postgres-1-6787577bd7-kqt4j 1/1 Running 0 9m38s 10.244.1.51 k8s-w1 <none> <none>
postgres-1-6787577bd7-m2w6k 1/1 Running 0 9m43s 10.244.1.49 k8s-w1 <none> <none>
postgres-2-65c7c6788f-6plvf 1/1 Running 0 15m 10.244.1.43 k8s-w1 <none> <none>
postgres-2-65c7c6788f-9cn9x 1/1 Running 0 15m 10.244.1.44 k8s-w1 <none> <none>
postgres-2-65c7c6788f-zsxxs 1/1 Running 0 15m 10.244.1.45 k8s-w1 <none> <none>
postgres への接続には以下の python スクリプトを使用します。postgres に接続後、inet_server_addr()
で pod の IP アドレスが取得できるのでどの pod にルーティングされたか判別できます。
import psycopg2
import csv
def connect(count, writer):
conn = psycopg2.connect(
dbname="postgres",
user="postgres",
password="postgres",
host="psql.test",
port=80
)
cur = conn.cursor()
cur.execute("SELECT inet_server_addr();")
server_ip = cur.fetchone()[0]
data = [count, server_ip]
print(data)
writer.writerow(data)
cur.close()
conn.close()
with open("out.csv", "w") as f:
writer = csv.writer(f)
for i in range(10000):
connect(i, writer)
上記を実行して結果を集計したところ、weight で割り当てたとおり postgres-1 svc に 6000 回、postgres-2 svc に 4000 回の通信が分散されていることが確認できました。よって TCP 通信も適切に分散されていることがわかります。
GRPC ルーティング
traefik では GRPC 通信のルーティングも設定できます。ドキュメントでは grpc-go helloworld を使った例が記載されているので、こちらを参考に動作検証してみます。
ローカルでの動作検証
まず quickstart の手順に沿ってローカルで grcp サーバーを起動して正常に動作することを確認します。
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
$ git clone -b v1.65.0 --depth 1 https://github.com/grpc/grpc-go
$ cd grpc-go/examples/helloworld
後のエラー回避のため server 側の go コードに以下を追加。
import (
+ "google.golang.org/grpc/reflection"
}
s := grpc.NewServer()
+ reflection.Register(s)
pb.RegisterGreeterServer(s, &server{})
サーバーを起動
$ go run greeter_server/main.go
2024/07/13 18:06:52 server listening at [::]:50051
client 用の go ファイルも用意されていますが、ここでは grpcurl を使って確認します。
$ grpcurl -plaintext 0.0.0.0:50051 list
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
$ grpcurl -plaintext -d '{"name": "test"}' 0.0.0.0:50051 helloworld.Greeter/SayHello
{
"message": "Hello test"
}
正常に返信が来ることが確認できました。次に k8s 内で pod で実行するため、grpc server をコンテナイメージ化します。
FROM golang:1.23rc1-bookworm
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 && \
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
RUN git clone -b v1.65.0 --depth 1 https://github.com/grpc/grpc-go
WORKDIR grpc-go/examples/helloworld
COPY greeter_server/main.go greeter_server/main.go
RUN go build -o /server greeter_server/main.go
ENTRYPOINT [ "/server" ]
k8s での実行
まず上記で作成したイメージを使用する pod, svc を作成。
apiVersion: apps/v1
kind: Deployment
metadata:
name: grpc-server
spec:
replicas: 1
selector:
matchLabels:
app: grpc
template:
metadata:
labels:
app: grpc
spec:
containers:
- name: grpc
image: registry.centre.com:5000/grpc-test-server
ports:
- containerPort: 50051
---
apiVersion: v1
kind: Service
metadata:
name: grpc
spec:
ports:
- name: grpc
port: 50051
targetPort: 50051
selector:
app: grpc
type: ClusterIP
ドキュメント では http の routes と server で grpc server への宛先を指定しているので、これに対応する IngressRoute で routes[].services
に上記の grpc svc を指定すればルーティングされるようになります。ただ実際に試すとそのままではルーティングされず、scheme: h2c
を設定する必要がありました(参考)。ドキュメントの以下の記述に対応しているのかもしれません。
We don't need specific configuration to use gRPC in Traefik, we just need to use h2c protocol, or use HTTPS communications to have HTTP2 with the backend.
最終的な IngressRoute
の中身は以下。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: grpc
spec:
entryPoints:
- websecure
routes:
- match: Host(`grpc.test`) && PathPrefix(`/`)
kind: Rule
services:
- name: grpc
port: 50051
scheme: h2c
これで k8s クラスタ外部からの通信も対象の grpc server pod にルーティングされるようになります。traefik の LoadBalancer svc に grpcurl でリクエストを送ると実際に通信できていることが確認できます。
$ cat /etc/hosts
192.168.3.124 grpc.test
$ grpcurl -insecure grpc.test:31830 list
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
$ grpcurl -insecure -d '{"name": "test"}' grpc.test:31830 helloworld.Greeter/SayHello
{
"message": "Hello test"
}
k8s クラスタ外へのルーティング
上記の例ではルーティング先はいずれも k8s 内のサービスとなっていますが、k8s の externalName を利用することで k8s 外にあるアプリケーションなどにルーティングすることも出来ます。
ただ実際試してみると externalName services not allowed
のエラーが出力されました。こちら を参考に traefik の args に以下の 4 つを追加することで機能しました。
Containers:
traefik:
Args:
...
--providers.kubernetesingress.allowemptyservices=true
--providers.kubernetesingress.allowexternalnameservices=true
--providers.kubernetescrd.allowemptyservices=true
--providers.kubernetescrd.allowexternalnameservices=true
後でわかりましたが helm chart で allowExternalNameServices
などを true に設定することで上記が引数に追加されるようです。
こちらの動作も実際に試してみます。今回は k8s クラスタ外部の IP アドレス 192.168.3.181
のホスト上で起動する nginx にドメイン名 external.test
で通信できるように設定します。
通信経路
IngressRoute と service を以下のように作成。IngressRoute の spec は今までと同じですが、service を spec.type: ExternalName
として作成します。
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: external
spec:
entryPoints:
- web
routes:
- match: Host(`external.test`)
kind: Rule
services:
- name: external-svc
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: external-svc
spec:
externalName: external.test
type: ExternalName
ports:
- port: 80
externalName
で指定したドメイン名 external.test
を名前解決できるように coredns を編集したり外部 DNS を参照するように設定する必要があります。ここでは簡単のため coredns configmap に直接ドメインと ip address を記載します。
$ k -n kube-system edit configmaps coredns
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
+ hosts {
+ 192.168.3.181 external.test
+ fallthrough
+ }
...
これで traefik を経由して対象の nginx に通信できるようになったので、クラスタ内から external.test
に向けてリクエストを送信するとレスポンスが返ってきます。クラスタ外部からは今までと同様に traefik LB svc にアクセスすれば通信できます。
$ curl -H "Host: external.test" http://10.109.84.42:80 -I
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 615
Content-Type: text/html
Date: Thu, 11 Jul 2024 17:22:17 GMT
Etag: "6655da96-267"
Last-Modified: Tue, 28 May 2024 13:22:30 GMT
Server: nginx/1.27.0
traefik pod のログから、external svc への route が serverName: f33c40c9f1fdd360
として作成されているのがわかり、上記の curl リクエストでこの server が選択されていることが確認できます。
2024-07-11T17:19:33Z DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:301 > Creating server entryPointName=web routerName=traefik-external-4004d7032e8ea90ff1d0@kubernetescrd serverName=f33c40c9f1fdd360 serviceName=traefik-external-4004d7032e8ea90ff1d0@kubernetescrd target=http://external.test:80
2024-07-11T17:22:40Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: f33c40c9f1fdd360
nginx 側のアクセスログでは、traefik pod が稼働する node の IP アドレス 192.168.3.124
からリクエストが送信されていることが確認できます。
192.168.3.124 - - [11/Jul/2024:17:22:40 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1" "10.244.0.0"
k8s 側での externalName svc の作成やドメイン名の名前解決の対応は必要ですが、この機能を利用すると traefik を docker で使うときのように通常の proxy server として利用できます。traefik バックエンドのサービスにアクセスするクライアントは traefik が k8s 上で動いていることを意識せず通常の proxy や LoadBalancer のように使用でき、traefik 管理側の観点では k8s の可用性などのメリットを享受できるためいろいろな使い方ができそうです。
OIDC 認証
traefik proxy の有償版 traefik enterprise では OIDC 認証 の機能があるのでデフォルトで外部の OIDC provider と連携できますが、OSS 版の traefik proxy ではこの機能がありません。しかし、Middleware の ForwardAuth を使うと外部の認証サービス側で認証を行い、その結果に基づいてルーティング先へのアクセスを許可・拒否できます。認証自体はは外部認証サービス側で実行するため、Basic 認証や OIDC 認証などが使用できます。
例えば以前に記事にした 認証・認可サーバの OSS Authelia の ドキュメント ではまさに traefik を使って OIDC 認証を行う例が記載されています。もちろん他の oidc provider でも同様の構成は実現できますが、ここでは一例として authelia を使った OIDC 認証が実行できることを試してみます。
ここではバックエンドの nginx にルーティングする前に authelia での OIDC 認証 (シングルサインオン) を実行し、ユーザー認証に成功したら実際に nginx pod にルーティングする構成にします。
通信経路
まず authelia への転送は traefik の middleware で行うので、 Middleware
カスタムリソースのマニフェストを作成します。
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: authelia
spec:
forwardAuth:
address: "http://auth.oidc.test:9091/api/authz/forward-auth"
trustForwardHeader: true
authResponseHeaders:
- 'Remote-User'
- 'Remote-Groups'
- 'Remote-Email'
- 'Remote-Name'
tls:
insecureSkipVerify: true
middleware を適用するには IngressRoute
で routes[].middleware
に上記の metadata.name を指定します。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: nginx-oidc
spec:
entryPoints:
- websecure
routes:
- match: Host(`nginx.oidc.test`) && PathPrefix(`/`)
kind: Rule
middlewares:
- name: authelia
services:
- name: nginx
port: http
authelia は k8s クラスタ外の別ホスト上で docker で作成します(k8s 内で立てても ok )。Authelia の設定ファイル configuration.yml
で必要な設定は以下。
- server.tls
- authelia を HTTPS に対応させるための証明書のパス。
- endpoints.authz.forward-auth
- implementation:
ForwardAuth
を指定。
- implementation:
- session.cookies
- authelia_url: authelia の認証を行うエンドポイントとなるドメイン名。好きな値で良いが、ここでは
auth.oidc.test
とする。 - domain: authelia_url で指定したドメイン名のサブドメイン名に設定する。
- authelia_url: authelia の認証を行うエンドポイントとなるドメイン名。好きな値で良いが、ここでは
- access_control.rules
- domain: OIDC 認証の対象とするドメイン名。
- policy:
one_factor
に指定。
theme: "dark"
server:
address: 'tcp://:9091'
tls:
key: /certs/authelia.centre.com.key
certificate: /certs/authelia.centre.com.crt
endpoints:
authz:
forward-auth:
implementation: 'ForwardAuth'
log:
level: 'debug'
identity_validation:
reset_password:
jwt_secret: 'a_very_important_secret'
authentication_backend:
file:
path: '/config/users_database.yml'
refresh_interval: 1m
access_control:
default_policy: 'deny'
rules:
- domain: 'nginx.oidc.test'
policy: 'one_factor'
- domain: 'auth.oidc.test'
policy: 'one_factor'
session:
secret: 'insecure_session_secret'
cookies:
- name: 'authelia_session'
domain: 'oidc.test'
authelia_url: 'https://auth.oidc.test:9091'
expiration: '1 hour' # 1 hour
inactivity: '5 minutes' # 5 minutes
regulation:
max_retries: 3
find_time: '2 minutes'
ban_time: '5 minutes'
storage:
encryption_key: 'you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this'
local:
path: '/config/db.sqlite3'
notifier:
filesystem:
filename: '/config/notification.txt'
authelia を起動するための docker-compose.yml
---
services:
authelia:
image: authelia/authelia
container_name: authelia
volumes:
- ./authelia:/config
- ./certs:/certs
ports:
- 9091:9091
environment:
- TZ=Asia/Tokyo
ディレクトリ構成
.
├── authelia
│ ├── configuration.yml
│ ├── notification.txt
│ └── users_database.yml
├── certs
│ ├── authelia.centre.com.crt
│ └── authelia.centre.com.key
└── docker-compose.yml
上記を実行して authelia の docker コンテナを起動。
k8s クラスタから外部の authelia に名前解決できるよう coredns の configmap に追加。
apiVersion: v1
data:
Corefile: |
.:53 {
....
hosts {
192.168.3.181 external.test
+ 192.168.3.18 auth.oidc.test
fallthrough
}
最後に k8s クラスタ外部のクライアントから各ドメイン名が解決できるよう /etc/hosts などに追加。
192.168.3.124 nginx.oidc.test # traefik pod の node の ip address
192.168.3.18 auth.oidc.test # authelia コンテナのホストの ip address
これで準備が完了しました。動作確認はブラウザを使ったほうが分かりやすいので chrome で確認します。
まず k8s 外部クライアントから k8s クラスタ上の traefik LoadBalancer svc にドメイン名 nginx.oidc.test
でアクセスすると証明書の警告が表示されます(traefik の証明書が信頼されていないため)。そのまま進むと authelia のドメイン auth.oidc.test
にリダイレクトされ、認証画面が表示されます。
authelia 側で設定した username/password を入力すると認証に成功し、nginx pod にルーティングされます。
2 回目以降は SSO が効くので authelia 側のセッションが切れるまではユーザー認証無しで nginx にアクセスできます。準備はやや大変ですが使う分には簡単に OIDC 認証できました。
このように ForwardAuth middleware を使用することで外部の OIDC プロバイダーを使用した OIDC 認証が実現できます。バックエンドのアプリケーションやサービス自体が OIDC 認証に対応していればそちらを使ってもいいですが、対応していないサービスを使う場合や自作アプリケーションで OIDC 処理の実装や管理が面倒な場合、この機能を使うことで処理を外部の OIDC プロバイダーに任せることができます。
Kubernetes Gateway API
概要
Gateway API は k8s の ingress や LoadBalancer の機能を元にロール志向や拡張性などを強化した次世代の機能です。詳細は Gateway API を参照。
Gateway API はそれ単体でリソースを管理するわけではなく、ingress リソースと ingress controller の関係のように Gateway Controller と呼ばれるコンポーネントを用いることで動作します。対応している Gateway Controller の一覧は Implementations にあり、Traefik Proxy も GA となっています。一方で traefik proxy のドキュメントでは実験的となっており、まだ全ての機能が実装されているわけでないようです。
This provider is proposed as an experimental feature and partially supports Gateway API v1.0.0 specification.
traefik における Kubernetes Gateway API provider では kubernetes CRD provider と同様に Gateway API のリソースに基づいて traefik の構成を更新するようになっています。現時点では以下のリソースが実装されている状況になっています。
- GatewayClass: defines a set of Gateways that share a common configuration and behaviour.
- Gateway: describes how traffic can be translated to Services within the cluster.
- HTTPRoute: defines HTTP rules for mapping requests from a Gateway to Kubernetes Services.
- TCPRoute: defines TCP rules for mapping requests from a Gateway to Kubernetes Services.
- TLSRoute: defines TLS rules for mapping requests from a Gateway to Kubernetes Services.
一方で GRPCRoute などはまだ未実装のようです。
kubernetes CRD で検証したように HTTP や TCP のルーティング設定、TLS の設定などは Traefik proxy 自体の機能で管理できるため、traefik をメインに使う場合はわざわざ Gateway API を使ってリソースを管理するメリットはあまりなさそうです。一方で ingressClass のように gateway を扱う gateway controller がクラスタ内に複数存在するような場合や、 gateway を分割して用途に合わせて運用していくようなケースでは Gateway API provider を使っていくメリットがあるとも言えます。
使ってみる
Gateway API の実装は各 gateway controller に依存することから、内容を見ただけではどのように動作するかわかりづらい部分も結構あります。なので実際に使って動作を見てみます。
Gateway API 機能を有効化するためには traefik の実行時引数に --experimental.kubernetesgateway
と --providers.kubernetesgateway=true
を指定する必要があります。helm values ではそれぞれ以下に対応しています。
experimental.kubernetesgateway は experimental.kubernetesGateway.enabled: true
に対応。
providers.kubernetesgateway は providers.kubernetesGateway.enabled: true
に対応。
helm install して構築すると、traefik.io/gateway-controller
という名前の gateway Controller によって管理される gatewayClass: traefik
が作成されます。
$ k describe gatewayclasses.gateway.networking.k8s.io traefik
Spec:
Controller Name: traefik.io/gateway-controller
Status:
Conditions:
Last Transition Time: 2024-07-14T14:38:34Z
Message: Handled by Traefik controller
Observed Generation: 1
Reason: Handled
Status: True
Type: Accepted
Events: <none>
これは ingress で言うところの ingressClass と ingress controller を用意した段階に対応するので、これから実際の gateway API リソースを作っていきます。リソースの設定例が Example に記載されているので、これを参考に Gateway
, HTTPRoute
を作成します。
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
gatewayClassName: traefik
listeners:
- name: websecure
protocol: HTTPS
port: 8443
tls:
certificateRefs:
- kind: Secret
name: nginx-secret
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-app
spec:
parentRefs:
- name: my-gateway
hostnames:
- nginx.gateway.centre.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: nginx
port: 80
weight: 1
これらのリソースを作成すると traefik の gateway controller が検知して traefik の構成をセットアップしてくれます。traefik pod の debug ログを見ると以下のようなルーティング設定が追加されています。
2024-07-13T15:09:02Z DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{"routers":{"traefik-http-app-my-gateway-websecure-82dd8725fafb6ba9097d":{"entryPoints":["websecure"],"rule":"Host(
nginx.gateway.centre.com) \u0026\u0026 PathPrefix(
/)","ruleSyntax":"v3","service":"traefik-http-app-my-gateway-websecure-82dd8725fafb6ba9097d-wrr","tls":{}}},"services":{"traefik-http-app-my-gateway-websecure-82dd8725fafb6ba9097d-wrr":{"weighted":{"services":[{"name":"traefik-nginx-80","weight":1}]}},"traefik-nginx-80":{"loadBalancer":{"passHostHeader":true,"responseForwarding":{"flushInterval":"100ms"},"servers":[{"url":"http://10.244.1.102:80"},{"url":"http://10.244.1.103:80"}]}}}},"tcp":{},"tls":{},"udp":{}} providerName=kubernetesgatewa
整形すると以下のような traefik リソースが追加されていることがわかります。
http:
routers:
traefik-http-app-my-gateway-websecure-82dd8725fafb6ba9097d:
entryPoints:
- websecure
rule: Host(`nginx.gateway.centre.com`) && PathPrefix(`/`)
ruleSyntax: v3
service: traefik-http-app-my-gateway-websecure-82dd8725fafb6ba9097d-wrr
tls: {}
services:
traefik-http-app-my-gateway-websecure-82dd8725fafb6ba9097d-wrr:
weighted:
services:
- name: traefik-nginx-80
weight: 1
traefik-nginx-80:
loadBalancer:
passHostHeader: true
responseForwarding:
flushInterval: 100ms
servers:
- url: http://10.244.1.102:80
- url: http://10.244.1.103:80
tcp: {}
tls: {}
udp: {}
HTTPRoute で定義した hostname: nginx.gateway.centre.com
で traefik LB svc に curl すると nginx pod に到達します。
$ curl -k -H "Host: nginx.gateway.centre.com" https://10.98.162.149:443
...
<title>Welcome to nginx!</title>
このように Gateway API CRD でも kubernetes CRD を使ったときと同様に対象の svc にルーティングするように設定できます。
作成した Gateway API の CRD と traefik のリソースを対応付けると以下のようになります。上記の例では HTTPRoute にバックエンドの nginx svc のみ指定していますが、用途によって middleware や serviceTransport (TraefikService) を関連付けることもできます。詳細は HTTPROute を参照。
上記の例では gateway リソースで gatewayClassName: traefik
を指定しているので CRD を元に traefik のリソースが自動で構成されましたが、他の gatewayClass を指定すればそちらの gateway controller を元にリソースが管理されます。ingressClass や storageClass のように複数の gatewayClass が存在する際に指定した class に基づいて実装や管理を分けられる点が Gateway API を使うメリットの一つとなっています。
その他
その他細かいもののメモ
Observability
Log
ログは通常の traefik のログの他に アクセスログ も有効化できます。デフォルトでは無効化されているので helm values の logs.access.enabled: true
で有効化します。その他 formta や記録するログのフィルタリング、buffesize なども設定できます。
Metrics
metrics は traefik pod の port 9100 でデフォルトで公開されており、traefik の entrypoint として metrics
という名前が設定されています。
Containers:
traefik:
Ports: 9100/TCP, 9000/TCP, 8000/TCP, 8443/TCP
Args:
--entryPoints.metrics.address=:9100/tcp
--metrics.prometheus=true
--metrics.prometheus.entrypoint=metrics
上記の port の /metrics
にアクセスすると prometheus 形式のメトリクスが取得できます。
$ curl http://10.244.1.55:9100/metrics
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.5735e-05
go_gc_duration_seconds{quantile="0.25"} 0.000143858
svc はデフォルトで作成されていないので、svc 経由で取得する場合は例えば以下のようなサービスを作成する必要があります。
apiVersion: v1
kind: Service
metadata:
name: traefik-metrics
spec:
selector:
app.kubernetes.io/instance: traefik-traefik
app.kubernetes.io/name: traefik
ports:
- name: metrics
protocol: TCP
port: 9100
targetPort: 9100
type: ClusterIP
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik-metrics ClusterIP 10.109.51.238 <none> 9100/TCP 12s
$ curl 10.109.51.238:9100/metrics
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.5735e-05
デフォルトは prometheus 形式ですが引数の値を変更することで以下の形式にも変更可能。
- Datadog
- InfluxDB2
- Prometheus
- StatsD
あとは Opentelemetry 形式にもできるようです。詳細は以下を参照。
Trace
opentelemetry 形式の trace に対応しています。
- https://doc.traefik.io/traefik/observability/tracing/overview/
- https://doc.traefik.io/traefik/observability/tracing/opentelemetry/
helm でインストールしたものではデフォルトで有効になっていないので、helm values のこのあたりを設定して有効化できます。
ちなみに github 上では http エンドポイントが http://localhost:4318/v1/metrics
と書かれていますが、ドキュメントでは http://localhost:4318/v1/traces
になっています。traces のエンドポイントはだいたい traces なのでおそらくドキュメントのほうが正しいです。
Dashboard
helm だとダッシュボードは有効化されていますが entrypoint が設定されていないので、helm values の
ingressRoute.dashboard.enabled
を true に設定します。
設定後に再デプロイすると http://[pod_ip]:9000/dashboard/
でダッシュボードにアクセスできます。ダッシュボードでは router や service の状態, 有効化されている provider の情報などの構成情報を視覚的に確認できます。
おわりに
Kubernetes で traefik を使う際は kubernetes CRD で traefik リソースを管理できるので、docker で設定ファイルに proxy 設定を記載するのとほぼ変わらない使用感で運用することができます。また、 k8s の ingress リソースを管理する ingress controller や Gateway API を管理する Gateway controller としても使用できます。traefik 自体の豊富な機能もそのまま使えるので、OSS の proxy サーバーでどれを使うか迷っている際は有力な候補となりそうです。
Discussion