🤖

おうちKubernetesで構築するCilium Gateway

に公開

今回のポストではCilium Gatewayをセットアップします。

おうちラボでのKubernetesクラスタ上にセットアップするのですが、LANからクラスタ内で動いているサービスにアクセスできるようGatewayのIPアドレスはCilium L2Announcementで広報し、クラスタ外のホストからもサービスへアクセスできるようにします。

今回はplain httpだけセットアップするのですが、別のポストでcert-managerもセットアップしてhttpsアクセスを構築するものも用意しています。よければご覧ください。

次のポスト:Cilium Gatewayと連動するcert-managerの導入でTLS証明書自動化

前提

"lab-hlv3"と名付けている試験用のクラスタで構築と記事作成をしています。またドメイン名は"lab.blink-1x52.net"としています。

手順

  • "testbed" namespaceの準備
  • traefik/whoamiのdeploymentとserviceを作成
  • Cilium Gatewayを使うための要件を確認
  • Gateway API CRDsをインストール
  • Gateway専用のnamespaceを用意
  • Gateway作成
  • IP pool作成
  • L2Announcement作成
  • 出来上がったものの確認

Namespaceの準備

先のポストで作成済みのnamespaceですが、こちらを用意します。

# ./clusters/lab-hlv3/namespaces/testbed.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: testbed

httpアクセス確認用のwhoamiサービスの用意

https://github.com/traefik/whoami

Tiny Go webserver that prints OS information and HTTP request to output.

こちらをdeploymentとして用意し、アクセスするためのserviceも作成します。

以下は最小限の内容のファイルですが、基本的にtraefik/whoamiコンテナを立ち上げ、ポート80で通信を受け、whoamiコンテナのポート80へ通信を流すserviceを用意するというものです。

# ./apps/base/testbed/whoami.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
  namespace: testbed
spec:
  ports:
    - name: http
      targetPort: 80
      port: 80
  selector:
    app: whoami
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  namespace: testbed
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      hostname: whoami
      containers:
        - name: whoami
          image: traefik/whoami:v1.10.3
          imagePullPolicy: IfNotPresent

Apps flux kustomizationでは上のファイルが入っているディレクトリを指しています。

# ./apps/lab-hlv3/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  # testbed
  - ../base/testbed

そしてそのtestbedディレクトリのkustomizationで、先のwhoami.yamlを含めています。

# ./apps/base/testbed/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - tools-deployment.yaml
  - whoami.yaml

ここまでで出来上がったもの

出来上がったものは次の通りです。

$ flux tree ks apps
Kustomization/flux-system/apps
├── Service/testbed/whoami
├── Deployment/testbed/tools
├── Deployment/testbed/whoami

$ kubectl get svc whoami -n testbed
NAME     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
whoami   ClusterIP   10.96.133.198   <none>        80/TCP    5d5h

Kubernetesクラスタ上でのサービスへのアクセス

クラスタのメンバーノードからは、サービスにアクセスすることが可能です。この時点では、クラスタ外からはアクセスできません。

# check the cluster IP of the service whoami
$ kubectl get svc -n testbed
NAME     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
whoami   ClusterIP   10.96.133.198   <none>        80/TCP    5d4h

# access the service
$ curl 10.96.133.198
Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 10.0.3.170
IP: fe80::e834:eff:fe1f:44f3
RemoteAddr: 10.0.3.163:55572
GET / HTTP/1.1
Host: 10.96.133.198
User-Agent: curl/7.76.1
Accept: */*

Cilium Gatewayの要件

https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gateway-api/#prerequisites

  • NodePort有効、あるいはkube-proxy replacement構成
  • L7プロキシ有効
  • Kubernetes Gateway API v1.2.0 CRDsのインストール
    • (optional) experimental TLSRoute CRDのインストール

Gateway APIのCRDsのインストールだけ未対応ですので次に実施します。その他はKubernetesクラスタ構築時およびCiliumインストール時に対応しており、以前のポストより確認できます。今回の構成は、Kube-proxyはインストールせず、Cilium (envoy)に代わりに活躍してもらっている構成です。

Gateway API CRDs

これらはKubernetesクラスタ構築時、デフォルトではインストールされていないものです。ファイルをダウンロードし、GitOpsレポジトリ内、infra-controllers flux kustomization配下に置きます。

infra-controllers kustomizationのファイルはこのような内容です。

# ./infrastructure/lab-hlv3/controllers/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cm-placeholder.yaml
  ### crds
  # gateway api
  # version 1.2.0
  - crds/gateway-api/standard/standard-install-v1.2.0.yaml
  - crds/gateway-api/experimental/gateway.networking.k8s.io_tlsroutes-v1.2.0.yaml

Gateway API CRDsのファイルはダウンロードし、次のように配置しました。

  • ./infrastructure/lab-hlv3/controllers/crds/gateway-api/standard/standard-install-v1.2.0.yaml
  • ./infrastructure/lab-hlv3/controllers/crds/gateway-api/experimental/gateway.networking.k8s.io_tlsroutes-v1.2.0.yaml

CRDsインストール結果

kubectl get crdskubectl api-resourcesなどのコマンドでインストールが確認できます。

$ kubectl api-resources | grep gateway
gatewayclasses                      gc                                  gateway.networking.k8s.io/v1             false        GatewayClass
gateways                            gtw                                 gateway.networking.k8s.io/v1             true         Gateway
grpcroutes                                                              gateway.networking.k8s.io/v1             true         GRPCRoute
httproutes                                                              gateway.networking.k8s.io/v1             true         HTTPRoute
referencegrants                     refgrant                            gateway.networking.k8s.io/v1beta1        true         ReferenceGrant
tlsroutes                                                               gateway.networking.k8s.io/v1alpha2       true         TLSRoute

Cilium helmチャートのvaluesファイル

Cilium v1.17.1のインストール時のオプションですが、以下のリストがvaluesファイル上での変更点です。

以前のポストではexample.netドメインで記載していたのですが、今後cert-managerなどもやる関係でドメイン名を変更しています。

  • k8sServiceHost: lab-kube-endpoint.lab.blink-1x52.net
  • k8sServicePort: "8443"
  • k8sClientRateLimit.qps: 33
  • k8sClientRateLimit.burst: 50
  • kubeProxyReplacement: "true"
  • kubeProxyReplacementHealthzBindAddr: "0.0.0.0:10256"
  • l2announcements.enabled: true
  • l2announcements.leaseDuration: 3s
  • l2announcements.leaseRenewDeadline: 1s
  • l2announcements.leaseRetryPeriod: 200ms
  • externalIPs.enabled: true
  • gatewayAPI.enabled: true
  • etcd.enabled: true
  • etcd.ssl: true
  • etcd.endpoints: ["https://192.0.2.5:2379", "https://192.0.2.6:2379", "https://192.0.2.7:2379"] # dummy ipaddr here
  • hubble.ui.enabled: true
  • hubble.relay.enabled: true
  • hubble.peerService.clusterDomain: lab.blink-1x52.net

Cilium Gateway用のnamespace

Cilium gatewayを動かすに際し、専用のnamespaceを用意することにしました。

# ./clusters/lab-hlv3/namespaces/gateway.yaml
---
kind: Namespace
apiVersion: v1
metadata:
  name: gateway
  labels:
    service: gateway
    type: infrastructure

Gateway作成

ではCiliumのgatewayを作成します。

以下のgatewayは"cilium"というGatewayClassを使うよう記載してあります。これはCiliumインストール時に自動的に作られるGatewayClassで、kubectl describe gcで内容を確認できます。他には、ダミーIPアドレスとして192.0.2.83を使うこと、またホスト名"whoami-kube.lab.blink-1x52.net"用のplain httpのリスナーを用意し、このリスナーを利用できるのは"gateway: cilium"というラベルがセットされているnamespaceからのみという条件を付けてあります。

# ./infrastructure/lab-hlv3/configs/cilium/gateway.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cilium-gateway
  namespace: gateway
spec:
  gatewayClassName: cilium
  addresses:
    - type: IPAddress
      value: 192.0.2.83
  listeners:
    - name: whoami-kube-http
      hostname: whoami-kube.lab.blink-1x52.net
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway: cilium

では"testbed" namespaceの方も更新し、ラベルをセットします。

# ./clusters/lab-hlv3/namespaces/testbed.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: testbed
  labels:
    service: testbed
    type: app
    gateway: cilium

Cilium Gateway用のIP PoolおよびL2Announcement

Gatewayが作られると、gatewayおよびserviceが出来ていることが確認できると思います。kubectl get gtw -n gatewaykubectl get svc -n gatewayといったコマンドで確認できます。

次に、クラスタ作成時のポストでHubble UIへのアクセスを用意したのと同じように、gatewayもLAN上、クラスタ外からアクセスできるようにします。

  • GatewayのserviceにIPアドレスを振るためのIP poolの作成
  • GatewayのserviceのIPアドレスをL2AnnouncementでLANへ広報

ダミーIPアドレスを使っていますがこちらがIP poolです。serviceSelectorとしては、"gateway" namespace上のサービス何にでも、という条件にしてあります。

---
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
  name: "ippool-gateway"
spec:
  blocks:
    - start: "192.0.2.83"
      stop: "192.0.2.83"
  serviceSelector:
    matchLabels:
      "io.kubernetes.service.namespace": "gateway"

次にL2Announcementです。また上とは異なるserviceSelectorをセットしてしまっていますが、これはkubectl get svc -n gateway -o yamlなどで確認できる、作成されたcilium gatewayのserviceにセットされているラベルを持ってきています。またinterfacesに関しては、labではなくメインで動かしているクラスタがVM, baremetal, OSディストロあれこれ混在しているので、どのノードでもマッチするよう対象リストを増やしており、それをそのまま今回も使っている形です。

---
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
  name: l2-cilium-gateway
spec:
  serviceSelector:
    matchLabels:
      io.cilium.gateway/owning-gateway: cilium-gateway
      gateway.networking.k8s.io/gateway-name: cilium-gateway
  interfaces:
    - ^eth[0-9]+
    - ^eno[0-9]+
    - ^enp[0-9]s[0-9]+
  loadBalancerIPs: true

infra-configs kustomization

更新後のinfra-configs flux kustomizationのファイルの中身は次の通りです。

# ./infrastructure/lab-hlv3/configs/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cm-placeholder.yaml
  # cilium gateway
  - cilium/gateway.yaml
  - cilium/ippools-cilium-gateway.yaml
  - cilium/l2announcement-cilium-gateway.yaml

GatewayにIPアドレスがセットされた状態

ダミーIPアドレスでの出力ですが、変更後のgateway (およびsvc)は以下の通りです。LAN上のホストでARP情報を確認すると(例えばWindowsでarp -aなど実行すると)、EXTERNAL-IPが認識されていることが確認できると思います。

$ kubectl get svc,gtw -n gateway
NAME                                    TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE
service/cilium-gateway-cilium-gateway   LoadBalancer   10.96.87.48   192.0.2.83   80:31710/TCP,443:30231/TCP   5d5h

NAME                                               CLASS    ADDRESS        PROGRAMMED   AGE
gateway.gateway.networking.k8s.io/cilium-gateway   cilium   192.0.2.83   True         5d18h

whomaiサービスアクセス用のHTTPRouteの作成

Gatewayもwhoamiサービスも出来ていますので、次はこの二つを繋ぐためのHTTPRouteを作成します。

既存のwhomai.yamlファイルに追加で着たらよかったのですが、一つのGitOpsレポジトリで複数のクラスタを管理しており、あるクラスタではGateway APIがインストールされていないということがあるので、HTTPRouteは個別のファイルで以下の通り用意しました。

  • parentRefsは"gateway" namespace上の"cilium-gateway"の"whoami-kube-http"リスナーを指定している
  • backendRefsは"whoami"サービスのポート80を指定している
# ./apps/lab-hlv3/httproutes/whoami-http.yaml
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: whoami-http
  namespace: testbed
spec:
  parentRefs:
    - name: cilium-gateway
      sectionName: whoami-kube-http
      namespace: gateway
  hostnames:
    - "whoami-kube.lab.blink-1x52.net"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: whoami
          port: 80

Flux apps kustomizationファイルはこのようになりました。

# ./apps/lab-hlv3/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  # testbed
  - ../base/testbed
  # http routes
  - httproutes/whoami-http.yaml

Cilium Gatewayを通じてwhoamiサービスへアクセス

出来上がりです!

$ curl http://whoami-kube.lab.blink-1x52.net
Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 10.0.3.170
IP: fe80::e834:eff:fe1f:44f3
RemoteAddr: 10.0.3.174:39409
GET / HTTP/1.1
Host: whoami-kube.lab.blink-1x52.net
User-Agent: curl/8.5.0
Accept: */*
X-Envoy-Internal: true
X-Forwarded-For: IP_ADDR_OF_HOST_EXECUTING_CURL
X-Forwarded-Proto: http
X-Request-Id: 96f39cec-ce39-4c26-97d3-216dae13b9d0

このセットアップが完成すれば、あとはcilium-gatewayのリスナー追加とHTTPRouteの作成で新たなウェブアクセスが簡単に用意できます。なお今回のセットアップでは"gateway: cilium"というラベルの有無でgateway利用可否がnamespace単位でコントロールできます。別のnamespace上に作ったあのサービスを新たにアクセスできるようにしよう、といった時にはラベルの更新もお忘れなく!

Discussion