やったことのまとめ
自分は普段マネージドKubernetes
ばかり使っていて、当たり前のようにIngress
を使ってアプリをクラスタ外に公開している。
でもよくよく考えるとIngress
が使えるのはクラスタの管理者がIngress Controller
を用意してくれているからで、
今までそれ自体を自分で設定したことはなかった。
仕事でこれだけKubernetes
触ってるのにIngress Controller
を設定したことがないのは流石に恥ずかしいと思い、
kubeadmで作った自前のクラスタにNGINX Ingress Controllerを設定して、サービスがクラスタ外に公開できるところまで試した。
kubeadm
で作った自前クラスタにingress-nginx(NGINX Ingress Controller)
を入れてNodePort Service
で動かしたIngress Class
の設定をしたMetalLB
を使ってingress-nginx
をLoadBalancer Service
で動かした
つかうもの
- Ubuntu 20.04.3 LTS (GNU/Linux 5.11.0-1020-gcp x86_64)
- containerd 1.4.11
- Calico v3.20.2
- Kubernetes v1.22.2
- NGINX Ingress controller v1.0.4
- MetalLB 0.10.3
やったこと
- 自前クラスタの作成
- ingress-nginxのインストール(NodePort)
- 動作確認(NodePort)
- MetalLBのインストール
- ingress-nginxのインストール(with MetalLB)
- 動作確認(with MetalLB)
自前クラスタの作成
前回作ったクラスタをそのまま使う。
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kubernetes-master Ready control-plane,master 5m56s v1.22.2 10.240.0.2 <none> Ubuntu 20.04.3 LTS 5.11.0-1020-gcp containerd://1.4.11
kubernetes-worker Ready <none> 3m30s v1.22.2 10.240.0.3 <none> Ubuntu 20.04.3 LTS 5.11.0-1020-gcp containerd://1.4.11
$ kubectl get deploy -A
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
calico-apiserver calico-apiserver 1/1 1 1 5m15s
calico-system calico-kube-controllers 1/1 1 1 6m16s
calico-system calico-typha 2/2 2 2 6m17s
kube-system coredns 2/2 2 2 7m27s
tigera-operator tigera-operator 1/1 1 1 6m24s
$ kubectl get ds -A
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-system calico-node 2 2 2 2 2 kubernetes.io/os=linux 6m36s
kube-system kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 7m46s
$ kubectl get service -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
calico-apiserver calico-api ClusterIP 10.96.0.75 <none> 443/TCP 6m2s
calico-system calico-kube-controllers-metrics ClusterIP 10.97.190.112 <none> 9094/TCP 6m27s
calico-system calico-typha ClusterIP 10.97.47.173 <none> 5473/TCP 7m4s
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8m16s
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 8m14s
$ kubectl get ingress -A
No resources found
ingress-nginxのインストール(NodePort)
Ingress Controllerにはいくつか種類があるが、Kubernetes
公式でサポートされている中ではNGINX Ingress Controllerが自前クラスタに使えそう。
公式の手順に従ってインストールしてみる。
# https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/baremetal/deploy.yaml
今回の手順だとクラスタ外のロードバランサーを用意していないので、Ingress Controller
自体がNodePort Service
でクラスタ外からのリクエストを受けてIngress
のルールに則ったトラフィックの振り分けを行うらしい。
# Ingress ControllerのDeploymentが立っている
$ kubectl -n ingress-nginx get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
ingress-nginx-controller 1/1 1 1 7m34s
$ kubectl -n ingress-nginx get pods --field-selector=status.phase=Running -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-controller-644555766d-4pwm2 1/1 Running 0 7m8s 192.168.231.195 kubernetes-worker <none> <none>
$ kubectl -n ingress-nginx exec -it ingress-nginx-controller-644555766d-4pwm2 -- /nginx-ingress-controller --version
-------------------------------------------------------------------------------
NGINX Ingress controller
Release: v1.0.4
Build: 9b78b6c197b48116243922170875af4aa752ee59
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.19.9
-------------------------------------------------------------------------------
# Ingress Controller自体がNodePort Serviceで公開されている
$ kubectl -n ingress-nginx get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.104.81.185 <none> 80:31862/TCP,443:30703/TCP 8m29s
ingress-nginx-controller-admission ClusterIP 10.98.56.107 <none> 443/TCP 8m29s
$ kubectl -n ingress-nginx describe service ingress-nginx-controller
Name: ingress-nginx-controller
Namespace: ingress-nginx
Labels: app.kubernetes.io/component=controller
app.kubernetes.io/instance=ingress-nginx
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/version=1.0.4
helm.sh/chart=ingress-nginx-4.0.6
Annotations: <none>
Selector: app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.104.81.185
IPs: 10.104.81.185
Port: http 80/TCP
TargetPort: http/TCP
NodePort: http 31862/TCP
Endpoints: 192.168.231.195:80
Port: https 443/TCP
TargetPort: https/TCP
NodePort: https 30703/TCP
Endpoints: 192.168.231.195:443
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
動作確認(NodePort)
Ingress Controller
が用意できたので、適当なPod
とIngress
を用意して動作を確認する。
手順はKubernetes公式のものを一部改変して試した。
# 動作確認用Deployment, Service, Ingressの作成
kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0 --port=8080
kubectl expose deployment web --port=8080
kubectl create ingress example-ingress --rule="hello-world.info/*=web:8080"
作成したリソースはこんな感じ。
$ kubectl get pods -l app=web -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-7c884bf475-tt9g6 1/1 Running 0 8m21s 192.168.231.196 kubernetes-worker <none> <none>
$ kubectl get service web -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
web ClusterIP 10.99.167.187 <none> 8080/TCP 6m7s app=web
# ちょっとあやしい...?
$ kubectl describe ingress example-ingress
Name: example-ingress
Namespace: default
Address:
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
Host Path Backends
---- ---- --------
hello-world.info
/ web:8080 (192.168.231.196:8080)
Annotations: <none>
Events: <none>
試しにmaster node
からクラスタを介さずIngress
経由で疎通できるか試してみる。
インストール後に確認したようにIngress Controller
自体がNodePort Service
で公開されているので、そこにリクエストを飛ばす。
# <nodeのIP>:<NodePort>にリクエストしたけどダメ...
$ curl -H "Host: hello-world.info" 10.240.0.3:31862/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
は?失敗したんだが😭
どうやら作ったIngress
にIngress Classの指定がないのでIngress Controller
に認識されてないっぽい。
(1クラスタに複数のIngress Controller
が存在することがあるので、Ingress Class
が指定されていないといろいろ問題になる)
対応方法としては
Ingress
作成時にspec.ingressClassName
フィールドを指定するIngress Class
にアノテーションをつけてデフォルト設定にする のどちらかだと思う。
今回はデフォルトIngress Class
を設定する方法を試してみる。
# Ingress Classにアノテーションをつけてデフォルト設定にする
kubectl annotate ingressclass nginx ingressclass.kubernetes.io/is-default-class=true
# ingressを作り直す
kubectl delete ingress example-ingress
kubectl create ingress example-ingress --rule="hello-world.info/*=web:8080"
# (optional)IngressClassのデフォルト設定をしない場合は--classでIngressClassを指定する
kubectl create ingress example-ingress --class=nginx --rule="hello-world.info/*=web:8080"
Ingress Class
の設定をした上で再度挑戦してみる。
# アノテーションがついている
$ kubectl describe ingressclass nginx
Name: nginx
Labels: app.kubernetes.io/component=controller
app.kubernetes.io/instance=ingress-nginx
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/version=1.0.4
helm.sh/chart=ingress-nginx-4.0.6
Annotations: ingressclass.kubernetes.io/is-default-class: true
Controller: k8s.io/ingress-nginx
Events: <none>
# さっきと違ってIngress Controllerに認識されてそう
$ kubectl describe ingress example-ingress
Name: example-ingress
Namespace: default
Address: 10.240.0.3
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
Host Path Backends
---- ---- --------
hello-world.info
/ web:8080 (192.168.231.196:8080)
Annotations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 2m8s (x2 over 2m12s) nginx-ingress-controller Scheduled for sync
# 今度は疎通できる
$ curl -H "Host: hello-world.info" 10.240.0.3:31862/
Hello, world!
Version: 1.0.0
Hostname: web-7c884bf475-tt9g6
今度はちゃんとIngress
経由で通信できた。🎉
やったぜ。
MetalLBのインストール
とはいえやっぱりクラスタ外からアクセスするときにデカいポートをいちいち指定するのはしんどい。
次はNodePort Service
ではなくLoadBalancer Service
を使って普通のポートでトラフィックを受けられるようにしてみる。
NGINX Ingress Controllerのドキュメントによると、クラウドプロバイダで管理されていないベアメタルクラスタでもMetalLBを導入すればLoadBalancer Service
が使えるようになるらしい。
# NodePort版Ingress Controllerの削除
kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/baremetal/deploy.yaml
# MetalLBのインストール
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.3/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.3/manifests/metallb.yaml
# L2 modeの設定
# https://metallb.universe.tf/configuration/#layer-2-configuration
# nodeのネットワークで使われていないIPを使用する(今回は10.240.0.0/24の中でnodeに割り当てられていないIP)
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 10.240.0.10-10.240.0.15
EOF
MetalLB
のインストールと設定が正常にできていればこんな感じになる。
$ k -n metallb-system get all
NAME READY STATUS RESTARTS AGE
pod/controller-77c44876d-wgqzj 1/1 Running 0 5m43s
pod/speaker-fdcrw 1/1 Running 0 5m43s
pod/speaker-kgz2w 1/1 Running 0 5m43s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/speaker 2 2 2 2 2 kubernetes.io/os=linux 5m43s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/controller 1/1 1 1 5m43s
NAME DESIRED CURRENT READY AGE
replicaset.apps/controller-77c44876d 1 1 1 5m43s
ingress-nginxのインストール(with MetalLB)
あとはLoadBalancer Service
を使う設定のingress-nginx
をインストールする。
# LoadBalancerになるはず
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/cloud/deploy.yaml
外部からリクエストを受けてトラフィックを振り分けるためのIngress Controller
が今度はNodePort Service
でなくちゃんとLoadBalancer Service
で公開されていて、しかもExternalIP
にMetalLB
で指定したIPが設定されている。
$ k -n ingress-nginx get all
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create--1-fbqsf 0/1 Completed 0 28s
pod/ingress-nginx-admission-patch--1-l2pz5 0/1 Completed 1 28s
pod/ingress-nginx-controller-5c8d66c76d-wfbjf 1/1 Running 0 29s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.111.44.72 10.240.0.10 80:31646/TCP,443:32459/TCP 29s
service/ingress-nginx-controller-admission ClusterIP 10.99.244.117 <none> 443/TCP 29s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 29s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-5c8d66c76d 1 1 1 29s
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 4s 29s
job.batch/ingress-nginx-admission-patch 1/1 4s 28s
動作確認(with MetalLB)
最後に、同じネットワークのnode
からMetalLB
で払い出されたLoadBalancer Service
のIP越しにIngress
経由のリクエストを送ってみる。
# 払い出したExretnalIPで接続できた!
$ curl -H "Host: hello-world.info" 10.240.0.10/
Hello, world!
Version: 1.0.0
Hostname: web-7c884bf475-tt9g6
できた〜〜🎉
10.240.0.10にサーバが立ってないのにリクエストができるのはMetalLBのspeaker
が同じネットワークに送られたARPリクエストに対して応答するおかげらしい(まだちゃんと理解してない)。
よくわかってないけどとりあえず普通のポートでIngress
が動いたのでヨシ!
おわり
自前のクラスタにIngress Controller
を突っ込む練習をしてみた。
正直なところドキュメント通りにyamlをapplyするだけで簡単に設定できてしまったので本当に身についたかは怪しいが、Ingress Class
についてはIngress Controller
を意識しないと触れる機会が少ないし、実際あまりわかってなかったので勉強になってよかった。MetalLB
についてはまたどこかでお世話になりそうなのでそのときにちゃんと勉強したい。