OpenStack容器服務Zun初探與原理分析
01
Zun服務簡介
Zun是OpenStack的容器服務(Containers as Service),類似於AWS的ECS服務,但實現原理不太一樣,ECS是把容器啟動在EC2虛擬機器例項上,而Zun會把容器直接執行在compute節點上。
和OpenStack另一個容器相關的Magnum專案不一樣的是:Magnum提供的是容器編排服務,能夠提供彈性Kubernetes、Swarm、Mesos等容器基礎設施服務,管理的單元是Kubernetes、Swarm、Mesos叢集,而Zun提供的是原生容器服務,支援不同的runtime如Docker、Clear Container等,管理的單元是container。
Zun服務的架構如圖:
Zun服務和Nova服務的功能和結構非常相似,只是前者提供容器服務,後者提供虛擬機器服務,二者都是主流的計算服務交付模式。功能類似體現在如下幾點:
透過Neutron提供網路服務。
透過Cinder實現資料的持久化儲存。
都支援使用Glance儲存映象。
其他如quota、安全組等功能。
元件結構結構相似則表現在:
二者都是由API、排程、計算三大元件模組構成,Nova由nova-api、nova-scheduler、nova-compute三大核心元件構成,而Zun由zun-api、zun-compute兩大核心元件構成,之所以沒有zun-scheduler是因為scheduler整合到zun-api中了。
nova-compute呼叫compute driver建立虛擬機器,如Libvirt。zun-compute呼叫container driver建立容器,如Docker。
Nova透過一系列的proxy代理實現VNC(nova-novncproxy)、Splice(nova-spiceproxy)等虛擬終端訪問,Zun也是透過proxy代理容器的websocket實現遠端attach容器功能。
02
Zun服務部署
Zun服務部署和Nova、Cinder部署模式類似,控制節點建立資料庫、Keystone建立service以及註冊endpoints等,最後安裝相關包以及初始化配置。計算節點除了安裝zun-compute服務,還需要安裝要使用的容器,比如Docker。詳細的安裝過程可以參考官方文件,如果僅僅是想進行POC測試,可以透過DevStack自動化快速部署一個AllInOne環境,供參考的local.conf配置檔案如下:
如上配置會自動透過DevStack安裝Zun相關元件、Kuryr元件以及Docker。
03
Zun服務入門
3.1 Dashboard
安裝Zun服務之後,可以透過zun命令列以及Dashboard建立和管理容器。
有一個非常讚的功能是如果安裝了Zun,Dashboard能夠支援Cloud Shell,使用者能夠在DashBoard中進行互動式輸入OpenStack命令列。
原理的話就是透過Zun啟動了一個gbraad/openstack-client:alpine容器。
透過Dashboard建立容器和建立虛擬機器的過程非常相似,都是透過panel依次選擇映象(image)、選擇規格(Spec)、選擇或者建立卷(volume)、選擇網路(network/port)、選擇安全組(SecuiryGroup)以及scheduler hint,如圖:
其中Miscellaneous雜項中則為針對容器的特殊配置,比如設定環境變數(Environment)、工作目錄(Working Directory)等。
3.2 命令列操作
透過命令列建立容器也非常類似,使用過nova以及docker命令列的基本不會有困難,下面以建立一個mysql容器為例:
如上透過--mount引數指定了volume大小,由於沒有指定volume_id,因此Zun會新建立一個volume。需要注意的是,Zun建立的volume在容器刪除後,volume也會自動刪除(auto remove),如果需要持久化volume卷,則應該先透過Cinder建立一個volume,然後透過source選項指定volume_id,此時當容器刪除時不會刪除已有的volume卷。
和虛擬機器不一樣,虛擬機器透過flavor配置規格,容器則直接指定cpu、memory、disk。
如上沒有指定--image-driver引數,則預設從dockerhub下載映象,如果指定glance,則會往glance下載映象。
另外mysql容器初始化時資料卷必須為空目錄,掛載的volume新卷格式化時會自動建立lost+found目錄,因此需要手動刪除,否則mysql容器會初始化失敗:
建立完成後可以透過zun list命令檢視容器列表:
可以看到mysql的容器fixed IP為192.168.233.80,和虛擬機器一樣,租戶IP預設與外面不通,需要繫結一個浮動IP(floating ip),
zun命令列目前還無法檢視floating ip,只能透過neutron命令檢視,獲取到floatingip並且安全組入訪允許3306埠後就可以遠端連線mysql服務了:
當然在同一租戶的虛擬機器也可以直接透過fixed ip訪問mysql服務:
可見,透過容器啟動mysql服務和在虛擬機器裡面部署mysql服務,使用者訪問上沒有什麼區別,在同一個環境中,虛擬機器和容器可共存,彼此可相互通訊,在應用層上可以完全把虛擬機器和容器透明化使用,底層透過應用場景選擇虛擬機器或者容器。
3.3 關於capsule
Zun除了管理容器container外,還引入了capsule的概念,capsule類似Kubernetes的pod,一個capsule可包含多個container,這些container共享network、ipc、pid namespace等。
透過capsule啟動一個mysql服務,宣告yaml檔案如下:
建立mysql capsule:
可見capsule的init container用的就是kubernetes的pause映象。
3.4 總結
OpenStack的容器服務本來是在Nova中實現的,實現了Nova ComputeDriver,因此Zun的其他的功能如容器生命週期管理、image管理、service管理、action管理等和Nova虛擬機器非常類似,可以檢視官方文件,這裡不再贅述。
04
Zun實現原理
4.1 呼叫容器介面實現容器生命週期管理
前面提到過Zun主要由zun-api和zun-compute服務組成,zun-api主要負責接收使用者請求、引數校驗、資源準備等工作,而zun-compute則真正負責容器的管理,Nova的後端透過compute_driver配置,而Zun的後端則透過container_driver配置,目前只實現了DockerDriver。因此呼叫Zun建立容器,最終就是zun-compute呼叫docker建立容器。
下面以建立一個container為例,簡述其過程。
4.1.1 zun-api
首先入口為zun-api,主要程式碼實現在zun/api/controllers/v1/containers.py以及zun/compute/api.py,建立容器的方法入口為post()方法,其呼叫過程如下:
zun/api/controllers/v1/containers.py
policy enforce: 檢查policy,驗證使用者是否具有建立container許可權的API呼叫。
check security group: 檢查安全組是否存在,根據傳遞的名稱返回安全組的ID。
check container quotas: 檢查quota配額。
build requested network: 檢查網路配置,比如port是否存在、network id是否合法,最後構建內部的network物件模型字典。注意,這一步只檢查並沒有建立port。
create container object:根據傳遞的引數,構造container物件模型。
build requeted volumes: 檢查volume配置,如果傳遞的是volume id,則檢查該volume是否存在,如果沒有傳遞volume id只指定了size,則呼叫Cinder API建立新的volume。
zun/compute/api.py
schedule container: 使用FilterScheduler排程container,返回宿主機的host物件。這個和nova-scheduler非常類似,只是Zun整合到zun-api中了。目前支援的filters如CPUFilter、RamFilter、LabelFilter、ComputeFilter、RuntimeFilter等。
image validation: 檢查映象是否存在,這裡會遠端呼叫zun-compute的image_search方法,其實就是呼叫docker search。這裡主要為了實現快速失敗,避免到了compute節點才發現image不合法。
record action: 和Nova的record action一樣,記錄container的操作日誌。
rpc cast container_create: 遠端非同步呼叫zun-compute的container_create()方法,zun-api任務結束。
4.1.2 zun-compute
zun-compute負責container建立,程式碼位於zun/compute/manager.py,過程如下:
wait for volumes avaiable: 等待volume建立完成,狀態變為avaiable。
attach volumes:掛載volumes,掛載過程後面再介紹。
checksupportdisk_quota: 如果使用本地盤,檢查本地的quota配額。
pull or load image: 呼叫Docker拉取或者載入映象。
建立docker network、建立neutron port,這個步驟下面詳細介紹。
create container: 呼叫Docker建立容器。
container start: 呼叫Docker啟動容器。
以上呼叫Dokcer拉取映象、建立容器、啟動容器的程式碼位於zun/container/docker/driver.py,該模組基本就是對社群Docker SDK for Python的封裝。
Zun的其他操作比如start、stop、kill等實現原理也類似,這裡不再贅述。
4.2 透過websocket實現遠端容器訪問
我們知道虛擬機器可以透過VNC遠端登入,物理伺服器可以透過SOL(IPMI Serial Over LAN)實現遠端訪問,容器則可以透過websocket介面實現遠端互動訪問。
Docker原生支援websocket連線,參考APIAttach to a container via a websocket,websocket地址為/containers/{id}/attach/ws,不過只能在計算節點訪問,那如何透過API訪問呢?
和Nova、Ironic實現完全一樣,也是透過proxy代理轉發實現的,負責container的websocket轉發的程式為zun-wsproxy。
當呼叫zun-compute的container_attach()方法時,zun-compute會把container的websocket_url以及websocket_token儲存到資料庫中.
zun-wsproxy則可讀取container的websocket_url作為目標端進行轉發:
透過Dashboard可以遠端訪問container的shell:
當然透過命令列zun attach也可以attach container。
4.3 使用Cinder實現容器持久化儲存
前面介紹過Zun透過Cinder實現container的持久化儲存,之前我的另一篇文章介紹了Docker使用OpenStack Cinder持久化volume原理分析及實踐,介紹了john griffith開發的docker-cinder-driver以及OpenStack Fuxi專案,這兩個專案都實現了Cinder volume掛載到Docker容器中。另外cinderclient的擴充套件模組python-brick-cinderclient-ext實現了Cinder volume的local attach,即把Cinder volume掛載到物理機中。
Zun沒有複用以上的程式碼模組,而是重新實現了volume attach的功能,不過實現原理和上面的方法完全一樣,主要包含如下過程:
connect volume: connect volume就是把volume attach(對映)到container所在的宿主機上,建立連線的的協議透過initialize_connection資訊獲取,如果是LVM型別則一般透過iscsi,如果是Ceph rbd則直接使用rbd map。
ensure mountpoit tree: 檢查掛載點路徑是否存在,如果不存在則呼叫mkdir建立目錄。
make filesystem:如果是新的volume,掛載時由於沒有檔案系統因此會失敗,此時會建立檔案系統。
do mount: 一切準備就緒,呼叫OS的mount介面掛載volume到指定的目錄點上。
Cinder Driver的程式碼位於`zun/volume/driver.py的Cinder類中,方法如下:
其中cinder.attach_volume()實現如上的第1步,而_mount_device()實現瞭如上的2-4步。
4.4 整合Neutron網路實現容器網路多租戶
4.4.1 關於容器網路
前面我們透過Zun建立容器,使用的就是Neutron網路,意味著容器和虛擬機器完全等同的共享Neutron網路服務,虛擬機器網路具有的功能,容器也能實現,比如多租戶隔離、floating ip、安全組、防火牆等。
Docker如何與Neutron網路整合呢?根據官方Docker network plugin API介紹,外掛位於如下目錄:
/run/docker/plugins
/etc/docker/plugins
/usr/lib/docker/plugins
由此可見Docker使用的是kuryr網路外掛。
Kuryr也是OpenStack中一個較新的專案,其目標是“Bridge between container framework networking and storage models to OpenStack networking and storage abstractions.”,即實現容器與OpenStack的網路與儲存整合,當然目前只實現了網路部分的整合。
而我們知道目前容器網路主要有兩個主流實現模型:
CNM:Docker公司提出,Docker原生使用的該方案,透過HTTP請求呼叫,模型設計可參考The Container Network Model Design,network外掛可實現兩個Driver,其中一個為IPAM Driver,用於實現IP地址管理,另一個為Docker Remote Drivers,實現網路相關的配置。
CNI:CoreOS公司提出,Kubernetes選擇了該方案,透過本地方法或者命令列呼叫。
因此Kuryr也分成兩個子專案,kuryr-network實現CNM介面,主要為支援原生的Docker,而kury-kubernetes則實現的是CNI介面,主要為支援Kubernetes,Kubernetes service還整合了Neutron LBaaS,下次再單獨介紹這個專案。
由於Zun使用的是原生的Docker,因此使用的是kuryr-network專案,實現的是CNM介面,透過remote driver的形式註冊到Docker libnetwork中,Docker會自動向外掛指定的socket地址傳送HTTP請求進行網路操作,我們的環境是,即kuryr-libnetwork.service監聽的地址,Remote API介面可以參考Docker Remote Drivers。
4.4.2 kuryr實現原理
前面4.1節介紹到zun-compute會呼叫docker driver的create()方法建立容器,其實這個方法不僅僅是呼叫python docker sdk的create_container()方法,還做了很多工作,其中就包括網路相關的配置。
首先檢查Docker的network是否存在,不存在就建立,network name為Neutron network的UUID,
然後會呼叫Neutron建立port,從這裡可以得出結論,容器的port不是Docker libnetwork也不是Kuryr建立的,而是Zun建立的。
回到前面的Remote Driver,Docker libnetwork會首先POST呼叫kuryr的/IpamDriver.RequestAddressAPI請求分配IP,但顯然前面Zun已經建立好了port,port已經分配好了IP,因此這個方法其實就是走走過場。如果直接呼叫docker命令指定kuryr網路建立容器,則會呼叫該方法從Neutron中建立一個port。
接下來會POST呼叫kuryr的/NetworkDriver.CreateEndpoint方法,這個方法最重要的步驟就是binding,即把port attach到宿主機中,binding操作單獨分離出來為kuryr.lib庫,這裡我們使用的是veth driver,因此由kuryr/lib/binding/drivers/veth.py模組的port_bind()方法實現,該方法建立一個veth對,其中一個為tap-xxxx,xxxx為port ID字首,放在宿主機的namespace,另一個為t_cxxxx放到容器的namespace,t_cxxxx會配置上IP,而tap-xxxx則呼叫shell指令碼(指令碼位於/usr/local/libexec/kuryr/)把tap裝置新增到ovs br-int橋上,如果使用HYBRID_PLUG,即安全組透過Linux Bridge實現而不是OVS,則會建立qbr-xxx,並建立一個veth對關聯到ovs br-int上。
從這裡可以看出,Neutron port繫結到虛擬機器和容器基本沒有什麼區別,如下所示:
唯一不同的就是虛擬機器是把tap裝置直接對映到虛擬機器的虛擬裝置中,而容器則透過veth對,把另一個tap放到容器的namespace中。
有人會說,br-int的流表在哪裡更新了?這其實是和虛擬機器是完全一樣的,當呼叫port update操作時,neutron server會傳送RPC到L2 agent中(如neutron-openvswitch-agent),agent會根據port的狀態更新對應的tap裝置以及流表。
因此其實kuryr只幹了一件事,那就是把Zun申請的port繫結到容器中。
05
總結
OpenStack Zun專案非常完美地實現了容器與Neutron、Cinder的整合,加上Ironic裸機服務,OpenStack實現了容器、虛擬機器、裸機共享網路與儲存。未來我覺得很長一段時間內裸機、虛擬機器和容器將在資料中心混合存在,OpenStack實現了容器和虛擬機器、裸機的完全平等、資源共享以及功能對齊,應用可以根據自己的需求選擇容器、虛擬機器或者裸機,使用上沒有什麼區別,使用者只需要關心業務針對效能的需求以及對硬體的特殊訪問,對負載(workload)是完全透明的。
參考文獻
docker python sdk:
Zun’s documentation:
使用OpenStack-Cinder持久化volume原理分析及實踐/
https://www.nuagenetworks.net/blog/container-networking-standards/
http://blog.kubernetes.io/2016/01/why-Kubernetes-doesnt-use-libnetwork.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69940582/viewspace-2658766/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Laravel 服務容器實現原理Laravel
- 事件與服務容器事件
- Nacos 服務註冊與發現原理分析
- 服務管理與通訊,基礎原理分析
- OpenStack中的服務型別型別
- Laravel服務容器Laravel
- Laravel修煉:服務容器繫結與解析Laravel
- 深入Laravel服務容器Laravel
- Openstack的Heat服務api支援HTTPSAPIHTTP
- 編譯原理與javacc初探編譯原理Java
- Vue 2 服務端渲染初探Vue服務端
- Nacos服務註冊與發現原理
- 建立sshd服務容器,並使宿主機與容器免密通訊
- 深入剖析 Laravel 服務容器Laravel
- Docker容器服務搭建(一)Docker
- openstack基礎構架以及服務方式解析
- 閒聊雲服務計算技術–OpenStack
- OpenStack collectd的從零安裝服務端服務端
- 閒聊雲服務計算技術--OpenStack
- PHP中的服務容器與依賴注入的思想PHP依賴注入
- Nacos服務註冊與發現的原理
- k8s叢集容器外部與容器內部服務互相訪問K8S
- 「Laravel 服務容器」自己的理解Laravel
- 簡單優化容器服務優化
- Laravel原始碼解析 — 服務容器Laravel原始碼
- Laravel 服務容器和提供器Laravel
- web應用服務端cache策略初探Web服務端
- Symfony 服務容器:使用 XML 或 YAML 檔案描述服務XMLYAML
- 初探Nacos(二)-- SpringCloud使用Nacos的服務註冊與發現SpringGCCloud
- 雲端計算管理平臺之OpenStack映象服務glance
- Laravel 服務容器、服務提供器、契約例項講解Laravel
- 精講Redis服務架構分析與搭建Redis架構
- 初探STL容器之Vector
- 順序容器初探(上)
- 第三話 初探容器
- Java服務端容器化:Docker與Kubernetes的應用Java服務端Docker
- Postgres On Docker-窺探容器服務Docker
- Laravel 重點概念理解-服務容器Laravel