Kubernetesクラスターをホームラボに立てる

Kubernetesクラスターをホームラボに立てる

Kubernetesとは

Kubernetesとは、コンテナのオーケストレーションツールで、昨今のエンジニア業界では切っても切れないです。コンテナを使うプラットフォームとして有名なのはDockerですね。このDockerを使って、開発作業をするととても便利で、環境依存しないし、そのまま運用環境にも持っていけるという素晴らしいものです。

Dockerについてはいずれ語っても良いのですが、ググればいくらでも出てきます。以下のページはとても分かりやすくまとめてあります。

Qiita – なぜ Docker を使うのか? 小規模シス管向けの Docker のススメ

単純なシステムであれば、コンテナ一つでサービスを提供すればいいのですが、実際の運用はそうはいきません。1日のアクセスが1000くらいのシステムなのに、何かのSNSでバズっていきなりある日から1日のアクセスが1万とか10万とかになるという事もあります。その時サイトが落ちてしまうと炎上します。または、絶対に落ちてはいけないシステムを言うものもあります。これを落ちないように管理構成するのがとても大変です。

オーケストレーションツールを使うと、アクセスや負荷によって1台だったWEBサーバを5台にするとか、バックエンドのアプリサーバが重くなってきたので2台にするとか、サーバが落ちたので自動で復旧させるとか、その手の仕事を自動でしてくれるのです。

なぜMinikubeではダメなのか?

Kubernetesは、クラスター構成のため複数サーバが必要です。システムを管理するマスターノードがあり、コンテナを走らせるワーカーノードが何台もあるというものです。運用環境は色々でオンプレの数台のサーバ構成もあれば、AWSなどのクラウドでマネージドのサービスを利用することもあります。

しかし、Minikubeはローカル環境(ローカルPC)で立てて、Kubernetesがどんなものか確認するためのテスト環境です。「Kubernetesってどんなものか?」、「どんなコマンドが実行できるのか?」、「その結果はどのように表示されるのか?」などの答えを得るものであって、お試しやテスト目的以外にはあまり使えません。

それでも、まずはお金をかけずに最低限動けばいいので使ってみたいという人にはお勧めです。以下の、公式サイトのドキュメントの通りにすれば30分から1時間でKubernetesを体験することができます。

kubernetes.io – Minikubeを使用してローカル環境でKubernetesを動かす

ただ、勉強するためでなく、これからシステムを運用するために急いで勉強が必要だったり、商用環境で使うためのトレーニングが必要な場合は、本物の複数台構成のクラスターを立てた方が実環境に近く、仕組みもよく理解することができます。

ホームラボのESXiに仮想環境を準備する

商用環境ほどのリソースはいらない

Kubernetesクラスターを立てると言っても、サーバPCが何台も必要とか、サーバラックを置かないといけないという事はありません。商用環境はCPU16コア、メモリ32GBのサーバが最低3台とか、個人でするにはコストがかかりすぎます。しかし、あくまでテスト環境なので、最低限のリソースで十分です。

ホームラボに関しては別途記事を作ろうと思いますが、vmware社のESXiやオープンソースのProxmoxなどを使えば、自宅の使っていないパソコンを仮想サーバのプラットフォームにすることができます。古いパソコンの中に仮想サーバを何台か立てて使用するわけです。

もし使っていないPCにCPU4コア、メモリ8GBのスペックがあれば、それで十分です。CPUは常にリソースを使い続けるわけでもないので、CPU2コアでメモリ2GBの仮想サーバを3台立てても十分使えます。Linuxのサーバはそもそもそんなにリソースを食いませんし、海外にはラズパイを並べてKubernetesクラスターを組んでいるエンジニアもたくさんいます。

今回のサーバ構成と参考情報

ESXiにCPU2コア、メモリ2G、HDD15GBの仮想サーバを3台立てます。スペックは以下の通り。

OSはUbuntu-20.04.2のサーバ版です。固定IPアドレスを設定し、追加のサービスは何も入れずにいちから設定します。

OSインストールして、最新の状態にアップグレードするまでは割愛します。

ネットワーク設定

例として、一番多そうなローカルIPの構成にしてみる。

  • ルーター:192.168.1.1/24

これを元に、各サーバには固定IPアドレスを設定する。

  • マスター:192.168.1.100/24
  • ワーカー1:192.168.1.101/24
  • ワーカー2:192.168.1.102/24

参考情報

以下のYoutube動画を参考に進めています。

github.com – “Install Kubernetes Cluster using kubeadm” by justmeandopensource

いちからすべて構築すると大変なので、公式のインストールツールであるkubeadmと公式ドキュメントを参考にします。

kubernetes.io – kubeadmのインストール

追加の情報として、以下のサイトも参照しました。

Kubernetes環境を構築して、実際にコンテナを動かしてみよう

KubeadmでKubernetesをインストールする方法ー簡易版

Kubernetesクラスターを構築する

1. マスター1台とワーカー2台の全てで同じ作業をする

Kubernetesインストール前の準備作業

rootユーザーになる

$ sudo su -

ファイヤーウォールを無効にする

# ufw disable
Firewall stopped and disabled on system startup

ファイヤーウォールが無効になっているのを確認する

# ufw status
Status: inactive

Swapを無効にして、起動時にも読み込まないようにする

# swapoff -a; sed -i '/swap/d' /etc/fstab

Kubernetes用のネットワーク設定をsysctlに行い、有効にする

# cat >>/etc/sysctl.d/kubernetes.conf<<EOF
> net.bridge.bridge-nf-call-ip6tables = 1
> net.bridge.bridge-nf-call-iptables = 1
> EOF
# sysctl --system
* Applying /etc/sysctl.d/10-console-messages.conf ...
kernel.printk = 4 4 1 7
・・・
* Applying /etc/sysctl.conf ...

Dockerインストール。とりあえず、ちゃんと動くのが確定しているバージョンを指定。

# apt install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
# add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# apt update
# apt install -y docker-ce=5:19.03.10~3-0~ubuntu-focal containerd.io

Dockerサービスが稼働しているかを確認する。Ubuntuはサービスがインストールされると自動で有効になるが、CentOSなどでは自動起動に設定してからサービスを起動してあげる必要がある。

~# systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-11-21 04:39:38 UTC; 7min ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 24658 (dockerd)
      Tasks: 10
     Memory: 35.6M
     CGroup: /system.slice/docker.service
             └─24658 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Nov 21 04:39:37 kube-master dockerd[24658]: time="2021-11-21T04:39:37.886498993Z" level=warning msg="Your kernel does not support cgroup rt runtime"
Nov 21 04:39:37 kube-master dockerd[24658]: time="2021-11-21T04:39:37.886511442Z" level=warning msg="Your kernel does not support cgroup blkio weight"
Nov 21 04:39:37 kube-master dockerd[24658]: time="2021-11-21T04:39:37.886517300Z" level=warning msg="Your kernel does not support cgroup blkio weight_d>
Nov 21 04:39:37 kube-master dockerd[24658]: time="2021-11-21T04:39:37.886687443Z" level=info msg="Loading containers: start."
Nov 21 04:39:37 kube-master dockerd[24658]: time="2021-11-21T04:39:37.970322326Z" level=info msg="Default bridge (docker0) is assigned with an IP addre>
Nov 21 04:39:38 kube-master dockerd[24658]: time="2021-11-21T04:39:38.015974929Z" level=info msg="Loading containers: done."
Nov 21 04:39:38 kube-master dockerd[24658]: time="2021-11-21T04:39:38.032958412Z" level=info msg="Docker daemon" commit=9424aeaee9 graphdriver(s)=overl>
Nov 21 04:39:38 kube-master dockerd[24658]: time="2021-11-21T04:39:38.033198058Z" level=info msg="Daemon has completed initialization"
Nov 21 04:39:38 kube-master systemd[1]: Started Docker Application Container Engine.
Nov 21 04:39:38 kube-master dockerd[24658]: time="2021-11-21T04:39:38.054864717Z" level=info msg="API listen on /run/docker.sock"

Kubernetesのインストール

キーとリポジトリを追加

# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
# echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list

Kubernetes本体のインストール

# apt update && apt install -y kubeadm=1.18.5-00 kubelet=1.18.5-00 kubectl=1.18.5-00

2. マスター1台で作業をする

Kubernetesクラスターを初期化する。※自分の環境のマスターノードのIPアドレスを間違えないように!

apiserver-advertise-address には、マスターノードのIPアドレスを指定する。

pod-network-cidr には、Pod間通信で使用するホストネットワークとかぶらないIPアドレス体系を指定する。これは、Calicoを使用するための指定項目。

# kubeadm init --apiserver-advertise-address=192.168.1.100 --pod-network-cidr=172.16.1.0/16  --ignore-preflight-errors=all

Calicoをデプロイ

# kubectl --kubeconfig=/etc/kubernetes/admin.conf create -f https://docs.projectcalico.org/v3.14/manifests/calico.yaml

マスターをクラスターに参加させる

# kubeadm token create --print-join-command

↑のコマンドの再度に、他のノードをクラスター参加させるために必要なトークン情報が出るので忘れずにメモすること。

3. ワーカーノード2台それぞれで作業をする

「 kubeadm token create –print-join-command 」コマンドの結果にあるものを、そのまま実行する。

# kubeadm join 192.168.1.100:6443 --token ea81ol.q6ffuvtc46v8w6g8     --discovery-token-ca-cert-hash sha256:ac6da276b9f6a29279f61fb8d649a86e857a5d2ff6a87a4e8a507cdd44a3f85b

それぞれのワーカーノードがクラスターに参加できればクラスターの構築終了です。

4. マスターノードの一般ユーザーでkubectlを使えるようにする

マスターノードの一般ユーザーとしてログインする。

※この処理を行えば他の端末からもkubectlコマンドを使ってkubernetesを操作できる。

マスターノードの「/etc/kubernetes/admin.conf」ファイルを、ユーザーホームの「.kube/config」として配置し、権限を与える。

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

ちゃんと動いているかチェックしてみる。なんだか、Serverバージョンが異なって見えるが、「get nodes」では正しく出ている。

$ kubectl version --short
Client Version: v1.18.5
Server Version: v1.18.20
$ kubectl cluster-info
Kubernetes master is running at https://192.168.1.100:6443
KubeDNS is running at https://192.168.1.100:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
$ kubectl get nodes
NAME           STATUS   ROLES    AGE   VERSION
kube-master    Ready    master   17m   v1.18.5
kube-worker1   Ready    <none>   12m   v1.18.5
kube-worker2   Ready    <none>   12m   v1.18.5
$ kubectl get pods
No resources found in default namespace.
$ kubectl get cs
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok                  
scheduler            Healthy   ok                  
etcd-0               Healthy   {"health":"true"}   
$ kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   21m
$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-65f8bc95db-bz77j   1/1     Running   1          23m
kube-system   calico-node-5b45z                          1/1     Running   1          23m
kube-system   calico-node-9jdbp                          1/1     Running   1          19m
kube-system   calico-node-wjwsm                          1/1     Running   1          19m
kube-system   coredns-66bff467f8-krjgc                   1/1     Running   1          24m
kube-system   coredns-66bff467f8-z5x2l                   1/1     Running   1          24m
kube-system   etcd-kube-master                           1/1     Running   1          24m
kube-system   kube-apiserver-kube-master                 1/1     Running   1          24m
kube-system   kube-controller-manager-kube-master        1/1     Running   1          24m
kube-system   kube-proxy-24px8                           1/1     Running   1          24m
kube-system   kube-proxy-f5xlh                           1/1     Running   1          19m
kube-system   kube-proxy-hk42p                           1/1     Running   1          19m
kube-system   kube-scheduler-kube-master                 1/1     Running   1          24m

5.デプロイができるかをチェックする

nginxをデプロイして、表示できるかテストする。

$ kubectl create deploy nginx --image nginx
deployment.apps/nginx created

ポッドが作成されたかを確認する。名前から「 pod/nginx-f89759699-q8v5p 」だと分かる。このステータスが「ContainerCreating」から「Running」になればOK。

$ kubectl get all
NAME                        READY   STATUS              RESTARTS   AGE
pod/nginx-f89759699-q8v5p   0/1     ContainerCreating   0          15s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   43m

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   0/1     1            0           15s

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-f89759699   1         1         0       15s

デプロイを直接確認することもできる。

$ kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           62m

次に、NodePortタイプとして、ポート80番でデプロイを公開する。ポッドはKubernetesクラスター内部のIPアドレスからのみアクセスすることができるため、サービスとしての公開が必要になる。

$ kubectl expose deploy nginx --port 80 --type NodePort
service/nginx exposed

公開されたかどうか、サービスをチェックする。公開されたポート番号が32479であることが確認できる。

$ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        44m
nginx        NodePort    10.98.166.162   <none>        80:32479/TCP   14s

再度、「get all」で情報を確認する。ステータスは「Running」になっている。

$ kubectl get all
NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-f89759699-q8v5p   1/1     Running   0          34m

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        78m
service/nginx        NodePort    10.98.166.162   <none>        80:32479/TCP   33m

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           34m

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-f89759699   1         1         1       34m

さらにポッドの詳細情報を確認する。「kube-worker2」でサービスが実行されていることが分かる。

$ kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
nginx-f89759699-q8v5p   1/1     Running   0          45m   172.16.161.193   kube-worker2   <none>           <none>

ただ、「NodePort」で公開しているので、クラスターのどのIPでもいいので、32479版のポートにhttpsでアクセスすれば、Nginxのウェルカム画面が表示される。例:https://192.168.1.100:32479/

確認が終わったので、サービスの公開を停止して、デプロイを削除する。

$ kubectl delete service nginx
service "nginx" deleted

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   110m

$ kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           66m

$ kubectl delete deployment nginx
deployment.apps "nginx" deleted

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   111m
$ kubectl get deployments
No resources found in default namespace.

6. ローカルPC(WSL)からアクセスできるようにしておく

ローカルPCからアクセスするって、サーバに入ってkubectlコマンドをたたけば良いわけですが、実運用でそんな危険なことは行いません。また、サーバに色々便利ツールを入れてみたりも日本ではあまりしないと思います。

サーバに入って作業することはあっても、基本はローカルPCにて作業しますし、コードはGitで管理するし、運用にはRancherやクラウドプロバイダの管理用WebUIを使います。

ローカル環境には、WSL(実際にはWSL2)のUbuntu20.04LTSを使用します。

WSLはLinuxなので、Linux用の入れ方を公式サイトで確認する。

kubernetes.io – Install and Set Up kubectl on Linux

kubectlはクラスターと同じバージョンを入れるのがセオリーなので、まずがマスターノードで確認しておきます。相変わらずサーババージョンは変な出方をしているが、1.18.5を入れることにする。

$ kubectl version --short
Client Version: v1.18.5
Server Version: v1.18.20

$ kubectl get nodes
NAME           STATUS   ROLES    AGE     VERSION
kube-master    Ready    master   3h21m   v1.18.5
kube-worker1   Ready    <none>   3h16m   v1.18.5
kube-worker2   Ready    <none>   3h16m   v1.18.5

まずは、ホームフォルダに以下のファイルを作成します。ファイルの中身はサーバからコピーしてきます。ただ、Ubuntuサーバは初期状態ではrootでログインできませんので、scpコマンドでコピーしてくるという事はできません。rootパスワードを設定してもいいのですが、実サーバでそれをすると結構なセキュリティホールになりかねないのでお勧めできません。

$ mkdir -p $HOME/.kube
$ vi ~/.kube/config
$ chmod 600 ~/.kube/config

次に、kubectlコマンドをインストールします。

$ sudo apt-get install -y apt-transport-https ca-certificates curl
$ sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
$ echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
$ sudo apt-get install -y kubectl=1.18.5-00

ちゃんと、コマンドが使えることを確かめてみる。これらの結果が表示されればサーバに接続されている。

$ kubectl cluster-info
$ kubectl get pods
$ kubectl get nodes

K9sを入れてKubernetesが使える人のふりをしてみる(おまけ)

K9sは、KubernetesをCLI(コマンドラインインターフェース)でグラフィカルに使う事の出来るツールです。

Kubernetesが、最初のKと最後のsの間に8文字のアルファベットがあるので「K8s」と表記されますが、それよりちょっと出たことができるという意味で「K9s」というしゃれたネーミングになっています。

k9scli.io – K9s公式ホームページ(英語のみ)

公式ドキュメントを見る限り、どうもサクッとインストールさせてくれないらしい。Snapでも入れられるが、WSLはsystemdが動いてないので、そこをごにょごにょする必要がある。ということで、LinuxBrewで入れることにする。

LinuxBrewのインストール

Homebrew-on-Linux(公式)

Qiita – Linuxbrew のススメ

$ sudo apt-get install build-essential curl git m4 ruby texinfo libbz2-dev libcurl4-openssl-dev libexpat-dev libncurses-dev zlib1g-dev
$ sudo apt-get install gettext
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"

ダウンロードや処理に時間がかかるので、しばらく放置してよい。その後は、画面の指示に従ってコマンドを実行するとhomebrewがインストールされる。

ちゃんとインストールされているかの確認は以下のコマンドで行える。

$ brew doctor

K9sのインストール

先ほどインストールしたbrewを使ってK9sをインストールする。

$ brew install k9s

インストールが終了したら、コマンドを実行してKubernetesクラスターの情報を参照できるかを確認する。

$ k9s

リモート接続のためか、リソースは表示されないようだ。使い方は以下のサイトを参照するとよい。

Qiita – k9s使い方まとめ

終了は「Ctrlキー+C」でOKの模様。

最後に一言

お疲れさまでした。

そのうち、RancherやPrometheus、Grafanaなども紹介できればいいかと思っています。

ホームラボについても電力と場所を取らずに、騒音も発しないやり方を紹介できればと思っています。

仕事の都合で、会社のナレッジベースには書き込んでも、ブログにするのは時間がかかるので面倒でやらないことが多いです。ただ、この手のドキュメントは英語の文献か英語の動画がほとんどで、困っている人も多いと思います。

何か記事にして欲しいことがあればコメントかお問い合わせフォームからご連絡いただければ助かります。

有料の情報の方が短時間で学べることに気が付いてきたので、ブラックフライデーを使ってUdemyで安く講座を受けようかと思っています。専門書もAmazonで安くなっているといいのですが。