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

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

本文分為兩個部分,第一部分比較常規,介紹如何用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)

分享創造快樂