歡迎光臨
每天分享高質量文章

一個可供參考的企業應用容器化實踐案例

本文分為兩個部分,第一部分比較常規,介紹如何用OpenShift搭建自動化測試、開發環境。第二部分介紹了在容器使用過程中遇到的問題,以及應對方案。作者在原有的OpenShift Router僅支持7層協議的基礎上,對其進行支持4層協議的擴充,聯合CoreDNS,讓平臺的使用者不用記錄靈活多變的IP地址,僅需自己定義自己熟悉的hostname(或者由平臺自動生成)就訪問對應資源,讓使用者幾乎沒有感知的使用容器還是物理機或者虛擬機。
在這裡跟大家分享一下容器使用方面的經驗,說的不好,肯定有不對的地方,還希望大家多多批評、指正。

OpenShift簡介

我們都知道在容器編排領域,有3個最著名的編排系統,它們分別是Docker Swarm,Mesos和Kubernetes。其中Docker Swarm是Docker公司自己推出的容器管理和編排系統,由於起步較晚,目前大規模的應用尚不多見。
Mesos是Apache下的開源分佈式資源管理框架,它被稱為是分佈式系統的內核。Mesos最初是由加州大學伯克利分校的AMPLab開發的,後在Twitter得到廣泛使用。
Kubernetes和Mesos同樣是起源於Google的Borg,是現在應用最廣,用戶數和社區活躍度最高的容器編排系統。OpenShift也是出於這個考慮,最終選擇Kubernetes作為底層架構。OpenShift是一個開源容器雲平臺,他是基於主流的容器技術Docker和Kubernetes構建的雲平臺,作為一個開源專案,他已經有5年的歷史,其最早的定位是一個應用雲平臺(Platform as a Service)。在Docker時代來臨之前,各個廠商和社區專案傾向構建自己的容器標準,比如Cloud Foundry的Warden、OpenShift的Gear,但是在Docker成為主流及社區的技術發展方向之後,OpenShift快速的擁抱了Docker,並推出了市場上第一個基於Docker及Kubernetes的容器PaaS解決方案。當然很多人會有疑問,OpenShift與Docker及Kubernetes的關係究竟是什麼?
OpenShift是基於容器技術構建的一個雲平臺。這裡所指的容器技術即包含Docker及Kubernetes。如下圖所示:

OpenShift底層以Docker作為容器引擎驅動,以Kubernetes作為容器編排引擎組件。OpenShift提供了開發語言、中間件、自動化流程工具及界面等元素,提供了一套完整的基於容器的應用雲平臺。OpenShift有三個版本,分別是企業版,社區版和在線版,我們這裡使用社區版(origin版)用做我們企業內部的部署。

部署介紹

我們的架構比較典型,前端有2個負載均衡器(HAProxy),兩個負載均衡通過Heartbeat共享一個VIP,後面連接3個master node,每個master node上分別運行了一個etcd,後面連接了N個slave node,因為集群中所有的狀態都會持久化到etcd中,所以api server基本上是無狀態運行,當有節點需要和master node打交道時,先訪問這個VIP,然後連接到vip後面的一個HAProxy上,HAProxy再選擇其中一個master node上的api server進行與etcd通信,下麵是我的HAProxy的配置檔案haproxy.conf:
backend atomic-openshift-api
   balance source
   mode tcp
   server      master0 10.10.xx.xx:8443 check
   server      master1 10.10.xx.xx:8443 check
   server      master2 10.10.xx.xx:8443 check
同時,其實我們如果集群裡面有其他的高可用的需求,比如我們使用了Harbor做為私有鏡像倉庫,三個鏡像倉庫配置了Replication規則,我們通過VIP推送一個鏡像到3個中的任意一個倉庫,其他兩個倉庫也會存在我們的同樣鏡像,從而實現高可用。下麵是Haproxy的配。
backend harbor-proxy
   balance source
   mode tcp
   server      harbor1 10.10.xx.xx:80 check
   server      harbor2 10.10.xx.xx:80 check
   server      harbor3 10.10.xx.xx:80 check

運行流程

第一個是每天定時的作業,這個比較簡單,我們快速過一下:首先在Jenkins里做定時任務,時間定為0:30,從代碼下載代碼,在編譯之前進行代碼風格檢查(snoar)和執行單元測試(junit),然後進行編譯,把編譯出的二進制包和事先寫好的Dockerfile進行docker build,然後把輸出的docker image push到Harbor的鏡像倉庫,同時這個編譯的jenkins job會觸發,測試部署job,測試部署job執行時會觸發OpenShift里的test project,我們把PullPolicy設置成alway是,每次執行從新部署,都會觸發新的鏡像的拉去,從而達到從新部署的目的,測試專案分為兩個階段,自動化測試和壓力測試,自動化測試採用selenume和robot結合的方式,在測試結束之後生成測試報告。
壓力測試使用jmeter。結束之後也會以郵件的方式發送給訂閱者。如果所有測試通過之後,測試job會先打給這個image打一個tag,然後push到Harbor的ready專案中。同時發送通知郵件。
待第二天測試人員需要測試的時候,它們會通過我們的release manager去拉去ready專案中最新的image,然後部署到它們自己的project里,進行特定功能的手工測試。整體流程大概如此。
第二個是針對開發測試環境的,由於資源有限,我們的開發人員也用容器的方式部署每個人的開發環境,這裡OpenShift/Kubernetes多租戶的優勢就體現出來了。在我們的容器平臺上,每一個開發人員對應一個賬號,每一個賬號就是我們OpenShift里的一個project,由於每一個project都可以做資源限制,所以只要給大家按照需求分配好固定份額,就可以做到資源的共享和互不干涉。舉個例子,比如說,我們有一個專案,它分為前端和後端。
前端人員開發的時候,他只關心前端代碼,後端代碼基本僅僅是呼叫而不做修改,那麼這個時候我們就可以把前端打成一個docker image,後端打成一個docker image(這裡是舉個例子,實際情況可能比較複雜,可能前後端都不止一個image),然後在前端開發人員寫完前端代碼後,把自己的代碼通過NFS共享給Release Manager,(NFS掛在可以是實現掛載好的,Windows、Linux、MacOS均可以,代碼就可以直接存在上面),然後點擊Relea se Manager上面的前端發佈按鈕,就會觸發Jenkins中的一個front-end job,它會完成代碼編譯,打包和鏡像推送,最後在這個開發人員對應的project里從新部署前端代碼的整個過程,接著這個開發人員就可以通過url(比如http://test/frontend-dev1)去訪問它剛剛部署的頁面。
如果是後端人員也會有同樣的流程,具體就不在贅述了。具體說明一點,由於OpenShift集成了Router功能,也就是類似於Kubernetes里的Ingress,但是它是用Haproxy實現的,並且能夠在Yaml檔案中對Router進行配置,剛纔取用的url我們可以配置成http://{集群名}/{專案名},所有整個集群都是用7層協議通過Router對外提供訪問。
生產和預發佈環境,這個環境的配置除了要求高可用之外,就是環境的硬體配置,一般意義上來將,預發佈的環境因該不高於正式生產環境。當決定對測試通過的鏡像版本進行上線時,首先會用docker tag把它換成tag換成pre-release,經過壓力測試和手工最後的verify就可以發佈到正式環境中了。

除錯問題的解決

下麵我們著重講一下在實際部署和使用容器過程中,遇到了哪些問題,以及是如何去解決:
1、有一個問題也許很多剛剛使用容器的公司都會遇到,就是開發人員喜歡把容器當成虛擬機用,在遇到程式bug的時候,很多開發者都喜歡SSH到容器里,親自看看log,或者是嘗試替換一下它們程式的debug版(這裡的所謂debug版,就是開發人員在代碼裡加入一些除錯信息或者print一下log),然後重新啟動應用。這裡我們不推薦給容器內部安裝SSHD,因為首先容器的IP是臨時分配的,我們無法確定的告訴開發者它這一次的IP地址是多少,即使告訴了他也不一定能夠訪問的到(我們的容器系統網絡層和外界不是一個網段),那麼如何解決這個問題呢?
我們開始也是儘量說服開發者,學會用log去debug,因為我們前面已經把log通過ES進行了收集,用Kabina可以去查看,但是沒有辦法,有些開發人員還是習慣自己去cat或者vim打開日誌檔案。所以這裡我們就用到了OpenShift里提供的一個oc子命令oc get pod和oc exec,前者用來得到當前用戶所在的專案中的Pod串列,後在類似docker exec命令可以直接跳進容器里(Kubernetes中也提供類似的命令),當開發人員需要把裡面的日誌檔案拷貝出來,後者是拷貝一個debug版本的程式到容器里去運行時,可以用oc cp(同樣這個在Kubernetes里也有類似的命令)。
雖然這幾個命令據我觀察有一些bug,比如拷貝的標的目錄不太準確,而且對容器里的tar命令有一些依賴。但這都是小問題,不影響使用,如果覺得幾條命令結合起來使用有些麻煩,可以自己用Python腳本進行一個簡單的封裝,這樣當開發人員使用的時候會更加簡單。
2、每一個系統幾乎都或多或少的使用了一些第三方工具,比如MySQL、MyCAT、Redis、ZooKeeper,這些組件我們都把它們進行了容器化,以便實現資源的整合和方便部署。那麼這就引發了另一個問題,開發人員在碰到程式bug的時候,往往需要直接去連接這些第三方組件,去改修改和查看裡面的信息。比如他需要查看Redis的鍵值是否存在,查看資料是否寫入到了資料庫里。
這個時候,由於所有組件在容器中,你不知道它的準確IP,你可能很容易想到用OpenShift提供的router功能去像剛纔的url那樣提供外界的訪問,但是由於這些中間件是4層的協議,而現有OpenShift的Router功能僅僅支持7層協議,所以我們為瞭解決這個問題就必須實現OpenShift的4層代理功能。
通過修改Openshift的原始碼haproxy-templte.conf和router部分的相關代碼,然後通過yaml route的annotation段,定義一個規則,把對應的端口傳進router的配置檔案,讓後端的第三方應用程式通過router節點對應的端口(Haproxy里的mode tcp)從而實現router代理4層協議的目的,但是這會導致另一個問題,因為router的每一個端口只能映射給一個後臺應用,比如3306端口,只能映射給一個MySQL,如果有兩個開發人員都要除錯MySQL,那第二個開發者的MySQL的映射端口肯定就得用除了3306以外的端口(比如3307、第三個人3308等)那麼就會產生一個問題router的映射端口如何告訴開發人員呢?
這個問題有兩個解決辦法,第一個是通過一個Web UI,去顯示的告訴開發人員他所有的資源對應的router節點的端口號,但是這有一個不方便的地方,如果的資源對應的Pod被重置了,那麼他的端口號也就會被改變,即使端口號不改變,記起來也比較麻煩(大型的專案可能要用到5、6個中間件產品,每一個開發人員都要記自己的那套環境的端口號,還要學會如何用自己的工具去修改預設的端口號顯然給開發者添加了許多不必要的麻煩。那麼基於此問題的考慮,我們使用了第二種方法,即我們內部實現了一個DNS,開發者每個人的資源的IP都可以用DNS查到,比如developer1,他有Redis、MySQL、MyCAT、ZooKeeper、PostgreSQL等資源,那他的這些資源對應的DNS域名為:developer1.redis developer1.mysql developer1.mycat developer1.zookeeper等,但是由於DNS只能傳回IP地址,無法傳回端口號,我們還是得不到router節點對應資源的端口號。
為瞭解決這個問題,我們可以用Haproxy加別名的方式,比如運行命令ifconfig eth0:1 10.10.xx.xx意思就是給eth0的NIC,加上了一個虛擬的IP10.10.xx.xx,那麼此時,這個網卡eth0就有兩2個IP,並且此時都能ping通。(當然實際實現的時候,我們不可能用是ifconfig命令完成這個網卡別名的方式,那樣太low,也太不可靠了,我查看了ifconfig的原始碼,把其中設置IP地址的代碼集成到了我們修改過的OpenShift里)。然後router的haproxy.conf做bind的時候就需要指定這個IP,端口號還用原來這個應用預設的端口號如下圖所示,這樣開發人員不用每次都記住不同的端口號,僅僅配置一個DNS,不管環境發生了什麼樣的改變,都可以用預設端口和hostname去連接自己的資源進行除錯。
3、配置Docker容器的時候,預設使用的是DeviceMapper方式,然而這種方式有眾多的限制,可以參考https://docs.openshift.org/3.6/install_config/install/host_preparation.html#configuring-docker-storage中的詳細配置說明,在生產環境中我們採用的是第2種方式。

Q&A;

Q:所有開發人員都是用一套OpenShift集群測試嗎?CI/CD也是同一套環境嗎?
A:我們是按業務分的,原則上,一套業務線(一個業務部門)用一套系統,這樣成本上也好分攤。

Q:OpenShift也是用Go編寫的?
A:是的,OpenShift用的是原始碼級別的和Kubernetes的集成,不是通過client-go或者REST API的方式,所以我們可以看到,Kubernetes髮型的版本總是比OpenShift的快1 到2個版本。

Q:對於OpenShift比較適合多大規模的團隊?
A:這個怎麼說呢,其實引入DevOps或者CI/CD的流程就是為了給企業減少人員成本,讓有些能夠自動化的東西通過計算機運行起來。所以因該是人員越少越好,但是人員如果少,就要求每個人的技術能里比較強,開源的東西往往用起來不難,但是真到出了問題的時候就需要看代碼去解決了。所以如果人少的話,可能每個人就要求懂得就比較多一些。

Q:router本身是否具備HAProxy?
A:OpenShift的Router就是用HAProxy實現的,當然我現在用的是3.6.1的版本,我不知道以後會不會支持Nginx或者其他別的LB,因為我看到代碼里已經有關於Nginx的相關配置了,但是沒有激活。OpenShift使用HAProxy的大致流程就是通過一個Yaml或者jason檔案,配置一條route信息,然後通過api-server持久化到etcd中,router的代碼啟動一個goroutine,去通過api-server watch etcd,然後根據配置信息和環境變數,通過haproxy-template模版,去生成 haproxy.conf,然後去動態reload。

Q:OpenShift的project和Kubernetes的namespace是一對一的關係麽?project可以設置資源配額麽?怎麼設的?
A:是一對一關係,當然有一些namespace 是有一些特殊意義的,不建議在裡面跑應用。project可以設置資源配額,具體怎麼設置就比較複雜了,建議參考一下官方文件,簡單的說就是可以根據CPU記憶體做資源的限定,這個和Kubernetes是一樣的。

Q:OpenShift中原生性能指標監控服務的Pod總掛有沒有相應的解決辦法?
A:解決Pod總掛的問題就得具體問題具體分析了,我記得它做性能監控的那個Pod比較吃資源,其實可以對他進行一下限定,比如:oc env rc hawkular-cassandra-1 MAX_HEAP_SIZE=1024M -n openshift-infra。

Q:OpenShift中的router預設情況下是跑在Pod里的,那麼當service特別多,route規則也特別多的時候,如何解決router服務的性能問題的?
A:這是一個好問題,但其實我覺得這個和HAProxy有很大的關係,跟在不在Pod中關係不大,因為router這個容器其實用的是主機網絡,那麼這個問題其實就轉化成瞭如何提升HAProxy的性能,這種情況其實有很多可以參考的方案,比如前面在加一層LVS負載,或者用DNS做域名解析的時候進行一定的負載功能。
基於Kubernetes的DevOps實踐培訓

本次培訓包含:Kubernetes核心概念;Kubernetes集群的安裝配置、運維管理、架構規劃;Kubernetes組件、監控、網絡;針對於Kubernetes API接口的二次開發;DevOps基本理念;微服務架構;微服務的容器化等,點擊識別下方二維碼加微信好友瞭解具體培訓內容

2月1日正式開課,還有最後3個名額,點擊閱讀原文鏈接即可報名。
赞(0)

分享創造快樂