hidemium's blog

日々学んだことをアウトプットする。

Cluster API Provider vSphereとArgo CDを使ったKubernetesクラスタの管理

前回、Cluster API Provider vSphereを使ってKubernetesクラスタを作成してみました。今回は、Cluster API Provider vSphereとArgo CDを使ってKubernetesクラスタを管理する方法を試してみたいと思います。

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Kubespray
  • kubernetes v1.25.5
  • Cluster API v1.3.2
    • clusterctl v1.3.2
    • cluster-api-provider-vsphere v1.5.1
    • ubuntu-2004-kube-v1.25.5.ova
  • Argo CD v2.6.0

Argo CDについて

Argo CD は、Kubernetes のための宣言的な GitOps 継続的デリバリーツールです。GitOpsパターンに従って、望ましいアプリケーション状態を定義するための信頼できる情報源(source of truth)として Git リポジトリを使用します。

Argo CDで利用できるファイルは以下のようなものがあります。

  • kustomize
  • helmチャート
  • jsonnetファイル
  • manifestsファイル

Gitリポジトリに、アプリケーションの定義ファイルを用意しておくことで、クラスタに自動デプロイすることができます。

Cluster APIとArgo CDを組み合わせて使うことで、Kubernetesクラスタの管理とアプリケーションの管理をGitリポジトリで一元管理することができるようになります。

Argo CDのインストール

前回作成した、management clusterに以下のようにArgo CDをインストールします。

$ kubectl create namespace argocd
$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

以下のように、Argo CD CLI をダウンロードします。

$ curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
$ sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
$ rm argocd-linux-amd64

デフォルトだと、Argo CD API サーバーは外部 IPで公開されていないため、API サーバーにアクセスするためにIngressを設定します。

$ vi ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  tls:
  - hosts:
      - argocd.kube.home.lab
    secretName: ingress-tls
  rules:
  - host: argocd.kube.home.lab
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              name: https
$ kubectl apply -f ingress.yaml

ブラウザから https://argocd.kube.home.lab にアクセスできることを確認します。

ブラウザでパスワードが求めれるため、以下のコマンドでパスワードを取得します。

$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

以下のように、CLI を使用してログインします。

$ argocd login argocd.kube.home.lab

動作確認

Argo CDのリポジトリにguestbookというアプリケーションが用意されているため、こちらを使って動作確認を行ってみます。

以下のコマンドでアプリケーションの登録を行ってみます。

$ argocd app create guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-server https://kubernetes.default.svc --dest-namespace default

guestbookのアプリケーションは自動同期の設定がされていないため、アプリケーションの登録後に以下のように同期を行います。同期を行うことで、リポジトリからマニフェストを取得し、kubectl applyマニフェストを実行します。

$ argocd app sync guestbook

Health StatusがHealthyとなっており、正常に同期されたことが分かります。

$ argocd app get guestbook
Name:               argocd/guestbook
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          default
URL:                https://argocd.kube.home.lab/applications/guestbook
Repo:               https://github.com/argoproj/argocd-example-apps.git
Target:             
Path:               guestbook
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        Synced to  (53e28ff)
Health Status:      Healthy

プライベートリポジトリの登録

Argo CDを利用するために、Gitリポジトリが必要なため、プライベートリポジトリとしてBitbucketのリポジトリを用意します。

BitbucketのPersonal settingsでSSH keysにsshの公開鍵を登録しておきます。

以下のように、ssh秘密鍵を利用し、プライベートリポジトリを登録します。

$ argocd repo add git@bitbucket.org:<username>/argocd-apps.git --ssh-private-key-path ~/.ssh/id_rsa

リポジトリマニフェストファイルを追加

リポジトリに以下のようにマニフェストファイルを配置します。

$ tree
.
├── app
│   └── workload-nginx-app.yaml
├── capi-cluster
│   └── cluster.yaml
├── management
│   └── capi-cluster-app.yaml
├── README.md
└── workload
    └── nginx
        ├── deployment.yaml
        └── service.yaml

ファイルについて解説していきます。

こちらは、Argro CDのApplicationを追加するマニフェストファイルになります。動作確認では、argocdコマンドで行っていましたが、マニフェストファイルでも管理することができます。management clusterにデプロイする形となります。

$ vi management/capi-cluster-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: capi-cluster
  namespace: argocd
spec:
  destination:
    namespace: default
    server: 'https://kubernetes.default.svc'
  source:
    path: capi-cluster
    repoURL: 'git@bitbucket.org:<username>/argocd-apps.git'
    targetRevision: HEAD
  project: default
  syncPolicy:
    automated: {}

capi-cluster/cluster.yamlには、clusterctl で生成したworkload clusterのcluster.yamlの中身をそのまま貼り付けます。

$ clusterctl generate cluster vsphere-quickstart \
    --infrastructure vsphere \
    --kubernetes-version v1.25.5 \
    --control-plane-machine-count 1 \
    --worker-machine-count 3 > cluster.yaml

こちらは、動作確認用のnginxのサービスをworkload clusterへデプロイするための、Argro CDのApplicationのマニフェストファイルになります。

serverには、workload clusterのcontrole planeのエンドポイントのURLを指定しておきます。

$ vi app/workload-nginx-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx
  namespace: argocd
spec:
  destination:
    namespace: default
    server: 'https://10.0.50.40:6443'
  source:
    path: workload/nginx
    repoURL: 'git@bitbucket.org:<username>/argocd-apps.git'
    targetRevision: HEAD
  project: default
  syncPolicy:
    automated: {}

動作確認用のnginxのサービスの設定になります。

$ vi workload/nginx/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
$ vi workload/nginx/service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx

workload clusterのインストール

以下のコマンドで、workload clusterをインストールするための、Argo CDのApplicationを設定します。

$ kubectl apply -f management/capi-cluster-app.yaml

Argo CDのUIかコマンドでCluster APIによりworkload clusterのインストールされている様子を確認します。

$ argocd app get argocd/capi-cluster

capi-clusterのステータスがProgressingのままとなっていますが、こちらはCNIのインストールが完了していたいためになります。

workload clusterにCNIのインストールを行います。

まずは、workload clusterにアクセスするためのconfigファイルを以下のように生成します。

$ clusterctl get kubeconfig vsphere-quickstart > vsphere-quickstart.kubeconfig

以下のようにcalicoをインストールします。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml

capi-clusterのステータスがHealthyとなれば、workload clusterのインストールの完了となります。

$ argocd app get argocd/nginx
Name:               argocd/capi-cluster
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          default
URL:                https://argocd.kube.home.lab/applications/capi-cluster
Repo:               git@bitbucket.org:<username>/argocd-apps.git
Target:             HEAD
Path:               capi-cluster
SyncWindow:         Sync Allowed
Sync Policy:        Automated
Sync Status:        Synced to HEAD (1c29848)
Health Status:      Healthy

ArgoCDのUI上だと以下のように見えます。

workload clusterでの動作確認

ArgoCDでworkload clusterを扱えるようにするために、clusterの登録を行います。

workload clusterのコンテキスト名を取得します。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl config get-contexts
CURRENT   NAME                                          CLUSTER              AUTHINFO                   NAMESPACE
*         vsphere-quickstart-admin@vsphere-quickstart   vsphere-quickstart   vsphere-quickstart-admin

以下のようにArgoCDにworkload clusterを新クラスタとして登録します。

$ argocd cluster add vsphere-quickstart-admin@vsphere-quickstart --insecure --kubeconfig ./vsphere-quickstart.kubeconfig

以下のコマンドで、動作確認用のnginxのサービスをworkload clusterへデプロイするための、Argro CDのApplicationを設定します。

$ kubectl apply -f app/workload-nginx-app.yaml

nginxのステータスがHealthyとなれば、nginxのインストールの完了となります。

$ argocd app get argocd/nginx
Name:               argocd/nginx
Project:            default
Server:             https://10.0.50.40:6443
Namespace:          default
URL:                https://argocd.kube.home.lab/applications/nginx
Repo:               git@bitbucket.org:<username>/argocd-apps.git
Target:             HEAD
Path:               workload/nginx
SyncWindow:         Sync Allowed
Sync Policy:        Automated
Sync Status:        Synced to HEAD (1c29848)
Health Status:      Healthy

おわりに

ArgoCDを使ってCluster APIを使ったKubernetesクラスタの管理を試してみました。ArgoCDへ作成したworkload clusterのクラスタ登録をどうやって自動で行うかなど課題がありますが、作りこんでいけば、CNIのインストールや、vSphere CSIの再インストールなども自動化して、Gitで管理できるようになるのではと思っています。

参考

Cluster API Provider vSphereでvSphere CSI ControllerがCrashLoopBackOffとなる事象を追ってみる

前回、Cluster API Provider vSphereを使ってKubernetesクラスタを作成してみました。今回は、Kubernetesクラスタを作成した際にvSphere CSI ControllerがCrashLoopBackOffとなる事象について原因を追ってみたので書いてみようと思います。

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Kubespray
  • kubernetes v1.25.5
  • Cluster API v1.3.2
    • clusterctl v1.3.2
    • cluster-api-provider-vsphere v1.5.1
    • ubuntu-2004-kube-v1.25.5.ova
  • vSphere CPI v1.18.1
  • vSphere CSI v2.1.0

発生した事象について

前回、Cluster APIを使ってKubernetesクラスタを作成し、calicoのインストールが完了後のpodの状態がこちらです。vsphere-csi-controller が CrashLoopBackOff となっています。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -A
NAMESPACE     NAME                                               READY   STATUS             RESTARTS      AGE
kube-system   calico-kube-controllers-74677b4c5f-m77w8           1/1     Running            0             16m
kube-system   calico-node-7bcpz                                  1/1     Running            0             16m
kube-system   calico-node-w7fs9                                  1/1     Running            0             16m
kube-system   calico-node-wckhz                                  1/1     Running            0             16m
kube-system   calico-node-zlc5b                                  1/1     Running            0             16m
kube-system   coredns-565d847f94-chn5z                           1/1     Running            0             21m
kube-system   coredns-565d847f94-dn4zq                           1/1     Running            0             21m
kube-system   etcd-vsphere-quickstart-d7r7k                      1/1     Running            0             21m
kube-system   kube-apiserver-vsphere-quickstart-d7r7k            1/1     Running            0             21m
kube-system   kube-controller-manager-vsphere-quickstart-d7r7k   1/1     Running            0             21m
kube-system   kube-proxy-br9fn                                   1/1     Running            0             20m
kube-system   kube-proxy-cvfzq                                   1/1     Running            0             20m
kube-system   kube-proxy-qxlj2                                   1/1     Running            0             20m
kube-system   kube-proxy-rmzww                                   1/1     Running            0             21m
kube-system   kube-scheduler-vsphere-quickstart-d7r7k            1/1     Running            0             21m
kube-system   kube-vip-vsphere-quickstart-d7r7k                  1/1     Running            0             21m
kube-system   vsphere-cloud-controller-manager-ctkr7             1/1     Running            0             20m
kube-system   vsphere-cloud-controller-manager-hl5w2             1/1     Running            0             20m
kube-system   vsphere-cloud-controller-manager-r9dhk             1/1     Running            0             20m
kube-system   vsphere-csi-controller-5d6484644d-nqwlh            4/5     CrashLoopBackOff   8 (37s ago)   21m
kube-system   vsphere-csi-node-h9p2x                             3/3     Running            0             21m
kube-system   vsphere-csi-node-hbgsl                             3/3     Running            0             20m
kube-system   vsphere-csi-node-tr99v                             3/3     Running            0             20m
kube-system   vsphere-csi-node-xk2jq                             3/3     Running            0             20m

ログの確認

まずは、vsphere-csi-controllerの状態について以下のコマンドで確認してみます。ログを見ると、以下のようなエラーが発生します。エラーは、vCenter名の証明書が不明な認証局によって署名されているためです。デフォルトでは、vCenterは証明書を発行する認証局(VMCA)があり、自己署名証明書を作成しています。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl logs vsphere-csi-controller-5d6484644d-nqwlh -c vsphere-csi-controller -n kube-system
level=fatal msg="grpc failed" error="Post https://vcsa-1.home.lab:443/sdk: x509: certificate signed by unknown authority"

自己署名証明書を使うオプションががあり、csi-vsphere.conf で insecure-flag を利用する必要があります。

Create a Kubernetes Secret for vSphere Container Storage Plug-in

Cluster APIを使ってKubernetesクラスタで、vSphere CSIcsi-vsphere.confの設定がどのようになっているかを確認するには、以下のコマンドを実行します。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl edit secret csi-vsphere-config -n kube-system
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  csi-vsphere.conf: <設定値>
kind: Secret
metadata:
  creationTimestamp: "<date>"
  name: csi-vsphere-config
  namespace: kube-system
  resourceVersion: "3908"
  uid: 2b96cb9c-5444-425a-a625-f1b6c4ca5754
type: Opaque

csi-vsphere.confにbase64エンコードされた設定値が入っており、値を確認してみます。設定値を見ると、 insecure-flag が設定されていないことが分かります。

$ echo "設定値" | base64 -d
[Global]
cluster-id = "default/vsphere-quickstart"

[VirtualCenter "vcsa-1.home.lab"]
user = "vCenter username"
password = "vCenter password"
datacenters = "datacenter name"

[Network]
public-network = "portgroup name"

csi-vsphere.confの定義ファイルに、 insecure-flag = "true" を追加し、base64でデコードします。

$ vi csi-vsphere.conf
[Global]
cluster-id = "default/vsphere-quickstart"

[VirtualCenter "vcsa-1.home.lab"]
insecure-flag = "true"
user = "vCenter username"
password = "vCenter password"
datacenters = "datacenter name"

[Network]
public-network = "portgroup name"
$ base64 csi-vsphere.conf
<出力値>

base64でデコードした値をsecretのcsi-vsphere.confに入力し、保存します。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl edit secret csi-vsphere-config -n kube-system
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  csi-vsphere.conf: <値を入れ替え>
kind: Secret
metadata:
  creationTimestamp: "<date>"
  name: csi-vsphere-config
  namespace: kube-system
  resourceVersion: "3908"
  uid: 2b96cb9c-5444-425a-a625-f1b6c4ca5754
type: Opaque

podを再起動し、vsphere-csi-controllerが起動してくることを確認します。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl rollout restart deploy vsphere-csi-controller -n kube-system

再起動後のpodの状態はこちらです。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -A
NAMESPACE     NAME                                               READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-74677b4c5f-87sdx           1/1     Running   0          66m
kube-system   calico-node-26nls                                  1/1     Running   0          66m
kube-system   calico-node-kgnzr                                  1/1     Running   0          66m
kube-system   calico-node-qhmm6                                  1/1     Running   0          66m
kube-system   calico-node-rpsbk                                  1/1     Running   0          66m
kube-system   coredns-565d847f94-drphk                           1/1     Running   0          68m
kube-system   coredns-565d847f94-qc66x                           1/1     Running   0          68m
kube-system   etcd-vsphere-quickstart-9nzbv                      1/1     Running   0          68m
kube-system   kube-apiserver-vsphere-quickstart-9nzbv            1/1     Running   0          68m
kube-system   kube-controller-manager-vsphere-quickstart-9nzbv   1/1     Running   0          68m
kube-system   kube-proxy-9ntsx                                   1/1     Running   0          68m
kube-system   kube-proxy-dqrpj                                   1/1     Running   0          67m
kube-system   kube-proxy-v5j24                                   1/1     Running   0          67m
kube-system   kube-proxy-zqp7h                                   1/1     Running   0          67m
kube-system   kube-scheduler-vsphere-quickstart-9nzbv            1/1     Running   0          68m
kube-system   kube-vip-vsphere-quickstart-9nzbv                  1/1     Running   0          68m
kube-system   vsphere-cloud-controller-manager-4fmp2             1/1     Running   0          67m
kube-system   vsphere-cloud-controller-manager-r7sq7             1/1     Running   0          67m
kube-system   vsphere-cloud-controller-manager-z6dcx             1/1     Running   0          67m
kube-system   vsphere-csi-controller-d695747dc-qhjqs             5/5     Running   0          35s
kube-system   vsphere-csi-node-98z65                             3/3     Running   0          67m
kube-system   vsphere-csi-node-jjx8r                             3/3     Running   0          67m
kube-system   vsphere-csi-node-rfg6w                             3/3     Running   0          67m
kube-system   vsphere-csi-node-stlbj                             3/3     Running   0          68m

起動はできましたが、vsphere-csi-controllerのバージョンを確認してみると、v2.1.0を利用していることが分かります。

VMware vSphere Container Storage Plug-inのリリースノートを見ると、v2.1.0はKubernetes v1.19までしかサポートしていないことが分かります。

VMware vSphere Container Storage Plug-in 2.1 Release Notes

この問題については、こちらのissueでも議論されているようでした。

CAPV CSI driver isn't passing TLS thumbprint · Issue #1162 · kubernetes-sigs/cluster-api-provider-vsphere · GitHub

vSphere CSI Controllerがデプロイされる動作

Cluster API Provider vSphereで、vSphere CSI Controllerが自動的にデプロイされている理由としては、ClusterResourceSetが利用されています。

ClusterResourceSetは、workload clusterを作成した際に、workload clusterに追加のコンポーネントを自動的にインストールできるようにする実験的な機能です。

CNIを毎回コマンドでインストールしているため、自動でインストールされると便利ではあります。

ClusterResourceSetの設定内容について確認してみようと思います。

workload clusterを作成する際に、cluster.yamlを作成しましたが、こちらの中身を見てみます。

$ clusterctl generate cluster vsphere-quickstart \
    --infrastructure vsphere \
    --kubernetes-version v1.25.5 \
    --control-plane-machine-count 1 \
    --worker-machine-count 3 > cluster.yaml

ClusterResourceSetのリソースの中に、csi-vsphere-configがあることが分かります。csi-vsphere-configの中身には、insecure-flag が設定されていないことが分かります。

$ vi cluster.yaml
---
apiVersion: addons.cluster.x-k8s.io/v1beta1
kind: ClusterResourceSet
metadata:
  labels:
    cluster.x-k8s.io/cluster-name: vsphere-quickstart
  name: vsphere-quickstart-crs-0
  namespace: default
spec:
  clusterSelector:
    matchLabels:
      cluster.x-k8s.io/cluster-name: vsphere-quickstart
  resources:
  - kind: Secret
    name: vsphere-csi-controller
  - kind: ConfigMap
    name: vsphere-csi-controller-role
  - kind: ConfigMap
    name: vsphere-csi-controller-binding
  - kind: Secret
    name: csi-vsphere-config
  - kind: ConfigMap
    name: csi.vsphere.vmware.com
  - kind: ConfigMap
    name: vsphere-csi-node
  - kind: ConfigMap
    name: vsphere-csi-controller
  - kind: Secret
    name: cloud-controller-manager
  - kind: Secret
    name: cloud-provider-vsphere-credentials
  - kind: ConfigMap
    name: cpi-manifests
:
---
apiVersion: v1
kind: Secret
metadata:
  name: csi-vsphere-config
  namespace: default
stringData:
  data: |
    apiVersion: v1
    kind: Secret
    metadata:
      name: csi-vsphere-config
      namespace: kube-system
    stringData:
      csi-vsphere.conf: |+
        [Global]
        cluster-id = "default/vsphere-quickstart"

        [VirtualCenter "vcsa-1.home.lab"]
        user = "vCenter username"
        password = "vCenter password"
        datacenters = "datacenter name"

        [Network]
        public-network = "portgroup name"

    type: Opaque
type: addons.cluster.x-k8s.io/resource-set

cluster.yamlcsi-vsphere-configを書き換えても、vsphere-csi-controllerが起動してくることが分かりました。

また、csi-vsphere-configにthumbprintオプションを追加し、vCenterのサーバー証明書のthumbprintやvCenterのCAのthumbprintを指定してみましたが、エラーは解消しませんでした。

現状は、csi-vsphere-configを書き換えても、vSphere CSIはサポートされない組み合わせとなってしまうため、ClusterResourceSetを使う場合でもバージョンの更新が課題になってきそうに見えています。

Continuous ClusterResourceSetStrategy · Issue #4807 · kubernetes-sigs/cluster-api · GitHub

また、clusterctl.yamlEXP_CLUSTER_RESOURCE_SET を false にしても、Taintの設定上podが起動してこないため、現状は一度vSphere CSIを削除し、最新バージョンのvSphere CSIをインストールし直さないといけないようには見えており、他にいい方法がないか気になっています。

参考

KubernetesクラスタにvSphere CPIとvSphere CSIをインストールする

前回は、Cluster APIを使ってvSphere上にKubernetesクラスタを作成してみました。今回は、vSphere CPIとvSphere CSIのインストールを試してみたいと思います。

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Kubespray
  • kubernetes v1.25.5
  • vSphere CPI v1.25.0
  • vSphere CSI v2.7.0

vSphere CPIとvSphere CSIについて

vSphere CSI は、KubernetesのPersistentVolume(PV)にてvSphere ストレージを利用するための、Container Storage Interface(CSI)プラグインになります。vSphere CSIを使うと、PVは仮想マシンの1つの仮想ディスクとしてworkerノードに接続されます。また、vSphere CSIで作成された仮想ディスクはFirst Class Disk(FCD)と呼ばれ、仮想マシンに接続しないと管理ができなかった通常の仮想ディスクと異なり、EBSのように独立して仮想ディスクの管理をすることができます。。FCDは、vSAN、VMFS、NFS、vVols データストアで利用でき、ReadWriteOnceのPVを作成することができます。vSANファイル共有を使うと、ReadWriteManyのPVを作成することができるようです。ストレージポリシーも利用でき、ディスクごとの可用性設定やIOコントロールをすることができます。

また、vSphere CSIは、vSphereのCloud Provider Interface (CPI) とともに使用されます。Controller Managerは、Kubernetesのコントローラをまとめて実行するコンポーネントです。Cloud Controller Managerは、Kubernetesで使用したいリソースと任意のクラウドで使用しているリソースの連携を行うCloud Providerを動作するためのコンポーネントです。In-Tree内のCloud Providerは、 vSphere Cloud Provider (VCP)と呼ばれていましたが、In-Tree外のCloud ProviderはvSphere CPIと呼ばれるようになりました。vSphere CPIは、vCenter Serverに接続し、VM、ディスクなどのインフラストラクチャに関する情報をKubernetes APIマッピングすることができます。

vSphere CPIのインストールの事前準備

vSphere CPIのインストールをする際に、vsphere-cloud-controller-managerがノードのリソースが少ない場合、起動してこないため、ノードのスペックを2vCPU、4GBメモリに変更しておきます。

Kubernetesクラスタのmasterノードとworkerノードの仮想マシンに対して、disk.EnableUUIDを有効にします。また、仮想ハードウェアバージョンは15以上、Paravirtual SCSIコントローラーが必要なようですが、すでに設定済みのためスキップします。

$ for VMNAME in kube-master0{1..3}; do
  echo ${VMNAME}
  export VM="/Datacenter/vm/k8s/$VMNAME"
  govc vm.change -vm $VM -e="disk.enableUUID=1"
done
$ for VMNAME in kube-worker0{1..2}; do
  echo ${VMNAME}
  export VM="/Datacenter/vm/k8s/$VMNAME"
  govc vm.change -vm $VM -e="disk.enableUUID=1"
done

masterノードとworkerノードに node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule というTaintを設定します。 Taintはノードに配置したくないPodの条件を指定し、Tolerationで容認がない場合は、Taintを設定したノードにPodが配置されない状態となります。

Taintを設定前の状態はこちらになります。

$ kubectl describe nodes | egrep "Taints:|Name:"
Name:               kube-master01
Taints:             node-role.kubernetes.io/master:NoSchedule
Name:               kube-master02
Taints:             node-role.kubernetes.io/master:NoSchedule
Name:               kube-master03
Taints:             node-role.kubernetes.io/master:NoSchedule
Name:               kube-worker01
Taints:             <none>
Name:               kube-worker02
Taints:             <none>

ノードにTaintを設定します。

$ for VMNAME in kube-master0{1..3}; do
  echo $VMNAME
  kubectl taint node $VMNAME node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
done
$ for VMNAME in kube-worker0{1..2}; do
  echo $VMNAME
  kubectl taint node $VMNAME node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
done

Taintの設定後の状態はこちらです。masterノードは設定前と変わっていないように見えました。

$ kubectl describe nodes | egrep "Taints:|Name:"
Name:               kube-master01
Taints:             node-role.kubernetes.io/master:NoSchedule
Name:               kube-master02
Taints:             node-role.kubernetes.io/master:NoSchedule
Name:               kube-master03
Taints:             node-role.kubernetes.io/master:NoSchedule
Name:               kube-worker01
Taints:             node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
Name:               kube-worker02
Taints:             node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule

vSphere CPIのインストール

vsphere-cloud-controller-manager.yamlをダウンロードします

$ VERSION=1.25
$ wget https://raw.githubusercontent.com/kubernetes/cloud-provider-vsphere/release-$VERSION/releases/v$VERSION/vsphere-cloud-controller-manager.yaml

vsphere-cloud-controller-manager.yamlにて、vCenterなどの情報を書き換えます。

$ vi vsphere-cloud-controller-manager.yaml
apiVersion: v1
kind: Secret
metadata:
  name: vsphere-cloud-secret
  labels:
    vsphere-cpi-infra: secret
    component: cloud-controller-manager
  namespace: kube-system
  # NOTE: this is just an example configuration, update with real values based on your environment
stringData:
  10.0.10.5.username: "vCenter username"
  10.0.10.5.password: "vCenter password"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: vsphere-cloud-config
  labels:
    vsphere-cpi-infra: config
    component: cloud-controller-manager
  namespace: kube-system
data:
  # NOTE: this is just an example configuration, update with real values based on your environment
  vsphere.conf: |
    # Global properties in this section will be used for all specified vCenters unless overriden in VirtualCenter section.
    global:
      port: 443
      # set insecureFlag to true if the vCenter uses a self-signed cert
      insecureFlag: true
      # settings for using k8s secret
      secretName: vsphere-cloud-secret
      secretNamespace: kube-system

    # vcenter section
    vcenter:
      vcsa-1.home.lab:
        server: 10.0.10.5
        user: <vCenter username>
        password: <vCenter password>
        datacenters:
          - <datascenter name>

vSphere CPIをインストールします。

$ kubectl apply -f vsphere-cloud-controller-manager.yaml

インストールに成功すると、podはこのようになりました。

$ kubectl get pod -A

kube-system                         vsphere-cloud-controller-manager-9zczs                           1/1     Running             0                155m
kube-system                         vsphere-cloud-controller-manager-fcs65                           1/1     Running             0                155m
kube-system                         vsphere-cloud-controller-manager-r5wfk                           1/1     Running             0                155m

vsphere-cloud-controller-manager の起動が失敗している場合は、以下のコマンドで原因を確認します。

$ kubectl describe pod vsphere-cloud-controller-manager -n kube-system

vSphere CSIのインストール

vmware-system-csi というネームスペースを作成します。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/vsphere-csi-driver/v2.7.0/manifests/vanilla/namespace.yaml

masterノードに対して、Taintを設定します。

for VMNAME in kube-master0{1..3}; do
  echo $VMNAME
  kubectl taint nodes $VMNAME node-role.kubernetes.io/control-plane=:NoSchedule
done

Taintの設定後の状態はこちらです。

kubectl describe nodes | egrep "Taints:|Name:"
Name:               kube-master01
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Name:               kube-master02
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Name:               kube-master03
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Name:               kube-worker01
Taints:             node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
Name:               kube-worker02
Taints:             node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedul

vSphere CSIのためのcsi-vsphere.confという定義ファイルを作成します。

$ vi csi-vsphere.conf
[Global]
cluster-id = "cluster-1"

[VirtualCenter "vcsa-1.home.lab"]
insecure-flag = "true"
user = "vCenter username"
password = "vCenter password"
port = "443"
datacenters = "datacenter name"

csi-vsphere.confからsecretを作成します。

$ kubectl create secret generic vsphere-config-secret --from-file=csi-vsphere.conf --namespace=vmware-system-csi

vSphere CSIをインストールします。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/vsphere-csi-driver/v2.7.0/manifests/vanilla/vsphere-csi-driver.yaml

vSphere CSIがインストールされたことを確認します。

$ kubectl get deployment --namespace=vmware-system-csi
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
vsphere-csi-controller   3/3     3            3           141m

インストールに成功すると、podはこのようになりました。

$ kubectl get pod -A

kube-system                         vsphere-cloud-controller-manager-9zczs                           1/1     Running             0                155m
kube-system                         vsphere-cloud-controller-manager-fcs65                           1/1     Running             0                155m
kube-system                         vsphere-cloud-controller-manager-r5wfk                           1/1     Running             0                155m
vmware-system-csi                   vsphere-csi-controller-68d96d8597-2cfcf                          7/7     Running             0                141m
vmware-system-csi                   vsphere-csi-controller-68d96d8597-5dszx                          7/7     Running             0                141m
vmware-system-csi                   vsphere-csi-controller-68d96d8597-82mcm                          7/7     Running             0                141m
vmware-system-csi                   vsphere-csi-node-b9ztz                                           3/3     Running              0                141m
vmware-system-csi                   vsphere-csi-node-cshf7                                           3/3     Running             0                141m
vmware-system-csi                   vsphere-csi-node-dqc79                                           3/3     Running             0                141m
vmware-system-csi                   vsphere-csi-node-fp2zd                                           3/3     Running             0                141m
vmware-system-csi                   vsphere-csi-node-qnfx6                                           3/3     Running             0                141m

動作確認

動作確認のため、PersistentVolumeを作成していきます。

ストレージポリシーの設定が必要なため、前回作成した Normal-Storage-Policy というストレージポリシーを利用します。IOPS制限は外しておきます。

hidemium.hatenablog.com

StorageClassの定義を作成し、インストールします。定義ファイルの中で先ほど用意したストレージポリシーを指定しておきます。

$ vi example-sc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: example-sc
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: csi.vsphere.vmware.com
parameters:
  storagepolicyname: "Normal-Storage-Policy"
  csi.storage.k8s.io/fstype: "ext4"
$ kubectl apply -f example-sc.yaml

PVCの定義を作成し、インストールします。

$ vi example-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: example-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: example-sc
$ kubectl apply -f example-pvc.yaml

PVとPVCが作成できていることを確認します。

$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
persistentvolume/pvc-6c1bc194-bbc0-4e26-b77c-3b2412960200   5Gi        RWO            Delete           Bound    default/example-pvc   example-sc              16s

NAME                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/example-pvc   Bound    pvc-6c1bc194-bbc0-4e26-b77c-3b2412960200   5Gi        RWO            example-sc     17s

vCenterを見ると、クラウドネイティブストレージ>コンテナ ボリュームにPVの情報が表示されていることが分かります。

さらにデータストアブラウザを見ると、First Class Disk用のフォルダのfcdフォルダ内にPVの実態となる、vmdkファイルが作成されいてることをが分かります。

上記の定義ファイルだと、workerノードにFCDのvmdkはまだアタッチされていない状態となるようです。

参考

Cluster APIを使ってvSphereにKubernetesクラスタを作成する

今まではKubesprayを使ってKubernetesのインストールを行ってきました。今回は、Cluster APIを使ったKubernetesクラスタを作成を試してみました。

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Kubespray
  • kubernetes v1.25.5
  • Cluster API v1.3.2
    • clusterctl v1.3.2
    • cluster-api-provider-vsphere v1.5.1
    • ubuntu-2004-kube-v1.25.5.ova

Cluster APIについて

Cluster APIは、Kubernetesの宣言型の機能を利用し、Kubernetesクラスタの作成や構成、管理をするプロジェクトになります。Kubesprayでは、masterノートやworkerノードの展開はgovcなど自身で行ってきましたが、Cluster APIではその作業も任せることができます。Cluster APIパブリッククラウドなど様々なプラットフォームに対応しており、vSphereにも対応しており、vSphereの実装として、Cluster API Provider vSphere(CAPV)があります。CAPVはTanzu Kubernetes Grid(TKG)の中でも利用されているようです。

Cluster APIを利用すると、Kubernetesクラスタを管理するmanagement clusterと、management clusterで管理されたワークロードを実行するworkload clusterが作成されます。

workload cluster用のネットワークの準備

workload clusterはDHCPIPアドレスを取得するため、workload clusterが接続するポートグループにDHCPIPアドレスを取得できるようにしておきます。homelabでは、EdgeRouterのDHCP機能でIPアドレスを払い出ししています。

マシンイメージの展開

CAPV用のマシンイメージが配布されており、 ubuntu-2004-kube-v1.25.5.ova をダウンロードし、vCenter上にデプロイします。

homelabでの利用なので、マシンイメージをそのまま利用しますが、production環境では独自ビルドが推奨なようです。

マシンイメージのビルド用のpackerテンプレートはこちらで管理されていました。

image-builder/images/capi/packer/ova at master · kubernetes-sigs/image-builder · GitHub

Cluster APIでマシンイメージを展開する際に、デフォルトだとlinked cloneが利用されるため、以下のようにlinked cloneを利用するためにスナップショットをとってから、テンプレート化しておきます。

$ govc snapshot.create -vm ubuntu-2004-kube-v1.25.5 root
$ govc vm.markastemplate ubuntu-2004-kube-v1.25.5

haproxyのマシンイメージについては、kube-vipを使うように変更となったため、非推奨となってようなので、利用はしていません。

management clusterのインストール

通常の手順ですと、kindを利用して、docker上にKubernetesをインストールして、bootstrap clusterを作成し、そこにclusterctlを使ってmanagement clusterをインストールします。

今回は、KubesprayですでにKubernetesクラスタがあるため、Kubesprayで作成したKubernetesクラスタにmanagement clusterをインストールしていきます。

clusterctlをインストールします。

$ curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.2/clusterctl-linux-amd64 -o clusterctl
$ sudo install -o root -g root -m 0755 clusterctl /usr/local/bin/clusterctl
$ clusterctl version

clusterctlを実行するために、以下のように ~/.cluster-api/clusterctl.yaml に定義ファイルを用意します。

$ vi ~/.cluster-api/clusterctl.yaml
## -- Controller settings -- ##
VSPHERE_USERNAME: "vCenterのログインユーザ"
VSPHERE_PASSWORD: "パスワード"

## -- Required workload cluster default settings -- ##
VSPHERE_SERVER: "vCenterのFQDN"
VSPHERE_DATACENTER: "データセンター名"
VSPHERE_DATASTORE: "データストア名"
VSPHERE_NETWORK: "DHCPが利用可能なポートグループ名"
VSPHERE_RESOURCE_POOL: "<クラスタ名 or リソースプール名>/Resources"
VSPHERE_FOLDER: "vm"
VSPHERE_TEMPLATE: "ubuntu-2004-kube-v1.25.5"
CONTROL_PLANE_ENDPOINT_IP: "workload clusterのkubectlで接続するコントロールプレーンのIPアドレス"
VIP_NETWORK_INTERFACE: "eth0"
VSPHERE_TLS_THUMBPRINT: "vCenterのthumbprint"
EXP_CLUSTER_RESOURCE_SET: "true"
VSPHERE_SSH_AUTHORIZED_KEY: "sshのための公開鍵"
VSPHERE_STORAGE_POLICY: ""

vCenterのthumbprintは、vCenterにsshでログインし、以下のコマンドで取得します。

# openssl x509 -in /etc/vmware-vpx/ssl/rui.crt -fingerprint -sha1 -noout

vCenterのログイン情報や、workload clusterを作成するために必要なリソースプールやデータストア名などを入力します。

上記の設定で困ったことは、 CONTROL_PLANE_ENDPOINT_IPVIP_NETWORK_INTERFACE の設定になります。

CONTROL_PLANE_ENDPOINT_IP はkube-vipで利用するworkload clusterのコントロールプレーンのIPアドレスになり、DHCPのIP帯以外のstaticなIPアドレスを指定する必要があります。ここのIPアドレスの指定を間違えると、コントロールプレーンにアクセスできないとエラーが出て、workload clusterの作成に失敗します。

$ kubectl logs capi-kubeadm-control-plane-controller-manager-785c94c5d4-sh9fg -n capi-kubeadm-control-plane-system -f
"Failed to update KubeadmControlPlane Status" err="failed to create remote cluster client: failed to create cluster accessor: error creating dynamic rest mapper for remote cluster \"default/vsphere-quickstart\": Get \"https://10.0.50
.30:6443/api?timeout=10s\": x509: certificate signed by unknown authority (possibly because of \"crypto/rsa: verification error\" while trying to verify candidate authority certificate \"kubernetes\")" controller="kubeadmcontrolplane" controllerGroup="controlplane.cluster.x-k8s.io"
 controllerKind="KubeadmControlPlane" KubeadmControlPlane="default/vsphere-quickstart" namespace="default" name="vsphere-quickstart" reconcileID=fa7159b4-97ff-45cd-b1ba-29a4d5f11548 Cluster="default/vsphere-quickstart"

VIP_NETWORK_INTERFACE は、コントロールプレーンのkube-vipで利用するインターフェイスの設定となりますが、公式のマシンイメージでは、インターフェイスにeth0を利用しており、サンプルではens192だったため、コントロールプレーンにアクセスできず、workload clusterの作成に失敗します。

~/.cluster-api/clusterctl.yaml が出来たところで、以下のコマンドで、management clusterのインストールを行います。

$ clusterctl init --infrastructure vsphere
Fetching providers
Installing cert-manager Version="v1.10.1"
Waiting for cert-manager to be available...
Installing Provider="cluster-api" Version="v1.3.2" TargetNamespace="capi-system"
Installing Provider="bootstrap-kubeadm" Version="v1.3.2" TargetNamespace="capi-kubeadm-bootstrap-system"
Installing Provider="control-plane-kubeadm" Version="v1.3.2" TargetNamespace="capi-kubeadm-control-plane-system"
Installing Provider="infrastructure-vsphere" Version="v1.5.1" TargetNamespace="capv-system"

Your management cluster has been initialized successfully!

You can now create your first workload cluster by running the following:

  clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -

以下のような、podが起動してきます。

$ kubectl get pod -A
NAMESPACE                           NAME                                                             READY   STATUS    RESTARTS       AGE
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-controller-manager-78c76cd689-flgtb       1/1     Running   0              3d
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-controller-manager-785c94c5d4-sh9fg   1/1     Running   0              3d
capi-system                         capi-controller-manager-8665f47486-thlh4                         1/1     Running   0              3d
capv-system                         capv-controller-manager-dfb77fcc6-tz7t8                          1/1     Running   0              2d19h
cert-manager                        cert-manager-74d949c895-v4g52                                    1/1     Running   0              3d
cert-manager                        cert-manager-cainjector-d9bc5979d-ktwrz                          1/1     Running   0              3d
cert-manager                        cert-manager-webhook-84b7ddd796-fdj8l                            1/1     Running   0              3d

workload clusterのインストール

次に、workload clusterのインストールをしていきます。

workload clusterのKubernetsのバージョンや、コントロールプレーンのVM数、wokerノードのVM数を指定して、cluster.yamlを生成します。

$ clusterctl generate cluster vsphere-quickstart \
    --infrastructure vsphere \
    --kubernetes-version v1.25.5 \
    --control-plane-machine-count 1 \
    --worker-machine-count 3 > cluster.yaml

必要に応じて、cluster.yamlを編集します。

以下のコマンドで、workload clusterのインストールを行います。

$ kubectl apply -f cluster.yaml

以下のコマンドで、workload clusterが起動してきかたを確認できます。10分ぐらいで立ち上がってくるように見えています。

$ kubectl get machines
NAME                                       CLUSTER              NODENAME                                   PROVIDERID                                       PHASE     AGE     VERSION
vsphere-quickstart-md-0-5c5849c5b9-5wbv8   vsphere-quickstart   vsphere-quickstart-md-0-5c5849c5b9-5wbv8   vsphere://42080fa2-7dab-7285-444a-7b0ecc66a35e   Running   7m15s   v1.25.5
vsphere-quickstart-md-0-5c5849c5b9-95595   vsphere-quickstart   vsphere-quickstart-md-0-5c5849c5b9-95595   vsphere://4208d8b7-8297-c452-26f9-e7061d023e8a   Running   7m15s   v1.25.5
vsphere-quickstart-md-0-5c5849c5b9-s4rz9   vsphere-quickstart   vsphere-quickstart-md-0-5c5849c5b9-s4rz9   vsphere://4208246c-a347-1f39-51c3-3907b502f884   Running   7m15s   v1.25.5
vsphere-quickstart-sxfqt                   vsphere-quickstart   vsphere-quickstart-sxfqt                   vsphere://4208aad8-d2e7-42bf-00ac-70511419ff77   Running   7m14s   v1.25.5

workload clusterにアクセスするためのconfigファイルを以下のように生成します。

$ kubectl get secret/vsphere-quickstart-kubeconfig -o json \
  | jq -r .data.value \
  | base64 --decode \
  > ./vsphere-quickstart.kubeconfig

workload clusterのノードを確認すると、NotReadyとなっています。こちらはCNIがインストールされていないため、利用ができない状態となっています。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get node
NAME                                       STATUS     ROLES           AGE   VERSION
vsphere-quickstart-md-0-5c5849c5b9-5wbv8   NotReady   <none>          10m   v1.25.5
vsphere-quickstart-md-0-5c5849c5b9-95595   NotReady   <none>          10m   v1.25.5
vsphere-quickstart-md-0-5c5849c5b9-s4rz9   NotReady   <none>          10m   v1.25.5
vsphere-quickstart-sxfqt                   NotReady   control-plane   11m   v1.25.5

以下のようにcalicoをインストールします。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml

Readyになったことを確認し、workload clusterのインストールは完了になります。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get node
NAME                                       STATUS   ROLES           AGE   VERSION
vsphere-quickstart-md-0-5c5849c5b9-5wbv8   Ready    <none>          28h   v1.25.5
vsphere-quickstart-md-0-5c5849c5b9-95595   Ready    <none>          28h   v1.25.5
vsphere-quickstart-md-0-5c5849c5b9-s4rz9   Ready    <none>          28h   v1.25.5
vsphere-quickstart-sxfqt                   Ready    control-plane   28h   v1.25.5

calicoのインストールが完了すると、podの状態はこのようになっていました。vsphere-csi-controller が CrashLoopBackOff となっている原因について別途書いてみようと思います。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -A
NAMESPACE     NAME                                               READY   STATUS             RESTARTS      AGE
kube-system   calico-kube-controllers-74677b4c5f-m77w8           1/1     Running            0             16m
kube-system   calico-node-7bcpz                                  1/1     Running            0             16m
kube-system   calico-node-w7fs9                                  1/1     Running            0             16m
kube-system   calico-node-wckhz                                  1/1     Running            0             16m
kube-system   calico-node-zlc5b                                  1/1     Running            0             16m
kube-system   coredns-565d847f94-chn5z                           1/1     Running            0             21m
kube-system   coredns-565d847f94-dn4zq                           1/1     Running            0             21m
kube-system   etcd-vsphere-quickstart-d7r7k                      1/1     Running            0             21m
kube-system   kube-apiserver-vsphere-quickstart-d7r7k            1/1     Running            0             21m
kube-system   kube-controller-manager-vsphere-quickstart-d7r7k   1/1     Running            0             21m
kube-system   kube-proxy-br9fn                                   1/1     Running            0             20m
kube-system   kube-proxy-cvfzq                                   1/1     Running            0             20m
kube-system   kube-proxy-qxlj2                                   1/1     Running            0             20m
kube-system   kube-proxy-rmzww                                   1/1     Running            0             21m
kube-system   kube-scheduler-vsphere-quickstart-d7r7k            1/1     Running            0             21m
kube-system   kube-vip-vsphere-quickstart-d7r7k                  1/1     Running            0             21m
kube-system   vsphere-cloud-controller-manager-ctkr7             1/1     Running            0             20m
kube-system   vsphere-cloud-controller-manager-hl5w2             1/1     Running            0             20m
kube-system   vsphere-cloud-controller-manager-r9dhk             1/1     Running            0             20m
kube-system   vsphere-csi-controller-5d6484644d-nqwlh            4/5     CrashLoopBackOff   8 (37s ago)   21m
kube-system   vsphere-csi-node-h9p2x                             3/3     Running            0             21m
kube-system   vsphere-csi-node-hbgsl                             3/3     Running            0             20m
kube-system   vsphere-csi-node-tr99v                             3/3     Running            0             20m
kube-system   vsphere-csi-node-xk2jq                             3/3     Running            0             20m

スモークテスト

動作確認を行います。

テスト用のpodを2台作成し、pod間で疎通が取れるか確認します。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl run myshell1 -it --rm --image busybox -- sh
/ # hostname -i
192.168.136.2
/ # ping 192.168.82.67
PING 192.168.82.67 (192.168.82.67): 56 data bytes
64 bytes from 192.168.82.67: seq=0 ttl=62 time=0.615 ms
64 bytes from 192.168.82.67: seq=1 ttl=62 time=3.218 ms
^C
--- 192.168.82.67 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.615/1.916/3.218 ms
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl run myshell2 -it --rm --image busybox -- sh
/ # hostname -i
192.168.82.67
/ # ping 192.168.136.2
PING 192.168.136.2 (192.168.136.2): 56 data bytes
64 bytes from 192.168.136.2: seq=0 ttl=62 time=0.918 ms
64 bytes from 192.168.136.2: seq=1 ttl=62 time=0.316 ms
^C
--- 192.168.136.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.316/0.617/0.918 ms

podの状態は以下の通りです。

$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP                NODE                                       NOMINATED NODE   READINESS GATES
myshell1   1/1     Running   0          50s   192.168.136.2     vsphere-quickstart-md-0-5c5849c5b9-5wbv8   <none>           <none>
myshell2   1/1     Running   0          28s   192.168.82.67   vsphere-quickstart-md-0-5c5849c5b9-95595   <none>           <none>

おわりに

KubesprayだとKubernetesクラスタのインストールに30分ぐらいかかりましたが、Cluster APIだと10分程度で利用ができるので、効率がよさそうでした。ただ、Kubesprayだとクラスタの初期設定(CNIやMetalLB)もある程度対応できたり、マシンイメージのビルドが楽だったり、構築中の動作やどのような設定を行っているかが分かりやすいということもあり、homelabだとKubesprayのほうが使いやすそうには見えています。用途によって使い分けるのがよさそうに見えました。

参考

PromethusとGrafanaを使ったRook Cephの監視

前回は、Rookのダッシュボードのインストールを試してみました。今回は、Rook Cephを監視するためのPromethusとGrafanaのインストールを試してみました。

hidemium.hatenablog.com

構成

Promethusのインストール

promethus operatorをインストールします。

LATEST=$(curl -s https://api.github.com/repos/prometheus-operator/prometheus-operator/releases/latest | jq -cr .tag_name)
curl -sL https://github.com/prometheus-operator/prometheus-operator/releases/download/${LATEST}/bundle.yaml | kubectl create -f -

promethus operatorが起動したことを確認します。

$ kubectl get pod
NAME                                   READY   STATUS    RESTARTS   AGE
prometheus-operator-57df45d67c-69f6z   1/1     Running   0          34s

Serviceをtype: LoadBalancerにするために、以下のように修正します。

$ cd rook/deploy/examples/monitoring
$ vi prometheus-service.yaml
-    type: NodePort
+   type: LoadBalancer

Rookクラスターを監視し、メトリックを定期的に収集するサービスモニターを作成します。

$ kubectl create -f service-monitor.yaml
$ kubectl create -f prometheus.yaml
$ kubectl create -f prometheus-service.yaml

Prometheusが起動していることを確認します。

$ kubectl -n rook-ceph get pod,service 
NAME                                                          READY   STATUS      RESTARTS   AGE
pod/prometheus-rook-prometheus-0                              2/2     Running     0          53s

NAME                              TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
service/prometheus-operated       ClusterIP      None            <none>        9090/TCP            54s
service/rook-prometheus           LoadBalancer   10.233.11.124   10.0.50.203   9090:30900/TCP      45s

ブラウザから、 http://10.0.50.203:9090/ にアクセスし、PrometheusのWebコンソールが開けることを確認します。

Grafanaのインストール

Grafanaのhelmチャートをダウンロードします。

$ helm repo add grafana https://grafana.github.io/helm-charts
$ helm repo update

ingressが利用できるように、以下のようにvalues.yamlファイルを作成し、Grafanaのインストールを行います。

$ vi values.yml
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  labels: {}
  path: /
  hosts:
    - grafana.kube.home.lab
  tls:
    - secretName: ingress-tls 
      hosts:
        - grafana.kube.home.lab
$ helm install my-release grafana/grafana -f values.yml

Grafanaが起動してきたことを確認します。

$ kubectl get pod,svc,ingress
NAME                                       READY   STATUS    RESTARTS   AGE
pod/my-release-grafana-6fc6cd88d4-x8tnr    1/1     Running   0          88m
pod/prometheus-operator-57df45d67c-69f6z   1/1     Running   0          143m

NAME                          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/my-release-grafana    ClusterIP      10.233.19.102   <none>        80/TCP         94m
service/prometheus-operator   ClusterIP      None            <none>        8080/TCP       143m

NAME                                           CLASS    HOSTS                   ADDRESS       PORTS     AGE
ingress.networking.k8s.io/my-release-grafana   <none>   grafana.kube.home.lab   10.0.50.201   80, 443   94m

以下のようにGrafanaにログインするためのパスワードを取得します。

$ kubectl get secret --namespace default my-release-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

ブラウザから、 https://grafana.kube.home.lab にアクセスし、ユーザはadminと上記のパスワードでログインします。

Grafanaの設定

GrafanaのConfigurationから、Add data sourceでデータソースを追加します。

Prometheusをクリックし、URLに http://10.0.50.203:9090/ を入力し、Save & testをクリックします。

DashboardsのNew>importをクリックし、Import via grafana.comに以下のリンク先にある、Dashbord ID copied!をクリックし、クリップボードに入った値をペーストします。

Ceph - Clusterの場合は以下のようなダッシュボードが表示されます。

リソースの削除

動作確認ができたため、リソースを削除しておきます。

$ helm uninstall my-release
$ kubectl delete -f service-monitor.yaml
$ kubectl delete -f prometheus.yaml
$ kubectl delete -f prometheus-service.yaml
$ kubectl delete -f https://raw.githubusercontent.com/coreos/prometheus-operator/v0.62.0/bundle.yaml

参考

ストレージポリシーによるIOPS制限をした仮想マシンのfioによるベンチマーク

前回は、仮想マシンで構築したRook Cephから払い出したPersistentVolumeについてfioを使ってパフォーマンスのベンチマークを取ってみました。その際に比較のために仮想マシンベンチマークを取ってみました。今回は、仮想マシンの状態の比較として、ストレージポリシーによるIOPS制限をかけた場合の仮想マシンの挙動を確認してみます。

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Ubuntu 22.04 (テンプレートVM)

ストレージポリシーについて

vSphereの機能のなかで、Storage Policy Based Management (SPBM)というストレージレイヤーを抽象化する機能があります。

SPBMには、 Storage I/O ControlのSIOC v1とSIOC v2が存在します。

SIOC v1は、ストレージ I/Oのレイテンシを監視し、I/Oが競合した場合に、仮想マシンの共有値に基づいて、データストア全体のディスクスケジューラを使用して、I/Oを調整します。

SIOC v2は、vSphere 6.5から導入され、基盤となるストレージに関係なく、仮想マシンのI/O パスに直接アクセスできる vSphere APIs for I/O Filtering (VAIO)を使用して仮想マシン単位で制御を行うことができます。

ストレージポリシーでホスト ベースのサービスを指定することで、SIOC v2を利用することができ、IOPSの制限と予約と共有を行うことができます。

ストレージポリシーによるSIOC v2の設定

ストレージポリシーによって仮想マシンにIOPS制限をかけてみます。

タグを作成します。

タグのカテゴリに Storage Disk Type をつけ、データストアとデータストアクラスタにチェックをいれます。

Storage Disk Type カテゴリ配下に、 NVMe というタグを作成します。

データストアのインベントリに移動し、ストレージポリシーを適用したいデータストアのタグに NVMe を付与します。

ポリシーおよびプロファイル>ストレージ ポリシー コンポーネントにて、作成をクリックします。

名前に Normal-IOPS-limit などの名前を入れます。

IOPS の制限に3000、IOPS の予約に1、IOPS のシェア数に1000の値を入れ、保存します。

ポリシーおよびプロファイル>仮想マシン ストレージ ポリシーにて、作成をクリックします。

名前に Normal-Storage-Policy などの名前を入れます。

ホスト ベースのサービスでホスト ベースのルールの有効化にチェックを入れ、データストア固有のルールにタグ ベースの配置ルールを有効化にチェックを入れます。

ホスト ベースのサービスで、ストレージ ポリシー コンポーネントの使用で先ほど作成した Normal-IOPS-limit をします。

タグ ベースの配置で、タグ カテゴリに Storage Disk Type 、タグに NVMe を指定して、保存します。

仮想マシンへのストレージポリシーの適用

ストレージポリシーの作成ができたため、仮想マシンにストレージポリシーを適用します。

前回作成したUbutuの仮想マシン仮想マシン ストレージ ポリシーの編集から Normal-Storage-Policy を選択し、保存します。

fioによるパフォーマンスのベンチマーク

前回と同様、複数のブロックサイズと複数のI/Oパターンのタイプで測定するため、以下のようなコマンドで実行してみました。128kのブロックサイズを追加しています。

direct=1でnon-buffered I/Oを利用し、iodepth=32でキュー中に待機させるIOリクエストの最大数を32に指定、ioengine=libaioでIOを発生させる方法としてLinux ネイティブの非同期 I/Oを指定、numjobs=8でスレッド数を8に指定、group_reportingで複数スレッドの結果をまとめてレポート、output-format=jsonで計測結果をjson形式で出力という設定を行っています。

# for BS in 4k 8k 16k 64k 128k 1m; do
  for RW in write randwrite read randread; do
    fio -filename=/mnt/rook-vol1/test -direct=1 -iodepth=32 -rw=$RW -ioengine=libaio -bs=$BS -size=1G -numjobs=8 -runtime=30 -name=32-$RW-$BS --group_reporting --output-format=json | \
    jq -cr '[."global options".iodepth,."global options".bs, ."global options".rw, 
            .jobs[0].read.iops, .jobs[0].write.iops, 
            .jobs[0].read.bw, .jobs[0].write.bw]|@csv'
  done
done

こちらが、ストレージポリシーで3000IOPSの制限をかけたUbuntu仮想マシンで測定したfioのパフォーマンスのベンチマーク結果です。

前回の結果も載せておきます。

ブロックサイズが64kまではきれいに3000IOPSで制限されることが分かります。

帯域の制限は、IO数に32KBを加重することで計算されます。今回は3000IOPSのため、3000x32KB=93.75MB/秒の帯域幅の IOPS 制限が設定されます。

以前はIOPSを制限する際に32KBで正規化されていましたが、vSphere6.5以降は正規化されず、ブロックサイズに関係なくIOPSの値で制限されるようになっています。

そのため、ブロックサイズが128kでは、帯域の制限が先にあたり、IOPSが3000より小さい値になったと考えられます。

参考

仮想マシンで構築したRook Cephのfioによるベンチマーク

前回は、Rook Cephを使って分散ストレージのインストールを試してみました。今回は、仮想マシンで構築したRook Cephから払い出したPersistentVolumeについてfioを使ってパフォーマンスのベンチマークを取ってみました。

hidemium.hatenablog.com

構成

ベンチマークについて

ストレージのパフォーマンスのベンチマークでよく使われるfioを利用してみました。

Kubernetesでfioを使ったパフォーマンスのベンチマークをする方法はいくつかあるようで、いくつか試してみましたが、最近更新がなくmanifestファイルを修正が必要だったり、fioの定義ファイルがConfigMapに測定のたびに変更が必要だったり、クラスタ外からCLIツールを使って計測するものなどいくつかありました。

いろいろな条件で測定してみかったので、シンプルにubutnuのDeploymentを作成し、その中でfioコマンドを実行することにしました。

PVCとDeploymentの作成

以下のようなmanifestファイルを作成します。ubuntuのイメージはUbuntu 22.04になります。

$ vi ubuntu.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ubuntu-pv-claim
  labels:
    app: ubuntu
spec:
  storageClassName: rook-ceph-block
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu-deployment
  labels:
    app: ubuntu
spec:
  selector:
    matchLabels:
      app: ubuntu
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: ubuntu
    spec:
      containers:
        - image: ubuntu
          name: ubuntu
          command:
            - sleep
            - infinity
          volumeMounts:
            - name: ubuntu-persistent-storage
              mountPath: /mnt/rook-vol1
      volumes:
        - name: ubuntu-persistent-storage
          persistentVolumeClaim:
            claimName: ubuntu-pv-claim

以下のコメントでPVCとDeploymentを作成します。

$ kubectl apply -f ubuntu.yaml

ubuntuのPodが起動し、PVとPVCが作成されたことを確認します。

$ kubectl get pod,pv,pvc
NAME                                     READY   STATUS    RESTARTS   AGE
pod/exdns-k8s-gateway-58bf7c6c75-vgjm8   1/1     Running   0          8d
pod/ubuntu-deployment-64548dcd7-l8gpq    1/1     Running   0          37h

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS      REASON   AGE
persistentvolume/pvc-39d09d74-62ec-4c14-920b-cec8995915d4   20Gi       RWO            Delete           Bound    default/ubuntu-pv-claim   rook-ceph-block            37h

NAME                                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
persistentvolumeclaim/ubuntu-pv-claim   Bound    pvc-39d09d74-62ec-4c14-920b-cec8995915d4   20Gi       RWO            rook-ceph-block   37h

fioのインストール

Dockerfileを作成して、先に独自イメージをビルドしてもよいですが、シンプルにコンテナ内でfioをインストールします。また合わせて、そのあとに必要なjqもンストールします。

$ kubectl exec -it ubuntu-deployment-64548dcd7-l8gpq -- bash
# apt update
# apt install -y fio jq

fioによるパフォーマンスのベンチマーク

複数のブロックサイズと複数のI/Oパターンのタイプで測定するため、以下のようなコマンドで実行してみました。

direct=1でnon-buffered I/Oを利用し、iodepth=32でキュー中に待機させるIOリクエストの最大数を32に指定、ioengine=libaioでIOを発生させる方法としてLinux ネイティブの非同期 I/Oを指定、numjobs=8でスレッド数を8に指定、group_reportingで複数スレッドの結果をまとめてレポート、output-format=jsonで計測結果をjson形式で出力という設定を行っています。

# for BS in 4k 8k 16k 64k 1m; do
  for RW in write randwrite read randread; do
    fio -filename=/mnt/rook-vol1/test -direct=1 -iodepth=32 -rw=$RW -ioengine=libaio -bs=$BS -size=1G -numjobs=8 -runtime=30 -name=32-$RW-$BS --group_reporting --output-format=json | \
    jq -cr '[."global options".iodepth,."global options".bs, ."global options".rw, 
            .jobs[0].read.iops, .jobs[0].write.iops, 
            .jobs[0].read.bw, .jobs[0].write.bw]|@csv'
  done
done

以下のような出力がでます。

"32","4k","write",0,807.73655,0,3230
"32","4k","randwrite",0,597.798587,0,2391
"32","4k","read",7619.117402,0,30476,0
"32","4k","randread",2980.814552,0,11923,0
:

合わせて、workerノード側のUbuntu仮想マシンでも同様にfioを使ってベンチマークを取得してみます。

こちらが仮想マシンで構築したRook Cephから払い出したPersistentVolumeで測定したfioのパフォーマンスのベンチマーク結果です。

こちらが、workerノード側のUbuntu仮想マシンで測定したfioのパフォーマンスのベンチマーク結果です。

workerノードはすべて同一のサーバーで稼働しているため、Rook Cephの性能はどうしても仮想マシン単体で測定した帯域の1/10ぐらいにはなってそうでした。

workerノードが稼働しているサーバーにはNVMeが接続されており、仮想マシン単体での性能の良さが良く分かります。

仮想マシンで構築したRook Cephですが、writeに比べreadの性能が良いように見えいます。また、ランダムに比べるとシーケンシャルのほうが性能がよさそうでした。

このあたりも物理サーバーで構築したRook CephだとIO特性が変わってくるのかもしれません。

仮想マシンで構築したRook Cephについて、homelabでPVCを利用して試す分には性能として問題なさそうには見えています。

参考