Translate

2023年2月21日火曜日

Kubernetes上にCNIを実現するためのCNIプラグイン選定に悩む



初心者がKubernetes環境を構築するとき、最初にとまどうのがCNI。

複数ノード間にまたがるPod間の通信を直接行えるようにするコンセプトらしいのだけど、その実装が公式ドキュメントにもCalicoがオススメと書かれて入るものの、いろんな人の記事ではflannelもしくは導入さえしていない..

導入さえしていない記事書いてる人たちは、インストールするだけで満足してしまったのかもしれないが、Kubernetesを「使う」場合はどうしてもいれておきたい。

で、どの実装を選べばいいのか迷っていたところ、以下の記事が引っかかった。


なにやらCNIの比較を(定性評価だけど)している感じ..

なので、この記事の CNI Comparisonセクションだけ勝手に翻訳してみました。
以下参照する場合は at your own risk でお願いします。

----

CNI 比較

Flannel

CoreOSが開発したプロジェクトであるFlannelは、おそらく最もわかりやすく、人気のあるCNIプラグインです。コンテナオーケストレーションシステムのためのネットワーキングファブリックの最も成熟した例の1つで、コンテナ間およびホスト間のより良いネットワーキングを可能にすることを意図しています。CNI コンセプトが提唱され始めた初期に Flannel 用の CNI プラグインが登場しました。


Flannel は他の選択肢と比べると、インストールや設定が比較的容易です。flanneldという単一のバイナリとしてパッケージ化されており、多くの一般的なKubernetesクラスタデプロイツールや多くのKubernetesディストリビューションでデフォルトでインストールすることができます。Flannelは、Kubernetesクラスタの既存のetcdクラスタを使用して、APIを使用して状態情報を格納することで、専用のデータストアをプロビジョニングする必要を回避することができます。

Flannelはレイヤ3のIPv4オーバーレイネットワークを構成します。クラスタ内のすべてのノードにまたがる大規模な内部ネットワークが作成されます。このオーバーレイネットワーク内で、各ノードには内部でIPアドレスを割り当てるためのサブネットが与えられます。Podがプロビジョニングされると、各ノード上のDockerブリッジインターフェイスは、各新規コンテナに対してアドレスを割り当てます。同じホスト内のPodはDockerブリッジを使用して通信できますが、異なるホスト上のPodは、適切な宛先にルーティングするために、flanneldによってUDPパケットにカプセル化されたトラフィックを持つことになります。

Flannelには、カプセル化とルーティングに利用できるいくつかの異なるタイプのバックエンドがあります。デフォルトかつ推奨されるアプローチは、VXLAN を使用することです。これは、パフォーマンスが良く、他のオプションよりも手動での介入が少ないからです。

全体として、Flannel はほとんどのユーザにとって良い選択です。管理面では、シンプルなネットワーキング・モデルを提供し、基本的なことだけが必要な場合に適した環境を構築することができます。一般的には、Flannel で提供できないものが必要になるまで、Flannel を使い始めるのが無難でしょう。

Calico

Project Calico、または単にCalicoは、Kubernetesエコシステムで人気のあるもう1つのネットワークオプションです。Flannelはシンプルな選択肢として位置づけられていますが、Calicoはそのパフォーマンス、柔軟性、そしてパワーでよく知られています。Calicoはネットワークをより包括的に捉え、ホストとポッド間のネットワーク接続を提供するだけでなく、ネットワークセキュリティや管理にも関与します。Calico CNIプラグインは、Calicoの機能をCNIフレームワーク内にラップします。

システム要件を満たすプロビジョニングされたばかりのKubernetesクラスタ上で、1つのマニフェストファイルを適用することにより、Calicoを迅速にデプロイすることができるのです。Calico のオプションのネットワーク ポリシー機能に興味がある場合は、追加のマニフェストをクラスターに適用することで有効にすることができます。

Calico をデプロイするために必要なアクションはかなり単純に見えますが、Calico が作成するネットワーク環境には単純なものと複雑なものの両方の属性があります。Flannel とは異なり、Calico はオーバーレイ・ネットワークを使用しません。その代わり、Calico は BGP ルーティングプロトコルを使ってホスト間のパケットをルーティングするレイヤ3ネットワークを構成します。つまり、ホスト間を移動する際にパケットをカプセル化の余分なレイヤで包む必要がないのです。BGPルーティングメカニズムは、トラフィックを追加のレイヤで包むという余分なステップを踏むことなく、パケットをネイティブに指示することができます。

このことによるパフォーマンスに加えて、副次的な効果として、ネットワークの問題が発生したときに、より従来通りのトラブルシューティングが可能になることが挙げられます。VXLANなどの技術を使ったカプセル化されたソリューションはうまく機能しますが、このプロセスではパケットを操作するため、トレースが困難になることがあります。Calicoでは、標準的なデバッグツールが単純な環境と同じ情報にアクセスできるため、より幅広い開発者や管理者が挙動を把握しやすくなっています。

ネットワーク接続性だけでなく、Calicoは高度なネットワーク機能でもよく知られています。ネットワークポリシーは、その中でも最も求められている機能の一つです。さらに、CalicoはサービスメッシュであるIstioと統合し、サービスメッシュ層とネットワークインフラ層の両方でクラスタ内のワークロードに対するポリシーを解釈し、実施することもできます。つまり、ポッドがどのようにトラフィックを送受信できるようにすべきかを記述した強力なルールを設定し、ネットワーク環境のセキュリティと制御を向上させることができるのです。

Project Calicoは、その要件をサポートし、パフォーマンスとネットワークおよびセキュリティ・ポリシーなどの機能が重要な場合に適した環境です。さらに、Calicoは、サポート契約を求めている場合や、将来的にその選択肢を残しておきたい場合、Calico EnterpriseやCalico Cloudとして商用サポートを提供しています。一般的には、一度設定したら忘れるのではなく、ネットワークをコントロールできるようにしたい場合に適した選択肢です。Kubernetesのネットワーキングとセキュリティの詳細については、電子ブックをダウンロードしてください。

Canal

Canal は、たくさんの理由からも興味深い選択肢といえます。

まず第一に、Canal は flannel が提供するネットワーキング層と Calico のネットワーキング・ポリシー機能を統合しようとするプロジェクトの名前なのです。しかし、貢献者たちが詳細を詰めていくうちに、標準化と柔軟性を確保するために両方のプロジェクトで作業を行えば、完全な統合は必ずしも必要ではないことが明らかになりました。その結果、公式プロジェクトはやや消滅しましたが、2つの技術を一緒に展開する意図した機能は達成されました。このため、たとえプロジェクトが存在しなくなったとしても、今でもこの組み合わせを「Canal」と呼ぶのが最も簡単な場合があります。

CanalはFlannelとCalicoの組み合わせであるため、その利点もこれら2つの技術の交差点になっています。ネットワーク層はFlannelが提供するシンプルなオーバーレイで、あまり追加設定をしなくても多くの異なるデプロイメント環境で機能します。その上に重ねられたネットワークポリシー機能は、Calicoの強力なネットワークルール評価によってベースネットワークを補完し、さらなるセキュリティと制御を提供します。

クラスタが必要なシステム要件を満たしていることを確認した後、Canal は 2 つのマニフェストを適用してデプロイすることができ、どちらかのプロジェクト単体よりも構成が難しくなることはありません。Canalは、チームが実際のネットワーキングを変更する実験をする前に、ネットワークポリシーの実験を開始し、経験を積むのに良い方法です。

一般に、Canal は Flannel が提供するネットワーキング・モデルが好きで、Calico の機能のいくつかに魅力を感じている場合には良い選択となります。ネットワークポリシールールを定義できることはセキュリティの観点から非常に大きな利点であり、多くの意味で Calico のキラー機能です。この技術を使い慣れたネットワーキング・レイヤに適用できるということは、より高性能な環境を移行することなく手に入れられるということです。

Weave Net

WeaveworksのWeave Netは、Kubernetes向けのCNI対応ネットワーキング・オプションで、これまで説明してきた他の製品とは異なるパラダイムを提供します。Weave はクラスタ内の各ノード間にメッシュのオーバーレイネットワークを作成し、参加者間の柔軟なルーティングを可能にします。これは、他のいくつかのユニークな機能と相まって、Weaveは他の方法では問題が発生する可能性がある状況でもインテリジェントにルーティングすることができます。

ネットワークを構築するために、Weaveはネットワーク内の各ホストにインストールされたルーティング・コンポーネントに依存しています。これらのルータはトポロジ情報を交換し、利用可能なネットワーク環境の最新情報を維持します。別のノードにあるポッドにトラフィックを送ろうとするとき、Weave ルータは「高速データパス」で送るか「スリーブ」パケット転送方式でフォールバックするかを自動的に決定します。

高速データパスは、カーネルのネイティブなOpen vSwitchデータパスモジュールに依存し、ユーザ空間に何度も出入りすることなく、パケットを適切なポッドに転送する手法です。WeaveルータはOpen vSwitchの設定を更新し、受信パケットのルーティング方法についてカーネル層が正確な情報を持っていることを確認します。一方、スリーブモードは、ネットワークのトポロジーが高速データパス・ルーティングに適していない場合のバックアップとして利用できます。これは、高速データパスが必要なルーティング情報や接続性を持っていない場合にパケットをルーティングできる、より低速なカプセル化モードです。トラフィックがルータを流れるにつれて、ルータはどのピアがどのMACアドレスと関連しているかを学習し、後続のトラフィックに対してより少ないホップでよりインテリジェントにルーティングできるようになります。これと同じ仕組みで、ネットワークの変更によって利用可能な経路が変更された場合、各ノードが自己修正することができます。

Calicoと同様に、Weaveもクラスタにネットワークポリシー機能を提供します。これはWeaveのセットアップ時に自動的にインストールされ、設定されるので、ネットワークルールを追加する以上の追加設定は必要ありません。Weaveが提供するもののうち、他のオプションにないものの1つは、ネットワーク全体の簡単な暗号化です。これはかなりのネットワーク・オーバーヘッドを追加しますが、Weave は、スリーブ・トラフィックに NaCl 暗号を使用し、カーネルで VXLAN トラフィックを暗号化する必要があるので、高速データパス・トラフィックに IPsec ESP を使用して、すべてのルート・トラフィックを自動的に暗号化するように設定することができます。

Weave は、大量の複雑な機能や管理を追加することなく、豊富な機能を持つネットワーキングを求める人々にとって素晴らしい選択肢となります。セットアップが比較的簡単で、多くの組み込み機能や自動設定機能を備えており、他のソリューションでは失敗する可能性のあるシナリオでもルーティングを提供することができます。メッシュ構造のため、合理的に収容できるネットワークのサイズに制限がありますが、ほとんどのユーザにとって、これは問題にはならないでしょう。さらに、Weaveは、ヘルプやトラブルシューティングのための連絡先を確保したい組織のために、有償サポートを提供しています。

まとめ

KubernetesがCNI標準を採用したことで、同じエコシステムの中に多くの異なるネットワークソリューションが存在するようになりました。利用可能なオプションの多様性は、ほとんどのユーザが現在のニーズとデプロイ環境に適したCNIプラグインを見つけることができ、同時に状況が変化したときの解決策も提供できることを意味します。運用要件は組織によって大きく異なるため、複雑さと機能の豊富さのレベルが異なる成熟したソリューションを多数用意することで、Kubernetesが独自の要件を満たしながら、かなり一貫したユーザエクスペリエンスを提供できるようになります。
---

勝手な個人の感想ですが、

  • セットアップしたいだけの人は、CNIを入れない
  • ちょっと使い・学習環境程度の人は、flannel
  • 小規模ながらも実行環境で..の人でサポート無用なら Calico、BGPよりもVXLANのトラブルになれていれば Canal(かflannel)
  • 有償でもサポートは必須の人は Weave Net


といったところだろうか..


Kubernetes 完全ガイドのセットアップ章でもflannelを使っているので、とりあえず最新のKubernetesでもflannelをまず入れてみればいいか。
どうせkubectl apply でのインストールなので引数同じで kubectl deleteすればもとにもどるし..

CNIという概念を適用したところで、Kubernetesの設計を基盤屋が担わないと難しくなったような気がする。CloudStack3.xの時代だったらまだVLANくらいの知識まででなんとかなっていたけど、VXLANやBGPなんか出てきたら..もはやネットワーク屋でないとCNIプラグインの選定なんて無理じゃないだろうか..

それに..ネットワークコンテナを隠しコンテナ(?)でつくるDockerと管理用namespaceで動かすCNIプラグインをわざわざいれさせるKubernetes..DockerとKubernetesの距離が空いたのはこのあたりなのかもしれないなあ..と。本当の事情は知らないけど..

2023年2月14日火曜日

Raspberry Pi OS bullseye/64bit を使って Kubernetes クラスタを構築

 Kubernetesを勉強するためにPlay with Kubernetesを使っていたのだけど..そもそもどんな構成になっているのかが気になり、余ったRaspberry Pi4B(2GB)とRaspberry Pi3B+(1GB)×3で作ってみた。


 

先に書いておくとコントロールプレーン/マスターノードにするRaspberry Piはメモリが2GB以上ないと動作しない。実は全部3B+(1GB)で色々こねくり回してみたのだけど、kubelet を動かすところで失敗してしまった。

ただ..後述の追記を読んでもらえるとわかると思いますが、運用するとワーカーノードにした3B+はバンバン落ちる..正直flannelのノードを維持することすら..4B 4GB秋月で14,800円(2023/2/15時点)..もっと待てば下がると思うんだよなあ..まだ上がるかもしれないしなあ..試しに動かすだけなのに購入するのは..ということで、まだ購入してません

自宅でやったので、いわゆる家庭用WiFiルータを使っている。デフォルトで192.168.11.0/24ネットワークをDHCPで割り当てる設定だが、DHCP 割当対象外の IP アドレスを固定 IP として割り当て直している。

有線LANの方は使用しなかった。CloudStackの経験から2つのNICをつかえたらいいんじゃとwlan0eth0両方有効にしてはじめたが、とたんにセットアップの難易度が上がってしまい、結局WiFiのみにしてしまった。

以下、セットアップの手順である。

動作確認は2023/2/13時点なので、最新バージョンをいれているところで、動作が変わっている場合がある。参考にする場合は注意のこと。

※上図の構成は予め組んでいる状態とします。

Raspberry Pi OS のセットアップ

  • WiFi接続したPCからpingを打ち、レスポンスのないIPアドレスをメモ
  • OSイメージを書き込んだSDカードを4枚作成
    https://www.raspberrypi.com/software/ からRaspberry Pi Imagerをダウンロード
  • SDカードを刺す(フォーマット)
  • Raspberry Pi OS Imagerを起動
  • OSイメージはRaspberry Pi OS Lite 64bitを選択

試行したバージョンは、bullseye 2022/9/22版 64bit

  • ホスト名(node01~04)、SSH、WiFi、ロケール・キーボードを設定し書き込みを押下
  • 1台づつ書き込んだSDカードをRaspberry Pi に刺し起動、WiFi接続したPCからpingを再度打ちDHCP割り当てされたIPアドレスをメモ

Kubernetesのセットアップ

TerminalソフトウェアからメモしたIPアドレスへSSH接続し、以下の作業を実行

cgroup 有効化

sudo sed -i 's/$/ cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory/g' /boot/cmdline.txt

NTP 設定

  • sudo vi /etc/systemd/timesyncd.conf を実行し、以下の行を `[Time]` 内に記述、保存
NTP=ntp.jst.mfeed.ad.jp ntp.nict.jp
FallbackNTP=time.google.com

  • 以下のコマンドを実行

sudo timedatectl set-ntp true
sudo systemctl daemon-reload

ファイルシステム領域の拡張

sudo raspi-config  --expand-rootfs

固定IPアドレス指定

  • WiFiルータ(DHCP)から割り当てられたIPアドレスが XXX.YYY.ZZZ.AAA である場合、node01~node04までの固定IPアドレス割り当てされない範囲でアドレスを決める
  • ここでは node01 ~ node04 に XXX.YYY.ZZZ.201 ~ XXX.YYY.ZZZ.204 を割り当てることとする
  • WiFiルータのIPアドレスは XXX.YYY.ZZZ.1/24 とする
  • sudo vi /etc/dhcpcd.conf を実行し、以下のように `wlan0` 設定を変更、保存する (以下の記述は node01 の場合)

interface wlan0
static ip_address=XXX.YYY.ZZZ.201/24
static routers=XXX.YYY.ZZZ.1
static domain_name_servers=XXX.YYY.ZZZ.1

hosts 設定

  • sudo vi /etc/hosts を実行し、固定IPアドレスを記述、保存

XXX.YYY.ZZZ.201    node01
XXX.YYY.ZZZ.202    node02
XXX.YYY.ZZZ.203    node03
XXX.YYY.ZZZ.204    node04

固定IPアドレスでSSH接続

  • sudo reboot を実行し、再起動
  • Terminal ソフトウェアで、割り当てた固定IPアドレスでSSH接続

OS/パッケージ の最新化

sudo apt update && sudo apt dist-upgrade -y
sudo apt update && sudo apt upgrade -y

スワップの無効化

sudo swapoff --all
sudo systemctl stop dphys-swapfile
sudo systemctl disable dphys-swapfile

Kubernetes 事前設定

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sudo apt 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

sudo sysctl --system

cat <<EOF | sudo tee /etc/modules-load.d/crio.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sysctl --system

OS 再起動

  • sudo rebootTerminal ソフトウェアで、割り当てた固定IPアドレスでSSH接続

CRIO のインストール

OS=Raspbian_10
VERSION=1.24
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /
EOF
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
OS=Raspbian_11
VERSION=1.24
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /
EOF
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -

sudo apt update
sudo apt upgrade -y
sudo apt install -y cri-o cri-o-runc
sudo systemctl daemon-reload
sudo systemctl enable crio
sudo systemctl start crio

執筆時点(2023/2/13)で、Raspbean_11のcontainerdがなかった。もうできているかもしれない..

Kubernetes のインストール

sudo apt update && sudo apt 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 update
sudo apt upgrade -y
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
cat <<EOF | sudo tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--container-runtime-endpoint='unix:///var/run/crio/crio.sock'
EOF

sudo systemctl daemon-reload
sudo systemctl restart kubelet

マスターノードの設定

  • node01 にて以下のコマンドを実行

sudo kubeadm init --apiserver-advertise-address=192.168.11.201 --pod-network-cidr=10.244.0.0/16

  • 表示されている以下の箇所をメモ(以下は、実行時のコンソール表示例)

pi@node01:~ $ sudo kubeadm init --apiserver-advertise-address=192.168.11.201 --pod-network-cidr=10.244.0.0/16
[init] Using Kubernetes version: v1.26.1
[preflight] Running pre-flight checks
        [WARNING SystemVerification]: missing optional cgroups: hugetlb
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local node01] and IPs [10.96.0.1 XXX.YYY.ZZZ.201]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [localhost node01] and IPs [192.168.11.201 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [localhost node01] and IPs [192.168.11.201 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 34.004780 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node node01 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node node01 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: xua9wk.gwe59et1dix4dw1q
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:


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


Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:


kubeadm join 192.168.11.201:6443 --token xua9wk.gwe59et1dix4dw1q \
        --discovery-token-ca-cert-hash sha256:4775e46c92965e5fc383731aa58429bb1d1246af22e85768bf8474f5869a4666

config 設定

  • node01 にて以下のコマンドを実行

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

以降、node01では sudo なして kubectl が実行可能になる

ワーカーノードの設定

ワーカーノードのjoin

  • 全ワーカーノードに対して、先の kubeadm init コマンド実行結果最後に表示されたコマンドを sudo をつけて実行

sudo kubeadm join 192.168.11.201:6443 --token xua9wk.gwe59et1dix4dw1q \
        --discovery-token-ca-cert-hash sha256:4775e46c92965e5fc383731aa58429bb1d1246af22e85768bf8474f5869a4666

  • マスターノードにて kubectl get nodes を実行し、全ワーカーノードがReadyであることを確認(以下、実行例)

pi@node01:~ $ kubectl get nodes
NAME     STATUS   ROLES           AGE    VERSION
node01   Ready    control-plane   3m7s   v1.26.1
node02   Ready    <none>          111s   v1.26.1
node03   Ready    <none>          74s    v1.26.1
node04   Ready    <none>          72s    v1.26.1
pi@node01:~ $
kubectl get pod -A
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE
kube-system   coredns-787d4945fb-6brdq         1/1     Running   0          3m38s
kube-system   coredns-787d4945fb-fm7bs         1/1     Running   0          3m38s
kube-system   etcd-node01                      1/1     Running   0          3m49s
kube-system   kube-apiserver-node01            1/1     Running   0          3m49s
kube-system   kube-controller-manager-node01   1/1     Running   0          3m53s
kube-system   kube-proxy-254ht                 1/1     Running   0          3m38s
kube-system   kube-proxy-9dtjv                 1/1     Running   0          2m38s
kube-system   kube-proxy-p9qmf                 1/1     Running   0          2m1s
kube-system   kube-proxy-q24qn                 1/1     Running   0          119s
kube-system   kube-scheduler-node01            1/1     Running   0          3m49s
pi@node01:~ $
kubectl get ns -A
NAME              STATUS   AGE
default           Active   4m12s
kube-node-lease   Active   4m14s
kube-public       Active   4m14s
kube-system       Active   4m14s
pi@node01:~ $

Flannelのインストール

  • コントロールプレーンノード(node01)上にて、以下のコマンドを実行し最新版の Flannel をデプロイする。
pi@node01:~ $ kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
namespace/kube-flannel created
serviceaccount/flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
pi@node01:~ $


PodのCIDR指定を10.244.0.0/16以外にした場合、直接kubectl applyしないで、node01上にyamlをcurl保管してネットワーク指定を変更する必要がある、とflannelドキュメントにかかれている。

 

2023/2/15 注記:

ワーカノード(Raspberry Pi 3B+)を参加(kubeadm join)させた状態で、flannelを以下の手順でkubectl applyすると、 OSが突然落ちますkubectl deleteして、ワーカノードを再起動すると動作します。

flannelは全ワーカノードにコンテナをあげるので、おそらくメモリ1GBだとそれらを上げきることはできないのだとおもいます。

 

 

Swapの再有効化

Swapを無効化するのは、Kubernetesをインストールする際の前提となっている。しかし今回のワーカノードはRaspberry Pi 3B+なので、メモリが1GBしかない。

なのでここではワーカノードだけSwapを再有効化しておく。

  • sudo vi /var/lib/kubelet/config.yaml を実行し、最終行に以下の行を追加、保存
failSwapOn: false
  • 以下のコマンドを実行する
sudo systemctl daemon-reload
sudo systemctl enable dphys-swapfile
sudo systemctl start dphys-swapfile
sudo systemctl restart kubelet

 

2023/2/15 追記:

ワーカノードのSwapを再度有効化した状態でkubeadm joinをかけると以下のようなエラーがでて動作しませんでした。

pi@node02:~ $ sudo kubeadm join 192.168.11.201:6443 --token 0hfvvz.63g939bbbv4iuoqz \
        --discovery-token-ca-cert-hash sha256:e4fad29f4cadb1fc68464bbae4767e18766365d1fa8c46678301c26a1f0911c7
[preflight] Running pre-flight checks
        [WARNING Swap]: swap is enabled; production deployments should disable swap unless testing the NodeSwap feature gate of the kubelet
        [WARNING SystemVerification]: missing optional cgroups: hugetlb
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
[kubelet-check] Initial timeout of 40s passed.
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.

Unfortunately, an error has occurred:
        timed out waiting for the condition

This error is likely caused by:
        - The kubelet is not running
        - The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)

If you are on a systemd-powered system, you can try to troubleshoot the error with the following commands:
        - 'systemctl status kubelet'
        - 'journalctl -xeu kubelet'
error execution phase kubelet-start: timed out waiting for the condition
To see the stack trace of this error execute with --v=5 or higher

このあとSwap無効化してkubeadm resetしたあと、再度kubeadm joinしたらkubectl get nodeにReadyで登場するようになりました。


以上

Dockerの場合、ネットワークを管理するコンテナを隠しでいれてくれているので、意識しなくても良かったが、Kubernetesの場合はインストールする側がきちんと選んでインストールしないといけない。ドキュメントによると、ネットワークポリシーを適用するCalicoなどがあるが、このあたりはサードパーティ製になるので、Kubernetesの責務の外というかんじのようだ。

CloudStackやOpenStack、Docker (Swarm)と学習していると、オーケストレーションを実現する思想的なものの違いがなんとなく見えてくる。こういったソフトウェアの責任範囲の切り分けがどこにあるかを見極められると、理解が少し早くなるのだと思う。

 再度書きますが、Raspberry Pi 3B+ をkubernetesクラスタに加えることは、ほぼできないと考えておいたほうが良いです。インストールはできますが、それまでです。書籍の学習でちょっとうごかしてみよう的な環境でも落ちるかもしれません。そうしたらsyslogやjouralctlとにらめっこが始まってしまい、学習は中断..という風になるでしょう。そういう場合はDocker Desktop(いつまでKubernetesが動くか..)やplay with kubernetes 環境(いつまで..再び)を使いましょう。

 

 

既存アプリケーションをK8s上でコンテナ化して動かす場合の設計注意事項メモ

既存アプリをK8sなどのコンテナにして動かすには、どこを注意すればいいか..ちょっと調べたときの注意事項をメモにした。   1. The Twelve Factors (日本語訳からの転記) コードベース   バージョン管理されている1つのコードベースと複数のデプロイ 依存関係 ...