動かざることバグの如し

近づきたいよ 君の理想に

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完全初期化コマンドを実行してサーバー再起動

Kubernetes Dashboardをインストールする

環境

先にmetrics-serverをインストールして kubectl top node コマンドが使えるようにしておく必要がある(Dashboardはただのビューアーなので

やりたいこと

自分のクラスタ上でダッシュボードを見てニヤニヤしたい

github.com

インストール

例によって公式README参照

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.0/aio/deploy/alternative.yaml

アクセス

まずはポートフォワードで画面を見る

kubectl port-forward service/kubernetes-dashboard 8000:80 -n kubernetes-dashboard

で、 http://localhost:8000/ へアクセスできれば成功

起動パラメータ編集

デフォルトだとhttps経由じゃないとアクセスできない。が、alternative版はそもそもhttpsをサポートしてないのでこのままだと一生ポートフォワードしないと見れない。

そこでパラメータを以下のように編集する

kubectl -n kubernetes-dashboard edit deploy kubernetes-dashboard
spec:
  containers:
  - args:
    - --namespace=kubernetes-dashboard
+    - --disable-settings-authorizer
+    - --enable-insecure-login
+    - --insecure-bind-address=0.0.0.0
+    - --enable-skip-login

でデプロイ再起動

確認

$ kubectl -n kubernetes-dashboard get deploy kubernetes-dashboard -o json| jq '.spec.template.spec.containers[0].args'
[
  "--namespace=kubernetes-dashboard",
  "--disable-settings-authorizer",
  "--enable-insecure-login",
  "--insecure-bind-address=0.0.0.0",
  "--enable-skip-login"
]

認証無しでログインしたい場合

とりあえず楽に見たかった。 --enable-skip-login を追加した場合、以下のように「Skip」ボタンが表示されクリックするとそのままダッシュボードの画面に飛べる

スクリーンショット 2022-07-29 9.02.30.png

が、

replicationcontrollers is forbidden: User "system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard" cannot list resource "replicationcontrollers" in API group "" in the namespace "default"

のようにエラーになってしまい各メトリクスを見る方法がわからなかった。

トークンで認証する場合

公式ドキュメントを参考

dashboard/creating-sample-user.md at master · kubernetes/dashboard

ダッシュボード用のサービスアカウント作成

kubectl create serviceaccount -n kubernetes-dashboard admin-user

ロール作成

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
EOF

トークン生成

kubectl -n kubernetes-dashboard create token admin-user

表示されたトークンでログインできる

ロードバランサー経由でアクセスしたい

結果から言うと成功していない

ロードバランサータイプに変更する

$ kubectl patch svc kubernetes-dashboard -n kubernetes-dashboard -p '{"spec": {"type": "LoadBalancer"}}'

が、「--enable-insecure-login」を有効にしてもログインできない。。。

バグと言うか仕様っぽいな

Dashboard login not authorized while --enable-insecure-login and protocolHttp options are set to true · Issue #5612 · kubernetes/dashboard

参考リンク