もう二等国民とは言わせない。
「MicroK8sでKubernetesのお勉強」にて、シングル構成(1台だけ)の Kubernetes環境を作り、「MicroK8sのクラスタ作成」にて、3台のクラスタ環境を作りました。
しかし依然として、Kubernetesクラスタ内の Podで展開されているサーバープロセスに外部からアクセスすることができません。
そんな環境を作って何の意味があるのか?
と思われると思いますが、これには理由があります。
ここからちょっと昔話を。
Dockerが世に出てきて、Dockerのお勉強を進めている時に複数ノードに渡る Docker環境を統合管理する Kubernetesが作られました。
革新的なものだったので当時コンテナをいじっていた連中がこぞってこれを使い始め、情報がインターネット上によく出ていました。
私は当時 CentOS 7に Dockerをインストールして Kubernetes環境を作りながらお勉強をしていたのですが、先行者のブログに書かれた手順をマンマ真似ても、ある箇所がどうしても上手く行かないのでした。
それは Kubernetesクラスターが提供するサーバーサービスに対してクラスター外部からアクセスするという至極当たり前の使い方でした。
複数ノードに冗長化/負荷分散された Podに対して外部からのアクセスをどうやって振り分けるかの機能の箇所でした。
ブログに載っているサービス作成の yamlファイルにはシレッと type行に「LoadBalancer」と書いてあり、それで上手く行くとあったのですが、実は LoadBalancerサービスはパブリッククラウドサービスが用意してくれている機能でオンプレじゃ使えないんですね。
Kubernetesはクラウドを使えない貧乏人には拓かれてなかったのでした…。
これを知った時に私は大層ガッカリして、当面仕事でクラウドを使う予定もなかったため、そこで Kubernetesのお勉強を止めてしまいました。
分散されたノードの IPアドレスとポートに対して、クラスタ外部の NGiNXロードバランサーで振り分ける事も考えましたが、何だか Kubernetesの機能がスポイルされている気がしたのです。
LoadBalancerが使えなければ Kubernetesは殆ど存在意義がないように思えました。
そんな ITエンジニアが世界中にいたんでしょうね。
オンプレ Kubernetes環境で LoadBalancerタイプのサービスを実現するものが作られました。
それがこれ。
MetalLB
「Why?」の段落を読んで私は「我が意を得たり!」と興奮しましたね。
「(パブリッククラウドでの利用が前提になっている)この仕様がオンプレ Kubernetesの民を二等国民(second-class citizens)たらしめた」的な事が書いてあって、「これに対抗するために作られた」ともあり、「そうだそうだ〜」と小躍りしたものです。
古傷のルサンチマンが喚起されて長くなってしまいましたが、ここでは MicroK8sで作った Kubernetesクラスタ環境に MetalLBを導入し、外部ノードから同クラスタで展開されているサーバーサービスに接続してみます。
2024年7月31日時点で、MetalLBの最新バージョンは 0.14.8ですが、依然として「まだ未熟なプロジェクトなので本番環境で使ってはいけない」という注意喚起があります。
残念ですが、まだお勉強用に留めておいて下さい。
(バージョン 1.0がその時なのでしょう。)
MicroK8sでの MetalLBの使用の仕方は、本家の Canonicalのサイトに書かれています。
Addon: MetalLB
導入手順もありますので、素直にそれに従って導入します。
「MicroK8sのクラスタ作成」で作った MicroK8sの 3台クラスタを起動した所からスタートです。
1.MetalLB有効化
まず [microk8s-master]サーバーで現在の Microk8s(Kubernetes)の状態を見てみます。
subro@microk8s-master:~$ sudo microk8s status --wait-ready
microk8s is running
high-availability: yes
datastore master nodes: 192.168.1.120:19001 192.168.1.121:19001 192.168.1.122:19001
datastore standby nodes: none
addons:
enabled:
dns # (core) CoreDNS
ha-cluster # (core) Configure high availability on the current node
helm # (core) Helm - the package manager for Kubernetes
helm3 # (core) Helm 3 - the package manager for Kubernetes
disabled:
cert-manager # (core) Cloud native certificate management
cis-hardening # (core) Apply CIS K8s hardening
community # (core) The community addons repository
dashboard # (core) The Kubernetes dashboard
gpu # (core) Alias to nvidia add-on
host-access # (core) Allow Pods connecting to Host services smoothly
hostpath-storage # (core) Storage class; allocates storage from host directory
ingress # (core) Ingress controller for external access
kube-ovn # (core) An advanced network fabric for Kubernetes
mayastor # (core) OpenEBS MayaStor
metallb # (core) Loadbalancer for your Kubernetes cluster
metrics-server # (core) K8s Metrics Server for API access to service metrics
minio # (core) MinIO object storage
nvidia # (core) NVIDIA hardware (GPU and network) support
observability # (core) A lightweight observability stack for logs, traces and metrics
prometheus # (core) Prometheus operator for monitoring and logging
rbac # (core) Role-Based Access Control for authorisation
registry # (core) Private image registry exposed on localhost:32000
rook-ceph # (core) Distributed Ceph storage using Rook
storage # (core) Alias to hostpath-storage add-on, deprecated
[metallb] は MicroK8sではデフォルトインストールされていて、無効化されているだけのようですね。
自分でインストールしなくて良いとは何と楽なことか。
手順に従って [metallb]サービスを有効化します。
subro@microk8s-master:~$ sudo microk8s enable metallb
Infer repository core for addon metallb
Enabling MetalLB
Enter each IP address range delimited by comma (e.g. '10.64.140.43-10.64.140.49,192.168.0.105-192.168.0.111'): 192.168.1.120-192.168.1.122
Applying Metallb manifest
customresourcedefinition.apiextensions.k8s.io/addresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created
customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created
customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
namespace/metallb-system created
serviceaccount/controller created
serviceaccount/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
role.rbac.authorization.k8s.io/controller created
role.rbac.authorization.k8s.io/pod-lister created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/controller created
secret/webhook-server-cert created
service/webhook-service created
rolebinding.rbac.authorization.k8s.io/pod-lister created
daemonset.apps/speaker created
deployment.apps/controller created
validatingwebhookconfiguration.admissionregistration.k8s.io/validating-webhook-configuration created
Waiting for Metallb controller to be ready.
error: timed out waiting for the condition on deployments/controller
MetalLB controller is still not ready
deployment.apps/controller condition met
ipaddresspool.metallb.io/default-addresspool created
l2advertisement.metallb.io/default-advertise-all-pools created
MetalLB is enabled
途中で、MetalLBがクラスタサービスの外部公開に使って良い IPアドレスの範囲を聞かれましたので、とりあえずこのクラスタ環境を作っている 3つの OSの IPを渡しました。
もう一度状態確認をすると MetalLBが有効になっていました。
subro@microk8s-master:~$ sudo microk8s status --wait-ready
microk8s is running
high-availability: yes
datastore master nodes: 192.168.1.120:19001 192.168.1.121:19001 192.168.1.122:19001
datastore standby nodes: none
addons:
enabled:
dns # (core) CoreDNS
ha-cluster # (core) Configure high availability on the current node
helm # (core) Helm - the package manager for Kubernetes
helm3 # (core) Helm 3 - the package manager for Kubernetes
metallb # (core) Loadbalancer for your Kubernetes cluster
disabled:
cert-manager # (core) Cloud native certificate management
cis-hardening # (core) Apply CIS K8s hardening
community # (core) The community addons repository
dashboard # (core) The Kubernetes dashboard
gpu # (core) Alias to nvidia add-on
host-access # (core) Allow Pods connecting to Host services smoothly
hostpath-storage # (core) Storage class; allocates storage from host directory
ingress # (core) Ingress controller for external access
kube-ovn # (core) An advanced network fabric for Kubernetes
mayastor # (core) OpenEBS MayaStor
metrics-server # (core) K8s Metrics Server for API access to service metrics
minio # (core) MinIO object storage
nvidia # (core) NVIDIA hardware (GPU and network) support
observability # (core) A lightweight observability stack for logs, traces and metrics
prometheus # (core) Prometheus operator for monitoring and logging
rbac # (core) Role-Based Access Control for authorisation
registry # (core) Private image registry exposed on localhost:32000
rook-ceph # (core) Distributed Ceph storage using Rook
storage # (core) Alias to hostpath-storage add-on, deprecated
これで type: LoadBalancer が使えるようになったと思ったら、手順では [ingress]サービスも有効化しないとダメとありました。
先に言えよと。
ingressは Kubernetesクラスターの IPを外部公開してくれます。
ingressも有効化します。
subro@microk8s-master:~$ sudo microk8s enable ingress
Infer repository core for addon ingress
Enabling Ingress
ingressclass.networking.k8s.io/public created
ingressclass.networking.k8s.io/nginx created
namespace/ingress created
serviceaccount/nginx-ingress-microk8s-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-microk8s-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-microk8s-role created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-microk8s created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-microk8s created
configmap/nginx-load-balancer-microk8s-conf created
configmap/nginx-ingress-tcp-microk8s-conf created
configmap/nginx-ingress-udp-microk8s-conf created
daemonset.apps/nginx-ingress-microk8s-controller created
Ingress is enabled
有効化できたようです。
再び状態は確認します。
subro@microk8s-master:~$ sudo microk8s status --wait-ready
microk8s is running
high-availability: yes
datastore master nodes: 192.168.1.120:19001 192.168.1.121:19001 192.168.1.122:19001
datastore standby nodes: none
addons:
enabled:
dns # (core) CoreDNS
ha-cluster # (core) Configure high availability on the current node
helm # (core) Helm - the package manager for Kubernetes
helm3 # (core) Helm 3 - the package manager for Kubernetes
ingress # (core) Ingress controller for external access
metallb # (core) Loadbalancer for your Kubernetes cluster
disabled:
cert-manager # (core) Cloud native certificate management
cis-hardening # (core) Apply CIS K8s hardening
community # (core) The community addons repository
dashboard # (core) The Kubernetes dashboard
gpu # (core) Alias to nvidia add-on
host-access # (core) Allow Pods connecting to Host services smoothly
hostpath-storage # (core) Storage class; allocates storage from host directory
kube-ovn # (core) An advanced network fabric for Kubernetes
mayastor # (core) OpenEBS MayaStor
metrics-server # (core) K8s Metrics Server for API access to service metrics
minio # (core) MinIO object storage
nvidia # (core) NVIDIA hardware (GPU and network) support
observability # (core) A lightweight observability stack for logs, traces and metrics
prometheus # (core) Prometheus operator for monitoring and logging
rbac # (core) Role-Based Access Control for authorisation
registry # (core) Private image registry exposed on localhost:32000
rook-ceph # (core) Distributed Ceph storage using Rook
storage # (core) Alias to hostpath-storage add-on, deprecated
有効化できました。
今度こそ良いはずです。
2.[type: LoadBalancer ] でサービスを開始
Metallbを使ってサービスを開始してみます。
まず NGiNX x9 個を以下の [nginx.yaml]ファイルで配備(デプロイ)します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 9
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
実行します。
subro@microk8s-master:~$ sudo microk8s kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment configured
できました。
subro@microk8s-master:~$ sudo microk8s kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
default pod/nginx-deployment-6cfb64b7c5-6pcdd 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-6xzzd 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-7hvtz 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-7pxst 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-9hwcn 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-9jdqs 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-kkgcs 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-rk9g2 1/1 Running 1 (45m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-zn7p5 1/1 Running 1 (45m ago) 32h
ingress pod/nginx-ingress-microk8s-controller-9n76f 1/1 Running 0 7m45s
ingress pod/nginx-ingress-microk8s-controller-ncpt9 1/1 Running 0 7m45s
ingress pod/nginx-ingress-microk8s-controller-vqhv5 1/1 Running 0 7m45s
kube-system pod/calico-kube-controllers-796fb75cc-9bmbf 1/1 Running 1 (45m ago) 32h
kube-system pod/calico-node-4zngp 1/1 Running 0 38m
kube-system pod/calico-node-ppmtd 1/1 Running 0 38m
kube-system pod/calico-node-rtbxf 1/1 Running 0 38m
kube-system pod/coredns-5986966c54-4lbp5 1/1 Running 1 (45m ago) 32h
metallb-system pod/controller-5484c5f99f-rflxj 1/1 Running 0 13m
metallb-system pod/speaker-6s5s8 1/1 Running 0 13m
metallb-system pod/speaker-lcxc6 1/1 Running 0 13m
metallb-system pod/speaker-qdsd9 1/1 Running 0 13m
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.152.183.1 <none> 443/TCP 32h
kube-system service/kube-dns ClusterIP 10.152.183.10 <none> 53/UDP,53/TCP,9153/TCP 32h
metallb-system service/webhook-service ClusterIP 10.152.183.191 <none> 443/TCP 13m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
ingress daemonset.apps/nginx-ingress-microk8s-controller 3 3 3 3 3 <none> 7m46s
kube-system daemonset.apps/calico-node 3 3 3 3 3 kubernetes.io/os=linux 32h
metallb-system daemonset.apps/speaker 3 3 3 3 3 kubernetes.io/os=linux 13m
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
default deployment.apps/nginx-deployment 9/9 9 9 32h
kube-system deployment.apps/calico-kube-controllers 1/1 1 1 32h
kube-system deployment.apps/coredns 1/1 1 1 32h
metallb-system deployment.apps/controller 1/1 1 1 13m
NAMESPACE NAME DESIRED CURRENT READY AGE
default replicaset.apps/nginx-deployment-6cfb64b7c5 9 9 9 32h
kube-system replicaset.apps/calico-kube-controllers-796fb75cc 1 1 1 32h
kube-system replicaset.apps/coredns-5986966c54 1 1 1 32h
metallb-system replicaset.apps/controller-5484c5f99f 1 1 1 13m
ピンクの行が NGiNXの Podです。
黄色の行が ingressに関するもので自動的にできていました。
Metallbの行もありますね。
この一連の NGiNXの Pod群を MetalLB(と ingress)で外部公開します。
手順に載っていた yamlファイルをこの環境に合わせてちょっと改変しました。
[ingress-service.yaml]ファイルとしました。
apiVersion: v1
kind: Service
metadata:
name: ingress
namespace: ingress
spec:
selector:
name: nginx-deployment
type: LoadBalancer
# loadBalancerIP is optional. MetalLB will automatically allocate an IP
# from its pool if not specified. You can also specify one manually.
# loadBalancerIP: x.y.z.a
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
ピンク色の箇所が NGiNXのデプロイに繋ぐところと、MetalLBを使う箇所です。
[type: LoadBalancer] の行は、以前 MetalLBが無い頃に他人のブログに書いた通りにやってみた際にサービス状態が [Pending] のまま何も進まなかったのです…。
では、満を持してサービスを開始します。
subro@microk8s-master:~$ sudo microk8s kubectl apply -f ingress-service.yaml
service/ingress created
以前も create はされたんですよね。
だけど Pendingのまま進まなかった…。
ちゃんと動いているんでしょうか。
確認します。
subro@microk8s-master:~$ sudo microk8s kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
default pod/nginx-deployment-6cfb64b7c5-6pcdd 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-6xzzd 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-7hvtz 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-7pxst 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-9hwcn 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-9jdqs 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-kkgcs 1/1 Running 1 (58m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-rk9g2 1/1 Running 1 (57m ago) 32h
default pod/nginx-deployment-6cfb64b7c5-zn7p5 1/1 Running 1 (57m ago) 32h
ingress pod/nginx-ingress-microk8s-controller-9n76f 1/1 Running 0 20m
ingress pod/nginx-ingress-microk8s-controller-ncpt9 1/1 Running 0 20m
ingress pod/nginx-ingress-microk8s-controller-vqhv5 1/1 Running 0 20m
kube-system pod/calico-kube-controllers-796fb75cc-9bmbf 1/1 Running 1 (58m ago) 32h
kube-system pod/calico-node-4zngp 1/1 Running 0 51m
kube-system pod/calico-node-ppmtd 1/1 Running 0 51m
kube-system pod/calico-node-rtbxf 1/1 Running 0 51m
kube-system pod/coredns-5986966c54-4lbp5 1/1 Running 1 (58m ago) 32h
metallb-system pod/controller-5484c5f99f-rflxj 1/1 Running 0 26m
metallb-system pod/speaker-6s5s8 1/1 Running 0 26m
metallb-system pod/speaker-lcxc6 1/1 Running 0 26m
metallb-system pod/speaker-qdsd9 1/1 Running 0 26m
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.152.183.1 <none> 443/TCP 32h
ingress service/ingress LoadBalancer 10.152.183.45 192.168.1.120 80:30642/TCP 71s
kube-system service/kube-dns ClusterIP 10.152.183.10 <none> 53/UDP,53/TCP,9153/TCP 32h
metallb-system service/webhook-service ClusterIP 10.152.183.191 <none> 443/TCP 26m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
ingress daemonset.apps/nginx-ingress-microk8s-controller 3 3 3 3 3 <none> 20m
kube-system daemonset.apps/calico-node 3 3 3 3 3 kubernetes.io/os=linux 32h
metallb-system daemonset.apps/speaker 3 3 3 3 3 kubernetes.io/os=linux 26m
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
default deployment.apps/nginx-deployment 9/9 9 9 32h
kube-system deployment.apps/calico-kube-controllers 1/1 1 1 32h
kube-system deployment.apps/coredns 1/1 1 1 32h
metallb-system deployment.apps/controller 1/1 1 1 26m
NAMESPACE NAME DESIRED CURRENT READY AGE
default replicaset.apps/nginx-deployment-6cfb64b7c5 9 9 9 32h
kube-system replicaset.apps/calico-kube-controllers-796fb75cc 1 1 1 32h
kube-system replicaset.apps/coredns-5986966c54 1 1 1 32h
metallb-system replicaset.apps/controller-5484c5f99f 1 1 1 26m
見た目は動いているようですが…。
外部IPは [192.168.1.120] の模様です。
ここの [80/tcp(http)] にアクセスすると 9個ある NGiNXの Podのどれかまで繋いでくれるはずです。
どうせならと、VMware Workstation Pro のホストOSである Windows10の Edgeでチャレンジです。
「やったよママ! NGINXが 404を返してくれた!\(^o^)/」
どうも引っ張ってきた NGiNXのコンテナイメージは index.htmlが無いようです。
404てのがちょっとアレですが、NGiNXが返してきたのは確かなんで良しとします。
こうして特に苦労もなく、サービスの設定で [type: LoadBalancer] を使えるようになってしまいました。
素晴らしい。
(決して面倒くさいからではない)
==========
パブリッククラウドの Kubernetesのサービスを使うには、ここまでの環境構築のスキルは一切必要ないのだと思います。
(環境構築しなくて良いのがパブリッククラウドの利点ですからね…)
ですが、あのお高いクラウド課金をかけずに Kubernetes環境にアプリを展開するのはどういう事かを味わうには、こういうように手を動かす事が必要です。
MicroK8sを使ったところ比較的簡単にできますので、実際に環境を作ってみると良いと思います。
慣れれば 2時間もあれば、OSインストールからここまで作れるんじゃないでしょうか。
ここでは 3台のクラスタを作っていますが、MetalLBの使用は 1台のクラスタでも問題ないはずですので、メモリが 8GBの中古ノートPCでやれるでしょう。
パブリッククラウド各社のサービスは、ベースは同じ Kubernetesであっても、提供されているユーザーインターフェイスはそれぞれ違うはずです。
応用を利かせるには素の Kubernetesをイジっておくのがお勧めなので「これが自宅にあるとお勉強が捗るなぁ」というのが素直な感想でした。
2024年になってから Kubernetesの本が余り出てませんねぇ…。