Vagrant と kubeadm で Kubernetes クラスターをつくる

勉強用の Kubernetes クラスターを kubeadm でつくりました。ローカル PC 上に Vagrant で仮想マシンを起動して、ノードとして利用します。

単にコンテナーのデプロイを試したいだけであれば、Docker DesktopKind を利用するのが早いと思いますが、Kubernetes コンポーネントの設定もふくめて色々触りながら勉強したかったので、VM を用意してクラスターをつくることにしました。

環境

  • macOS Monterey version 12.2.1

Vagrant で Ubuntu の VM を起動する

Vagrant は Homebrew を使ってインストールできます。

brew install vagrant

Vagrantfile を記述していきます。

Ubuntu 20.04 の Box を利用して、まずは 2 ノード分の VM 定義を用意しました。

プロビジョニング用のシェル スクリプトを使って Swap を無効化しています。

Vagrant.configure("2") do |config|
  config.vm.box = "generic/ubuntu2004"

  config.vm.define "node01" do |server|
    server.vm.hostname = "node01"
    server.vm.network "private_network", ip: "192.168.56.10"
  end

  config.vm.define "node02" do |server|
    server.vm.hostname = "node02"
    server.vm.network "private_network", ip: "192.168.56.11"
  end

  config.vm.provision "shell", inline: <<-SHELL
    # Disable swap
    swapoff -a
    sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
    sed -i '/ swap / s/^\\(.*\\)$/#\\1/g' /etc/fstab
  SHELL
end

あとは vagrant up コマンドを実行するだけ。 vagrant ssh で各 VM に SSH できる。 これで VM の用意は完了。

vagrant up
vagrant ssh node01
vagrant ssh node02

kubeadm のインストール

VM が用意できたら Kubernetes ドキュメントに従って kubeadm やクラスター コンポーネントのインストールをすすめていきます。 用意した各 VM に対してインストールします。

kubeadmのインストール | Kubernetes

このページではkubeadmコマンドをインストールする方法を示します。このインストール処理実行後にkubeadmを使用してクラスターを作成する方法については、kubeadmを使用したシングルマスタークラスターの作成を参照してください。 始める前に 次のいずれかが動作しているマシンが必要です Ubuntu 16.04+ Debian 9+ CentOS 7 Red Hat Enterprise Linux (RHEL) 7 Fedora 25+ HypriotOS v1.0.1+ Container Linux (tested with 1800.6.0) 1台あたり2GB以上のメモリ(2GBの場合、アプリ用のスペースはほとんどありません) 2コア以上のCPU クラスター内のすべてのマシン間で通信可能なネットワーク(パブリックネットワークでもプライベートネットワークでも構いません) ユニークなhostname、MACアドレス、とproduct_uuidが各ノードに必要です。詳細はここを参照してください。 マシン内の特定のポートが開いていること。詳細はここを参照してください。 Swapがオフであること。kubeletが正常に動作するためにはswapは必ずオフでなければなりません。 MACアドレスとproduct_uuidが全てのノードでユニークであることの検証 ネットワークインターフェースのMACアドレスはip linkもしくはifconfig -aコマンドで取得できます。 product_uuidはsudo cat /sys/class/dmi/id/product_uuidコマンドで確認できます。 ハードウェアデバイスではユニークなアドレスが割り当てられる可能性が非常に高いですが、VMでは同じになることがあります。Kubernetesはこれらの値を使用して、クラスター内のノードを一意に識別します。これらの値が各ノードに固有ではない場合、インストール処理が失敗することもあります。 ネットワークアダプタの確認 複数のネットワークアダプターがあり、Kubernetesコンポーネントにデフォルトで到達できない場合、IPルートを追加して、Kubernetesクラスターのアドレスが適切なアダプターを経由するように設定することをお勧めします。 必須ポートの確認 Kubernetesのコンポーネントが互いに通信するためには、これらの必要なポートが開いている必要があります。 netcatなどのツールを使用することで、下記のようにポートが開いているかどうかを確認することが可能です。 nc 127.0.0.1 6443 使用するPodネットワークプラグインによっては、特定のポートを開く必要がある場合もあります。 これらは各Podネットワークプラグインによって異なるため、どのようなポートが必要かについては、プラグインのドキュメントを参照してください。 ランタイムのインストール Podのコンテナを実行するために、Kubernetesはコンテナランタイムを使用します。 Linuxノード その他のOS デフォルトでは、Kubernetesは選択されたコンテナランタイムと通信するためにContainer Runtime Interface (CRI)を使用します。 ランタイムを指定しない場合、kubeadmはよく知られたUnixドメインソケットのリストをスキャンすることで、インストールされたコンテナランタイムの検出を試みます。 次の表がコンテナランタイムと関連するソケットのパスリストです。 コンテナランタイムとソケットパス ランタイム Unixドメインソケットのパス Docker /var/run/docker.sock containerd /run/containerd/containerd.sock CRI-O /var/run/crio/crio.sock Dockerとcontainerdの両方が同時に検出された場合、Dockerが優先されます。Docker 18.09にはcontainerdが同梱されており、両方が検出可能であるため、この仕様が必要です。他の2つ以上のランタイムが検出された場合、kubeadmは適切なエラーメッセージで終了します。 kubeletは、組み込まれたdockershimCRIを通してDockerと連携します。

iptablesがブリッジを通過するトラフィックを処理できるようにする

sudo su -

cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

iptablesがnftablesバックエンドを使用しないようにする

# レガシーバイナリがインストールされていることを確認してください
sudo apt-get install -y iptables arptables ebtables

# レガシーバージョンに切り替えてください。
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy

ランタイムのインストール

ここでは containerd を利用します。

必要な設定の追加

sudo su -

cat > /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

# 必要なカーネルパラメータの設定をします。これらの設定値は再起動後も永続化されます。
cat > /etc/sysctl.d/99-kubernetes-cri.conf <<EOF
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

sysctl --system

containerdのインストール

sudo su -

# (containerdのインストール)
## リポジトリの設定
### HTTPS越しのリポジトリの使用をaptに許可するために、パッケージをインストール
apt-get update && apt-get install -y apt-transport-https ca-certificates curl software-properties-common

## Docker公式のGPG鍵を追加
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

## Dockerのaptリポジトリの追加
add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

## containerdのインストール
apt-get update && apt-get install -y containerd.io

# containerdの設定
mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml

# containerdの再起動
systemctl restart containerd

kubeadm、kubelet、kubectlのインストール

sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

コントロールプレーンノードのkubeletによって使用されるcgroupドライバーの設定

ワーカーノードの IP アドレスの指定を KUBELET_EXTRA_ARGS であわせて行います。 参考: Playing with kubeadm in Vagrant Machines, Part 2

cat << EOF | sudo tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd --node-ip=<worker IP address (ex: 192.168.56.10)>
EOF

sudo systemctl daemon-reload
sudo systemctl restart kubelet

コントロール プレーン ノードのセットアップ

node01 VM をコントローラー ノードとして、シングル コントロール プレーンのクラスターをセットアップします。

コントロールプレーンノードの初期化

kubeadm init コマンドを実行するだけで非常にかんたん。

sudo su -
kubeadm init \
  --pod-network-cidr 10.244.0.0/16 \
  --apiserver-advertise-address 192.168.56.10

初期化が完了したら kubeconfig ファイルをホーム ディレクトリ配下にコピーしておきます。

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

これで kubectl コマンドでクラスター操作ができるようになります。 この時点では、ネットワーク プラグインが未インストールのため、ノードは NotReady ステータスです。

% kubectl get nodes
NAME     STATUS     ROLES                  AGE   VERSION
node01   NotReady   control-plane,master   71s   v1.23.3

% kubectl describe node node01
  ...
Conditions:
  ...
  Ready            False   Mon, 07 Feb 2022 20:35:14 +0000   Mon, 07 Feb 2022 20:34:56 +0000   KubeletNotReady              container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized

Podネットワークアドオンのインストール

ここでは flannel をインストールしました。 https://github.com/flannel-io/flannel#getting-started-on-kubernetes

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

flannel のマニフェストの apply 後、ノードが Ready ステータスに変わることを確認します。

% kubectl get pods -A
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE
kube-system   coredns-64897985d-4g6dm          1/1     Running   0          7m57s
kube-system   coredns-64897985d-c579n          1/1     Running   0          7m57s
kube-system   etcd-node01                      1/1     Running   1          8m5s
kube-system   kube-apiserver-node01            1/1     Running   1          8m5s
kube-system   kube-controller-manager-node01   1/1     Running   1          8m5s
kube-system   kube-flannel-ds-76tbb            1/1     Running   0          39s
kube-system   kube-proxy-nfk6r                 1/1     Running   0          7m57s
kube-system   kube-scheduler-node01            1/1     Running   1          8m5s

% kubectl get nodes
NAME     STATUS   ROLES                  AGE     VERSION
node01   Ready    control-plane,master   8m16s   v1.23.3

ノードの追加

2 ノード目以降では、kubeadm join コマンドを実行してクラスターにノードが追加できる。 kubeadm init の実行結果にトークンを含むコマンド例が表示されるので、そのコマンドをそのまま実行すればよい。

sudo su -
kubeadm join 192.168.56.10:6443 --token 1ubbqx.mad3qulm6jk77dvc \
  --discovery-token-ca-cert-hash sha256:a347de9fa17bc6cda79ff3fdcf9804fb6862fe14ad8a711fdb53cf16c6be6373

node02 が追加された。

% kubectl get nodes
NAME     STATUS   ROLES                  AGE   VERSION
node01   Ready    control-plane,master   15m   v1.23.3
node02   Ready    <none>                 49s   v1.23.3

kubeadm init の実行結果を控えておらず、kubeadm join に渡すトークンがわからないときにはどうすればよいか?

--token オプションに渡すトークンは、kubeadm token list コマンドで確認ができる。

% kubeadm token list
TOKEN                     TTL         EXPIRES                USAGES                   DESCRIPTION                                                EXTRA GROUPS
1ubbqx.mad3qulm6jk77dvc   23h         2022-02-08T20:35:01Z   authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token

トークンの有効期限が切れていて利用できない場合には、kubeadm token create コマンドで新しいトークンが作成できる。

% kubeadm token create
pl1f37.3rrw0sl7x5jk2kug

% kubeadm token list
TOKEN                     TTL         EXPIRES                USAGES                   DESCRIPTION                                                EXTRA GROUPS
1ubbqx.mad3qulm6jk77dvc   23h         2022-02-08T20:35:01Z   authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token
pl1f37.3rrw0sl7x5jk2kug   23h         2022-02-08T20:55:58Z   authentication,signing   <none>                                                     system:bootstrappers:kubeadm:default-node-token

--discovery-token-ca-cert-hash オプションの値は、以下コマンドで確認できる。

% openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
   openssl dgst -sha256 -hex | sed 's/^.* //'
a347de9fa17bc6cda79ff3fdcf9804fb6862fe14ad8a711fdb53cf16c6be6373

あとは kubeadm join コマンドに対して、コントロール プレーンのエンドポイントと一緒にトークンを渡せば、ノードの追加ができる。 --discovery-token-ca-cert-hash オプションの値には、先頭に sha256: をつける。

% kubeadm join 192.168.56.10:6443 \
  --token pl1f37.3rrw0sl7x5jk2kug \
  --discovery-token-ca-cert-hash sha256:a347de9fa17bc6cda79ff3fdcf9804fb6862fe14ad8a711fdb53cf16c6be6373

comments powered by Disqus