お金をかけずにサーバーの勉強をしよう

オンプレ Kubernetes環境に LoadBalancerサービスを

2024年7月31日

メニューへ戻る

もう二等国民とは言わせない。

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にアクセスした結果の絵
「やったよママ! NGINXが 404を返してくれた!\(^o^)/」

どうも引っ張ってきた NGiNXのコンテナイメージは index.htmlが無いようです。
404てのがちょっとアレですが、NGiNXが返してきたのは確かなんで良しとします。

こうして特に苦労もなく、サービスの設定で [type: LoadBalancer] を使えるようになってしまいました。
素晴らしい。

※本当はこの後で NGiNXの Pod全てで同じ DocumetRootを参照するよう NFSなり環境を整備するべきなんですが、それはまた別な話なのでここではやりません。
(決して面倒くさいからではない)


==========
パブリッククラウドの Kubernetesのサービスを使うには、ここまでの環境構築のスキルは一切必要ないのだと思います。
(環境構築しなくて良いのがパブリッククラウドの利点ですからね…)

ですが、あのお高いクラウド課金をかけずに Kubernetes環境にアプリを展開するのはどういう事かを味わうには、こういうように手を動かす事が必要です。

MicroK8sを使ったところ比較的簡単にできますので、実際に環境を作ってみると良いと思います。
慣れれば 2時間もあれば、OSインストールからここまで作れるんじゃないでしょうか。

ここでは 3台のクラスタを作っていますが、MetalLBの使用は 1台のクラスタでも問題ないはずですので、メモリが 8GBの中古ノートPCでやれるでしょう。

パブリッククラウド各社のサービスは、ベースは同じ Kubernetesであっても、提供されているユーザーインターフェイスはそれぞれ違うはずです。

応用を利かせるには素の Kubernetesをイジっておくのがお勧めなので「これが自宅にあるとお勉強が捗るなぁ」というのが素直な感想でした。


2024年になってから Kubernetesの本が余り出てませんねぇ…。