Oracle Container Engine for Kubernetes(OKE)でNginx Ingress Controllerを利用する

# はじめに
Oracle Cloud上でKubernetesクラスタを構成するサービスOKE上で、Nginx Ingress Controllerを使用し、
Ingressを介してKubernetesのクラスタ内のサービスへアクセスできるようになるまでにした事を書きます。

# 前提条件
ローカル環境でOKEへkubectlによりアクセスできる状態である事が前提です。

OKEの始め方のチュートリアルや記事については以下が参考になります。

- [公式のチュートリアル](https://www.oracle.com/webfolder/technetwork/tutorials/obe/oci/oke-full/index.html)
- [Qiita記事](https://qiita.com/hhiroshell/items/5e812a4cccbdbb16a3fb)

# Nginx Ingress Controllerの設定
[nginxinc/kubernetes-ingressのinstallation.md](https://github.com/nginxinc/kubernetes-ingress/blob/master/docs/installation.md)に従って作業を進めました。

### 用意
[nginxinc/kubernetes-ingress](https://github.com/nginxinc/kubernetes-ingress)をgithubよりcloneします。
このレポジトリのdeployment以下のファイルを使用しました。

```shell
$ git clone https://github.com/nginxinc/kubernetes-ingress
$ cd kubernetes-ingress/deployments
```

### Nginx Ingress Controllerのデプロイ作業

1. Kubernetes上にNginx Ingress Controller用のNamespaceとServiceAccountを追加します。

```shell
$ kubectl apply -f common/ns-and-sa.yaml
```

common/ns-and-sa.yamlの中身は以下のようになっています。

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: nginx-ingress 
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress 
  namespace: nginx-ingress
```

2.　Kubernetes上にtls用のSecretを作成します。

```shell
$ kubectl apply -f common/default-server-secret.yaml
```

common/default-server-secret.yaml

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: default-server-secret
  namespace: nginx-ingress
type: Opaque
data:
  tls.crt: ???
  tls.key: ???
```

KubernetesのSecretについては以下のURLが参考になります。
https://kubernetes.io/docs/concepts/configuration/secret/

3.　Kubernetes上にNginx Ingress Controller用のConfigMapを作成します。

```shell
$ kubectl apply -f common/nginx-config.yaml
```

common/nginx-config.yaml

```yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
  namespace: nginx-ingress
data:
```

dataセクションに書ける内容については、以下のURLを参照してください。
https://github.com/nginxinc/kubernetes-ingress/blob/master/docs/configmap-and-annotations.md

4.　RBACの設定を行います。
ここでは、適切な権限を持ったOracle Cloud InfrastructureのユーザとKubernetes上のClusterRoleBindingを紐付ける必要があるようです。
UserはPolicyで以下の権限を持たせたgroupに割り当てられているものを使用しました。

```
allow group <group名> to manage cluster-family in tenancy
allow group <group名> to inspect vcns in tenancy
allow group <group名> to inspect subnets in tenancy
```

実行するコマンドは以下の様になります。

```shell
kubectl create clusterrolebinding nginx-ingress --clusterrole=cluster-admin --user=<user OCID> --serviceaccount=nginx-ingress:nginx-ingress
```

参考ドキュメント:
https://docs.cloud.oracle.com/iaas/Content/ContEng/Concepts/contengaboutaccesscontrol.htm

5.　ClusterRoleを追加します。
以下のファイルを作成します。

clusterrole.yaml

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: nginx-ingress
rules:
- apiGroups:
  - ""
  resources:
  - services
  - endpoints
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - get
  - list
  - watch
  - update
  - create
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - list
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - extensions
  resources:
  - ingresses
  verbs:
  - list
  - watch
  - get
- apiGroups:
  - "extensions"
  resources:
  - ingresses/status
  verbs:
  - update
```

次のコマンドを実行します。

```shell
$ kubectl apply -f clusterrole.yaml
```

6.　Kubernetes上にNginx Ingress Controllerをデプロイします。

```shell
$ kubectl apply -f deployment/nginx-ingress.yaml
```

deployment/nginx-ingress.yaml

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress
  namespace: nginx-ingress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-ingress
  template:
    metadata:
      labels:
        app: nginx-ingress
    spec:
      serviceAccountName: nginx-ingress
      containers:
      - image: nginx/nginx-ingress:edge
        imagePullPolicy: Always
        name: nginx-ingress
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        args:
          - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
          - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
         #- -v=3 # Enables extensive logging. Useful for trooublshooting.
         #- -report-ingress-status
         #- -external-service=nginx-ingress
         #- -enable-leader-election
```

7.　デプロイができたかを確認します。
以下のコマンドを実行してPodの状態がRunningならば大丈夫です。

```shell
$ kubectl get pods --namespace=nginx-ingress
```

8.　次のコマンドを実行します。

```shell
$ kubectl apply -f service/loadbalancer.yaml
```

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress
  namespace: nginx-ingress
spec:
  externalTrafficPolicy: Local
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    targetPort: 443
    protocol: TCP
    name: https
  selector:
    app: nginx-ingress
```

この時点で、Oracle Cloud Infrastructure上にPublic IPが自動的に取得されるようです。

```shell
$ kubectl get service -n nginx-ingress
NAME            TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE
nginx-ingress   LoadBalancer   10.96.6.156   <Public IP>    80:30024/TCP,443:32241/TCP   4h
```

以上でNginx Ingress Controllerのデプロイができました。

### サービスのデプロイ
実際にIngressとサービスをデプロイする手順です。

1.　例として次のファイルでサービスをデプロイします。

hello.yaml

```yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello-k8s-deployment
spec:
  selector:
    matchLabels:
      app: helloworld
  replicas: 1
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: helloworld
        image: karthequian/helloworld:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hello-k8s-service
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: helloworld
```

```shell
$ kubectl apply -f hello.yaml
```

2.　次のファイルを使用してIngressを作り、hello-k8s-serviceと紐付けます。

ingress.yaml

```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: helloworld-ingress
spec:
  rules:
    - host: hello.example.com
      http:
        paths:
        - path: /
          backend:
            serviceName: hello-k8s-service
            servicePort: 80
```

```shell
$ kubectl apply -f ingress.yaml
```

```shell
$ kubectl get ingress
NAME                 HOSTS               ADDRESS   PORTS     AGE
helloworld-ingress   hello.example.com             80        3h
```

この時点で、LoadBalancer作成じに取得されたOracle Cloud InfrastructureのPublic IPに
hello.example.comのドメイン名でアクセスすれば、HelloWorldのPodの動作を確認できます。

#### curlコマンドでの確認

```shell
$ curl --resolve hello.example.com:80:<Public IP> http://hello.example.com 
<html>
......
</html>
```

#### ブラウザからの確認

まずhello.example.comとPublic IPを紐付けるDNSサーバを用意して、
そこを参照するようにしてください。

macOSの場合は[ここ](https://qiita.com/superbrothers/items/13d8ce012ef23e22cb74#ingress-%E3%81%AE%E3%83%9B%E3%82%B9%E3%83%88%E5%90%8D%E3%82%92%E5%90%8D%E5%89%8D%E8%A7%A3%E6%B1%BA%E3%81%99%E3%82%8B)を参考にしてdnsmasqを設定できました。
dnsmasqを設定したら、システム環境設定->ネットワーク->詳細->DNSで、
参照するDNSサーバの表の一番上に127.0.0.1を指定してください。

DNSサーバの設定ができれば、ブラウザからPublic IPにアクセスしてページを見る事ができます。

今回は以上です。