本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
CSI 驅動的測試
在開發 容器儲存介面 (CSI) 驅動程式時,利用盡可能多的前期工作非常有用。這包括原始碼(如示例 CSI hostpath 驅動程式)以及現有測試。除了節省時間,使用別人編寫的測試還有一個優點,那就是它可以指出規範中可能被忽略的方面。
一篇關於端到端測試的早期博文已經展示瞭如何使用 Kubernetes 儲存測試來測試第三方 CSI 驅動程式。這種方法在目標是新增自定義 E2E 測試時很有意義,但它需要花費相當大的精力來設定和維護一個測試套件。
如果目標僅僅是執行現有測試,那麼有更簡單的方法。這篇博文將介紹這些方法。
健全性測試
csi-test 健全性透過以各種方式呼叫 gRPC 方法並檢查結果是否符合要求來確保 CSI 驅動程式符合 CSI 規範。儘管它目前託管在 Kubernetes-CSI 組織下,但它完全獨立於 Kubernetes。測試透過其 Unix 域套接字連線到正在執行的 CSI 驅動程式,因此儘管測試是用 Go 編寫的,但驅動程式本身可以用任何語言實現。
主 README 解釋瞭如何將這些測試包含到現有 Go 測試套件中。更簡單的替代方法是直接呼叫 csi-sanity 命令。
安裝
從 csi-test v3.0.0 開始,您可以使用 go get github.com/kubernetes-csi/csi-test/cmd/csi-sanity
構建 csi-sanity
命令,您將在 $GOPATH/bin/csi-sanity
中找到編譯後的二進位制檔案。
go get
總是從 master 分支構建最新版本。要構建特定版本,請獲取原始碼並執行 make -C cmd/csi-sanity
。這將生成 cmd/csi-sanity/csi-sanity
。
用法
csi-sanity
二進位制檔案是一個完整的 Ginkgo 測試套件,因此具有常規的 -gingko
命令列標誌。特別是,-ginkgo.focus
和 -ginkgo.skip
可用於選擇要執行或不執行的測試。
在測試執行期間,csi-sanity
透過建立 CSI 規範要求的 staging 和 target 目錄並通過 gRPC 呼叫 CSI 驅動程式來模擬容器編排器 (CO) 的行為。驅動程式必須在呼叫 csi-sanity
之前啟動。儘管測試目前只檢查 gRPC 返回程式碼,但這可能會改變,因此驅動程式確實應該進行呼叫請求的更改,例如掛載檔案系統。這可能意味著它必須以 root 身份執行。
呼叫 csi-sanity
時,必須透過 -csi.endpoint
引數指定至少一個 gRPC 端點,可以是 Unix 域套接字的絕對路徑(unix:/tmp/csi.sock
),也可以是 TCP 的主機名加埠(dns:///my-machine:9000
)。然後 csi-sanity
將該端點用於節點和控制器操作。可以透過 -csi.controllerendpoint
指定控制器操作的單獨端點。預設情況下,目錄在 /tmp
中建立。這可以透過 -csi.mountdir
和 -csi.stagingdir
更改。
有些驅動程式無法部署,以保證所有內容都在同一主機上執行。在這種情況下,必須使用自定義指令碼來處理目錄:它們登入到 CSI 節點控制器執行的主機並在那裡建立或刪除目錄。
例如,在 CI 測試期間,CSI hostpath 示例驅動程式在呼叫 csi-sanity
之前部署在真實的 Kubernetes 叢集上,然後 csi-sanity
透過 socat
提供的埠轉發連線到它。指令碼用於建立和刪除目錄。
下面是使用 CSI hostpath 驅動程式 v1.2.0 版本如何重現此操作的示例
$ cd csi-driver-host-path
$ git describe --tags HEAD
v1.2.0
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
127.0.0.1 Ready <none> 42m v1.16.0
$ deploy/kubernetes-1.16/deploy-hostpath.sh
applying RBAC rules
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-provisioner/v1.4.0/deploy/kubernetes/rbac.yaml
...
deploying hostpath components
deploy/kubernetes-1.16/hostpath/csi-hostpath-attacher.yaml
using image: quay.io/k8scsi/csi-attacher:v2.0.0
service/csi-hostpath-attacher created
statefulset.apps/csi-hostpath-attacher created
deploy/kubernetes-1.16/hostpath/csi-hostpath-driverinfo.yaml
csidriver.storage.k8s.io/hostpath.csi.k8s.io created
deploy/kubernetes-1.16/hostpath/csi-hostpath-plugin.yaml
using image: quay.io/k8scsi/csi-node-driver-registrar:v1.2.0
using image: quay.io/k8scsi/hostpathplugin:v1.2.0
using image: quay.io/k8scsi/livenessprobe:v1.1.0
...
service/hostpath-service created
statefulset.apps/csi-hostpath-socat created
07:38:46 waiting for hostpath deployment to complete, attempt #0
deploying snapshotclass
volumesnapshotclass.snapshot.storage.k8s.io/csi-hostpath-snapclass created
$ cat >mkdir_in_pod.sh <<EOF
#!/bin/sh
kubectl exec csi-hostpathplugin-0 -c hostpath -- mktemp -d /tmp/csi-sanity.XXXXXX
EOF
$ cat >rmdir_in_pod.sh <<EOF
#!/bin/sh
kubectl exec csi-hostpathplugin-0 -c hostpath -- rmdir "\$@"
EOF
$ chmod u+x *_in_pod.sh
$ csi-sanity -ginkgo.v \
-csi.endpoint dns:///127.0.0.1:$(kubectl get "services/hostpath-service" -o "jsonpath={..nodePort}") \
-csi.createstagingpathcmd ./mkdir_in_pod.sh \
-csi.createmountpathcmd ./mkdir_in_pod.sh \
-csi.removestagingpathcmd ./rmdir_in_pod.sh \
-csi.removemountpathcmd ./rmdir_in_pod.sh
Running Suite: CSI Driver Test Suite
====================================
Random Seed: 1570540138
Will run 72 of 72 specs
...
Controller Service [Controller Server] ControllerGetCapabilities
should return appropriate capabilities
/nvme/gopath/src/github.com/kubernetes-csi/csi-test/pkg/sanity/controller.go:111
STEP: connecting to CSI driver
STEP: creating mount and staging directories
STEP: checking successful response
•
------------------------------
Controller Service [Controller Server] GetCapacity
should return capacity (no optional values added)
/nvme/gopath/src/github.com/kubernetes-csi/csi-test/pkg/sanity/controller.go:149
STEP: reusing connection to CSI driver at dns:///127.0.0.1:30056
STEP: creating mount and staging directories
...
Ran 53 of 72 Specs in 148.206 seconds
SUCCESS! -- 53 Passed | 0 Failed | 0 Pending | 19 Skipped
PASS
一些評論
- 這些測試的原始碼位於
pkg/sanity
包中。 - 如何確定節點的外部 IP 地址取決於叢集。在此示例中,叢集是使用
hack/local-up-cluster.sh
啟動的,因此它在本地主機 (127.0.0.1
) 上執行。它使用 Kubernetes 分配的埠,如上文透過kubectl get "services/hostpath-service"
獲得。Kubernetes-CSI CI 使用 kind,並且 Docker 命令可以在那裡使用。 - 建立指令碼必須列印最終目錄。為每個測試用例使用唯一的目錄的優點是,如果在一個測試用例中出現問題,其他測試用例仍然可以從頭開始。
- “暫存目錄”,即 CSI 規範中的
NodePublishVolumeRequest.target_path
,必須由 CSI 驅動程式建立和刪除,而 CO 負責父目錄。csi-sanity
透過建立一個目錄,然後將該目錄路徑附加/target
交給 CSI 驅動程式來處理。Kubernetes 在這方面出了錯,並建立了實際的target_path
目錄,因此希望與 Kubernetes 配合使用的 CSI 驅動程式目前必須寬容,並且在該目錄已存在時不得失敗。 - “掛載目錄”對應於
NodeStageVolumeRequest.staging_target_path
,並且確實由 CO(即csi-sanity
)建立。
端到端測試
與 csi-sanity
不同,端到端測試透過 Kubernetes API 與 CSI 驅動程式互動,即它模擬普通使用者的操作,例如建立 PersistentVolumeClaim。在 Kubernetes 1.14.0 中新增了對測試外部 CSI 驅動程式的支援。
安裝
對於每個 Kubernetes 版本,都會發佈一個測試 tar 歸檔檔案。它沒有在釋出說明中列出(例如,1.16 的釋出說明),所以必須知道完整的 URL 是 https://dl.k8s.io/<version>/kubernetes-test-linux-amd64.tar.gz
(例如 v1.16.0)。
這些包括一個用於 x86-64 Linux 的 e2e.test
二進位制檔案。其他平臺的歸檔檔案也可用,請參閱此 KEP。e2e.test
二進位制檔案是完全自包含的,因此可以“安裝”它和 ginkgo
測試執行程式,如下所示
curl --location https://dl.k8s.io/v1.16.0/kubernetes-test-linux-amd64.tar.gz | \
tar --strip-components=3 -zxf - kubernetes/test/bin/e2e.test kubernetes/test/bin/ginkgo
每個 e2e.test
二進位制檔案都包含與相應版本中可用功能匹配的測試。特別是,[Feature: xyz]
標籤在不同版本之間發生變化:它們將 Alpha 功能的測試與非 Alpha 功能的測試分開。此外,舊版本中的測試可能依賴於在最新 Kubernetes 版本中刪除的 API。為了避免問題,最好只使用與用於測試的 Kubernetes 版本匹配的 e2e.test
二進位制檔案。
用法
並非所有 CSI 驅動程式的功能都可以透過 Kubernetes API 發現。因此,需要一個 YAML 或 JSON 格式的配置檔案來描述要測試的驅動程式。該檔案用於填充 driverDefinition 結構和嵌入在其中的 DriverInfo 結構。有關單個欄位的詳細使用說明,請參閱這些結構。
警告:測試通常只在設定某些欄位時執行,並且檔案解析器不會警告未知欄位,因此請務必檢查檔案是否確實與這些結構匹配。
這是一個測試 csi-driver-host-path
的示例
$ cat >test-driver.yaml <<EOF
StorageClass:
FromName: true
SnapshotClass:
FromName: true
DriverInfo:
Name: hostpath.csi.k8s.io
Capabilities:
block: true
controllerExpansion: true
exec: true
multipods: true
persistence: true
pvcDataSource: true
snapshotDataSource: true
InlineVolumes:
- Attributes: {}
EOF
至少,您需要定義要在測試中使用的儲存類、驅動程式的名稱以及要測試的功能。與 csi-sanity
一樣,驅動程式必須在測試之前在叢集中執行。實際的 e2e.test
呼叫然後使用 -storage.testdriver
啟用此驅動程式的測試,並使用 -ginkgo.focus
選擇其儲存測試
$ ./e2e.test -ginkgo.v \
-ginkgo.focus='External.Storage' \
-storage.testdriver=test-driver.yaml
Oct 8 17:17:42.230: INFO: The --provider flag is not set. Continuing as if --provider=skeleton had been used.
I1008 17:17:42.230210 648569 e2e.go:92] Starting e2e run "90b9adb0-a3a2-435f-80e0-640742d56104" on Ginkgo node 1
Running Suite: Kubernetes e2e suite
===================================
Random Seed: 1570547861 - Will randomize all specs
Will run 163 of 5060 specs
Oct 8 17:17:42.237: INFO: >>> kubeConfig: /var/run/kubernetes/admin.kubeconfig
Oct 8 17:17:42.241: INFO: Waiting up to 30m0s for all (but 0) nodes to be schedulable
...
------------------------------
SSSSSSSSSSSSSSSSSSSS
------------------------------
External Storage [Driver: hostpath.csi.k8s.io] [Testpattern: Dynamic PV (filesystem volmode)] multiVolume [Slow]
should access to two volumes with different volume mode and retain data across pod recreation on the same node
/workspace/anago-v1.16.0-rc.2.1+2bd9643cee5b3b/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/storage/testsuites/multivolume.go:191
[BeforeEach] [Testpattern: Dynamic PV (filesystem volmode)] multiVolume [Slow]
...
您可以使用 ginkgo
並行執行某些型別的測試。Alpha 功能測試或那些設計上必須按順序執行的測試則需要單獨執行
$ ./ginkgo -p -v \
-focus='External.Storage' \
-skip='\[Feature:|\[Disruptive\]|\[Serial\]' \
./e2e.test \
-- \
-storage.testdriver=test-driver.yaml
$ ./ginkgo -v \
-focus='External.Storage.*(\[Feature:|\[Disruptive\]|\[Serial\])' \
./e2e.test \
-- \
-storage.testdriver=test-driver.yaml
參與進來
Kubernetes 儲存測試和健全性測試都旨在適用於任意 CSI 驅動程式。但是,測試可能基於額外的假設,並且您的驅動程式雖然符合 CSI 規範,但未能透過測試。如果發生這種情況,請提交問題(下方連結)。
這些是開源專案,依賴於使用者的幫助,因此一旦問題得到確認,我們將非常歡迎解決該問題的拉取請求。
編寫新測試也適用同樣的情況。以下問題跟蹤器中的搜尋專門選擇標記為需要某人幫助的問題
測試愉快!願它發現的問題少且易於修復。