Docker結合OpenStack在思源的經驗分享
大家好,谷锎,目前就職于思源科技集團的云服務中心, 任技術經理,負責OpenStack 和 Docker相關開發維護工作。目前對Docker在OpenStack環境的有一些測試和生產經驗,愿意與大家分享探討。
無Docker不OpenStack,當前討論OpenStack總是離不開Docker。這里我先嚼一下剩飯,下面是OpenStack上Docker技術分布的老圖。
我們包括生產化/測試/調研階段的Docker化項目包括了:Heat、Magnum、Sahara、Nova、以及OpenStack平臺本身的自動打包和平臺穩定性測試方面。
1. Docker OpenStack平臺穩定性測試
OpenStack平臺本身是一個SOA的項目,具體服務的參數設置需要依據集群規模,服務搭建架構等進行相關測試和調優。Fake是OpenStack Nova Compute下的一個Driver,絕大多數Compute API走到這里簡單處理后返回成功。
我們使用Docker來封裝Nova Compute,并在Nova 配置中使用fake。這樣每個Docker Container便成為一個虛擬的Nova Hypervisor Node, 便可以模擬Controller 集群管理超大量Compute節點的狀況;同時fake driver 收到請求直接返回成功的特性,讓我們可以測試超大量的VM同時創建和同時銷毀時給控制節點和MQ帶來的壓力狀況。
這里Docker模擬了物理服務器,解決了測試服務器不足的狀況。這只是一個測試例子,由于測試的不同需求,可能 Nova Fake需要頻繁的變更配置,Docker 的快速啟動和快速銷毀也提供了變更測試環境的便利,Dockerfile的定制化需求不僅為鏡像頻繁變更帶來方便也讓測試環境本身更易追溯。
2. OpenStack 自動打包
個人認為私有云平臺壓力通常沒有公有云高,但是個性化定制更強。我們內部的定制化需求也很高(例如集群中計算資源的主機級別和機架級別的反親和等等)。OpenStack的平臺的組件需要頻繁更新。
我們內部使用的是Puppet推送RPM更新的方式進行,且維護了兩個OpenStack的版本,大量編譯的依賴和依賴的沖突以及編譯后的臟數據成為我們的痛點。于是我們將OpenStack所涉及的包括Nova、Nueturn、Glance、Cinder、Trove、Sahara等等項目的編譯依賴環境統統放進一個Docker Image中。
我們維護了一個腳本,通過參數來指定要編譯的OpenStack的版本和組件。該腳本會自動從Docker Registry服務器中pull一個指定版本的的編譯環境鏡像。并將GIT服務器其中指定版本的分支代碼clone到容器中,通過掛卷的方式將編譯后的RPM包輸出到外部打包服務器上。編譯結束后輸出編譯的狀態結果。
這樣我們便不需要再維護一個編譯環境了,只需要維護編譯鏡像和GIT庫內部源碼。可以在任意筆記本環境來生成打包環境。
3. Docker 加速 Sahara
Sahara是OpenStack中 "大數據即服務"的項目,支持Hadoop、Spark、CDH 5.x等。通過Heat編排可以使用KVM或者Docker作為計算資源。我們測試使用了Hadoop的服務,通過運行KVM和Docker的測試,Docker在啟動速度、資源利用率、以及性能開銷上具有優勢,我這里簡單羅列一下測試對比。
基本測試環境
服務器:2臺 24Core 128G memory
hadoop: 1.2
job:*streaming MapperReduce
集群規模10
說明:
1. 不同的配置參數,不同的mapreduce程序,hadoop計算的時間都不相同,這里只是給出在相同環境下docker和kvm建的差別
2. Docker的測試數據是在Container內部的。
4. Docker Nova項目
這個是大家爭議最大的項目,不過對于我們平臺來說,服務云化這是第一步。需要其他開發團隊逐步熟悉面向容器的開發以及我們對Docker本身逐步的摸索,才敢真正把環境切換到Kubernetes/Mesos上來,進而推進Magnum。借用京東鮑永成的那句話:”讓能夠接受新世界的團隊慢慢先適應“。
通過Nova API調度Nova Compute生產 Nova instance,而Nova instance的類型由具體配置的hypervisor Driver來決定,這里我們設置Docker作為Driver就可以讓Nova Compute節點生產Docker。NovaDocker項目來自于社區,我們結合社區代碼進行了一定量的修改,并且在鏡像定制,具體使用上有一些自己不同的方式。
承載業務方面
novadocker這塊目前最主要的是tomcat的服務,docker用于搭建java tomcat運行環境 dockerfile中將jdk和tomcat安裝好,之后docker啟動后通過ansible-playbook 個性化修改tomcat配置,推送war包至遠程容器.
鏡像方面:
1. 引入supervisor作為進程管理器
2. 設計了3層的鏡像管理格式,最底層為最小化系統,中間層引入了公司yun源,入侵檢測設置、ssh/pam等安全設置。 上層可以最小化的實現APP的環境版本管理。支持了Tomcat/Cloudinit/Hadoop的完全或者初步測試使用。
3. 設計了Docker的hostname、dns、網卡名稱每次都重置的問題,提供固化機制。
4. Container實現了支持類似于物理機的FirstBoot和init機制
5. Glance管理docker鏡像和docker快照鏡像。
計算方面:
1. 支持Compute節點配置的超分,Docker節點能夠超分相對KVM更多的CPU/MEM等資源。
2. nova配置文件設置cpushare、cpuset、cpumix三種cpu的管理模式,可以針對不同模式的Container環境來設置CPU模式。
3. nova 配置為主機預留cpu/,保證Container不會侵占預留資源。
4. 上層鏡像開機隨機生成用戶UID,避免映射到宿主機相同的UID.
5. nova 配置不同的docker api 版本
6. 快照、快照恢復、遷移等基本實現
7. 支持通過flavor配置元數據,生成一組類似于k8s 的pod.
存儲:
1.使用Direct LVM代替 Docker默認loop模式,增強穩定性
2..初步支持了Container掛卷的Feature.
3. 依據不同的OpenStack Aggregate 設置不同的Contianer存儲空間.
網絡:
1.Docker使用OpenStack的網絡組建Neutron網絡提供Vlan服務
2.Docker配置ovs直連和混在模式。
3.Docker支持安全組的添加、刪除、查詢、更新等操作
3.Switch APR Proxy 老化時間過問題,開機發送free arp
4.虛擬網卡TSO的自動關閉
5.解決Docker的hostname、dns、網卡名稱每次都重置的問題,提供固化機制。
6.網絡限流
5. 遇到的問題
5.1 幽靈容器問題
我們環境中早期的Docker 是1.5版本的,在升級1.7的時候,部分container的進程從容器逃逸,容器處于Destroyed狀態,容器進行任何stop、remove都會出現如下報錯
Container does not exist: container destroyed。這是個社區已知的問題,目前社區沒有完整的解決方案。升級過程中先關閉老的容器后再升級Docker可以避免該問題。出現問題之后要恢復相對麻煩。
5.2 用戶隔離不足
我們測試環境中,容器密度較大。Container新建用戶對外全部映射為 UID 500或者501,出現了Resource Temporarily unavailable。
CentOS默認用戶UID從500開始,雖然ulimit設置上限是相對獨立的,但是統計已經使用資源時卻是一起統計的。所以在密度較大的測試和預生產環境可能會出現這樣的問題。我們的解法是在我們添加的FirstBoot中創建一個隨機UID的用戶。這樣相同的鏡像創建出的用戶UID也不同。大家各自統計,盡可能避免問題。
5.3 NFS Server無法啟動
這個問題是兩個小問題:
1. kernel模塊的reload設置。
2. kthreadd創建進程。
第一個問題代表了一系列問題,這個是由于因為文件系統沒有kernel的目錄,模塊依賴關系無從查起。通常此類服務都可以在配置文件中關閉模塊的reload過程,例如NFS就可以配置。第二個問題是rpc.nfsd 通知kernel去建立nfsd服務,kernel通過kthreadd來創建nfsd服務。所以nfsd進程不會出現在Container內部,而是暴露在宿主機上。
5.4 線程數量上限" fork: Cannot allocate memory"
我們的環境中出現過1次,表現為宿主機無法ssh登錄,通過IPMI Console進行登錄閃斷。這個問題原因是由于某個應用的問題導致生成大量的線程,達到了系統線程的上線。
我們認為:
1. pid_max 和 threads-max 值如何設置不影響單個進程的線程數量,上限目前為32768
2. pid_max 和 threads-max 影響所有線程的總量,二者較小者為系統上限。超過系統上限后部分系統命令都無法使用。
3. 調整系統上限后雖然可以有更多的線程,但是太多的線程將會對系統穩定性造成影響。
解決思路
1. 環境中所有宿主機將/proc/sys/kernel/pid-max設置為65535,并通過nagios監控告警宿主機上的線程數量。
2. 從應用層(tomcat)限制線程上限。
5.5 .device mapper discard導致的宕機
這個問題反復出現在某些服務器上,宕機重啟后通過IPMI consule進入時系統已經重新掛載了CoreDump的Kernel,看到CoreDump生成dump之前進行Recover操作和Data Copying操作,導致恢復時間很慢。通過Coredump分析屬于Kernel在DM discard方面的一個BUG,方法為禁用docker devicemapper的discard。具體為設置docker啟動參數"--storage-opt dm.mountopt=nodiscard --storage-opt dm.blkdiscard=false”。該問題已經在多個公司分享中看到,解決思路也基本一致。
6. 未來
Cobbler puppet in Docker 快速部署openstack。
Magnum + k8s的微服務架構管理.
Neutron 插件服務用Docker替換 Netns
7. Q.A
Q 1 :能否詳細敘述一下幽靈容器問題
A 1:
從低于1.5(包括1.5)向高于1.6及其以上進行docker daemon過程中,如果沒有關閉所有的containe。那么當高版本Docker Daemon啟動后再次start新的container時,這些container將無法關閉。大量操作都會報錯。
執行stop或者remove命令將會有如下報錯:
Server Error: Internal Server Error ("Cannot stop container XXX: [2] Container does not exist: container destroyed")
Remote API 針對該Contianer的報錯如下:
json, stats, changes, top, logs returned valid responses
stop, pause, wait, kill reported 404 (!)
復現方法:
1. 在1.5版本的Docker中run一個Container
2. 將docker daemon升級為1.7.
3. 重新start該Container
4. 嘗試執行stop 該 Container。
高版本Docker的升級過程
1. 當docker Daemon非正常關閉的情況下,所有Container首進程都會失去父進程,從而被 init 收養。此時Contaienr內部進程逃逸。
2. 當docker Daemon重新啟動時,將會針將已經處于關閉狀態的Container原有已經逃逸的進程 Kill 掉。
1.5版本之前向高版本升級過程
1. 當docker Daemon非正常關閉的情況下,所有Container首進程都會失去父進程,從而被 init 收養。此時Contaienr內部進程逃逸。
2. 當docker Daemon重新啟動時,Docker Daemon 無法殺死老版本Docker創建的現在已經逃逸的進程。
3. 當逃逸進程對應的Container啟動時,逃逸進程將會和新進程同時存在。
4. 當該Contaienr關閉時,新進程被殺死,逃逸進程依舊存活。Container標記Destroyed。
解決方案:
目前來看方案如下只能重啟物理服務器來解決。由于我們內部Contianer首進程一定是supervisor,可以先關閉Docker Daemon后殺死全部的幽靈Supervisor后再重啟Docker Daemon后就沒問題了。
預防方案還是要在升級過程中,保證關閉所有的Container,首先保證不會有逃逸進程,從而避免形成Ghost Container。
Q.2 Hostname DNS 貴方 用什么方案固定?
A 2: 首先Container創建之初,hostname和dns都是通過docker API來設置的。Hostname是nova instance的name,dns是公司內部設置。如果想修改container默認設置也是可以的,我們在內部鏡像預留了一個目錄,該目錄下的hosts、hostname、dns如果存在都會在container啟動后主動覆蓋container外部掛載的內容。
Q3:在使用docker去封裝nova compute模擬大規模集群測試時,運行一段時間后總出現部分使用docker封裝的nova compute服務出現down的狀態,不知道你們是否遇到過這樣的問題?
A:我們這邊沒有遇到,有沒有可能是模擬的nova compute進程數量過多消息有所積壓。nova方面考慮增加nova 時間戳超時設置。docker方面建議docker的網絡使用host模式,并在nova配置文件中設置不同的host,以便成為不同的hypervisor node。
Q4: sahara在使用docker替代KVM創建hadoop集群時,是直接使用heat創建docker,還是使用nova-docker?sahara相關的代碼是否需要改動,遇到過哪些坑?
A: 我們是使用nova docker的driver創建docker container的,sahara本身相關的代碼有部分改動,但是不大,主要改動在使用container和虛機的差別,比如hostname,cloudinit的部分配置等等。
Q5 . docker 的網絡模式中,中間添加一層linux bridge的原因是什么,這么做是否會有性能問題。
這個還是為了安全組,實際上我們支持配置兩種模式,linux bridge并不是默認配置的。openvswitch 2.4以后可以根據流表設置安全組。
Q6. Container限速是如何實現的,是否有必要針對container進行限速。
我們的環境中使用的openvswitch,通過veth pair的方式建立虛擬網絡設備的關系。限速主要是使用tc,畢竟openvswitch的限速也是使用tc做的。
Q7:nova組件中docker的高級特性無法使用你怎么看,是否使用docker api來控制容器
A:上面已經說過這個問題了,其實通過flavor metadata的設置,nova docker driver 可以實現生成一組容器。nova docker這塊過去確實是直接調用docker api的,但是為了應對不斷變化的API,我們使用了docker-py作為Client,并在nova 配置文件中增加了api版本的設置。從而盡可能拿到docker本身升級帶來的福利。
Q8:ops已經有超分設置,你設置超分的意義是什么?
A: 我們Docker和KVM都在一個openstack平臺中,而nova的超分實在nova Conductor中生效的。Nova compute Libvirt Driver是直接上報的服務器核數。而我們認為Docker在密度上存在比KVM密度更高的需求,所以在Compute上支持超分是有必要的。
Q9:使用cpushare是否會出現單個容器負載很高的場景,出現這種情況如何處理
A; 還是會出現的,記得有個容器cpu占用1600%的場景(32核心)。通常這種情況還是應用出現了問題,最簡單的方法是通過cgroup本身的命令進行限制。容器重啟之后該限制就會丟失。限制方法例如: cgset -r cpuset.cpus=20-23 cpuset:/docker/91d943c55687630dd20775128e2ba70ad1a0c9145799025e403be6c2a7480cb2
Q10. Docker 的監控和scale-auto是如何實現的
監控方面目前主要是通docker stats api 和 部分腳本來實現,集成到zabbix中,后面會考慮使用CAdvisor。
后者目前不支持,計劃上會在k8s平臺中支持,而非heat或nova中。畢竟這是k8s、mesos它們的專長。
Q11 你的三層鏡像中,第一層和第二層都是系統層,為什么不合并成為一層。
A: 首先我們的第一層鏡像并不是通過dockerfile創建的,而是依據官方文檔從0建立的最小的鏡像,這一層是很少變動的。而第二層的設置是為上層設計的通用層,涉及到進程管理器、ssh設置、pam設置、入侵檢測設置、開機初始化設置、還是有很大可能變動的,我們希望有關配置都應該放入dockerfile以便管理。
Q12.nova-docker如何支持cloudinit
A:因為在novadocker中就是完全模擬kvm的網絡模式,所以cloudinit除了一些小幅配置變更之外沒有什么特殊的。
sed -e 's/disable_root. /disable_root: 0/' -e 's/ssh_pwauth. /ssh_pwauth: 1/' -e '/ssh_pwauth:/a\ndatasource:\n OpenStack:\n max_wait: 120\n timeout:30' cloud.cfg
Q13 :能否詳細介紹下ARP問題?
A:由于建立的vm的ip之前分配給了已經刪除的vm,導致mac被記錄在交換機上。數據交換經過3層,3層交換機會將mac直接返回給ping的一方,導致無法ping通、
啟動container后通過arping -c 3 -f -U -I eth0 172.28.19.243 -c 3開機發送免費arp來處理
Q14:nova Docker實現了熱遷移嗎?如何做快照?
A:熱遷移目前還沒有支持,nova docker快照就是將容器commit成一個鏡像,然后使用glance的接口上傳glance中。通過快照可以重新建立新的container。
Q15 :nova-docker不是早在H版本就廢棄了嗎?你們自己維護的?
A:確實廢棄了,我們自己維護。不過github上有了更新,我們剛剛merge機那里一些特性。可以再關注一下。
Q16 :openstack 如何對novadocker環境的container進行監控?在監控指標上是否與其他hypervisor driver有區別?
A:監控方面目前主要是通docker stats api 和 部分腳本來實現,集成到zabbix中,后面會考慮使用CAdvisor。監控上有一些區別。主要是pid_max,docker daemon存活,和docker自身存儲pool等docker特有的,其他方面沒有太大區別。
Q17:您好,貴公司只維護git代碼和鏡像容器。請問假如同一個編譯環境,能編譯不同操作系統版本的庫嗎?或者鏡像。因為同一套代碼會部署到不同的系統上
A:我們這條編譯環境只是用來編譯ops本身的,如果需要增加新的編譯環境,我們會向Registry推送一個新的編譯鏡像。
Q18 :glance管理鏡像和快照時有沒有能用到docker的分層?如果有,如何利用的?
A:沒有,tar包形式,compute節點下載之后load到compute節點上。
Q19:生產環境相比測試環境有什么不同嗎?
A:docker在cpu超分系數不同,系統pid_max等調優參數略有不同
Q20. Nova docker快照是如何實現的?
A.將操作的Container commit成為一個鏡像,并上傳到glance中。