OKEでNGINX Gateway Fabricを使ってみた

# はじめに

(1年ぶりに記事を書いています...)

この記事は、[Oracle Cloud Infrastructure Advent Calender 2024 シリーズ 1 Day6](https://qiita.com/advent-calendar/2024/oci)の記事として書いています。

 久々にしては平凡？な記事ですが、OKEでGateway APIを動かしてみたので、その内容について書いていこうと思います。

 # Gateway APIについて

 まず、Gateway APIについて軽く説明します。

Gateway APIとはKubernetesでのクラスタ外からクラスタ内へのL4/L7ロードバランサー相当の機能です。
Kubernetesでのロードバランサー機能といえば、ServiceやIngressがありますが、昨年の10月にGateway APIという新たな実装がGAになりました。

現在までに様々なベンダーによるGateway APIの実装がリリースされています。
参考までに以下のような実装があります。

- [AWS Gateway API （α）（Amazon VPC Latticeを利用）](https://github.com/aws/aws-application-networking-k8s)
- [Azure Application Gateway（GA)](https://learn.microsoft.com/ja-jp/azure/application-gateway/for-containers/overview)
- [GKE Gateway（GA)](https://cloud.google.com/kubernetes-engine/docs/concepts/gateway-api?hl=ja)
- [Cilium（β）](https://isovalent.com/blog/post/cilium-release-114/)
- [Istio（GA)](https://gateway-api.sigs.k8s.io/mesh/)
- [NGINX Gateway Fabric（GA）](https://github.com/nginxinc/nginx-gateway-fabric)

~~なお、OKEの実装がないのは察してください...~~

 Gateway APIの特徴は一般的に以下と言われています。

- Roleによって、扱うべきリソースが明確に分かれている
- L4からL7プロトコルまで幅広いプロトコルをサポート（実装による）
    - ServiceはL4のみ、IngressはL7のみサポートしています
- 高い拡張性と可搬性
    - Ingressはアノテーション(`.metadata.annotation`)を利用してベンダー独自の仕様を取り入れていましたが、Gateway APIは標準仕様(`.spec`)として様々な機能を組み込んでいる
- リソースはIngressよりかなり数が多い（管理対象が多い）

この中で、一番特筆すべきは「Roleによって、扱うべきリソースが明確に分かれている」という特徴です。

公式ドキュメントでは以下のような図で説明されています。

![CleanShot 2024-12-06 at 12.37.35@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/62bf3bd6-9521-e265-fb34-1006cd646250.png)
参考：https://gateway-api.sigs.k8s.io/

これによって、以下のような課題を解決できます。

- ServiceやIngressを設定するのは開発者…?それともクラスタ管理者(インフラエンジニア)…??
    - 開発者だとしたら、Load Balancerをボコボコ作ってしまっていいのか…?課金管理とかどうなる…??
    - クラスタ管理者(インフラエンジニア)だとしたら、わざわざ開発者が連絡しないといけないの…??

Gateway APIであれば、インフラエンジニア、クラスタ管理者、アプリケーション開発者がどのリソースを扱うべきかを標準化しています。

では、早速OKE上にGatewa APIを構築してみましょう。

# NGINX Gateway Fabricを利用したGateway API

ここからは~~Gateway APIがまだ未実装なので~~NGINX Gateway Fabricを利用してGateway APIをOKE上に構築していきます。

## 環境構築(OKE)

OKEのプロビジョニングは[こちら](https://oracle-japan.github.io/ocitutorials/cloud-native/oke-for-commons/)を参考に実施してください。
Worker NodeのスペックはVM.Standard.E5.Flexシェイプで2oCPU、16GB RAM × 3 Nodeで作成してください。
[こちら](https://kubectl.docs.kubernetes.io/installation/kustomize/binaries/)を参考にkustomizeもインストールしてください。

## NGINX Gateway Fabricのインストール

```sh
kubectl kustomize "https://github.com/nginxinc/nginx-gateway-fabric/config/crd/gateway-api/standard?ref=v1.5.0" | kubectl apply -f -
```

```sh
kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.5.0/deploy/crds.yaml
```

```sh
kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.5.0/deploy/default/deploy.yaml
```

以下のリソースが作成できていれば問題ありません。

```sh
kubectl get pods -n nginx-gateway
NAME                             READY   STATUS    RESTARTS   AGE
nginx-gateway-7f7748c4d9-nr94g   2/2     Running   0          6d1h
```

これがGateway APIの役割を果たすリバースプロキシとなるNGINXサーバです。

### Gateway APIリソースのインストール

以下のManifestを作成します。  

```yaml:gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    hostname: "*.example.com"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: coffee
spec:
  parentRefs:
  - name: gateway
    sectionName: http
  hostnames:
  - "cafe.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /coffee
    backendRefs:
    - name: coffee
      port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: tea
spec:
  parentRefs:
  - name: gateway
    sectionName: http
  hostnames:
  - "cafe.example.com"
  rules:
  - matches:
    - path:
        type: Exact
        value: /tea
    backendRefs:
    - name: tea
      port: 80
```

リソースを適用します。

```sh
kubectl apply -f gateway.yaml
```

リソースを確認します。

```sh
kubectl describe httproutes
Name:         coffee
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  gateway.networking.k8s.io/v1
Kind:         HTTPRoute
Metadata:
  Creation Timestamp:  2024-12-01T13:46:51Z
  Generation:          1
  Resource Version:    821
  UID:                 cc591089-d3aa-44d3-a851-e2bbfa285029
Spec:
  Hostnames:
    cafe.example.com
  Parent Refs:
    Group:         gateway.networking.k8s.io
    Kind:          Gateway
    Name:          gateway
    Section Name:  http
  Rules:
    Backend Refs:
      Group:
      Kind:    Service
      Name:    coffee
      Port:    80
      Weight:  1
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /coffee
Status:
  Parents:
    Conditions:
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               The route is accepted
      Observed Generation:   1
      Reason:                Accepted
      Status:                True
      Type:                  Accepted
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               All references are resolved
      Observed Generation:   1
      Reason:                ResolvedRefs
      Status:                True
      Type:                  ResolvedRefs
    Controller Name:         gateway.nginx.org/nginx-gateway-controller
    Parent Ref:
      Group:         gateway.networking.k8s.io
      Kind:          Gateway
      Name:          gateway
      Namespace:     default
      Section Name:  http
Events:              <none>


Name:         tea
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  gateway.networking.k8s.io/v1
Kind:         HTTPRoute
Metadata:
  Creation Timestamp:  2024-12-01T13:46:51Z
  Generation:          1
  Resource Version:    823
  UID:                 d72d2a19-1c4d-48c4-9808-5678cff6c331
Spec:
  Hostnames:
    cafe.example.com
  Parent Refs:
    Group:         gateway.networking.k8s.io
    Kind:          Gateway
    Name:          gateway
    Section Name:  http
  Rules:
    Backend Refs:
      Group:
      Kind:    Service
      Name:    tea
      Port:    80
      Weight:  1
    Matches:
      Path:
        Type:   Exact
        Value:  /tea
Status:
  Parents:
    Conditions:
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               The route is accepted
      Observed Generation:   1
      Reason:                Accepted
      Status:                True
      Type:                  Accepted
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               All references are resolved
      Observed Generation:   1
      Reason:                ResolvedRefs
      Status:                True
      Type:                  ResolvedRefs
    Controller Name:         gateway.nginx.org/nginx-gateway-controller
    Parent Ref:
      Group:         gateway.networking.k8s.io
      Kind:          Gateway
      Name:          gateway
      Namespace:     default
      Section Name:  http
Events:              <none>
```

リソースを確認します。

```sh
kubectl describe gateway 
Name:         gateway
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  gateway.networking.k8s.io/v1
Kind:         Gateway
Metadata:
  Creation Timestamp:  2024-12-01T13:46:36Z
  Generation:          1
  Resource Version:    824
  UID:                 2ae8ec42-70eb-41a4-b249-3e47177aea48
Spec:
  Gateway Class Name:  nginx
  Listeners:
    Allowed Routes:
      Namespaces:
        From:  Same
    Hostname:  *.example.com
    Name:      http
    Port:      80
    Protocol:  HTTP
Status:
  Addresses:
    Type:   IPAddress
    Value:  10.244.0.5
  Conditions:
    Last Transition Time:  2024-12-01T13:46:51Z
    Message:               Gateway is accepted
    Observed Generation:   1
    Reason:                Accepted
    Status:                True
    Type:                  Accepted
    Last Transition Time:  2024-12-01T13:46:51Z
    Message:               Gateway is programmed
    Observed Generation:   1
    Reason:                Programmed
    Status:                True
    Type:                  Programmed
  Listeners:
    Attached Routes:  2
    Conditions:
      Last Transition Time:  2024-12-0113:46:51Z
      Message:               Listener is accepted
      Observed Generation:   1
      Reason:                Accepted
      Status:                True
      Type:                  Accepted
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               Listener is programmed
      Observed Generation:   1
      Reason:                Programmed
      Status:                True
      Type:                  Programmed
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               All references are resolved
      Observed Generation:   1
      Reason:                ResolvedRefs
      Status:                True
      Type:                  ResolvedRefs
      Last Transition Time:  2024-12-01T13:46:51Z
      Message:               No conflicts
      Observed Generation:   1
      Reason:                NoConflicts
      Status:                False
      Type:                  Conflicted
    Name:                    http
    Supported Kinds:
      Group:  gateway.networking.k8s.io
      Kind:   HTTPRoute
      Group:  gateway.networking.k8s.io
      Kind:   GRPCRoute
Events:       <none>
```

## サンプルアプリケーションのインストール

以下のDeploymentとServiceを作成します。

```yaml:app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: coffee
spec:
  replicas: 1
  selector:
    matchLabels:
      app: coffee
  template:
    metadata:
      labels:
        app: coffee
    spec:
      containers:
      - name: coffee
        image: nginxdemos/nginx-hello:plain-text
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: coffee
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: coffee
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tea
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tea
  template:
    metadata:
      labels:
        app: tea
    spec:
      containers:
      - name: tea
        image: nginxdemos/nginx-hello:plain-text
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: tea
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: tea
```

リソースを適用します。

```sh
kubectl apply -f app.yaml
```

以下のPodが作成されています。

```sh
kubectl -n default get pods
NAME                      READY   STATUS    RESTARTS   AGE
coffee-6db9sdd495b-wk2mm   1/1     Running   0          12s
tea-7b7d6de47d-d4qcf      1/1     Running   0          11s
```

## 動作確認

作成した環境を動かしてみます。

```sh
curl --resolve cafe.example.com:8080:127.0.0.1 http://cafe.example.com:8080/coffee
```

```sh
Server address: 10.244.0.6:8080
Server name: coffee-6db9sdd495b-wk2mm
Date: 5/Dec/2024:13:52:13 +0000
URI: /coffee
Request ID: fb226a5s45g4f927b484dd31fb30e747
```


```sh
curl --resolve cafe.example.com:8080:127.0.0.1 http://cafe.example.com:8080/tea
```

```sh
Server address: 10.244.0.7:8080
Server name: tea-7b7d6de47d-d4qcf
Date: 5/Dec/2024:13:55:26 +0000
URI: /tea
Request ID: 43882f2f5794a1ee05dr5a017a035ce3
```

こんな感じでアクセスできます。
挙動はIngressとほぼ同じですが、Gateway APIでは重み付けルーティングやB/GデプロイなどのIngressやServiceではできなかった様々なルーティング方式を実現できます。

# おわりに

今回は簡単にGateway APIをOKEで動かしてみました！
他にも様々なことができますが、それはまた別に機械に記事化できればと思います。

今回ご紹介したGateway APIを含めてKubernetesでのトライックルーティングの解説を[こちら](https://ochacafe.connpass.com/event/333245/)でお話しするので、お時間がある方はぜひご参加ください。

https://gateway-api.sigs.k8s.io/

https://docs.nginx.com/nginx-gateway-fabric/installation/installing-ngf/manifests/

https://docs.nginx.com/nginx-gateway-fabric/get-started/