--- 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を使用します。 **目次** ### 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 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 8080/TCP 24m service/kubernetes ClusterIP 10.100.200.1 443/TCP 45d NAME HOSTS ADDRESS PORTS AGE ingress.extensions/hello-pks hello-pks.example.com 80 24m ``` 次のコマンドでアプリケーションにアクセスしてください。 ``` curl http://:80 -v -H "Host: hello-pks.example.com" ``` `type: NodePort`を使用している場合は、次のコマンドを実行してください。 ``` curl http://: -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)カスタムリソースを使用することもできます。
> `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`に次の行を追加してください。 ``` hello-pks.example.com ``` 次のコマンドでアプリケーションにアクセスしてください。 ``` curl https://hello-pks.example.com:443 -k -v ``` `type: NodePort`を使用している場合は、次のコマンドを実行してください。 ``` curl https://hello-pks.example.com: -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でアクセスできるようになりました。