OCI Native Ingress Controllerを試してみよう

# はじめに

先日OCI Native Ingress Controllerがリリースされました！

https://docs.oracle.com/en-us/iaas/releasenotes/changes/382db118-aeca-4011-af49-f59bbc94ae0


IngressはKubernetesにおけるL7ロードバランサーに相当するものです。

今までは、OCI NativeなIngress Controllerがなかったため、NIGNX Ingress ControllerなどのサードパーティのIngress Controllerに頼っていました。

そのため、例えばパスベースのルーティングやHTTPヘッダーによるルーティングを行うためにOKEのクラスタ内にリバースプロキシの役割を果たすPodをデプロイする必要がありました。

このOCI Native Ingress Controllerの登場により、OCI LoadBalancerの機能としてパスベースのルーティングやHTTPヘッダーによるルーティングを設定したり、TLS証明書をOCI証明書サービスと連携して管理したりということが可能になります。

つまり、OKEとOCI LoadBalancerおよびその関連リソースがよりシームレスに連携できるようになるということです。

この記事では、簡単にOCI Native Ingress Controllerの使い方を見ていきましょう。

# 事前準備

まずは事前準備を行なっていきます。

## 動的グループとポリシーの設定

まずは、OKEからLoad Balancerや証明書サービスのプロビジョニングや設定を行えるようにするためのインスタンスプリンシパルを設定します。
OCI Native Ingress Controllerではインスタンスプリンシパルもしくはユーザープリンシパル(鍵やパスワードなどのユーザ情報)のいずれかを利用して運用しますが、今回はインスタンスプリンシパルを利用します。
ユーザプリンシパルで設定を実施したい場合は、以下のドキュメントをご確認ください。

https://docs.oracle.com/ja-jp/iaas/Content/ContEng/Tasks/contengsettingupnativeingresscontroller.htm#contengsettingupnativeingresscontroller-auth__section_userprincipalauthenticaton

まずは以下の動的グループを作成します。
ここで作成する動的グループを`Native-Ingress-Controller-Dyn-Group`とします。

今回は任意のコンパートメント配下にある全てのインスタンス(Worker Node)を対象とします。
よりセキュアに管理するには特定のWorker Nodeのみを対象とすることもできます。

```
ALL {instance.compartment.id = '<compartment-ocid>'}
```

次に作成した動的グループに対して以下のポリシーを設定します。
また、同様のポリシーが操作しているユーザグループにも必要です。

```
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to manage load-balancers in compartment acme-oke-cluster-compartment   
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to use virtual-network-family in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to manage cabundles in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to manage cabundle-associations in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to manage leaf-certificates in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to read leaf-certificate-bundles in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to manage certificate-associations in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to read certificate-authorities in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to manage certificate-authority-associations in compartment acme-oke-cluster-compartment
Allow dynamic-group Native-Ingress-Controller-Dyn-Group to read certificate-authority-bundles in compartment acme-oke-cluster-compartment
```

これで、動的グループとポリシーの設定は完了です。

:::note warn 
**ワークロードアイデンティティについて**
OKEでの拡張クラスタではワークロードアイデンティティと呼ばれるPodレベルでのOCIリソースに対する認可がサポートされていますが、OCI Native Ingress Controllerでは2023/6時点でサポートされておりません。
:::

## OKEクラスタの構築

今回利用するOKEクラスタを構築します。

OCI Native Ingress Controllerを利用するには2023/6時点で以下の条件があります。

- 仮想ノード(Virtual Nodes)でないこと
- CNIにVCN Native Pod Networkingを使用していること
- Kubernetesバージョンがv1.25.4以上であること

OKEクラスタの作成は以下のチュートリアルを参考にしてください。

https://oracle-japan.github.io/ocitutorials/cloud-native/oke-for-commons/

ここまでで事前準備は完了です。

# OCI Native Ingress Controllerの動作確認

ここからOCI Native Ingress Controllerの動作確認を行なっていきます。

## cert-managerのインストール

~~証明書サービスとの連携を行うためにcert-managerをインストールします。~~
Podの[Readiness Gates](https://kubernetes.io/ja/docs/concepts/workloads/pods/pod-lifecycle/#pod-readiness-gate)で利用するcert-managerをインストールします。
これはReadiness GatesをサポートするWebhookでのTLS通信に利用します。

```sh
kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml
```

これでcert-managerのインストールは完了です。

## OCI Native Ingress Controllerのインストール

まずはOCI Native Ingress Controllerをインストールします。

まずはGitHubからレポジトリをcloneします。

```sh
git clone https://github.com/oracle/oci-native-ingress-controller
```

`helm/oci-native-ingress-controller/values.yaml`を編集します。

編集する箇所は`compartment_id`と`subnet_id`です。
`subnet_id`にはOCI LoadBalancerで利用するサブネットのOCIDを定義します。

```yaml:helm/oci-native-ingress-controller/values.yaml
#
# OCI Native Ingress Controller
#
# Copyright (c) 2023 Oracle America, Inc. and its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
#

# Default values for oci-native-ingress-controller.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

controller_class: oci.oraclecloud.com/native-ingress-controller
lease_lock_name: oci-native-ingress-controller
compartment_id: "ocid1.compartment.oc1..aaaaaaaaw4afdnpdddddddddcnamvmrzgdq"
subnet_id: "ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaa4hwibdtrczy7mhddddddd6mx74j2kcnzaoleka"
====
```

編集が完了したら、helmを利用してインストールします。

```sh
helm install oci-native-ingress-controller helm/oci-native-ingress-controller
```

インストールされたことを確認します。

```sh
kubectl get pods -n native-ingress-controller-system --selector='app.kubernetes.io/name in (oci-native-ingress-controller)' -o wide
```

以下のように出力されれば問題ありません。

```sh
NAME                                             READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
oci-native-ingress-controller-58fc779658-8d658   1/1     Running   1          25h   10.0.10.97    10.0.10.105   <none>           <none>
```

これでOCI Native Ingress Controllerのインストールは完了です。

## IngressClassParametersとIngressClassのデプロイ

ここでは、IngressClassParametersとIngressClassという2つのリソースを確認します。

これを作成することにより、OCI LoadBalancerがプロビジョニングされます。
ただし、バックエンドセットは以降に作成するIngressを作成することにより、作成されます。

`compartmentId`と`subnetId`は先ほどの`values.yaml`と同じ値を設定します。

```yaml:IngressClassParameters.yaml
apiVersion: "ingress.oraclecloud.com/v1beta1"
kind: IngressClassParameters
metadata:
  name: native-ic-params
spec:
  compartmentId: "ocid1.compartment.oc1..aaaaaaaaw4afdnptbjyff7ojizzddddddrzgdq"
  subnetId: "ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaa4hwibdtrczy7mhdhiwtxsddddkcnzaoleka"
  loadBalancerName: "native-ic-lb"
  isPrivate: false
  maxBandwidthMbps: 10
  minBandwidthMbps: 10
```

`.spec.parameters`配下は環境に合わせてください。
今回は`default`ネームスペースを利用します。

```yaml:IngressClass.yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: native-ic-ingress-class
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: oci.oraclecloud.com/native-ingress-controller
  parameters:
    scope: Namespace
    namespace: default
    apiGroup: ingress.oraclecloud.com
    kind: ingressclassparameters
    name: native-ic-params
```

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

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


この2つのManifestをデプロイすると、OCI LoadBalancerがプロビジョニングされます。

これでIngressClassParametersとIngressClassのデプロイは完了です。

## Ingressのデプロイ

Ingressをデプロイする前に以下のコマンドで適当なnginxサービスを作成しておきましょう。

```sh
kubectl run nginx1 --image nginx 
kubectl run nginx2 --image nginx
kubectl expose pod nginx1 --target-port=80 --port=80
kubectl expose pod nginx2 --target-port=80 --port=81
```

最後にIngressをデプロイします。
これにより、先ほどプロビジョンビングされたOCI LoadBalancerにバックエンドセットやルーティングルールなどが設定されます。
今回は以下のようなルールを作成します。

- `/test:80`でアクセスすると`nginx1`という名前のサービスに紐づけられた80番ポートで公開されているサービスにアクセス
- `/sample:81`でアクセスすると`nginx2`という名前のサービスに紐づけられた80番ポートで公開されているサービスにアクセス
- `/test:80`または`/sample:81`以外でアクセスされると`nginx1`という名前のサービスに紐づけられた80番ポートで公開されているサービスにアクセス


```yaml:ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
  annotations:
    oci-native-ingress.oraclecloud.com/healthcheck-port: "80"
spec:
    ingressClassName: native-ic-ingress-class
    rules:
    - http:
        paths:
        - path: /test
          pathType: Prefix
          backend:
           service:
            name: nginx1
            port:
              number: 80
        - path: /sample
          pathType: Prefix
          backend:
           service:
            name: nginx2
            port:
              number: 81
    defaultBackend:
      service:
        name: nginx1
        port:
          number: 80
```

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

これでIngressのデプロイは完了です。

## 動作確認

Ingressが作成されるとOCI LoadBalancerにバックエンドセットやルーティングルールが設定されます。

OCI LoadBalancerのステータスをOCIコンソールから見ると以下のようになります。

- リスナーの状態
![スクリーンショット 2023-06-02 16.51.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/d8a54534-fd89-2820-bd5f-8ef59cf5ea5f.png)

- ルーティングポリシーの状態
![スクリーンショット 2023-06-02 17.12.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/b47f83bb-0037-45ba-f1bb-69954f44ae90.png)
![スクリーンショット 2023-06-02 16.52.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/b5c97847-cc6d-1400-ccf0-14e80361fa19.png)

- バックエンドセットの状態
![スクリーンショット 2023-06-02 16.52.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/28ceb059-b843-17f8-10af-bf2718f81c7e.png)

![スクリーンショット 2023-06-02 16.53.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/1126a762-20ed-6924-9210-b68ee8fe8d6d.png)

![スクリーンショット 2023-06-02 16.53.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/0c4a7192-dfea-fd3a-585d-6d558e75f273.png)

![スクリーンショット 2023-06-02 16.54.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40392/5b6308ba-d11c-8732-3c40-f74399783091.png)

バックエンドセットに紐づいているIPアドレスはPodのIPアドレスです。

```sh
$ kubectl get pods -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
nginx1   1/1     Running   0          76s   10.0.10.66   10.0.10.105   <none>           <none>
nginx2   1/1     Running   1          26h   10.0.10.36   10.0.10.105   <none>           <none>
```

そのため、OCI Native Ingress Controllerを利用するとkube-proxyなどを経由せずに直接LBからPodにアクセス可能なため、余分なホップを回避できます。
ちなみに、OCI Native Ingress Controllerを利用しない場合はWorker Node(Compute)のIPアドレスになっています。


# まとめ

このようにOCI Native Ingress Controllerを利用すると今まで実現できなかったOCI LoadBalancerでのルーティングポリシーの設定やよりホップ数の少ないルーティングが可能になります。

後日OCI Native Ingress ControllerとOCI証明書サービスの連携を行う記事も公開しようと思います。

# 参考資料

https://docs.oracle.com/ja-jp/iaas/Content/ContEng/Tasks/contengsettingupnativeingresscontroller.htm