おうちKubernetes2024実況
始まりのツイート
こちらのリポジトリを参考にします!
この2箇所にはヒートシンクを貼れません!貼るとPoE HATと干渉して取り付け不可になります!
※万が一貼ってしまったり、PoE HATが途中で止まってしまっても両方慎重にやれば手で取り外せます
ここのヒートシンクもこんな具合にずらして貼らないとPoE HATのネジと干渉します
PoE HAT接続後はこんな感じ
こちらのRaspberry Pi Imagerを使ってMicro SDカードにUbuntuを焼いていきます。Raspberry Pi Imagerのバージョンは1.8.4でした。
デバイスはRaspberry Pi 4
OSは"Other general-purpose OS">Ubuntu>"Ubuntu Server 22.04.3 LTS(64-bit)
を選択します。
NEXTボタンを押して、EDIT SETTINGSを押します。次のOS Customization画面では
GENERALタブ
Set hostname: raspi01
Set username and password: matoruru/password
Configure wireless LAN: 無効
Set locale settings: Asia/Tokyo
SERVICESタブ
Enable SSH: Use password authentication
に設定し、SAVE。
Would you like to apply OS customization settings?にYes>Yesを押して焼きこみ開始。
ルータとPoE SwitchとRaspberry Piをこんな感じで繋いで...
パソコンを同ルータにWi-Fi接続します(LAN内で完結するのでインターネットとは繋がってなくてOK)。
arp -a
でRaspberry Piを検出できなかったので、PCのIPアドレスを確認してそれに続く連番のアドレスあたりにpingをしてから、ヒットしたものにssh接続をトライしていきます。
> ipconfig
...
IPv4 Address. . . . . . . . . . . : 192.168.11.3
...
> ping 192.168.11.4
(応答なし)
> ping 192.168.11.5
Reply from 192.168.11.5: bytes=32 time=1ms TTL=64
Reply from 192.168.11.5: bytes=32 time=1ms TTL=64
> ping 192.168.11.6
Reply from 192.168.11.6: bytes=32 time=1ms TTL=64
Reply from 192.168.11.6: bytes=32 time=1ms TTL=64
なぜ11.4が欠番になっていたのかはいまいち不明ですが、上の結果から5番と6番にSSHをトライしていきます。
> ssh matoruru@192.168.11.5
The authenticity of host '192.168.11.5 (192.168.11.5)' can't be established.
ED25519 key fingerprint is SHA256:liv0M6v+BC68760fZxqW69T3WydQ7i3vvPyXgO0TT+U.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.11.5' (ED25519) to the list of known hosts.
matoruru@192.168.11.5's password:
来ました!パスワードを打つと無事SSHログインできました。
matoruru@raspi01:~$ whoami
matoruru
matoruru@raspi01:~$ echo Hello, world!
Hello, world!
来ました!残りのRaspberry Piにも同じように接続します。
追記(同日)
for /l %i in (0,1,50) do ping -w 1 -n 1 192.168.11.%i
でarpテーブルを更新して、各IPを発見できます。50は255でもいいけどそんなに大きい数字が振られそうでなければ適宜減らしましょう。11も環境により異なります。
追記(2/2)
SSHのパスワード認証を無効にして、公開鍵認証に切り替えました。以下に流れを残しておきます。
クライアントで以下のコマンドを実行
ssh-keygen -t ed25519 # 全部エンターで飛ばしてOK(気になる方は適宜入力)
ssh-copy-id -i ~/.ssh/id_ed25519.pub matoruru@100.64.1.101
以下の内容をクライアント上の~/.ssh/config
に追加する(なければ新規作成)
Host raspi01
HostName 100.64.1.101
User matoruru
IdentityFile ~/.ssh/id_ed25519
今後はssh raspi01
で安全に接続できる!
パスワード認証を無効にする(オプション)
この手順は必須ではないですが、認証方法を公開鍵認証のみにすることでセキュリティ強度を上げることができます。
⚠️もし失敗した場合リモート接続することが大変困難になるため、万が一の時に作業を切り戻せるように、実施する場合は必ず別コンソールでSSH接続を保持したまま行なうこと⚠️
マスターノードのマシンにSSH接続して、/etc/ssh/sshd_config.d/50-cloud-init.conf
のPasswordAuthentication yes
をPasswordAuthentication no
に書き換えて保存します。その後sudo systemctl restart sshd
でSSHのサービスを再起動します。
ここで本当にパスワード認証が無効になっているのか確かめたいですが、公開鍵認証を設定済みなのでパスワードの入力を求められることなくログインしてしまいます。そのため、一旦秘密鍵の名前を変えて無効化してからパスワードでのログインを試みてみます。
> mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519_
> ssh raspi01
ssh raspi01
no such identity: /Users/matoruru/.ssh/id_ed25519: No such file or directory
matoruru@100.64.1.101: Permission denied (publickey).
無事パスワードを求められることなく弾かれました!今後は秘密鍵を所持していないとマスターノードのマシンにログインできません!
このままだと公開鍵認証もできないので、先ほど壊した秘密鍵ファイルを元に戻しておきます。
mv ~/.ssh/id_ed25519_ ~/.ssh/id_ed25519
とりあえず全端末の起動・接続確認が済んだのでクラスタを組み上げていきます
電源ぶち切るのはお行儀が悪いので各端末にログインしてsudo poweroff
でおねんねしてもらいます。
もし4台目が来た場合は上に積み上げていくことになるので、下からホスト名raspi01、raspi02、..と積み上げていきます
底面になるアクリル板だけファンの穴がないので留意しておく
ネジ止めを忘れていたのでPoE HATを外して付け直し..
1段目のネジ止め完了
上から見た図。ファンの一部を覆ってしまっている。結構ずれるなあ🤔
2段目3段目もやっていきます
クラスタケースの組み立て完了!✨
配線は課題しかし盆栽
どの端末に接続しているかわからなくなった時は、適当にキーボード連打してキー入力を送ればLANケーブルのあたりが点滅するので特定できます
ここからはついにKubernetesの設定に取り掛かろうと思う、がその前にごはん...
固定IPの設定方法
現在のIPとサブネットを調べる
> ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether d8:3a:dd:88:37:4c brd ff:ff:ff:ff:ff:ff
inet 100.64.1.53/22 metric 100 brd 100.64.3.255 scope global dynamic eth0
valid_lft 603768sec preferred_lft 603768sec
inet6 2400:4050:a743:5700:da3a:ddff:fe88:374c/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 2591812sec preferred_lft 604612sec
inet6 fe80::da3a:ddff:fe88:374c/64 scope link
valid_lft forever preferred_lft forever
より、現在のIPは100.64.1.53
でサブネットマスクは/22
。
ネームサーバを調べる
> cat /etc/resolv.conf | grep nameserver
nameserver 127.0.0.53
デフォルトゲートウェイを調べる
< ip route | grep default
default via 100.64.1.1 dev eth0 proto dhcp src 100.64.1.54 metric 100
より、100.64.1.1
。
編集作成する
集めた情報を使って設定ファイルをデフォルトの/etc/netplan/50-cloud-init.yaml
を弄るのはお行儀が良くないらしいので、99-fixed-ip.yaml
を作成してデフォルト設定を上書きすることにする。
> cd /etc/netplan
> sudo vim 99-fixed-ip.yaml
内容は以下の通り。
network:
ethernets:
eth0:
dhcp4: false
addresses: [192.168.11.101/22]
nameservers:
addresses: [1.1.1.1]
routes:
- to: default
via: 192.168.11.1
version: 2
このあとsudo reboot
で再起動すれば同ラズパイ端末のIPが100.64.1.101
に固定される。
ほかの端末にも同様に102、103の連番で設定していく。
> arp -a
Interface: 100.64.1.31 --- 0x11
Internet Address Physical Address Type
...
100.64.1.101 d8-3a-dd-88-37-4c dynamic
100.64.1.102 d8-3a-dd-88-35-9d dynamic
100.64.1.103 d8-3a-dd-88-5b-59 dynamic
...
IPアドレスを固定できた!
インターネットにアクセスできるかどうかも確認しておこう。
curl google.com
スワップを無効化する
sudo swapoff -a
IPv4フォワーディングを有効化し、iptablesからブリッジされたトラフィックを見えるようにする
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# この構成に必要なカーネルパラメーター、再起動しても値は永続します
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 再起動せずにカーネルパラメーターを適用
sudo sysctl --system
pstree
でinitシステムを確認
matoruru@raspi01:~$ pstree
systemd─┬─2*[agetty]
├─bluetoothd
├─cron
├─dbus-daemon
├─hciattach
├─irqbalance───{irqbalance}
├─multipathd───6*[{multipathd}]
├─networkd-dispat
├─rsyslogd───3*[{rsyslogd}]
├─snapd───13*[{snapd}]
├─sshd───sshd───sshd───bash───pstree
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-network
├─systemd-resolve
├─systemd-timesyn───{systemd-timesyn}
├─systemd-udevd
├─unattended-upgr───{unattended-upgr}
└─wpa_supplicant
systemdでした。
言い忘れていたけれどまずはMasterノードから構築してます
containerd入れていきます
aptで入れていきます
パッケージの更新
sudo apt-get update && sudo apt-get upgrade -y
containerdのインストール
sudo apt-get install containerd -y
containerd config default
のコマンドでcontainerdのデフォルト設定を取得できるようなので、
sudo sh -c 'mkdir /etc/containerd && containerd config default > /etc/containerd/config.toml'
で当該ファイルを作成。
下記のコマンドを使って、上で生成したファイルのSystemdCgroup = false
をSystemdCgroup = true
に変更。
sudo sed -ie 's/\(\s* SystemdCgroup = \)false.*/\1true/' /etc/containerd/config.toml
そして
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
でcontainerdを自動起動設定を有効にし、起動する。
kubeadm、kubelet、kubectlをインストールする
このページに従う。
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
kubeletが使用するcgroupドライバの設定
Ubuntuではsystemdを使っているので以下のように設定する。
cat << EOF | sudo tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd
EOF
kubeletの再起動
sudo systemctl daemon-reload
sudo systemctl restart kubelet
ワーカーノードの1台にもkubeadm、kubelet、kubectlをインストールした
マスターノードで
sudo kubeadm init
を実行。クラスタ起動まで1〜2分くらいかかる。
kubectlをnon-rootユーザで実行できるように以下のコマンドを実行する
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
試しにkubectlを実行
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
raspi01 NotReady control-plane 3m14s v1.28.2
動いた!!!🎉
ノード追加のため、ワーカーノードからkubeadm join
を実行(トークンやIPは環境依存なのでここでは省略)。
マスターノードでkubectl get nodes
を実行してノードの参加状況を確認。
kubectl get nodes
NAME STATUS ROLES AGE VERSION
raspi01 NotReady control-plane 16m v1.28.2
raspi02 NotReady <none> 63s v1.28.2
入ってきた!!!
ただ、なぜか両ノードともStatusがNotReady・・・。
原因調査のためkubectl describe nodes raspi01
でマスターノードの状態を確認したところ、
container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
というメッセージを確認できた。
CNIプラグインをインストールする必要があるみたい。ドキュメント見落としかな・・
マスターノードで以下のコマンドを実行してCNIプラグインFlannelをインストール。
> 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
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
raspi01 Ready control-plane 32m v1.28.2
raspi02 Ready <none> 16m v1.28.2
Readyになった!!!
FlannelからCalicoへ移行
Flannelはだいぶ前にKubernetesのドキュメントから消えたらしく、Network Policyにも非対応で非推奨のように見えるので、一旦クラスタを削除して別のCNIプラグインを入れたい。
Calicoを入れようと思う(FlannelのGitHubにもNetwork Policy使いたいならCalico等にしろって書いてある)
nodeの削除 (コントロールプレーンノードで行う)
> kubectl drain raspi02 --delete-local-data --force --ignore-daemonsets
> kubectl delete node raspi02
node "raspi02" deleted
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
raspi01 Ready control-plane 2d v1.28.2
kubeadmの設定のリセット(ワーカーノードのマシンで行う)
sudo kubeadm reset
sudo rm -rf /etc/cni/net.d/
kubeadmの設定のリセット&初期化(コントロールプレーンのマシンで行う)
sudo kubeadm reset
sudo rm -rf /etc/cni/net.d/
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
ワーカーノードの追加
kubeadm init
の実行ログに表示されたkubeadm join
コマンドをワーカーノードのマシン上で実行しておく。
ノードの確認
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
raspi01 NotReady control-plane 2m9s v1.28.2
raspi02 NotReady <none> 74s v1.28.2
初期化完了!
Calico CNI Pluginをインストールする
に従う。
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
# 10.244.0.0/16は`kubeadm init`のときに使ったランダムなCIDR
curl -s https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml | sed 's/192.168.0.0\/16/10.244.0.0\/16/' | kubectl create -f -
インストールしたPodがRunningになるまで待つ
Statusを眺めるコマンド
watch kubectl get pods -n calico-system
いつまで経ってもRunningにならない・・・
> kubectl -n calico-system get all
NAME READY STATUS RESTARTS AGE
pod/calico-kube-controllers-566964964d-g4rv2 0/1 Pending 0 16m
pod/calico-node-l9wh7 1/1 Running 0 37m
pod/calico-typha-c96b57dcb-sfnhc 1/1 Running 0 37m
pod/csi-node-driver-fmc8p 0/2 ContainerCreating 0 37m
こちらの記事を参考にlinux-modules-extra-raspi
をインストールし、ラズパイを再起動してみる(ワーカーノードのマシンにも必要)。
sudo apt install -y linux-modules-extra-raspi
sudo reboot
> kubectl get pods -n calico-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-566964964d-g4rv2 1/1 Running 0 22m
calico-node-l9wh7 1/1 Running 1 (2m8s ago) 43m
calico-typha-c96b57dcb-sfnhc 1/1 Running 1 (2m8s ago) 43m
csi-node-driver-fmc8p 2/2 Running 0 43m
ついにCalicoをデプロイできた!!
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
raspi01 Ready control-plane 46m v1.28.2
ノードもReadyになった。
ワーカーノードをjoinさせたいが、kubeadm起動直後のjoinコマンドをロストしたため、以下のコマンドで再生成してもらう(生成されたコマンドをワーカーノードのマシンで実行する)。
kubeadm token create --print-join-command
ワーカーノードを追加してしばらくするとワーカーノード側にも自動的にcalicoのPodが配置されて、ノードもReadyになる。
> kubectl -n calico-system get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
calico-kube-controllers-566964964d-g4rv2 1/1 Running 0 39m 26.125.95.195 raspi01 <none> <none>
calico-node-j9nkc 1/1 Running 1 (2m29s ago) 12m 100.64.1.102 raspi02 <none> <none>
calico-node-l9wh7 1/1 Running 1 (18m ago) 60m 100.64.1.101 raspi01 <none> <none>
calico-typha-c96b57dcb-sfnhc 1/1 Running 1 (18m ago) 60m 100.64.1.101 raspi01 <none> <none>
csi-node-driver-fmc8p 2/2 Running 0 60m 26.125.95.196 raspi01 <none> <none>
csi-node-driver-xkgtw 2/2 Running 0 12m 26.125.133.129 raspi02 <none> <none>
試しにサンプル用のDeploymentファイルを使ってNginxのPodを5つデプロイしてみる。
kubectl apply -f https://raw.githubusercontent.com/matoruru/playground/main/playground-nginx-deployment-sample/multi-nginx-pods.yaml
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-687f7bcd54-2dfsd 1/1 Running 0 83s 26.125.95.7 raspi03 <none> <none>
nginx-deployment-687f7bcd54-7bfxq 1/1 Running 0 83s 26.125.133.133 raspi02 <none> <none>
nginx-deployment-687f7bcd54-ms4b9 1/1 Running 0 83s 26.125.133.132 raspi02 <none> <none>
nginx-deployment-687f7bcd54-n2v7m 1/1 Running 0 83s 26.125.95.9 raspi03 <none> <none>
nginx-deployment-687f7bcd54-tb9pz 1/1 Running 0 83s 26.125.95.8 raspi03 <none> <none>
ちゃんと各ワーカーノードに分散配置されていることがわかる!
ここまでのゴチャついた作業履歴は別記事にまとめて近々投稿します😀
Kustomizeを使ってMetalLBをインストールした。
起こった問題はここにメモしている。
MetalLBでロードバランサーは導入済みだが、各サービスごとにExternal IPを振るのはイケてない感じがするので、Nginx Ingress Contrllerを導入してそこへトラフィックを集約、そこからIngressのルールに従って各サービスへ振り分ける構成にする
Nginx Ingress Controllerのインストールは簡単で、単にhttps://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml
をapplyするだけ。
Kustomize形式でGitOpsなどしたい場合はKustomizationファイルのresourcesから読み込む。
k8s.matoru.ru
をAWS Route 53のDNSに設定したい
の記事に従って、Route 53のPublicなHosted zoneとしてmatoru.ruを作成。
Hosted zonematoru.ru
にAレコードでサブドメインk8s.
を設定してみた。トラフィック先は192.168.11.200
。WiFiに接続している時だけ、インターネットを経由してクラスタにアクセスできることを試したい
> dig k8s.matoru.ru
; <<>> DiG 9.16.1-Ubuntu <<>> k8s.matoru.ru
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22856
;; flags: qr rd ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;k8s.matoru.ru. IN A
;; ANSWER SECTION:
k8s.MATORU.ru. 0 IN A 192.168.11.200
きた!!
cert-managerでNginx Ingress ControllerをTLS対応させてみる
リソース監視のためにPixieを導入してみた!Self-hosted版もあったがまずはお試しのために導入が簡単なクラウド版を使ってみる。
kubeadmでクラスタを作成するとPersistentVolumeとStorageClassが作成されてないので、StatefulSetを作れない。それぞれ作成する必要がある。手順↓
- AzureのStorage Account作成