---
title: Kubernetesハンズオン - 7. IngressによるL7ロードバランシング
tags: ["Kubernetes Handson", "Kubernetes", "PKS"]
categories: ["Dev", "CaaS", "Kubernetes"]
date: 2019-09-23T16:20:35Z
updated: 1970-01-01T00:00:00Z
---

これまでにServiceによるロードバランシングは見てきましたが、Serviceは現時点ではL4 LoadBalancer相当であり、
HTTPヘッダーなどは考慮されません。TLS TerminationもServiceレベルでは行えないため、Podまたは外部LoadBalancer単位で行う必要があります。

また外部に公開するアプリケーションに対して、`type=LoadBalancer`なServiceを一つ一つ用意すると、Serviceの分だけLoadBalancerのコストがかかります。

![image](https://user-images.githubusercontent.com/106908/39979824-9dd4f640-5783-11e8-98cd-cff838861c05.png)

このような課題に対して、[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)はService前のL7レベル LoadBalancer相当の機能を提供します。

IngressはServiceに対するHost名ベースまたはパスベースのルーティングを提供します。外部LoadBalancerはIngress (Controller)に対してルーティングすれば複数のLoadBalancerを用意する必要はありません。
またTLS Terminationを行うこともできます。

![image](https://user-images.githubusercontent.com/106908/39988628-d408f224-57a2-11e8-91a9-b150232b7d0d.png)

`Ingress`はKubernetesのリソースであり、このリソースを以ってServiceへのルーティングを定義できます。
`Ingree`リソースを使用するにはIngress Controllerを用意することが必要です。Ingress ControllerはPodとしてデプロイ可能です。

Ingress Controller自体はAPI仕様であり、Ingress Controllerの実装としては

* [Nginx](https://kubernetes.github.io/ingress-nginx/)
* [Contour](https://github.com/projectcontour/contour)
* [GCE](https://github.com/kubernetes/ingress-gce)
* [ALB](https://github.com/kubernetes-sigs/aws-alb-ingress-controller)
* [Istio](https://istio.io/docs/tasks/traffic-management/ingress.html)

などが有名です。

本ドキュメントではIaaSに依らずに使用できるContourを使用します。

**目次**
<!-- toc -->

### Contourのインストール

次のコマンドを実行してください。

```
kubectl apply -f https://raw.githubusercontent.com/projectcontour/contour/release-0.15/examples/render/deployment-rbac.yaml
```

デフォルトで`type: LoadBalancer`なServiceが作成されます。`type: NodePort`を使用したい場合は次のコマンドを実行してください。

```
curl https://raw.githubusercontent.com/projectcontour/contour/release-0.15/examples/render/deployment-rbac.yaml | sed 's/LoadBalancer/NodePort/' | kubectl apply -f -
```

```
kubectl get -n heptio-contour all
```

出力結果
```
NAME                           READY   STATUS    RESTARTS   AGE
pod/contour-596f4bf6c4-48fs7   2/2     Running   0          13m
pod/contour-596f4bf6c4-ztmrc   2/2     Running   0          13m

NAME              TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
service/contour   NodePort   10.100.200.168   <none>        80:31005/TCP,443:31353/TCP   13m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/contour   2/2     2            2           13m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/contour-596f4bf6c4   2         2         2       13m
```

### Ingressリソースの作成

`hello-pks`アプリのデプロイとそのアプリに`hello-pks.example.com`でアクセスするための`Ingress`リソースを作成します。

```yaml
kind: Service
apiVersion: v1
metadata:
  name: hello-pks
spec:
  type: ClusterIP
  selector:
    app: hello-pks
  ports:
  - protocol: TCP
    port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-pks
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.2
        name: hello-pks
        ports:
        - containerPort: 8080
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: NODE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-pks
  annotations:
    kubernetes.io/ingress.class: contour
spec:
  rules:
  - host: hello-pks.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-pks
          servicePort: 8080
```

ここでは`type: ClusterIP`で`Service`リソースを作成するので、このままではクラスタ外から直接アクセスできません。
`Ingress`リソースを作成し、Hostヘッダが`hello-pks.example.com`の場合に`hello-pks`サービスにルーティングされるようにします。

`kubernetes.io/ingress.class`アノテーションはIngress Controllerが複数インストールされている場合は必須です。一つしかインストールされていない場合は不要です。



次のコマンドで作成されたリソースを確認してください。
```
kubectl get pod,service,ingress
```
出力結果
```
NAME                             READY   STATUS    RESTARTS   AGE
pod/hello-pks-7f897d97b9-g8tlp   1/1     Running   0          24m
pod/hello-pks-7f897d97b9-tbr7t   1/1     Running   0          24m

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/hello-pks    ClusterIP   10.100.200.179   <none>        8080/TCP   24m
service/kubernetes   ClusterIP   10.100.200.1     <none>        443/TCP    45d

NAME                           HOSTS                   ADDRESS   PORTS   AGE
ingress.extensions/hello-pks   hello-pks.example.com             80      24m
```

次のコマンドでアプリケーションにアクセスしてください。

```
curl http://<contour ServiceのExternal-IP>:80  -v -H "Host: hello-pks.example.com"
```

`type: NodePort`を使用している場合は、次のコマンドを実行してください。	

```
curl http://<Worker NodeのIP>:<contour Serviceの80ポートに対するnodePort>  -v -H "Host: hello-pks.example.com"
```

出力結果
```
> GET / HTTP/1.1
> Host: hello-pks.example.com
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< content-type: application/json;charset=UTF-8
< x-envoy-upstream-service-time: 7
< date: Sun, 29 Sep 2019 16:50:48 GMT
< server: envoy
< transfer-encoding: chunked
< 
{
  "node" : {
    "NODE_IP" : "192.168.3.21",
    "NODE_NAME" : "093a9c8b-0827-41b2-bc12-e129f2c33833"
  },
  "pod" : {
    "POD_IP" : "10.200.33.56",
    "POD_NAME" : "hello-pks-7f897d97b9-g8tlp",
    "POD_NAMESPACE" : "default"
  },
  "container" : { }
}
```

> Contourの場合は`Ingress`リソースの代わりに[`IngressRoute`](https://github.com/projectcontour/contour/blob/master/docs/ingressroute.md)カスタムリソースを使用することもできます。<br>
> `IngressRoute`リソースはContour独自ですが、`Ingress`よりも高度な設定ができます。

### TLS終端の有効化

次にTLSを有効にします。

[5. ConfigMap/Secretで設定情報の管理](/entries/555)で作成したように`gen_ssl_certs.sh`でTLS証明書を作成します。

`gen_ssl_certs.sh`をダウンロードしていない場合は次のコマンドで取得してください。
```
wget https://raw.githubusercontent.com/aws-quickstart/quickstart-pivotal-cloudfoundry/master/scripts/gen_ssl_certs.sh
chmod +x gen_ssl_certs.sh 
```

`*.example.com`に対する証明書を作成します。

```
./gen_ssl_certs.sh example.com
```

次のコマンドでこの証明書に対する`Secret`リソースを作成するYAMLを生成します。

```
kubectl create secret tls hello-pks-tls \
  --cert=example.com.crt \
  --key=example.com.key \
  --dry-run -o yaml > hello-pks-tls.yml
```

`hello-pks.yml`を次のように変更してください。

```
kind: Service
apiVersion: v1
metadata:
  name: hello-pks
spec:
  type: ClusterIP
  selector:
    app: hello-pks
  ports:
  - protocol: TCP
    port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-pks
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.2
        name: hello-pks
        ports:
        - containerPort: 8080
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: NODE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-pks
  annotations:
    kubernetes.io/ingress.class: contour
spec:
  rules:
  - host: hello-pks.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-pks
          servicePort: 8080
  tls: # ここを追加
  - hosts:
    - hello-pks.example.com
    secretName: hello-pks-tls
```


次のコマンドで変更を反映します。
```
kubectl apply -f hello-pks.yml -f hello-pks-tls.yml
```



次のコマンドで`Ingress`リソースを確認します。	
```
kubectl get ingress
```

`PORTS`列に`443`が追加されたことを確認してください。
```
NAME        HOSTS                   ADDRESS   PORTS     AGE
hello-pks   hello-pks.example.com             80, 443   33m
```


`/etc/hosts`に次の行を追加してください。

```
<Worker NodeのIPまたはExternal IP>	hello-pks.example.com
```

次のコマンドでアプリケーションにアクセスしてください。

```
curl https://hello-pks.example.com:443 -k  -v 
```

`type: NodePort`を使用している場合は、次のコマンドを実行してください。	

```
curl https://hello-pks.example.com:<contour Serviceの443ポートに対するnodePort> -k  -v 
```

実行結果
```
> GET / HTTP/2
> Host: hello-pks.example.com:31353
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/2 200 
< content-type: application/json;charset=UTF-8
< x-envoy-upstream-service-time: 10
< date: Sun, 29 Sep 2019 17:12:06 GMT
< server: envoy
< 
{
  "node" : {
    "NODE_IP" : "192.168.3.21",
    "NODE_NAME" : "093a9c8b-0827-41b2-bc12-e129f2c33833"
  },
  "pod" : {
    "POD_IP" : "10.200.33.56",
    "POD_NAME" : "hello-pks-7f897d97b9-g8tlp",
    "POD_NAMESPACE" : "default"
  },
  "container" : { }
}
```

TLS終端をIngress Controllerで行うことで、アプリケーション側では特にTLSの設定をすることなく、HTTPSでアクセスできるようになりました。
