本文由1月19日晚36Kr運維開發工程師田翰明在Rancher技術交流羣的技術分享整理而成。微信搜索rancher2,添加Rancher小助手爲好友,加入技術羣,實時參加下一次分享~
html田翰明,36Kr 運維開發工程師,在 36Kr 主要負責運維自動化,CI/CD 的建設,以及應用容器化的推進。前端
36Kr是一家創立於2010年,專一於科技創投領域的媒體公司,業務場景並不複雜,前端主要使用NodeJS進行Render,移動端有Android也有iOS,後端服務幾乎全都由PHP來支持。使用PHP的主要緣由是在最初進行技術選型的時候發現,PHP進行Web開發效率比較高,後來就一直這樣延續下來了。java
可是在後期,隨着業務的突飛猛漲,在程序設計中又沒能進行解耦,就致使了許多服務耦合成了一個很臃腫的單體應用,邏輯耦合嚴重,進而致使了不少的性能問題,隨着問題愈來愈難改,開發任務又愈來愈緊,就不得不日後拖,越日後拖留下的問題就更難改,造成了一個惡性循環,留下了不少的技術債,很不利於後續的開發任務,而且一旦出現了問題,也很難追溯具體緣由,因此在那時候常常聽到一句話 「這是歷史遺留問題」 。
node
B/S、C/S、單體應用,這是一種很傳統 也很簡單的架構,可是缺點也暴露無遺,因此常常由於一個業務邏輯的性能問題,進而影響到全部的業務。在運維側,運維只可以經過堆機器,升配置等策略來應對,投入了不少的機器成本和人力成本,可是收效甚微,非常被動。
git
這種狀況已是迫在眉睫了,終於技術團隊決定使用 Java 語言進行重構,將單體應用進行微服務化拆解,完全改變這種由於單體應用故障而致使生產環境出現大範圍的故障。
github
在重構計劃開始一段時間後,爲了節省虛機資源,咱們一臺虛機上運行了多個 Java 程序,可是由於沒有資源隔離和靈活的調度系統,其實也會致使一些資源的浪費。而且在高併發場景下,偶爾會有資源搶佔致使一個應用影響另外一個應用的狀況。爲此,咱們運維專門開發了一套自動化部署系統,系統內包括部署、監控檢測、部署失敗回滾、重啓等基礎功能。web
隨着當時 K8s 的風靡,還有 Rancher 2.x 的發佈,咱們逐漸發現,咱們所面臨的這些問題,它們基本都能解決,好比資源隔離、deployment 的控制器模型、靈活的調度系統,這些都有,這就是最好的自動化部署系統啊,因而咱們運維側,也開始決定向容器化進軍。docker
在選型上,由於咱們的服務基本都在阿里雲上面,因此第一個想到的是阿里雲。時由於咱們和華爲有一些業務的往來,因此華爲的 CCE 也做爲了備選,可是考慮到咱們的服務資源所有在阿里雲上,這個遷移成本實在太大了,因此就沒再考慮華爲雲。數據庫
咱們一開始使用過Rancher 1.6,可是隻是用來管理主機上部署的原生 Docker。也所以對Rancher的產品產生了很大的好感。
後端
需求方面,由於要下降咱們研發人員的學習成本,容器管理平臺的易用性十分重要。此外,K8s 的基礎功能是必須的,由於 K8s 還在高速發展階段,因此能須要夠隨時跟上更新,有安全漏洞後也須要第一時間進行更新打補丁,同時還要有基本的權限控制。並且咱們公司內部沒有專門的K8S團隊,運維人員也只有2位,因此若是可以有專業人員進行技術上的交流,發生了問題能夠有專業的服務團隊來協助也十分重要。
綜上,基本上就是 Rancher 完勝,UI 作得很是友好,開發人員可以很快上手,更新迭代速度也很是快,發現漏洞後也會有詳細的補丁方案,認證策略也完美支持咱們的 OpenLDAP 協議,可以對開發、測試、運維人員進行不一樣權限控制,而且也是第一家作到支持多雲環境的,方便之後咱們作跨雲的方案。
咱們此次容器化的過程,主要經歷瞭如下幾個因素的考慮,今天我就來和你們分享咱們在 Rancher 上的一些實踐,但願能給你們帶來幫助:
應用的容器化改造
Rancher 的高可用性
容器的運維
由於咱們的開發人員,有至關一部分是沒有接觸過容器的,爲了能對開發人員更友好一些,咱們的鏡像分紅了兩層,主要的 Dockerfile 編寫是由咱們運維人員來編寫的,而開發人員代碼倉庫裏的 Dockerfile 是最簡單的,基本上只有代碼拷貝的過程和一些必傳的變量,具體能夠參考如下示例:
## 這是運維人員維護的 Dockerfile 示例 ## 本示例僅作參考 FROM alpine:3.8 MAINTAINER yunwei <yunwei@36kr.com> WORKDIR /www RUN mv /etc/apk/repositories /etc/apk/repositories.bak \ && echo "http://mirrors.aliyun.com/alpine/v3.8/main/" >> /etc/apk/repositories \ && apk update && apk upgrade RUN apk --no-cache add ca-certificates wget && \ wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \ wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk && \ apk add glibc-2.29-r0.apk && rm -f glibc-2.29-r0.apk RUN apk add -U --no-cache \ bash \ sudo \ tzdata \ drill \ iputils \ curl \ busybox-extras \ && rm -rf /var/cache/apk/* \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime COPY java-jar/jdk1.8.0_131 /usr/local/jdk1.8.0_131 ENV TZ="Asia/Shanghai" ENV JAVA_HOME=/usr/local/jdk1.8.0_131 ENV CLASSPATH=$JAVA_HOME/bin ENV PATH=.:$JAVA_HOME/bin:$PATH ENV JAVA_OPTS="-server -Xms1024m -Xmx1024m" CMD java -jar $JAVA_OPTS -Dserver.port=8080 server.jar ======================================= ## 這是開發人員維護的 Dockerfile 的示例 FROM harbor.36kr.com/java:v1.1.1 MAINTAINER developer <developer@36kr.com> ADD web.jar ./server.jar
能夠看到,開發人員所維護的 Dockerfile 能夠說至關簡單了,這大大的下降了開發人員維護的難度。
另外,由於構建產物的大小,很大程度上決定了部署時間的長短,因此咱們使用了號稱最小的鏡像——alpine,alpine 有不少的優勢:
體積小
有包管理器、有豐富的依賴
可是他有一個缺點,alpine 上並無 glibc 庫,他所使用的是一個 musl libc 的小體積替代版,可是 Java 是必須依賴的 glibc 的,不過早就有大神瞭解了這點,在 GitHub 上已經提供了預編譯的 glibc 庫,名字爲alpine-pkg-glibc
,裝上這個庫就能夠完美支持 Java,同時還可以保持體積很小。
安裝 Rancher 的方式有兩種:單節點安裝和高可用集羣安裝。通常單節點安裝僅適用於測試或者 demo 環境,因此要正式投入使用的話,仍是推薦高可用集羣的安裝方式。
咱們一開始測試環境就使用了單節點安裝的方式,後來由於 Rancher Server 那臺機器出現過一次重啓,就致使了測試環境故障,雖然備份了,可是仍是丟失了少許數據,最後咱們測試環境也採用了 HA 高可用部署,整個架構以下圖所示。
Rancher Server 我是採用的 RKE 安裝,而且爲了防止阿里雲出現區域性的故障,咱們將 Rancher Server 的三臺機器,部署在了兩個可用區,Rancher Server-00一、003 在北京的 H 區、Rancher Server-002 在北京的 G 區。
負載均衡,咱們採用的是阿里雲的 SLB,也是採購的主備型實例,防止單點故障,由於 Rancher 必須使用 SSL 證書,咱們也有本身的域名證書,爲了方便在 SLB 上進行 SSL 證書的維護,咱們使用的是 7 層協議,在 SLB 上作的 SSL 終止,Rancher Server 的架構圖能夠參考下圖:
下游集羣,也就是用來承載業務的 K8s 集羣,咱們也是一半一半,在阿里雲的兩個可用區進行部署的,須要注意的是,爲了保證兩個區的網絡時延 <= 15 ms,這就完成了一個高可用的災備架構。
備份方面,咱們也使用了阿里雲 ECS 快照 + ETCD S3 協議備份到了阿里雲的 OSS 對象存儲兩種方案,確保出現故障後,可以及時恢復服務。
部署的詳細教程能夠參考 Rancher 官方文檔。
容器的運維,這裏主要指容器的日誌收集和容器監控,容器監控方面呢,Rancher 自帶了 Prometheus 和 Grafana,並且和 Rancher 的 UI 有一些整合,就很是的方便,因此監控方面我就不展開講了,我主要說一說日誌收集。
在 K8s 裏,日誌的收集相比傳統的物理機、虛機等方式要複雜一些,由於 K8s 所提供的是動態的環境,像綁定 hostpath 這種方式是不適用的,咱們能夠經過如下這個表格直觀的對比一下:
能夠看到,K8s 須要採集的日誌種類比較多,而容器化的部署方式,在單機器內的應用數是很高的,並且都是動態的,因此傳統的採集方式是不適用於 K8s 的。
目前 K8s 的採集方式大致能夠分爲兩種,被動採集和主動推送。
主動推送通常有 DockerEngine 和 業務直寫兩種方式:DockerEngine 是 Docker 的 LogDriver 原生自帶的,通常只能收集 STDOUT、通常不建議使用;而業務直寫,則須要在應用裏集成日誌收集的 SDK,經過 SDK 直接發送到收集端,日誌不須要落盤,也不須要部署Agent,可是業務會和 SDK 強綁定,靈活性偏低,建議對於日誌量較大,或者對日誌有定製化要求的場景使用。
被動推送是採用部署日誌收集 Agent 進行採集的,有兩種方式,一種是 Daemonset 每一個機器節點上部署一個 Agent,還有一種 Sidecar,每一個 Pod 以 Sidecar 的形式部署一個 Agent。
Sidecar 部署方式比較消耗資源,至關於每一個 Pod 都有一個 agent,可是這種方式 靈活性以及隔離性較強,適合大型的 K8s 集羣或者做爲 PaaS 平臺爲業務方提供服務的羣使用,Daemonset 部署方式,資源消耗較小,適合功能單1、業務很少的集羣。
結合咱們自身的場景,屬於小規模集羣,而且業務也不算多,咱們選擇了 Daemonset 的部署方式,在測試環境,咱們通過調研選擇了阿里開源的一個日誌收集組件log-pilot GitHub 地址是:github.com/AliyunContainerService/log-pilot ,經過結合 Elasticsearch、Kibana 等算是一個不錯的 K8s 日誌解決方案。
由於咱們的服務器都在阿里雲上,咱們運維人員比較少只有2位,沒有精力再去維護一個大型的分佈式存儲集羣,因此咱們的業務日誌選擇存儲在了阿里雲的日誌服務,因此在生產環境,咱們的 K8s 也使用了阿里雲日誌服務,目前單日日誌 6億+ 沒有任何問題。
使用阿里雲收集日誌呢,你須要開通阿里雲的日誌服務,而後安裝 Logtail 日誌組件alibaba-log-controller Helm
,這個在官方文檔裏有安裝腳本,我把文檔連接貼在下面,在安裝組件的過程當中會自動建立aliyunlogconfigs CRD,部署alibaba-log-controller
的Deployment,最後以 DaemonSet 模式安裝 Logtail。而後你就能夠在控制檯,接入你想要收集的日誌了。安裝完之後是這樣的:
Logtail支持採集容器內產生的文本日誌,並附加容器的相關元數據信息一塊兒上傳到日誌服務。Kubernetes文件採集具有如下功能特色:
只需配置容器內的日誌路徑,無需關心該路徑到宿主機的映射
支持經過Label指定採集的容器
支持經過Label排除特定容器
支持經過環境變量指定採集的容器
支持經過環境變量指定排除的容器
支持多行日誌(例如java stack日誌)
支持Docker容器數據自動打標籤
若是你想了解更多,能夠查看阿里雲日誌服務的官方文檔:
https://help.aliyun.com/document_detail/157317.html?spm=a2c4g.11186623.6.621.193c25f44oLO1V
我這裏所講的,主要指的是企業內部用戶的多租戶隔離,而不是指的 SaaS、KaaS 服務模型的多租戶隔離。
在權限方面,由於我司對於權限的管控較嚴格,而 Rancher 剛好提供了很是方便的基於 集羣、項目、命名空間等多個粒度的權限控制,而且支持我司基於 OpenLDAP 的認證協議,很是便於管理,我能夠給不一樣項目組的開發、測試人員開通相對應的 集羣/項目/命名空間的權限。
好比下圖,我能夠給集羣添加用戶、也能夠給某個 Project 添加用戶,而且能夠指定幾個不一樣的角色,甚至能夠自定義角色。
好比場景1:我能夠給 項目組長,分配開發環境集羣->項目1 全部者(Owner)權限,而後項目組長能夠自由控制給本項目添加他的成員,並分配相應權限。
場景2:我能夠給 測試經理,分配測試集羣的全部者(Owner)權限,由測試經理來分配,誰來負責哪一個項目的測試部署,以及開發人員只能查看日誌等。
在資源方面,必定要進行容器的資源配額設置,若是不設置資源限額,一旦某一個應用出現了性能問題,將會影響整個 node 節點上的全部應用,K8s 會將出現問題的應用調度到其餘 node 上,若是你的資源不夠,將會出現整個系統的癱瘓,致使雪崩。
Java 應用的資源配額限制也有一個坑,由於默認 Java 是經過 /proc/meminfo
來獲取內存信息的,默認 JVM 會使用系統內存的 25%
做爲 Max Heap Size
,可是容器內的/proc/meminfo
是宿主機只讀模式掛載到容器裏的,因此採起默認值是行不通的,會致使應用超過容器限制的內存配額後被OOM,而健康檢查又將服務重啓,形成應用不斷的重啓。
那是否是經過手動參數設置 JVM 內存 = 容器內存限額
呢?不行!由於 JVM消耗的內存不只僅是 Heap,由於 JVM 也是一個應用,它須要額外的空間去完成它的工做,你須要配置的限額應該是 Metaspace + Threads + heap + JVM 進程運行所需內存 + 其餘數據
關於這塊,由於涉及到的內容較多,就不進行展開,感興趣的同窗能夠本身去搜索 一下。
由於咱們的業務場景並不複雜,因此咱們的容器化之路,其實走的也相對來說蠻順暢的,咱們的運維人員不多,只有 2 位,因此咱們也沒有太多的時間精力去維護太多的自建系統,咱們使用了不少的阿里雲產品,包括 Rancher,他很方便的部署方式,友好的 UI,包括集成好的監控等等,在容器化之路上給了咱們很大的信心。
咱們使用構建兩層鏡像的方式,下降了開發人員的學習複雜度。使用了小體積鏡像 alpine + 預編譯 glibc 減少了鏡像體積。提升了部署的時間,在架構上,咱們採用了阿里雲雙區機房的災備的架構,以及完備的備份方案。使用 Daemonset 部署的日誌收集組件,收集到阿里雲日誌服務,支撐咱們 6億/日的日誌系統。Rancher 還提供給了咱們深度集成的監控系統、多租戶隔離等。還有咱們本身踩坑 踩出來的資源配額設置。
其實容器化並不複雜,若是沒有 K8s,咱們須要本身構建健康監測系統、發版系統、維護不一樣的主機環境,不能細粒度的進行資源劃分,不能更有效的利用計算資源,運維的工做主要是什麼?在我看來其實就是 節約成本、提升效率。虛擬化、自動化、智能化、高性能、高可用、高併發 等等,這些無一不是圍繞着成本和效率這兩個詞,而 K8s 其實已經幫咱們都作好了,而像 Rancher 這種編排平臺又幫咱們下降了 K8s 的學習複雜度,因此你要作的就是加入 K8s,好了,到這裏此次的分享就結束了。感謝~
Q1:K8S在生產環境的高可用存儲方案有推薦嗎?
A1:存儲方案沒有標準答案,咱們主要使用阿里雲,因此用的是阿里雲的塊存儲,比較常見的方案還有 Ceph、GlusterFS、Portworx、OpenEBS 等,他們各有優劣,需結合本身的業務需求進行選擇
Q2:灰度發佈,Kubernetes網絡流量能夠經過服務網格分流實現網絡層面的分發,可是涉及到應用大版本的更新時候,涉及到數據庫結構的變動的時候,如何實現灰度發佈?
A2:沒有遇到過這個場景,不過提供一個思路,能夠準備兩套數據庫,網絡分流也能夠分流到不通數據庫,具體須要你本身驗證一下是否可行
要分清楚這是兩層,一層是邏輯層,一層是數據層,不能混爲一談
Q3:Pipeline是用什麼作的?Pipeline下,如何處理同一個分支,須要並行測試多個版本的場景?我用Rancher的Pipeline,侷限性比較大,就是同一個分支沒法並行多套進行測試。命名空間在使用,可是同一個分支下,命名空間是寫在.rancher.yml下的,因此沒法區分,Rancher的Pipeline不能在外面注入變量進行區分。
A3:Rancher 的 Pipline 目前仍是有一些不夠靈活,咱們使用的是自建 Jenkins 作 Pipeline 的,並行測試,能夠用命名空間等隔離策略進行隔離,或者準備多套測試環境
Q4: 大家運維的Dockerfile和開發的Dockerfile是怎麼合併的?
A4:開發的 Dockerfile 是 From 運維的 Dockerfile
Q5:大家k8s的漏洞掃描用的什麼工具?通常什麼級別的鏡像漏洞須要進行修復?
A5:暫時沒有使用漏掃工具,咱們主要根據 Rancher 企業服務通知的修復建議進行修復
Q6: 就是好比說從外網,經過service ip可以登錄而且管理容器。想實現這一步必須經過將service ip暴露出來,而後這個service ip怎麼暴露出來?麻煩解答一下。
A6:若是需求是管理容器,其實可使用 Rancher 的用戶權限控制,讓某一用戶擁有某一容器的權限,暴露 service ip 到公網,讓用戶管理容器是沒法實現的
Q6 : 好的,謝謝,我還有一點不明白,這個service ip有什麼辦法能讓他暴露出來呢?你意思是說讓不一樣的用戶經過rancher平臺去管理不一樣的容器嗎?麻煩再給解答一下,謝謝。
A6:可使用 NodePort 暴露,經過 Node ip 和 端口進行訪問,或者使用 公有云的負載均衡產品
Q6 : 我不是這個意思,我是想把service ip暴露出來,不僅單單想經過集羣內部訪問。
A6:service ip 原本就是 K8s 內部的,暴露不了,只能轉發
Q7: 爲什麼沒有放在3個可用區,若是可用區H掛掉,是否會致使集羣不可訪問?
A7:3個可用區固然也是能夠的,Rancher HA 架構,只要有一個 Server 可用就沒有關係
Q8:請教下大家多套開發測試環境的pipeline是怎麼樣的流程呢 (差別化)?有使用helm template嗎,方便講解下更多細節麼?
A8:目前是經過 Jenkins 部署參數,部署的時候能夠選擇 命名空間、環境標識、分支等,經過 sed 修改 template
Q9:請問大家的devops流是怎樣的呢?一個環境對應一個docker鏡像,仍是說test pre prd共用一個docker鏡像呢?若是是一個docker鏡像共用test pre prd的話是怎麼作的呢(好比不一樣環境的配置以及開發的協‘同開發流)?
A9:咱們是用的同一個鏡像,部署時經過選擇不一樣的環境標識參數,程序會自動注入不一樣環境的配置,須要開發進行一些相應的配置修改
Q10:不大懂容器的資源限制該如何配置,本身配置了感受不起做用
A10:Rancher 能夠在項目、命名空間、Pod 三個粒度進行設置,優先級相反