動かざることバグの如し

近づきたいよ 君の理想に

UbuntuからReadyNASのホームディレクトリをNFSマウントできない問題

環境

問題

自宅のNASではネットギアのReadyNASを使用している。特に不満はないんだが、気づいたらネットギア自体がコンシューマ向けNAS事業から撤退したので購入後しばらくしたら廃盤になってた(

まあそれはいいとして、NASLinuxでアクセスしたいので、NFSを有効化したい。以前はSambaプロトコルでお茶を濁したが、Kubernetes経由ともなればNFSが必須

thr3a.hatenablog.com

いざLinuxサーバーから試しにマウント NFSのバージョンはv4ではなくv3にした

mount -t nfs 192.168.16.xxx:/data/home/admin /mnt/nas -o vers=3

が、何回やってもホームディレクトリのマウントが「Access Denied」エラーでできない。

調査

NAS自体にSSH機能があるので有効化して ssh root@192.168.16.xxx でアクセス(パスワード認証しかできなかった。パスワードはadmin権限の初期ユーザーのパスワード)すると

Jul 24 11:57:03 mynas rpc.mountd[2775]: refused mount request from 192.168.16.xxx for /data/home/xxx (/): not exported

なるほど、アクセス拒否と。。。で/etc/exportsを見てみると

root@mynas:~# cat /etc/exports
"/home" *(insecure,insecure_locks,root_squash,anonuid=99,anongid=99,no_subtree_check,rw,sync)

一見/homeが書かれているので問題なさそうだが マウント元は /dataから始まっている。

これはReadyNASでX-RAIDが有効になっている場合は/dataがスタート地点になっているためである。

mount -t nfs (NASIPアドレス):/(NASのボリューム名)/(NASの共有名) (Linux側のマウント用ディレクトリ名)
X-RAID を使用されている場合、ボリューム名は "data" となります。
mount␣–t␣nfs␣192.168.10.1:/data/share1␣/mnt

引用元: ReadyNAS(OS6)の共有をLinuxにてnfsマウントするコマンドが判りません。 | ネットギア【NETGEAR】

つまり、/data自体が許可されていないからマウントできないと。これバグやんけ

解決方法

/etc/exportsに /dataから始まるユーザーのディレクトリパスを追加した

"/home" *(insecure,insecure_locks,root_squash,anonuid=99,anongid=99,no_subtree_check,rw,sync)
+"/data/home/xxx" *(insecure,insecure_locks,root_squash,anonuid=99,anongid=99,no_subtree_check,rw,sync)

反映 これでマウントできるようになった

exportfs -ra

Kubernetesにnfs-subdir-external-provisionerをインストール

環境

Kubernetesのデータ永続化にNFSを使ってみる。

github.com

NFSサーバーの構築

Kubernetesクラスタ以外のサーバーにインストールすること

sudo apt-get install -y nfs-kernel-server

/etc/exports に以下追記 *は流石に不味いので 172.16.0.0/24 とかCIDR指定すること

/data *(rw,sync,no_subtree_check,no_root_squash,no_all_squash)

反映

exportfs -ra

nfs-kernel-server起動

sudo systemctl enable nfs-kernel-server.service
sudo systemctl start nfs-kernel-server.service

クラスタサーバーにNFSクライアントのインストール

sudo apt install nfs-common

nfs-subdir-external-provisionerのインストール

手元のMacbookにて

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/

いざインストール

helm install nfs-test-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --set nfs.server=192.168.16.12 \
  --set nfs.path=/data/kube \
  --set storageClass.name=nfs-test

確認

❯ kubectl get storageclass
NAME         PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-client   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   3m13s

KubernetesのServiceリソースとは

環境

KubernetesのServiceリソースとは

難しい。公式ドキュメントだと

Podの集合で実行されているアプリケーションをネットワークサービスとして公開する抽象的な方法

としか書かれていない。自分としては「複数のPodに共通のIPアドレス提供するなど、ロードバランサとDNSを設定するためのリソース」で落ち着いた。

ServiceのIPアドレス確認

まずはNginxのDeploymentを作成してみる

kubectl create deploy nginx --image=nginx

そしてServiceリソースを作成

kubectl expose deploy nginx --port=80 --target-port=80

諸々割り当てられたIPアドレスを確認してみる

❯ kubectl get svc nginx
NAME    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.96.184.249   <none>        80/TCP    13s

~
❯ kubectl get ep nginx
NAME    ENDPOINTS        AGE
nginx   10.244.0.77:80   30s

~
❯ kubectl get pods --selector=app=nginx -o wide
NAME                    READY   STATUS    RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
nginx-8f458dc5b-qr65w   1/1     Running   0          3m16s   10.244.0.77   ubuntu01   <none>           <none>
  • ServiceのClusterIP: 10.96.184.249
  • PodのIPアドレス: 10.244.0.77

つまり10.96.184.249へアクセスすると、ロードバランサによって10.244.0.77へルーティングされるということになる

DNSでアクセスする

IPアドレスわかるんだからこれでOK、とはならない。KubernetesではIPアドレスの変化が激しいからいつ 10.96.184.249 が変わってもおかしくない。

そこで標準でDNSサービスが提供されている。

同じNamespace内に適当なPodを立てて中に入って検証する

kubectl create deploy debugger --image=thr3a/debugger -- sleep infinity

サービス名である「nginx」で名前を引ける。ちゃんとnginx ServiceのClusterIPである 10.96.184.249 が返ってきている。

/ # nslookup nginx
Server:     10.96.0.10
Address:    10.96.0.10#53

Name:   nginx.test.svc.cluster.local
Address: 10.96.184.249

なんで出来るのかっていうとKubernetes内にDNSサーバーがあるから。コンテナ内の /etc/resolv.conf を見てみる

/ # cat /etc/resolv.conf
search test.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

もちろんcurlでアクセス可能

/ # curl nginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
....(略

サービス名だけでアクセス出来るのは同一Namespace内のみ。それ以外は「..svc.」のようにフルパスでアクセスしなくてはいけない。

# namespaceがtest以外の場合
nslookup nginx.test.svc.cluster.local

スケールしてみるとどうなるのか

nginx Deploymentをスケールアウトさせてみるとどうなるのか。試しにレプリカセットを2に増やしてみる。

$ kubectl scale deploy nginx --replicas=2

するとエンドポイントのIPが増える。Nginxのログ見てるとわかるが、均一にアクセスが負荷分散されている。

❯ kubectl get ep nginx
NAME    ENDPOINTS                        AGE
nginx   10.244.0.205:80,10.244.0.77:80   5m46s

Headless Serviceを試す

ServiceのclusterIP(.spec.clusterIP)の値を None に設定すると、Headless Serviceになる。つまりService自体にロードバランサー機能は持たず、IPアドレスも持たなくなる。

物は試しに一度Serviceを削除してから --cluster-ip=None を追加して再作成してみる。

kubectl expose deploy nginx --port=80 --target-port=80 --cluster-ip=None

epは変わらず

❯ kubectl get ep nginx
NAME    ENDPOINTS                        AGE
nginx   10.244.0.205:80,10.244.0.77:80   5m46s

が、DNSで名前を引くとIPがPodの数だけ返ってくる。

/ # nslookup nginx
Server:     10.96.0.10
Address:    10.96.0.10#53

Name:   nginx.test.svc.cluster.local
Address: 10.244.0.205
Name:   nginx.test.svc.cluster.local
Address: 10.244.0.77

digコマンドだとIPアドレスが解決できない

digコマンドはデフォルトでは resolv.confのsearchを考慮しない。よってnslookup相当のことがしたければ

dig nginx +search

と明示的にsearchオプションを付ける必要がある。

参考リンク

KubernetesでCIDRNotAvailableエラーでうまく接続できない問題

問題

なんかMySQL構築して、サービスリソース作ってCulsterIP振ってもアクセスできない問題が発生した

設定何も間違ってないんだが???と思ってふとnodeのイベントログ見てると「CIDRNotAvailable」の文字が沢山。。。

❯ kubectl get events
LAST SEEN   TYPE     REASON             OBJECT          MESSAGE
71s         Normal   CIDRNotAvailable   node/ubuntu01   Node ubuntu01 status is now: CIDRNotAvailable
3m17s       Normal   CIDRNotAvailable   node/ubuntu02   Node ubuntu02 status is now: CIDRNotAvailable
3m3s        Normal   CIDRNotAvailable   node/ubuntu03   Node ubuntu03 status is now: CIDRNotAvailable

ServiceのIP範囲とPodのIP範囲は重複してはいけない

これが原因

まずはクラスタのIP範囲を確認

❯ kubectl cluster-info dump | grep -m 1 service-cluster-ip-range
                            "--service-cluster-ip-range=10.96.0.0/12"

そしてpodのIP範囲を確認。イキってデフォルトの設定変えたのが良くなかった。。。

❯ kubectl cluster-info dump | grep -m 1 cluster-cidr
                            "--cluster-cidr=10.100.0.0/16",

対応

最初はマスターノードの /etc/kubernetes/manifests/kube-apiserver.yaml のargs弄ってCIDR範囲被らないようにしたんだけど 結果的にクラスタが壊れてしまった。。。ので再インストールしてしまった

デフォルトのIP範囲は?

これが面倒くさいことに公式ドキュメントで明記されていない

goのパッケージのドキュメントに記載されてた。ここをソースと言っていいと思う

v1beta3 package - k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3 - Go Packages

それを見ると、

networking:
  serviceSubnet: "10.96.0.0/16"
  podSubnet: "10.244.0.0/24"
  dnsDomain: "cluster.local"

てかプリフライトの段階でエラーになってくれよkubeadmさん

参考リンク

Kubernetesクラスタを完全に初期化する方法

環境

Kubernetesの設定にミスった、istioがうまく動かなくなった。。。etc等で泣く泣く初期化するも、そもそも正常に初期化できておらず再インストール後もつまずくケースが多い。

実は 「kubeadm reset」だけだとうまく初期化できないケースもあるので完全に初期化する方法をメモ

Kubernetes完全初期化コマンド

コピペで出来るようにした

kubeadm reset --force
systemctl stop kubelet
rm -rf /etc/kubernetes/
rm -rf ~/.kube/
rm -rf /var/lib/kubelet/
rm -rf /var/lib/cni/
rm -rf /etc/cni/
rm -rf /var/lib/etcd/
iptables -F && iptables -X

サーバー再起動

以下エラーパターン

Unmounting mounted directories in "/var/lib/kubelet"で止まる

ここで延々止まってしまうパターン

# kubeadm reset --force
[preflight] Running pre-flight checks
W0727 23:16:11.210479    2357 removeetcdmember.go:84] [reset] No kubeadm config, using etcd pod spec to get data directory
[reset] No etcd config found. Assuming external etcd
[reset] Please, manually reset etcd to prevent further issues
[reset] Stopping the kubelet service
[reset] Unmounting mounted directories in "/var/lib/kubelet"
ip netns list | cut -d' ' -f 1 | xargs -n1 ip netns delete; crictl rmp -af

元記事は Reset cluster and remove node error at crictl can't remove all pods · Issue #7177 · kubernetes-sigs/kubespray

failed to stop running pod, cni plugin not initializedでエラー

kubeadm reset自体は実行できるが以下のようなログが出るときはおそらくpodが生き残ったままなので正常に初期化できていない

, error: exit status 1,  7f261f7372ce55dffaddaa71a8d347b04409e398519c9e6f572c05a1cebd6fd2: output: time="2022-07-27T23:03:37+09:00" level=fatal msg="stopping the pod sandbox \"7f261f7372ce55dffaddaa71a8d347b04409e398519c9e6f572c05a1cebd6fd2\": rpc error: code = Unknown desc = failed to destroy network for sandbox \"7f261f7372ce55dffaddaa71a8d347b04409e398519c9e6f572c05a1cebd6fd2\": cni plugin not initialized"

その場合はkubeadmでは同しようもないので「crictl」コマンドで掃除する

全pod削除 削除対象がいない場合はなぜかシンタックスエラー扱いになるが問題ない

crictl --runtime-endpoint unix:///run/containerd/containerd.sock rmp --all --force

全コンテナ削除

crictl --runtime-endpoint unix:///run/containerd/containerd.sock rm --all --force

全イメージ削除

crictl --runtime-endpoint unix:///run/containerd/containerd.sock rmi --prune

で再度Kubernetes完全初期化コマンドを実行してサーバー再起動