kubernetesのnginx-ingress、いろいろ調べたり手を動かしてみても、どうしても理解ができなくてずっと悩んでましたが、少し理解が進んだ気がしたので、自分用まとめ。
前提
- type:loadbalancerは使わない。metal-lbも使わない。諸般の事情により公開はnodeportのみ。
- クラスタはVagrantでmaster x 1, worker x 2とします。以下のレポジトリで公開しています。
シンプルなnodeport serviceの場合
kubernetes bootcampのイメージをサンプルに、Deploymentを作ります。podは8080で待ち受けます。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: bootcamp
name: bootcamp
spec:
replicas: 2
selector:
matchLabels:
app: bootcamp
template:
metadata:
labels:
app: bootcamp
spec:
containers:
- image: gcr.io/google-samples/kubernetes-bootcamp:v1
name: bootcamp
ports:
- containerPort: 8080
dnsPolicy: ClusterFirst
restartPolicy: Always
Serviceはこんな感じです。nodeport:31001 -> port:80 -> targetport:8080 という感じでDeploymentのpodまで到達します。
apiVersion: v1
kind: Service
metadata:
name: bootcamp
labels:
app: bootcamp
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 8080
nodePort: 31001
selector:
app: bootcamp
適用します。
$ kubectl apply -f bootcamp-deployment.yaml deployment.apps/bootcamp created $ kubectl apply -f bootcamp-service.yaml service/bootcamp created
確認します
$ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE bootcamp 2/2 2 2 39s $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES bootcamp-7465f56-4zt4d 1/1 Running 0 4m25s 192.168.226.81 worker-1 <none> <none> bootcamp-7465f56-gphwd 1/1 Running 0 4m26s 192.168.133.207 worker-2 <none> <none> $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE bootcamp NodePort 10.101.29.7 <none> 80:31001/TCP 2m13s
実際にアクセスしてみましょう。pod ip/cluster ip/nodeportでどれもアクセスができているのがわかりますね。
$ curl 192.168.226.81:8080 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-4zt4d | v=1 $ curl 192.168.133.207:8080 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-gphwd | v=1 $ curl 10.101.29.7:80 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-gphwd | v=1 $ curl 10.101.29.7:80 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-4zt4d | v=1 $ curl 10.240.0.22:31001 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-gphwd | v=1 $ curl 10.240.0.21:31001 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-4zt4d | v=1
図だとこんな感じかなと思います。

nginx-ingress の場合
ではnginx-ingressを使ってbootcampを公開してみましょう。まず、helmで nginx-ingress をインストールします。
$ helm install nginx-ingress stable/nginx-ingress
まずこの状態で見てみます。deploymentから。
$ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE nginx-ingress-controller 1/1 1 1 93s nginx-ingress-default-backend 1/1 1 1 93s bootcamp 2/2 2 2 60m
nginx-ingress-controllerのpodが80番と443番ポートで待ち受けています。
$ kubectl get deployment nginx-ingress-controller -o yaml
apiVersion: apps/v1
kind: Deployment
・・・
metadata:
・・・
name: nginx-ingress-controller
namespace: default
・・・
name: nginx-ingress-controller
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
・・・
nginx-ingress-default-backend のpodが8080で待ち受けています。
$ kubectl get deployment nginx-ingress-default-backend -o yaml
apiVersion: apps/v1
kind: Deployment
・・・
name: nginx-ingress-default-backend
namespace: default
・・・
ports:
- containerPort: 8080
name: http
protocol: TCP
・・・
podに直接アクセスしてみます。
$ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES bootcamp-7465f56-4zt4d 1/1 Running 0 73m 192.168.226.81 worker-1 <none> <none> bootcamp-7465f56-gphwd 1/1 Running 0 73m 192.168.133.207 worker-2 <none> <none> nginx-ingress-controller-857967b4f-2sz64 1/1 Running 0 14m 192.168.226.82 worker-1 <none> <none> nginx-ingress-default-backend-7c868597f4-fftss 1/1 Running 0 14m 192.168.133.208 worker-2 <none> <none>
全部404ではありますけど、何かしら応答はしてますね。ちょっと置いときましょう。
$ curl http://192.168.226.82/ default backend - 404 $ curl --insecure https://192.168.226.82/ default backend - 404 $ curl 1192.168.133.208:8080 default backend - 404
serviceを見てみます。
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-ingress-controller LoadBalancer 10.103.246.235 <pending> 80:32012/TCP,443:32050/TCP 19m nginx-ingress-default-backend ClusterIP 10.99.30.83 <none> 80/TCP 19m
もう少し詳しく。まずはnginx-ingress-controllerから。
$ kubectl get svc nginx-ingress-controller -o yaml
apiVersion: v1
kind: Service
・・・
name: nginx-ingress-controller
namespace: default
・・・
spec:
clusterIP: 10.103.246.235
externalTrafficPolicy: Cluster
ports:
- name: http
nodePort: 32012
port: 80
protocol: TCP
targetPort: http
- name: https
nodePort: 32050
port: 443
protocol: TCP
targetPort: https
・・・
type: LoadBalancer
冒頭でお伝えしたとおり、type: LoadBalancerは使えない(だからEXTERNAL-IPはpending)なのですが、そこはちょっと置いといて、ポートのところを見ると、
- nodeport:32012 -> port:80 -> targetport:80
- nodeport:32005 -> port:443 -> targetport:443
になっています。つまり、nginx-ingress-controllerのpodに向かって転送しているわけですね。
ということはすなわち、cluster ipでもnodeportでもアクセスができるはずです。
$ curl http://10.103.246.235:80 default backend - 404 $ curl --insecure https://10.103.246.235:443 default backend - 404 $ curl http://10.240.0.21:32012 default backend - 404 $ curl http://10.240.0.22:32012 default backend - 404 $ curl --insecure https://10.240.0.21:32050 default backend - 404 $ curl --insecure https://10.240.0.22:32050 default backend - 404
できましたね。nginx-ingress-default-backendも詳しく見てみましょう。
$ kubectl get svc nginx-ingress-default-backend -o yaml
apiVersion: v1
kind: Service
・・・
name: nginx-ingress-default-backend
namespace: default
spec:
clusterIP: 10.99.30.83
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
・・・
type: ClusterIP
こちらはcluster ipなので外部からのアクセスはできません。cluster ipに対してアクセスすると応答します。
$ curl 10.99.30.83:80 default backend - 404
これがnginx-ingressをインストールした直後です。nginx-ingressを使うと関係者が多くなるのでちょっとわかりにくくなるのですが、これだけ見てるとtype: nodeportで公開しているのとそんなに変わらないですよね。
で、今回type: loadbalancerは使えないので、nginx-ingress-controllerのserviceはtype: nodeportに変更します。また80/443それぞれのnodeportも固定します。
$ kubectl edit service nginx-ingress-controller
変更点だけ。
・・・
- name: http
nodePort: 32001
・・・
- name: https
nodePort: 32002
・・・
type: NodePort
確認します。
$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-ingress-controller NodePort 10.103.246.235 <none> 80:32001/TCP,443:32002/TCP 49m nginx-ingress-default-backend ClusterIP 10.99.30.83 <none> 80/TCP 49m
変わりました。
ではingressを追加します。こんな感じにしてみました。時間の都合上httpだけ、default-backendは後で調べます。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
name: bootcamp-nginx-ingress
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: bootcamp
servicePort: 80
path: /
serviceNameのところはserviceのmetadata.nameと合わせる必要がありますので注意してください。
ではアクセスしてみましょう。架空のホスト名なのでhostヘッダをつけています。
$ curl -H 'host: example.com' http://10.240.0.21:32001 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-gphwd | v=1 $ curl -H 'host: example.com' http://10.240.0.22:32001 Hello Kubernetes bootcamp! | Running on: bootcamp-7465f56-4zt4d | v=1
うまくいきました!アクセスログも見てみましょう。
$ kubectl logs nginx-ingress-controller-857967b4f-2sz64 10.0.2.15 - - [18/May/2020:17:40:08 +0000] "GET / HTTP/1.1" 200 81 "-" "curl/7.58.0" 75 0.008 [default-bootcamp-80] [] 192.168.226.81:8080 92 0.004 200 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 192.168.133.192 - - [18/May/2020:17:40:12 +0000] "GET / HTTP/1.1" 200 92 "-" "curl/7.58.0" 75 0.003 [default-bootcamp-80] [] 192.168.133.207:8080 92 0.004 200 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
アクセス元IPアドレスのところがおかしいのはvagrantだからですね。ちゃんとアクセスが来てるので良しとします。図にまとめるとこんな感じになるのかな。

serviceがmanfiestだけで動くのに対し、nginx-ingressはservice/pod/ingressといろいろなものが強調して動くことになるので、非常にわかりにくいですね。しかもtype: LoadBalancerならもちょっとシンプルなはずなんですが、そうじゃないので余計にわかりにくい・・・
とりあえずなんとなくは理解できたんじゃないかと。