ROSA で Nginx Ingress Operator を使ってみる

# はじめに
`ROSA(Red Hat OpenShift on AWS)` には、標準の `Ingress Controller` が存在していますが、標準の `Ingress` の代わりに `Nginx` の `Ingress Operator` を使用して、`Nginx`の`Ingress Controller` を使用してみます。

# ROSA(Red Hat OpenShift on AWS) とは？
`ROSA` は、`Red Hat OpenShift` の `AWS` 上で提供されているマネージド・サービスです。名前のフォーマットは違いますが、簡単に言うと、`AKS`、`EKS`、`GKE` の `Red Hat` 版です。

`Red Hat` の Kubernetes Districution である `OpenShift` が使われています。



# 参考資料

以下の Git Hub のレポを参考にしました。

https://github.com/nginxinc/nginx-ingress-helm-operator

# Nginx Ingress Operator のインストール

Operator Hub より Ingress Operator をインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/b655a314-dc04-f480-2102-c282e113d9f8.png)

ここは特に何の設定もせず、デフォルのままインストールボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/acb2016d-3f07-1ae2-49e0-2438b1870209.png)

インストール完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/445c123d-31aa-59df-f955-855ff4df80dc.png)


# Operatorインストール後の観察

Pod を見てみます。インストールされた Operator が Pod として稼働しています。

```
$ oc project nginx-ingress
Now using project "nginx-ingress" on server "https://api.f5cluster.m51o.p1.openshiftapps.com:6443".
$ oc get pods
NAME                                                         READY   STATUS    RESTARTS   AGE
nginx-ingress-operator-controller-manager-85b49c485b-p89sp   2/2     Running   0          90s
$ 
```


今度は `Service` リソースを見てみます。
名前からして、metrics を提供するもののようです。

```
$ oc get svc
NAME                                                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
nginx-ingress-operator-controller-manager-metrics-service   ClusterIP   172.30.208.138   <none>        8443/TCP   38s
$ 
```


# Nginx Ingress Controller の作成

`Nginx Ingress Controller` を作成していきます。`NginxIngress` リソースを作成する事で、`Nginx Ingress Controller` の Pod が生成されます。

## SCC の作成と観察

`Ingress Controller` の作成前に、`OpenShift` では、以下の `SCC(Security Context Cosntrain)` が必要になります。

```
kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-ingress-helm-operator/main/resources/scc.yaml
```

一応、apply した yaml の中身を確認しておきます。 `nginx-ingress-admin` という `SCC` を作成している事がわかります。

```
$   curl https://raw.githubusercontent.com/nginxinc/nginx-ingress-helm-operator/main/resources/scc.yaml
# Create SCC for IC resources
kind: SecurityContextConstraints
apiVersion: security.openshift.io/v1
metadata:
  name: nginx-ingress-admin
allowPrivilegedContainer: false
runAsUser:
  type: MustRunAs
  uid: 101
seLinuxContext:
  type: MustRunAs
fsGroup:
  type: MustRunAs
supplementalGroups:
  type: MustRunAs
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowHostDirVolumePlugin: false
allowHostIPC: false
readOnlyRootFilesystem: false
seccompProfiles:    
- runtime/default
volumes:
 - secret
requiredDropCapabilities:
 - ALL
users:
 - 'system:serviceaccount:*:nginx-ingress'
allowedCapabilities:
  - NET_BIND_SERVICE
$ 
```

## NginxIngess リソース (Ingress Controller) の作成


「Nginx Ingress Controller」というタブをクリックして、「Create NginxIngess」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/f25b823d-35f8-d710-e879-e26f72ae147d.png)

殺風景ですが、とりあえずデフォルトのまま`NginxIngress` というカスタムリソースを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/5f3c6793-9081-721b-6bd5-e8b771ce7d9e.png)

Namespace `nginx-ingress` 内に `nginxingress-sample` という名前の `NginxIngress` リソースが作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/6e6c95d5-2427-e254-7e5b-81bcf84dfa87.png)


`nginxingress-sample-nginx-ingress-controller-788b95fb47-gdt2j` という Pod が新しくできています。名前からもわかるように、この新しくできた Pod(下の行) が `Nginx Ingress Controller` です。

```
$  oc get pods
NAME                                                            READY   STATUS    RESTARTS   AGE
nginx-ingress-operator-controller-manager-85b49c485b-p89sp      2/2     Running   0          9m50s
nginxingress-sample-nginx-ingress-controller-788b95fb47-27fcj   1/1     Running   0          6s
$ 
```

また `Serivce` も確認してみます。`Type=Loadbalancer` の `Service`リソースが作成されています。

```
$ oc get svc
NAME                                                        TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)                      AGE
nginx-ingress-operator-controller-manager-metrics-service   ClusterIP      172.30.208.138   <none>                                                                        8443/TCP                     15m
nginxingress-sample-nginx-ingress-controller                LoadBalancer   172.30.108.226   a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com   80:30936/TCP,443:32344/TCP   5m24s
$ 
```

この `Service` `Type=LoadBalancer` をもう少し詳しく見てみましょう。

```
$ oc describe service nginxingress-sample-nginx-ingress-controller
Name:                     nginxingress-sample-nginx-ingress-controller
Namespace:                nginx-ingress
Labels:                   app.kubernetes.io/instance=nginxingress-sample
                          app.kubernetes.io/managed-by=Helm
                          app.kubernetes.io/name=nginx-ingress
                          app.kubernetes.io/version=3.1.1
                          helm.sh/chart=nginx-ingress-0.17.1
Annotations:              meta.helm.sh/release-name: nginxingress-sample
                          meta.helm.sh/release-namespace: nginx-ingress
Selector:                 app.kubernetes.io/instance=nginxingress-sample,app.kubernetes.io/name=nginx-ingress
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       172.30.108.226
IPs:                      172.30.108.226
LoadBalancer Ingress:     a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  30936/TCP
Endpoints:                10.128.2.71:80
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  32344/TCP
Endpoints:                10.128.2.71:443
Session Affinity:         None
External Traffic Policy:  Local
HealthCheck NodePort:     31957
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  EnsuringLoadBalancer  45m   service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer   45m   service-controller  Ensured load balancer
$ 
```

トラフィックの流れは、以下のようになっているようです。
新規に `ELB (Classic Load Balancer)` が、デプロイされていました。

|ELB(NGINX専用) | | Node Port  | |Pod Network (Ingress Controller Pod) | 
|---|---|---|---|---|
|80/TCP | => | 30396/TCP | => |  10.128.2.71:80/TCP |
|443/TCP | => | 32344/TCP |=> | 10.128.2.71:443/TCP |


この最後の `Pod Network` の IP (Ednpoint) の持ち主を調べてみると、`Nginx` の `Ingress Controller Pod` である事がわかります。

```
$ oc get endpoints
NAME                                                        ENDPOINTS                        AGE
nginx-ingress-operator-controller-manager-metrics-service   10.131.0.121:8443                59m
nginxingress-sample-nginx-ingress-controller                10.128.2.71:443,10.128.2.71:80   49m
$ 
```


ここで `Nginx Ingress Controller` 用に作成されてい `ELB` は、`ROSA` のものとは別に専用に作成されていました。(一番上のハイライトした行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/3bbf6a0b-130b-01e1-d851-488f7ed2848a.png)




# サンプルのアプリケーションのデプロイ

アプリケーションをデプロイするための 新しいプロジェクト (namespace)、 `hello-openshift` を作成します。

```
oc new-project hello-openshift
```

アプリケーションをデプロイします。

```
oc new-app --image=docker.io/openshift/hello-openshift  --labels=test=app
```

`Service` リソースも作成されている事を確認します。

```
$ oc get svc
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
hello-openshift   ClusterIP   172.30.126.194   <none>        8080/TCP,8888/TCP   10s
$ 
```


# Ingress リソースを使ったサンプル・アプリの公開

アプリケーションのプロジェクト(namespace)、`hello-openshift` 内に `Ingress` リソースを作成します。

宛先は、先ほど作成された `hello-openshift` という `Service` リソースです。

OpenShift は、標準で [`Ingress` リソース](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.13/html-single/networking/index#nw-installation-ingress-config-asset_configuring-ingress) も `Route` リソースも処理できるようになっています。

`Nginx Ingress Controller` に作成した `Ingress` リソースを処理させるためには、以下の `annotation` が必要になるようです。

```
  annotations:
    kubernetes.io/ingress.class: "nginx"
```

全体として以下のようになります。

```ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  defaultBackend:
    service:
      name: hello-openshift
      port:
        number: 8080
  rules:
  - host: test.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hello-openshift
            port:
              number: 8080
```



```
oc apply -f ingress.yaml
```

`ingress` リソースが作成されているのを確認します。

```
$ oc get ingress
NAME           CLASS    HOSTS              ADDRESS                                                                       PORTS   AGE
test-ingress   <none>   test.example.com   a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com   80      6s
$ 
```


-H で Host ヘッダーを `test.example.com` に偽装してアクセスしてみます。

```
$  curl -H 'Host:test.example.com' a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com
Hello OpenShift!
$
```

無事アクセスできました。

## 補則 : annotation: kubernetes.io/ingress.class 


`Ingress` リソースに以下の

```
 annotations:
    kubernetes.io/ingress.class: "nginx"
```

`annoation` を付けた場合、`Ingress` リソース作成時に `Nginx Ingress Controller` の `Deployment` のログに、この `annotation` は DEPRECATED なので、無視されると Warning が出ます。

```
W0624 05:10:44.855157       1 controller.go:3915] Using the DEPRECATED annotation 'kubernetes.io/ingress.class'. The 'ingressClassName' field will be ignored.
```

一方で、この `annotation` を付けないで `Ingres` リソースを作成した場合、OpenShift の標準の `Ingress` として処理されるようです。そのため、`Nginx Ingress Controller` に `Ingress` を処理させたい場合は、`annotation` を付ける必要がありそうです。

以下の　`test-ingress` は、annotation 付き、 `test-ingress2` は、annotation 無しで `Ingress` リソースを作成した例です。

```
$ oc get ingress
NAME            CLASS    HOSTS               ADDRESS                                                                       PORTS   AGE
test-ingress    <none>   test.example.com    a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com   80      59m
test-ingress2   <none>   test2.example.com   router-default.apps.f5cluster.m51o.p1.openshiftapps.com                       80      14s
$ 
```

`annotation` の部分と名前以外は同じ `Ingress`リソースですが、下の方は `ROSA` の標準のアプリケーションドメインと紐付いている事がわかります。

# Nginx Ingress Controller Pod を増やす

デフォルトでは1個の `Nginx Ingress Controller` の `Pod` がデプロイされます。

冗長化を図るために、`Nginx Ingress Controller` の `Pod` を2つに増やしてみます。
これは、`NginxIngress` リソースの `replicaCaount` 数を編集する事で増やす事ができました。

```
$ oc get nginxingress 
NAME                  AGE
nginxingress-sample   104m
$
```

`replicaCount` を探します。

```
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: charts.nginx.org/v1alpha1
kind: NginxIngress
metadata:
  annotations:
    operator-sdk/primary-resource: /nginx
    operator-sdk/primary-resource-type: IngressClass.networking.k8s.io
  creationTimestamp: "2023-06-24T04:00:23Z"
  finalizers:
  - helm.sdk.operatorframework.io/uninstall-release
  generation: 2
  name: nginxingress-sample
  namespace: nginx-ingress
  resourceVersion: "1326212"
  uid: 9f9a09ac-b916-4779-9b6b-a86c688e3105
spec:
  controller:
    affinity: {}
    annotations: {}
    appprotect:
      enable: false

<省略>

    pod:
      annotations: {}
      extraLabels: {}
    podDisruptionBudget:
      annotations: {}
      enabled: false
    readyStatus:
      enable: true
      initialDelaySeconds: 0
      port: 8081
    replicaCount: 2  ## ここを 1=> 2 に増やす
    reportIngressStatus:
      annotations: {}
      enable: true
      enableLeaderElection: true
      ingressLink: ""

<省略>

```

`Ingress Controller` の `Pod` が増えました。(下の2行)

```
$ oc get pods -o wide
NAME                                                            READY   STATUS    RESTARTS   AGE    IP             NODE                                              NOMINATED NODE   READINESS GATES
nginx-ingress-operator-controller-manager-85b49c485b-p89sp      2/2     Running   0          132m   10.131.0.121   ip-10-0-225-101.ap-northeast-1.compute.internal   <none>           <none>
nginxingress-sample-nginx-ingress-controller-788b95fb47-27fcj   1/1     Running   0          123m   10.128.2.71    ip-10-0-225-132.ap-northeast-1.compute.internal   <none>           <none>
nginxingress-sample-nginx-ingress-controller-788b95fb47-rx6gx   1/1     Running   0          13s    10.131.0.136   ip-10-0-225-101.ap-northeast-1.compute.internal   <none>           <none>
$ 
```

`Service` `Type=LoadBalancer` の EndPoint も増えている事が確認できます。

```
$ oc get svc
NAME                                                        TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)                      AGE
nginx-ingress-operator-controller-manager-metrics-service   ClusterIP      172.30.208.138   <none>                                                                        8443/TCP                     133m
nginxingress-sample-nginx-ingress-controller                LoadBalancer   172.30.108.226   a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com   80:30936/TCP,443:32344/TCP   123m
$ oc describe svc nginxingress-sample-nginx-ingress-controller
Name:                     nginxingress-sample-nginx-ingress-controller
Namespace:                nginx-ingress
Labels:                   app.kubernetes.io/instance=nginxingress-sample
                          app.kubernetes.io/managed-by=Helm
                          app.kubernetes.io/name=nginx-ingress
                          app.kubernetes.io/version=3.1.1
                          helm.sh/chart=nginx-ingress-0.17.1
Annotations:              meta.helm.sh/release-name: nginxingress-sample
                          meta.helm.sh/release-namespace: nginx-ingress
Selector:                 app.kubernetes.io/instance=nginxingress-sample,app.kubernetes.io/name=nginx-ingress
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       172.30.108.226
IPs:                      172.30.108.226
LoadBalancer Ingress:     a231a916ac00948919cea40dbc043dfd-866973151.ap-northeast-1.elb.amazonaws.com
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  30936/TCP
Endpoints:                10.128.2.71:80,10.131.0.136:80
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  32344/TCP
Endpoints:                10.128.2.71:443,10.131.0.136:443
Session Affinity:         None
External Traffic Policy:  Local
HealthCheck NodePort:     31957
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  EnsuringLoadBalancer  124m  service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer   123m  service-controller  Ensured load balancer
$ 


```


# [おまけ] Routeリソースを使用したサンプル・アプリの公開

今度は同じ `Service` を、OpenShift の `Route` リソースで公開できるか確認してみます。
OpenShift の `Route` リソースは、Ingress の元になった OpenShift 独自の実装です。

アプリケーションのプロジェクト(namespace) `hello-openshift` 内に `Route`リソースを作成します。
`Route` を使って HTTPで `Service` を公開する場合は `oc expose` を使用するのが簡単です。


```
$ oc expose service  hello-openshift 
route.route.openshift.io/hello-openshift exposed
$ 
```

作成された `Route` リソースを確認します。

```
$ oc get route
NAME              HOST/PORT                                                                  PATH   SERVICES          PORT       TERMINATION   WILDCARD
hello-openshift   hello-openshift-hello-openshift.apps.f5cluster.m51o.p1.openshiftapps.com          hello-openshift   8080-tcp                 None
$ 
```

アクセスできる確認してみます。特に Hostname を指定しなければ、ROSA のサービスが標準で持っている Red Hat が取得済みのパブリック・ドメインが、ベースのドメインとしてデフォルトで使用されているのがわかります。

`curl` コマンドでアクセスできる確認してみます。

```
$ curl http://hello-openshift-hello-openshift.apps.f5cluster.m51o.p1.openshiftapps.com
Hello OpenShift!
$ 
```

同じ `Service` に対して、`Ingress`  と `Route` を作成し、それぞれのリソースが公開する URL経由でアクセスできる事が確認できました。