您好,登錄后才能下訂單哦!
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請求進行網絡操作,我們的環境是http://127.0.0.1:23750,即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: https://docker-py.readthedocs.io/en/stable/
Zun’s documentation: https://docs.openstack.org/zun/latest/
https://docs.docker.com/engine/api/v1.39/#operation/ContainerAttachWebsocket
http://int32bit.me/2017/10/04/Docker使用OpenStack-Cinder持久化volume原理分析及實踐/
https://specs.openstack.org/openstack/cinder-specs/specs/mitaka/use-cinder-without-nova.html
https://docs.docker.com/engine/extend/plugin_api/
https://github.com/docker/libnetwork/blob/master/docs/design.md
https://github.com/docker/libnetwork/blob/master/docs/ipam.md
https://github.com/docker/libnetwork/blob/master/docs/remote.md
https://docs.openstack.org/kuryr-libnetwork/latest/
https://docs.openstack.org/magnum/latest/user/
https://github.com/docker/libnetwork
https://www.nuagenetworks.net/blog/container-networking-standards/
http://blog.kubernetes.io/2016/01/why-Kubernetes-doesnt-use-libnetwork.html
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。