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

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

2022年9月11日

メニューへ戻る

MicroK8sでKubernetesのお勉強」にて、シングル構成(1台だけ)の Kubernetes環境を作り、「MicroK8sのクラスタ作成」にて、3台のクラスタ環境を作りました。

しかし依然として、Kubernetesクラスタ内の Podで展開されているサーバープロセスに外部からアクセスすることができません。

そんな環境を作って何の意味があるのか?

と思われると思いますが、これには理由があります。
ここからちょっと昔話を。

Dockerが世に出てきて、Dockerのお勉強を進めている時に複数ノードに渡る Docker環境を統合管理する Kubernetesが作られました。

革新的なもので当時コンテナをいじっていた連中がこぞってこれを使い始めたので、情報がインターネット上によく出ていました。

私は当時 CentOS7に Dockerをインストールして Kubernetes環境を作りながらお勉強をしていたのですが、ブログの情報をマンマ真似ても、ある箇所がどうしても上手く行かないのでした。

それは Kubernetesクラスターが提供するサービスに対してクラスター外部からアクセスするという至極当たり前の使い方、複数のノードに冗長化/負荷分散された Podに対してのアクセスをどうやって振り分けるかの機能の箇所でした。

ブログに載っているサービス作成の yamlファイルには、シレッと type行に LoadBalancerと書いてあり、それで上手く行くとあったのですが、実は LoadBalancerサービスはパブリッククラウドサービス側が用意してくれている機能でオンプレじゃ使えないんですね。

Kubernetesはクラウドを使えない貧乏人には拓かれてなかったのでした…。

これを知った時に私は大層ガッカリして、当面仕事でクラウドを使う予定もなかったため、そこで Kubernetesのお勉強を止めてしまいました。

ノードポートに対して、クラスタ外部の NGINXロードバランサーで振り分ける事も考えましたが、何だか Kubernetesの機能がスポイルされている気がしたのです。

LoadBalancerが使えなければ、Kubernetesは殆ど存在意義がないように思えました。


そんな ITエンジニアが世界中にいたんでしょうね。
オンプレ Kubernetes環境で LoadBalancerタイプのサービスを実現するものが作られました。

それがこれ。
MetalLB

「Why?」の段落を読んで私は「我が意を得たり!」と興奮しましたね。

「この仕様(パブリッククラウド前提)がオンプレ Kubernetesの民を二等国民たらしめた」的な事が書いてあって、「これに対抗するために作られた」ともあり、「そうだそうだ〜」と小躍りしたものです。


古傷のルサンチマンが喚起されて長くなってしまいましたが、ここでは MicroK8sで作った Kubernetesクラスタ環境に MetalLBを導入し、外部ノードから同クラスタで展開されているサーバーサービスに接続してみます。

2022年9月11日時点で、MetalLBの最新バージョンは 0.13.5ですが、依然として「まだ未熟なプロジェクトなので本番環境で使ってはいけない」という注意喚起があります。
残念ですが、まだお勉強用に留めておいて下さい。

MicroK8sでの MetalLBの使用の仕方は、本家の Canonicalのサイトに書かれています。
Addon: MetalLB

導入手順もありますので、素直にそれに従って導入します。

先だって作っておいた MicroK8sの 3台クラスタを起動した所からスタートです。

まずマスターノード(サブロウ家では microk8s-master)で現在の設定を見てみます。

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:
    ha-cluster           # (core) Configure high availability on the current node
  disabled:
    community            # (core) The community addons repository
    dashboard            # (core) The Kubernetes dashboard
    dns                  # (core) CoreDNS
    gpu                  # (core) Automatic enablement of Nvidia CUDA
    helm                 # (core) Helm 2 - the package manager for Kubernetes
    helm3                # (core) Helm 3 - Kubernetes package manager
    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
    mayastor             # (core) OpenEBS MayaStor
    metallb             # (core) Loadbalancer for your Kubernetes cluster     
    metrics-server       # (core) K8s Metrics Server for API access to service 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
    storage              # (core) Alias to hostpath-storage add-on, deprecated

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
namespace/metallb-system created
secret/memberlist created
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/controller created
podsecuritypolicy.policy/speaker 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/config-watcher 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/config-watcher created
rolebinding.rbac.authorization.k8s.io/pod-lister created
Warning: spec.template.spec.nodeSelector[beta.kubernetes.io/os]: deprecated since v1.14; use "kubernetes.io/os" instead
daemonset.apps/speaker created
deployment.apps/controller created
configmap/config created
MetalLB is enabled

途中で、MetalLBがクラスタサービスの外部公開に使って良い IPアドレスの範囲を聞かれましたので、とりあえずこのクラスタ環境を作っている 3つの OSの IPを渡しました。

2箇所非推奨(古い)機能利用のワーニングが出ていますが、これは Canonicalにお願いする所なので無視します。

もう一度状態確認をすると 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:
    ha-cluster           # (core) Configure high availability on the current node
    metallb             # (core) Loadbalancer for your Kubernetes cluster
  disabled:
    community            # (core) The community addons repository
    dashboard            # (core) The Kubernetes dashboard
    dns                  # (core) CoreDNS
    gpu                  # (core) Automatic enablement of Nvidia CUDA
    helm                 # (core) Helm 2 - the package manager for Kubernetes
    helm3                # (core) Helm 3 - Kubernetes package manager
    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
    mayastor             # (core) OpenEBS MayaStor
    metrics-server       # (core) K8s Metrics Server for API access to service 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
    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
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:
    ha-cluster           # (core) Configure high availability on the current node
    ingress             # (core) Ingress controller for external access
    metallb              # (core) Loadbalancer for your Kubernetes cluster
  disabled:
    community            # (core) The community addons repository
    dashboard            # (core) The Kubernetes dashboard
    dns                  # (core) CoreDNS
    gpu                  # (core) Automatic enablement of Nvidia CUDA
    helm                 # (core) Helm 2 - the package manager for Kubernetes
    helm3                # (core) Helm 3 - Kubernetes package manager
    host-access          # (core) Allow Pods connecting to Host services smoothly
    hostpath-storage     # (core) Storage class; allocates storage from host directory
    mayastor             # (core) OpenEBS MayaStor
    metrics-server       # (core) K8s Metrics Server for API access to service 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
    storage              # (core) Alias to hostpath-storage add-on, deprecated

有効化できました。
今度こそ良いはずです。


実験してみます。

まず NGINX x30個を以下の yamlファイルでデプロイします。

subro@microk8s-master:~$ cat nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 30
  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

PCのファンがウンウン唸り始めました。
Podが 30個出来上がるまでちょっと待ちます。

できました。

subro@microk8s-master:~$ sudo microk8s kubectl get all --all-namespaces
NAMESPACE        NAME                                           READY   STATUS    RESTARTS      AGE
kube-system      pod/calico-kube-controllers-75f94bc9d6-v85bq   1/1     Running   2 (23m ago)   2d4h
kube-system      pod/calico-node-6gjm2                          1/1     Running   1 (22m ago)   2d
kube-system      pod/calico-node-tbfql                          1/1     Running   1 (23m ago)   2d
kube-system      pod/calico-node-48s54                          1/1     Running   2 (22m ago)   2d
metallb-system   pod/speaker-9fspt                              1/1     Running   0             14m
metallb-system   pod/speaker-97tpg                              1/1     Running   0             14m
metallb-system   pod/speaker-8vppd                              1/1     Running   0             14m
metallb-system   pod/controller-6dfbf9b9c6-5mvn4                1/1     Running   0             14m
default          pod/nginx-deployment-6c8b449b8f-lwbsm          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-fgl4j          1/1     Running   0             100s
default          pod/nginx-deployment-6c8b449b8f-svqtr          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-d9nqb          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-5gmd5          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-qdpwn          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-94rxb          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-t7zw6          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-2hp5c          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-bp88w          1/1     Running   0             100s
default          pod/nginx-deployment-6c8b449b8f-k9n5s          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-mdcsr          1/1     Running   0             100s
default          pod/nginx-deployment-6c8b449b8f-tm8mp          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-7hfd8          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-sfv6w          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-7bgvt          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-62g5r          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-dvsc5          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-ps546          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-znqs9          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-wmtmx          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-2xljx          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-sz8px          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-gpbls          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-tqv66          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-vz9sq          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-vczlg          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-rdjn8          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-7484x          1/1     Running   0             99s
default          pod/nginx-deployment-6c8b449b8f-8zx5d          1/1     Running   0             99s

NAMESPACE   NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
default     service/kubernetes   ClusterIP   10.152.183.1   <none>        443/TCP   2d4h

NAMESPACE        NAME                         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
kube-system      daemonset.apps/calico-node   3         3         3       3            3           kubernetes.io/os=linux        2d4h
metallb-system   daemonset.apps/speaker       3         3         3       3            3           beta.kubernetes.io/os=linux   14m

NAMESPACE        NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system      deployment.apps/calico-kube-controllers   1/1     1            1           2d4h
metallb-system   deployment.apps/controller                1/1     1            1           14m
default          deployment.apps/nginx-deployment          30/30   30           30          47h

NAMESPACE        NAME                                                 DESIRED   CURRENT   READY   AGE
kube-system      replicaset.apps/calico-kube-controllers-75f94bc9d6   1         1         1       2d4h
metallb-system   replicaset.apps/controller-6dfbf9b9c6                1         1         1       14m
default          replicaset.apps/nginx-deployment-6c8b449b8f          30        30        30      47h

この一連の NGINX群([nginx-deployment]という名前でデプロイされている)を MetalLB(と ingress)で外部公開します。

手順に載っていた yamlをウチのものに合わせてちょっと改変しました。
ピンク色の箇所が NGINXのデプロイに繋ぐところと、MetalLBを使う箇所です。

subro@microk8s-master:~$ cat 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

type: LoadBalancer の行は、以前 MetalLBが無い頃にやった際にサービス状態が [Pending] のまま何も進まなかったのです…。

では、満を持してサービスを開始します。

subro@microk8s-master:~$ sudo microk8s kubectl apply -f ingress-service.yaml
service/ingress created

以前も create はされたんですよね。
ちゃんと動いているんでしょうか。

subro@microk8s-master:~$ sudo microk8s kubectl get all --all-namespaces
NAMESPACE        NAME                                           READY   STATUS    RESTARTS      AGE
kube-system      pod/calico-kube-controllers-75f94bc9d6-v85bq   1/1     Running   2 (53m ago)   2d5h
kube-system      pod/calico-node-6gjm2                          1/1     Running   1 (53m ago)   2d
kube-system      pod/calico-node-tbfql                          1/1     Running   1 (53m ago)   2d
kube-system      pod/calico-node-48s54                          1/1     Running   2 (53m ago)   2d
default          pod/nginx-deployment-6c8b449b8f-lwbsm          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-fgl4j          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-svqtr          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-d9nqb          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-5gmd5          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-qdpwn          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-94rxb          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-t7zw6          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-2hp5c          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-bp88w          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-k9n5s          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-mdcsr          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-tm8mp          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-7hfd8          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-sfv6w          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-7bgvt          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-62g5r          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-dvsc5          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-ps546          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-znqs9          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-wmtmx          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-2xljx          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-sz8px          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-gpbls          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-tqv66          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-vz9sq          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-vczlg          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-rdjn8          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-7484x          1/1     Running   0             32m
default          pod/nginx-deployment-6c8b449b8f-8zx5d          1/1     Running   0             32m
ingress          pod/nginx-ingress-microk8s-controller-hph6l    1/1     Running   0             22m
ingress          pod/nginx-ingress-microk8s-controller-f9h6f    1/1     Running   0             22m
ingress          pod/nginx-ingress-microk8s-controller-dxcc6    1/1     Running   0             22m
metallb-system   pod/speaker-85hh9                              1/1     Running   0             2m5s
metallb-system   pod/speaker-r2m85                              1/1     Running   0             2m5s
metallb-system   pod/speaker-tnwp6                              1/1     Running   0             2m5s
metallb-system   pod/controller-6dfbf9b9c6-zx2fc                1/1     Running   0             2m5s

NAMESPACE   NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
default     service/kubernetes   ClusterIP      10.152.183.1    <none>          443/TCP        2d5h
ingress     service/ingress     LoadBalancer  10.152.183.12  192.168.1.120   80:30745/TCP  9s

NAMESPACE        NAME                                               DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
kube-system      daemonset.apps/calico-node                         3         3         3       3            3           kubernetes.io/os=linux        2d5h
ingress          daemonset.apps/nginx-ingress-microk8s-controller   3         3         3       3            3           <none>                        22m
metallb-system   daemonset.apps/speaker                             3         3         3       3            3           beta.kubernetes.io/os=linux   2m5s

NAMESPACE        NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system      deployment.apps/calico-kube-controllers   1/1     1            1           2d5h
default          deployment.apps/nginx-deployment          30/30   30           30          2d
metallb-system   deployment.apps/controller                1/1     1            1           2m5s

NAMESPACE        NAME                                                 DESIRED   CURRENT   READY   AGE
kube-system      replicaset.apps/calico-kube-controllers-75f94bc9d6   1         1         1       2d5h
default          replicaset.apps/nginx-deployment-6c8b449b8f          30        30        30      2d
metallb-system   replicaset.apps/controller-6dfbf9b9c6                1         1         1       2m5s

見た目は動いているようですが…
外部IPは 192.168.1.120 の模様。

ここの 80/tcp にアクセスすると 30個ある NGINXの Podのどれかまで繋いでくれるはずです。

どうせならと、VMware Workstation Player のホストOSである Windows10の Edgeでチャレンジです。

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

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

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


パブリッククラウドの Kubernetesのサービスを使うに、ここまでの環境構築のスキルは一切必要ないのだと思います。

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

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

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

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

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


現場で使えるkubernetes [ 牧田剣吾/松浦崇仁 ]

価格:4,202円
(2022/9/11 17:50時点)
感想(0件)

Kubernetes CI/CDパイプラインの実装 (impress top gear impress top gear) [ 北山晋吾 ]

価格:3,960円
(2022/9/11 17:52時点)
感想(0件)