---
# System prepended metadata

title: Kubernetes Ingress
tags: [' Kubernetes', Infrastructure]

---

---
lang: ja-jp
tags: Infrastructure, Kubernetes
title: Kubernetes Ingress
---
# Kubernetes Ingress
- [KubernetesにIngressを導入する方法](https://qiita.com/Hirata-Masato/items/8e6b4536b6f1b23c5270)
- [learnk8s: A visual guide on troubleshooting Kubernetes deployments](https://learnk8s.io/troubleshooting-deployments)
- [Ingress TLS](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls)
- [Kubernetes docs: Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)

## Overview
アプリケーションを構成するリソースは（基本的に）以下の通り：

1. **Pods**: 同一のIPアドレスが割り当てられた１つ以上のコンテナから構成される基本単位
2. **ReplicaSet**: Podの複製を作成し、サービスを構成するアプリケーションの個数を維持
3. **Deployment**: ReplicaSetを管理し、ローリングアップデートといったデプロイの機能を提供

クラスタに展開するDeploymentを、外部ネットワークに公開するには、基本的には**Service**リソースを利用することになる。`type=LoadBalancer`のServiceを作成することで、外部からのアクセスを受け付けることが可能になる。

しかし、Serviceリソースでは実現できない機能がいくつか存在する:

- IPアドレスはServiceごとに生成されるため、アクセスポイントが変わりうる
- HTTPSへのリダイレクトを実現できない
- 同一Pod内に存在する（i.e. ローカルホストが同じ）コンテナに別々のルーティングを割り当てる場合に、Nginxのようなリバースプロキシを追加するひつようがある

このような問題を解決するためのリソースとして、アプリケーションレイヤのロードバランスとして**Ingress**リソースが存在する。

## Infra as Code: Service, Deployment
- Serviceリソースの種類を`type=NodePort`にする
- Deploymentの`spec.template.spec.containers.ports[]`にServiceの`spec.ports.targetPort`に一致するポート番号を入れる
- Deploymentの`spec.template.metadata.labels`のラベル付けを正しく行う

```yaml=
apiVersion: v1
kind: Service
metadata:
  name: my-api-service
  namespace: my-namespace
  labels:
    app: my-api
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080  # コンテナのポート
    protocol: TCP
  selector:
    app: my-api-pod  # どのラベルが割り当てられたPodと接続するかのセレクタ
---
apiVersion: apps/v1beta
kind: Deployment
metadata:
  name: my-api-deployment
  namespace: my-namespace
spec:
  replicas: 1
  revisionHistoryLimit: 3
  template:
    metadata:
      namespace: my-namespace
      labels:
        app: my-api-pod  # このラベルをServiceリソースが探す
        version: latest
    spec:
      containers:
      - name: my-api
        image: gcr.io/moriaki3193/my-api:latest
        ports:
        - containerPort: 8080
        # IngressとServiceの連携まで時間がかかるため
        # ヘルスチェックの設定をうまくやるには以下を参照
        # https://qiita.com/Hirata-Masato/items/8e6b4536b6f1b23c5270#%E8%BF%BD%E8%A8%98201823
```

## Infra as Code: Ingress
- `spec.rules[].http.paths[].<pathname>.serviceName`にServiceに割り当てた`metadata.name`の値を設定する

```yaml=
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  namespace: my-namespace
  annotations:  # optional
    kubernetes.io/ingress.global-static-ip-name: my-ipaddress
spec:
  rules:
  - host: myapi.example.com
    http:
      paths:
      - <pathname>:
        serviceName: my-api
        servicePort: 8080  # Serviceのポートを指定
```

**optional**な箇所については、Ingressを削除し、再設定した際にIPアドレスが変わりうるため、固定するようにするための設定となっている。

## Terminology
- **Node**: Kubernetesクラスタのワーカマシンのこと。
- **Cluster**: Kubernetesにより管理される、コンテナ化されたアプリケーションを実行するNodeの集合。クラスタに属するノードは外部ネットワークには接続されていない。
- **Edge Router**: クラスタのファイアウォールのポリシーを強制するルータのこと。クラウドベンダーにより管理されるゲートウェイであったり、物理的なハードウェアであったりする。
- **Cluster network**: クラスタ内部のやりとりを執り行う論理的あるいは物理的なリンクの集合のこと。詳細は[K8s networking model](https://kubernetes.io/docs/concepts/cluster-administration/networking/)を参考のこと。
- **Service**: ラベルセレクタを利用してPodの集合を識別するK8sのサービスのこと。特に他のリソースで指定されない限り、サービスはクラスタネットワーク内部でのみ接続可能な仮想IPアドレスを持つ。

## What is Ingress?
クラスタ外部からのHTTPやHTTPSによる通信をクラスタ内部のサービスに接続する役割を担うリソースのこと。**トラフィックルーティングはIngressリソースにより定義されるルールによってコントロールされる**。

```
    internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]
```

IngressはServiceに外部からアクセス可能なURLを設定したり、トラフィックをロードバランシングしたり、SSL/TLS通信のエンドポイントとなったりするように設定できる。Ingress ControllerがIngressの実現に責務を負っていて、大抵の場合はロードバランサと合わせて利用される。

Ingressは任意のポートやプロトコルを公開することはない。HTTPとHTTPS以外の場合にインターネットにサービスを公開する際には、Serviceリソースがその責任を負う。`type=NodePort`または`type=LoadBalancer`が該当する。

## Prerequisites
Ingressリソースを立ち上げるには、`ingress controller`が必要になる。Ingressリソースを作成するだけでは、クラスタにはなんら影響はない。Ingress Controllerについては[こちら](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers)を参考にする。

`ingress-nginx`といったようなIngress Controllerをデプロイする必要があるかもしれない。種類は数多くあるので、用途にあったものを選択すればよい。

## The Ingress Resource
最低限のIngressリソースの具体例は次の通り：

```yaml=
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
    paths:
    - path: /testpath
      backend:
        serviceName: test
        servicePort: 80
```

### Service type: NodePort
> NodePortは、全てのKubernetes Nodeの`ipaddr:port`で受信したトラフィックをコンテナに転送する形で、外部疎通性を確立します

内部的には、マシンワーカとしてのノードに割り当てられた仮想的なソケットを指定して、Pod内のコンテナに通信を引き渡す役割を担っている。

### ingress-nginx
[kuberntes/ingress-nginx](https://github.com/kubernetes/ingress-nginx/blob/master/docs/examples/rewrite/README.md)に詳細が記載されている。

#### ingress.class annotations
- `nginx.ingress.kubernetes.io/rewrite-target`
  - トラフィックがリダイレクトされなければならないターゲットURIを指定できる
- `nginx.ingress.kubernetes.io/app-root`
  - '/'コンテキストである場合にコントローラがリダイレクトしなければならないApplication Rootを定義できる

#### Rewrite Target
```yaml=
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: rewrite
  namespace: default
spec:
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /something(/|$)(.*)
```

このIngressリソースの定義では、`(.*)`文字列でキャプチャされる文字がすべて`$2`プレースホルダに代入される。

このような設定を行うことで、次のような結果が得られる。
- `rewrite.bar.com/something` → `rewrite.bar.com/`
- `rewrite.bar.com/something/` → `rewrite.bar.com/`
- `rewrite.bar.com/something/new` → `rewrite.bar.com/new`

#### App Root
`spec.rules[].http.paths[].path`で指定されたパスのパターンに対してリダイレクトをかける。

```yaml=
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/app-root: /app1
  name: approot
  namespace: default
spec:
  rules:
  - host: approot.bar.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /
```

- `http://approot.bar.com/` → 302 Moved Temporarily
  - `http://approot.bar.com/app1`になる


### メモ
```
# / にアクセスされたら3000番ポートに
# /api にアクセスされたら、apiの文字列を削除して5000番ポートに
# :80/api/estimate → :5000/estimate ということ

# フロントエンドのアプリケーションの静的ファイルがすべてルーティングされていないので、Ingressを修正する
# Flaskの方のエンドポイントを修正する
```
