KubernetesにIngressを設定して、Kubernetes内部でホストベースルーティング出来るようにする


はじめに

自宅ラボでは外部のNginxを利用して、Kubernetes内部のアプリに対してホストベースルーティングを設定している。
この場合、Kubernetesへの通信はNodePortを利用して通信しているが、アプリが増えるたびに外部のNginxの設定変更が必要となる。
外部のNginxの変更はメンテナンス性がよろしくないので、Ingressを設定してKubernetes内部でルーティング可能とする。

現状の構成

以下の通り、アプリが増えるたびにNodePortが必要となり、外部Nginxの設定変更が必要となっている状態となっている。 これを、Ingressを用いて解決する。

インターネット

外部Nginx

NodePort:A

NodePort:B

NodePort:C

アプリ1

アプリ2

アプリ3

Ingress導入後の構成

Ingressを導入することで、Kubernetes内部でホストベースルーティングが可能となり、外部Nginxの設定変更が不要となる。 また、NodePortを必要としないため、不要なIPアドレスを公開する必要が無くなる。

インターネット

外部Nginx

Ingress Controller

ClusterIP: A

ClusterIP: B

ClusterIP: C

アプリ1

アプリ2

アプリ3

Ingress導入

Ingressを利用するためにはIngress Controllerの導入が必要です。
https://kubernetes.io/ja/docs/concepts/services-networking/ingress-controllers/
上記のURLを確認すると、AWS GCP Nginxがメインでサポートされているようです。
今回は自宅ラボなので、Nginxを利用します。

Ingress Controllerのインストール

以下を参考にNginx Ingress Controllerをインストールします。

https://github.com/kubernetes/ingress-nginx https://kubernetes.github.io/ingress-nginx/deploy/

今回は自宅ラボでkubeadmを利用してにKubernetes環境を構築済みのため、Bare metal Clusters用の設定を行います。

Terminal window
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.3/deploy/static/provider/baremetal/deploy.yaml

また、Bare metalの場合、NodePortServiceが公開されます。 外部NginxからはNodePortを意識したくないため、CloudのLoadBalancerのようにBare metalでも利用可能となる、MetalLBの導入を今後検討します。

今回はNodePortのまま進めるため、以下コマンドで該当のポートを確認します。 今回はport 80に対しては30901でした。

Terminal window
kubectl get svc -n ingress-nginx
-------
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.103.154.124 <none> 80:30901/TCP,443:30771/TCP 5m11s
ingress-nginx-controller-admission ClusterIP 10.108.187.52 <none> 443/TCP 5m11s

なお、NodePortを固定したい場合はKustomizationを準備し、以下のように設定します

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.3/deploy/static/provider/baremetal/deploy.yaml
patches:
# NodePortを固定
- target:
kind: Service
name: ingress-nginx-controller
patch: |-
- op: add
path: /spec/ports/0/nodePort
value: 30080
- op: add
path: /spec/ports/1/nodePort
value: 30443

これで、HTTPは30080、HTTPSは30443でアクセス可能です。

Terminal window
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller NodePort 10.111.186.246 <none> 80:30080/TCP,443:30443/TCP 20s
service/ingress-nginx-controller-admission ClusterIP 10.96.80.110 <none> 443/TCP 20s

これでingress controllerの導入が完了したので、次はingressの定義です。

Ingressへの変更

これまでは、以下のような形でアプリごとのServiceNodePortで定義していましたが、これをClusterIPで定義し、Ingressを追加します。

service
apiVersion: v1
kind: Service
metadata:
name: redmine-service
spec:
type: NodePort
type: ClusterIP
ports:
- port: 80
targetPort: 3000
nodePort: 30001
selector:
app: redmine

backendに指定するserviceは上記のservicenameと一致させてください。 hostを設定することでkubernetesがホストベースルーティングを実施してくれます。

ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: redmine
namespace: redmine
labels:
app.kubernetes.io/name: redmine
spec:
ingressClassName: nginx
rules:
- host: redmine.home.tessy.dev
http:
paths:
- pathType: Prefix
path: '/'
backend:
service:
name: redmine-service
port:
number: 80

クラスタ外部からのアクセス

これまではクラスタ外部のNginxからアプリごとのNodePortに通信を行なっていましたが、Ingressを導入したことによって、IngressNodePortに集約することが出来ました。 また、同一のドメインであれば、まとめてkubernetesに転送できるため、外部Nginx側のメンテナンス性も上がりました。

nginx.conf
upstream k8s-home {
server k8s:30080; # Ingress Controllerで確認したNodePort
}
server {
listen 80;
server_name *.home.tessy.com; # サブドメインをまとめて転送し、k8sのIngressでルーティングを行う。
location / {
proxy_pass http://k8s-home/;
proxy_set_header Host $host; # これを設定しないとk8sにHost情報が伝達されない。
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

これらの設定を反映することで、Ingress経由でアクセスが可能です。

まとめ

Ingressを導入する前は、これまではアプリごとにNodePortを消費しており、外部からのアクセスもそれに応じた設定を行う必要がありました。
Ingress導入でこれらの問題を解決することが出来、Kubernetes側でホストベースルーティングを行うことでメンテナンス性が向上しました。。

ただし、まだIngress向けのNodePortは指定する必要があるため、MetalLBを利用して今後解消したいと思います。