這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在《使用Ceph RBD為Kubernetes叢集提供儲存卷》一文中,我們提到:藉助Kubernetes和Ceph的整合,Kubernetes可以使用Ceph RBD為叢集內的Pod提供Persistent Volume。但這一過程中,RBD所使用的image的建立、刪除還需要手動管理,於是我們又基於go-ceph實現了對RBD image的程式化管理,我們的最終目標是要這種對RBD image的管理服務以一個K8s service的形式發布到Kubernetes叢集中去,這就是本文標題中描述的那樣:Kuberize Ceph RBD API服務。
一、Dockerize Ceph RBD API服務
要想使得ceph rbd api Kuberizable,首先要Dockerize Ceph RBD API Service,即容器化。由於go-ceph是Go語言開發,我們的rbd-rest-api同樣用Go語言開發。使用Go語言開發有一個眾所周知的好處,那就是可以編譯為靜態二進位檔案,可以在運行時不依賴任何外部庫,生來內建“適合容器”標籤。但由於go-ceph是一個go binding for librados和librbd,其通過cgo實現Go語言對C庫的連結和調用。這樣一來,我們如果要做static linking,那麼我們就要準備齊全所有librados和librbd所依賴的第三方庫的.a(archive file)。如果你僅僅是執行下面編譯命令,你將得到w行層級的錯誤資訊輸出:
$ go build --ldflags '-extldflags "-static"' .
從錯誤的資訊中,我們可以得到rbd-rest-api靜態編譯依賴的各種第三方庫,包括boost庫(apt-get install libboost-all-dev)、libssl(apt-get install libssl)以及libnss3(apt-get install libnss3-dev)。安裝好這些庫,再修改一下命令列,可將編譯錯誤輸出降低到百行以內:
# go build --ldflags '-extldflags "-static -L /usr/lib/x86_64-linux-gnu -lboost_system -lboost_thread -lboost_iostreams -lboost_random -lcrypto -ldl -lpthread -lm -lz -lc -L /usr/lib/gcc/x86_64-linux-gnu/4.8/ -lstdc++"' .
不過,你將依舊得到諸多錯誤:
... .../usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/librados.a(Crypto.o): In function `CryptoAESKeyHandler::init(ceph::buffer::ptr const&, std::basic_ostringstream, std::allocator >&)':/build/ceph-10.2.3/src/auth/Crypto.cc:280: undefined reference to `PK11_GetBestSlot'/build/ceph-10.2.3/src/auth/Crypto.cc:291: undefined reference to `PK11_ImportSymKey'/build/ceph-10.2.3/src/auth/Crypto.cc:304: undefined reference to `PK11_ParamFromIV'/build/ceph-10.2.3/src/auth/Crypto.cc:282: undefined reference to `PR_GetError'/build/ceph-10.2.3/src/auth/Crypto.cc:293: undefined reference to `PR_GetError'... ...
這些”undefined reference”指向的符號都是libnss3-dev庫中的,但由於libnss3-dev的安裝並沒有包含libnss3.a檔案,因此即便將libnss3顯式放在連結參數列表中,比如:”-lnss3″也無法連結成功:
/usr/bin/ld: cannot find -lnss3
libnss庫著實不是一個省油燈,經過幾番折騰發現,要想使用libnss的static archive,我們只能手工編譯,代碼在這裡可以擷取到:https://github.com/nss-dev/nss,並且這裡提供了nss的手工編譯方法。
綜上可以看出,純靜態編譯rbd-rest-api是很繁瑣的,於是我們這次選擇預設的動態連結方式,我們只需在docker image中安裝librados和librbd這兩個依賴庫即可,於是rbd-rest-api的Dockerfile的雛形可見:
From ubuntu:14.04MAINTAINER Tony Bai # use aliyun source for ubuntu# before building image ,make sure copy /etc/apt/sources.list here# COPY sources.list /etc/apt/RUN apt-get update && apt-get install -y --no-install-recommends librados-dev librbd-dev \ && rm -rf /var/lib/apt/lists/*RUN mkdir -p /root/rbd-rest-apiCOPY rbd-rest-api /root/rbd-rest-apiCOPY conf /root/rbd-rest-api/confRUN chmod +x /root/rbd-rest-api/rbd-rest-apiEXPOSE 8080WORKDIR /root/rbd-rest-apiENTRYPOINT ["/root/rbd-rest-api/rbd-rest-api"]
我們一直在Ubuntu 14.04.x環境下進行各種測試,於是我們自然而然的選擇ubuntu:14.04作為我們的base image,構建鏡像:
# docker build -t "test/rbd-rest-api" .... ...Setting up librados-dev (0.80.11-0ubuntu1.14.04.1) ...Setting up librbd-dev (0.80.11-0ubuntu1.14.04.1) ...Processing triggers for libc-bin (2.19-0ubuntu6.9) ... ---> c987abc7a24dRemoving intermediate container 5257ac37392aStep 5 : RUN mkdir -p /root/rbd-rest-api ---> Running in dcabdb990c60 ---> ce0db2a027aaRemoving intermediate container dcabdb990c60Step 6 : COPY rbd-rest-api /root/rbd-rest-api ---> 453fd4b9a27aRemoving intermediate container 8b07b5de7537Step 7 : COPY conf /root/rbd-rest-api/conf ---> e956add07d60Removing intermediate container 6eaf6e4cf334Step 8 : RUN chmod +x /root/rbd-rest-api/rbd-rest-api ---> Running in cb278d1919c7 ---> 1e7b86072011Removing intermediate container cb278d1919c7Step 9 : EXPOSE 8080 ---> Running in 6a3f457eefca ---> e60cefb50f77Removing intermediate container 6a3f457eefcaStep 10 : WORKDIR /root/rbd-rest-api ---> Running in 703baf8c5564 ---> 6f1a5e5e145cRemoving intermediate container 703baf8c5564Step 11 : ENTRYPOINT /root/rbd-rest-api/rbd-rest-api ---> Running in 16dd4e7e3995 ---> 43f885b958c7Removing intermediate container 16dd4e7e3995Successfully built 43f885b958c7# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEtest/rbd-rest-api latest 43f885b958c7 57 seconds ago 298 MB
測試啟動鏡像,注意我們“唯讀”掛載了本地路徑/etc/ceph:
# docker run --name rbd-rest-api --rm -p 8080:8080 -v /etc/ceph/:/etc/ceph/:ro test/rbd-rest-api2016/11/14 14:58:17 [I] [asm_amd64.s:2086] http server Running on http://:8080
我們來測試一下這個Docker中的rbd-rest-api service:
# curl -v http://localhost:8080/api/v1/pools/* Hostname was NOT found in DNS cache* Trying 127.0.0.1...* Connected to localhost (127.0.0.1) port 8080 (#0)> GET /api/v1/pools/ HTTP/1.1> User-Agent: curl/7.35.0> Host: localhost:8080> Accept: */*>< HTTP/1.1 200 OK< Content-Length: 130< Content-Type: application/json; charset=utf-8* Server beegoServer:1.7.1 is not blacklisted< Server: beegoServer:1.7.1< Date: Mon, 14 Nov 2016 14:59:29 GMT<{ "Kind": "PoolList", "APIVersion": "v1", "Items": [ { "name": "rbd" }, { "name": "rbd1" } ]* Connection #0 to host localhost left intact}
測試OK。
這裡不得不提的是:如果你掛載的是僅僅是/etc/ceph/ceph.conf的話,那麼當rbd-rest-api服務收到請求後,會返回:
Errcode=300, errmsg:error rados: No such file or directory
這是因為容器中的rbd-rest-api沒有看到ceph.client.admin.keyring,因此在登入ceph monitor時鑒權失敗了。當然你也可以不映射本地目錄,取而代之的是將/etc/ceph/ceph.conf和/etc/ceph/ceph.client.admin.keyring放入到鏡像中,後一種方法這裡就不詳細描述了。librados給出的錯誤提示真是太差了,本來應該是一個許可權的問題,居然說找不到librados。
二、Kuberize Ceph RBD API服務
容器化測試成功了,接下來就是將Ceph RBD API Kuberize化。根據上面Docker鏡像的設計,承載Ceph RBD API服務 Pod的Node上,必須要安裝了Ceph client,即包括ceph.conf和ceph.client.admin.keyring,於是有選擇性的調度Ceph RBD API服務到安裝了ceph client的kubernetes node上是這一節必須考慮的問題。
我們的思路是將rbd-rest-api的pod通過k8s調度到帶有指定label的k8s node上去,我們給kubernetes叢集的node打標籤,安裝了ceph client的叢集node,打的標籤為:zone=ceph。
# kubectl label nodes 10.46.181.146 zone=ceph# kubectl label nodes 10.47.136.60 zone=ceph# kubectl get nodes --show-labelsNAME STATUS AGE LABELS10.46.181.146 Ready 32d beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.46.181.146,zone=ceph10.47.136.60 Ready 32d beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.47.136.60,zone=ceph
接下來就是在rbd-rest-api service的yaml中設定pod的調度策略了:
//rbd-rest-api.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata: name: rbd-rest-apispec: replicas: 2 template: metadata: labels: app: rbd-rest-api spec: containers: - name: rbd-rest-api image: registry.cn-hangzhou.aliyuncs.com/xxxx/rbd-rest-api:latest #imagePullPolicy: IfNotPresent imagePullPolicy: Always ports: - containerPort: 8080 volumeMounts: - mountPath: /etc/ceph name: ceph-default-config-volume volumes: - name: ceph-default-config-volume hostPath: path: /etc/ceph nodeSelector: zone: ceph imagePullSecrets: - name: rbd-rest-api-default-secret---apiVersion: v1kind: Servicemetadata: name: rbd-rest-api labels: app: rbd-rest-apispec: ports: - port: 8080 selector: app: rbd-rest-api
我們可以看到在Deployment的spec中有一個nodeSelector,這個設定可以讓k8s scheduler在調度service時只選擇具備zone=ceph label的Node。注意關於imagePullSecrets的設定,可以參考《Kubernetes從Private Registry中拉取容器鏡像的方法》一文。
2016, bigwhite. 著作權.