Traefik から Envoy Gateway に乗り換えた
以前書いたように、おうち k8s クラスタではクラスタ外からのアクセスのために Traefik を導入していた。
ところが、 (最初はうまくいっていたものの) あるときから Helm でインストールした MySQL が IngressRouteTCP で外部からアクセスできなくなってしまった。
(telnet で接続してみたところ通常 server から接続直後に応答があるべきところ client からなんらかのパケットを送らないとセッションが開始しない)
いろいろ調べてみたがどうにもうまくいかず、せっかくなので別のソフトウェアに乗り換えることにした。
Istio にしようかとも思ったが、サービスメッシュのような複雑なことはするつもりはなくコンパクトなものがいいなと思い、 同じ Envoy ベースの Envoy Gateway にすることにした。
Traefik Proxy | Envoy Gateway | |
---|---|---|
ベース | Traefik Proxy | Envoy |
Ingress Controller | ○ | × |
Gateway API | ○ | ○ |
独自 CRD | ○ | ○ |
TLS 証明書管理 | ○ | × |
星取表でみるかぎり完全に Traefik に負けてしまうのだが、裏を返すと限定した機能にフォーカスしているといえる。
Ingress に対応していないのがちょっとつらいところだが、 Gateway API のほうが実現できる機能も多いため、 Envoy Gateway でいくことにする。 Envoy に興味もあったし。
インストール
Helm でインストールした。
Traefik とちがい、 values はデフォルトからいじっていない。
これは、 Traefik にくらべて TLS 証明書管理機能がないこと、また、対象とするポート等の設定は Resource として定義することから、本体自体のカスタマイズは特に必要なかったことなどが理由と思う。
Argo CD での Helm インストール
CRD が大きすぎて、デフォルトだと Argo CD で管理できない (Too long: must have at most 262144 bytes のように怒られる)。
適当に調べて Replace mode で配布するようにしてみたが、それでもうまくいかなかったので、結局手で helm install することにした。
いま改めて調べると Replace ではなく Server Side Apply を利用するほうがよいらしい (ref. Fixing Argo CD "Too long must have at most 262144 bytes" error)。
時間ができたらふたたび Argo CD による配布に挑戦してみようと思う。
(2025-03-14 追記: spec.syncPolicy.syncOptions
に ServerSideApply=true
を付与することで、無事 Argo CD で管理できるようになった)
セットアップ
cert-manager による TLS 証明書管理
Traefik はいい感じにTLS の証明書を管理してくれる (ACME 対応の認証局であれば自動発行も可能) が、 Envoy Gateway にはそのような機能はない。
なので、定番である cert-manager を利用することにする。
自分は k8s 基盤として MicroK8s を利用しているので cert-manager addon を enable してインストールした。
(MicroK8s での説明 だと、あたかも Ingress 環境があることが必須のように読めてしまうが、 ACME DNS01 Challenge をするぶんには Ingress は必要ではない)
Let's Encrypt で *.wildcard.example.com
の証明書を発行する resource は以下のようになる。
--- apiVersion: cert-manager.io/v1 kind: Certificate metadata: namespace: default name: wildcard.example.com spec: issuerRef: kind: ClusterIssuer name: luadns-issuer secretName: wildcard.example.com-tls commonName: wildcard.example.com dnsNames: - wildcard.example.com - "*.wildcard.example.com"
metadata.name
や spec.secretName
はなんでもかまわない。
spec.issueRef
はこのあいだの記事で定義した ClusterIssuer を参照している。
spec.secretName
で指定した Secret に証明書が格納される。
これを後述する Gateway から参照することになる。
GatewayClass の定義
Gateway をハンドリングする controller を指定する GatewayClass を定義する。
Ingress における IngressClass と同じようなものと思えば問題ない。
--- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: envoy-gateway spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller
Envoy Gateway (Controller) の設定をカスタマイズするためには、以下のような記述になる。 (オフィシャルの example が参考になる)
--- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: envoy-gateway spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller parametersRef: group: gateway.envoyproxy.io kind: EnvoyProxy namespace: envoy-gateway-config name: config --- apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: namespace: envoy-gateway-config name: config spec: ...
spec に設定可能なパラメータは オフィシャルドキュメントの EnvoyProxySpec を参照のこと。
とはいえ、通常の場合わざわざカスタマイズする必要もなく、 GatewayClass だけ定義すればよいと思う。
Gateway の定義
Traefik の場合、どの port を開けるかといった設定はすべて起動時オプションで指定する必要があった (Helm でインストールする場合は values.yaml で指定)。
Gateway API ではそういった port 設定もすべて CRD になっている。 具体的には Gateway resource を利用する。
--- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: namespace: default name: envoy-gateway spec: gatewayClassName: envoy-gateway addresses: - type: IPAddress value: 192.168.0.100 listeners: - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - kind: Secret namespace: default name: wildcard.example.com-tls allowedRoutes: kinds: - kind: HTTPRoute namespaces: from: All
spec.gatewayClassName
に上記で設定した GatewayClass の name を指定する- Ingress や PersistentVolume に似ている
spec.listeners[].tls.certificateRefs
に上記で設定した cert-manager による Certificate resource により生成される Secret resource を指定しているspec.listeners[].allowedRoutes.kinds
に、当該 Gateway に利用可能な route resource を指定している (後述)spec.addresses
は、External LoadBalancer (metallb 等) が適切にセットアップされていれば、とくに設定する必要はない- 筆者の環境では (MicroK8s に) metallb は導入しておらず、 single node の物理 I/F にアサインされたアドレスをそのまま利用しているので、 IP アドレスを明示的に指定している
ルーティングの設定
詳細な説明は不要かと思うが、実際の Service に対応する HTTPRoute resource の定義はたとえば以下のようになる。 (ちなみに HTTPS であっても HTTPRoute を利用する。HTTPSRoute はない)
--- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httproute spec: parentRefs: - name: envoy-gateway namespace: default sectionName: https hostnames: - 'nantoka.wildcard.example.com' rules: - backendRefs: - name: service-name port: 80 matches: - path: type: PathPrefix value: /
spec.parentRefs
に上記で設定した Gateway resource を指定するspec.parentRefs[].sectionName
は Gateway resource の listener で設定した name を指定する
spec.rules[].backendRefs
にトラフィックをルーティングする Service を指定する
感想
以下に Traefik から乗り換えてよかったところ、残念だったところをあげる。
よかったところ
- 標準にのっとっている安心感 (まだ core ではないが)
- 設定まわりがきちんと CRD として定義されており、管理がしやすい
- これはまさに Gateway API の狙いの一つ かと思う